moses.c

00001 /* $Id: moses.c,v 1.116 2013-10-15 09:27:38 cgarcia Exp $
00002  *
00003  * This file is part of the MOSES library
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013-10-15 09:27:38 $
00024  * $Revision: 1.116 $
00025  * $Name: not supported by cvs2svn $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <math.h>
00037 #include <time.h>
00038 
00039 #include <fors_utils.h>
00040 #include <moses.h>
00041 
00042 /* Prototypes */
00043 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row);
00044 
00045 
00046 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
00047  * even if cpl_tool.h is not public. It should be removed as soon as 
00048  * an image median filtering with generic kernel will be implemented
00049  * in the CPL, or as soon as this module will be moved into the CPL. */
00050 /* FIXME: This has to be removed!! */
00051 
00052 float cpl_tools_get_median_float(float *, cpl_size);
00053 
00054 #define MAX_COLNAME      (80)
00055 #define STRETCH_FACTOR   (1.20)
00056 
00057 // Related to mos_identify_peaks(), used in multiplex mode
00058 
00059 static int mos_multiplex   = -1;
00060 static int mos_region_size = 800;
00061 
00062 static double default_lines_hi[] = {   /* Default sky line catalog */
00063                     5577.338,          /* for high res data        */
00064                     5889.953,
00065                     5895.923,
00066                     5915.301,
00067                     5932.862,
00068                     5953.420,
00069                     6257.961,
00070                     6287.434,
00071                     6300.304,
00072                     6306.869,
00073                     6363.780,
00074                     6498.729,
00075                     6533.044,
00076                     6553.617,
00077                     6841.945,
00078                     6863.955,
00079                     6870.994,
00080                     6889.288,
00081                     6900.833,
00082                     6912.623,
00083                     6923.220,
00084                     6939.521,
00085                     6969.930,
00086                     7003.858,
00087                     7244.907,
00088                     7276.405,
00089                     7284.439,
00090                     7316.282,
00091                     7329.148,
00092                     7340.885,
00093                     7358.659,
00094                     7571.746,
00095                     7750.640,
00096                     7759.996,
00097                     7794.112,
00098                     7808.467,
00099                     7821.503,
00100                     7841.266,
00101                     7913.708,
00102                     7949.204,
00103                     7964.650,
00104                     7993.332,
00105                     8014.059,
00106                     8310.719,
00107                     8344.602,
00108                     8382.392,
00109                     8399.170,
00110                     8415.231,
00111                     8430.174,
00112                     8452.250,
00113                     8493.389,
00114                     8791.186,
00115                     8827.096,
00116                     8885.850,
00117                     8903.114,
00118                     8943.395,
00119                     8988.366
00120                     };
00121 
00122 static double default_lines_lo[] = {   /* Default sky line catalog */
00123                     5577.338,          /* for low res data         */
00124                     6300.304,
00125                     6863.955,
00126                     7571.746,
00127                     7964.650,
00128                     7993.332
00129                     };
00130 
00131 
00141 /*
00142  * The following macros and function for finding the k-th smallest
00143  * value on a float array will be accessible from cpl_tools once
00144  * this module will be moved into the CPL.
00145  */
00146 
00147 /****
00148 
00149 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00150 
00151 static float kthSmallest(float a[], int n, int k)
00152 {
00153   register int i,j,l,m;
00154   register float x;
00155 
00156   l = 0;
00157   m = n-1;
00158   while (l < m) {
00159     x = a[k];
00160     i = l;
00161     j = m;
00162     do {
00163       while (a[i] < x) {
00164         i++;
00165       }
00166       while (x < a[j]) {
00167         j--;
00168       }
00169       if (i <= j) {
00170         PIX_SWAP(a[i],a[j]);
00171         i++;
00172         j--;
00173       }
00174     } while (i <= j);
00175 
00176     if (j < k) {
00177       l = i;
00178     }
00179     if (k < i) {
00180       m = j;
00181     }
00182 
00183   }
00184   return(a[k]);
00185 }
00186 
00187 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00188 
00189 ****/
00190 
00191 /* 
00192  * Return random number with gaussian distribution (mean = 0, variance = 1)
00193  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00194  * indicating whether to "seed" or not the sequence of generated random 
00195  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00196  * and at further calls the input argument is ignored. This function
00197  * generates two random numbers at each call, returning the first one
00198  * at odd calls, and the second one at even calls.
00199  */
00200 
00201 static void mos_seed(void)
00202 {
00203     srand((unsigned int)time((time_t *)0));
00204 }
00205 
00206 static double mos_randg(int seme)
00207 {
00208     static int doit = 1;
00209     static int gotit = 1;
00210     double x1, x2, w, y1;
00211     static double y2;
00212 
00213     if (gotit && seme) {
00214         mos_seed();
00215         gotit = 0;
00216     }
00217 
00218     if (doit) {
00219         doit = 0;
00220         do {
00221             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00222             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00223             w = x1 * x1 + x2 * x2;
00224         } while (w >= 1.0 || w == 0.0);
00225     
00226         w = sqrt( (-2.0 * log(w)) / w);
00227     
00228         y1 = x1 * w;
00229         y2 = x2 * w;
00230         return y1;
00231     }
00232 
00233     doit = 1;
00234     return y2;
00235 }
00236 
00237 /* 
00238  * This function contained a dependency on the VIMOS library
00239  * (function medianPixelvalue()): it should be removed as soon as an 
00240  * image median filtering with generic kernel will be implemented
00241  * in the CPL. Currently it has been solved by a direct call to
00242  * a cpl_tool function.
00243  */
00244 
00245 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00246                                             int filtsizey, int refrow,
00247                                             int above, int below, int step)
00248 {
00249 
00250   const char *func = "mos_image_general_median_filter";
00251 
00252   cpl_image  *filt_img = NULL;
00253   int         col, row;
00254   float      *buf = NULL;
00255   float      *data;
00256   float      *fdata;
00257   int         upright_y, loleft_y;
00258   int         j;
00259   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00260   int         f2y;
00261   int         nx = cpl_image_get_size_x(ima_in);
00262   int         ny = cpl_image_get_size_y(ima_in);
00263   int         firstRow;
00264 
00265 
00266   if (yIsEven) filtsizey++;
00267 
00268   if (ny <= filtsizey) {
00269     cpl_msg_error(func, 
00270                   "Median filter size: %d, image size: %d", filtsizey, ny);
00271     return NULL;
00272   }
00273 
00274   f2y = filtsizey / 2;
00275 
00276   filt_img = cpl_image_duplicate(ima_in);
00277   buf = cpl_malloc(filtsizey * sizeof(float));
00278   data = cpl_image_get_data(ima_in);
00279   fdata = cpl_image_get_data(filt_img);
00280 
00281   firstRow = refrow - step * (below / step);
00282   if (firstRow < f2y)
00283     firstRow += step;
00284 
00285   for (col = 0; col < nx; col++) {
00286     for (row = firstRow; row < refrow + above; row += step) {
00287       if (row >= ny - f2y)
00288         break;
00289       loleft_y = row - f2y;
00290       upright_y = row + f2y + 1;
00291       for (j = loleft_y; j < upright_y; j++)
00292         buf[j - loleft_y] = data[col + j * nx];
00293 
00294       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00295     }
00296   }
00297 
00298   cpl_free(buf);
00299 
00300   return filt_img;
00301 
00302 }
00303 
00304 
00305 /*
00306  * The following static function is used to find an accurate position
00307  * of a peak within a short interval (however at least 5 pixels long). 
00308  * The basic idea is to find the baricenter of all the pixel values 
00309  * that pass a threshold level between the median value and the maximum
00310  * value within the examined interval (in case such levels are equal,
00311  * the input is considered flat and no position is returned). At least
00312  * minPoints must pass this threshold, or no position is computed. To
00313  * evaluate the significance of the computed baricenter, the variance 
00314  * of the contributing positions (relative to the found baricenter) is 
00315  * also evaluated, and compared with the expected variance for a uniform 
00316  * distribution of positions. If the observed variance is greater than 
00317  * 80% of the variance of the uniform distribution, the found position 
00318  * is rejected.
00319  */
00320 
00321 static int peakPosition(const float *data, int size, float *position,
00322                         int minPoints)
00323 {
00324   int    i;
00325   int    count = 0;
00326   float *copy;
00327   float  max, median, level, pos, variance, uniformVariance;
00328   double sum, weights;
00329 
00330 
00331   if (data == NULL)
00332       return 1;
00333 
00334   if (size < 5)         /* Hardcoded, I know... */
00335       return 1;
00336 
00337 
00338   /*
00339    *  Find median level
00340    */
00341 
00342   copy = (float *) cpl_malloc(size*sizeof(float));
00343   for (i = 0; i < size; i++)
00344       copy[i] = data[i];
00345   median = cpl_tools_get_median_float(copy, size);
00346   cpl_free(copy);
00347 
00348 
00349   /*
00350    *  Find max
00351    */
00352 
00353   max = data[0];
00354   for (i = 1; i < size; i++)
00355       if (data[i] > max)
00356           max = data[i];
00357 
00358 
00359   /*
00360    *  If the max equals the median we have a flat input, therefore
00361    *  no peak is found.
00362    */
00363 
00364   if (max-median < 0.00001)
00365       return 1;
00366 
00367 
00368   /*
00369    *  Discrimination level: only pixels with values above this
00370    *  level are considered in baricenter calculation.
00371    */
00372 
00373   level = (max + median) / 2;
00374 
00375 
00376   /*
00377    *  Of the values above this level compute the baricenter and
00378    *  then the variance of the positions used. Note that the weights
00379    *  are taken as the difference between the pixels values and
00380    *  the median level (supposedly the background).
00381    */
00382 
00383   count = 0;
00384   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00385       if (data[i] > level) {
00386           count++;
00387           weights += (data[i] - median);
00388           sum     += i * (data[i] - median);
00389       }
00390   }
00391 
00392 
00393   /*
00394    *  If too few values are above threshold, refuse the position
00395    *  as insignificant
00396    */
00397 
00398   if (count < minPoints)
00399       return 1;
00400 
00401   pos = sum / weights;
00402   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00403       if (data[i] > level) {
00404           weights++;
00405           sum += (i - pos) * (i - pos);
00406       }
00407   }
00408   variance = sqrt(sum / weights);
00409 
00410 
00411  /*
00412   *  The "uniform variance" is the variance that should be obtained
00413   *  in the case of uniform distribution of the points positions in
00414   *  the selected interval. If the real variance is comparable with
00415   *  this value, the peak is considered not found.
00416   */
00417 
00418   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00419 
00420   if (variance > 0.8 * uniformVariance)
00421       return 1;
00422 
00423   *position = pos + 0.5;
00424 
00425   return 0;
00426 }
00427 
00428 
00429 /*
00430  *  The following static function determines the quantity dx to be
00431  *  added to the position of the highest pixel of a fiber profile,
00432  *  to get the true position of the profile maximum. All is needed
00433  *  is the maximum observed value v2 in the profile, and the observed
00434  *  values v1 and v3 of the previous and the next pixels in the profile.
00435  *  
00436  *  The following ratio is defined:
00437  *  
00438  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00439  *      
00440  *  This is a conventional ratio that wouldn't diverge for any set of
00441  *  pixel values, and that would not depend on the presence of background
00442  *  (with the assumption that the background level is the same for the 
00443  *  three pixels). R has also been chosen in such a way that its value
00444  *  is already quite close to the real dx. It should be noted that the
00445  *  following condition should be fulfilled:
00446  *
00447  *           v1  <= v2   and   v3  <  v2
00448  *  or
00449  *           v1  <  v2   and   v3  <=  v2
00450  *
00451  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00452  *  boundary cases, one has:
00453  *
00454  *           v2 = v1   and   R = dx = -0.5
00455  *           v2 = v3   and   R = dx =  0.5
00456  *
00457  *  Another special case is when the observed pixel values are perfectly
00458  *  symmetrical:
00459  *
00460  *           v1 = v3   and   R = dx =  0.0
00461  *
00462  *  In all the intermediate cases the relation between R and dx depends
00463  *  on the shape of the fiber profile, that has been determined elsewhere.
00464  *  Using the accurate reconstruction of the fiber profile obtained by 
00465  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00466  *  that R differs from dx always less than 0.01 pixels. If the condition
00467  *
00468  *           v1  <= v2   and   v3  <  v2
00469  *  or
00470  *           v1  <  v2   and   v3  <=  v2
00471  *
00472  *  is not fulfilled, then this function returns the value 2.0.
00473  */
00474 
00475 static double values_to_dx(double v1, double v2, double v3)
00476 {
00477 
00478   static double epsilon = 0.00000001;
00479   double        r       = 2.0;
00480 
00481 
00482   if (v1 > v2 || v3 > v2)
00483     return r;
00484 
00485   if (2 * v2 - v1 - v3 < epsilon)
00486     return r;
00487 
00488   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00489 
00490   return r;
00491 
00492 }
00493 
00494 
00495 /*
00496  * The following static function passes a min filter of given box
00497  * size on the data buffer. The box size must be a positive odd integer.
00498  */
00499 
00500 static float *min_filter(float *buffer, int length, int size)
00501 {
00502     float *minf  = cpl_calloc(length, sizeof(float));
00503     float  min;
00504     int    start = size / 2;
00505     int    end   = length - size / 2;
00506     int    i, j;
00507 
00508 
00509     for (i = start; i < end; i++) {
00510         min = buffer[i-start];
00511         for (j = i - start + 1; j <= i + start; j++)
00512             if (min > buffer[j])
00513                 min = buffer[j];
00514         minf[i] = min;
00515     }
00516 
00517     for (i = 0; i < start; i++)
00518         minf[i] = minf[start];
00519 
00520     for (i = end; i < length; i++)
00521         minf[i] = minf[end-1];
00522 
00523     return minf;
00524 }
00525 
00526 
00527 /*
00528  * The following static function passes a max filter of given box
00529  * size on the data buffer. The box size must be a positive odd integer.
00530  */
00531  
00532 static float *max_filter(float *buffer, int length, int size)
00533 {
00534     float *maxf  = cpl_calloc(length, sizeof(float));
00535     float  max;
00536     int    start = size / 2;
00537     int    end   = length - size / 2;
00538     int    i, j;
00539 
00540 
00541     for (i = start; i < end; i++) {
00542         max = buffer[i-start];
00543         for (j = i - start + 1; j <= i + start; j++)
00544             if (max < buffer[j])
00545                 max = buffer[j];
00546         maxf[i] = max;
00547     }
00548 
00549     for (i = 0; i < start; i++)
00550         maxf[i] = maxf[start];
00551 
00552     for (i = end; i < length; i++)
00553         maxf[i] = maxf[end-1];
00554 
00555     return maxf;
00556 }
00557 
00558 
00559 /*
00560  * The following static function passes a running average of given box
00561  * size on the data buffer. The box size must be a positive odd integer.
00562  */
00563  
00564 static float *smo_filter(float *buffer, int length, int size)
00565 {
00566     float *smof  = cpl_calloc(length, sizeof(float));
00567     double sum;
00568     int    start = size / 2;
00569     int    end   = length - size / 2;
00570     int    i, j;
00571 
00572 
00573     for (i = start; i < end; i++) {
00574         sum = 0.0;
00575         for (j = i - start; j <= i + start; j++)
00576             sum += buffer[j];
00577         smof[i] = sum / size;
00578     }
00579 
00580     for (i = 0; i < start; i++)
00581         smof[i] = smof[start];
00582 
00583     for (i = end; i < length; i++)
00584         smof[i] = smof[end-1];
00585 
00586     return smof;
00587 }
00588 
00589 /*
00590  * The following two static functions are used to read and write from the 
00591  * global distortion table the different model components. Conventionally
00592  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00593  * storage for model coefficients, and these functions guarantee that the
00594  * coefficients are read in and written out correctly, independent on their
00595  * physical meaning. The first 6 table rows are a description of the IDS
00596  * coefficients, followed by a row containing only the used reference 
00597  * wavelength. The remaining 3 are a description of the spectral curvature.
00598  * The first row is a description of coefficient c0, the second of coefficient
00599  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00600  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00601  * bivariate polynomialx on x,y mask coordinates. If the input table
00602  * to the write routine is NULL, it is allocated and initialised. Also
00603  * the input polynomial could be NULL, and nothing would be written to 
00604  * the table. If both pointers are NULL the function is basically a
00605  * constructor of the global distortion table.
00606  */
00607 
00608 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row)
00609 {
00610     cpl_polynomial *poly = NULL;
00611     cpl_size        p[2];
00612     cpl_size        degree = 2;
00613     int             null;
00614     double          coeff;
00615 
00616     char   name[MAX_COLNAME];
00617 
00618 
00619     for (p[0] = 0; p[0] <= degree; p[0]++) {
00620         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00621             snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00622             coeff = cpl_table_get_double(global, name, row, &null);
00623             if (null)
00624                 continue;
00625             if (poly == NULL)
00626                 poly = cpl_polynomial_new(2);
00627             cpl_polynomial_set_coeff(poly, p, coeff);
00628         }
00629     }
00630 
00631     return poly;
00632 }
00633 
00634 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00635                                           cpl_polynomial *poly)
00636 {
00637     cpl_table *table;
00638     cpl_size   p[2];
00639     cpl_size   degree = 2;
00640     int        nrow = 13;
00641 
00642     char       name[MAX_COLNAME];
00643 
00644 
00645     if (global) {
00646         table = global;
00647     }
00648     else {
00649         table = cpl_table_new(nrow);
00650         for (p[0] = 0; p[0] <= degree; p[0]++) {
00651             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00652                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00653                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00654             }
00655         }
00656     }
00657 
00658     if (poly) {
00659         for (p[0] = 0; p[0] <= degree; p[0]++) {
00660             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00661                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00662                 cpl_table_set_double(table, name, row, 
00663                                      cpl_polynomial_get_coeff(poly, p));
00664             }
00665         }
00666     }
00667 
00668     return table;
00669 }
00670 
00671 
00672 /*
00673  * The following static function is performing a robust linear fit
00674  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00675  *
00676  *  ----> y = a + b * x
00677  *
00678  * This function return 0 on success.
00679  */
00680 
00681 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00682 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00683                            double *abdev)
00684 {
00685     cpl_vector *vx;
00686     cpl_vector *vy;
00687     cpl_vector *va;
00688 
00689     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00690     double  sx, sy, sxy, sxx, chisq;
00691     double *arr;
00692     double  aa_ls, bb_ls;
00693     double *x;
00694     double *y;
00695     int     np;
00696     int     iter;
00697     int     max_iterate = 30;
00698     int     i;
00699 
00700 
00701     np = cpl_bivector_get_size(list);
00702     vx = cpl_bivector_get_x(list);
00703     vy = cpl_bivector_get_y(list);
00704     x = cpl_vector_get_data(vx);
00705     y = cpl_vector_get_data(vy);
00706 
00707     sx = sy = sxx = sxy = 0.00;
00708     for (i = 0; i < np; i++) {
00709         sx  += x[i];
00710         sy  += y[i];
00711         sxy += x[i] * y[i];
00712         sxx += x[i] * x[i];
00713     }
00714 
00715     del = np * sxx - sx * sx;
00716     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00717     bb_ls = bb = (np * sxy - sx * sy) / del;
00718 
00719     chisq = 0.00;
00720     for (i = 0; i < np; i++) {
00721         temp = y[i] - (aa+bb*x[i]);
00722         temp *= temp;
00723         chisq += temp;
00724     }
00725 
00726     va = cpl_vector_new(np);
00727     arr = cpl_vector_get_data(va);
00728     sigb = sqrt(chisq/del);
00729     b1 = bb;
00730 
00731     bcomp = b1;
00732     sum = 0.00;
00733     for (i = 0; i < np; i++) {
00734         arr[i] = y[i] - bcomp * x[i];
00735     }
00736     aa = cpl_vector_get_median_const(va);
00737     abdevt = 0.0;
00738     for (i = 0; i < np; i++) {
00739         d = y[i] - (bcomp * x[i] + aa);
00740         abdevt += fabs(d);
00741         if (y[i] != 0.0) 
00742             d /= fabs(y[i]);
00743         if (fabs(d) > 1e-7) 
00744             sum += (d >= 0.0 ? x[i] : -x[i]);
00745     }
00746     f1 = sum;
00747 
00748     b2 = bb + SEGNO(3.0 * sigb, f1);
00749 
00750     bcomp = b2;
00751     sum = 0.00;
00752     for (i = 0; i < np; i++) {
00753         arr[i] = y[i] - bcomp * x[i];
00754     }
00755     aa = cpl_vector_get_median_const(va);
00756     abdevt = 0.0;
00757     for (i = 0; i < np; i++) {
00758         d = y[i] - (bcomp * x[i] + aa);
00759         abdevt += fabs(d);
00760         if (y[i] != 0.0) 
00761             d /= fabs(y[i]);
00762         if (fabs(d) > 1e-7) 
00763             sum += (d >= 0.0 ? x[i] : -x[i]);
00764     }
00765     f2 = sum;
00766 
00767     if (fabs(b2-b1)<1e-7) {
00768         *a = aa;
00769         *b = bb;
00770         *abdev = abdevt / (double)np;
00771         cpl_vector_delete(va);
00772         return 0;
00773     }
00774 
00775     iter = 0;
00776     while (f1*f2 > 0.0) {
00777         bb = 2.0*b2-b1;
00778         b1 = b2;
00779         f1 = f2;
00780         b2 = bb;
00781 
00782         bcomp = b2;
00783         sum = 0.00;
00784         for (i = 0; i < np; i++) {
00785             arr[i] = y[i] - bcomp * x[i];
00786         }
00787         aa = cpl_vector_get_median_const(va);
00788         abdevt = 0.0;
00789         for (i = 0; i < np; i++) {
00790             d = y[i] - (bcomp * x[i] + aa);
00791             abdevt += fabs(d);
00792             if (y[i] != 0.0) 
00793                 d /= fabs(y[i]);
00794             if (fabs(d) > 1e-7) 
00795                 sum += (d >= 0.0 ? x[i] : -x[i]);
00796         }
00797         f2 = sum;
00798         iter++;
00799         if (iter >= max_iterate) 
00800             break;
00801     }
00802     if (iter >= max_iterate) {
00803         *a = aa_ls;
00804         *b = bb_ls;
00805         *abdev = -1.0;
00806         cpl_vector_delete(va);
00807         return 1;
00808     }
00809 
00810     sigb = 0.01 * sigb;
00811     while (fabs(b2-b1) > sigb) {
00812         bb = 0.5 * (b1 + b2);
00813         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00814             break;
00815         bcomp = bb;
00816         sum = 0.0;
00817         for (i = 0; i < np; i++) {
00818             arr[i] = y[i] - bcomp * x[i];
00819         }
00820         aa = cpl_vector_get_median_const(va);
00821         abdevt = 0.0;
00822         for (i = 0; i < np; i++) {
00823             d = y[i] - (bcomp * x[i] + aa);
00824             abdevt += fabs(d);
00825             if (y[i] != 0.0) 
00826                 d /= fabs(y[i]);
00827             if (fabs(d) > 1e-7) 
00828                 sum += (d >= 0.0 ? x[i] : -x[i]);
00829         }
00830         f = sum;
00831 
00832         if (f*f1 >= 0.0) {
00833             f1=f;
00834             b1=bb;
00835         } 
00836         else {
00837             f2=f;
00838             b2=bb;
00839         }
00840     }
00841     cpl_vector_delete(va);
00842     *a = aa;
00843     *b = bb;
00844     *abdev = abdevt / np;
00845     return 0;
00846 }
00847 #undef SEGNO
00848 
00849 /*      
00850  * The following static function applies the Hough transform from a table
00851  * of points to another table of points. Given the points p_i = (x_i,y_i)
00852  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00853  * and Y = y_i - X*x_i is computed and added to the output table for each
00854  * p_i, p_j pair. This means that if the input table has N points, the
00855  * output table has N*(N-1)/2 points.
00856  */
00857     
00858 /* static */
00859 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00860 {
00861     cpl_table *output;
00862     double    *xdata;
00863     double    *ydata;
00864     double    *xodata;
00865     double    *yodata;
00866     int        npoints;
00867     int        opoints;
00868     int        i, j, k;
00869 
00870 
00871     npoints = cpl_table_get_nrow(table);
00872     opoints = npoints*(npoints-1)/2;
00873 
00874     output = cpl_table_new(opoints);
00875     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00876     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00877     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00878     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00879 
00880     xodata = cpl_table_get_data_double(output, "m");
00881     yodata = cpl_table_get_data_double(output, "q");
00882     
00883     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00884     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00885 
00886     xdata = cpl_table_get_data_double(table, "x");
00887     ydata = cpl_table_get_data_double(table, "y");
00888 
00889     k = 0;
00890     for (i = 0; i < npoints; i++) {
00891         for (j = i+1; j < npoints; j++) {
00892             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00893             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00894             k++;
00895         }
00896     }
00897 
00898     if (k != opoints)
00899         printf("Assert k = %d, expected %d\n", k, opoints);
00900 
00901     cpl_table_erase_column(table, "x");
00902     cpl_table_erase_column(table, "y");
00903 
00904     return output;
00905 }
00906 
00907 
00908 /*
00909  * The following static function is performing the spectral
00910  * extraction for the function mos_extract_objects()
00911  */
00912 
00913 static void mos_extraction(cpl_image *sciwin, cpl_image *sci_var_win, 
00914                            cpl_image *skywin, 
00915                            cpl_image *extracted, cpl_image *sky, 
00916                            cpl_image *error, int nobjects, int extraction, 
00917                            double ron, double conad, int ncomb)
00918 {
00919 
00920   cpl_vector *vprofile;
00921   cpl_image  *smowin;
00922 
00923   int i, j;
00924   int specLen;
00925   int numRows;
00926   int index;
00927   int iter;
00928   int maxIter   = 2;         /* Not less than 2 !!! */
00929   int smoothBox = 31;        /* Not less than 5 !!! */
00930   double nsigma = 5.0;
00931 
00932   double sumWeight, sum, sumSky, sumProf, sumVar, variance, weight;
00933   double *profile;
00934   double *buffer;
00935   float  *edata;
00936   float  *ekdata;
00937   float  *endata;
00938   float  *sdata;
00939   float  *kdata;
00940   float  *fdata;
00941   float  *vardata;
00942 
00943   double value;
00944 
00945 
00946   specLen = cpl_image_get_size_x(sciwin);
00947   numRows = cpl_image_get_size_y(sciwin);
00948 
00949   edata = cpl_image_get_data(extracted);
00950   edata += nobjects * specLen;
00951 
00952   ekdata = cpl_image_get_data(sky);
00953   ekdata += nobjects * specLen;
00954 
00955   endata = cpl_image_get_data(error);
00956   endata += nobjects * specLen;
00957 
00958   sdata   = cpl_image_get_data(sciwin);
00959   kdata   = cpl_image_get_data(skywin);
00960   if(sci_var_win != NULL)
00961       vardata = cpl_image_get_data(sci_var_win);
00962 
00963   /*
00964    * Initial spectrum estimate
00965       if (sdata[i + j * specLen] > 0.0)
00966    */
00967 
00968   if (extraction && numRows > 5) {
00969       smowin = mos_image_filter_median(sciwin, 3, 3);
00970       fdata = cpl_image_get_data(smowin);
00971       for (i = 0; i < specLen; i++)
00972         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00973             edata[i] += fdata[i + j * specLen];
00974       cpl_image_delete(smowin);
00975   }
00976   else {
00977       for (i = 0; i < specLen; i++)
00978         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00979             edata[i] += sdata[i + j * specLen];
00980   }
00981 
00982   if (extraction) {
00983 
00984     profile = cpl_calloc(specLen * numRows, sizeof(double));
00985     buffer  = cpl_calloc(specLen, sizeof(double));
00986 
00987     for (iter = 0; iter < maxIter; iter++) {
00988 
00989       /*
00990        * Normalised spatial profile
00991        */
00992 
00993       for (i = 0; i < specLen; i++) {
00994         for (j = 0; j < numRows; j++) {
00995           index = i + j * specLen;
00996 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00997           if (fabs(edata[i]) > 0.00001)
00998             profile[index] = sdata[index] / edata[i];
00999           else
01000             profile[index] = 0.0;
01001         }
01002       }
01003 
01004       for (j = 0; j < numRows; j++) {
01005 
01006         /*
01007          * Smooth each row in the dispersion direction, and enforce positivity
01008          */
01009 
01010         for (i = 0; i < specLen - smoothBox; i++) {
01011           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01012           value = cpl_vector_get_median_const(vprofile);
01013           cpl_vector_unwrap(vprofile);
01014           if (value < 0)
01015             value = 0.0;
01016           buffer[i + smoothBox / 2] = value;
01017         }
01018 
01019         /*
01020          * Replace the end portions (i.e., not median filtered) with a mean
01021          */
01022 
01023         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01024         value = cpl_vector_get_mean(vprofile);
01025         cpl_vector_unwrap(vprofile);
01026 
01027         if (value < 0)
01028             value = 0.0;
01029 
01030         for (i = 0; i < smoothBox / 2; i++)
01031           buffer[i] = value;
01032 
01033         vprofile = cpl_vector_wrap(smoothBox / 2, 
01034                                    profile + specLen - smoothBox/2 + j*specLen);
01035         value = cpl_vector_get_mean(vprofile);
01036         cpl_vector_unwrap(vprofile);
01037 
01038         if (value < 0)
01039             value = 0.0;
01040 
01041         for (i = 0; i < smoothBox / 2; i++)
01042           buffer[i + specLen - smoothBox / 2] = value;
01043 
01044         for (i = 0; i < specLen; i++)
01045           profile[i + j * specLen] = buffer[i];
01046 
01047       }
01048 
01049       /*
01050        * Enforce normalization of spatial profile after smoothing
01051        */
01052 
01053       for (i = 0; i < specLen; i++) {
01054         for (j = 0, value = 0.0; j < numRows; j++)
01055           value += profile[i + j * specLen];
01056         if (value > 0.00001)
01057           for (j = 0; j < numRows; j++)
01058             profile[i + j * specLen] /= value;
01059         else
01060           for (j = 0; j < numRows; j++)
01061             profile[i + j * specLen] = 0.0;
01062       }
01063 
01064 
01065       /*
01066        * Optimal extraction
01067        */
01068 
01069       for (i = 0; i < specLen; i++) {
01070         sum = 0.0;
01071         sumSky = 0.0;
01072         sumWeight = 0.0;
01073         sumProf = 0.0;
01074         sumVar = 0;
01075         for (j = 0; j < numRows; j++) {
01076             index = i + j * specLen;
01077             /*        
01078             if (sdata[index] > 0.0) {
01079              */
01080             //This is the theoretical estimated variance. In principle, since we
01081             //have the propagated variance, we could use that one, but I leave
01082             //this as this is the original algorithm (cgarcia)
01083             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01084                                      / conad;
01085             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01086             value = sdata[index] - edata[i] * profile[index];
01087             if (fabs(value) / sqrt(variance) < nsigma) {
01088                 weight = 1000000 * profile[index] / variance;
01089                 sum += weight * sdata[index];
01090                 sumSky += weight * kdata[index];
01091                 sumWeight += weight * profile[index];
01092                 sumProf += profile[index];
01093                 //This is how we propagated the variance. We assume that the
01094                 //weigth has no error, although in has been computed from the
01095                 //profile and the theoretical variance (which also includes the data)
01096                 if(sci_var_win != NULL)
01097                     sumVar += weight * weight * vardata[index];
01098             }
01099         }
01100 
01101         if (sumWeight > 0.00001) {
01102           edata[i] = sum / sumWeight;
01103           ekdata[i] = sumSky / sumWeight;
01104           if(sci_var_win != NULL)
01105               endata[i] = sqrt(sumVar / sumWeight / sumWeight); //This is the error, not the variance.
01106           else              
01107               endata[i] = 1000 * sqrt(sumProf / sumWeight); //This was the old formula, which is not a real error propagation
01108         }
01109         else {
01110 /*
01111           edata[i] = 0.0;
01112           ekdata[i] = 0.0;
01113           endata[i] = 0.0;
01114 */
01115           //endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01116         }
01117       }
01118     }
01119     cpl_free(profile);
01120     cpl_free(buffer);
01121   }
01122   else {
01123 
01124     /*
01125      * Add sky estimation for the simple aperture extraction.
01126         if (kdata[i + j * specLen] > 0.0)
01127      */
01128 
01129     for (i = 0; i < specLen; i++)
01130       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01131           ekdata[i] += kdata[i + j * specLen];
01132 
01133     /*
01134      * Add error estimation for the simple aperture extraction.
01135      */
01136     for (i = 0; i < specLen; i++)
01137     {
01138         if(sci_var_win != NULL)
01139         {
01140             //We propagate the variance of a simple addition
01141             for (j = 0, endata[i] = 0.0; j < numRows; j++)
01142                 endata[i] += vardata[i + j * specLen];
01143             endata[i] = sqrt(endata[i]); //We return the error, not the variance
01144         }
01145         else 
01146             endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01147 
01148     }
01149   }
01150 
01151 }
01152 
01153 
01200 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01201                                  cpl_table *ids, cpl_table *crv, 
01202                                  double reference)
01203 {
01204     const char *func = "mos_global_distortion";
01205 
01206     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01207 
01208     cpl_table      *global = NULL;
01209     cpl_table      *coeff;
01210     cpl_table      *dummy;
01211     cpl_vector     *ci;
01212     cpl_vector     *xmask;
01213     cpl_vector     *ymask;
01214     cpl_bivector   *mask;
01215     cpl_vector     *xccd;
01216     cpl_vector     *yccd;
01217     cpl_bivector   *ccd;
01218     cpl_polynomial *poly;
01219     double         *xtop;
01220     double         *ytop;
01221     double         *xbottom;
01222     double         *ybottom;
01223     double         *mxtop;
01224     double         *mytop;
01225     double         *mxbottom;
01226     double         *mybottom;
01227     int            *position;
01228     int            *length;
01229     int            *slit_id;
01230     int            *mslit_id;
01231     int             nslits, nmaskslits, npoints;
01232     int             order;
01233     int             i, j;
01234     int             minslit = 6;    // 12;
01235 
01236 
01237     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01238         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01239         return NULL;
01240     }
01241 
01242     nslits = cpl_table_get_nrow(slits);
01243 
01244     if (nslits < minslit) {
01245         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01246                         "distortion model determination", nslits, minslit);
01247         return NULL;
01248     }
01249 
01250     nmaskslits = cpl_table_get_nrow(maskslits);
01251 
01252     length   = cpl_table_get_data_int(slits, "length");
01253     position = cpl_table_get_data_int(slits, "position");
01254     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01255     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01256     xtop     = cpl_table_get_data_double(slits, "xtop");
01257     ytop     = cpl_table_get_data_double(slits, "ytop");
01258     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01259     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01260     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01261     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01262     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01263     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01264 
01265 
01266     /*
01267      * Global IDS
01268      */
01269 
01270     coeff = cpl_table_new(nslits);
01271     cpl_table_copy_structure(coeff, ids);
01272     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01273     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01274     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01275     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01276 
01277     for (i = 0; i < nslits; i++) {
01278         for (j = 0; j < nmaskslits; j++) {
01279             if (slit_id[i] == mslit_id[j]) {
01280                 cpl_table_set_double(coeff, "xmask", i,
01281                                      (mxtop[j] + mxbottom[j]) / 2);
01282                 cpl_table_set_double(coeff, "ymask", i,
01283                                      (mytop[j] + mybottom[j]) / 2);
01284             }
01285         }
01286     }
01287 
01288     if (cpl_table_has_invalid(coeff, "xmask")) {
01289         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01290         cpl_table_delete(coeff);
01291         return NULL;
01292     }
01293 
01294     for (i = 0; i < nslits; i++) {
01295         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01296         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01297     }
01298 
01299     for (i = 0; i < nslits; i++) {
01300 
01301         if (length[i] == 0)
01302             continue;
01303 
01304         cpl_table_and_selected_window(ids, position[i], length[i]);
01305         dummy = cpl_table_extract_selected(ids);
01306         for (j = 0; j < 6; j++) {
01307             if (cpl_table_has_column(dummy, clab[j])) {
01308                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01309                     cpl_table_set_double(coeff, clab[j], i, 
01310                          cpl_table_get_column_median(dummy, clab[j]));
01311                 }
01312             }
01313         }
01314 
01315         cpl_table_delete(dummy);
01316         cpl_table_select_all(ids);
01317             
01318     }
01319 
01320     for (j = 0; j < 6; j++) {
01321         if (cpl_table_has_column(coeff, clab[j])) {
01322             cpl_table_and_selected_invalid(coeff, clab[j]);
01323 
01324             if (cpl_table_not_selected(coeff))
01325                 dummy = cpl_table_extract_selected(coeff);
01326             else
01327                 break;
01328 
01329             npoints = cpl_table_get_nrow(dummy);
01330 
01331             if (npoints >= 6) {
01332 
01333                 if (npoints >= 12)
01334                     order = 2;
01335                 else
01336                     order = 1;
01337                    
01338                 ci = cpl_vector_wrap(npoints,
01339                                      cpl_table_get_data_double(dummy, clab[j]));
01340                 if (j) {
01341                     xccd = cpl_vector_wrap(npoints,
01342                                      cpl_table_get_data_double(dummy, "xccd"));
01343                     yccd = cpl_vector_wrap(npoints,
01344                                      cpl_table_get_data_double(dummy, "yccd"));
01345                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01346 
01347 /* %%% */
01348                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01349 
01350                     cpl_bivector_unwrap_vectors(ccd);
01351                     cpl_vector_unwrap(xccd);
01352                     cpl_vector_unwrap(yccd);
01353                     cpl_vector_unwrap(ci);
01354                 }
01355                 else {
01356                     xmask = cpl_vector_wrap(npoints,
01357                                      cpl_table_get_data_double(dummy, "xmask"));
01358                     ymask = cpl_vector_wrap(npoints,
01359                                      cpl_table_get_data_double(dummy, "ymask"));
01360                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01361 
01362 /* %%% */
01363                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01364 
01365                     cpl_bivector_unwrap_vectors(mask);
01366                     cpl_vector_unwrap(xmask);
01367                     cpl_vector_unwrap(ymask);
01368                     cpl_vector_unwrap(ci);
01369                 }
01370             }
01371             else {
01372                 cpl_size p[2] = {0, 0};
01373                 poly = cpl_polynomial_new(2);
01374                 cpl_polynomial_set_coeff(poly, p, 
01375                                cpl_table_get_column_median(dummy, clab[j]));
01376             }
01377 
01378             cpl_table_delete(dummy);
01379 
01380             global = write_global_distortion(global, j, poly);
01381 
01382             cpl_polynomial_delete(poly);
01383 
01384             cpl_table_select_all(coeff);
01385         }
01386     }
01387 
01388     cpl_table_delete(coeff);
01389 
01390 
01391     /*
01392      * Add model's reference wavelength
01393      */
01394 
01395     cpl_table_set_double(global, "a00", 6, reference);
01396 
01397 
01398     /*
01399      * Global curvature model
01400      */
01401 
01402     coeff = cpl_table_duplicate(crv);
01403     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01404     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01405     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01406     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01407     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01408     npoints = cpl_table_get_nrow(coeff);
01409 
01410     for (i = 0; i < npoints; i++) {
01411         for (j = 0; j < nmaskslits; j++) {
01412             if (slit_id[i] == mslit_id[j]) {
01413                 if (i%2) {
01414                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01415                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01416                 }
01417                 else {
01418                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01419                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01420                 }
01421             }
01422         }
01423         if (i%2)
01424         {
01425             cpl_table_set_double(coeff, "xccd", i, xtop[(i-1)/2]);
01426             cpl_table_set_double(coeff, "yccd", i, ytop[(i-1)/2]);
01427         }
01428         else 
01429         {
01430             cpl_table_set_double(coeff, "xccd", i, xbottom[i/2]);
01431             cpl_table_set_double(coeff, "yccd", i, ybottom[i/2]);
01432         }
01433     }
01434 
01435     if (cpl_table_has_invalid(coeff, "xmask")) {
01436         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01437         cpl_table_delete(coeff);
01438         return NULL;
01439     }
01440 
01441     for (j = 0; j < 3; j++) {
01442         cpl_polynomial * poly_ccd;
01443         if (cpl_table_has_column(coeff, clab[j])) {
01444             cpl_table_and_selected_invalid(coeff, clab[j]);
01445 
01446             if (cpl_table_not_selected(coeff))
01447                 dummy = cpl_table_extract_selected(coeff);
01448             else
01449                 break;
01450 
01451             npoints = cpl_table_get_nrow(dummy);
01452 
01453             if (npoints >= 6) {
01454 
01455                 if (npoints >= 12)
01456                     order = 2;
01457                 else
01458                     order = 1;
01459 
01460                 ci = cpl_vector_wrap(npoints,
01461                                      cpl_table_get_data_double(dummy, clab[j]));
01462                 xmask = cpl_vector_wrap(npoints,
01463                                      cpl_table_get_data_double(dummy, "xmask"));
01464                 ymask = cpl_vector_wrap(npoints,
01465                                      cpl_table_get_data_double(dummy, "ymask"));
01466                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01467 
01468                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01469                 
01470                 xccd = cpl_vector_wrap(npoints,
01471                                        cpl_table_get_data_double(dummy, "xccd"));
01472                 yccd = cpl_vector_wrap(npoints,
01473                                        cpl_table_get_data_double(dummy, "yccd"));
01474                 ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01475 
01476                 poly_ccd = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01477                 
01478 
01479                 cpl_bivector_unwrap_vectors(mask);
01480                 cpl_vector_unwrap(ci);
01481                 cpl_vector_unwrap(xmask);
01482                 cpl_vector_unwrap(ymask);
01483             }
01484             else {
01485                 cpl_size p[2] = {0, 0};
01486                 poly = cpl_polynomial_new(2);
01487                 cpl_polynomial_set_coeff(poly, p,
01488                                cpl_table_get_column_median(dummy, clab[j]));
01489             }
01490 
01491             cpl_table_delete(dummy);
01492 
01493             global = write_global_distortion(global, j + 7, poly);
01494             global = write_global_distortion(global, j + 10, poly_ccd);
01495 
01496             cpl_polynomial_delete(poly);
01497             cpl_polynomial_delete(poly_ccd);
01498             cpl_table_select_all(coeff);
01499         }
01500     }
01501 
01502     cpl_table_delete(coeff);
01503 
01504     return global;
01505 
01506 }
01507 
01508 
01546 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01547                                    int ysize)
01548 {
01549     const char *func = "mos_build_slit_location";
01550 
01551     cpl_propertylist *sort_col;
01552     cpl_polynomial   *ids0;
01553     cpl_polynomial   *crv[3];
01554     cpl_polynomial   *loc_crv;
01555     cpl_vector       *point;
01556     cpl_table        *slits;
01557     cpl_size         nslits;
01558     int              *slit_id;
01559     double           *dpoint;
01560     double           *xtop;
01561     double           *ytop;
01562     double           *xbottom;
01563     double           *ybottom;
01564     double           *mxtop;
01565     double           *mytop;
01566     double           *mxbottom;
01567     double           *mybottom;
01568     cpl_size          i;
01569     cpl_size          j;
01570 
01571 
01572     if (global == NULL || maskslits == NULL) {
01573         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01574         return NULL;
01575     }
01576 
01577     nslits   = cpl_table_get_nrow(maskslits);
01578     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01579     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01580     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01581     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01582     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01583 
01584     slits = cpl_table_duplicate(maskslits);
01585 
01586     xtop    = cpl_table_get_data_double(slits, "xtop");
01587     ytop    = cpl_table_get_data_double(slits, "ytop");
01588     xbottom = cpl_table_get_data_double(slits, "xbottom");
01589     ybottom = cpl_table_get_data_double(slits, "ybottom");
01590 
01591     ids0 = read_global_distortion(global, 0);
01592     crv[0] = read_global_distortion(global, 7);
01593     crv[1] = read_global_distortion(global, 8);
01594     crv[2] = read_global_distortion(global, 9);
01595 
01596     loc_crv = cpl_polynomial_new(1);
01597 
01598     point = cpl_vector_new(2);
01599     dpoint = cpl_vector_get_data(point);
01600 
01601     for (i = 0; i < nslits; i++) {
01602         dpoint[0] = mxtop[i];
01603         dpoint[1] = mytop[i];
01604 
01605         xtop[i] = cpl_polynomial_eval(ids0, point);
01606 
01607         for (j = 0; j < 3; j++)
01608             if (crv[j])
01609                 cpl_polynomial_set_coeff(loc_crv, &j, 
01610                                          cpl_polynomial_eval(crv[j], point));
01611 
01612         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01613 
01614         dpoint[0] = mxbottom[i];
01615         dpoint[1] = mybottom[i];
01616         xbottom[i] = cpl_polynomial_eval(ids0, point);
01617 
01618         for (j = 0; j < 3; j++)
01619             if (crv[j])
01620                 cpl_polynomial_set_coeff(loc_crv, &j,
01621                                          cpl_polynomial_eval(crv[j], point));
01622 
01623         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01624     }
01625 
01626     cpl_vector_delete(point);
01627     cpl_polynomial_delete(ids0);
01628     cpl_polynomial_delete(loc_crv);
01629     for (j = 0; j < 3; j++)
01630         cpl_polynomial_delete(crv[j]);
01631 
01632     sort_col = cpl_propertylist_new();
01633     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01634     cpl_table_sort(slits, sort_col);
01635     cpl_table_sort(maskslits, sort_col);
01636     cpl_propertylist_delete(sort_col);
01637 
01638     /*
01639      * Eliminate slits which are _entirely_ outside the CCD
01640      */
01641 
01642     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01643     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01644     cpl_table_erase_selected(slits);
01645 
01646     nslits = cpl_table_get_nrow(slits);
01647 
01648     if (nslits == 0) {
01649         cpl_msg_warning(func, "No slits found on the CCD");
01650         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01651         cpl_table_delete(slits);
01652         return NULL;
01653     }
01654 
01655     if (nslits > 1)
01656         cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slits are entirely or partially "
01657                      "contained in CCD", nslits);
01658     else
01659         cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slit is entirely or partially "
01660                      "contained in CCD", nslits);
01661 
01662     return slits;
01663 
01664 }
01665 
01666 
01693 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01694                                 cpl_table *slits)
01695 {
01696     const char *func = "mos_build_curv_coeff";
01697 
01698     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01699                                                  /* Max order is 5 */
01700 
01701     cpl_polynomial *crv[3];
01702     cpl_vector     *point;
01703     cpl_table      *polytraces;
01704     double         *dpoint;
01705     double         *xtop;
01706     double         *ytop;
01707     double         *xbottom;
01708     double         *ybottom;
01709     int            *slit_id;
01710     int            *valid_id;
01711     int             nslits, nvalid;
01712     int             found;
01713     int             i, j, k;
01714 
01715 
01716     if (global == NULL || slits == NULL || maskslits == NULL) {
01717         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01718         return NULL;
01719     }
01720 
01721     nslits  = cpl_table_get_nrow(maskslits);
01722     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01723     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01724     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01725     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01726     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01727 
01728     polytraces = cpl_table_new(2*nslits);
01729     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01730     for (i = 0; i < 3; i++)
01731         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01732 
01733     crv[0] = read_global_distortion(global, 7);
01734     crv[1] = read_global_distortion(global, 8);
01735     crv[2] = read_global_distortion(global, 9);
01736 
01737     point = cpl_vector_new(2);
01738     dpoint = cpl_vector_get_data(point);
01739 
01740     for (i = 0; i < nslits; i++) {
01741         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01742 
01743             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01744 
01745             if (j) {
01746                 dpoint[0] = xbottom[i];
01747                 dpoint[1] = ybottom[i];                
01748             }
01749             else {
01750                 dpoint[0] = xtop[i];
01751                 dpoint[1] = ytop[i];                
01752             }
01753 
01754             for (k = 0; k < 3; k++)
01755                 if (crv[j])
01756                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01757                                          cpl_polynomial_eval(crv[k], point));
01758         }
01759     }
01760 
01761     cpl_vector_delete(point);
01762     for (j = 0; j < 3; j++)
01763         cpl_polynomial_delete(crv[j]);
01764 
01765     /*
01766      * Eliminate slits which are _entirely_ outside the CCD
01767      */
01768  
01769     nvalid  = cpl_table_get_nrow(slits);
01770     valid_id = cpl_table_get_data_int(slits, "slit_id");
01771     cpl_table_unselect_all(polytraces);
01772     for (i = 0; i < nslits; i++) {
01773         found = 0;
01774         for (j = 0; j < nvalid; j++) {
01775             if (slit_id[i] == valid_id[j]) {
01776                 found = 1;
01777                 break;
01778             }
01779         }
01780         if (!found) {
01781             cpl_table_select_row(polytraces, 2*i);
01782             cpl_table_select_row(polytraces, 2*i + 1);
01783         }
01784     }
01785     cpl_table_erase_selected(polytraces);
01786  
01787     nslits = cpl_table_get_nrow(polytraces);
01788 
01789     if (nslits == 0) {
01790         cpl_msg_warning(func, "No slits found on the CCD");
01791         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01792         cpl_table_delete(polytraces);
01793         return NULL;
01794     }
01795 
01796     if (nslits > 2) 
01797         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01798                      "partially contained in CCD", nslits / 2);
01799     else
01800         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01801                      "partially contained in CCD", nslits / 2);
01802 
01803     return polytraces;
01804 }
01805 
01806 
01848 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01849 {
01850     const char *func = "mos_build_disp_coeff";
01851 
01852     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01853 
01854     cpl_polynomial *ids[6];
01855     cpl_vector     *point;
01856     cpl_table      *idscoeff;
01857     double         *dpoint;
01858     double         *xtop;
01859     double         *ytop;
01860     double         *xbottom;
01861     double         *ybottom;
01862     int            *position;
01863     int            *length;
01864     int             nslits;
01865     int             nrows;
01866     int             order;
01867     int             ylow, yhig;
01868     int             i, j, k;
01869 
01870 
01871     if (global == NULL || slits == NULL) {
01872         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01873         return NULL;
01874     }
01875     
01876     nslits   = cpl_table_get_nrow(slits);
01877     position = cpl_table_get_data_int(slits, "position");
01878     length   = cpl_table_get_data_int(slits, "length");
01879     xtop     = cpl_table_get_data_double(slits, "xtop");
01880     ytop     = cpl_table_get_data_double(slits, "ytop");
01881     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01882     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01883 
01884     for (i = 0; i < 6; i++)
01885         ids[i] = read_global_distortion(global, i);
01886 
01887     for (i = 0; i < 6; i++)
01888         if (ids[i] == NULL)
01889             break;
01890 
01891     order = i - 1;
01892 
01893     if (order < 1) {
01894         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01895         return NULL;
01896     }
01897 
01898     nrows = 0;
01899     for (i = 0; i < nslits; i++)
01900         nrows += length[i]; 
01901 
01902     idscoeff = cpl_table_new(nrows);
01903 
01904     for (j = 0; j <= order; j++)
01905         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01906 
01907     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01908     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01909     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01910     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01911 
01912     point = cpl_vector_new(2);
01913     dpoint = cpl_vector_get_data(point);
01914 
01915     for (i = 0; i < nslits; i++) {
01916 
01917         if (length[i] == 0)
01918             continue;
01919 
01920         ylow = position[i];
01921         yhig = ylow + length[i];
01922 
01923         for (j = 0; j <= order; j++) {
01924             if (j) {
01925                 for (k = 0; k < length[i]; k++) {
01926                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01927                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01928                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01929                                          cpl_polynomial_eval(ids[j], point));
01930                 }
01931             }
01932             else {
01933                 for (k = 0; k < length[i]; k++) {
01934                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01935                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01936                 }
01937             }
01938         }
01939     }
01940 
01941     cpl_vector_delete(point);
01942     for (j = 0; j < 6; j++)
01943         cpl_polynomial_delete(ids[j]);
01944 
01945     return idscoeff;
01946 
01947 }
01948 
01949 
01972 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01973                             cpl_table *polytraces, double reference, 
01974                             double blue, double red, double dispersion)
01975 {
01976     const char     *func = "mos_subtract_sky";
01977 
01978     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01979                                                  /* Max order is 5 */
01980 
01981     cpl_image      *sky;
01982     cpl_bivector   *list;
01983     cpl_vector     *listx;
01984     cpl_vector     *listy;
01985     cpl_polynomial *polytop;
01986     cpl_polynomial *polybot;
01987     cpl_polynomial *trend;
01988 
01989     int            *slit_id;
01990     double         *dlistx;
01991     double         *dlisty;
01992     float          *sdata;
01993     float          *kdata;
01994     double          top, bot;
01995     int             itop, ibot;
01996     double          coeff;
01997     double          ytop, ybot;
01998     double          m, q, err;
01999     int             npix;
02000 
02001     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02002     int             nx, ny;
02003     int             nslits;
02004     int            *length;
02005     int             missing_top, missing_bot;
02006     int             order;
02007     int             null;
02008     int             window = 50;  /* Longer slits have polynomial sky model */
02009     int             count;
02010     int             i, j;
02011     cpl_size        k;
02012 
02013 
02014     if (science == NULL || slits == NULL || polytraces == NULL) {
02015         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02016         return NULL;
02017     }
02018  
02019     if (dispersion <= 0.0) {
02020         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02021         return NULL;
02022     }
02023 
02024     if (red - blue < dispersion) {
02025         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02026         return NULL;
02027     }
02028 
02029     nx = cpl_image_get_size_x(science);
02030     ny = cpl_image_get_size_y(science);
02031 
02032     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02033 
02034     sdata = cpl_image_get_data(science);
02035     kdata = cpl_image_get_data(sky);
02036 
02037     nslits   = cpl_table_get_nrow(slits);
02038     order    = cpl_table_get_ncol(polytraces) - 2;
02039     length   = cpl_table_get_data_int(slits, "length");
02040     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02041 
02042     /*
02043      * The spatial resampling is performed for a certain number of
02044      * pixels above and below the position of the reference wavelength:
02045      */
02046     
02047     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02048     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02049 
02050     for (i = 0; i < nslits; i++) {
02051 
02052         if (length[i] == 0)
02053             continue;
02054 
02055         
02056         /*
02057          * Recover from the table of spectral curvature coefficients
02058          * the curvature polynomials.
02059          */
02060 
02061         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02062 
02063         start_pixel = refpixel - pixel_below;
02064         if (start_pixel < 0)
02065             start_pixel = 0;
02066 
02067         end_pixel = refpixel + pixel_above;
02068         if (end_pixel > nx)
02069             end_pixel = nx;
02070 
02071         missing_top = 0;
02072         polytop = cpl_polynomial_new(1);
02073         for (k = 0; k <= order; k++) {
02074             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02075             if (null) {
02076                 cpl_polynomial_delete(polytop);
02077                 missing_top = 1;
02078                 break;
02079             }
02080             cpl_polynomial_set_coeff(polytop, &k, coeff);
02081         }
02082 
02083         missing_bot = 0;
02084         polybot = cpl_polynomial_new(1);
02085         for (k = 0; k <= order; k++) {
02086             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02087             if (null) {
02088                 cpl_polynomial_delete(polybot);
02089                 missing_bot = 1;
02090                 break;
02091             }
02092             cpl_polynomial_set_coeff(polybot, &k, coeff);
02093         }
02094 
02095         if (missing_top && missing_bot) {
02096             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02097                           slit_id[i]);
02098             continue;
02099         }
02100 
02101         /*
02102          * In case just one of the two edges was not traced, the other
02103          * edge curvature model is duplicated and shifted to the other
02104          * end of the slit: better than nothing!
02105          */
02106 
02107         if (missing_top) {
02108             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02109                           "the spectral curvature of the lower edge "
02110                           "is used instead.", slit_id[i]);
02111             polytop = cpl_polynomial_duplicate(polybot);
02112             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02113             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02114             k = 0;
02115             coeff = cpl_polynomial_get_coeff(polybot, &k);
02116             coeff += ytop - ybot;
02117             cpl_polynomial_set_coeff(polytop, &k, coeff);
02118         }
02119 
02120         if (missing_bot) {
02121             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02122                           "the spectral curvature of the upper edge "
02123                           "is used instead.", slit_id[i]);
02124             polybot = cpl_polynomial_duplicate(polytop);
02125             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02126             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02127             k = 0;
02128             coeff = cpl_polynomial_get_coeff(polytop, &k);
02129             coeff -= ytop - ybot;
02130             cpl_polynomial_set_coeff(polybot, &k, coeff);
02131         }
02132 
02133 
02134         /*
02135          * Now read pixel values along spatial direction, and fit them.
02136          */
02137 
02138         for (j = start_pixel; j < end_pixel; j++) {
02139             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02140             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02141             itop = floor(top + 0.5) + 1;
02142             ibot = floor(bot + 0.5);
02143             if (itop > ny)
02144                 itop = ny;
02145             if (ibot < 0)
02146                 ibot = 0;
02147             npix = itop - ibot;
02148             if (npix < 5)
02149                 break;
02150 
02151             list = cpl_bivector_new(npix);
02152             listx = cpl_bivector_get_x(list);
02153             listy = cpl_bivector_get_y(list);
02154             dlistx = cpl_vector_get_data(listx);
02155             dlisty = cpl_vector_get_data(listy);
02156 
02157             for (k = 0; k < npix; k++) {
02158                 dlistx[k] = k;
02159                 dlisty[k] = sdata[j + (ibot + k)*nx];
02160             }
02161 
02162             if (robustLinearFit(list, &q, &m, &err)) {
02163                 cpl_bivector_delete(list);
02164                 continue;
02165             }
02166 
02167             cpl_bivector_delete(list);
02168 
02169             for (k = 0; k < npix; k++) {
02170                 kdata[j + (ibot + k)*nx] = m*k + q;
02171             }
02172 
02173             if (npix > window) {
02174 
02175                 /*
02176                  * Polynomial iteration
02177                  */
02178 
02179                 err = 3*sqrt(err);
02180 
02181                 count = 0;
02182                 for (k = 0; k < npix; k++)
02183                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02184                         count++;
02185 
02186                 if (count < 10)
02187                     continue;
02188 
02189                 list = cpl_bivector_new(count);
02190                 listx = cpl_bivector_get_x(list);
02191                 listy = cpl_bivector_get_y(list);
02192                 dlistx = cpl_vector_get_data(listx);
02193                 dlisty = cpl_vector_get_data(listy);
02194 
02195                 count = 0;
02196                 for (k = 0; k < npix; k++) {
02197                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02198                         dlistx[count] = k;
02199                         dlisty[count] = sdata[j + (ibot + k)*nx];
02200                         count++;
02201                     }
02202                 }
02203 
02204                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02205  
02206                 cpl_bivector_delete(list);
02207 
02208                 err = 3*sqrt(err);
02209 
02210                 count = 0;
02211                 for (k = 0; k < npix; k++)
02212                     if (fabs(sdata[j + (ibot + k)*nx] 
02213                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02214                         count++;
02215 
02216                 if (count < 10) {
02217                     cpl_polynomial_delete(trend);
02218                     continue;
02219                 }
02220 
02221                 list = cpl_bivector_new(count);
02222                 listx = cpl_bivector_get_x(list);
02223                 listy = cpl_bivector_get_y(list);
02224                 dlistx = cpl_vector_get_data(listx);
02225                 dlisty = cpl_vector_get_data(listy);
02226 
02227                 count = 0;
02228                 for (k = 0; k < npix; k++) {
02229                     if (fabs(sdata[j + (ibot + k)*nx] 
02230                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02231                         dlistx[count] = k;
02232                         dlisty[count] = sdata[j + (ibot + k)*nx];
02233                         count++;
02234                     }
02235                 }
02236 
02237                 cpl_polynomial_delete(trend);
02238 
02239                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02240 
02241                 cpl_bivector_delete(list);
02242  
02243                 for (k = 0; k < npix; k++) {
02244                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02245                                                k, NULL);
02246                 }
02247 
02248                 cpl_polynomial_delete(trend);
02249             }
02250         }
02251         cpl_polynomial_delete(polytop);
02252         cpl_polynomial_delete(polybot);
02253     }
02254 
02255     cpl_image_subtract(science, sky);
02256 
02257     return sky;
02258 }
02259 
02260 
02293 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02294                               cpl_table *slits, cpl_table *polytraces, 
02295                               double reference, double blue, double red, 
02296                               double dispersion, int sradius, int polyorder)
02297 {
02298     const char     *func = "mos_normalise_flat";
02299 
02300     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02301                                                  /* Max order is 5 */
02302 
02303     cpl_image      *rectified;
02304     cpl_image      *smo_flat;
02305     cpl_image      *exslit;
02306     cpl_vector     *positions;
02307     cpl_vector     *flux;
02308     cpl_vector     *smo_flux;
02309     cpl_polynomial *trend;
02310     cpl_polynomial *polytop;
02311     cpl_polynomial *polybot;
02312 
02313     int            *slit_id;
02314     float          *p;
02315     float          *data;
02316     double         *fdata;
02317     double         *pdata;
02318     float          *sdata;
02319     float          *xdata;
02320     float          *wdata;
02321     double          vtop, vbot, value;
02322     double          top, bot;
02323     double          coeff;
02324     double          ytop, ybot;
02325     double          ypos;
02326     double          fvalue;
02327     int             ivalue;
02328     int             yint, yprev;
02329     int             npseudo;
02330 
02331     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02332     int             nx, ny, nsubx, nsuby;
02333     int             xlow, ylow, xhig, yhig;
02334     int             nslits;
02335     int            *position;
02336     int            *length;
02337     int             missing_top, missing_bot;
02338     int             order;
02339     int             npoints;
02340     int             uradius;
02341     int             null;
02342     int             i, j;
02343     cpl_size        k;
02344 
02345 /*    int             exclude = 5;     Number of excluded pixels at edges */
02346 
02347     /* For debug puposes only: cpl_image      *smo_rectified; */
02348 
02349 
02350     if (flat == NULL || slits == NULL || polytraces == NULL) {
02351         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02352         return NULL;
02353     }
02354  
02355     if (dispersion <= 0.0) {
02356         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02357         return NULL;
02358     }
02359 
02360     if (red - blue < dispersion) {
02361         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02362         return NULL;
02363     }
02364 
02365     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02366                                         blue, red, dispersion, 0, NULL);
02367 
02368     nx = cpl_image_get_size_x(rectified);
02369     ny = cpl_image_get_size_y(rectified);
02370 
02371     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02372                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02373     wdata = cpl_image_get_data(smo_flat);
02374 
02375     nslits   = cpl_table_get_nrow(slits);
02376     order    = cpl_table_get_ncol(polytraces) - 2;
02377     position = cpl_table_get_data_int(slits, "position");
02378     length   = cpl_table_get_data_int(slits, "length");
02379     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02380 
02381     /*
02382      * The spatial resampling is performed for a certain number of
02383      * pixels above and below the position of the reference wavelength:
02384      */
02385     
02386     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02387     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02388 
02389     xlow = 1;
02390     xhig = nx;
02391     for (i = 0; i < nslits; i++) {
02392 
02393         if (length[i] == 0)
02394             continue;
02395 
02396         /*
02397          * We DON'T write:
02398          *
02399          * ylow = position[i];
02400          * yhig = ylow + length[i];
02401          *
02402          * because the cpl_image pixels are counted from 1, and because in 
02403          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02404          */
02405 
02406         ylow = position[i] + 1;
02407         yhig = ylow + length[i] - 1;
02408 
02409         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02410 
02411         if (polyorder < 0) {
02412 
02413             cpl_image_turn(exslit, -1);   /* For faster memory access */
02414     
02415             nsubx = cpl_image_get_size_x(exslit);
02416             nsuby = cpl_image_get_size_y(exslit);
02417             data = cpl_image_get_data(exslit);
02418             flux = cpl_vector_new(nsubx);
02419 
02420             uradius = nsubx / 2;
02421             if (uradius > sradius)
02422                 uradius = sradius;
02423 
02424             for (j = 0; j < nsuby; j++) {
02425                 fdata = cpl_vector_get_data(flux);
02426                 p = data;
02427                 for (k = 0; k < nsubx; k++)
02428                     *fdata++ = *p++;
02429                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02430                 fdata = cpl_vector_get_data(smo_flux);
02431                 p = data;
02432                 for (k = 0; k < nsubx; k++)
02433                     *p++ = *fdata++;
02434                 cpl_vector_delete(smo_flux);
02435                 data += nsubx;
02436             }
02437 
02438             cpl_vector_delete(flux);
02439 
02440 
02441             /*
02442              * First fit fluxes along the spatial direction with a low-degree
02443              * polynomial (excluding the first and the last pixels, close to
02444              * the edges)
02445              */
02446 /*
02447             if (nsubx-2*exclude > 10) {
02448                 flux = cpl_vector_new(nsubx-2*exclude);
02449                 fdata = cpl_vector_get_data(flux);
02450                 positions = cpl_vector_new(nsubx-2*exclude);
02451                 for (j = 0; j < nsubx-2*exclude; j++)
02452                     cpl_vector_set(positions, j, j+exclude);
02453         
02454                 for (k = 0; k < nsuby; k++) {
02455                     for (j = 0; j < nsubx-2*exclude; j++)
02456                         fdata[j] = data[j+exclude];
02457                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02458                                                          1, NULL);
02459                     for (j = 0; j < nsubx; j++)
02460                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02461                     cpl_polynomial_delete(trend);
02462                     data += nsubx;
02463                 }
02464 
02465                 cpl_vector_delete(flux);
02466                 cpl_vector_delete(positions);
02467             }
02468 */
02469 
02470             /*
02471              * Now smooth along the dispersion direction 
02472              */
02473 
02474             cpl_image_turn(exslit, 1);   /* For faster memory access */
02475             nsubx = cpl_image_get_size_x(exslit);
02476             nsuby = cpl_image_get_size_y(exslit);
02477             data = cpl_image_get_data(exslit);
02478 
02479             for (j = 0; j < nsuby; j++) {
02480                 flux = cpl_vector_new(nsubx);
02481                 fdata = cpl_vector_get_data(flux);
02482                 p = data;
02483                 for (k = 0; k < nsubx; k++)
02484                     *fdata++ = *p++;
02485                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02486                 cpl_vector_delete(flux);
02487                 fdata = cpl_vector_get_data(smo_flux);
02488                 p = data;
02489                 for (k = 0; k < nsubx; k++)
02490                     *p++ = *fdata++;
02491                 cpl_vector_delete(smo_flux);
02492                 data += nsubx;
02493             }
02494         }
02495         else {
02496 
02497             /*
02498              * Fit with a polynomial the flat field trend row by row.
02499              */
02500 
02501             nsubx = cpl_image_get_size_x(exslit);
02502             nsuby = cpl_image_get_size_y(exslit);
02503             data = cpl_image_get_data(exslit);
02504 
02505             for (j = 0; j < nsuby; j++) {
02506 
02507                 /*
02508                  * First get the size of the vectors to allocate:
02509                  */
02510 
02511                 npoints = 0;
02512                 p = data + j*nsubx;
02513                 for (k = 0; k < nsubx; k++)
02514                     if (p[k] > 1.0)
02515                         npoints++;
02516 
02517                 if (npoints > polyorder + 1) {
02518 
02519                     /*
02520                      * Fill the vectors for the fitting
02521                      */
02522 
02523                     flux = cpl_vector_new(npoints);
02524                     fdata = cpl_vector_get_data(flux);
02525                     positions = cpl_vector_new(npoints);
02526                     pdata = cpl_vector_get_data(positions);
02527 
02528                     npoints = 0;
02529                     p = data + j*nsubx;
02530                     for (k = 0; k < nsubx; k++) {
02531                         if (p[k] > 1.0) {
02532                             fdata[npoints] = p[k];
02533                             pdata[npoints] = k;
02534                             npoints++;
02535                         }
02536                     }
02537     
02538                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02539                                                          polyorder, NULL);
02540 
02541                     cpl_vector_delete(flux);
02542                     cpl_vector_delete(positions);
02543 
02544                     if (trend) {
02545                         p = data + j*nsubx;
02546                         for (k = 0; k < nsubx; k++)
02547                             if (p[k] > 1.0)
02548                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02549                         cpl_polynomial_delete(trend);
02550                     }
02551                     else {
02552                         cpl_msg_warning(func, "Invalid flat field flux fit "
02553                                         "(ignored)");
02554                     }
02555                 }
02556             }
02557         }
02558 
02559         
02560         /*
02561          * Recover from the table of spectral curvature coefficients
02562          * the curvature polynomials.
02563          */
02564 
02565         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02566 
02567         start_pixel = refpixel - pixel_below;
02568         if (start_pixel < 0)
02569             start_pixel = 0;
02570 
02571         end_pixel = refpixel + pixel_above;
02572         if (end_pixel > nx)
02573             end_pixel = nx;
02574 
02575         missing_top = 0;
02576         polytop = cpl_polynomial_new(1);
02577         for (k = 0; k <= order; k++) {
02578             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02579             if (null) {
02580                 cpl_polynomial_delete(polytop);
02581                 missing_top = 1;
02582                 break;
02583             }
02584             cpl_polynomial_set_coeff(polytop, &k, coeff);
02585         }
02586 
02587         missing_bot = 0;
02588         polybot = cpl_polynomial_new(1);
02589         for (k = 0; k <= order; k++) {
02590             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02591             if (null) {
02592                 cpl_polynomial_delete(polybot);
02593                 missing_bot = 1;
02594                 break;
02595             }
02596             cpl_polynomial_set_coeff(polybot, &k, coeff);
02597         }
02598 
02599         if (missing_top && missing_bot) {
02600             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02601                           slit_id[i]);
02602             continue;
02603         }
02604 
02605         /*
02606          * In case just one of the two edges was not traced, the other
02607          * edge curvature model is duplicated and shifted to the other
02608          * end of the slit: better than nothing!
02609          */
02610 
02611         if (missing_top) {
02612             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02613                           "the spectral curvature of the lower edge "
02614                           "is used instead.", slit_id[i]);
02615             polytop = cpl_polynomial_duplicate(polybot);
02616             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02617             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02618             k = 0;
02619             coeff = cpl_polynomial_get_coeff(polybot, &k);
02620             coeff += ytop - ybot;
02621             cpl_polynomial_set_coeff(polytop, &k, coeff);
02622         }
02623 
02624         if (missing_bot) {
02625             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02626                           "the spectral curvature of the upper edge "
02627                           "is used instead.", slit_id[i]);
02628             polybot = cpl_polynomial_duplicate(polytop);
02629             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02630             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02631             k = 0;
02632             coeff = cpl_polynomial_get_coeff(polytop, &k);
02633             coeff -= ytop - ybot;
02634             cpl_polynomial_set_coeff(polybot, &k, coeff);
02635         }
02636 
02637 
02638         /*
02639          * Now map smoothed image to CCD.
02640          * Note that the npseudo value related to this slit is equal
02641          * to the number of spatial pseudo-pixels decreased by 1
02642          * (compare with function mos_spatial_calibration()).
02643          */
02644 
02645         nx = cpl_image_get_size_x(flat);
02646         ny = cpl_image_get_size_y(flat);
02647 
02648         sdata = cpl_image_get_data(spatial);
02649         xdata = cpl_image_get_data(exslit);
02650         npseudo = cpl_image_get_size_y(exslit) - 1;
02651 
02652         /*
02653          * Write interpolated smoothed values to CCD image
02654          */
02655 
02656         for (j = start_pixel; j < end_pixel; j++) {
02657             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02658             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02659             for (k = 0; k <= npseudo; k++) {
02660                 ypos = top - k*(top-bot)/npseudo;
02661                 yint = ypos;
02662 
02663                 /*
02664                  * The line:
02665                  *     value = sdata[j + nx*yint];
02666                  * should be equivalent to:
02667                  *     value = npseudo*(top-yint)/(top-bot);
02668                  */
02669 
02670                 if (yint < 0 || yint >= ny-1) {
02671                     yprev = yint;
02672                     continue;
02673                 }
02674 
02675                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02676                 ivalue = value;               /* Nearest spatial pixels:   */
02677                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02678                 if (ivalue < npseudo && ivalue >= 0) {
02679                     vtop = xdata[j + nx*(npseudo-ivalue)];
02680                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02681                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02682 
02683                     if (k) {
02684 
02685                         /*
02686                          * This is added to recover lost pixels on
02687                          * the CCD image (pixels are lost because
02688                          * the CCD pixels are less than npseudo+1).
02689                          */
02690 
02691                         if (yprev - yint > 1) {
02692                             value = sdata[j + nx*(yint+1)];
02693                             ivalue = value;
02694                             fvalue = value - ivalue;
02695                             if (ivalue < npseudo && ivalue >= 0) {
02696                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02697                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02698                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02699                                                        + vbot*fvalue;
02700                             }
02701                         }
02702                     }
02703                 }
02704                 yprev = yint;
02705             }
02706         }
02707         cpl_polynomial_delete(polytop);
02708         cpl_polynomial_delete(polybot);
02709         cpl_image_delete(exslit);
02710     }
02711 
02712     cpl_image_delete(rectified);
02713 
02714     cpl_image_divide(flat, smo_flat);
02715 
02716     return smo_flat;
02717 }
02718 
02719 
02744 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02745                                   int polyorder)
02746 {
02747     const char     *func = "mos_normalise_longflat";
02748 
02749     cpl_image      *smo_flat;
02750     cpl_image      *profile;
02751     cpl_vector     *flux;
02752     cpl_vector     *smo_flux;
02753     cpl_vector     *positions;
02754     cpl_polynomial *trend;
02755 
02756     float          *level;
02757     float          *p;
02758     float          *data;
02759     double         *fdata;
02760     double         *pdata;
02761 
02762     int             nx, ny;
02763     int             npoints;
02764     int             i, j;
02765 
02766 
02767     if (flat == NULL) {
02768         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02769         return NULL;
02770     }
02771  
02772     if (sradius < 1 || dradius < 1) {
02773         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02774         return NULL;
02775     }
02776 
02777     smo_flat = cpl_image_duplicate(flat);
02778 
02779     if (polyorder < 0) {
02780 
02781         /*
02782          * First smooth along the spatial direction
02783          */
02784 
02785         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02786 
02787         nx = cpl_image_get_size_x(smo_flat);
02788         ny = cpl_image_get_size_y(smo_flat);
02789         data = cpl_image_get_data(smo_flat);
02790     
02791         for (i = 0; i < ny; i++) {
02792             flux = cpl_vector_new(nx);
02793             fdata = cpl_vector_get_data(flux);
02794             p = data;
02795             for (j = 0; j < nx; j++)
02796                 *fdata++ = *p++;
02797             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02798             cpl_vector_delete(flux);
02799             fdata = cpl_vector_get_data(smo_flux);
02800             p = data;
02801             for (j = 0; j < nx; j++)
02802                 *p++ = *fdata++;
02803             cpl_vector_delete(smo_flux);
02804             data += nx;
02805         }
02806 
02807         /*
02808          * Second smooth along the dispersion direction
02809          */
02810 
02811         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02812 
02813         nx = cpl_image_get_size_x(smo_flat);
02814         ny = cpl_image_get_size_y(smo_flat);
02815         data = cpl_image_get_data(smo_flat);
02816 
02817         for (i = 0; i < ny; i++) {
02818             flux = cpl_vector_new(nx);
02819             fdata = cpl_vector_get_data(flux);
02820             p = data;
02821             for (j = 0; j < nx; j++)
02822                 *fdata++ = *p++;
02823             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02824             cpl_vector_delete(flux);
02825             fdata = cpl_vector_get_data(smo_flux);
02826             p = data;
02827             for (j = 0; j < nx; j++)
02828                 *p++ = *fdata++;
02829             cpl_vector_delete(smo_flux);
02830             data += nx;
02831         }
02832     }
02833     else {
02834 
02835         /*
02836          * Fit with a polynomial the flat field trend column by column.
02837          */
02838 
02839         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02840 
02841         nx = cpl_image_get_size_x(smo_flat);
02842         ny = cpl_image_get_size_y(smo_flat);
02843         data = cpl_image_get_data(smo_flat);
02844 
02845         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02846         level = cpl_image_get_data(profile);
02847 
02848         for (i = 0; i < ny; i++) {
02849 
02850             /*
02851              * First get the size of the vectors to allocate:
02852              * eliminate from fit any value more than 20% away
02853              * from median level in current column.
02854              */
02855 
02856             npoints = 0;
02857             p = data + i*nx;
02858             for (j = 0; j < nx; j++)
02859                 if (fabs(p[j]/level[i] - 1) < 0.20)
02860                     npoints++;
02861 
02862             if (npoints > polyorder + 1) {
02863 
02864                 /*
02865                  * Fill the vectors for the fitting
02866                  */
02867 
02868                 flux = cpl_vector_new(npoints);
02869                 fdata = cpl_vector_get_data(flux);
02870                 positions = cpl_vector_new(npoints);
02871                 pdata = cpl_vector_get_data(positions);
02872 
02873                 npoints = 0;
02874                 p = data + i*nx;
02875                 for (j = 0; j < nx; j++) {
02876                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02877                         fdata[npoints] = p[j];
02878                         pdata[npoints] = j;
02879                         npoints++;
02880                     }
02881                 }
02882     
02883                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02884                                                      polyorder, NULL);
02885 
02886                 cpl_vector_delete(flux);
02887                 cpl_vector_delete(positions);
02888 
02889                 if (trend) {
02890                     p = data + i*nx;
02891                     for (j = 0; j < nx; j++)
02892                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02893                     cpl_polynomial_delete(trend);
02894                 }
02895                 else {
02896                     cpl_msg_warning(func, 
02897                                     "Invalid flat field flux fit (ignored)");
02898                 }
02899             }
02900         }
02901 
02902         cpl_image_delete(profile);
02903         cpl_image_turn(smo_flat, 1);
02904 
02905     }
02906 
02907     cpl_image_divide(flat, smo_flat);
02908 
02909     return smo_flat;
02910 }
02911 
02912 
02935 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
02936                                               cpl_table *slits, 
02937                                               int order, int global)
02938 {
02939     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02940                                                  /* Max order is 5 */
02941     int nrow = cpl_table_get_nrow(slits);
02942     int i, j;
02943 
02944     
02945     if (order < 0)
02946         return CPL_ERROR_NONE;
02947 
02948     cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
02949     cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
02950 
02951     for (i = 0; i < nrow; i++) {
02952         int        position = cpl_table_get_int   (slits, "position", i, NULL);
02953         int        length   = cpl_table_get_int   (slits, "length",   i, NULL);
02954         double     xtop     = cpl_table_get_double(slits, "xtop",     i, NULL);
02955         double     xbot     = cpl_table_get_double(slits, "xbottom",  i, NULL);
02956         double     ytop     = cpl_table_get_double(slits, "ytop",     i, NULL);
02957         double     ybot     = cpl_table_get_double(slits, "ybottom",  i, NULL);
02958         double     dx       = xtop - xbot;
02959         double     dy       = ytop - ybot;
02960         cpl_table *table    = cpl_table_extract(idscoeff, position, length);
02961 
02962         if (mos_interpolate_wavecalib(table, NULL, 2, order))
02963             continue;
02964 
02965         cpl_table_erase_window(idscoeff, position, length);
02966         cpl_table_insert(idscoeff, table, position);
02967 
02968         cpl_table_delete(table);
02969 
02970         for (j = 0; j < length; j++) {
02971             cpl_table_set_double(idscoeff, "x", j + position,
02972                                  xbot + j*(dx/length));
02973             cpl_table_set_double(idscoeff, "y", j + position,
02974                                  ybot + j*(dy/length));
02975         }
02976     }
02977 
02978     if (global) {
02979 
02980         /*
02981          * Now fit a global solution
02982          */
02983 
02984         nrow = cpl_table_get_nrow(idscoeff);
02985 
02986         for (i = 0; i < 6; i++) {
02987             cpl_table      *dummy;
02988             cpl_vector     *x;
02989             cpl_vector     *y;
02990             cpl_bivector   *z;
02991             cpl_vector     *c;
02992             cpl_polynomial *p;
02993             cpl_vector     *point;
02994             double         *dpoint;
02995             int             npoints;
02996 
02997             if (!cpl_table_has_column(idscoeff, clab[i]))
02998                 break;
02999 
03000             npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
03001             if (npoints < 18)
03002                 break;
03003 
03004             dummy = cpl_table_new(nrow);
03005             cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
03006             cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
03007             cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
03008             cpl_table_erase_invalid(dummy);
03009 
03010             x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
03011             y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
03012             z = cpl_bivector_wrap_vectors(x, y);
03013             c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 
03014                                                                    clab[i]));
03015             p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
03016             cpl_bivector_unwrap_vectors(z);
03017             cpl_vector_unwrap(x);
03018             cpl_vector_unwrap(y);
03019             cpl_vector_unwrap(c);
03020             cpl_table_delete(dummy);
03021 
03022             point  = cpl_vector_new(2);
03023             dpoint = cpl_vector_get_data(point);
03024             for (j = 0; j < nrow; j++) {
03025                 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
03026                 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
03027                 cpl_table_set_double(idscoeff, clab[i], j,
03028                                      cpl_polynomial_eval(p, point));
03029             }
03030             cpl_vector_delete(point);
03031             cpl_polynomial_delete(p);
03032         }
03033     }
03034 
03035     return CPL_ERROR_NONE;
03036 }
03037 
03038 
03064 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
03065                                          cpl_image *wavemap, int mode,
03066                                          int degree)
03067 {
03068     const char *func = "mos_interpolate_wavecalib";
03069 
03070     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
03071                                                  /* Max order is 5 */
03072 
03073     cpl_vector     *wave;
03074     cpl_vector     *positions;
03075     cpl_polynomial *trend;
03076 
03077     float          *p;
03078     float          *data;
03079     double         *wdata;
03080     double         *pdata;
03081 
03082     double          c;
03083     double          mse, ksigma;
03084 
03085     int             order;
03086     int             nrows, first_row, last_row;
03087     int             nx, ny;
03088     int             npoints, rpoints;
03089     int             null;
03090     int             i, j, k;
03091 
03092     int             polyorder = 4;  /* Candidate input argument */
03093 
03094 
03095     if (idscoeff == NULL)
03096         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03097 
03098     if (mode < 0 || mode > 2)
03099         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03100 
03101     if (mode == 0 || degree < 0)
03102         return CPL_ERROR_NONE;
03103 
03104     if (wavemap) {
03105 
03106         /*
03107          * Fit with a polynomial the wavelength trend column by column.
03108          */
03109 
03110         cpl_image_turn(wavemap, -1);   /* For faster memory access */
03111 
03112         nx = cpl_image_get_size_x(wavemap);
03113         ny = cpl_image_get_size_y(wavemap);
03114         data = cpl_image_get_data(wavemap);
03115 
03116         for (i = 0; i < ny; i++) {
03117 
03118             /*
03119              * First get the size of the vectors to allocate:
03120              * eliminate from fit any value with "impossible" wavelength.
03121              */
03122 
03123             npoints = 0;
03124             p = data + i*nx;
03125             for (j = 0; j < nx; j++)
03126                 if (p[j] > 1.0)
03127                     npoints++;
03128 
03129             if (npoints > polyorder + 1) {
03130 
03131                 /*
03132                  * Fill the vectors for the fitting
03133                  */
03134 
03135                 wave = cpl_vector_new(npoints);
03136                 wdata = cpl_vector_get_data(wave);
03137                 positions = cpl_vector_new(npoints);
03138                 pdata = cpl_vector_get_data(positions);
03139 
03140                 npoints = 0;
03141                 p = data + i*nx;
03142                 for (j = 0; j < nx; j++) {
03143                     if (p[j] > 1.0) {
03144                         wdata[npoints] = p[j];
03145                         pdata[npoints] = j;
03146                         npoints++;
03147                     }
03148                 }
03149     
03150                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03151                                                      polyorder, &mse);
03152 
03153                 ksigma = 3*sqrt(mse);
03154 
03155                 cpl_vector_delete(wave);
03156                 cpl_vector_delete(positions);
03157 
03158                 if (trend) {
03159 
03160                     /*
03161                      * Apply 3-sigma rejection
03162                      */
03163 
03164                     rpoints = 0;
03165                     p = data + i*nx;
03166                     for (j = 0; j < nx; j++)
03167                         if (p[j] > 1.0)
03168                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03169                                                     - p[j]) < ksigma)
03170                                 rpoints++;
03171 
03172                     if (rpoints < npoints && rpoints > polyorder + 1) {
03173 
03174                         wave = cpl_vector_new(rpoints);
03175                         wdata = cpl_vector_get_data(wave);
03176                         positions = cpl_vector_new(rpoints);
03177                         pdata = cpl_vector_get_data(positions);
03178 
03179                         npoints = 0;
03180                         p = data + i*nx;
03181                         for (j = 0; j < nx; j++) {
03182                             if (p[j] > 1.0) {
03183                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03184                                                                 j, NULL) - p[j])
03185                                                                 < ksigma) {
03186                                     wdata[npoints] = p[j];
03187                                     pdata[npoints] = j;
03188                                     npoints++;
03189                                 }
03190                             }
03191                         }
03192         
03193                         cpl_polynomial_delete(trend);
03194                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03195                                                              polyorder, NULL);
03196 
03197                         cpl_vector_delete(wave);
03198                         cpl_vector_delete(positions);
03199                     }
03200                 }
03201 
03202                 if (trend) {
03203                     p = data + i*nx;
03204                     if (mode == 1) {
03205                         for (j = 0; j < nx; j++)
03206                             if (p[j] < 1.0)
03207                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03208                     }
03209                     else if (mode == 2) {
03210                         for (j = 0; j < nx; j++)
03211                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03212                     }
03213                     cpl_polynomial_delete(trend);
03214                 }
03215                 else {
03216                     cpl_msg_warning(func, 
03217                                     "Invalid wavelength field fit (ignored)");
03218                 }
03219             }
03220     
03221         }
03222 
03223         cpl_image_turn(wavemap, 1);
03224 
03225     }
03226 
03227 
03228     /*
03229      * Interpolating the IDS coefficients
03230      */
03231 
03232     nrows = cpl_table_get_nrow(idscoeff);
03233 
03234     order = 0;
03235     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03236         ++order;
03237     --order;
03238 
03239     if (degree == 0) {
03240         for (k = 0; k <= order; k++) {
03241             double m;
03242             if (cpl_table_has_column(idscoeff, clab[k])) {
03243                 m = cpl_table_get_column_median(idscoeff, clab[k]);
03244                 cpl_table_fill_column_window_double(idscoeff, clab[k], 
03245                                                     0, nrows, m);
03246             }
03247         }
03248 
03249         return CPL_ERROR_NONE;
03250     }
03251 
03252     first_row = 0;
03253     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03254         first_row++;
03255 
03256     last_row = nrows - 1;
03257     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03258         last_row--;
03259 
03260     for (k = 0; k <= order; k++) {
03261 
03262         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03263         wave = cpl_vector_new(npoints);
03264         wdata = cpl_vector_get_data(wave);
03265         positions = cpl_vector_new(npoints);
03266         pdata = cpl_vector_get_data(positions);
03267 
03268         npoints = 0;
03269         for (i = first_row; i <= last_row; i++) {
03270             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03271             if (null == 0) {
03272                 wdata[npoints] = c;
03273                 pdata[npoints] = i;
03274                 npoints++;
03275             }
03276         }
03277 
03278 // This doesn't seem to provide good results, I have not understood why.
03279 // Restore for robust linear fitting.
03280 //
03281         if (degree == 1) {
03282             cpl_vector   *p;
03283             cpl_vector   *w;
03284             cpl_bivector *list;
03285             double        q, m;
03286 
03287             if (npoints > 4) {
03288                 p = cpl_vector_extract(positions, 2, npoints - 2, 1);
03289                 w = cpl_vector_extract(wave, 2, npoints - 2, 1);
03290             }
03291             else {
03292                 p = positions;
03293                 w = wave;
03294             }
03295 
03296             list = cpl_bivector_wrap_vectors(p, w);
03297 
03298             robustLinearFit(list, &q, &m, &mse);
03299             cpl_bivector_unwrap_vectors(list);
03300             for (i = first_row; i <= last_row; i++)
03301                  cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
03302 
03303             if (npoints > 4) {
03304                 cpl_vector_delete(p);
03305                 cpl_vector_delete(w);
03306             }
03307 
03308             cpl_vector_delete(wave);
03309             cpl_vector_delete(positions);
03310 
03311             continue;
03312         }
03313 
03314 // End robust linear fitting
03315 
03316         trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
03317 
03318         ksigma = 3*sqrt(mse);
03319 
03320         cpl_vector_delete(wave);
03321         cpl_vector_delete(positions);
03322 
03323         /*
03324          * Iteration
03325          */
03326 
03327         if (trend) {
03328             rpoints = 0;
03329             for (i = first_row; i <= last_row; i++) {
03330                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03331                 if (null == 0) {
03332                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03333                                                                  < ksigma) {
03334                         rpoints++;
03335                     }
03336                 }
03337             }
03338 
03339             if (rpoints > 0 && rpoints < npoints) {
03340                 cpl_msg_debug(func, "%d points rejected from "
03341                               "wavelength calibration fit", 
03342                               npoints - rpoints);
03343 
03344                 wave = cpl_vector_new(rpoints);
03345                 wdata = cpl_vector_get_data(wave);
03346                 positions = cpl_vector_new(rpoints);
03347                 pdata = cpl_vector_get_data(positions);
03348 
03349                 npoints = 0;
03350                 for (i = first_row; i <= last_row; i++) {
03351                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03352                     if (null == 0) {
03353                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03354                                                                  < ksigma) {
03355                             wdata[npoints] = c;
03356                             pdata[npoints] = i;
03357                             npoints++;
03358                         }
03359                     }
03360                 }
03361 
03362                 if (npoints) {
03363                     cpl_polynomial_delete(trend);
03364                     trend = cpl_polynomial_fit_1d_create(positions, 
03365                                                          wave, degree, NULL);
03366                 }
03367 
03368                 cpl_vector_delete(wave);
03369                 cpl_vector_delete(positions);
03370 
03371             }
03372         }
03373 
03374         if (trend) {
03375             for (i = first_row; i <= last_row; i++) {
03376                 if (mode == 1) {
03377                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03378                         cpl_table_set_double(idscoeff, clab[k], i, 
03379                                              cpl_polynomial_eval_1d(trend, i,
03380                                                                     NULL));
03381                     }
03382                 }
03383                 else if (mode == 2) {
03384                     cpl_table_set_double(idscoeff, clab[k], i, 
03385                                      cpl_polynomial_eval_1d(trend, i, NULL));
03386                 }
03387             }
03388             cpl_polynomial_delete(trend);
03389         }
03390         else {
03391             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03392         }
03393 
03394     }
03395 
03396     return CPL_ERROR_NONE;
03397 }
03398 
03416 cpl_error_code mos_interpolate_wavecalib_mos(cpl_table *idscoeff, 
03417                                              int mode,
03418                                              int degree)
03419 {
03420     const char *func = "mos_interpolate_wavecalib";
03421 
03422     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
03423                                                  /* Max order is 5 */
03424 
03425     cpl_vector     *wave;
03426     cpl_vector     *positions;
03427     cpl_polynomial *trend;
03428 
03429     double         *wdata;
03430     double         *pdata;
03431 
03432     double          c;
03433     double          mse, ksigma;
03434 
03435     int             order;
03436     int             nrows, first_row, last_row;
03437     int             npoints, rpoints;
03438     int             null;
03439     int             i;
03440 
03441 
03442     if (idscoeff == NULL)
03443         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03444 
03445     if (mode < 0 || mode > 2)
03446         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03447 
03448     if (mode == 0 || degree < 0)
03449         return CPL_ERROR_NONE;
03450 
03451     /*
03452      * Interpolating the IDS coefficients
03453      */
03454 
03455     nrows = cpl_table_get_nrow(idscoeff);
03456 
03457     order = 0;
03458     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03459         ++order;
03460     --order;
03461 
03462     //For coefficients > 0 always do a median 
03463     
03464     int kinit = 1;
03465     if (degree == 0) 
03466         kinit = 0;
03467     for (int k = kinit; k <= order; k++) {
03468         double m;
03469         if (cpl_table_has_column(idscoeff, clab[k])) {
03470             m = cpl_table_get_column_median(idscoeff, clab[k]);
03471             cpl_table_fill_column_window_double(idscoeff, clab[k], 
03472                                                 0, nrows, m);
03473         }
03474     }
03475 
03476     if(degree > 0)
03477     {
03478         first_row = 0;
03479         while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03480             first_row++;
03481 
03482         last_row = nrows - 1;
03483         while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03484             last_row--;
03485 
03486         int korder = 0;
03487 
03488         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[korder]);
03489         wave = cpl_vector_new(npoints);
03490         wdata = cpl_vector_get_data(wave);
03491         positions = cpl_vector_new(npoints);
03492         pdata = cpl_vector_get_data(positions);
03493 
03494         npoints = 0;
03495         for (i = first_row; i <= last_row; i++) {
03496             c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
03497             if (null == 0) {
03498                 wdata[npoints] = c;
03499                 pdata[npoints] = i;
03500                 npoints++;
03501             }
03502         }
03503 
03504         // This doesn't seem to provide good results, I have not understood why.
03505         // Restore for robust linear fitting.
03506         //
03507         if (degree == 1) {
03508             cpl_vector   *p;
03509             cpl_vector   *w;
03510             cpl_bivector *list;
03511             double        q, m;
03512 
03513             if (npoints > 4) {
03514                 p = cpl_vector_extract(positions, 2, npoints - 2, 1);
03515                 w = cpl_vector_extract(wave, 2, npoints - 2, 1);
03516             }
03517             else {
03518                 p = positions;
03519                 w = wave;
03520             }
03521 
03522             list = cpl_bivector_wrap_vectors(p, w);
03523 
03524             robustLinearFit(list, &q, &m, &mse);
03525             cpl_bivector_unwrap_vectors(list);
03526             for (i = first_row; i <= last_row; i++)
03527                 cpl_table_set_double(idscoeff, clab[korder], i, q + m*i);
03528 
03529             if (npoints > 4) {
03530                 cpl_vector_delete(p);
03531                 cpl_vector_delete(w);
03532             }
03533 
03534             cpl_vector_delete(wave);
03535             cpl_vector_delete(positions);
03536 
03537         }
03538         else
03539         {
03540 
03541             // End robust linear fitting
03542 
03543             trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
03544 
03545             ksigma = 3*sqrt(mse);
03546 
03547             cpl_vector_delete(wave);
03548             cpl_vector_delete(positions);
03549 
03550             /*
03551              * Iteration
03552              */
03553 
03554             if (trend) {
03555                 rpoints = 0;
03556                 for (i = first_row; i <= last_row; i++) {
03557                     c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
03558                     if (null == 0) {
03559                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03560                                 < ksigma) {
03561                             rpoints++;
03562                         }
03563                     }
03564                 }
03565 
03566                 if (rpoints > 0 && rpoints < npoints) {
03567                     cpl_msg_debug(func, "%d points rejected from "
03568                             "wavelength calibration fit", 
03569                             npoints - rpoints);
03570 
03571                     wave = cpl_vector_new(rpoints);
03572                     wdata = cpl_vector_get_data(wave);
03573                     positions = cpl_vector_new(rpoints);
03574                     pdata = cpl_vector_get_data(positions);
03575 
03576                     npoints = 0;
03577                     for (i = first_row; i <= last_row; i++) {
03578                         c = cpl_table_get_double(idscoeff, clab[korder], i, &null);
03579                         if (null == 0) {
03580                             if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03581                                     < ksigma) {
03582                                 wdata[npoints] = c;
03583                                 pdata[npoints] = i;
03584                                 npoints++;
03585                             }
03586                         }
03587                     }
03588 
03589                     if (npoints) {
03590                         cpl_polynomial_delete(trend);
03591                         trend = cpl_polynomial_fit_1d_create(positions, 
03592                                 wave, degree, NULL);
03593                     }
03594 
03595                     cpl_vector_delete(wave);
03596                     cpl_vector_delete(positions);
03597 
03598                 }
03599             }
03600 
03601             if (trend) {
03602                 for (i = first_row; i <= last_row; i++) {
03603                     if (mode == 1) {
03604                         if (!cpl_table_is_valid(idscoeff, clab[korder], i)) {
03605                             cpl_table_set_double(idscoeff, clab[korder], i, 
03606                                     cpl_polynomial_eval_1d(trend, i,
03607                                             NULL));
03608                         }
03609                     }
03610                     else if (mode == 2) {
03611                         cpl_table_set_double(idscoeff, clab[korder], i, 
03612                                 cpl_polynomial_eval_1d(trend, i, NULL));
03613                     }
03614                 }
03615                 cpl_polynomial_delete(trend);
03616             }
03617             else {
03618                 cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03619             }
03620         }
03621     }
03622 
03623     return CPL_ERROR_NONE;
03624 }
03625 
03626 
03651 //TODO:Deprecate this function. Change all the recipes which still use it.
03652 //Use the new functionality to remove overscan, trimm and bias.
03653 
03654 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03655                            cpl_table *overscans)
03656 {
03657     const char *func = "mos_remove_bias";
03658 
03659     cpl_image *unbiased;
03660     cpl_image *overscan;
03661     double     mean_bias_level;
03662     double     mean_overscans_level;
03663     int        count;
03664     int        nrows;
03665     int        xlow, ylow, xhig, yhig;
03666     int        i;
03667 
03668 
03669     if (image == NULL || overscans == NULL) {
03670         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03671         return NULL;
03672     }
03673 
03674     nrows = cpl_table_get_nrow(overscans);
03675 
03676     if (nrows == 0) {
03677         cpl_msg_error(func, "Empty overscan table");
03678         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03679         return NULL;
03680     }
03681 
03682     if (bias) {
03683         if (nrows == 1) {
03684             unbiased = cpl_image_subtract_create(image, bias);
03685             if (unbiased == NULL) {
03686                 cpl_msg_error(func, "Incompatible master bias");
03687                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03688             }
03689             return unbiased;
03690         }
03691         mean_bias_level = cpl_image_get_mean(bias);
03692     }
03693     else {
03694         if (nrows == 1) {
03695             cpl_msg_error(func, "No master bias in input, and no overscan "
03696                           "regions in input image: bias subtraction "
03697                           "cannot be performed!");
03698             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03699             return NULL;
03700         }
03701         mean_bias_level = 0.0;
03702     }
03703 
03704     mean_overscans_level = 0.0;
03705     count = 0;
03706     for (i = 0; i < nrows; i++) {
03707         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03708         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03709         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03710         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03711 
03712         if (i == 0) {
03713             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03714             if (unbiased == NULL) {
03715                 cpl_msg_error(func, "Incompatible overscan table");
03716                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03717                 return NULL;
03718             }
03719             if (bias) {
03720                 if (cpl_image_subtract(unbiased, bias)) {
03721                     cpl_msg_error(func, "Incompatible master bias");
03722                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03723                     cpl_image_delete(unbiased);
03724                     return NULL;
03725                 }
03726             }
03727         }
03728         else {
03729             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03730             if (overscan == NULL) {
03731                 cpl_msg_error(func, "Incompatible overscan table");
03732                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03733                 cpl_image_delete(unbiased);
03734                 return NULL;
03735             }
03736 
03737             mean_overscans_level += cpl_image_get_median(overscan);
03738             count++;
03739 
03740 /***
03741  * Here the mean level was used: not very robust...
03742 
03743             mean_overscans_level += cpl_image_get_flux(overscan);
03744             count += cpl_image_get_size_x(overscan)
03745                    * cpl_image_get_size_y(overscan);
03746 ***/
03747             cpl_image_delete(overscan);
03748         }
03749     }
03750 
03751     /*
03752      * Overscan correction
03753      */
03754 
03755     mean_overscans_level /= count;
03756 
03757     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03758 
03759     cpl_msg_info(cpl_func, 
03760                  "Difference between mean overscans level "
03761                  "and mean bias level: %.2f",
03762                  mean_overscans_level - mean_bias_level);
03763 
03764     return unbiased;
03765 
03766 }
03767 
03768 
03827 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03828                                      int length, int msize, int fsize) 
03829 {
03830     const char *func = "mos_arc_background_1D";
03831 
03832     float  *minf;
03833     float  *maxf;
03834     float  *smof;
03835     int     i;
03836 
03837 
03838     if (spectrum == NULL || back == NULL)
03839         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03840 
03841     if (msize % 2 == 0)
03842         msize++;
03843 
03844     if (fsize % 2 == 0)
03845         fsize++;
03846 
03847     if (msize < 3 || fsize < msize || length < 2*fsize)
03848         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03849 
03850 
03851     minf = min_filter(spectrum, length, msize);
03852     smof = smo_filter(minf, length, fsize);
03853     cpl_free(minf);
03854     maxf = max_filter(smof, length, 2*msize+1);
03855     cpl_free(smof);
03856     smof = smo_filter(maxf, length, 2*fsize+1);
03857     cpl_free(maxf);
03858     minf = min_filter(smof, length, 2*msize+1);
03859     cpl_free(smof);
03860     smof = smo_filter(minf, length, 2*fsize+1);
03861     cpl_free(minf);
03862 
03863     for (i = 0; i < length; i++)
03864         back[i] = smof[i];
03865 
03866     cpl_free(smof);
03867 
03868     return CPL_ERROR_NONE;
03869 
03870 }
03871 
03872 
03929 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03930 {
03931     const char *func = "mos_arc_background";
03932 
03933     cpl_image  *fimage;
03934     cpl_image  *bimage;
03935     float      *data;
03936     float      *bdata;
03937     float      *row;
03938     float      *brow;
03939     int         nx, ny;
03940     int         i;
03941 
03942 
03943     if (image == NULL) {
03944         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03945         return NULL;
03946     }
03947 
03948     if (msize % 2 == 0)
03949         msize++;
03950 
03951     if (fsize % 2 == 0)
03952         fsize++;
03953 
03954     nx = cpl_image_get_size_x(image);
03955     ny = cpl_image_get_size_y(image);
03956 
03957     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03958 
03959     fimage = mos_image_filter_median(image, 3, 3);
03960 
03961     data = cpl_image_get_data_float(fimage);
03962     bdata = cpl_image_get_data_float(bimage);
03963 
03964     for (i = 0; i < ny; i++) {
03965         row = data + i * nx;
03966         brow = bdata + i * nx;
03967         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03968             cpl_error_set_where(func); 
03969             cpl_image_delete(fimage);
03970             cpl_image_delete(bimage);
03971             return NULL;
03972         }
03973     }
03974 
03975     cpl_image_delete(fimage);
03976 
03977     return bimage;
03978 }
03979 
03980 
04001 int mos_lines_width(const float *spectrum, int length)
04002 {
04003 
04004   const char *func = "mos_lines_width";
04005 
04006   double *profile1 = cpl_calloc(length - 1, sizeof(double));
04007   double *profile2 = cpl_calloc(length - 1, sizeof(double));
04008 
04009   double  norm, value, max;
04010   int     radius = 20;
04011   int     short_length = length - 2*radius - 1;
04012   int     width;
04013   int     i, j, k;
04014 
04015 
04016   /*
04017    * Derivative, and separation of positive and negative derivatives
04018    */
04019 
04020   for (j = 0, i = 1; i < length; j++, i++) {
04021       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
04022       if (profile1[j] < 0)
04023           profile1[j] = 0;
04024       if (profile2[j] > 0)
04025           profile2[j] = 0;
04026       else
04027           profile2[j] = -profile2[j];
04028   }
04029 
04030 
04031   /*
04032    * Profiles normalisation
04033    */
04034 
04035   length--;
04036 
04037   norm = 0;
04038   for (i = 0; i < length; i++)
04039       if (norm < profile1[i])
04040           norm = profile1[i];
04041 
04042   for (i = 0; i < length; i++) {
04043       profile1[i] /= norm;
04044       profile2[i] /= norm;
04045   }
04046 
04047 
04048   /*
04049    * Cross-correlation
04050    */
04051 
04052   max = -1;
04053   for (i = 0; i <= radius; i++) {
04054       value = 0;
04055       for (j = 0; j < short_length; j++) {
04056           k = radius+j;
04057           value += profile1[k] * profile2[k+i];
04058       }
04059       if (max < value) {
04060           max = value;
04061           width = i;
04062       }
04063   }
04064 
04065   cpl_free(profile1);
04066   cpl_free(profile2);
04067 
04068   if (max < 0.0) {
04069       cpl_msg_debug(func, "Cannot estimate line width");
04070       width = 1;
04071   }
04072 
04073   return width;
04074 
04075 }
04076 
04077 
04104 cpl_vector *mos_peak_candidates(const float *spectrum, 
04105                                 int length, float level, 
04106                                 float exp_width)
04107 { 
04108 
04109   const char *func = "mos_peak_candidates";
04110 
04111   int     i, j;
04112   int     nint   = length - 1;
04113   int     n      = 0;
04114   int     width  = 2 * ceil(exp_width / 2) + 1;
04115   int     start  = width / 2;
04116   int     end    = length - width / 2;
04117   int     step;
04118   float  *smo;
04119   double *data   = cpl_calloc(length/2, sizeof(double));
04120 
04121 
04122   if (spectrum == NULL) {
04123       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04124       return NULL;
04125   }
04126 
04127 
04128   /*
04129    * If lines have a flat top (as in the case of broad slit), smooth
04130    * before determining the max.
04131    */
04132 
04133   if (width > 7) {
04134     smo = cpl_calloc(length, sizeof(float));
04135     start = width / 2;
04136     end = length - width / 2;
04137     for (i = 0; i < start; i++)
04138       smo[i] = spectrum[i];
04139     for (i = start; i < end; i++) {
04140       for (j = i - start; j <= i + start; j++)
04141         smo[i] += spectrum[j];
04142       smo[i] /= width;
04143     }
04144     for (i = end; i < length; i++)
04145       smo[i] = spectrum[i];
04146   }
04147   else {
04148       smo = (float *)spectrum;
04149   }
04150 
04151   /*
04152    * Collect all relative maxima along spectrum, that are higher than the
04153    * specified level.
04154    */
04155 
04156   if (width > 20)
04157     step = width / 2;
04158   else
04159     step = 1;
04160 
04161   for (i = step; i < nint - step + 1; i += step) {
04162     if (smo[i] > level) {
04163       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
04164         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
04165           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
04166           ++n;
04167         }
04168       }
04169     }
04170   }
04171 
04172   if (width > 7) {
04173     cpl_free(smo);
04174   }
04175 
04176   if (n == 0) {
04177     cpl_free(data);
04178     return NULL;
04179   }
04180 
04181   return cpl_vector_wrap(n, data);
04182 
04183 }
04184 
04185 
04207 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
04208                              cpl_vector *peaks, int sradius)
04209 {
04210 
04211     const char *func = "mos_refine_peaks";
04212 
04213     double *data;
04214     float   pos;
04215     int     npeaks;
04216     int     startPos, endPos;
04217     int     window = 2*sradius+1;
04218     int     i, j;
04219 
04220 
04221     if (peaks == NULL || spectrum == NULL) {
04222         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04223         return NULL;
04224     }
04225 
04226     npeaks = cpl_vector_get_size(peaks);
04227     data = cpl_vector_unwrap(peaks);
04228 
04229     for (i = 0; i < npeaks; i++) {
04230         startPos = data[i] - window/2;
04231         endPos   = startPos + window;
04232         if (startPos < 0 || endPos >= length)
04233             continue;
04234 
04235         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
04236             pos += startPos;
04237             data[i] = pos;
04238         }
04239     }
04240 
04241     for (i = 1; i < npeaks; i++)
04242         if (data[i] - data[i-1] < 0.5)
04243             data[i-1] = -1.0;
04244 
04245     for (i = 0, j = 0; i < npeaks; i++) {
04246         if (data[i] > 0.0) {
04247             if (i != j)
04248                 data[j] = data[i];
04249             j++;
04250         }
04251     }
04252 
04253     return cpl_vector_wrap(j, data);
04254 
04255 }
04256 
04257 
04258 void mos_set_multiplex(int multiplex)
04259 {
04260     mos_multiplex = multiplex;
04261 }
04262 
04316 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
04317                                  double min_disp, double max_disp,
04318                                  double tolerance)
04319 {
04320 
04321   int      i, j, k, l;
04322   int      nlint, npint;
04323   int      minpos;
04324   float    min;
04325   double   lratio, pratio;
04326   double   lo_start, lo_end, hi_start, hi_end, denom;
04327   double   disp, variation, prev_variation;
04328   int      max, maxpos, minl, mink;
04329   int      ambiguous;
04330   int      npeaks_lo, npeaks_hi;
04331   int     *peak_lo;
04332   int     *peak_hi;
04333   int    **ident;
04334   int     *nident;
04335   int     *lident;
04336 
04337   double  *peak;
04338   double  *line;
04339   int      npeaks, nlines;
04340 
04341   double  *xpos;
04342   double  *lambda;
04343   int     *ilambda;
04344   double  *tmp_xpos;
04345   double  *tmp_lambda;
04346   int     *tmp_ilambda;
04347   int     *flag;
04348   int      n = 0;
04349   int      nn;
04350   int      nseq = 0;
04351   int      gap;
04352   int     *seq_length;
04353   int      found;
04354 
04355   peak        = cpl_vector_get_data(peaks);
04356   npeaks      = cpl_vector_get_size(peaks);
04357   line        = cpl_vector_get_data(lines);
04358   nlines      = cpl_vector_get_size(lines);
04359 
04360   if (npeaks < 4)
04361       return NULL;
04362 
04363   peak_lo     = cpl_malloc(npeaks * sizeof(int));
04364   peak_hi     = cpl_malloc(npeaks * sizeof(int));
04365   nident      = cpl_calloc(npeaks, sizeof(int));
04366   lident      = cpl_calloc(nlines, sizeof(int));
04367   xpos        = cpl_calloc(npeaks, sizeof(double));
04368   lambda      = cpl_calloc(npeaks, sizeof(double));
04369   ilambda     = cpl_calloc(npeaks, sizeof(int));
04370   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
04371   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
04372   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
04373   flag        = cpl_calloc(npeaks, sizeof(int));
04374   seq_length  = cpl_calloc(npeaks, sizeof(int));
04375   ident       = cpl_malloc(npeaks * sizeof(int *));
04376   for (i = 0; i < npeaks; i++)
04377     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
04378 
04379   /*
04380    * This is just the number of intervals - one less than the number
04381    * of points (catalog wavelengths, or detected peaks).
04382    */
04383 
04384   nlint = nlines - 1;
04385   npint = npeaks - 1;
04386 
04387 
04388   /*
04389    * Here the big loops on catalog lines begins.
04390    */
04391 
04392   for (i = 1; i < nlint; i++) {
04393 
04394 
04395     /*
04396      * For each catalog wavelength I take the previous and the next one, 
04397      * and compute the ratio of the corresponding wavelength intervals.
04398      * This ratio will be compared to all the ratios obtained doing the
04399      * same with all the detected peaks positions.
04400      */
04401 
04402     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
04403 
04404 
04405     /*
04406      * Here the loop on detected peaks positions begins.
04407      */
04408 
04409     for (j = 1; j < npint; j++) {
04410 
04411       /*
04412        * Not all peaks are used for computing ratios: just the ones
04413        * that are compatible with the expected spectral dispersion
04414        * are taken into consideration. Therefore, I define the pixel
04415        * intervals before and after any peak that are compatible with
04416        * the specified dispersion interval, and select just the peaks
04417        * within such intervals. If either of the two intervals doesn't
04418        * contain any peak, then I skip the current peak and continue
04419        * with the next.
04420        */
04421 
04422       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
04423       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
04424       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
04425       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
04426 
04427       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04428         if (peak[k] > lo_end)
04429           break;
04430         if (peak[k] > lo_start) {
04431           peak_lo[npeaks_lo] = k;
04432           ++npeaks_lo;
04433         }
04434       }
04435 
04436       if (npeaks_lo == 0)
04437         continue;
04438 
04439       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04440         if (peak[k] > hi_end)
04441           break;
04442         if (peak[k] > hi_start) {
04443           peak_hi[npeaks_hi] = k;
04444           ++npeaks_hi;
04445         }
04446       }
04447 
04448       if (npeaks_hi == 0)
04449         continue;
04450 
04451 
04452       /*
04453        * Now I have all peaks that may help for a local identification.
04454        * peak_lo[k] is the sequence number of the k-th peak of the lower
04455        * interval; peak_hi[l] is the sequence number of the l-th peak of
04456        * the higher interval. j is, of course, the sequence number of the
04457        * current peak (second big loop).
04458        */
04459 
04460       prev_variation = 1000.0;
04461       minl = mink = 0;
04462 
04463       for (k = 0; k < npeaks_lo; k++) {
04464         denom = peak[j] - peak[peak_lo[k]];
04465         for (l = 0; l < npeaks_hi; l++) {
04466 
04467           /*
04468            * For any pair of peaks - one from the lower and the other
04469            * from the higher interval - I compute the same ratio that
04470            * was computed with the current line catalog wavelength.
04471            */
04472 
04473           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04474 
04475           /*
04476            * If the two ratios are compatible within the specified
04477            * tolerance, we have a preliminary identification. This
04478            * will be marked in the matrix ident[][], where the first
04479            * index corresponds to a peak sequence number, and the second
04480            * index is the counter of the identifications made during
04481            * this whole process. The array of counters is nident[].
04482            * If more than one interval pair fulfills the specified
04483            * tolerance, the closest to the expected ratio is selected.
04484            */
04485 
04486           variation = fabs(lratio-pratio) / pratio;
04487 
04488           if (variation < tolerance) {
04489             if (variation < prev_variation) {
04490               prev_variation = variation;
04491               minl = l;
04492               mink = k;
04493             }
04494           }
04495         }
04496       }
04497       if (prev_variation < tolerance) {
04498         ident[j][nident[j]]                         = i;
04499         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04500         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04501         ++nident[j];
04502         ++nident[peak_hi[minl]];
04503         ++nident[peak_lo[mink]];
04504       }
04505     }   /* End loop on positions */
04506   }    /* End loop on lines     */
04507 
04508 
04509   /*
04510    * At this point I have filled the ident matrix with all my preliminary
04511    * identifications. Ambiguous identifications must be eliminated.
04512    */
04513 
04514 
04515   for (i = 0; i < npeaks; i++) {
04516 
04517 
04518     /*
04519      * I don't take into consideration peaks that were never identified.
04520      * They are likely contaminations, or emission lines that were not
04521      * listed in the input wavelength catalog.
04522      */
04523 
04524     if (nident[i] > 1) {
04525 
04526 
04527       /*
04528        * Initialise the histogram of wavelengths assigned to the i-th peak.
04529        */
04530 
04531       for (j = 0; j < nlines; j++)
04532         lident[j] = 0;
04533 
04534 
04535       /*
04536        * Count how many times each catalog wavelength was assigned
04537        * to the i-th peak.
04538        */
04539 
04540       for (j = 0; j < nident[i]; j++)
04541         ++lident[ident[i][j]];
04542 
04543 
04544       /*
04545        * What wavelength was most frequently assigned to the i-th peak?
04546        */
04547 
04548       max = 0;
04549       maxpos = 0;
04550       for (j = 0; j < nlines; j++) {
04551         if (max < lident[j]) {
04552           max = lident[j];
04553           maxpos = j;
04554         }
04555       }
04556 
04557 
04558       /*
04559        * Were there other wavelengths assigned with the same frequency?
04560        * This would be the case of an ambiguous identification. It is
04561        * safer to reject this peak...
04562        */
04563 
04564       ambiguous = 0;
04565 
04566       for (k = maxpos + 1; k < nlines; k++) {
04567         if (lident[k] == max) {
04568           ambiguous = 1;
04569           break;
04570         }
04571       }
04572 
04573       if (ambiguous)
04574         continue;
04575 
04576 
04577       /*
04578        * Otherwise, I assign to the i-th peak the wavelength that was
04579        * most often assigned to it.
04580        */
04581 
04582       tmp_xpos[n]   = peak[i];
04583       tmp_lambda[n] = line[maxpos];
04584       tmp_ilambda[n] = maxpos;
04585 
04586       ++n;
04587 
04588     }
04589 
04590   }
04591 
04592 
04593   /*
04594    * Check on identified peaks. Contaminations from other spectra might 
04595    * be present and should be excluded: this type of contamination 
04596    * consists of peaks that have been _correctly_ identified! The non-
04597    * spectral type of light contamination should have been almost all 
04598    * removed already in the previous steps, but it may still be present.
04599    * Here, the self-consistent sequences of identified peaks are
04600    * separated one from the other. At the moment, just the longest of
04601    * such sequences is selected (in other words, spectral multiplexing
04602    * is ignored).
04603    */
04604 
04605   if (n > 1) {
04606     nn = 0;                  /* Number of peaks in the list of sequences */
04607     nseq = 0;                /* Current sequence */
04608     for (k = 0; k < n; k++) {
04609       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04610         flag[k] = 1;
04611         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04612         lambda[nn] = tmp_lambda[k];
04613         ilambda[nn] = tmp_ilambda[k];
04614         ++seq_length[nseq];
04615         ++nn;
04616 
04617         /*
04618          * Now look for all the following peaks that are compatible
04619          * with the expected spectral dispersion, and add them in 
04620          * sequence to xpos. Note that missing peaks are not a problem...
04621          */
04622          
04623         i = k;
04624         while (i < n - 1) {
04625           found = 0;
04626           for (j = i + 1; j < n; j++) {
04627             if (flag[j] == 0) {
04628               disp = (tmp_lambda[j] - tmp_lambda[i])
04629                    / (tmp_xpos[j] - tmp_xpos[i]);
04630               if (disp >= min_disp && disp <= max_disp) {
04631                 flag[j] = 1;
04632                 xpos[nn] = tmp_xpos[j];
04633                 lambda[nn] = tmp_lambda[j];
04634                 ilambda[nn] = tmp_ilambda[j];
04635                 ++seq_length[nseq];
04636                 ++nn;
04637                 i = j;
04638                 found = 1;
04639                 break;
04640               }
04641             }
04642           }
04643           if (!found)
04644             break;
04645         }
04646 
04647         /*
04648          * Current sequence is completed: begin new sequence on the
04649          * excluded peaks, starting the loop on peaks again.
04650          */
04651 
04652         ++nseq;
04653         k = 0;
04654       }
04655     }
04656 
04657 
04658     /*
04659      * Find the longest sequence of self-consistent peaks.
04660      */
04661 
04662     maxpos = max = 0;
04663 
04664     if (mos_multiplex < 0) {
04665       for (i = 0; i < nseq; i++) {
04666         if (seq_length[i] > max) {
04667           max = seq_length[i];
04668           maxpos = i;
04669         }
04670       }
04671     }
04672     else {
04673 
04674       /*
04675        * Now consider the sequence which lays in the specified 
04676        * CCD region (indicated by mos_multiplex): that is, _most_ 
04677        * of its lines (more than half) must be in that region...
04678        */
04679 
04680       nn = 0;
04681       found = 0;
04682 
04683       for (i = 0; i < nseq; i++) {
04684         n = seq_length[i];
04685         if (n > 5) {
04686           cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
04687           int        region;
04688 
04689           for (j = 0; j < n; j++)
04690             cpl_array_set_int(regions, j, 
04691                               ((int)floor(xpos[nn + j])) / mos_region_size);
04692 
04693           region = (int)cpl_array_get_median(regions);
04694           cpl_array_delete(regions);
04695 
04696           if (mos_multiplex == region) {
04697             if (found) {
04698               cpl_msg_debug(cpl_func, "More than one spectrum found in "
04699                             "region %d (only the first one is extracted)", 
04700                             mos_multiplex);
04701               break;
04702             }
04703             found = 1;
04704             max = seq_length[i];
04705             maxpos = i;
04706           }
04707         }
04708         nn += seq_length[i];
04709       }
04710     }
04711 
04712     /*
04713      * Find where this sequence starts in the whole peak position
04714      * storage.
04715      */
04716 
04717     nn = 0;
04718     for (i = 0; i < maxpos; i++)
04719       nn += seq_length[i];
04720 
04721     /*
04722      * Move the longest sequence at the beginning of the returned lists
04723      */
04724 
04725     n = max;
04726     for (i = 0; i < n; i++, nn++) {
04727       xpos[i] = xpos[nn];
04728       lambda[i] = lambda[nn];
04729       ilambda[i] = ilambda[nn];
04730     }
04731 
04732 
04733     /*
04734      * Are some wavelengths missing? Recover them.
04735      */
04736 
04737     for (i = 1; i < n; i++) {
04738       gap = ilambda[i] - ilambda[i-1];
04739       for (j = 1; j < gap; j++) {
04740 
04741         if (j == 1) {
04742 
04743           /*
04744            * Determine the local dispersion from the current pair of peaks
04745            */
04746   
04747           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04748         }
04749 
04750         /*
04751          * With this, find the expected position of the missing
04752          * peak by linear interpolation.
04753          */
04754 
04755         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04756 
04757         /*
04758          * Is there a peak at that position? Here a peak from the
04759          * original list is searched, that is closer than 2 pixels
04760          * to the expected position. If it is found, insert it at
04761          * the current position on the list of identified peaks,
04762          * and leave immediately the loop (taking the new position
04763          * for the following linear interpolation, in case more
04764          * than one peak is missing in the current interval).
04765          * If it is not found, stay in the loop, looking for 
04766          * the following missing peaks in this interval.
04767          */
04768 
04769         found = 0;
04770         for (k = 0; k < npeaks; k++) {
04771           if (fabs(peak[k] - hi_start) < 2) {
04772             for (l = n; l > i; l--) {
04773               xpos[l] = xpos[l-1];
04774               lambda[l] = lambda[l-1];
04775               ilambda[l] = ilambda[l-1];
04776             }
04777             xpos[i] = peak[k];
04778             lambda[i] = line[ilambda[i-1] + j];
04779             ilambda[i] = ilambda[i-1] + j;
04780             ++n;
04781             found = 1;
04782             break;
04783           }
04784         }
04785         if (found)
04786           break;
04787       }
04788     }
04789 
04790 
04791     /*
04792      * Try to extrapolate forward
04793      */
04794 
04795     found = 1;
04796     while (ilambda[n-1] < nlines - 1 && found) {
04797 
04798       /*
04799        * Determine the local dispersion from the last pair of 
04800        * identified peaks
04801        */
04802 
04803       if (n > 1)
04804           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04805       else
04806           disp = 0.0;
04807 
04808       if (disp > max_disp || disp < min_disp)
04809         break;
04810 
04811 
04812       /*
04813        * With this, find the expected position of the missing
04814        * peak by linear interpolation.
04815        */
04816 
04817       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04818 
04819       /*
04820        * Is there a peak at that position? Here a peak from the
04821        * original list is searched, that is closer than 6 pixels
04822        * to the expected position. If it is found, insert it at
04823        * the end of the list of identified peaks. If it is not
04824        * found, leave the loop.
04825        */
04826 
04827       found = 0;
04828       min = fabs(peak[0] - hi_start);
04829       minpos = 0;
04830       for (k = 1; k < npeaks; k++) {
04831         if (min > fabs(peak[k] - hi_start)) {
04832             min = fabs(peak[k] - hi_start);
04833             minpos = k;
04834         }
04835       }
04836       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04837         xpos[n] = peak[minpos];
04838         lambda[n] = line[ilambda[n-1] + 1];
04839         ilambda[n] = ilambda[n-1] + 1;
04840         ++n;
04841         found = 1;
04842       }
04843     }
04844 
04845 
04846     /*
04847      * Try to extrapolate backward
04848      */
04849 
04850     found = 1;
04851     while (ilambda[0] > 0 && found) {
04852 
04853       /*
04854        * Determine the local dispersion from the first pair of
04855        * identified peaks
04856        */
04857 
04858       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04859 
04860       if (disp > max_disp || disp < min_disp)
04861         break;
04862 
04863 
04864       /*
04865        * With this, find the expected position of the missing
04866        * peak by linear interpolation.
04867        */
04868 
04869       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04870 
04871 
04872       /*
04873        * Is there a peak at that position? Here a peak from the
04874        * original list is searched, that is closer than 6 pixels
04875        * to the expected position. If it is found, insert it at
04876        * the beginning of the list of identified peaks. If it is not
04877        * found, leave the loop.
04878        */
04879 
04880       found = 0;
04881       min = fabs(peak[0] - hi_start);
04882       minpos = 0;
04883       for (k = 1; k < npeaks; k++) {
04884         if (min > fabs(peak[k] - hi_start)) {
04885             min = fabs(peak[k] - hi_start);
04886             minpos = k;
04887         }
04888       }
04889       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04890         for (j = n; j > 0; j--) {
04891           xpos[j] = xpos[j-1];
04892           lambda[j] = lambda[j-1];
04893           ilambda[j] = ilambda[j-1];
04894         }
04895         xpos[0] = peak[minpos];
04896         lambda[0] = line[ilambda[0] - 1];
04897         ilambda[0] = ilambda[0] - 1;
04898         ++n;
04899         found = 1;
04900       }
04901     }
04902   }
04903 
04904 
04905   /*
04906    * At this point all peaks are processed. Free memory, and return
04907    * the result.
04908    */
04909 
04910 /************************************************+
04911   for (i = 0; i < npeaks; i++) {
04912     printf("Peak %d:\n   ", i);
04913     for (j = 0; j < nident[i]; j++)
04914       printf("%.2f, ", line[ident[i][j]]);
04915     printf("\n");
04916   }
04917 
04918   printf("\n");
04919 
04920   for (i = 0; i < n; i++)
04921     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04922 +************************************************/
04923   for (i = 0; i < npeaks; i++)
04924     cpl_free(ident[i]);
04925   cpl_free(ident);
04926   cpl_free(nident);
04927   cpl_free(lident);
04928   cpl_free(ilambda);
04929   cpl_free(tmp_xpos);
04930   cpl_free(tmp_lambda);
04931   cpl_free(tmp_ilambda);
04932   cpl_free(peak_lo);
04933   cpl_free(flag);
04934   cpl_free(seq_length);
04935   cpl_free(peak_hi);
04936 
04937   if (n == 0) {
04938     cpl_free(xpos);
04939     cpl_free(lambda);
04940     return NULL;
04941   }
04942 
04943   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04944                                    cpl_vector_wrap(n, lambda));
04945 }
04946 
04947 
04965 /*
04966 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04967                     double refwave, double pixel)
04968 {
04969     double yellow;
04970     double cpixel;
04971     double tolerance = 0.02;
04972     int    max_iter = 20;
04973     int    iter = 0;
04974 
04975     
04976     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04977         return 0.0;
04978     
04979     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04980         return 0.0;
04981 
04982     yellow = (blue + red) / 2;
04983 
04984     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04985 
04986     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04987 
04988         if (cpixel > pixel)
04989             red = yellow;
04990         else
04991             blue = yellow;
04992 
04993         yellow = (blue + red) / 2;
04994         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04995 
04996         iter++;
04997 
04998     }
04999 
05000     return yellow;
05001 
05002 }
05003 */
05004 
05005 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
05006                     double refwave, double pixel)
05007 {
05008     double     yellow;
05009     double     coeff;
05010     cpl_size   zero = 0;
05011 
05012     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
05013         return 0.0;
05014 
05015     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
05016         return 0.0;
05017 
05018     yellow = (blue + red) / 2 - refwave;
05019 
05020     coeff = cpl_polynomial_get_coeff(ids, &zero);
05021     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
05022 
05023     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
05024     if (cpl_error_get_code() != CPL_ERROR_NONE) {
05025         cpl_error_reset();
05026         return 0.0;
05027     }
05028 
05029     cpl_polynomial_set_coeff(ids, &zero, coeff);
05030 
05031     return yellow + refwave;
05032 
05033 }
05034 
05060 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
05061                                  double reject, int minlines, 
05062                                  int *nlines, double *err,
05063                                  cpl_bivector **pixwav_used)
05064 {
05065     const char   *func = "mos_poly_wav2pix";
05066 
05067     cpl_bivector *pixwav2;
05068     cpl_vector   *wavel;
05069     cpl_vector   *pixel;
05070     double       *d_wavel;
05071     double       *d_pixel;
05072     double        pixpos;
05073     int           fitlines;
05074     int           rejection = 0;
05075     int           i, j;
05076 
05077     cpl_polynomial *ids;
05078 
05079 
05080     *nlines = 0;
05081     *err = 0;
05082 
05083     if (pixwav == NULL) {
05084         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05085         return NULL;
05086     }
05087 
05088     fitlines = cpl_bivector_get_size(pixwav);
05089 
05090     if (fitlines < minlines) {
05091         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05092         return NULL;
05093     }
05094 
05095 
05096     /*
05097      * If outliers rejection was requested, allocate a working
05098      * vector (that can be modified as soon as outliers are removed)
05099      */
05100 
05101     if (reject > 0.0)
05102         rejection = 1;
05103 
05104     if (rejection)
05105         pixwav2 = cpl_bivector_duplicate(pixwav);
05106     else
05107         pixwav2 = pixwav;
05108 
05109 
05110     /*
05111      * The single vectors are extracted just because the fitting routine
05112      * requires it
05113      */
05114 
05115     pixel = cpl_bivector_get_x(pixwav2);
05116     wavel = cpl_bivector_get_y(pixwav2);
05117 
05118 
05119     /*
05120      * Get rid of the wrapper, in case of duplication
05121      */
05122 
05123     if (rejection)
05124         cpl_bivector_unwrap_vectors(pixwav2);
05125 
05126 
05127     /*
05128      * Here begins the iterative fit of identified lines
05129      */
05130 
05131     while (fitlines >= minlines) {
05132 
05133         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
05134         *err = sqrt(*err);
05135     
05136         if (ids == NULL) {
05137             cpl_msg_debug(cpl_error_get_where(), "%s", cpl_error_get_message());
05138             cpl_msg_debug(func, "Fitting IDS");
05139             cpl_error_set_where(func);
05140             if (rejection) {
05141                 cpl_vector_delete(wavel);
05142                 cpl_vector_delete(pixel);
05143             }
05144             return NULL;
05145         }
05146 
05147         if (rejection) {
05148             cpl_vector * wavel_used = cpl_vector_duplicate(wavel);
05149             cpl_vector * pixel_used = cpl_vector_duplicate(pixel);
05150 
05151 
05152             /*
05153              * Now work directly with the vector data buffers...
05154              */
05155 
05156             d_pixel = cpl_vector_unwrap(pixel);
05157             d_wavel = cpl_vector_unwrap(wavel);
05158 
05159             for (i = 0, j = 0; i < fitlines; i++) {
05160                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
05161                 if (fabs(pixpos - d_pixel[i]) < reject) {
05162                     d_pixel[j] = d_pixel[i];
05163                     d_wavel[j] = d_wavel[i];
05164                     j++;
05165                 }
05166             }
05167     
05168             if (j == fitlines) {       /* No rejection in last iteration */
05169                 cpl_bivector * pixwav_used_temp = 
05170                         cpl_bivector_wrap_vectors(pixel_used, wavel_used);
05171                 *pixwav_used = cpl_bivector_duplicate(pixwav_used_temp);
05172                 cpl_bivector_unwrap_vectors(pixwav_used_temp);
05173                 cpl_vector_delete(wavel_used);
05174                 cpl_vector_delete(pixel_used);
05175                 cpl_free(d_wavel);
05176                 cpl_free(d_pixel);
05177                 *nlines = fitlines;
05178                 return ids;
05179             }
05180             else {                     /* Some lines were rejected       */
05181                 fitlines = j;
05182                 cpl_polynomial_delete(ids);
05183                 if (fitlines >= minlines) {
05184                     pixel = cpl_vector_wrap(fitlines, d_pixel);
05185                     wavel = cpl_vector_wrap(fitlines, d_wavel);
05186                 }
05187                 else {                 /* Too few lines: failure         */
05188                     cpl_free(d_wavel);
05189                     cpl_free(d_pixel);
05190                     cpl_error_set(func, CPL_ERROR_CONTINUE);
05191                     return NULL;
05192                 }
05193             }
05194             cpl_vector_delete(wavel_used);
05195             cpl_vector_delete(pixel_used);
05196         }
05197         else {
05198             *nlines = fitlines;
05199             *pixwav_used = cpl_bivector_duplicate(pixwav2);
05200             return ids;       /* Exit at first iteration if no rejection */
05201         }
05202     }
05203     
05204     return ids;               /* To avoid compiler warnings */
05205 }
05206 
05207 
05232 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
05233                                  double reject, int minlines, 
05234                                  int *nlines, double *err)
05235 {
05236 
05237     cpl_bivector *wavpix;
05238     cpl_vector   *wavel;
05239     cpl_vector   *pixel;
05240 
05241     cpl_polynomial *dds;
05242 
05243     cpl_bivector *wavepix_used;
05244 
05245 
05246     /*
05247      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
05248      */
05249 
05250     pixel = cpl_bivector_get_x(pixwav);
05251     wavel = cpl_bivector_get_y(pixwav);
05252 
05253     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
05254 
05255     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err,
05256                            &wavepix_used);
05257 
05258     cpl_bivector_unwrap_vectors(wavpix);
05259 
05260     cpl_bivector_delete(wavepix_used);
05261 
05262     return dds;
05263 
05264 }
05265 
05266 
05289 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 
05290                              cpl_vector *lines, cpl_polynomial *ids, 
05291                              double refwave, int sradius)
05292 {
05293     const char   *func = "mos_find_peaks";
05294 
05295     double       *data;
05296     double       *d_pixel;
05297     double       *d_wavel;
05298     float         pos;
05299     int           nlines;
05300     int           pixel;
05301     int           i, j;
05302 
05303 
05304     if (spectrum == NULL || lines == NULL || ids == NULL) {
05305         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05306         return NULL;
05307     }
05308 
05309     nlines = cpl_vector_get_size(lines);
05310 
05311     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
05312         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05313         return NULL;
05314     }
05315 
05316     d_wavel = cpl_malloc(nlines * sizeof(double));
05317     d_pixel = cpl_malloc(nlines * sizeof(double));
05318 
05319     data = cpl_vector_get_data(lines);
05320 
05321     for (i = 0, j = 0; i < nlines; i++) {
05322         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
05323         if (pixel < 0 || pixel - sradius < 0 || pixel + sradius >= length)
05324             continue;
05325         if (peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1) == 0) {
05326             pos += pixel - sradius;
05327             d_pixel[j] = pos;
05328             d_wavel[j] = data[i];
05329             j++;
05330         }
05331     }
05332 
05333     if (j > 0) {
05334         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
05335                                          cpl_vector_wrap(j, d_wavel));
05336     }
05337     else {
05338         cpl_free(d_wavel);
05339         cpl_free(d_pixel);
05340         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
05341         return NULL;
05342     }
05343 }
05344 
05345 
05469 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
05470                                           cpl_vector *lines,
05471                                           double dispersion, float level,
05472                                           int sradius, int order,
05473                                           double reject, double refwave, 
05474                                           double *wavestart, double *waveend,
05475                                           int *nlines, double *error, 
05476                                           cpl_table *idscoeff,
05477                                           cpl_image *calibration,
05478                                           cpl_image *residuals, 
05479                                           cpl_table *restable,
05480                                           cpl_mask *refmask,
05481                                           cpl_table *detected_lines)
05482 {
05483 
05484     const char *func = "mos_wavelength_calibration_raw";
05485 
05486     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
05487                                                  /* Max order is 5 */
05488 
05489     double  tolerance = 20.0;     /* Probably forever...                */
05490     int     step      = 10;       /* Compute restable every "step" rows */
05491 
05492     char            name[MAX_COLNAME];
05493     cpl_image      *resampled;
05494     cpl_bivector   *output;
05495     cpl_bivector   *new_output;
05496     cpl_vector     *peaks;
05497     cpl_vector     *wavel;
05498     cpl_polynomial *ids;
05499     cpl_polynomial *lin;
05500     cpl_matrix     *kernel;
05501     double          ids_err;
05502     double          max_disp, min_disp;
05503     double         *line;
05504     double          firstLambda, lastLambda, lambda;
05505     double          value, wave, pixe;
05506     cpl_binary     *mdata;
05507     const float    *sdata;
05508     float          *rdata;
05509     float          *idata;
05510     float          *ddata;
05511     float           v1, v2, vi;
05512     float           fpixel;
05513     int            *have_it;
05514     int             pixstart, pixend;
05515     int             extrapolation;
05516     int             nref;
05517     int             nl, nx, ny, pixel;
05518     int             countLines, usedLines;
05519     int             uorder;
05520     int             in, first, last;
05521     int             width, uradius;
05522     int             i, j;
05523     int             null;
05524     cpl_size        k;
05525 
05526 
05527     if (dispersion == 0.0) {
05528         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05529         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05530         return NULL;
05531     }
05532 
05533     if (dispersion < 0.0) {
05534         cpl_msg_error(func, "The expected dispersion must be positive");
05535         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05536         return NULL;
05537     }
05538 
05539     max_disp = dispersion + dispersion * tolerance / 100;
05540     min_disp = dispersion - dispersion * tolerance / 100;
05541 
05542     if (order < 1) {
05543         cpl_msg_error(func, "The order of the fitting polynomial "
05544                       "must be at least 1");
05545         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05546         return NULL;
05547     }
05548 
05549     if (image == NULL || lines == NULL) {
05550         cpl_msg_error(func, "Both spectral exposure and reference line "
05551                       "catalog are required in input");
05552         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05553         return NULL;
05554     }
05555 
05556     nx = cpl_image_get_size_x(image);
05557     ny = cpl_image_get_size_y(image);
05558     sdata = cpl_image_get_data_float_const(image);
05559 
05560     nref = cpl_vector_get_size(lines);
05561     line = cpl_vector_get_data(lines);
05562 
05563     if (*wavestart < 1.0 && *waveend < 1.0) {
05564         firstLambda = line[0];
05565         lastLambda = line[nref-1];
05566         extrapolation = (lastLambda - firstLambda) / 10;
05567         firstLambda -= extrapolation;
05568         lastLambda += extrapolation;
05569         *wavestart = firstLambda;
05570         *waveend = lastLambda;
05571     }
05572     else {
05573         firstLambda = *wavestart;
05574         lastLambda = *waveend;
05575     }
05576 
05577     nl = (lastLambda - firstLambda) / dispersion;
05578     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05579     rdata = cpl_image_get_data_float(resampled);
05580 
05581     if (calibration)
05582         idata = cpl_image_get_data_float(calibration);
05583 
05584     if (residuals)
05585         ddata = cpl_image_get_data_float(residuals);
05586 
05587     if (idscoeff)
05588         for (j = 0; j <= order; j++)
05589             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05590 
05591     if (restable) {
05592         cpl_table_set_size(restable, nref);
05593         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05594         cpl_table_copy_data_double(restable, "wavelength", line);
05595         for (i = 0; i < ny; i += step) {
05596              snprintf(name, MAX_COLNAME, "r%d", i);
05597              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05598              snprintf(name, MAX_COLNAME, "d%d", i);
05599              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05600              snprintf(name, MAX_COLNAME, "p%d", i);
05601              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05602         }
05603     }
05604 
05605     if (detected_lines) {
05606         cpl_table_set_size(detected_lines, 0);
05607         cpl_table_new_column(detected_lines, "xpos", CPL_TYPE_DOUBLE);
05608         cpl_table_new_column(detected_lines, "ypos", CPL_TYPE_DOUBLE);
05609         cpl_table_new_column(detected_lines, "xpos_iter", CPL_TYPE_DOUBLE);
05610         cpl_table_new_column(detected_lines, "ypos_iter", CPL_TYPE_DOUBLE);
05611         cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
05612         cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
05613         cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
05614         cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
05615         cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
05616         cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
05617     }
05618 
05619     /*
05620      * Here is the real thing: detecting and identifying peaks,
05621      * and then fit the transformation from wavelength to pixel
05622      * and from pixel to wavelength.
05623      */
05624 
05625     for (i = 0; i < ny; i++) {
05626         width = mos_lines_width(sdata + i*nx, nx);
05627         if (sradius > 0) {
05628             if (width > sradius) {
05629                 uradius = width;
05630             }
05631             else {
05632                 uradius = sradius;
05633             }
05634         }
05635         if (width < 5)
05636             width = 5;
05637         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05638         if (peaks) {
05639             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05640         }
05641         if (peaks) {
05642             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05643             if (output) {
05644                 cpl_bivector * peaks_ident_used_fit;
05645                 countLines = cpl_bivector_get_size(output);
05646                 if (countLines < 4) {
05647                     cpl_bivector_delete(output);
05648                     cpl_vector_delete(peaks);
05649                     if (nlines)
05650                         nlines[i] = 0;
05651                     if (error)
05652                         error[i] = 0.0;
05653                     continue;
05654                 }
05655 
05656                 /*
05657                  * Set reference wavelength as zero point
05658                  */
05659 
05660                 wavel = cpl_bivector_get_y(output);
05661                 cpl_vector_subtract_scalar(wavel, refwave);
05662 
05663                 uorder = countLines / 2 - 1;
05664                 if (uorder > order)
05665                     uorder = order;
05666 
05667 /* This part is now commented out. In case the first-guess iteration
05668  * was requested, the first fit was made with a lower polynomial degree:
05669  * more robust, and still accurate enough to be used as a first-guess.
05670 
05671                 if (sradius > 0 && uorder > 2)
05672                     --uorder;
05673 
05674  * End of commented part */
05675 
05676                 ids = mos_poly_wav2pix(output, uorder, reject,
05677                                        2 * (uorder + 1), &usedLines,
05678                                        &ids_err, &peaks_ident_used_fit);
05679 
05680                 if (ids == NULL) {
05681                     cpl_bivector_delete(output);
05682                     cpl_vector_delete(peaks);
05683                     if (nlines)
05684                         nlines[i] = 0;
05685                     if (error)
05686                         error[i] = 0.0;
05687                     cpl_error_reset();
05688                     continue;
05689                 }
05690 
05691                 if (idscoeff) {
05692 
05693                     /*
05694                      * Write it anyway, even in case a first-guess based
05695                      * solution will be searched afterwards: in case of
05696                      * failure, the "blind" solution is kept.
05697                      */
05698 
05699                     for (k = 0; k <= order; k++) {
05700                         if (k > uorder) {
05701                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05702                         }
05703                         else {
05704                             cpl_table_set_double(idscoeff, clab[k], i,
05705                                       cpl_polynomial_get_coeff(ids, &k));
05706                         }
05707                     }
05708                 }
05709 
05710                 if(detected_lines)
05711                 {
05712                     cpl_size newlines = cpl_vector_get_size(peaks); 
05713                     cpl_size oldsize = cpl_table_get_nrow(detected_lines); 
05714                     cpl_table_set_size(detected_lines, oldsize + newlines);
05715                     for(cpl_size iline = 0; iline < newlines; ++iline)
05716                     {
05717                         cpl_table_set_double(detected_lines, "xpos",
05718                              oldsize + iline, cpl_vector_get(peaks, iline) + 1);
05719                         cpl_table_set_double(detected_lines, "ypos",
05720                              oldsize + iline, (double)i + 1);
05721                         cpl_table_set_double(detected_lines, "peak_flux",
05722                              oldsize + iline, 
05723                              sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
05724                         cpl_table_set_int(detected_lines,
05725                                           "fit_used",
05726                                           oldsize + iline, 0);
05727                     }
05728                 }
05729 
05730                 //Fill the line identification information in 
05731                 //the detected_lines table
05732                 if(detected_lines)
05733                 {
05734                     cpl_size nidentlines = cpl_bivector_get_size(output); 
05735                     cpl_size ndetectlines = cpl_vector_get_size(peaks); 
05736                     cpl_size totalsize = cpl_table_get_nrow(detected_lines);
05737                     for(cpl_size idline = 0; idline < nidentlines; ++idline)
05738                     {
05739                         for(cpl_size detline = 0; detline < ndetectlines; ++detline)
05740                         {
05741                             if(cpl_vector_get(peaks, detline) == 
05742                                cpl_bivector_get_x_data(output)[idline])
05743                             {
05744                                 cpl_size table_pos = totalsize - ndetectlines + detline;
05745                                 double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
05746                                 double xpix_fit = cpl_polynomial_eval_1d(ids,
05747                                         wave_ident - refwave, NULL);
05748                                 double xpos_det = cpl_table_get_double(detected_lines,
05749                                         "xpos",
05750                                         table_pos, &null);
05751                                 cpl_table_set_double(detected_lines,
05752                                                      "wave_ident",
05753                                                      table_pos,
05754                                                      wave_ident);
05755                                 cpl_table_set_double(detected_lines,
05756                                                      "xpos_fit_rect_wavecal",
05757                                                      table_pos,
05758                                                      xpix_fit + 1);
05759                                 cpl_table_set_double(detected_lines,
05760                                                      "res_xpos",
05761                                                      table_pos,
05762                                                      xpos_det - xpix_fit - 1);
05763                                 cpl_table_set_int(detected_lines,
05764                                                   "fit_used",
05765                                                   table_pos, 0);
05766                                 for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
05767                                 {
05768                                     if(cpl_bivector_get_x_data(output)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
05769                                         cpl_table_set_int(detected_lines,
05770                                                          "fit_used",
05771                                                          table_pos, 1);
05772                                 }
05773                            }
05774                         }
05775                     }
05776                 }
05777 
05778                 if (sradius > 0) {
05779                     cpl_bivector * peaks_ident_used_fit;
05780 
05781                     /*
05782                      * Use ids as a first-guess
05783                      */
05784 
05785                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05786                                                 ids, refwave, uradius);
05787 
05788                     if (new_output) {
05789                         cpl_bivector_delete(output);
05790                         output = new_output;
05791                     }
05792                     else
05793                         cpl_error_reset();
05794 
05795 
05796                     cpl_polynomial_delete(ids);
05797 
05798                     countLines = cpl_bivector_get_size(output);
05799 
05800                     if (countLines < 4) {
05801                         cpl_bivector_delete(output);
05802                         cpl_vector_delete(peaks);
05803 
05804                         /* 
05805                          * With the following code a decision is taken:
05806                          * if using the first-guess gives no results,
05807                          * then also the "blind" solution is rejected.
05808                          */
05809 
05810                         if (nlines)
05811                             nlines[i] = 0;
05812                         if (error)
05813                             error[i] = 0.0;
05814                         if (idscoeff)
05815                             for (k = 0; k <= order; k++)
05816                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05817                         continue;
05818                     }
05819 
05820                     wavel = cpl_bivector_get_y(output);
05821                     cpl_vector_subtract_scalar(wavel, refwave);
05822 
05823                     uorder = countLines / 2 - 1;
05824                     if (uorder > order)
05825                         uorder = order;
05826 
05827                     ids = mos_poly_wav2pix(output, uorder, reject,
05828                                            2 * (uorder + 1), &usedLines,
05829                                            &ids_err, &peaks_ident_used_fit);
05830 
05831                     if (ids == NULL) {
05832                         cpl_bivector_delete(output);
05833                         cpl_vector_delete(peaks);
05834 
05835                         /* 
05836                          * With the following code a decision is taken:
05837                          * if using the first-guess gives no results,
05838                          * then also the "blind" solution is rejected.
05839                          */
05840 
05841                         if (nlines)
05842                             nlines[i] = 0;
05843                         if (error)
05844                             error[i] = 0.0;
05845                         if (idscoeff)
05846                             for (k = 0; k <= order; k++)
05847                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05848                         cpl_error_reset();
05849                         continue;
05850                     }
05851 
05852                     if (idscoeff) {
05853                         for (k = 0; k <= order; k++) {
05854                             if (k > uorder) {
05855                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05856                             }
05857                             else {
05858                                 cpl_table_set_double(idscoeff, clab[k], i,
05859                                             cpl_polynomial_get_coeff(ids, &k));
05860                             }
05861                         }
05862                     }
05863                     
05864                     
05865                     if(detected_lines)
05866                     {
05867                         cpl_size oldsize = cpl_table_get_nrow(detected_lines); 
05868                         cpl_size nidentlines = cpl_bivector_get_size(output); 
05869                         cpl_table_set_size(detected_lines, oldsize + nidentlines);
05870                         for(cpl_size idline = 0; idline < nidentlines ; ++idline)
05871                         {
05872                             double wave_ident = cpl_bivector_get_y_data(output)[idline] + refwave;
05873                             double xpix_fit = cpl_polynomial_eval_1d(ids,
05874                                     wave_ident - refwave, NULL);
05875                             cpl_table_set_double(detected_lines, "xpos_iter",
05876                                  oldsize + idline, cpl_bivector_get_x_data(output)[idline] + 1);
05877                             cpl_table_set_double(detected_lines, "ypos_iter",
05878                                  oldsize + idline, (double)i + 1);
05879                             cpl_table_set_double(detected_lines, "peak_flux",
05880                                  oldsize + idline, 
05881                                  sdata[i*nx+(int)(cpl_bivector_get_x_data(output)[idline]+0.5)]);
05882                             cpl_table_set_double(detected_lines, "wave_ident_iter",
05883                                  oldsize + idline, wave_ident);
05884                             cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
05885                                  oldsize + idline, xpix_fit + 1);
05886                         }
05887                     }
05888                     
05889                 } /* End of "use ids as a first-guess" */
05890 
05891                 if (nlines)
05892                     nlines[i] = usedLines;
05893                 if (error)
05894                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05895 
05896                 pixstart = cpl_polynomial_eval_1d(ids, 
05897                     cpl_bivector_get_y_data(output)[0], NULL);
05898                 pixend = cpl_polynomial_eval_1d(ids,
05899                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05900                 extrapolation = (pixend - pixstart) / 5;
05901                 pixstart -= extrapolation;
05902                 pixend += extrapolation;
05903                 if (pixstart < 0)
05904                     pixstart = 0;
05905                 if (pixend > nx)
05906                     pixend = nx;
05907 
05908                 /*
05909                  * Wavelength calibrated image (if requested):
05910                  */
05911 
05912                 if (calibration) {
05913                     for (j = pixstart; j < pixend; j++) {
05914                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05915                                                          lastLambda, refwave, 
05916                                                          j);
05917                     }
05918                 }
05919 
05920                 /*
05921                  * Resampled image:
05922                  */
05923 
05924                 for (j = 0; j < nl; j++) {
05925                     lambda = firstLambda + j * dispersion;
05926                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05927                                                     NULL);
05928                     pixel = fpixel;
05929                     if (pixel >= 0 && pixel < nx-1) {
05930                         v1 = (sdata + i*nx)[pixel];
05931                         v2 = (sdata + i*nx)[pixel+1];
05932                         vi = v1 + (v2-v1)*(fpixel-pixel);
05933                         (rdata + i*nl)[j] = vi;
05934                     }
05935                 }
05936 
05937                 /*
05938                  * Residuals image
05939                  */
05940 
05941                 if (residuals || (restable && !(i%step))) {
05942                     if (restable && !(i%step)) {
05943                         lin = cpl_polynomial_new(1);
05944                         for (k = 0; k < 2; k++)
05945                             cpl_polynomial_set_coeff(lin, &k, 
05946                                           cpl_polynomial_get_coeff(ids, &k));
05947                     }
05948                     for (j = 0; j < countLines; j++) {
05949                         pixe = cpl_bivector_get_x_data(output)[j];
05950                         wave = cpl_bivector_get_y_data(output)[j];
05951                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05952                         if (residuals) {
05953                             pixel = pixe + 0.5;
05954                             (ddata + i*nx)[pixel] = value;
05955                         }
05956                         if (restable && !(i%step)) {
05957                             for (k = 0; k < nref; k++) {
05958                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05959                                     snprintf(name, MAX_COLNAME, "r%d", i);
05960                                     cpl_table_set_double(restable, name, 
05961                                                          k, value);
05962                                     value = pixe
05963                                           - cpl_polynomial_eval_1d(lin, wave,
05964                                                                    NULL);
05965                                     snprintf(name, MAX_COLNAME, "d%d", i);
05966                                     cpl_table_set_double(restable, name, 
05967                                                          k, value);
05968                                     snprintf(name, MAX_COLNAME, "p%d", i);
05969                                     cpl_table_set_double(restable, name,
05970                                                          k, pixe);
05971                                     break;
05972                                 }
05973                             }
05974                         }
05975                     }
05976                     if (restable && !(i%step)) {
05977                         cpl_polynomial_delete(lin);
05978                     }
05979                 }
05980 
05981                 /*
05982                  * Mask at reference wavelength
05983                  */
05984 
05985                 if (refmask) {
05986                     mdata = cpl_mask_get_data(refmask);
05987                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05988                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05989                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05990                         mdata[pixel + i*nx] = CPL_BINARY_1;
05991                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05992                     }
05993                 }
05994 
05995                 cpl_polynomial_delete(ids);
05996                 cpl_bivector_delete(output);
05997             }
05998             cpl_vector_delete(peaks);
05999         }
06000     }
06001 
06002     if (refmask) {
06003         kernel = cpl_matrix_new(3, 3);
06004         cpl_matrix_set(kernel, 0, 1, 1.0);
06005         cpl_matrix_set(kernel, 1, 1, 1.0);
06006         cpl_matrix_set(kernel, 2, 1, 1.0);
06007 
06008         cpl_mask_dilation(refmask, kernel);
06009         cpl_mask_erosion(refmask, kernel);
06010         cpl_mask_erosion(refmask, kernel);
06011         cpl_mask_dilation(refmask, kernel);
06012 
06013         cpl_matrix_delete(kernel);
06014 
06015         /*
06016          *  Fill possible gaps
06017          */
06018 
06019         mdata = cpl_mask_get_data(refmask);
06020         have_it = cpl_calloc(ny, sizeof(int));
06021 
06022         for (i = 0; i < ny; i++, mdata += nx) {
06023             for (j = 0; j < nx; j++) {
06024                 if (mdata[j] == CPL_BINARY_1) {
06025                     have_it[i] = j;
06026                     break;
06027                 }
06028             }
06029         }
06030 
06031         mdata = cpl_mask_get_data(refmask);
06032         in = 0;
06033         first = last = 0;
06034 
06035         for (i = 0; i < ny; i++) {
06036             if (have_it[i]) {
06037                 if (!in) {
06038                     in = 1;
06039                     if (first) {
06040                         last = i;
06041                         if (abs(have_it[first] - have_it[last]) < 3) {
06042                             for (j = first; j < last; j++) {
06043                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
06044                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
06045                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
06046                             }
06047                         }
06048                     }
06049                 }
06050             }
06051             else {
06052                 if (in) {
06053                     in = 0;
06054                     first = i - 1;
06055                 }
06056             }
06057         }
06058 
06059         cpl_free(have_it);
06060 
06061     }
06062 
06063 /*
06064     for (i = 0; i < ny; i++) {
06065         if (nlines[i] == 0) {
06066             for (k = 0; k <= order; k++) {
06067                 cpl_table_set_invalid(idscoeff, clab[k], i);
06068             }
06069         }
06070     }
06071 */
06072 
06073     return resampled;
06074 }
06075 
06076 
06098 /*
06099 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
06100 {
06101     const char *func = "mos_locate_spectra_bis";
06102 
06103     cpl_apertures    *slits;
06104     cpl_image        *labimage;
06105     cpl_image        *refimage;
06106     cpl_binary       *mdata;
06107     cpl_table        *slitpos;
06108     cpl_propertylist *sort_col;
06109     int               nslits;
06110     int              *have_it;
06111     int               in, first, last;
06112     int               i, j;
06113 
06114 
06115     if (mask == NULL) {
06116         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
06117         return NULL;
06118     }
06119 
06120     nx = cpl_mask_get_size_x(mask);
06121     ny = cpl_mask_get_size_y(mask);
06122 
06123     mdata = cpl_mask_get_data(refmask);
06124     have_it = cpl_calloc(ny, sizeof(int));
06125 
06126     for (i = 0; i < ny; i++, mdata += nx) {
06127         for (j = 0; j < nx; j++) {
06128             if (mdata[j] == CPL_BINARY_1) {
06129                 have_it[i] = j + 1;
06130                 break;
06131             }
06132         }
06133     }
06134 
06135     mdata = cpl_mask_get_data(refmask);
06136     in = 0;
06137     first = last = 0;
06138     nslits = 0;
06139 
06140     for (i = 0; i < ny; i++) {
06141         if (have_it[i]) {
06142             if (in) {
06143                 if (i) {
06144                     if (abs(have_it[i] - have_it[i-1]) > 3) {
06145                         nslits++;
06146                     }
06147                 }
06148             }
06149             else {
06150                 in = 1;
06151                 nslits++;
06152             }
06153         }
06154         else {
06155             if (in) {
06156                 in = 0;
06157             }
06158         }
06159     }
06160 }
06161 */
06162 
06163 
06185 cpl_table *mos_locate_spectra(cpl_mask *mask)
06186 {
06187     const char *func = "mos_locate_spectra";
06188 
06189     cpl_apertures    *slits;
06190     cpl_image        *labimage;
06191     cpl_image        *refimage;
06192     cpl_table        *slitpos;
06193     cpl_propertylist *sort_col;
06194     cpl_size          nslits;
06195     int               i;
06196 
06197 
06198     if (mask == NULL) {
06199         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
06200         return NULL;
06201     }
06202 
06203     labimage = cpl_image_labelise_mask_create(mask, &nslits);
06204 
06205     if (nslits < 1) {
06206         cpl_image_delete(labimage);
06207         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06208         return NULL;
06209     }
06210 
06211     refimage = cpl_image_new_from_mask(mask);
06212 
06213     slits = cpl_apertures_new_from_image(refimage, labimage);
06214 
06215     cpl_image_delete(labimage);
06216     cpl_image_delete(refimage);
06217 
06218     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
06219     if (nslits < 1) {
06220         cpl_apertures_delete(slits);
06221         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06222         return NULL;
06223     }
06224 
06225     slitpos = cpl_table_new(nslits);
06226     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
06227     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
06228     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
06229     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
06230     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
06231     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
06232     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
06233     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
06234 
06235     for (i = 0; i < nslits; i++) {
06236         cpl_table_set_double(slitpos, "xtop", i, 
06237                              cpl_apertures_get_top_x(slits, i+1) - 1);
06238         cpl_table_set_double(slitpos, "ytop", i, 
06239                              cpl_apertures_get_top(slits, i+1));
06240         cpl_table_set_double(slitpos, "xbottom", i, 
06241                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
06242         cpl_table_set_double(slitpos, "ybottom", i, 
06243                              cpl_apertures_get_bottom(slits, i+1));
06244     }
06245 
06246     cpl_apertures_delete(slits);
06247 
06248     sort_col = cpl_propertylist_new();
06249     cpl_propertylist_append_bool(sort_col, "ytop", 1);
06250     cpl_table_sort(slitpos, sort_col);
06251     cpl_propertylist_delete(sort_col);
06252 
06253     return slitpos;
06254 
06255 }
06256 
06257 
06273 cpl_error_code mos_validate_slits(cpl_table *slits) 
06274 {
06275     const char *func = "mos_validate_slits";
06276 
06277 
06278     if (slits == NULL)
06279         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
06280 
06281     if (1 != cpl_table_has_column(slits, "xtop"))
06282         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06283 
06284     if (1 != cpl_table_has_column(slits, "ytop"))
06285         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06286 
06287     if (1 != cpl_table_has_column(slits, "xbottom"))
06288         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06289 
06290     if (1 != cpl_table_has_column(slits, "ybottom"))
06291         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06292 
06293     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
06294         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06295 
06296     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
06297         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06298 
06299     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
06300         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06301 
06302     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
06303         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06304 
06305     return CPL_ERROR_NONE;
06306 }
06307 
06308 
06337 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
06338 {
06339     const char *func = "mos_rotate_slits";
06340 
06341     cpl_error_code error;
06342     char aux_name[] = "_0";
06343     int i;
06344 
06345 
06346     rotation %= 4;
06347     if (rotation < 0)
06348         rotation += 4;
06349 
06350     if (rotation == 0)
06351         return CPL_ERROR_NONE;
06352 
06353     error = mos_validate_slits(slits);
06354     if (error)
06355         return cpl_error_set(func, error);
06356 
06357     if (rotation == 1 || rotation == 3) {
06358 
06359         /*
06360          * Swap x and y column names
06361          */
06362 
06363         for (i = 0; i < 77; i++)
06364             if (1 == cpl_table_has_column(slits, aux_name))
06365                 aux_name[1]++;
06366         if (1 == cpl_table_has_column(slits, aux_name))
06367             return cpl_error_set(func, CPL_ERROR_CONTINUE);
06368         cpl_table_name_column(slits, "xtop", aux_name);
06369         cpl_table_name_column(slits, "ytop", "xtop");
06370         cpl_table_name_column(slits, aux_name, "ytop");
06371         cpl_table_name_column(slits, "xbottom", aux_name);
06372         cpl_table_name_column(slits, "ybottom", "xbottom");
06373         cpl_table_name_column(slits, aux_name, "ybottom");
06374     }
06375 
06376     if (rotation == 1 || rotation == 2) {
06377         cpl_table_multiply_scalar(slits, "xtop", -1.0);
06378         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
06379         cpl_table_add_scalar(slits, "xtop", nx);
06380         cpl_table_add_scalar(slits, "xbottom", nx);
06381     }
06382 
06383     if (rotation == 3 || rotation == 2) {
06384         cpl_table_multiply_scalar(slits, "ytop", -1.0);
06385         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
06386         cpl_table_add_scalar(slits, "ytop", ny);
06387         cpl_table_add_scalar(slits, "ybottom", ny);
06388     }
06389 
06390     return CPL_ERROR_NONE;
06391 }
06392 
06393 
06451 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
06452                               cpl_table *global)
06453 {
06454     cpl_array        *top_ident = NULL;;
06455     cpl_array        *bot_ident = NULL;;
06456     cpl_matrix       *mdata;
06457     cpl_matrix       *mpattern;
06458     cpl_matrix       *top_data;
06459     cpl_matrix       *top_pattern;
06460     cpl_matrix       *top_mdata;
06461     cpl_matrix       *top_mpattern;
06462     cpl_matrix       *bot_data;
06463     cpl_matrix       *bot_pattern;
06464     cpl_matrix       *bot_mdata;
06465     cpl_matrix       *bot_mpattern;
06466     cpl_propertylist *sort_col;
06467     double           *xtop;
06468     double           *ytop;
06469     double           *xmtop;
06470     double           *ymtop;
06471     double           *xbot;
06472     double           *ybot;
06473     double           *xmbot;
06474     double           *ymbot;
06475     double            top_scale, bot_scale;
06476     double            angle, top_angle, bot_angle;
06477     double            xmse, ymse;
06478     double            xrms, top_xrms, bot_xrms;
06479     double            yrms, top_yrms, bot_yrms;
06480     int               nslits;
06481     int               nmaskslits, use_pattern;
06482     int               found_slits, found_slits_top, found_slits_bot;
06483     int               i;
06484     cpl_table        *positions;
06485     cpl_error_code    error;
06486 
06487     cpl_vector       *point;
06488     double           *dpoint;
06489     cpl_vector       *xpos;
06490     cpl_vector       *ypos;
06491     cpl_vector       *xmpos;
06492     cpl_vector       *ympos;
06493     cpl_bivector     *mpos;
06494     cpl_polynomial   *xpoly     = NULL;
06495     cpl_polynomial   *ypoly     = NULL;
06496     cpl_polynomial   *top_xpoly = NULL;
06497     cpl_polynomial   *top_ypoly = NULL;
06498     cpl_polynomial   *bot_xpoly = NULL;
06499     cpl_polynomial   *bot_ypoly = NULL;
06500 
06501     char *msg_multiplex = " ";
06502 
06503 
06504     error = mos_validate_slits(slits);
06505     if (error) {
06506         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
06507                       cpl_error_get_message());
06508         cpl_error_set(cpl_func, error);
06509         return NULL;
06510     }
06511 
06512     error = mos_validate_slits(maskslits);
06513     if (error) {
06514         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
06515                       cpl_error_get_message());
06516         cpl_error_set(cpl_func, error);
06517         return NULL;
06518     }
06519 
06520     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06521         cpl_msg_error(cpl_func, "Missing slits identifiers");
06522         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
06523         return NULL;
06524     }
06525 
06526     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06527         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
06528         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
06529         return NULL;
06530     }
06531 
06532     nslits = cpl_table_get_nrow(slits);
06533     nmaskslits = cpl_table_get_nrow(maskslits);
06534 
06535     if (nslits == 0 || nmaskslits == 0) {
06536         cpl_msg_error(cpl_func, "Empty slits table");
06537         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
06538         return NULL;
06539     }
06540 
06541     if (nslits > 100 && mos_multiplex < 0) {
06542         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
06543         positions = mos_identify_slits_fast(slits, maskslits, global);
06544         if (positions == NULL)
06545             cpl_error_set_where(cpl_func);
06546         return positions;
06547     }
06548 
06549     /*
06550      * Guarantee that both input tables are sorted in the same way
06551      */
06552 
06553     sort_col = cpl_propertylist_new();
06554     cpl_propertylist_append_bool(sort_col, "ytop", 1);
06555     cpl_table_sort(slits, sort_col);
06556     cpl_table_sort(maskslits, sort_col);
06557     cpl_propertylist_delete(sort_col);
06558 
06559     /*
06560      * First we handle all the special cases (too few slits...)
06561      */
06562 
06563     if (nslits < 3 && nmaskslits > nslits) {
06564 
06565         /*
06566          * If there are just 1 or 2 slits on the CCD, and more on the
06567          * mask, the ambiguity cannot be solved, and an error is returned.
06568          * This is a case that must be solved with a first-guess relation
06569          * between mask and CCD.
06570          */
06571 
06572         if (nslits > 1)
06573             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
06574                             "with the %d mask slits: process will continue "
06575                             "using the detected CCD slits positions", nslits,
06576                             nmaskslits);
06577         else
06578             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
06579                             "the %d mask slits: process will continue using "
06580                             "the detected CCD slit position", nmaskslits);
06581         return NULL;
06582     }
06583 
06584     if (nmaskslits < 3 && nslits > nmaskslits) {
06585 
06586         /*
06587          * If there are less than 3 slits on the mask the ambiguity cannot
06588          * be solved, and an error is returned. This is a case that must
06589          * be solved with a first-guess relation between mask and CCD.
06590          */
06591 
06592         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
06593                         "the %d mask slits: process will continue using "
06594                         "the detected CCD slits positions", nslits,
06595                         nmaskslits);
06596         return NULL;
06597     }
06598 
06599     /*
06600      * Pattern matching related operations begin here. Two pattern
06601      * matching will be run, one based on the "top" and another one
06602      * based on the "bottom" slit coordinates. The one with the
06603      * smallest rms in the Y coordinate will be chosen.
06604      */
06605 
06606     xtop  = cpl_table_get_data_double(slits, "xtop");
06607     ytop  = cpl_table_get_data_double(slits, "ytop");
06608     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06609     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06610 
06611     xbot  = cpl_table_get_data_double(slits, "xbottom");
06612     ybot  = cpl_table_get_data_double(slits, "ybottom");
06613     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06614     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06615 
06616     top_data    = cpl_matrix_new(2, nslits);
06617     top_pattern = cpl_matrix_new(2, nmaskslits);
06618     bot_data    = cpl_matrix_new(2, nslits);
06619     bot_pattern = cpl_matrix_new(2, nmaskslits);
06620 
06621     for (i = 0; i < nslits; i++)
06622         cpl_matrix_set(top_data, 0, i, xtop[i]);
06623 
06624     for (i = 0; i < nslits; i++)
06625         cpl_matrix_set(top_data, 1, i, ytop[i]);
06626 
06627     for (i = 0; i < nmaskslits; i++)
06628         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06629 
06630     for (i = 0; i < nmaskslits; i++)
06631         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06632 
06633     for (i = 0; i < nslits; i++)
06634         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06635 
06636     for (i = 0; i < nslits; i++)
06637         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06638 
06639     for (i = 0; i < nmaskslits; i++)
06640         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06641 
06642     for (i = 0; i < nmaskslits; i++)
06643         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06644 
06645     if (nmaskslits > nslits)
06646         use_pattern = nslits;
06647     else
06648         use_pattern = nmaskslits;
06649 
06650     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06651                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06652                                      &top_mpattern, &top_scale, &top_angle);
06653 
06654     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06655                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06656                                      &bot_mpattern, &bot_scale, &bot_angle);
06657     cpl_matrix_delete(top_data);
06658     cpl_matrix_delete(top_pattern);
06659     cpl_matrix_delete(bot_data);
06660     cpl_matrix_delete(bot_pattern);
06661 
06662     if (top_ident == NULL && bot_ident == NULL) {
06663         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06664                         "the %d found CCD slits with the %d mask slits: "
06665                         "process will continue using the detected CCD "
06666                         "slits positions", nslits, nmaskslits);
06667         return NULL;
06668     }
06669 
06670     found_slits_top = 0;
06671     found_slits_bot = 0;
06672     if (top_ident && bot_ident) {
06673         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06674                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06675         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06676                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06677         if (fabs(top_angle) < fabs(bot_angle))
06678             angle = fabs(top_angle);
06679         else
06680             angle = fabs(bot_angle);
06681         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06682         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06683     }
06684     else if (top_ident) {
06685         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06686         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06687         angle = fabs(top_angle);
06688         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06689     }
06690     else {
06691         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06692         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06693         angle = fabs(bot_angle);
06694         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06695     }
06696 
06697     cpl_array_delete(top_ident);
06698     cpl_array_delete(bot_ident);
06699 
06700     if (angle > 4.0) {
06701         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06702                         "angle is expected to be around zero. This match is "
06703                         "rejected: the process will continue using the %d "
06704                         "detected CCD slits positions", nslits);
06705         return NULL;
06706     }
06707 
06708     found_slits = found_slits_top;
06709     if (found_slits < found_slits_bot)
06710         found_slits = found_slits_bot;     /* Max value */
06711 
06712     if (found_slits < 4) {
06713         cpl_msg_warning(cpl_func,
06714                         "Too few safely identified slits: %d out of %d "
06715                         "candidates (%d expected). Process will continue "
06716                         "using the detected CCD slits positions", found_slits,
06717                         nslits, nmaskslits);
06718         return NULL;
06719     }
06720 
06721     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06722                  "candidates\n(%d expected)", found_slits, nslits,
06723                  nmaskslits);
06724 
06725     if (found_slits_top < 4)
06726         found_slits_top = 0;
06727 
06728     if (found_slits_bot < 4)
06729         found_slits_bot = 0;
06730 
06731     /*
06732      * Now for each set select the points of the identified slits, and fit
06733      * two bivariate polynomials to determine a first approximate relation
06734      * between positions on the mask and positions on the CCD.
06735      */
06736 
06737     for (i = 0; i < 2; i++) {
06738         cpl_size    mindeg2d[] = {0, 0};
06739         cpl_size    maxdeg2d[2];
06740         cpl_vector  * fitresidual;
06741         if (i) {
06742             found_slits = found_slits_top;
06743             mdata = top_mdata;
06744             mpattern = top_mpattern;
06745         }
06746         else {
06747             found_slits = found_slits_bot;
06748             mdata = bot_mdata;
06749             mpattern = bot_mpattern;
06750         }
06751 
06752         if (found_slits == 0)
06753             continue;
06754         else if (found_slits < 10)
06755             maxdeg2d[0] = maxdeg2d[1] = 1;
06756         else
06757             maxdeg2d[0] = maxdeg2d[1] = 2;
06758 
06759         xpos  = cpl_vector_wrap(found_slits,
06760                                 cpl_matrix_get_data(mdata)              );
06761         ypos  = cpl_vector_wrap(found_slits,
06762                                 cpl_matrix_get_data(mdata) + found_slits);
06763         xmpos = cpl_vector_wrap(found_slits,
06764                                 cpl_matrix_get_data(mpattern)              );
06765         ympos = cpl_vector_wrap(found_slits,
06766                                 cpl_matrix_get_data(mpattern) + found_slits);
06767         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06768         fitresidual = cpl_vector_new(cpl_vector_get_size(xpos));
06769         xpoly = cpl_polynomial_new(2);
06770         cpl_polynomial_fit(xpoly, mpattern, NULL, xpos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
06771         cpl_vector_fill_polynomial_fit_residual(fitresidual, xpos, NULL, xpoly, mpattern, NULL);
06772         xmse = cpl_vector_product(fitresidual, fitresidual)
06773                          / cpl_vector_get_size(fitresidual);
06774         ypoly = cpl_polynomial_new(2);
06775         cpl_polynomial_fit(ypoly, mpattern, NULL, ypos, NULL, CPL_FALSE, mindeg2d, maxdeg2d);
06776         cpl_vector_fill_polynomial_fit_residual(fitresidual, ypos, NULL, ypoly, mpattern, NULL);
06777         ymse = cpl_vector_product(fitresidual, fitresidual)
06778                          / cpl_vector_get_size(fitresidual);
06779 
06780         cpl_bivector_unwrap_vectors(mpos);
06781         cpl_vector_unwrap(xpos);
06782         cpl_vector_unwrap(ypos);
06783         cpl_vector_unwrap(xmpos);
06784         cpl_vector_unwrap(ympos);
06785         cpl_matrix_delete(mdata);
06786         cpl_matrix_delete(mpattern);
06787         cpl_vector_delete(fitresidual);
06788 
06789         if (i) {
06790             top_xpoly = xpoly;
06791             top_ypoly = ypoly;
06792             top_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
06793             top_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
06794         }
06795         else {
06796             bot_xpoly = xpoly;
06797             bot_ypoly = ypoly;
06798             bot_xrms = sqrt(xmse*2*maxdeg2d[0]/(found_slits - 1));
06799             bot_yrms = sqrt(ymse*2*maxdeg2d[0]/(found_slits - 1));
06800         }
06801     }
06802 
06803     if (top_xpoly && bot_xpoly) {
06804         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06805             xrms = top_xrms;
06806             xpoly = top_xpoly;
06807             cpl_polynomial_delete(bot_xpoly);
06808         }
06809         else {                      /* bottom X solution wins... */
06810             xrms = bot_xrms;
06811             xpoly = bot_xpoly;
06812             cpl_polynomial_delete(top_xpoly);
06813         }
06814     }
06815     else if (top_xpoly) {
06816         xrms = top_xrms;
06817         xpoly = top_xpoly;
06818     }
06819     else {
06820         xrms = bot_xrms;
06821         xpoly = bot_xpoly;
06822     }
06823 
06824     if (top_ypoly && bot_ypoly) {
06825         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06826             yrms = top_yrms;
06827             ypoly = top_ypoly;
06828             cpl_polynomial_delete(bot_ypoly);
06829         }
06830         else {                      /* bottom Y solution wins... */
06831             yrms = bot_yrms;
06832             ypoly = bot_ypoly;
06833             cpl_polynomial_delete(top_ypoly);
06834         }
06835     }
06836     else if (top_ypoly) {
06837         yrms = top_yrms;
06838         ypoly = top_ypoly;
06839     }
06840     else {
06841         yrms = bot_yrms;
06842         ypoly = bot_ypoly;
06843     }
06844 
06845     if (xpoly == NULL || ypoly == NULL) {
06846         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06847                         "identified slits positions cannot be improved.");
06848         cpl_polynomial_delete(xpoly);
06849         cpl_polynomial_delete(ypoly);
06850         cpl_error_reset();
06851         return NULL;
06852     }
06853 
06854     cpl_msg_info(cpl_func,
06855                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06856                  xrms, yrms);
06857 
06858     if (global) {
06859         write_global_distortion(global, 0, xpoly);
06860         write_global_distortion(global, 7, ypoly);
06861     }
06862 
06863     /*
06864      * The fit was successful: use the polynomials to obtain a new
06865      * position table with the improved positions of the slits
06866      */
06867 
06868     positions = cpl_table_duplicate(maskslits);
06869     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06870     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06871     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06872     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06873 
06874     point = cpl_vector_new(2);
06875     dpoint = cpl_vector_get_data(point);
06876 
06877     for (i = 0; i < nmaskslits; i++) {
06878         double position_x;
06879         double position_y;
06880 
06881         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06882         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06883         position_x  = cpl_polynomial_eval(xpoly, point);
06884 //        if (mos_multiplex >= 0) {
06885 //            if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
06886 //                cpl_table_unselect_row(positions, i);
06887 //                continue;
06888 //            }
06889 //        }
06890         cpl_table_set_double(positions, "xtop", i, position_x);
06891         position_y  = cpl_polynomial_eval(ypoly, point);
06892         cpl_table_set_double(positions, "ytop", i, position_y);
06893         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06894         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06895         position_x  = cpl_polynomial_eval(xpoly, point);
06896         cpl_table_set_double(positions, "xbottom", i, position_x);
06897         position_y  = cpl_polynomial_eval(ypoly, point);
06898         cpl_table_set_double(positions, "ybottom", i, position_y);
06899     }
06900 
06901 //    if (mos_multiplex >= 0) {
06902 //        cpl_table_not_selected(positions);
06903 //        cpl_table_erase_selected(positions);
06904 //        nmaskslits = cpl_table_get_nrow(positions);
06905 //    }
06906 
06907     cpl_vector_delete(point);
06908     cpl_polynomial_delete(xpoly);
06909     cpl_polynomial_delete(ypoly);
06910 
06911     cpl_table_erase_column(positions, "xmtop");
06912     cpl_table_erase_column(positions, "ymtop");
06913     cpl_table_erase_column(positions, "xmbottom");
06914     cpl_table_erase_column(positions, "ymbottom");
06915 
06916     if (mos_multiplex >= 0) {
06917         msg_multiplex = 
06918         cpl_sprintf("in the CCD section between %d and %d pixel", 
06919                     mos_multiplex * mos_region_size, 
06920                     (mos_multiplex + 1) * mos_region_size);
06921     }
06922 
06923     if (nmaskslits > nslits)
06924         cpl_msg_info(cpl_func,
06925                      "Finally identified slits: %d out of %d expected %s\n"
06926                      "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
06927                      nmaskslits - nslits);
06928     else if (nmaskslits < nslits)
06929         cpl_msg_info(cpl_func,
06930                      "Finally identified slits: %d out of %d expected %s\n"
06931                      "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
06932                      nslits - nmaskslits);
06933     else
06934         cpl_msg_info(cpl_func,
06935                      "Finally identified slits: %d out of %d expected %s",
06936                      nmaskslits, nmaskslits, msg_multiplex);
06937 
06938     if (mos_multiplex >= 0) {
06939         cpl_free(msg_multiplex);
06940     }
06941 
06942     return positions;
06943 
06944 }
06945 
06946 
06947 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06948                                    cpl_table *global)
06949 {
06950     const char *func = "mos_identify_slits_fast";
06951 
06952     cpl_propertylist *sort_col;
06953     cpl_table        *positions;
06954     cpl_vector       *scales;
06955     cpl_vector       *angles;
06956     cpl_vector       *point;
06957     cpl_vector       *xpos;
06958     cpl_vector       *ypos;
06959     cpl_vector       *xmpos;
06960     cpl_vector       *ympos;
06961     cpl_bivector     *mpos;
06962     cpl_polynomial   *xpoly = NULL;
06963     cpl_polynomial   *ypoly = NULL;
06964     cpl_error_code    error;
06965     int nslits;
06966     int nmaskslits;
06967     int found_slits;
06968     int i, j, k;
06969 
06970     double  dist1, dist2, dist3, dist, mindist;
06971     double  scale, minscale, maxscale;
06972     double  angle, minangle, maxangle;
06973     double *dscale;
06974     double *dangle;
06975     double *dpoint;
06976     double *xtop;
06977     double *ytop;
06978     double *xbottom;
06979     double *ybottom;
06980     double *xcenter;
06981     double *ycenter;
06982     double *xpseudo;
06983     double *ypseudo;
06984     int    *slit_id;
06985     double *xmtop;
06986     double *ymtop;
06987     double *xmbottom;
06988     double *ymbottom;
06989     double *xmcenter;
06990     double *ymcenter;
06991     double *xmpseudo;
06992     double *ympseudo;
06993     double  xmse, ymse;
06994     int    *mslit_id;
06995     int    *good;
06996     int     minpos;
06997     int     degree;
06998 
06999     double  sradius = 0.01;   /* Candidate input argument... */
07000     int     in_sradius;
07001 
07002     double pi = 3.14159265358979323846;
07003 
07004 
07005     error = mos_validate_slits(slits);
07006     if (error) {
07007         cpl_msg_error(func, "CCD slits table validation: %s", 
07008                       cpl_error_get_message());
07009         cpl_error_set(func, error);
07010         return NULL;
07011     }
07012 
07013     error = mos_validate_slits(maskslits);
07014     if (error) {
07015         cpl_msg_error(func, "Mask slits table validation: %s", 
07016                       cpl_error_get_message());
07017         cpl_error_set(func, error);
07018         return NULL;
07019     }
07020 
07021     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
07022         cpl_msg_error(func, "Missing slits identifiers");
07023         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
07024         return NULL;
07025     }
07026 
07027     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
07028         cpl_msg_error(func, "Wrong type used for slits identifiers");
07029         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
07030         return NULL;
07031     }
07032 
07033     nslits = cpl_table_get_nrow(slits);
07034     nmaskslits = cpl_table_get_nrow(maskslits);
07035 
07036     if (nslits == 0 || nmaskslits == 0) {
07037         cpl_msg_error(func, "Empty slits table");
07038         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07039         return NULL;
07040     }
07041 
07042 
07043     /*
07044      * Compute middle point coordinates for each slit listed in both
07045      * input tables.
07046      */
07047 
07048     if (cpl_table_has_column(slits, "xcenter"))
07049         cpl_table_erase_column(slits, "xcenter");
07050 
07051     if (cpl_table_has_column(slits, "ycenter"))
07052         cpl_table_erase_column(slits, "ycenter");
07053 
07054     if (cpl_table_has_column(maskslits, "xcenter"))
07055         cpl_table_erase_column(maskslits, "xcenter");
07056 
07057     if (cpl_table_has_column(maskslits, "ycenter"))
07058         cpl_table_erase_column(maskslits, "ycenter");
07059 
07060     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
07061     cpl_table_add_columns(slits, "xcenter", "xbottom");
07062     cpl_table_divide_scalar(slits, "xcenter", 2.0);
07063     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
07064     cpl_table_add_columns(slits, "ycenter", "ybottom");
07065     cpl_table_divide_scalar(slits, "ycenter", 2.0);
07066 
07067     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
07068     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
07069     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
07070     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
07071     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
07072     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
07073 
07074 
07075     /*
07076      * Guarantee that both input tables are sorted in the same way
07077      */
07078 
07079     sort_col = cpl_propertylist_new();
07080     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
07081     cpl_table_sort(slits, sort_col);
07082     cpl_table_sort(maskslits, sort_col);
07083     cpl_propertylist_delete(sort_col);
07084 
07085 
07086     /*
07087      * First we handle all the special cases (too few slits...)
07088      */
07089 
07090     if (nslits < 3 && nmaskslits > nslits) {
07091 
07092         /*
07093          * If there are just 1 or 2 slits on the CCD, and more on the
07094          * mask, the ambiguity cannot be solved, and an error is returned.
07095          * This is a case that must be solved with a first-guess relation
07096          * between mask and CCD.
07097          */
07098 
07099         if (nslits > 1)
07100             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
07101                             "%d mask slits: process will continue using the "
07102                             "detected CCD slit position", nmaskslits);
07103         else
07104             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
07105                             "the %d mask slits: process will continue using "
07106                             "the detected CCD slits positions", nslits, 
07107                             nmaskslits);
07108         return NULL;
07109     }
07110 
07111     if (nslits <= 3 && nslits == nmaskslits) {
07112 
07113         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
07114         cpl_msg_warning(func, "Their detected positions are left unchanged");
07115 
07116         /*
07117          * If there are just up to 3 slits, both on the mask and on the CCD,
07118          * we can reasonably hope that those slits were found, and accept 
07119          * that their positions on the CCD cannot be improved. We prepare
07120          * therefore an output position table containing the slits with
07121          * their original positions. We can however give an estimate of
07122          * the platescale if there is more than one slit.
07123          */
07124 
07125         positions = cpl_table_duplicate(slits);
07126         cpl_table_erase_column(slits, "xcenter");
07127         cpl_table_erase_column(slits, "ycenter");
07128         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
07129         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
07130         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
07131         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
07132         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
07133         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
07134         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
07135         cpl_table_erase_column(maskslits, "xcenter");
07136         cpl_table_erase_column(maskslits, "ycenter");
07137 
07138         if (nslits > 1) {
07139             xcenter = cpl_table_get_data_double(positions, "xcenter");
07140             ycenter = cpl_table_get_data_double(positions, "ycenter");
07141             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
07142             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
07143 
07144             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
07145                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
07146             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
07147                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
07148             scale = sqrt(dist1/dist2);
07149 
07150             if (nslits == 3) {
07151                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
07152                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
07153                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
07154                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
07155                 scale += sqrt(dist1/dist2);
07156                 scale /= 2;
07157             }
07158 
07159             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
07160         }
07161 
07162         return positions;
07163     }
07164 
07165     if (nmaskslits < 3 && nslits > nmaskslits) {
07166 
07167         /*
07168          * If there are less than 3 slits on the mask the ambiguity cannot 
07169          * be solved, and an error is returned. This is a case that must 
07170          * be solved with a first-guess relation between mask and CCD.
07171          */
07172 
07173         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
07174                         "the %d mask slits: process will continue using "
07175                         "the detected CCD slits positions", nslits, 
07176                         nmaskslits);
07177         return NULL;
07178     }
07179 
07180 
07181     /*
07182      * At this point of the program all the region of the plane
07183      * (nslits, nmaskslits) where either or both mask and CCD display
07184      * less than 3 slits are handled in some way. It would be better
07185      * to add in this place a special handling for identifying slits
07186      * in case of a very reduced number of slits (say, below 6).
07187      * It is also clear that if there are many more slits on the
07188      * mask than on the CCD, or many more on the CCD than on the
07189      * mask, something went deeply wrong with the preliminary 
07190      * wavelength calibration. Such cases should be handled with
07191      * a _complete_ pattern-recognition algorithm based on the
07192      * construction of all possible triangles. For the moment, 
07193      * we go directly to the limited pattern-recognition applied
07194      * below, based on triangles build only for consecutive slits.
07195      * This is reasonably safe, since the preliminary wavelength
07196      * calibration performed by mos_identify_peaks() is generally
07197      * robust.
07198      */
07199 
07200 
07201     /*
07202      * Compute (X, Y) coordinates on pseudo-plane describing the
07203      * different position ratios of successive slits, in both
07204      * input tables.
07205      */
07206 
07207     if (cpl_table_has_column(slits, "xpseudo"))
07208         cpl_table_erase_column(slits, "xpseudo");
07209 
07210     if (cpl_table_has_column(slits, "ypseudo"))
07211         cpl_table_erase_column(slits, "ypseudo");
07212 
07213     if (cpl_table_has_column(maskslits, "xpseudo"))
07214         cpl_table_erase_column(maskslits, "xpseudo");
07215 
07216     if (cpl_table_has_column(maskslits, "ypseudo"))
07217         cpl_table_erase_column(maskslits, "ypseudo");
07218 
07219     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
07220     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
07221 
07222     xcenter = cpl_table_get_data_double(slits, "xcenter");
07223     ycenter = cpl_table_get_data_double(slits, "ycenter");
07224     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
07225     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
07226 
07227     for (i = 1; i < nslits - 1; i++) {
07228         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
07229               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
07230         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
07231               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
07232         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
07233               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
07234         xpseudo[i] = sqrt(dist1/dist2);
07235         ypseudo[i] = sqrt(dist3/dist2);
07236     }
07237 
07238     cpl_table_set_invalid(slits, "xpseudo", 0);
07239     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
07240     cpl_table_set_invalid(slits, "ypseudo", 0);
07241     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
07242 
07243     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
07244     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
07245 
07246     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
07247     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
07248     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
07249     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
07250 
07251     for (i = 1; i < nmaskslits - 1; i++) {
07252         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
07253               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
07254         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
07255               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
07256         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
07257               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
07258         xmpseudo[i] = sqrt(dist1/dist2);
07259         ympseudo[i] = sqrt(dist3/dist2);
07260     }
07261     
07262     cpl_table_set_invalid(maskslits, "xpseudo", 0);
07263     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
07264     cpl_table_set_invalid(maskslits, "ypseudo", 0);
07265     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
07266 
07267 
07268     /*
07269      * For each (X, Y) on the pseudo-plane related to the mask positions,
07270      * find the closest (X, Y) on the pseudo-plane related to the CCD
07271      * positions. If the closest point is closer than a given search
07272      * radius, a triangle has been found, and 3 slits are identified.
07273      * However, if more than one point is found within the search
07274      * radius, we have an ambiguity and this is rejected.
07275      */
07276 
07277     if (cpl_table_has_column(slits, "slit_id"))
07278         cpl_table_erase_column(slits, "slit_id");
07279     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07280     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
07281 
07282     for (i = 1; i < nmaskslits - 1; i++) {
07283         in_sradius = 0;
07284         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
07285                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
07286         minpos = 1;
07287         if (mindist < sradius*sradius)
07288             in_sradius++;
07289         for (j = 2; j < nslits - 1; j++) {
07290             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
07291                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
07292             if (dist < sradius*sradius)
07293                 in_sradius++;
07294             if (in_sradius > 1)    /* More than one triangle within radius */
07295                 break;
07296             if (mindist > dist) {
07297                 mindist = dist;
07298                 minpos = j;
07299             }
07300         }
07301 
07302         mindist = sqrt(mindist);
07303 
07304         if (mindist < sradius && in_sradius == 1) {
07305             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
07306             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
07307             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
07308         }
07309     }
07310 
07311 
07312     /*
07313      * At this point, the slit_id column contains invalid elements 
07314      * corresponding to unidentified slits.
07315      */
07316 
07317     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
07318 
07319     if (found_slits < 3) {
07320         cpl_msg_warning(func, "Too few preliminarily identified slits: "
07321                         "%d out of %d", found_slits, nslits);
07322         if (nslits == nmaskslits) {
07323             cpl_msg_warning(func, "(this is not an error, it could be caused "
07324                             "by a mask with regularly located slits)");
07325             cpl_msg_warning(func, "The detected slits positions are left "
07326                             "unchanged");
07327 
07328             /*
07329              * If there are less than 3 identified slits, this is probably 
07330              * a mask with regularly spaced slits (leading to an ambiguous
07331              * pattern). Only in the case all expected slits appear to have 
07332              * been found on the CCD we can proceed...
07333              */
07334 
07335             cpl_table_erase_column(slits, "slit_id");
07336             cpl_table_erase_column(slits, "xpseudo");
07337             cpl_table_erase_column(slits, "ypseudo");
07338             positions = cpl_table_duplicate(slits);
07339             cpl_table_erase_column(slits, "xcenter");
07340             cpl_table_erase_column(slits, "ycenter");
07341 
07342             cpl_table_erase_column(maskslits, "xpseudo");
07343             cpl_table_erase_column(maskslits, "ypseudo");
07344             cpl_table_duplicate_column(positions, "xmtop", 
07345                                        maskslits, "xtop");
07346             cpl_table_duplicate_column(positions, "ymtop", 
07347                                        maskslits, "ytop");
07348             cpl_table_duplicate_column(positions, "xmbottom", 
07349                                        maskslits, "xbottom");
07350             cpl_table_duplicate_column(positions, "ymbottom", 
07351                                        maskslits, "ybottom");
07352             cpl_table_duplicate_column(positions, "xmcenter", 
07353                                        maskslits, "xcenter");
07354             cpl_table_duplicate_column(positions, "ymcenter", 
07355                                        maskslits, "ycenter");
07356             cpl_table_duplicate_column(positions, "slit_id", 
07357                                        maskslits, "slit_id");
07358             cpl_table_erase_column(maskslits, "xcenter");
07359             cpl_table_erase_column(maskslits, "ycenter");
07360             return positions;
07361         }
07362         else {
07363             cpl_table_erase_column(slits, "slit_id");
07364             cpl_table_erase_column(slits, "xpseudo");
07365             cpl_table_erase_column(slits, "ypseudo");
07366             positions = cpl_table_duplicate(slits);
07367             cpl_table_erase_column(slits, "xcenter");
07368             cpl_table_erase_column(slits, "ycenter");
07369             cpl_msg_warning(func, "(the failure could be caused "
07370                             "by a mask with regularly located slits)");
07371             return NULL;
07372         }
07373     }
07374     else {
07375         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
07376                      "candidates (%d expected)", found_slits, nslits, 
07377                      nmaskslits);
07378     }
07379 
07380 
07381     /*
07382      * Create a table with the coordinates of the preliminarily identified 
07383      * slits, both on CCD and mask. The original order of the slits positions 
07384      * is preserved.
07385      */
07386 
07387     positions = cpl_table_new(found_slits);
07388     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
07389     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
07390     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
07391     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
07392     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
07393     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
07394     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
07395     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
07396     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
07397     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
07398     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
07399     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
07400     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
07401     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
07402     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
07403 
07404     slit_id = cpl_table_get_data_int   (slits, "slit_id");
07405     xtop    = cpl_table_get_data_double(slits, "xtop");
07406     ytop    = cpl_table_get_data_double(slits, "ytop");
07407     xbottom = cpl_table_get_data_double(slits, "xbottom");
07408     ybottom = cpl_table_get_data_double(slits, "ybottom");
07409     xcenter = cpl_table_get_data_double(slits, "xcenter");
07410     ycenter = cpl_table_get_data_double(slits, "ycenter");
07411 
07412     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
07413     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
07414     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
07415     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
07416     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
07417     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
07418     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
07419 
07420 
07421     /*
07422      * Transferring the valid slits information to the new table.
07423      * Note that invalid elements are coded as 0 in the internal
07424      * buffer, and this is the way they are recognised and excluded.
07425      */
07426 
07427     k = 0;
07428     cpl_table_fill_invalid_int(slits, "slit_id", 0);
07429     for (i = 0; i < nmaskslits; i++) {
07430         for (j = 0; j < nslits; j++) {
07431             if (slit_id[j] == 0)
07432                 continue; /* Skip invalid slit */
07433             if (mslit_id[i] == slit_id[j]) {
07434                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
07435 
07436                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
07437                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
07438                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
07439                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
07440                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
07441                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
07442 
07443                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
07444                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
07445                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
07446                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
07447                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
07448                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
07449 
07450                 k++;
07451 
07452                 break;
07453             }
07454         }
07455     }
07456 
07457     found_slits = k;
07458 
07459     cpl_table_erase_column(slits, "slit_id");
07460     cpl_table_erase_column(slits, "xpseudo");
07461     cpl_table_erase_column(slits, "ypseudo");
07462     cpl_table_erase_column(slits, "xcenter");
07463     cpl_table_erase_column(slits, "ycenter");
07464     cpl_table_erase_column(maskslits, "xpseudo");
07465     cpl_table_erase_column(maskslits, "ypseudo");
07466     cpl_table_erase_column(maskslits, "xcenter");
07467     cpl_table_erase_column(maskslits, "ycenter");
07468 
07469 
07470     /*
07471      * Find the median platescale and rotation angle from the identified 
07472      * slits, and then exclude slits outlaying more than 10% from the 
07473      * median platescale, and more than 2 degrees from the median
07474      * rotation angle.
07475      */
07476 
07477     ytop    = cpl_table_get_data_double(positions, "ytop");
07478     ybottom = cpl_table_get_data_double(positions, "ybottom");
07479     xcenter = cpl_table_get_data_double(positions, "xcenter");
07480     ycenter = cpl_table_get_data_double(positions, "ycenter");
07481     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
07482     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
07483 
07484     scales = cpl_vector_new(found_slits - 1);
07485     dscale = cpl_vector_get_data(scales);
07486     angles = cpl_vector_new(found_slits - 1);
07487     dangle = cpl_vector_get_data(angles);
07488 
07489     for (i = 1; i < found_slits; i++) {
07490         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
07491               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
07492         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
07493               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
07494         dscale[i-1] = sqrt(dist1/dist2);
07495         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
07496                             xcenter[i-1] - xcenter[i])
07497                     - atan2(ymcenter[i-1] - ymcenter[i], 
07498                             xmcenter[i-1] - xmcenter[i]);
07499         dangle[i-1] *= 180;
07500         dangle[i-1] /= pi;
07501     }
07502 
07503     minscale = cpl_vector_get_min(scales);
07504     scale = cpl_vector_get_median_const(scales);
07505     maxscale = cpl_vector_get_max(scales);
07506 
07507     minangle = cpl_vector_get_min(angles);
07508     angle = cpl_vector_get_median_const(angles);
07509     maxangle = cpl_vector_get_max(angles);
07510 
07511     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
07512     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
07513                  minscale, maxscale);
07514 
07515     cpl_msg_info(func, "Median rotation: %f degrees", angle);
07516     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
07517                  minangle, maxangle);
07518 
07519     good = cpl_table_get_data_int(positions, "good");
07520 
07521     good[0] = good[found_slits - 1] = 1;
07522     for (i = 1; i < found_slits; i++) {
07523         if (fabs((dscale[i-1] - scale)/scale) < 0.10
07524          && fabs(dangle[i-1] - angle) < 2) {
07525             good[i-1]++;
07526             good[i]++;
07527         }
07528     }
07529 
07530     for (i = 0; i < found_slits; i++) {
07531         if (good[i] < 2)
07532             good[i] = 0;
07533         else
07534             good[i] = 1;
07535     }
07536 
07537 /*
07538     for (i = 1; i < found_slits; i++)
07539         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
07540             good[i-1] = good[i] = 1;
07541 */
07542 
07543 /* DEBUG ************+
07544     for (i = 0; i < found_slits; i++) {
07545         if (good[i]) {
07546             if (i == found_slits - 1)
07547                 printf("include slit %d, prev = %f, %f\n", 
07548                        i, dscale[i-1], dangle[i-1]);
07549             else if (i == 0)
07550                 printf("include slit %d, next %f, %f\n", 
07551                        i, dscale[i], dangle[i]);
07552             else
07553                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
07554                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07555         }
07556         else {
07557             if (i == found_slits - 1)
07558                 printf("EXclude slit %d, prev = %f, %f\n", 
07559                        i, dscale[i-1], dangle[i-1]);
07560             else if (i == 0)
07561                 printf("EXclude slit %d, next %f, %f\n", 
07562                        i, dscale[i], dangle[i]);
07563             else
07564                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
07565                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07566         }
07567     }
07568 +*********** DEBUG */
07569 
07570     cpl_vector_delete(scales);
07571     cpl_vector_delete(angles);
07572 
07573     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
07574     cpl_table_erase_selected(positions);
07575     cpl_table_erase_column(positions, "good");
07576     found_slits = cpl_table_get_nrow(positions);
07577 
07578     if (found_slits < 4) {
07579 
07580         /*
07581          * If the self-consistency check gives such a poor result,
07582          * something must have gone really wrong in the preliminary
07583          * wavelength calibration... Nothing can be done.
07584          */
07585 
07586         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
07587                         "candidates (%d expected). Process will continue "
07588                         "using the detected CCD slits positions", found_slits, 
07589                         nslits, nmaskslits);
07590         cpl_table_delete(positions);
07591         return NULL;
07592     }
07593     else {
07594         cpl_msg_info(func, "Safely identified slits: %d out of %d "
07595                      "candidates\n(%d expected)", found_slits, nslits,
07596                      nmaskslits);
07597     }
07598 
07599 
07600     /*
07601      * Now select the central points of the identified slits, and
07602      * fit two bivariate polynomials to determine a first approximate
07603      * relation between positions on the mask and positions on the CCD.
07604      */
07605 
07606     xpos = cpl_vector_wrap(found_slits, 
07607                            cpl_table_get_data_double(positions, "xcenter"));
07608     ypos = cpl_vector_wrap(found_slits, 
07609                            cpl_table_get_data_double(positions, "ycenter"));
07610     xmpos = cpl_vector_wrap(found_slits, 
07611                             cpl_table_get_data_double(positions, "xmcenter"));
07612     ympos = cpl_vector_wrap(found_slits, 
07613                             cpl_table_get_data_double(positions, "ymcenter"));
07614     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
07615 
07616     if (found_slits < 10)
07617         degree = 1;
07618     else
07619         degree = 2;
07620 
07621     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
07622     if (xpoly != NULL)
07623         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
07624     cpl_bivector_unwrap_vectors(mpos);
07625     cpl_vector_unwrap(xpos);
07626     cpl_vector_unwrap(ypos);
07627     cpl_vector_unwrap(xmpos);
07628     cpl_vector_unwrap(ympos);
07629     if (ypoly == NULL) {
07630         if (found_slits == nmaskslits) {
07631             cpl_msg_warning(func, "Fit failure: the accuracy of the "
07632                             "identified slits positions is not improved.");
07633 
07634             /*
07635              * The determination of the transformation from mask to CCD
07636              * failed, but since all slits have been already identified
07637              * this is not a problem: data can be reduced also without
07638              * such transformation. Simply the accuracy of the slits 
07639              * positions cannot be improved.
07640              */ 
07641 
07642         } else {
07643             cpl_msg_info(func, "Fit failure: not all slits have been "
07644                          "identified. Process will continue using "
07645                          "the detected CCD slits positions");
07646         }
07647 
07648         cpl_polynomial_delete(xpoly);
07649         return positions;
07650     }
07651 
07652     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07653                  sqrt(xmse), sqrt(ymse));
07654 
07655     if (global) {
07656         write_global_distortion(global, 0, xpoly);
07657         write_global_distortion(global, 7, ypoly);
07658     }
07659 
07660     /*
07661      * The fit was successful: use the polynomials to obtain a new 
07662      * position table with the improved positions of the slits
07663      */
07664 
07665     cpl_table_delete(positions);
07666 
07667     positions = cpl_table_duplicate(maskslits);
07668     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07669     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07670     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07671     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07672 
07673     point = cpl_vector_new(2);
07674     dpoint = cpl_vector_get_data(point);
07675 
07676     for (i = 0; i < nmaskslits; i++) {
07677         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07678         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07679         cpl_table_set_double(positions, "xtop", i, 
07680                              cpl_polynomial_eval(xpoly, point));
07681         cpl_table_set_double(positions, "ytop", i, 
07682                              cpl_polynomial_eval(ypoly, point));
07683         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07684         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07685         cpl_table_set_double(positions, "xbottom", i, 
07686                              cpl_polynomial_eval(xpoly, point));
07687         cpl_table_set_double(positions, "ybottom", i, 
07688                              cpl_polynomial_eval(ypoly, point));
07689     }
07690 
07691     cpl_vector_delete(point);
07692     cpl_polynomial_delete(xpoly);
07693     cpl_polynomial_delete(ypoly);
07694 
07695     cpl_table_erase_column(positions, "xmtop");
07696     cpl_table_erase_column(positions, "ymtop");
07697     cpl_table_erase_column(positions, "xmbottom");
07698     cpl_table_erase_column(positions, "ymbottom");
07699 
07700     if (nmaskslits > nslits)
07701         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07702                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07703     else if (nmaskslits < nslits)
07704         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07705                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07706     else
07707         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07708                  nmaskslits, nmaskslits);
07709 
07710     return positions;
07711 }
07712 
07713 
07755 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07756                           double blue, double red, double dispersion)
07757 {
07758 
07759     const char  *func = "mos_trace_flat";
07760 
07761     cpl_image   *gradient;
07762     cpl_image   *sgradient;
07763     float       *dgradient;
07764     float        level = 500;   /* It was 250... */
07765     cpl_vector  *row;
07766     cpl_vector  *srow;
07767     cpl_vector **peaks;
07768     double      *peak;
07769     int         *slit_id;
07770     float       *g;
07771     double      *r;
07772     double      *xtop;
07773     double      *ytop;
07774     double      *xbottom;
07775     double      *ybottom;
07776     double       min, dist;
07777     double       sradius;
07778     double       tolerance;
07779     double       start_y, prev_y;
07780     int          minpos;
07781     int          nslits;
07782     int          nrows;
07783     int          step = 10;
07784     int          filtbox = 15;
07785     int          nx, ny, npix;
07786     int          pos, ypos;
07787     int          npeaks;
07788     int          pixel_above, pixel_below;
07789     int          i, j, k, l;
07790     char         trace_id[MAX_COLNAME];
07791 
07792     cpl_table   *traces;
07793 
07794 
07795     if (flat == NULL || slits == NULL) {
07796         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07797         return NULL;
07798     }
07799 
07800     if (dispersion <= 0.0) {
07801         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07802         return NULL;
07803     }
07804 
07805     if (red - blue < dispersion) {
07806         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07807         return NULL;
07808     }
07809 
07810     /*
07811      * Create a dummy slit_id column if it is missing in the
07812      * input slits table
07813      */
07814 
07815     nslits  = cpl_table_get_nrow(slits);
07816     if (1 != cpl_table_has_column(slits, "slit_id")) {
07817         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07818         for (i = 0; i < nslits; i++)
07819             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07820     }
07821 
07822     slit_id = cpl_table_get_data_int(slits, "slit_id");
07823 
07824     nx = cpl_image_get_size_x(flat);
07825     ny = cpl_image_get_size_y(flat);
07826     npix = nx * ny;
07827 
07828     gradient = cpl_image_duplicate(flat);
07829     dgradient = cpl_image_get_data_float(gradient);
07830 
07831     for (i = 0; i < ny - 1; i++) {
07832         k = i * nx;
07833         for (j = 0; j < nx; j++) {
07834             l = k + j;
07835             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07836         }
07837     }
07838 
07839     npix--;
07840     for (j = 0; j < nx; j++)
07841         dgradient[npix - j] = 0.0;
07842 
07843     cpl_image_turn(gradient, -1);
07844     nx = cpl_image_get_size_x(gradient);
07845     ny = cpl_image_get_size_y(gradient);
07846     sgradient = mos_image_vertical_median_filter(gradient, 
07847                                                  filtbox, 0, ny, 0, step);
07848     cpl_image_delete(gradient);
07849 
07850 
07851     /*
07852      * Remove background from processed image rows
07853      */
07854 
07855     dgradient = cpl_image_get_data_float(sgradient);
07856 
07857     for (i = 1; i <= ny; i += step) {
07858         row = cpl_vector_new_from_image_row(sgradient, i);
07859         srow = cpl_vector_filter_median_create(row, filtbox);
07860         cpl_vector_subtract(row, srow);
07861         cpl_vector_delete(srow);
07862         g = dgradient + (i-1)*nx;
07863         r = cpl_vector_get_data(row);
07864         for (j = 0; j < nx; j++)
07865             g[j] = r[j];
07866         cpl_vector_delete(row);
07867     }
07868 
07869 
07870     /*
07871      * Rotate (temporarily) the input slits table, to get coordinates
07872      * compatible with the rotated gradient image.
07873      */
07874 
07875     mos_rotate_slits(slits, 1, nx, ny);
07876     xtop    = cpl_table_get_data_double(slits, "xtop");
07877     ytop    = cpl_table_get_data_double(slits, "ytop");
07878     xbottom = cpl_table_get_data_double(slits, "xbottom");
07879     ybottom = cpl_table_get_data_double(slits, "ybottom");
07880 
07881 
07882     /*
07883      * Get positions of peaks candidates for each processed gradient
07884      * image row
07885      */
07886 
07887     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07888 
07889     for (i = 0; i < ny; i += step) {
07890         g = dgradient + i*nx;
07891         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07892 
07893         /* I thought this would be required, but apparently I was wrong.
07894          * Check twice... */
07895         if (peaks[i])
07896             cpl_vector_subtract_scalar(peaks[i], 0.5);
07897          
07898     }
07899 
07900     cpl_image_delete(sgradient);
07901 
07902 
07903     /*
07904      * Tracing the flat field spectra edges, starting from the
07905      * slits positions obtained at reference wavelength. The 
07906      * gradient maximum closest to each slits ends is found and
07907      * accepted only within a given search radius:
07908      */
07909 
07910     sradius = 5.0;  /* Pixel */
07911 
07912     /*
07913      * The tracing proceeds along the processed gradient image rows,
07914      * above and below the start position, finding the closest peak
07915      * to the previous obtained position, accepting the new position
07916      * only if it is closer than a given tolerance:
07917      */
07918 
07919     tolerance = 0.9;  /* Pixel */
07920 
07921     /*
07922      * The trace is attempted for a certain number of pixels above
07923      * and below the position of the reference wavelength:
07924      */
07925 
07926     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
07927     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
07928 
07929 
07930     /*
07931      * Prepare the structure of the output table:
07932      */
07933 
07934     nrows = (ny-1)/step + 1;
07935     traces = cpl_table_new(nrows);
07936     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07937     cpl_table_set_column_unit(traces, "x", "pixel");
07938     for (i = 0, j = 0; i < ny; i += step, j++)
07939         cpl_table_set(traces, "x", j, i);
07940 
07941     for (i = 0; i < nslits; i++) {
07942 
07943         /*
07944          * Find the closest processed gradient image row
07945          */
07946 
07947         ypos = ytop[i];
07948 
07949         if (ypos < 0)
07950             ypos = 0;
07951         if (ypos >= ny)
07952             ypos = ny - 1;
07953 
07954         pos = ypos / step;
07955         pos *= step;
07956 
07957         /*
07958          * Find the peak in that row that is closest to xtop[i]
07959          */
07960 
07961         if (peaks[pos]) {
07962             peak = cpl_vector_get_data(peaks[pos]);
07963             npeaks = cpl_vector_get_size(peaks[pos]);
07964 
07965             min = fabs(peak[0] - xtop[i]);
07966             minpos = 0;
07967             for (j = 1; j < npeaks; j++) {
07968                 dist = fabs(peak[j] - xtop[i]);
07969                 if (min > dist) {
07970                     min = dist;
07971                     minpos = j;
07972                 }
07973             }
07974         }
07975         else {
07976             npeaks = 0;
07977         }
07978 
07979         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07980         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07981 
07982         if (min > sradius || npeaks == 0) {
07983             cpl_msg_warning(func, "Cannot find spectrum edge for "
07984                             "top (or left) end of slit %d", slit_id[i]);
07985         }
07986         else {
07987 
07988             /*
07989              * Add to output table the start y position. Note that
07990              * y positions are written in coordinates of the unrotated
07991              * image, i.e., with horizontal dispersion. Currently nx
07992              * is the x size of the temporarily rotated image (for
07993              * faster memory access), but it has the sense of a y size.
07994              */
07995 
07996             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07997             start_y = peak[minpos];
07998 
07999             /*
08000              * Perform the tracing of current edge. Above:
08001              */
08002 
08003             prev_y = start_y;
08004 
08005             for (j = pos + step; j < ny; j += step) {
08006                 if (j - pos > pixel_above)
08007                     break;
08008                 if (peaks[j]) {
08009                     peak = cpl_vector_get_data(peaks[j]);
08010                     npeaks = cpl_vector_get_size(peaks[j]);
08011                     min = fabs(peak[0] - prev_y);
08012                     minpos = 0;
08013                     for (k = 1; k < npeaks; k++) {
08014                         dist = fabs(peak[k] - prev_y);
08015                         if (min > dist) {
08016                             min = dist;
08017                             minpos = k;
08018                         }
08019                     }
08020                     if (min < tolerance) {
08021                         cpl_table_set(traces, trace_id, j/step, 
08022                                       nx - peak[minpos]);
08023                         prev_y = peak[minpos];
08024                     }
08025                 }
08026             }
08027 
08028             /*
08029              * Perform the tracing of current edge. Below:
08030              */
08031 
08032             prev_y = start_y;
08033 
08034             for (j = pos - step; j >= 0; j -= step) {
08035                 if (pos - j > pixel_below)
08036                     break;
08037                 if (peaks[j]) {
08038                     peak = cpl_vector_get_data(peaks[j]);
08039                     npeaks = cpl_vector_get_size(peaks[j]);
08040                     min = fabs(peak[0] - prev_y);
08041                     minpos = 0;
08042                     for (k = 1; k < npeaks; k++) {
08043                         dist = fabs(peak[k] - prev_y);
08044                         if (min > dist) {
08045                             min = dist;
08046                             minpos = k;
08047                         }
08048                     }
08049                     if (min < tolerance) {
08050                         cpl_table_set(traces, trace_id, j/step, 
08051                                       nx - peak[minpos]);
08052                         prev_y = peak[minpos];
08053                     }
08054                 }
08055             }
08056         }
08057 
08058 
08059         /*
08060          * Find the peak in that row that is closest to xbottom[i]
08061          */
08062 
08063         if (peaks[pos]) {
08064             peak = cpl_vector_get_data(peaks[pos]);
08065             npeaks = cpl_vector_get_size(peaks[pos]);
08066     
08067             min = fabs(peak[0] - xbottom[i]);
08068             minpos = 0;
08069             for (j = 1; j < npeaks; j++) {
08070                 dist = fabs(peak[j] - xbottom[i]);
08071                 if (min > dist) {
08072                     min = dist;
08073                     minpos = j;
08074                 }
08075             }
08076         }
08077         else {
08078             npeaks = 0;
08079         }
08080 
08081         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
08082         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
08083 
08084         if (min > sradius || npeaks == 0) {
08085             cpl_msg_warning(func, "Cannot find spectrum edge for "
08086                             "bottom (or right) end of slit %d", slit_id[i]);
08087         }
08088         else {
08089 
08090             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
08091             start_y = peak[minpos]; 
08092 
08093             /*
08094              * Perform the tracing of current edge. Above:
08095              */
08096 
08097             prev_y = start_y;
08098 
08099             for (j = pos + step; j < ny; j += step) {
08100                 if (j - pos > pixel_above)
08101                     break;
08102                 if (peaks[j]) {
08103                     peak = cpl_vector_get_data(peaks[j]);
08104                     npeaks = cpl_vector_get_size(peaks[j]);
08105                     min = fabs(peak[0] - prev_y);
08106                     minpos = 0;
08107                     for (k = 1; k < npeaks; k++) {
08108                         dist = fabs(peak[k] - prev_y);
08109                         if (min > dist) {
08110                             min = dist;
08111                             minpos = k;
08112                         }
08113                     }
08114                     if (min < tolerance) {
08115                         cpl_table_set(traces, trace_id, j/step, 
08116                                       nx - peak[minpos]);
08117                         prev_y = peak[minpos];
08118                     }
08119                 }
08120             }
08121 
08122             /*
08123              * Perform the tracing of current edge. Below:
08124              */
08125 
08126             prev_y = start_y;
08127 
08128             for (j = pos - step; j >= 0; j -= step) {
08129                 if (pos - j > pixel_below)
08130                     break;
08131                 if (peaks[j]) {
08132                     peak = cpl_vector_get_data(peaks[j]);
08133                     npeaks = cpl_vector_get_size(peaks[j]);
08134                     min = fabs(peak[0] - prev_y);
08135                     minpos = 0;
08136                     for (k = 1; k < npeaks; k++) {
08137                         dist = fabs(peak[k] - prev_y);
08138                         if (min > dist) {
08139                             min = dist;
08140                             minpos = k;
08141                         }
08142                     }
08143                     if (min < tolerance) {
08144                         cpl_table_set(traces, trace_id, j/step, 
08145                                       nx - peak[minpos]);
08146                         prev_y = peak[minpos];
08147                     }
08148                 }
08149             }
08150         }
08151 
08152     }   /* End of loop on slits */
08153 
08154     for (i = 0; i < ny; i += step)
08155         cpl_vector_delete(peaks[i]);
08156     cpl_free(peaks);
08157 
08158     /*
08159      * Restore original orientation of slits positions table
08160      */
08161 
08162     mos_rotate_slits(slits, -1, ny, nx);
08163 
08164     return traces;
08165 
08166 }
08167 
08168 
08189 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
08190 {
08191     const char *func = "mos_poly_trace";
08192 
08193     cpl_table      *polytraces;
08194     cpl_table      *dummy;
08195     cpl_vector     *x;
08196     cpl_vector     *trace;
08197     cpl_polynomial *polytrace;
08198     char            trace_id[MAX_COLNAME];
08199     char            trace_res[MAX_COLNAME];
08200     char            trace_mod[MAX_COLNAME];
08201     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08202                                                  /* Max order is 5 */
08203     double         *xdata;
08204     int            *slit_id;
08205     int             nslits;
08206     int             nrows;
08207     int             npoints;
08208     int             i, j;
08209     cpl_size        k;
08210 
08211 
08212     if (traces == NULL || slits == NULL) {
08213         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08214         return NULL;
08215     }
08216 
08217     if (order > 5) {
08218         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08219         return NULL;
08220     }
08221 
08222     nrows   = cpl_table_get_nrow(traces);
08223     xdata   = cpl_table_get_data_double(traces, "x");
08224     nslits  = cpl_table_get_nrow(slits);
08225     slit_id = cpl_table_get_data_int(slits, "slit_id");
08226 
08227     polytraces = cpl_table_new(2*nslits);
08228     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
08229     for (i = 0; i <= order; i++)
08230         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
08231 
08232     for (i = 0; i < nslits; i++) {
08233         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
08234 
08235             if (j) {
08236                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
08237                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
08238                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
08239             }
08240             else {
08241                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
08242                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
08243                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
08244             }
08245 
08246             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
08247 
08248             /*
08249              * The "dummy" table is just a tool for eliminating invalid
08250              * points from the vectors to be fitted.
08251              */
08252 
08253             dummy = cpl_table_new(nrows);
08254             cpl_table_duplicate_column(dummy, "x", traces, "x");
08255             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
08256             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
08257             if (npoints < 2 * order) {
08258                 cpl_table_delete(dummy);
08259                 continue;
08260             }
08261             cpl_table_erase_invalid(dummy);
08262             x     = cpl_vector_wrap(npoints, 
08263                                     cpl_table_get_data_double(dummy, "x"));
08264             trace = cpl_vector_wrap(npoints, 
08265                                     cpl_table_get_data_double(dummy, trace_id));
08266             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
08267             cpl_vector_unwrap(x);
08268             cpl_vector_unwrap(trace);
08269             cpl_table_delete(dummy);
08270 
08271             /*
08272              * Screen bad solutions. At the moment, a primitive screening
08273              * consists in excluding solutions displaying excessive
08274              * curvature (larger than 1E-5 / pixel)
08275              */
08276 
08277             k = 2;
08278             if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) >  1.E-4) {
08279                 cpl_polynomial_delete(polytrace);
08280                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
08281                 cpl_table_duplicate_column(traces, trace_res, traces, 
08282                                            trace_mod);
08283                 if (j) 
08284                     cpl_msg_warning(func, "Exclude bad curvature solution "
08285                            "for bottom (right) edge of slit %d", slit_id[i]);
08286                 else
08287                     cpl_msg_warning(func, "Exclude bad curvature solution "
08288                                 "for top (left) edge of slit %d", slit_id[i]);
08289                 continue;
08290             }
08291 
08292             /*
08293              * Write polynomial coefficients to the output table,
08294              * tagged with the appropriate slit_id
08295              */
08296 
08297             for (k = 0; k <= order; k++)
08298                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
08299                                      cpl_polynomial_get_coeff(polytrace, &k));
08300 
08301             /*
08302              * Add column of residuals to input traces table
08303              */
08304 
08305             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
08306             cpl_table_set_column_unit(traces, trace_mod, "pixel");
08307 
08308             for (k = 0; k < nrows; k++) {
08309                 cpl_table_set_double(traces, trace_mod, k,
08310                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
08311             }
08312 
08313             cpl_polynomial_delete(polytrace);
08314 
08315             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
08316             cpl_table_subtract_columns(traces, trace_res, trace_id);
08317             cpl_table_multiply_scalar(traces, trace_res, -1.0);
08318 
08319         }
08320     }
08321 
08322     return polytraces;
08323 
08324 }
08325 
08326 
08350 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
08351                                 int mode)
08352 {
08353     const char *func = "mos_global_trace";
08354 
08355     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08356                                                  /* Max order is 5 */
08357     cpl_table      *table;
08358     cpl_vector     *c0;
08359     cpl_vector     *cn;
08360     cpl_bivector   *list;
08361 /* alternative (not robust)
08362     cpl_polynomial *poly;
08363 */
08364 
08365     double *offset;
08366     double  rms, q, m;
08367 
08368     int order, nrows, nslits;
08369     int i, j;
08370 
08371 
08372     if (polytraces == NULL) {
08373         cpl_msg_error(func, "Missing spectral curvature table");
08374         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08375     }
08376 
08377     if (slits == NULL) {
08378         cpl_msg_error(func, "Missing slits positions table");
08379         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08380     }
08381 
08382     nslits = cpl_table_get_nrow(slits);
08383 
08384     table = cpl_table_duplicate(polytraces);
08385     cpl_table_erase_invalid(table);
08386 
08387     nrows = cpl_table_get_nrow(table);
08388 
08389     if (nrows < 4) {
08390         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
08391                       "(%d): the determination of a global curvature model "
08392                       "failed", nrows);
08393         return CPL_ERROR_NONE;
08394     }
08395     
08396     order = cpl_table_get_ncol(polytraces) - 2;
08397 
08398     for (i = 0; i <= order; i++) {
08399         if (!cpl_table_has_column(table, clab[i])) {
08400             cpl_msg_error(func, "Wrong spectral curvature table");
08401             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08402         }
08403     }
08404 
08405 
08406     /*
08407      * Fill in advance the missing offset terms
08408      */
08409 
08410     for (i = 0; i < nslits; i++) {
08411         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
08412             cpl_table_set_double(polytraces, clab[0], 2*i, 
08413                                 cpl_table_get_double(slits, "ytop", i, NULL));
08414         }
08415         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
08416             cpl_table_set_double(polytraces, clab[0], 2*i+1,
08417                              cpl_table_get_double(slits, "ybottom", i, NULL));
08418         }
08419     }
08420 
08421     offset = cpl_table_get_data_double(polytraces, clab[0]);
08422 
08423 
08424     /*
08425      * Fit the global model and modify polytraces table accordingly
08426      */
08427 
08428     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
08429 
08430     for (i = 1; i <= order; i++) {
08431         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
08432         list = cpl_bivector_wrap_vectors(c0, cn);
08433         robustLinearFit(list, &q, &m, &rms);
08434 /* alternative (not robust)
08435         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
08436 */
08437         for (j = 0; j < 2*nslits; j++) {
08438             if (mode == 1)
08439                 if (cpl_table_is_valid(polytraces, clab[i], j))
08440                     continue;
08441             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
08442 /* alternative (not robust)
08443             cpl_table_set_double(polytraces, clab[i], j, 
08444                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
08445 */
08446         }
08447         cpl_bivector_unwrap_vectors(list);
08448 /* alternative (not robust)
08449         cpl_polynomial_delete(poly);
08450 */
08451         cpl_vector_unwrap(cn);
08452     }
08453 
08454     cpl_vector_unwrap(c0);
08455     cpl_table_delete(table);
08456 
08457     return CPL_ERROR_NONE;
08458 
08459 }
08460 
08461 
08531 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
08532                                    cpl_table *polytraces, double reference,
08533                                    double blue, double red, double dispersion,
08534                                    int flux, cpl_image *calibration)
08535 {
08536     const char *func = "mos_spatial_calibration";
08537 
08538     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08539                                                  /* Max order is 5 */
08540     cpl_polynomial *polytop;
08541     cpl_polynomial *polybot;
08542     cpl_image     **exslit;
08543     cpl_image      *resampled;
08544     float          *data;
08545     float          *sdata;
08546     float          *xdata;
08547     double          vtop, vbot, value;
08548     double          top, bot;
08549     double          coeff;
08550     double          ytop, ybot;
08551     double          ypos, yfra;
08552     double          factor;
08553     int             yint, ysize, yprev;
08554     int             nslits;
08555     int             npseudo;
08556     int            *slit_id;
08557     int            *length;
08558     int             nx, ny;
08559     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
08560     int             missing_top, missing_bot;
08561     int             null;
08562     int             order;
08563     int             i, j; 
08564     cpl_size        k;
08565 
08566     int             create_position = 1;
08567 
08568 
08569     if (spectra == NULL || slits == NULL || polytraces == NULL) {
08570         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08571         return NULL;
08572     }
08573 
08574     if (dispersion <= 0.0) {
08575         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08576         return NULL;
08577     }
08578 
08579     if (red - blue < dispersion) {
08580         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08581         return NULL;
08582     }
08583 
08584     nx = cpl_image_get_size_x(spectra);
08585     ny = cpl_image_get_size_y(spectra);
08586     sdata = cpl_image_get_data(spectra);
08587     if (calibration)
08588         data = cpl_image_get_data(calibration);
08589 
08590     if (cpl_table_has_column(slits, "position"))
08591         create_position = 0;
08592 
08593     if (create_position) {
08594         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
08595         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
08596         cpl_table_set_column_unit(slits, "position", "pixel");
08597         cpl_table_set_column_unit(slits, "length", "pixel");
08598     }
08599     else
08600         length = cpl_table_get_data_int(slits, "length");
08601 
08602     nslits   = cpl_table_get_nrow(slits);
08603     slit_id  = cpl_table_get_data_int(slits, "slit_id");
08604     order    = cpl_table_get_ncol(polytraces) - 2;
08605 
08606     /*
08607      * The spatial resampling is performed for a certain number of 
08608      * pixels above and below the position of the reference wavelength:
08609      */
08610 
08611     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
08612     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
08613 
08614     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
08615 
08616     for (i = 0; i < nslits; i++) {
08617         
08618         if (create_position == 0)
08619             if (length[i] == 0)
08620                 continue;
08621 
08622         /*
08623          * Note that the x coordinate of the reference pixels on the CCD
08624          * is taken arbitrarily at the top end of each slit. This wouldn't
08625          * be entirely correct in case of curved slits, or in presence of
08626          * heavy distortions: in such cases the spatial resampling is
08627          * really performed across a wide range of wavelengths. But
08628          * the lag between top and bottom spectral curvature models 
08629          * would introduce even in such cases negligible effects on
08630          * the spectral spatial resampling.
08631          */
08632 
08633         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
08634 
08635         start_pixel = refpixel - pixel_below;
08636         if (start_pixel < 0)
08637             start_pixel = 0;
08638 
08639         end_pixel = refpixel + pixel_above;
08640         if (end_pixel > nx)
08641             end_pixel = nx;
08642 
08643         /*
08644          * Recover from the table of spectral curvature coefficients
08645          * the curvature polynomials.
08646          */
08647 
08648         missing_top = 0;
08649         polytop = cpl_polynomial_new(1);
08650         for (k = 0; k <= order; k++) {
08651             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08652             if (null) {
08653                 cpl_polynomial_delete(polytop);
08654                 missing_top = 1;
08655                 break;
08656             }
08657             cpl_polynomial_set_coeff(polytop, &k, coeff);
08658         }
08659 
08660         missing_bot = 0;
08661         polybot = cpl_polynomial_new(1);
08662         for (k = 0; k <= order; k++) {
08663             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08664             if (null) {
08665                 cpl_polynomial_delete(polybot);
08666                 missing_bot = 1;
08667                 break;
08668             }
08669             cpl_polynomial_set_coeff(polybot, &k, coeff);
08670         }
08671 
08672         if (missing_top && missing_bot) {
08673             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08674                             "traced: no extraction!", 
08675                             slit_id[i]);
08676             continue;
08677         }
08678 
08679         /*
08680          * In case just one of the two edges was not traced, the other
08681          * edge curvature model is duplicated and shifted to the other
08682          * end of the slit: better than nothing!
08683          */
08684 
08685         if (missing_top) {
08686             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08687                             "the spectral curvature of the lower edge "
08688                             "is used instead.", slit_id[i]);
08689             polytop = cpl_polynomial_duplicate(polybot);
08690             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08691             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08692             k = 0;
08693             coeff = cpl_polynomial_get_coeff(polybot, &k);
08694             coeff += ytop - ybot;
08695             cpl_polynomial_set_coeff(polytop, &k, coeff);
08696         }
08697 
08698         if (missing_bot) {
08699             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08700                             "the spectral curvature of the upper edge "
08701                             "is used instead.", slit_id[i]);
08702             polybot = cpl_polynomial_duplicate(polytop);
08703             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08704             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08705             k = 0;
08706             coeff = cpl_polynomial_get_coeff(polytop, &k);
08707             coeff -= ytop - ybot;
08708             cpl_polynomial_set_coeff(polybot, &k, coeff);
08709         }
08710 
08711         /*
08712          * Allocate image for current extracted slit
08713          */
08714 
08715         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08716         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08717         npseudo = ceil(top-bot) + 1;
08718 
08719         if (npseudo < 1) {
08720             cpl_polynomial_delete(polytop);
08721             cpl_polynomial_delete(polybot);
08722             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08723                             slit_id[i]);
08724             continue;
08725         }
08726 
08727         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08728         xdata = cpl_image_get_data(exslit[i]);
08729 
08730         /*
08731          * Write interpolated values to slit image.
08732          */
08733 
08734         for (j = start_pixel; j < end_pixel; j++) {
08735             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08736             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08737             factor = (top-bot)/npseudo;
08738             for (k = 0; k <= npseudo; k++) {
08739                 ypos = top - k*factor;
08740                 yint = floor(ypos);
08741                 yfra = ypos - yint;
08742                 if (yint >= 0 && yint < ny-1) {
08743                     vtop = sdata[j + nx*yint];
08744                     vbot = sdata[j + nx*(yint+1)];
08745 
08746                     //This means that the top and bottom traces are crossing,
08747                     //which is physically impossible, so let's set it to 0. 
08748                     if(factor <= 0 )  
08749                         value = 0;
08750                     else if(vtop == FLT_MAX || vbot == FLT_MAX)
08751                         value = FLT_MAX;
08752                     else
08753                     {
08754                         value = vtop*(1-yfra) + vbot*yfra;
08755                         if (flux)
08756                             value *= factor;
08757                     }
08758                     xdata[j + nx*(npseudo-k)] = value;
08759                     if (calibration) {
08760                         data[j + nx*yint] = (top-yint)/factor;
08761                         if (k) {
08762 
08763                             /*
08764                              * This is added to recover lost pixels on
08765                              * the CCD image (pixels are lost because
08766                              * the CCD pixels are less than npseudo+1).
08767                              */
08768 
08769                             if (yprev - yint > 1) {
08770                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08771                             }
08772                         }
08773                     }
08774                 }
08775                 yprev = yint;
08776             }
08777         }
08778         cpl_polynomial_delete(polytop);
08779         cpl_polynomial_delete(polybot);
08780     }
08781 
08782     /*
08783      * Now all the slits images are copied to a single image
08784      */
08785 
08786     ysize = 0;
08787     for (i = 0; i < nslits; i++)
08788         if (exslit[i])
08789             ysize += cpl_image_get_size_y(exslit[i]);
08790 
08791     if(ysize == 0)
08792         return NULL;
08793 
08794     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08795 
08796     yint = -1;
08797     for (i = 0; i < nslits; i++) {
08798         if (exslit[i]) {
08799             yint += cpl_image_get_size_y(exslit[i]);
08800             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08801             if (create_position) {
08802                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08803                 cpl_table_set_int(slits, "length", i, 
08804                                   cpl_image_get_size_y(exslit[i]));
08805             }
08806             cpl_image_delete(exslit[i]);
08807         }
08808         else if (create_position) {
08809             cpl_table_set_int(slits, "position", i, -1);
08810             cpl_table_set_int(slits, "length", i, 0);
08811         }
08812     }
08813 
08814 
08815     /*
08816      * Elimination of non-traced slits from slit position table: we cannot do
08817      * it because we would lose sync with polytraces and other slit-oriented
08818      * tables. COMMENTED OUT.
08819      * 
08820 
08821     if (create_position) {
08822 
08823         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08824             cpl_table_erase_selected(slits);
08825 
08826     }
08827 
08828     */
08829 
08830     cpl_free(exslit);
08831 
08832     return resampled;
08833 
08834 }
08835 
08836 
08949 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08950                                             cpl_vector *lines,
08951                                             double dispersion, float level,
08952                                             int sradius, int order,
08953                                             double reject, double refwave,
08954                                             double *wavestart, double *waveend,
08955                                             int *nlines, double *error, 
08956                                             cpl_table *idscoeff,
08957                                             cpl_image *calibration,
08958                                             cpl_image *residuals,
08959                                             cpl_table *restable,
08960                                             cpl_table *detected_lines)
08961 {
08962 
08963     const char *func = "mos_wavelength_calibration_final";
08964 
08965     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08966                                                  /* Max order is 5 */
08967 
08968     double  tolerance = 20.0;    /* Probably forever...                */
08969     int     step = 10;           /* Compute restable every "step" rows */
08970 
08971     char            name[MAX_COLNAME];
08972 
08973     cpl_image      *resampled;
08974     cpl_bivector   *peaks_ident;
08975     cpl_vector     *wavel;
08976     cpl_vector     *peaks;
08977     cpl_polynomial *ids;
08978     cpl_polynomial *lin;
08979     cpl_polynomial *fguess;
08980     cpl_table      *coeff;
08981     double          ids_err;
08982     double          max_disp, min_disp;
08983     double         *line;
08984     double          firstLambda, lastLambda, lambda;
08985     double          wave, pixe, value;
08986     double          c;
08987     float          *sdata;
08988     float          *rdata;
08989     float          *idata;
08990     float          *ddata;
08991     float           v1, v2, vi;
08992     float           fpixel;
08993     int            *length;
08994     int             pixstart, pixend;
08995     int             row_top, row_bot;
08996     int             extrapolation;
08997     int             nref;
08998     int             nslits;
08999     int             nfits;
09000     int             nl, nx, ny, pixel;
09001     int             countLines, usedLines;
09002     int             uorder;
09003     int             missing;
09004     int             null;
09005     int             width, uradius;
09006     int             i, j, s;
09007     cpl_size        k;
09008 
09009 
09010     if (dispersion == 0.0) {
09011         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
09012         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09013         return NULL;
09014     }
09015 
09016     if (dispersion < 0.0) {
09017         cpl_msg_error(func, "The expected dispersion must be positive");
09018         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09019         return NULL;
09020     }
09021 
09022     if (idscoeff == NULL) {
09023         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
09024         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09025         return NULL;
09026     }
09027 
09028     max_disp = dispersion + dispersion * tolerance / 100;
09029     min_disp = dispersion - dispersion * tolerance / 100;
09030 
09031     if (order < 1) {
09032         cpl_msg_error(func, "The order of the fitting polynomial "
09033                       "must be at least 1");
09034         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09035         return NULL;
09036     }
09037 
09038     if (image == NULL || lines == NULL) {
09039         cpl_msg_error(func, "Both spectral exposure and reference line "
09040                       "catalog are required in input");
09041         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09042         return NULL;
09043     }
09044 
09045     nx = cpl_image_get_size_x(image);
09046     ny = cpl_image_get_size_y(image);
09047     sdata = cpl_image_get_data_float(image);
09048 
09049     nref = cpl_vector_get_size(lines);
09050     line = cpl_vector_get_data(lines);
09051 
09052     if (*wavestart < 1.0 && *waveend < 1.0) {
09053         firstLambda = line[0];
09054         lastLambda = line[nref-1];
09055         extrapolation = (lastLambda - firstLambda) / 10;
09056         firstLambda -= extrapolation;
09057         lastLambda += extrapolation;
09058         *wavestart = firstLambda;
09059         *waveend = lastLambda;
09060     }
09061     else {
09062         firstLambda = *wavestart;
09063         lastLambda = *waveend;
09064     }
09065 
09066     nl = (lastLambda - firstLambda) / dispersion;
09067     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
09068     rdata = cpl_image_get_data_float(resampled);
09069 
09070     /*
09071      * Allocate total output table of IDS coefficients
09072      */
09073 
09074     for (j = 0; j <= order; j++)
09075         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
09076 
09077     if (calibration)
09078         idata = cpl_image_get_data_float(calibration);
09079 
09080     if (residuals)
09081         ddata = cpl_image_get_data_float(residuals);
09082 
09083     if (restable) {
09084         cpl_table_set_size(restable, nref);
09085         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
09086         cpl_table_copy_data_double(restable, "wavelength", line);
09087         for (i = 0; i < ny; i += step) {
09088              snprintf(name, MAX_COLNAME, "r%d", i);
09089              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
09090              snprintf(name, MAX_COLNAME, "d%d", i);
09091              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
09092              snprintf(name, MAX_COLNAME, "p%d", i);
09093              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
09094         }
09095     }
09096 
09097     if (detected_lines) {
09098         cpl_table_set_size(detected_lines, 0);
09099         cpl_table_new_column(detected_lines, "slit_id", CPL_TYPE_INT);
09100         cpl_table_new_column(detected_lines, "xpos_rectified", CPL_TYPE_DOUBLE);
09101         cpl_table_new_column(detected_lines, "ypos_rectified", CPL_TYPE_DOUBLE);
09102         cpl_table_new_column(detected_lines, "xpos_rectified_iter", CPL_TYPE_DOUBLE);
09103         cpl_table_new_column(detected_lines, "ypos_rectified_iter", CPL_TYPE_DOUBLE);
09104         cpl_table_new_column(detected_lines, "peak_flux", CPL_TYPE_DOUBLE);
09105         cpl_table_new_column(detected_lines, "wave_ident", CPL_TYPE_DOUBLE);
09106         cpl_table_new_column(detected_lines, "wave_ident_iter", CPL_TYPE_DOUBLE);
09107         cpl_table_new_column(detected_lines, "xpos_fit_rect_wavecal", CPL_TYPE_DOUBLE);
09108         cpl_table_new_column(detected_lines, "res_xpos", CPL_TYPE_DOUBLE);
09109         cpl_table_new_column(detected_lines, "fit_used", CPL_TYPE_INT);
09110     }
09111 
09112 
09113     /*
09114      * Process all slits separately.
09115      */
09116 
09117     nslits   = cpl_table_get_nrow(slits);
09118     length   = cpl_table_get_data_int(slits, "length");
09119 
09120     row_top = ny;
09121     for (s = 0; s < nslits; s++) {
09122 
09123         int slit_id;
09124         slit_id = cpl_table_get_int(slits, "slit_id", s, NULL);
09125         
09126         if (length[s] == 0)
09127             continue;
09128 
09129         /*
09130          * row_top and row_bot define the boundaries of the current slit.
09131          * Here we begin (arbitrarily...) from the top slit.
09132          */
09133 
09134         row_bot = cpl_table_get_int(slits, "position", s, NULL);
09135 
09136         if (sradius > 0) {
09137 
09138             /*
09139              * If a search radius was defined, allocate the table of
09140              * the fitting polynomials coefficients. This table is
09141              * just used to generate the first-guess polynomial made
09142              * of the median coefficients of all polynomials found
09143              * for this slit.
09144              */
09145 
09146             coeff = cpl_table_new(row_top - row_bot);
09147             for (j = 0; j <= order; j++)
09148                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
09149         }
09150 
09151         /*
09152          * Here is the loop on all rows of the current slit. They are
09153          * wavelength calibrated one by one.
09154          */
09155 
09156         for (i = row_bot; i < row_top; i++) {
09157             width = mos_lines_width(sdata + i*nx, nx);
09158             if (width < 5)
09159                 width = 5;
09160             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
09161             if (peaks) {
09162                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
09163             }
09164             if (peaks) {
09165                 int keep_multiplex = mos_multiplex;
09166                 mos_multiplex = -1;
09167                 if(detected_lines)
09168                 {
09169                     cpl_size newlines = cpl_vector_get_size(peaks); 
09170                     cpl_size oldsize = cpl_table_get_nrow(detected_lines); 
09171                     cpl_table_set_size(detected_lines, oldsize + newlines);
09172                     for(cpl_size iline = 0; iline < newlines; ++iline)
09173                     {
09174                         cpl_table_set_int(detected_lines, "slit_id",
09175                              oldsize + iline, slit_id);
09176                         cpl_table_set_double(detected_lines, "xpos_rectified",
09177                              oldsize + iline, cpl_vector_get(peaks, iline) + 1);
09178                         cpl_table_set_double(detected_lines, "ypos_rectified",
09179                              oldsize + iline, (double)i + 1);
09180                         cpl_table_set_double(detected_lines, "peak_flux",
09181                              oldsize + iline, 
09182                              sdata[i*nx+(int)(cpl_vector_get(peaks, iline)+0.5)]);
09183                         cpl_table_set_int(detected_lines,
09184                                           "fit_used",
09185                                           oldsize + iline, 0);
09186                     }
09187                 }
09188                 peaks_ident = mos_identify_peaks(peaks, lines, 
09189                                             min_disp, max_disp, 0.05);
09190                 mos_multiplex = keep_multiplex;
09191                 if (peaks_ident) {
09192                     cpl_bivector * peaks_ident_used_fit;
09193                     countLines = cpl_bivector_get_size(peaks_ident);
09194                     if (countLines < 4) {
09195                         cpl_bivector_delete(peaks_ident);
09196                         cpl_vector_delete(peaks);
09197                         if (nlines)
09198                             nlines[i] = 0;
09199                         if (error)
09200                             error[i] = 0.0;
09201                         continue;
09202                     }
09203 
09204                     /*
09205                      * Set reference wavelength as zero point
09206                      */
09207 
09208                     wavel = cpl_bivector_get_y(peaks_ident);
09209                     cpl_vector_subtract_scalar(wavel, refwave);
09210 
09211                     uorder = countLines / 2 - 1;
09212                     if (uorder > order)
09213                         uorder = order;
09214 
09215                     ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
09216                                            2 * (uorder + 1), &usedLines,
09217                                            &ids_err, &peaks_ident_used_fit);
09218 
09219                     if (ids == NULL) {
09220                         cpl_bivector_delete(peaks_ident);
09221                         cpl_vector_delete(peaks);
09222                         if (nlines)
09223                             nlines[i] = 0;
09224                         if (error)
09225                             error[i] = 0.0;
09226                         cpl_error_reset();
09227                         continue;
09228                     }
09229 
09230                     if (sradius > 0) {
09231                         for (k = 0; k <= order; k++) {
09232                             if (k > uorder) {
09233                                 cpl_table_set_double(coeff, clab[k], 
09234                                 i - row_bot, 0.0);
09235                             }
09236                             else {
09237                                 cpl_table_set_double(coeff, clab[k], 
09238                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
09239                             }
09240                         }
09241                     }
09242                /*   else {   */
09243                         if (calibration) {
09244                             pixstart = cpl_polynomial_eval_1d(ids,
09245                               cpl_bivector_get_y_data(peaks_ident)[0], 
09246                               NULL);
09247                             pixend = cpl_polynomial_eval_1d(ids,
09248                               cpl_bivector_get_y_data(peaks_ident)[countLines-1],
09249                               NULL);
09250                             extrapolation = (pixend - pixstart) / 5;
09251                             pixstart -= extrapolation;
09252                             pixend += extrapolation;
09253                             if (pixstart < 0)
09254                                 pixstart = 0;
09255                             if (pixend > nx)
09256                                 pixend = nx;
09257    
09258                             for (j = pixstart; j < pixend; j++) {
09259                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
09260                                      firstLambda, lastLambda, refwave, j);
09261                             }
09262                         }
09263 
09264                         /*
09265                          * Residuals image
09266                          */
09267         
09268                         if (residuals || (restable && !(i%step))) {
09269                             if (restable && !(i%step)) {
09270                                 lin = cpl_polynomial_new(1);
09271                                 for (k = 0; k < 2; k++)
09272                                     cpl_polynomial_set_coeff(lin, &k,
09273                                           cpl_polynomial_get_coeff(ids, &k));
09274                             }
09275                             for (j = 0; j < countLines; j++) {
09276                                 pixe = cpl_bivector_get_x_data(peaks_ident)[j];
09277                                 wave = cpl_bivector_get_y_data(peaks_ident)[j];
09278                                 value = pixe 
09279                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
09280                                 if (residuals) {
09281                                     pixel = pixe + 0.5;
09282                                     (ddata + i*nx)[pixel] = value;
09283                                 }
09284                                 if (restable && !(i%step)) {
09285                                     for (k = 0; k < nref; k++) {
09286                                         if (fabs(line[k]-refwave-wave) < 0.1) {
09287                                             snprintf(name, MAX_COLNAME, 
09288                                                      "r%d", i);
09289                                             cpl_table_set_double(restable, name,
09290                                                                  k, value);
09291                                             value = pixe
09292                                                   - cpl_polynomial_eval_1d(lin,
09293                                                               wave, NULL);
09294                                             snprintf(name, MAX_COLNAME, 
09295                                                      "d%d", i);
09296                                             cpl_table_set_double(restable, name,
09297                                                                  k, value);
09298                                             snprintf(name, MAX_COLNAME,
09299                                                      "p%d", i);
09300                                             cpl_table_set_double(restable, name,
09301                                                                  k, pixe);
09302                                             break;
09303                                         }
09304                                     }
09305                                 }
09306                             }
09307                             if (restable && !(i%step)) {
09308                                 cpl_polynomial_delete(lin);
09309                             }
09310 /***
09311                             for (j = 0; j < countLines; j++) {
09312                                 pixel = cpl_bivector_get_x_data(output)[j] 
09313                                       + 0.5;
09314                                 (ddata + i*nx)[pixel] =
09315                                 cpl_bivector_get_x_data(output)[j]
09316                               - cpl_polynomial_eval_1d(ids,
09317                                 cpl_bivector_get_y_data(output)[j], 
09318                                 NULL);
09319                             }
09320 ***/
09321                             //Fill the line identification information in 
09322                             //the detected_lines table
09323                             if(detected_lines)
09324                             {
09325                                 cpl_size nidentlines = cpl_bivector_get_size(peaks_ident); 
09326                                 cpl_size ndetectlines = cpl_vector_get_size(peaks); 
09327                                 cpl_size totalsize = cpl_table_get_nrow(detected_lines);
09328                                 for(cpl_size idline = 0; idline < nidentlines; ++idline)
09329                                 {
09330                                     for(cpl_size detline = 0; detline < ndetectlines; ++detline)
09331                                     {
09332                                         if(cpl_vector_get(peaks, detline) == 
09333                                            cpl_bivector_get_x_data(peaks_ident)[idline])
09334                                         {
09335                                             cpl_size table_pos = totalsize - ndetectlines + detline;
09336                                             double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
09337                                             double xpix_fit = cpl_polynomial_eval_1d(ids,
09338                                                     wave_ident - refwave, NULL);
09339                                             double xpos_det = cpl_table_get_double(detected_lines,
09340                                                     "xpos_rectified",
09341                                                     table_pos, &null);
09342                                             cpl_table_set_double(detected_lines,
09343                                                                  "wave_ident",
09344                                                                  table_pos,
09345                                                                  wave_ident);
09346                                             cpl_table_set_double(detected_lines,
09347                                                                  "xpos_fit_rect_wavecal",
09348                                                                  table_pos,
09349                                                                  xpix_fit + 1);
09350                                             cpl_table_set_double(detected_lines,
09351                                                                  "res_xpos",
09352                                                                  table_pos,
09353                                                                   xpos_det - xpix_fit - 1);
09354                                             cpl_table_set_int(detected_lines,
09355                                                               "fit_used",
09356                                                               table_pos, 0);
09357                                             for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
09358                                             {
09359                                                 if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
09360                                                     cpl_table_set_int(detected_lines,
09361                                                                      "fit_used",
09362                                                                      table_pos, 1);
09363                                             }
09364                                         }
09365                                     }
09366                                 }
09367                             }
09368 
09369                         }
09370                 /*  }   */
09371 
09372                     /*
09373                      * Write it anyway, even in case a first-guess based
09374                      * solution will be searched afterwards: in case of
09375                      * failure, the "blind" solution is kept.
09376                      */
09377 
09378                     if (nlines)
09379                         nlines[i] = usedLines;
09380                     if (error)
09381                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
09382 
09383                     for (k = 0; k <= order; k++) {
09384                         if (k > uorder) {
09385                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
09386                         }
09387                         else {
09388                             cpl_table_set_double(idscoeff, clab[k], i,
09389                                       cpl_polynomial_get_coeff(ids, &k));
09390                         }
09391                     }
09392 
09393                     cpl_polynomial_delete(ids);
09394                     cpl_bivector_delete(peaks_ident);
09395                 }
09396                 cpl_vector_delete(peaks);
09397             }
09398         }       /* End of loop on current slit's rows */
09399 
09400 
09401         if (sradius > 0) {
09402 
09403             /*
09404              * See whether there are valid fits at all...
09405              */
09406     
09407             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
09408     
09409             if (nfits) {
09410                 int slope = 0;
09411 
09412                 fguess = cpl_polynomial_new(1);
09413 
09414                 if (mos_interpolate_wavecalib_mos(coeff, 2, 1)) {
09415 
09416                     slope = 0;
09417 
09418                     /*
09419                      * Compute a median IDS polynomial for the current slit
09420                      */
09421 
09422                     for (k = 0; k <= order; k++) {
09423                         c = cpl_table_get_column_median(coeff, clab[k]);
09424                         cpl_polynomial_set_coeff(fguess, &k, c);
09425                     }
09426                 }
09427                 else {
09428                     slope = 1;
09429                 }
09430 
09431                 for (i = row_bot; i < row_top; i++) {
09432                     cpl_bivector * peaks_ident_used_fit;
09433 
09434                     /*
09435                      * Use first-guess to find the reference lines again
09436                      */
09437 
09438                     width = mos_lines_width(sdata + i*nx, nx);
09439                     if (width > sradius) {
09440                         uradius = width; 
09441                     }
09442                     else {
09443                         uradius = sradius;
09444                     }
09445 
09446                     if (slope) {
09447                         for (k = 0; k <= order; k++) {
09448                             c = cpl_table_get_double(coeff, clab[k], 
09449                                                      i - row_bot, NULL);
09450                             cpl_polynomial_set_coeff(fguess, &k, c);
09451                         }
09452                     }
09453 
09454                     peaks_ident = mos_find_peaks(sdata + i*nx, nx, lines, 
09455                                             fguess, refwave, uradius);
09456 
09457                     if (peaks_ident == NULL) {
09458                         cpl_error_reset();
09459                         continue;
09460                     }
09461 
09462                     countLines = cpl_bivector_get_size(peaks_ident);
09463 
09464                     if (countLines < 4) {
09465                         cpl_bivector_delete(peaks_ident);
09466                         continue;
09467                     }
09468 
09469                     /*
09470                      * Set reference wavelength as zero point
09471                      */
09472 
09473                     wavel = cpl_bivector_get_y(peaks_ident);
09474                     cpl_vector_subtract_scalar(wavel, refwave);
09475 
09476                     uorder = countLines / 2 - 1;
09477                     if (uorder > order)
09478                         uorder = order;
09479 
09480                     ids = mos_poly_wav2pix(peaks_ident, uorder, reject,
09481                                            2 * (uorder + 1), &usedLines,
09482                                            &ids_err, &peaks_ident_used_fit);
09483 
09484                     if (ids == NULL) {
09485                         cpl_error_reset();
09486                         cpl_bivector_delete(peaks_ident);
09487                         continue;
09488                     }
09489 
09490                     if (nlines)
09491                         nlines[i] = usedLines;
09492                     if (error)
09493                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
09494 
09495                     if (calibration) {
09496                         pixstart = cpl_polynomial_eval_1d(ids,
09497                            cpl_bivector_get_y_data(peaks_ident)[0], 
09498                            NULL);
09499                         pixend = cpl_polynomial_eval_1d(ids,
09500                            cpl_bivector_get_y_data(peaks_ident)[countLines-1], 
09501                            NULL);
09502                         extrapolation = (pixend - pixstart) / 5;
09503                         pixstart -= extrapolation;
09504                         pixend += extrapolation;
09505                         if (pixstart < 0)
09506                             pixstart = 0;
09507                         if (pixend > nx)
09508                             pixend = nx;
09509 
09510                         for (j = pixstart; j < pixend; j++) {
09511                             (idata + i*nx)[j] = mos_eval_dds(ids,
09512                                      firstLambda, lastLambda, refwave, j);
09513                         }
09514                     }
09515 
09516                     /*
09517                      * Residuals image
09518                      */
09519 
09520                     if (residuals || (restable && !(i%step))) {
09521                         if (restable && !(i%step)) {
09522                             lin = cpl_polynomial_new(1);
09523                             for (k = 0; k < 2; k++)
09524                                 cpl_polynomial_set_coeff(lin, &k,
09525                                       cpl_polynomial_get_coeff(ids, &k));
09526                         }
09527                         for (j = 0; j < countLines; j++) {
09528                             pixe = cpl_bivector_get_x_data(peaks_ident)[j];
09529                             wave = cpl_bivector_get_y_data(peaks_ident)[j];
09530                             value = pixe
09531                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
09532                             if (residuals) {
09533                                 pixel = pixe + 0.5;
09534                                 (ddata + i*nx)[pixel] = value;
09535                             }
09536                             if (restable && !(i%step)) {
09537                                 for (k = 0; k < nref; k++) {
09538                                     if (fabs(line[k]-refwave-wave) < 0.1) {
09539                                         snprintf(name, MAX_COLNAME,
09540                                                  "r%d", i);
09541                                         cpl_table_set_double(restable, name,
09542                                                              k, value);
09543                                         value = pixe
09544                                               - cpl_polynomial_eval_1d(lin,
09545                                                           wave, NULL);
09546                                         snprintf(name, MAX_COLNAME,
09547                                                  "d%d", i);
09548                                         cpl_table_set_double(restable, name,
09549                                                              k, value);
09550                                         snprintf(name, MAX_COLNAME,
09551                                                  "p%d", i);
09552                                         cpl_table_set_double(restable, name,
09553                                                              k, pixe);
09554                                         break;
09555                                     }
09556                                 }
09557                             }
09558                         }
09559                         if (restable && !(i%step)) {
09560                             cpl_polynomial_delete(lin);
09561                         }
09562 /***
09563                         for (j = 0; j < countLines; j++) {
09564                             pixel = cpl_bivector_get_x_data(output)[j]
09565                                   + 0.5; 
09566                             (ddata + i*nx)[pixel] =
09567                             cpl_bivector_get_x_data(output)[j]
09568                           - cpl_polynomial_eval_1d(ids,
09569                             cpl_bivector_get_y_data(output)[j], 
09570                             NULL);
09571                         }
09572 ***/
09573                     }
09574                     
09575                     if(detected_lines)
09576                     {
09577                         cpl_size oldsize = cpl_table_get_nrow(detected_lines); 
09578                         cpl_size nidentlines = cpl_bivector_get_size(peaks_ident); 
09579                         cpl_table_set_size(detected_lines, oldsize + nidentlines);
09580                         for(cpl_size idline = 0; idline < nidentlines ; ++idline)
09581                         {
09582                             double wave_ident = cpl_bivector_get_y_data(peaks_ident)[idline] + refwave;
09583                             double xpix_fit = cpl_polynomial_eval_1d(ids,
09584                                     wave_ident - refwave, NULL);
09585                             cpl_table_set_int(detected_lines, "slit_id",
09586                                  oldsize + idline, slit_id);
09587                             cpl_table_set_double(detected_lines, "xpos_rectified_iter",
09588                                  oldsize + idline, cpl_bivector_get_x_data(peaks_ident)[idline] + 1);
09589                             cpl_table_set_double(detected_lines, "ypos_rectified_iter",
09590                                  oldsize + idline, (double)i + 1);
09591                             cpl_table_set_double(detected_lines, "peak_flux",
09592                                  oldsize + idline, 
09593                                  sdata[i*nx+(int)(cpl_bivector_get_x_data(peaks_ident)[idline]+0.5)]);
09594                             cpl_table_set_double(detected_lines, "wave_ident_iter",
09595                                  oldsize + idline, wave_ident);
09596                             cpl_table_set_double(detected_lines, "xpos_fit_rect_wavecal",
09597                                  oldsize + idline, xpix_fit + 1);
09598                             cpl_table_set_int(detected_lines,
09599                                               "fit_used",
09600                                               oldsize + idline, 0);
09601                             for(cpl_size i_used = 0; i_used < cpl_bivector_get_size(peaks_ident_used_fit); ++i_used)
09602                             {
09603                                 if(cpl_bivector_get_x_data(peaks_ident)[idline] == cpl_bivector_get_x_data(peaks_ident_used_fit)[i_used])
09604                                     cpl_table_set_int(detected_lines,
09605                                                      "fit_used",
09606                                                      oldsize + idline, 1);
09607                             }
09608                         }
09609                     }
09610 
09611 
09612                     for (k = 0; k <= order; k++) {
09613                         if (k > uorder) {
09614                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
09615                         }
09616                         else {
09617                             cpl_table_set_double(idscoeff, clab[k], i,
09618                                         cpl_polynomial_get_coeff(ids, &k));
09619                         }
09620                     }
09621 
09622                     cpl_bivector_delete(peaks_ident);
09623                     cpl_polynomial_delete(ids);
09624 
09625                 } /* End of loop "use ids as a first-guess" */
09626 
09627                 cpl_polynomial_delete(fguess);
09628             }
09629 
09630             cpl_table_delete(coeff);
09631 
09632         }
09633 
09634         row_top = row_bot;
09635 
09636     } /* End of loop on slits */
09637 
09638 
09639     /*
09640      * At this point the idscoeff table has been filled with all the 
09641      * fits coefficients obtained for all the rows of the input image.
09642      * Now we apply these coefficients to resample the input image
09643      * at constant wavelength step.
09644      */
09645 
09646     for (i = 0; i < ny; i++) {
09647 
09648         missing = 0;
09649         ids = cpl_polynomial_new(1);
09650         for (k = 0; k <= order; k++) {
09651             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09652             if (null) {
09653                 cpl_polynomial_delete(ids);
09654                 missing = 1;
09655                 break;
09656             }
09657             cpl_polynomial_set_coeff(ids, &k, c);
09658         }
09659         if (missing)
09660             continue;
09661 
09662         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09663         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09664         if (pixstart < 0)
09665             pixstart = 0;
09666         if (pixend > nx)
09667             pixend = nx;
09668 
09669         /*
09670          * Resampled image:
09671          */
09672 
09673         for (j = 0; j < nl; j++) {
09674             lambda = firstLambda + j * dispersion;
09675             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
09676             pixel = fpixel;
09677             if (pixel >= 0 && pixel < nx-1) {
09678                 v1 = (sdata + i*nx)[pixel];
09679                 v2 = (sdata + i*nx)[pixel+1];
09680                 vi = v1 + (v2-v1)*(fpixel-pixel);
09681                 (rdata + i*nl)[j] = vi;
09682             }
09683         }
09684 
09685         cpl_polynomial_delete(ids);
09686     }
09687 
09688     return resampled;
09689 }
09690 
09691 
09718 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
09719                                       double firstLambda, double lastLambda, 
09720                                       double dispersion, cpl_table *idscoeff, 
09721                                       int flux)
09722 {
09723 
09724     const char *func = "mos_wavelength_calibration";
09725 
09726     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09727                                                  /* Max order is 5 */
09728 
09729     cpl_image      *resampled;
09730     cpl_polynomial *ids;
09731     double          pixel_per_lambda;
09732     double          lambda;
09733     double          c;
09734     float          *sdata;
09735     float          *rdata;
09736     float           v0, v1, v2, v3, vi;
09737     float           fpixel;
09738     int             order;
09739     int             pixstart, pixend;
09740     int             nl, nx, ny, pixel;
09741     int             missing;
09742     int             null;
09743     int             i, j;
09744     cpl_size        k;
09745 
09746 
09747     if (dispersion <= 0.0) {
09748         cpl_msg_error(func, "The resampling step must be positive");
09749         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09750         return NULL;
09751     }
09752 
09753     if (lastLambda - firstLambda < dispersion) {
09754         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
09755                       firstLambda, lastLambda);
09756         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09757         return NULL;
09758     }
09759 
09760     if (idscoeff == NULL) {
09761         cpl_msg_error(func, "An IDS coeff table must be given");
09762         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09763         return NULL;
09764     }
09765 
09766     if (image == NULL) {
09767         cpl_msg_error(func, "A scientific spectral image must be given");
09768         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09769         return NULL;
09770     }
09771 
09772     nx = cpl_image_get_size_x(image);
09773     ny = cpl_image_get_size_y(image);
09774     sdata = cpl_image_get_data_float(image);
09775 
09776     nl = (lastLambda - firstLambda) / dispersion;
09777     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
09778     rdata = cpl_image_get_data_float(resampled);
09779 
09780     order = 0;
09781     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
09782         ++order;
09783     --order;
09784 
09785     for (i = 0; i < ny; i++) {
09786 
09787         missing = 0;
09788         ids = cpl_polynomial_new(1);
09789         for (k = 0; k <= order; k++) {
09790             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09791             if (null) {
09792                 cpl_polynomial_delete(ids);
09793                 missing = 1;
09794                 break;
09795             }
09796             cpl_polynomial_set_coeff(ids, &k, c);
09797         }
09798         if (missing)
09799             continue;
09800 
09801         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09802         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09803         if (pixstart < 0)
09804             pixstart = 0;
09805         if (pixend > nx)
09806             pixend = nx;
09807 
09808         /*
09809          * Resampled image:
09810          */
09811 
09812         for (j = 0; j < nl; j++) {
09813             lambda = firstLambda + j * dispersion;
09814             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09815                                             &pixel_per_lambda);
09816 
09817             /*
09818              * The local dispersion is 1 / pixel_per_lambda
09819              * and this factor is used for applying the flux
09820              * conservation correction (if requested).
09821              */
09822 
09823             pixel = fpixel;
09824 
09825             // if (dispersion * pixel_per_lambda < 2.0) {
09826             if (1) {  /* Old behaviour: this is safe. */
09827 
09828                 /*
09829                  * In this case we just sample interpolating the
09830                  * signal of nearby pixels.
09831                  */
09832 
09833                 //the wave calibration should be a monotonically increasing 
09834                 //function. If the detivative is negative, we are outside of
09835                 //the wavelength solution domain.
09836                 if(pixel_per_lambda <= 0) 
09837                     vi = 0;
09838                 else if (fpixel < 0)
09839                     vi = 0;
09840                 else if (pixel >= 1 && pixel < nx-2) {
09841                     v0 = (sdata + i*nx)[pixel-1];
09842                     v1 = (sdata + i*nx)[pixel];
09843                     v2 = (sdata + i*nx)[pixel+1];
09844                     v3 = (sdata + i*nx)[pixel+2];
09845                     vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09846                                       + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09847                                       + 2*v1;
09848                     vi /= 2;
09849                     if (v1 > v2) {
09850                         if (vi > v1) { 
09851                             vi = v1;
09852                         }
09853                         else if (vi < v2) {
09854                             vi = v2;
09855                         }
09856                     }
09857                     else {
09858                         if (vi > v2) { 
09859                             vi = v2;
09860                         }
09861                         else if (vi < v1) {
09862                             vi = v1;
09863                         }
09864                     }
09865                     if (flux)
09866                         vi *= dispersion * pixel_per_lambda;
09867                 }
09868                 else if (pixel >= 0 && pixel < nx-1) {
09869                     v1 = (sdata + i*nx)[pixel];
09870                     v2 = (sdata + i*nx)[pixel+1];
09871                     vi = v1 + (v2-v1)*(fpixel-pixel);
09872                     if (flux)
09873                         vi *= dispersion * pixel_per_lambda;
09874                 }
09875                 else
09876                     vi = 0;
09877                 (rdata + i*nl)[j] = vi;
09878             }
09879             else {
09880 
09881                 /*
09882                  * Here instead we integrate the pixel values in
09883                  * the interval centered at the interpolation point.
09884                  * This interval is long dispersion * pixel_per_lambda
09885                  * of the original pixels, and is centered at fpixel.
09886                  * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
09887                  * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
09888                  */
09889 
09890                 double spos = fpixel - dispersion * pixel_per_lambda / 2;
09891                 double epos = fpixel + dispersion * pixel_per_lambda / 2;
09892 
09893                 /*
09894                  * Brutal sum over all involved pixels
09895                  */
09896 
09897                 int spix = spos;
09898                 int epix = epos + 1;
09899 
09900                 if (spix < 0)
09901                     spix = 0;
09902 
09903                 if (epix > nx)
09904                     epix = nx;
09905 
09906                 vi = 0.0;
09907                 for (k = spix; k < epix; k++) {
09908                     if (pixel >= 0 && pixel < nx) {
09909                         vi += (sdata + i*nx)[k];
09910                     }
09911                 }
09912 
09913                 /*
09914                  * Correct integrated flux by true length
09915                  * of interval. This is clearly an approximation,
09916                  * but it's good enough if the PSF is much larger
09917                  * than the original pix.
09918                  */
09919 
09920                 vi *= dispersion * pixel_per_lambda / (epix - spix);
09921 
09922                 /*
09923                  * Flux conservation is a geometric factor that is applied
09924                  * always in the same way...
09925                  */
09926 
09927                 if (flux)
09928                     vi *= dispersion * pixel_per_lambda;
09929 
09930                 (rdata + i*nl)[j] = vi;
09931             }
09932         }
09933 
09934         cpl_polynomial_delete(ids);
09935     }
09936 
09937     return resampled;
09938 }
09939 
09940 
10007 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
10008                                 double refwave, double firstLambda, 
10009                                 double lastLambda, cpl_table *idscoeff,
10010                                 cpl_vector *skylines, int highres, int order,
10011                                 cpl_image *calibration, int sradius)
10012 {
10013     const char *func = "mos_wavelength_align";
10014 
10015     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10016                                                  /* Max order is 5 */
10017     double         *line;
10018     double         *data;
10019     double          expPos, offset;
10020     double          c;
10021     double          lambda1, lambda2;
10022     double          rms;
10023     float           pos;
10024     float          *sdata;
10025     float          *cdata;
10026     int            *idata;
10027     int             startPos, endPos;
10028     int             window = 2*sradius + 1;
10029     int             nlines;
10030     int             nslits;
10031     int             npoints;
10032     int             nrows;
10033     int             nx, ny;
10034     int             xlow, ylow, xhig, yhig;
10035     int             idsorder, uorder;
10036     int            *slit_id;
10037     int            *position;
10038     int            *length;
10039     int             missing;
10040     int             null;
10041     int             i;
10042     cpl_size        j, k;
10043 
10044     char            offname[MAX_COLNAME];
10045     char            name[MAX_COLNAME];
10046 
10047     cpl_polynomial *ids;
10048     cpl_polynomial *polycorr;
10049     cpl_image      *exslit;
10050     cpl_image      *sky;
10051     cpl_table      *offsets;
10052     cpl_table      *dummy;
10053     cpl_vector     *wave;
10054     cpl_vector     *offs;
10055     
10056 
10057     if (idscoeff == NULL) {
10058         cpl_msg_error(func, "An IDS coeff table must be given");
10059         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10060         return NULL;
10061     }
10062 
10063     if (image == NULL) {
10064         cpl_msg_error(func, "A scientific spectral image must be given");
10065         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10066         return NULL;
10067     }
10068 
10069     if (slits == NULL) {
10070         cpl_msg_error(func, "A slit position table must be given");
10071         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10072         return NULL;
10073     }
10074 
10075     if (skylines) {
10076         line = cpl_vector_get_data(skylines);
10077         nlines = cpl_vector_get_size(skylines);
10078     }
10079     else {
10080         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10081                         "given: using internal list of reference sky lines");
10082         if (highres) {
10083            line = default_lines_hi;
10084            nlines = sizeof(default_lines_hi) / sizeof(double);
10085         }
10086         else {
10087            line = default_lines_lo;
10088            nlines = sizeof(default_lines_lo) / sizeof(double);
10089         }
10090     }
10091 
10092     if (calibration)
10093         cdata = cpl_image_get_data(calibration);
10094 
10095     nx = cpl_image_get_size_x(image);
10096     ny = cpl_image_get_size_y(image);
10097 
10098     nslits   = cpl_table_get_nrow(slits);
10099     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10100     position = cpl_table_get_data_int(slits, "position");
10101     length   = cpl_table_get_data_int(slits, "length");
10102 
10103 
10104     /*
10105      * Define the output table of offsets
10106      */
10107 
10108     nrows = 0;
10109     for (i = 0; i < nlines; i++)
10110         if (line[i] > firstLambda && line[i] < lastLambda)
10111             nrows++;
10112 
10113     offsets = cpl_table_new(nrows);
10114     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10115     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10116 
10117     nrows = 0;
10118     for (i = 0; i < nlines; i++) {
10119         if (line[i] > firstLambda && line[i] < lastLambda) {
10120             cpl_table_set_double(offsets, "wave", nrows, line[i]);
10121             nrows++;
10122         }
10123     }
10124 
10125     /*
10126      * Here "line" is made to point to the new list of selected wavelengths
10127      */
10128 
10129     line = cpl_table_get_data_double(offsets, "wave");
10130     nlines = nrows;
10131 
10132     idsorder = 0;
10133     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10134         ++idsorder;
10135     --idsorder;
10136 
10137     xlow = 1;
10138     xhig = nx;
10139     for (i = 0; i < nslits; i++) {
10140 
10141         if (length[i] == 0)
10142             continue;
10143 
10144         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10145         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
10146 
10147         /* 
10148          * Define the extraction boundaries. We DON'T write:
10149          *
10150          * ylow = position[i];
10151          * yhig = ylow + length[i];
10152          *
10153          * because the cpl_image pixels are counted from 1, and because in
10154          * cpl_image_extract() the coordinates of the last pixel are inclusive.
10155          */
10156 
10157         ylow = position[i] + 1;
10158         yhig = ylow + length[i] - 1;
10159 
10160         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
10161         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
10162         sdata  = cpl_image_get_data(sky);
10163 
10164         cpl_image_delete(exslit);
10165 
10166         /* 
10167          * Return here to a decent way of counting pixels (i.e., starting
10168          * from 0)
10169          */
10170          
10171         ylow--;
10172 
10173         /*
10174          * Allocate a dummy table for collecting all the offsets
10175          * for all the lines: this is only needed for the computation
10176          * of the median offset for each sky line
10177          */
10178 
10179         dummy = cpl_table_new(yhig - ylow);
10180         for (j = 0; j < nlines; j++) {
10181             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10182             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10183         }
10184 
10185         for (j = ylow; j < yhig; j++) {
10186 
10187             /*
10188              * Get the IDS polynomial for the current slit row
10189              */
10190 
10191             missing = 0;
10192             ids = cpl_polynomial_new(1);
10193             for (k = 0; k <= idsorder; k++) {
10194                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
10195                 if (null) {
10196                     cpl_polynomial_delete(ids);
10197                     missing = 1;
10198                     break;
10199                 }
10200                 cpl_polynomial_set_coeff(ids, &k, c);
10201             }
10202             if (missing)
10203                 continue;
10204 
10205             for (k = 0; k < nlines; k++) {
10206                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10207                 startPos = expPos - sradius;
10208                 endPos   = startPos + window;
10209                 if (startPos < 0 || endPos >= nx)
10210                     continue;
10211            
10212                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10213                     pos += startPos;
10214                     offset = pos - expPos;
10215                     snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
10216                     cpl_table_set_double(dummy, name, j - ylow, offset);
10217                 }
10218             }
10219 
10220             cpl_polynomial_delete(ids);
10221         }
10222 
10223         cpl_image_delete(sky);
10224 
10225         for (j = 0; j < nlines; j++) {
10226             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
10227             if (cpl_table_has_valid(dummy, name)) {
10228                 offset = cpl_table_get_column_median(dummy, name);
10229                 cpl_table_set_double(offsets, offname, j, offset);
10230             }
10231         }
10232 
10233         cpl_table_delete(dummy);
10234 
10235     }
10236 
10237 
10238     /*
10239      * In the following the input idscoeff table is modified by simply
10240      * adding the coefficients of the polynomial used to fit the sky
10241      * line residuals to the coefficients of the IDS polynomials.
10242      */
10243 
10244     for (i = 0; i < nslits; i++) {
10245 
10246         if (length[i] == 0)
10247             continue;
10248 
10249         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
10250 
10251         /*
10252          * In the following, the "dummy" table is just a tool for
10253          * eliminating invalid points from the vectors to be fitted.
10254          */
10255 
10256         dummy = cpl_table_new(nlines);
10257         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
10258         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
10259 
10260         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
10261         if (npoints == 0) {
10262             cpl_msg_warning(func, "No sky lines alignment was possible "
10263                             "for slit ID=%d: no sky line found", slit_id[i]);
10264             cpl_table_delete(dummy);
10265             continue;
10266         }
10267 
10268         uorder = order;
10269         if (npoints <= uorder) {
10270             uorder = npoints - 1;
10271             if (uorder) {
10272                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
10273                                 "ID=%d, while a polynomial order %d was "
10274                                 "requested. Using polynomial order %d for "
10275                                 "this slit!", npoints, slit_id[i], order, 
10276                                 uorder);
10277             }
10278             else {
10279                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
10280                                 "ID=%d, while a polynomial order %d was "
10281                                 "requested. Computing a median offset for "
10282                                 "this slit!", npoints, slit_id[i], order);
10283             }
10284         }
10285 
10286         cpl_table_erase_invalid(dummy);
10287 
10288         if (uorder > 1) {
10289 
10290             /*
10291              * Model offsets with polynomial fitting
10292              */
10293 
10294             wave = cpl_vector_wrap(npoints,
10295                                    cpl_table_get_data_double(dummy, "wave"));
10296             offs = cpl_vector_wrap(npoints,
10297                                    cpl_table_get_data_double(dummy, "offset"));
10298 
10299             /*
10300              * Set reference wavelength as zero point
10301              */
10302 
10303             cpl_vector_subtract_scalar(wave, refwave);
10304 
10305             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10306 
10307             rms = sqrt(rms * (uorder + 1) / npoints);
10308 
10309             cpl_vector_unwrap(wave);
10310             cpl_vector_unwrap(offs);
10311             cpl_table_delete(dummy);
10312 
10313             /*
10314              * Now correct the coefficients of the corresponding IDS
10315              * polynomials related to this slit:
10316              */
10317 
10318             ylow = position[i];
10319             yhig = ylow + length[i];
10320 
10321             for (j = 0; j <= uorder; j++) {
10322                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10323                 c = cpl_polynomial_get_coeff(polycorr, &j);
10324                 for (k = ylow; k < yhig; k++)
10325                     data[k] += c;
10326             }
10327 
10328             data = cpl_table_get_data_double(idscoeff, "error");
10329             for (k = ylow; k < yhig; k++)
10330                  data[k] = sqrt(data[k]*data[k] + rms*rms);
10331 
10332             idata = cpl_table_get_data_int(idscoeff, "nlines");
10333             for (k = ylow; k < yhig; k++)
10334                  idata[k] = npoints;
10335 
10336             /*
10337              * If a wavelengths map was provided, correct it to keep
10338              * into account the alignment to skylines:
10339              */
10340 
10341             if (calibration) {
10342                 for (j = ylow; j < yhig; j++) {
10343                     for (k = 1; k < nx; k++) {
10344                         lambda1 = cdata[k - 1 + j*nx];
10345                         lambda2 = cdata[k + j*nx];
10346                         if (lambda1 < 1.0 || lambda2 < 1.0)
10347                             continue;
10348                         offset = cpl_polynomial_eval_1d(polycorr, 
10349                                                         lambda1-refwave, NULL);
10350                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10351                     }
10352                 }
10353             }
10354     
10355             cpl_polynomial_delete(polycorr);
10356         }
10357         else if (uorder == 1) {
10358 
10359             /*
10360              * Model offsets with robust linear fitting
10361              */
10362 
10363             double        q, m;
10364             cpl_bivector *list;
10365 
10366 
10367             wave = cpl_vector_wrap(npoints,
10368                                    cpl_table_get_data_double(dummy, "wave"));
10369             offs = cpl_vector_wrap(npoints,
10370                                    cpl_table_get_data_double(dummy, "offset"));
10371 
10372             list = cpl_bivector_wrap_vectors(wave, offs);
10373 
10374             /*
10375              * Set reference wavelength as zero point
10376              */
10377 
10378             cpl_vector_subtract_scalar(wave, refwave);
10379 
10380             robustLinearFit(list, &q, &m, &rms);
10381 
10382             rms = sqrt(rms * (uorder + 1) / npoints);
10383 
10384             cpl_bivector_unwrap_vectors(list);
10385             cpl_vector_unwrap(wave);
10386             cpl_vector_unwrap(offs);
10387             cpl_table_delete(dummy);
10388 
10389             /*
10390              * Now correct the coefficients of the corresponding IDS
10391              * polynomials related to this slit:
10392              */
10393 
10394             ylow = position[i];
10395             yhig = ylow + length[i];
10396 
10397             for (j = 0; j <= uorder; j++) {
10398                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10399                 if (j)
10400                     c = m;
10401                 else
10402                     c = q;
10403                 for (k = ylow; k < yhig; k++)
10404                     data[k] += c;
10405             }
10406 
10407             data = cpl_table_get_data_double(idscoeff, "error");
10408             for (k = ylow; k < yhig; k++)
10409                  data[k] = sqrt(data[k]*data[k] + rms*rms);
10410 
10411             idata = cpl_table_get_data_int(idscoeff, "nlines");
10412             for (k = ylow; k < yhig; k++)
10413                  idata[k] = npoints;
10414 
10415             /*
10416              * If a wavelengths map was provided, correct it to keep
10417              * into account the alignment to skylines:
10418              */
10419 
10420             if (calibration) {
10421                 for (j = ylow; j < yhig; j++) {
10422                     for (k = 1; k < nx; k++) {
10423                         lambda1 = cdata[k - 1 + j*nx];
10424                         lambda2 = cdata[k + j*nx];
10425                         if (lambda1 < 1.0 || lambda2 < 1.0)
10426                             continue;
10427                         offset = q + m*(lambda1-refwave);
10428                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10429                     }
10430                 }
10431             }
10432         }
10433         else {
10434 
10435             /*
10436              * Just compute median offset
10437              */
10438 
10439             offs = cpl_vector_wrap(npoints,
10440                                    cpl_table_get_data_double(dummy, "offset"));
10441 
10442             offset = cpl_vector_get_median_const(offs);
10443 
10444             if (npoints > 1)
10445                 rms = cpl_table_get_column_stdev(dummy, "offset");
10446             else
10447                 rms = 0.0;
10448 
10449             rms /= sqrt(npoints);
10450 
10451             cpl_vector_unwrap(offs);
10452             cpl_table_delete(dummy);
10453 
10454             /*
10455              * Now correct the constant term of the corresponding IDS
10456              * polynomials related to this slit:
10457              */
10458 
10459             ylow = position[i];
10460             yhig = ylow + length[i];
10461 
10462             data = cpl_table_get_data_double(idscoeff, clab[0]);
10463             for (k = ylow; k < yhig; k++)
10464                 data[k] += offset;
10465 
10466             data = cpl_table_get_data_double(idscoeff, "error");
10467             for (k = ylow; k < yhig; k++)
10468                  data[k] = sqrt(data[k]*data[k] + rms*rms);
10469 
10470             idata = cpl_table_get_data_int(idscoeff, "nlines");
10471             for (k = ylow; k < yhig; k++)
10472                  idata[k] = npoints;
10473 
10474             /*
10475              * If a wavelengths map was provided, correct it to keep
10476              * into account the alignment to skylines. Note that 
10477              * the offset must be converted from pixels to wavelengths.
10478              */
10479 
10480             if (calibration) {
10481                 for (j = ylow; j < yhig; j++) {
10482                     for (k = 1; k < nx; k++) {
10483                         lambda1 = cdata[k - 1 + j*nx];
10484                         lambda2 = cdata[k + j*nx];
10485                         if (lambda1 < 1.0 || lambda2 < 1.0)
10486                             continue; 
10487                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
10488                     }
10489                 }
10490             }
10491         }
10492     }
10493 
10494     return offsets;
10495 
10496 }
10497 
10498 
10560 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
10561                                     double firstLambda, double lastLambda, 
10562                                     cpl_table *idscoeff, cpl_vector *skylines, 
10563                                     int highres, int order, 
10564                                     cpl_image *calibration, int sradius)
10565 {
10566     const char *func = "mos_wavelength_align_lss";
10567 
10568     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10569                                                  /* Max order is 5 */
10570     double         *line;
10571     double         *data;
10572     double         *wdata;
10573     double         *odata;
10574     double          expPos, offset;
10575     double          c;
10576     double          lambda1, lambda2;
10577     double          rms;
10578     float           pos;
10579     float          *sdata;
10580     float          *cdata;
10581     int            *idata;
10582     int             startPos, endPos;
10583     int             window = 2*sradius + 1;
10584     int             nlines;
10585     int             npoints;
10586     int             nrows;
10587     int             nx, ny;
10588     int             idsorder, uorder;
10589     int             missing;
10590     int             i;
10591     cpl_size        j, k;
10592 
10593     char            name[MAX_COLNAME];
10594     char            fname[MAX_COLNAME];
10595 
10596     cpl_polynomial *ids;
10597     cpl_polynomial *polycorr;
10598     cpl_table      *offsets;
10599     cpl_table      *fittable;
10600     cpl_table      *dummy;
10601     cpl_vector     *wave;
10602     cpl_vector     *offs;
10603     cpl_vector     *row;
10604     
10605 
10606     if (idscoeff == NULL) {
10607         cpl_msg_error(func, "An IDS coeff table must be given");
10608         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10609         return NULL;
10610     }
10611 
10612     if (image == NULL) {
10613         cpl_msg_error(func, "A scientific spectral image must be given");
10614         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10615         return NULL;
10616     }
10617 
10618     if (skylines) {
10619         line = cpl_vector_get_data(skylines);
10620         nlines = cpl_vector_get_size(skylines);
10621     }
10622     else {
10623         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10624                         "given: using internal list of reference sky lines");
10625         if (highres) {
10626            line = default_lines_hi;
10627            nlines = sizeof(default_lines_hi) / sizeof(double);
10628         }
10629         else {
10630            line = default_lines_lo;
10631            nlines = sizeof(default_lines_lo) / sizeof(double);
10632         }
10633     }
10634 
10635     if (calibration)
10636         cdata = cpl_image_get_data(calibration);
10637 
10638     nx = cpl_image_get_size_x(image);
10639     ny = cpl_image_get_size_y(image);
10640 
10641     sdata = cpl_image_get_data(image);
10642 
10643     if (ny != cpl_table_get_nrow(idscoeff)) {
10644         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10645         return NULL;
10646     }
10647     
10648 
10649     /*FIXME: This is a remnant of the adaptation of the function
10650      * mos_wavelength_align(), where an offset table was created.
10651      * I leave it here because I am in a hurry, it is just used to
10652      * hold the list of selected sky lines.
10653      *
10654      * Define table of wavelengths
10655      */
10656 
10657     nrows = 0;
10658     for (i = 0; i < nlines; i++)
10659         if (line[i] > firstLambda && line[i] < lastLambda)
10660             nrows++;
10661 
10662     offsets = cpl_table_new(nrows);
10663     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10664     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10665 
10666     nrows = 0;
10667     for (i = 0; i < nlines; i++) {
10668         if (line[i] > firstLambda && line[i] < lastLambda) {
10669             cpl_table_set_double(offsets, "wave", nrows, line[i]);
10670             nrows++;
10671         }
10672     }
10673 
10674     /*
10675      * Here "line" is made to point to the new list of selected wavelengths
10676      */
10677 
10678     line = cpl_table_get_data_double(offsets, "wave");
10679     nlines = nrows;
10680 
10681     idsorder = 0;
10682     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10683         ++idsorder;
10684     --idsorder;
10685 
10686 
10687     /*
10688      * Allocate a dummy table for collecting all the offsets
10689      * for all the lines
10690      */
10691 
10692     dummy = cpl_table_new(ny);
10693     for (j = 0; j < nlines; j++) {
10694         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10695         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10696         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10697         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10698     }
10699 
10700     for (j = 0; j < ny; j++, sdata += nx) {
10701 
10702         /*
10703          * Get the IDS polynomial for the current slit row
10704          */
10705 
10706         missing = 0;
10707         ids = cpl_polynomial_new(1);
10708         for (k = 0; k <= idsorder; k++) {
10709             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10710             if (missing) {
10711                 cpl_polynomial_delete(ids);
10712                 break;
10713             }
10714             cpl_polynomial_set_coeff(ids, &k, c);
10715         }
10716         if (missing)
10717             continue;
10718 
10719         for (k = 0; k < nlines; k++) {
10720             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10721             startPos = expPos - sradius;
10722             endPos   = startPos + window;
10723             if (startPos < 0 || endPos >= nx)
10724                 continue;
10725            
10726             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10727                 pos += startPos;
10728                 offset = pos - expPos;
10729                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10730                 cpl_table_set_double(dummy, name, j, offset);
10731             }
10732         }
10733 
10734         cpl_polynomial_delete(ids);
10735     }
10736 
10737 
10738     /*
10739      * At this point for each sky line we model its offset along
10740      * the image rows using a robust linear fitting
10741      */
10742 
10743     for (j = 0; j < nlines; j++) {
10744         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10745         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10746         if (cpl_table_has_valid(dummy, name)) {
10747 
10748             /*
10749              * In the following, the "fittable" is just a tool for
10750              * eliminating invalid points from the vectors to be fitted.
10751              */
10752 
10753             double        q, m;
10754             cpl_bivector *list;
10755 
10756             fittable = cpl_table_new(ny);
10757             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10758             cpl_table_set_column_unit(fittable, "row", "pixel");
10759             for (k = 0; k < ny; k++)
10760                  cpl_table_set_double(fittable, "row", k, k);
10761             cpl_table_duplicate_column(fittable, "offset", dummy, name);
10762             npoints = ny - cpl_table_count_invalid(fittable, "offset");
10763             cpl_table_erase_invalid(fittable);
10764             row = cpl_vector_wrap(npoints,
10765                                cpl_table_get_data_double(fittable, "row"));
10766             offs = cpl_vector_wrap(npoints,
10767                                cpl_table_get_data_double(fittable, "offset"));
10768             list = cpl_bivector_wrap_vectors(row, offs);
10769             robustLinearFit(list, &q, &m, &rms);
10770             cpl_bivector_unwrap_vectors(list);
10771             cpl_vector_unwrap(row);
10772             cpl_vector_unwrap(offs);
10773             cpl_table_delete(fittable);
10774             for (k = 0; k < ny; k++)
10775                  cpl_table_set_double(dummy, fname, k, q + m*k);
10776         }
10777     }
10778 
10779 
10780     /*
10781      * Now each dummy table row consists of a sequence of offsets,
10782      * one for each wavelength. A table row corresponds to an image row.
10783      * We must fit a polynomial to each one of these rows, in order to
10784      * express the offsets as a function of wavelength. The obtained 
10785      * polynomial coefficients are used to correct the IDS coefficients.
10786      */
10787 
10788     for (i = 0; i < ny; i++) {
10789 
10790         if (!cpl_table_is_valid(idscoeff, clab[0], i))
10791             continue;
10792 
10793         npoints = 0;
10794         for (j = 0; j < nlines; j++) {
10795             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10796             if (cpl_table_is_valid(dummy, name, i))
10797                 npoints++;
10798         }
10799 
10800         if (npoints == 0)
10801             continue;
10802 
10803         uorder = order;
10804         if (npoints <= uorder)
10805             uorder = npoints - 1;
10806 
10807         if (uorder > 1) {
10808 
10809             /*
10810              * Model offsets with polynomial fitting
10811              */
10812 
10813             wave = cpl_vector_new(npoints);
10814             wdata = cpl_vector_get_data(wave);
10815             offs = cpl_vector_new(npoints);
10816             odata = cpl_vector_get_data(offs);
10817 
10818             npoints = 0;
10819             for (j = 0; j < nlines; j++) {
10820                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10821                 if (cpl_table_is_valid(dummy, name, i)) {
10822                     wdata[npoints] = line[j] - refwave;
10823                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10824                     npoints++;
10825                 }
10826             }
10827 
10828             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10829 
10830             rms = sqrt(rms * (uorder + 1) / npoints);
10831 
10832             cpl_vector_delete(wave);
10833             cpl_vector_delete(offs);
10834 
10835             /*
10836              * Now correct the coefficients of the corresponding IDS
10837              * polynomials related to this slit:
10838              */
10839 
10840             for (j = 0; j <= uorder; j++) {
10841                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10842                 c = cpl_polynomial_get_coeff(polycorr, &j);
10843                 data[i] += c;
10844             }
10845 
10846             data = cpl_table_get_data_double(idscoeff, "error");
10847             data[i] = sqrt(data[i]*data[i] + rms*rms);
10848 
10849             idata = cpl_table_get_data_int(idscoeff, "nlines");
10850             idata[i] = npoints;
10851 
10852             /*
10853              * If a wavelengths map was provided, correct it to keep
10854              * into account the alignment to skylines:
10855              */
10856 
10857             if (calibration) {
10858                 for (k = 1; k < nx; k++) {
10859                     lambda1 = cdata[k - 1 + i*nx];
10860                     lambda2 = cdata[k + i*nx];
10861                     if (lambda1 < 1.0 || lambda2 < 1.0)
10862                         continue;
10863                     offset = cpl_polynomial_eval_1d(polycorr,
10864                                                     lambda1-refwave, NULL);
10865                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10866                 }
10867             }
10868 
10869             cpl_polynomial_delete(polycorr);
10870 
10871         }
10872         else if (uorder == 1) {
10873 
10874             /*
10875              * Model offsets with robust linear fitting
10876              */
10877 
10878             cpl_bivector *list;
10879             double        q, m;
10880 
10881             wave = cpl_vector_new(npoints);
10882             wdata = cpl_vector_get_data(wave);
10883             offs = cpl_vector_new(npoints);
10884             odata = cpl_vector_get_data(offs);
10885 
10886             npoints = 0;
10887             for (j = 0; j < nlines; j++) {
10888                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10889                 if (cpl_table_is_valid(dummy, name, i)) {
10890                     wdata[npoints] = line[j] - refwave;
10891                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10892                     npoints++;
10893                 }
10894             }
10895 
10896             list = cpl_bivector_wrap_vectors(wave, offs);
10897             robustLinearFit(list, &q, &m, &rms);
10898 
10899             rms = sqrt(rms * (uorder + 1) / npoints);
10900 
10901             cpl_bivector_unwrap_vectors(list);
10902             cpl_vector_delete(wave);
10903             cpl_vector_delete(offs);
10904 
10905             /*
10906              * Now correct the coefficients of the corresponding IDS
10907              * polynomials related to this row:
10908              */
10909 
10910             for (j = 0; j <= uorder; j++) {
10911                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10912                 if (j)
10913                     c = m;
10914                 else
10915                     c = q;
10916                 data[i] += c;
10917             }
10918 
10919             data = cpl_table_get_data_double(idscoeff, "error");
10920             data[i] = sqrt(data[i]*data[i] + rms*rms);
10921 
10922             idata = cpl_table_get_data_int(idscoeff, "nlines");
10923             idata[i] = npoints;
10924 
10925             /*
10926              * If a wavelengths map was provided, correct it to keep
10927              * into account the alignment to skylines:
10928              */
10929 
10930             if (calibration) {
10931                 for (k = 1; k < nx; k++) {
10932                     lambda1 = cdata[k - 1 + i*nx];
10933                     lambda2 = cdata[k + i*nx];
10934                     if (lambda1 < 1.0 || lambda2 < 1.0)
10935                         continue;
10936                     offset = q + m*(lambda1-refwave);
10937                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10938                 }
10939             }
10940         }
10941         else {
10942 
10943             /*
10944              * Just compute median offset
10945              */
10946 
10947             offs = cpl_vector_new(npoints);
10948             odata = cpl_vector_get_data(offs);
10949 
10950             npoints = 0;
10951             for (j = 0; j < nlines; j++) {
10952                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10953                 if (cpl_table_is_valid(dummy, name, i)) {
10954                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10955                     npoints++;
10956                 }
10957             }
10958 
10959             offset = cpl_vector_get_median_const(offs);
10960 
10961             if (npoints > 1) {
10962                 rms = cpl_vector_get_stdev(offs);
10963             }
10964             else if (npoints == 1) {
10965                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10966                 if (cpl_table_has_valid(dummy, name)) {
10967                     rms = cpl_table_get_column_stdev(dummy, name);
10968                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10969                 }
10970                 else {
10971                     rms = 0.0;
10972                 }
10973             }
10974             else {
10975                 rms = 0.0;
10976             }
10977 
10978             rms /= sqrt(npoints);
10979 
10980             cpl_vector_delete(offs);
10981 
10982             /*
10983              * Now correct the constant term of the corresponding IDS
10984              * polynomials related to this slit:
10985              */
10986 
10987             data = cpl_table_get_data_double(idscoeff, clab[0]);
10988             data[i] += offset;
10989 
10990             data = cpl_table_get_data_double(idscoeff, "error");
10991             data[i] = sqrt(data[i]*data[i] + rms*rms);
10992 
10993             idata = cpl_table_get_data_int(idscoeff, "nlines");
10994             idata[i] = npoints;
10995 
10996             /*
10997              * If a wavelengths map was provided, correct it to keep
10998              * into account the alignment to skylines. Note that
10999              * the offset must be converted from pixels to wavelengths.
11000              */
11001 
11002             if (calibration) {
11003                 for (k = 1; k < nx; k++) {
11004                     lambda1 = cdata[k - 1 + i*nx];
11005                     lambda2 = cdata[k + i*nx];
11006                     if (lambda1 < 1.0 || lambda2 < 1.0)
11007                         continue;
11008                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
11009                 }
11010             }
11011         }
11012     }
11013 
11014     missing = 1;
11015     for (j = 0; j < nlines; j++) {
11016         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
11017         if (cpl_table_has_valid(dummy, name)) {
11018             missing = 0;
11019             offset = cpl_table_get_column_median(dummy, name);
11020             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
11021                          line[j], offset);
11022         }
11023         else {
11024             cpl_msg_info(func, 
11025                          "Median offset for %.2f: not available", line[j]);
11026         }
11027     }
11028 
11029     cpl_table_delete(offsets);
11030 
11031     if (missing) {
11032         cpl_table_delete(dummy);
11033         dummy = NULL;
11034     }
11035 
11036     return dummy;
11037 
11038 }
11039 
11040 
11068 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
11069                            double wavestart, double dispersion, int radius,
11070                            int highres)
11071 {
11072 
11073     const char *func = "mos_distortions_rms";
11074 
11075     int xlen;
11076     int ylen;
11077     int numLines;
11078     int cpix, npix, nzero;
11079     int sp, ep;
11080     int i, j, k;
11081     int npeaks, allPeaks;
11082 
11083     float *profile;
11084     float  peak, expectPeak, offset;
11085     double lambda;
11086 
11087     double  average;
11088     double  rms, oneRms;
11089 
11090     float  *sdata;
11091     double *wdata;
11092 
11093   
11094     xlen = cpl_image_get_size_x(rectified);
11095     ylen = cpl_image_get_size_y(rectified);
11096     sdata = cpl_image_get_data(rectified);
11097 
11098     if (lines) {
11099         wdata = cpl_vector_get_data(lines);
11100         numLines = cpl_vector_get_size(lines);
11101     }
11102     else {
11103         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
11104                         "given: using internal list of reference sky lines");
11105         if (highres) {
11106            wdata = default_lines_hi;
11107            numLines = sizeof(default_lines_hi) / sizeof(double);
11108         }
11109         else {
11110            wdata = default_lines_lo;
11111            numLines = sizeof(default_lines_lo) / sizeof(double);
11112         }
11113     }
11114 
11115     npix = 2 * radius + 1;
11116     profile = cpl_calloc(npix, sizeof(float));
11117 
11118     rms = 0.0;
11119     allPeaks = 0;
11120 
11121     for (i = 0; i < numLines; i++) {
11122 
11123         /*
11124          *  Expected peak and closest pixel to specified wavelength.
11125          */
11126 
11127         lambda = wdata[i];
11128         expectPeak = (lambda - wavestart) / dispersion;
11129         cpix = floor(expectPeak + 0.5);
11130 
11131         /*
11132          *  Search interval for peak. Abort if too close to image border.
11133          */
11134 
11135         sp = cpix - radius;
11136         ep = cpix + radius;
11137 
11138         if (sp < 0 || ep > xlen)
11139             continue;
11140 
11141         average = 0.0;
11142         npeaks = 0;
11143         oneRms = 0.0;
11144 
11145         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
11146             nzero = 0;
11147             for (k = 0; k < npix; k++) {
11148                 profile[k] = sdata[sp + k + j * xlen];
11149                 if (fabs(profile[k]) < 0.0001)
11150                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
11151             }
11152             if (nzero > 0)
11153                 continue;
11154 
11155             if (peakPosition(profile, npix, &peak, 1) == 0) {
11156                 offset = (sp + peak) - expectPeak;
11157                 average += offset;
11158                 rms += fabs(offset);
11159                 oneRms += fabs(offset);
11160                 npeaks++;
11161                 allPeaks++;
11162             }
11163         }
11164 
11165         if (npeaks)
11166             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
11167                          lambda, oneRms / npeaks * 1.25, npeaks);
11168         else
11169             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
11170     }
11171 
11172     cpl_free(profile);
11173 
11174     if (allPeaks < 10)
11175         return 0.0;
11176 
11177     rms /= allPeaks;
11178     rms *= 1.25;       /* Factor to convert average deviation to sigma */
11179 
11180     return rms;
11181 
11182 }
11183 
11184 
11205 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
11206                          double blue, double red, double dispersion, int trend)
11207 {
11208     const char *func = "mos_map_pixel";
11209 
11210     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11211                                                  /* Max order is 5 */
11212 
11213     cpl_polynomial *ids;
11214     cpl_image      *map;
11215     float          *mdata;
11216     double          lambda;
11217     double          c;
11218     int             order;
11219     int             xsize, ysize;
11220     int             missing;
11221     int             i, j;
11222     cpl_size        k;
11223 
11224 
11225     if (idscoeff == NULL) {
11226         cpl_msg_error(func, "An IDS coeff table must be given");
11227         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11228         return NULL;
11229     }
11230 
11231     xsize = (red - blue) / dispersion;
11232     ysize = cpl_table_get_nrow(idscoeff);
11233     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11234     mdata = cpl_image_get_data(map);
11235 
11236     order = 0;
11237     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11238         ++order;
11239     --order;
11240 
11241     for (i = 0; i < ysize; i++, mdata += xsize) {
11242 
11243         missing = 0;
11244         ids = cpl_polynomial_new(1);
11245         for (k = trend; k <= order; k++) {
11246             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11247             if (missing) {
11248                 cpl_polynomial_delete(ids);
11249                 break;
11250             }
11251             cpl_polynomial_set_coeff(ids, &k, c);
11252         }
11253         if (missing)
11254             continue;
11255 
11256         for (j = 0; j < xsize; j++) {
11257             lambda = blue + j*dispersion;
11258             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
11259         }
11260 
11261         cpl_polynomial_delete(ids);
11262     }
11263 
11264     return map;
11265 
11266 }
11267 
11268 
11290 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
11291                             double blue, double red)
11292 {
11293     const char *func = "mos_map_idscoeff";
11294 
11295     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11296                                                  /* Max order is 5 */
11297 
11298     cpl_polynomial *ids;
11299     cpl_image      *map;
11300     float          *mdata;
11301     double          lambda;
11302     double          c;
11303     int             order;
11304     int             ysize;
11305     int             missing;
11306     int             i, j;
11307     cpl_size        k;
11308 
11309 
11310     if (idscoeff == NULL) {
11311         cpl_msg_error(func, "An IDS coeff table must be given");
11312         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11313         return NULL;
11314     }
11315 
11316     if (xsize < 1) {
11317         cpl_msg_error(func, "Invalid image size");
11318         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11319         return NULL;
11320     }
11321 
11322     if (xsize < 20 || xsize > 5000) {
11323         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
11324                         xsize);
11325     }
11326 
11327     ysize = cpl_table_get_nrow(idscoeff);
11328     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
11329     mdata = cpl_image_get_data(map);
11330 
11331     order = 0;
11332     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
11333         ++order;
11334     --order;
11335 
11336     for (i = 0; i < ysize; i++, mdata += xsize) {
11337 
11338         missing = 0;
11339         ids = cpl_polynomial_new(1);
11340         for (k = 0; k <= order; k++) {
11341             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
11342             if (missing) {
11343                 cpl_polynomial_delete(ids);
11344                 break;
11345             }
11346             cpl_polynomial_set_coeff(ids, &k, c);
11347         }
11348         if (missing)
11349             continue;
11350 
11351         for (j = 0; j < xsize; j++) {
11352             lambda = mos_eval_dds(ids, blue, red, reference, j);
11353 
11354             if (lambda >= blue && lambda <= red) {
11355                 mdata[j] = lambda;
11356             }
11357         }
11358 
11359         cpl_polynomial_delete(ids);
11360     }
11361 
11362     return map;
11363 
11364 }
11365 
11366 
11401 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
11402                                cpl_table *slits, cpl_table *polytraces, 
11403                                double reference, double blue, double red, 
11404                                double dispersion)
11405 {
11406     const char *func = "mos_map_wavelengths";
11407 
11408     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11409                                                  /* Max order is 5 */
11410     cpl_polynomial *polytop;
11411     cpl_polynomial *polybot;
11412     cpl_image      *remapped;
11413     float          *data;
11414     float          *wdata;
11415     float          *sdata;
11416     float          *xdata;
11417     double          vtop, vbot, value;
11418     double          top, bot;
11419     double          coeff;
11420     double          ytop, ybot;
11421     double          ypos;
11422     double          fvalue;
11423     int             ivalue;
11424     int             yint, ysize, yprev;
11425     int             nslits;
11426     int             npseudo;
11427     int            *slit_id;
11428     int            *position;
11429     int            *length;
11430     int             nx, ny;
11431     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11432     int             missing_top, missing_bot;
11433     int             null;
11434     int             order;
11435     int             i, j;
11436     cpl_size        k;
11437 
11438 
11439     if (spatial == NULL || calibration == NULL || 
11440         slits == NULL || polytraces == NULL) {
11441         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11442         return NULL;
11443     }
11444 
11445     if (dispersion <= 0.0) {
11446         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11447         return NULL;
11448     }
11449 
11450     if (red - blue < dispersion) {
11451         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11452         return NULL;
11453     }
11454 
11455     nx = cpl_image_get_size_x(spatial);
11456     ny = cpl_image_get_size_y(spatial);
11457     ysize = cpl_image_get_size_y(calibration);
11458     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11459     data  = cpl_image_get_data(remapped);
11460     sdata = cpl_image_get_data(spatial);
11461     wdata = cpl_image_get_data(calibration);
11462 
11463     nslits   = cpl_table_get_nrow(slits);
11464     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11465     order    = cpl_table_get_ncol(polytraces) - 2;
11466     position = cpl_table_get_data_int(slits, "position");
11467     length   = cpl_table_get_data_int(slits, "length");
11468 
11469     /*
11470      * The spatial resampling is performed for a certain number of 
11471      * pixels above and below the position of the reference wavelength:
11472      */
11473 
11474     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11475     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11476 
11477     for (i = 0; i < nslits; i++) {
11478 
11479         if (length[i] == 0)
11480             continue;
11481 
11482         /*
11483          * Note that the x coordinate of the reference pixels on the CCD
11484          * is taken arbitrarily at the top end of each slit. This wouldn't
11485          * be entirely correct in case of curved slits, or in presence of
11486          * heavy distortions: in such cases the spatial resampling is
11487          * really performed across a wide range of wavelengths. But
11488          * the lag between top and bottom spectral curvature models 
11489          * would introduce even in such cases negligible effects on
11490          * the spectral spatial resampling.
11491          */
11492 
11493         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11494 
11495         start_pixel = refpixel - pixel_below;
11496         if (start_pixel < 0)
11497             start_pixel = 0;
11498 
11499         end_pixel = refpixel + pixel_above;
11500         if (end_pixel > nx)
11501             end_pixel = nx;
11502 
11503         /*
11504          * Recover from the table of spectral curvature coefficients
11505          * the curvature polynomials.
11506          */
11507 
11508         missing_top = 0;
11509         polytop = cpl_polynomial_new(1);
11510         for (k = 0; k <= order; k++) {
11511             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11512             if (null) {
11513                 cpl_polynomial_delete(polytop);
11514                 missing_top = 1;
11515                 break;
11516             }
11517             cpl_polynomial_set_coeff(polytop, &k, coeff);
11518         }
11519 
11520         missing_bot = 0;
11521         polybot = cpl_polynomial_new(1);
11522         for (k = 0; k <= order; k++) {
11523             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11524             if (null) {
11525                 cpl_polynomial_delete(polybot);
11526                 missing_bot = 1;
11527                 break;
11528             }
11529             cpl_polynomial_set_coeff(polybot, &k, coeff);
11530         }
11531 
11532         if (missing_top && missing_bot) {
11533             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
11534                           slit_id[i]);
11535             continue;
11536         }
11537 
11538         /*
11539          * In case just one of the two edges was not traced, the other
11540          * edge curvature model is duplicated and shifted to the other
11541          * end of the slit: better than nothing!
11542          */
11543 
11544         if (missing_top) {
11545             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11546                           "the spectral curvature of the lower edge "
11547                           "is used instead.", slit_id[i]);
11548             polytop = cpl_polynomial_duplicate(polybot);
11549             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11550             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11551             k = 0;
11552             coeff = cpl_polynomial_get_coeff(polybot, &k);
11553             coeff += ytop - ybot;
11554             cpl_polynomial_set_coeff(polytop, &k, coeff);
11555         }
11556 
11557         if (missing_bot) {
11558             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11559                           "the spectral curvature of the upper edge "
11560                           "is used instead.", slit_id[i]);
11561             polybot = cpl_polynomial_duplicate(polytop);
11562             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11563             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11564             k = 0;
11565             coeff = cpl_polynomial_get_coeff(polytop, &k);
11566             coeff -= ytop - ybot;
11567             cpl_polynomial_set_coeff(polybot, &k, coeff);
11568         }
11569 
11570         /*
11571          * Point to current slit on wavelength calibration image.
11572          * Note that the npseudo value related to this slit is equal 
11573          * to the number of spatial pseudo-pixels decreased by 1 
11574          * (compare with function mos_spatial_calibration()).
11575          */
11576 
11577         xdata = wdata + nx*position[i];
11578         npseudo = length[i] - 1;
11579 
11580         /*
11581          * Write interpolated wavelengths to CCD image
11582          */
11583 
11584         for (j = start_pixel; j < end_pixel; j++) {
11585             top = cpl_polynomial_eval_1d(polytop, j, NULL);
11586             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11587             for (k = 0; k <= npseudo; k++) {
11588                 ypos = top - k*(top-bot)/npseudo;
11589                 yint = ypos;
11590 
11591                 /* 
11592                  * The line:
11593                  *     value = sdata[j + nx*yint];
11594                  * should be equivalent to:
11595                  *     value = npseudo*(top-yint)/(top-bot);
11596                  */
11597 
11598                 if (yint < 0 || yint >= ny-1) {
11599                     yprev = yint;
11600                     continue;
11601                 }
11602 
11603                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
11604                 ivalue = value;               /* Nearest spatial pixels:   */
11605                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
11606                 if (ivalue < npseudo && ivalue >= 0) {
11607                     vtop = xdata[j + nx*(npseudo-ivalue)];
11608                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
11609                     if (vtop < 1.0) {  /* Impossible wavelength */
11610                         if (vbot < 1.0) {
11611                             value = 0.0;
11612                         }
11613                         else {
11614                             value = vbot;
11615                         }
11616                     }
11617                     else if (vbot < 1.0) {
11618                         if (k)
11619                             value = vtop;
11620                         else
11621                             value = 0.0;
11622                     }
11623                     else if (fabs(vbot-vtop) > 10*dispersion) {
11624                         value = 0.0;
11625                     }
11626                     else {
11627                         value = vtop*(1-fvalue) + vbot*fvalue;
11628                     }
11629                     data[j + nx*yint] = value;
11630 
11631                     if (k) {
11632 
11633                         /*
11634                          * This is added to recover lost pixels on
11635                          * the CCD image (pixels are lost because
11636                          * the CCD pixels are less than npseudo+1).
11637                          */
11638 
11639                         if (yprev - yint > 1) {
11640                             value = sdata[j + nx*(yint+1)];
11641                             ivalue = value;
11642                             fvalue = value - ivalue;
11643                             if (ivalue < npseudo && ivalue >= 0) {
11644                                 vtop = xdata[j + nx*(npseudo-ivalue)];
11645                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
11646                                 if (vtop < 1.0) {
11647                                     if (vbot < 1.0) {
11648                                         value = data[j + nx*(yint+1)];
11649                                     }
11650                                     else {
11651                                         value = vbot;
11652                                     }
11653                                 }
11654                                 else if (vbot < 1.0) {
11655                                     value = vtop;
11656                                 }
11657                                 else if (fabs(vbot-vtop) > 2*dispersion) {
11658                                     value = vtop;
11659                                 }
11660                                 else {
11661                                     value = vtop*(1-fvalue) + vbot*fvalue;
11662                                 }
11663                                 data[j + nx*(yint+1)] = value;
11664                             }
11665                         }
11666                     }
11667                 }
11668                 yprev = yint;
11669             }
11670         }
11671         cpl_polynomial_delete(polytop);
11672         cpl_polynomial_delete(polybot);
11673     }
11674 
11675     return remapped;
11676 }
11677 
11751 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
11752                             cpl_image *spatial, cpl_table *slits,
11753                             cpl_table *polytraces, double reference,
11754                             double blue, double red, double dispersion,
11755                             int flux)
11756 {
11757     const char *func = "mos_map_spectrum";
11758     
11759     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11760                                                  /* Max order is 5 */
11761     cpl_polynomial *polytop;
11762     cpl_polynomial *polybot;
11763     cpl_image      *remapped;
11764     cpl_image     **exslit;
11765     float          *data;
11766     float          *wdata;
11767     float          *sdata;
11768     float          *xdata;
11769     double          lambda00, lambda01, lambda10, lambda11, lambda;
11770     double          space00, space01, space10, space11, space;
11771     double          value00, value01, value10, value11, value0, value1, value;
11772     double          dL, dS;
11773     double          top, bot;
11774     double          coeff;
11775     double          ytop, ybot;
11776     double          xfrac, yfrac;
11777     int             yint, ysize;
11778     int             itop, ibot;
11779     int             shift;
11780     int             L, S;
11781     int             nslits;
11782     int             npseudo;
11783     int            *slit_id;
11784     int            *position;
11785     int            *length;
11786     int             nx, ny;
11787     int             x, y;
11788     int             nlambda;
11789     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11790     int             missing_top, missing_bot; 
11791     int             null;
11792     int             order;
11793     int             i; 
11794     cpl_size        k;
11795     
11796 
11797     flux += flux;
11798 
11799     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11800         slits == NULL || polytraces == NULL) { 
11801         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11802         return NULL;
11803     }
11804 
11805     if (dispersion <= 0.0) {
11806         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11807         return NULL;
11808     }
11809 
11810     if (red - blue < dispersion) {
11811         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11812         return NULL;
11813     }
11814     
11815     nx = cpl_image_get_size_x(spectra);
11816     ny = cpl_image_get_size_y(spectra);
11817 
11818     if (nx != cpl_image_get_size_x(spatial) ||
11819         ny != cpl_image_get_size_y(spatial) ||
11820         nx != cpl_image_get_size_x(wavecalib) ||
11821         ny != cpl_image_get_size_y(wavecalib)) {
11822         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11823         return NULL;
11824     }
11825 
11826     nlambda     = STRETCH_FACTOR * (red - blue) / dispersion;
11827     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11828     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11829 
11830     data  = cpl_image_get_data(spectra);
11831     sdata = cpl_image_get_data(spatial);
11832     wdata = cpl_image_get_data(wavecalib);
11833     
11834     nslits   = cpl_table_get_nrow(slits);
11835     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11836     order    = cpl_table_get_ncol(polytraces) - 2;
11837     position = cpl_table_get_data_int(slits, "position");
11838     length   = cpl_table_get_data_int(slits, "length");
11839     
11840     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11841 
11842     for (i = 0; i < nslits; i++) {
11843 
11844          if (length == 0)
11845              continue;
11846 
11847         /*
11848          * Note that the x coordinate of the reference pixels on the CCD
11849          * is taken arbitrarily at the top end of each slit. This wouldn't
11850          * be entirely correct in case of curved slits, or in presence of
11851          * heavy distortions: in such cases the spatial resampling is
11852          * really performed across a wide range of wavelengths. But
11853          * the lag between top and bottom spectral curvature models
11854          * would introduce even in such cases negligible effects on
11855          * the spectral spatial resampling.
11856          */
11857 
11858         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11859 
11860         start_pixel = refpixel - pixel_below;
11861         if (start_pixel < 1)
11862             start_pixel = 1;
11863 
11864         end_pixel = refpixel + pixel_above;
11865         if (end_pixel > nx)
11866             end_pixel = nx;
11867 
11868         /*
11869          * Recover from the table of spectral curvature coefficients
11870          * the curvature polynomials.
11871          */
11872 
11873         missing_top = 0;
11874         polytop = cpl_polynomial_new(1);
11875         for (k = 0; k <= order; k++) {
11876             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11877             if (null) {
11878                 cpl_polynomial_delete(polytop);
11879                 missing_top = 1;
11880                 break;
11881             }
11882             cpl_polynomial_set_coeff(polytop, &k, coeff);
11883         }
11884 
11885         missing_bot = 0;
11886         polybot = cpl_polynomial_new(1);
11887         for (k = 0; k <= order; k++) {
11888             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11889             if (null) {
11890                 cpl_polynomial_delete(polybot);
11891                 missing_bot = 1;
11892                 break;
11893             }
11894             cpl_polynomial_set_coeff(polybot, &k, coeff);
11895         }
11896 
11897         if (missing_top && missing_bot) {
11898             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11899                           slit_id[i]);
11900             continue;
11901         }
11902 
11903         /*
11904          * In case just one of the two edges was not traced, the other
11905          * edge curvature model is duplicated and shifted to the other
11906          * end of the slit: better than nothing!
11907          */
11908 
11909         if (missing_top) {
11910             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11911                           "the spectral curvature of the lower edge "
11912                           "is used instead.", slit_id[i]);
11913             polytop = cpl_polynomial_duplicate(polybot);
11914             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11915             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11916             k = 0;
11917             coeff = cpl_polynomial_get_coeff(polybot, &k);
11918             coeff += ytop - ybot;
11919             cpl_polynomial_set_coeff(polytop, &k, coeff);
11920         }
11921 
11922         if (missing_bot) {
11923             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11924                           "the spectral curvature of the upper edge "
11925                           "is used instead.", slit_id[i]);
11926             polybot = cpl_polynomial_duplicate(polytop);
11927             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11928             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11929             k = 0;
11930             coeff = cpl_polynomial_get_coeff(polytop, &k);
11931             coeff -= ytop - ybot;
11932             cpl_polynomial_set_coeff(polybot, &k, coeff);
11933         }
11934 
11935         /*
11936          * Allocate image for current extracted slit
11937          */
11938 
11939         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11940         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11941         npseudo = ceil(top-bot) + 1;
11942 
11943         if (npseudo < 1) {
11944             cpl_polynomial_delete(polytop);
11945             cpl_polynomial_delete(polybot);
11946             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11947                           slit_id[i]);
11948             continue;
11949         }
11950 
11951         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11952         xdata = cpl_image_get_data(exslit[i]);
11953 
11954         /*
11955          * Write interpolated spectral values to remapped slit spectrum.
11956          */
11957 
11958         for (x = start_pixel; x < end_pixel; x++) {
11959             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11960             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11961             itop = top + 1;
11962             ibot = bot;
11963             if (itop < 0)
11964                 itop = 0;
11965             if (itop > ny - 1)
11966                 itop = ny - 1;
11967             if (ibot < 0)
11968                 ibot = 0;
11969             if (ibot > ny - 1)
11970                 ibot = ny - 1;
11971             for (y = ibot; y < itop; y++) {
11972                  lambda11 = wdata[x + y*nx];
11973                  if (lambda11 < 1.0)        /* Impossible wavelength */
11974                      continue;
11975                  space11 = sdata[x + y*nx];
11976                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11977                      continue;
11978                  lambda01 = wdata[x - 1 + y*nx];
11979                  if (lambda01 < 1.0)        /* Impossible wavelength */
11980                      continue;
11981                  space01 = sdata[x - 1 + y*nx];
11982                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11983                      continue;
11984 
11985                  shift = 0;
11986 
11987 /****+
11988                  if (wdata[x + (y+1)*nx] > 1.0) {
11989                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11990                          shift = -1;
11991                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11992                              shift--;
11993                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11994                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11995                              shift++;
11996                          }
11997                      }
11998                      else {
11999                          shift = 1;
12000                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
12001                              shift++;
12002                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
12003                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
12004                              shift--;
12005                          }
12006                      }
12007                  }
12008 ****/
12009 
12010 /****
12011 printf("y = %d, shift = %d\n", y, shift);
12012 ****/
12013 
12014                  lambda10 = wdata[x + shift + (y+1)*nx];
12015                  if (lambda10 < 1.0)        /* Impossible wavelength */
12016                      continue;
12017                  space10 = sdata[x + shift + (y+1)*nx];
12018                  if (space10 < 0.0)         /* Impossible spatial coordinate */
12019                      continue;
12020                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
12021                  if (lambda00 < 1.0)        /* Impossible wavelength */
12022                      continue;
12023                  space00 = sdata[x - 1 + shift + (y+1)*nx];
12024                  if (space00 < 0.0)         /* Impossible spatial coordinate */
12025                      continue;
12026                  
12027                  /*
12028                   * Find the variation in lambda and space in this
12029                   * position for each CCD pixel (both quantities are 
12030                   * expected to be positive).
12031                   */
12032 
12033                  dL = lambda11 - lambda01;
12034                  dS = space11 - space10;
12035 
12036                  /*
12037                   * Find the position (L,S) of the output pixel 
12038                   * (by integer truncation).
12039                   */
12040 
12041                  L = (lambda11 - blue)/dispersion + 0.5;
12042                  S = space11 + 0.5;                   /* Counted from top! */
12043 
12044                  if (L < 0 || L >= nlambda)
12045                      continue;
12046                  if (S < 0 || S > npseudo)
12047                      continue;
12048 
12049                  /*
12050                   * Find the coordinate of pixel (L,S)
12051                   */
12052 
12053                  lambda = blue + L*dispersion;
12054                  space  = S;
12055 
12056                  /*
12057                   * Find the interpolation point on the CCD: it is
12058                   * defined as the (positive) distance from current
12059                   * CCD pixel (x,y) of the interpolation point (x',y'),
12060                   * measured in CCD pixels. The interpolation point
12061                   * is located between the four CCD pixels selected
12062                   * above.
12063                   */
12064 
12065                  xfrac = (lambda11-lambda)/dL;
12066                  yfrac = (space11-space)/dS;
12067 
12068 /*
12069 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
12070 printf("xyfrac = %f, %f\n", xfrac, yfrac);
12071 */
12072 
12073                  /*
12074                   * Get the four values to interpolate
12075                   */
12076 
12077                  value11 = data[x + y*nx];
12078                  value01 = data[x - 1 + y*nx];
12079                  value10 = data[x + shift + (y+1)*nx];
12080                  value00 = data[x + shift - 1 + (y+1)*nx];
12081 
12082                  /*
12083                   * Interpolation
12084                   */
12085 
12086                  value1 = (1-xfrac)*value11 + xfrac*value01;
12087                  value0 = (1-xfrac)*value10 + xfrac*value00;
12088                  value  = (1-yfrac)*value1  + yfrac*value0;
12089 
12090                  /*
12091                   * Write this value to the appropriate (L,S) coordinate
12092                   * on output slit
12093                   */
12094 
12095                  xdata[L + nlambda*(npseudo-S)] = value;
12096                  
12097             }
12098         }
12099         cpl_polynomial_delete(polytop);
12100         cpl_polynomial_delete(polybot);
12101     }
12102 
12103     /*
12104      * Now all the slits images are copied to a single image
12105      */
12106 
12107     ysize = 0;
12108     for (i = 0; i < nslits; i++)
12109         if (exslit[i])
12110             ysize += cpl_image_get_size_y(exslit[i]);
12111 
12112     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
12113 
12114     yint = -1;
12115     for (i = 0; i < nslits; i++) {
12116         if (exslit[i]) {
12117             yint += cpl_image_get_size_y(exslit[i]);
12118             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
12119             cpl_image_delete(exslit[i]);
12120             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
12121         }
12122     }
12123 
12124     cpl_free(exslit);
12125 
12126     return remapped;
12127 
12128 }
12129 
12130 
12163 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
12164                              double dispersion, double factor, int minpoints,
12165                              cpl_image *skymap)
12166 {
12167     const char *func = "mos_sky_map_super";
12168 
12169     cpl_vector **vector;
12170     cpl_vector **wvector;
12171     double       firstLambda, lastLambda;
12172     double       lambda, lambda1, lambda2;
12173     double       value, value1, value2;
12174     double       frac;
12175     float        min, max;
12176     int         *count;
12177     int          nbin, bin;
12178     int          nx, ny, npix;
12179     int          first_valid, valid_bins;
12180     int          i, j;
12181 
12182     cpl_table   *sky;
12183     double      *sky_spectrum;
12184     double      *sky_wave;
12185     float       *data;
12186     float       *sdata;
12187     float       *kdata;
12188 
12189 
12190     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12191         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12192         return NULL;
12193     }
12194     
12195     if (dispersion <= 0.0) {
12196         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12197         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
12198         return NULL;
12199     }
12200 
12201     nx = cpl_image_get_size_x(spectra);
12202     ny = cpl_image_get_size_y(spectra);
12203     npix = nx * ny;
12204 
12205     if (nx != cpl_image_get_size_x(wavemap) ||
12206         ny != cpl_image_get_size_y(wavemap) ||
12207         nx != cpl_image_get_size_x(skymap) ||
12208         ny != cpl_image_get_size_y(skymap)) {
12209         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12210         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
12211         return NULL;
12212     }
12213 
12214     if (factor < 1.0) {
12215         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12216         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
12217                       cpl_error_get_message());
12218         return NULL;
12219     }
12220 
12221     if (minpoints < 0) {
12222         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12223         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
12224         return NULL;
12225     }
12226 
12227     dispersion /= factor;
12228 
12229 
12230     /*
12231      * Find bluest and reddest wavelengths in the whole image
12232      */
12233 
12234     data = cpl_image_get_data(wavemap);
12235 
12236     for (i = 0; i < npix; i++) {
12237         if (data[i] > 1.0) {
12238             min = max = data[i];
12239             j = i+1;
12240             break;
12241         }
12242     }
12243 
12244     for (i = j; i < npix; i++) {
12245         if (data[i] < 1.0)      /* Impossible wavelength */
12246             continue;
12247         if (min > data[i])
12248             min = data[i];
12249         if (max < data[i])
12250             max = data[i];
12251     }
12252 
12253     firstLambda = min;
12254     lastLambda = max;
12255 
12256 
12257     /*
12258      * Determine length of median spectrum
12259      */
12260 
12261     nbin = (lastLambda - firstLambda) / dispersion;
12262 
12263     /*
12264      * Count how many values will be found for each spectral bin.
12265      * The ith bin has a wavelength range from firstLambda + i*dispersion
12266      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12267      * it is assigned to its central wavelength.
12268      */
12269 
12270     count = cpl_calloc(nbin, sizeof(int));
12271 
12272     data = cpl_image_get_data(wavemap);
12273 
12274     for (i = 0; i < npix; i++) {
12275         if (data[i] < 1.0)
12276             continue;
12277         bin = (data[i] - firstLambda) / dispersion;
12278         if (bin < nbin)                               /* Safer */
12279             count[bin]++;
12280     }
12281 
12282     valid_bins = 0;
12283     for (i = 0; i < nbin; i++)
12284         if (count[i] >= minpoints)
12285             valid_bins++;
12286 
12287     if (valid_bins < nbin/3) {
12288         cpl_msg_warning(func, "Cannot determine a good global sky "
12289                         "spectrum from input data");
12290         return NULL;
12291     }
12292 
12293 
12294     /*
12295      * Allocate an array of vectors with the appropriate size, to
12296      * contain a list of all the spectral pixels values. At the same
12297      * time, reset the array of counters (because we will have to
12298      * count again...).
12299      */
12300 
12301     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12302     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
12303     for (i = 0; i < nbin; i++) {
12304         if (count[i] >= minpoints) {
12305             vector[i] = cpl_vector_new(count[i]);
12306             wvector[i] = cpl_vector_new(count[i]);
12307         }
12308         count[i] = 0;
12309     }
12310 
12311 
12312     /*
12313      * Read the wavemap and the spectral images, and add the data values
12314      * to the appropriate wavelength bins
12315      */
12316 
12317     data  = cpl_image_get_data(wavemap);
12318     sdata = cpl_image_get_data(spectra);
12319 
12320     for (i = 0; i < npix; i++) {
12321         if (data[i] < 1.0)
12322             continue;
12323         bin = (data[i] - firstLambda) / dispersion;
12324         if (bin < nbin) {                             /* Safer */
12325             if (vector[bin]) {
12326                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
12327                 cpl_vector_set(wvector[bin], count[bin], data[i]);
12328             }
12329             count[bin]++;
12330         }
12331     }
12332 
12333 
12334     /*
12335      * Compute the median flux for each wavelength bin, and destroy
12336      * at the same time the used vectors
12337      */
12338 
12339     sky_spectrum = cpl_calloc(nbin, sizeof(double));
12340     sky_wave = cpl_calloc(nbin, sizeof(double));
12341     for (i = 0; i < nbin; i++) {
12342         if (vector[i]) {
12343             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12344             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
12345             cpl_vector_delete(vector[i]);
12346             cpl_vector_delete(wvector[i]);
12347         }
12348     }
12349 
12350     cpl_free(vector);
12351     cpl_free(wvector);
12352 
12353 
12354     /*
12355      * Here possible gaps in the final spectrum are filled by interpolation
12356      */
12357 
12358     for (i = 0; i < nbin; i++) {
12359         if (count[i] >= minpoints) {
12360             first_valid = i;
12361             break;
12362         }
12363     }
12364     
12365     for (i = first_valid; i < nbin; i++) {
12366         if (count[i] < minpoints) {
12367             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
12368             for (j = i+1; j < nbin; j++) {
12369                 if (count[j] >= minpoints) {
12370                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
12371                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
12372                                         / 2;
12373                     }
12374                     else {
12375                         frac = (sky_wave[i] - sky_wave[i-1]) 
12376                              / (sky_wave[j] - sky_wave[i-1]);
12377                         sky_spectrum[i] = frac * sky_spectrum[j]
12378                                         + (1 - frac) * sky_spectrum[i-1];
12379                     }
12380                 }
12381             }
12382         }
12383     }
12384 
12385 
12386     /*
12387      * Create the output table
12388      */
12389 
12390     sky = cpl_table_new(nbin);
12391     cpl_table_wrap_double(sky, sky_wave, "wavelength");
12392     cpl_table_wrap_double(sky, sky_spectrum, "sky");
12393     cpl_table_wrap_int(sky, count, "npoints");
12394 
12395 
12396     /*
12397      * Fill the sky map
12398      */
12399 
12400     data  = cpl_image_get_data(wavemap);
12401     sdata = cpl_image_get_data(spectra);
12402     kdata = cpl_image_get_data(skymap);
12403 
12404     for (i = 0; i < npix; i++) {
12405 
12406         /*
12407          * Currently based on linear interpolation
12408          */
12409 
12410         lambda = data[i];
12411         if (lambda < 1.0)
12412             continue;
12413         bin = (lambda - firstLambda) / dispersion;
12414         lambda1 = sky_wave[bin];
12415         value1 = sky_spectrum[bin];
12416         if (lambda1 < lambda) {
12417             bin++;
12418             if (bin < nbin) {
12419                 lambda2 = sky_wave[bin];
12420                 value2  = sky_spectrum[bin];
12421                 if (lambda2 - lambda1 < 0.1) {
12422                     value = (value1 + value2) / 2;
12423                 }
12424                 else {
12425                     frac = (lambda - lambda1) / (lambda2 - lambda1);
12426                     value = frac * value2 + (1 - frac) * value1;
12427                 }
12428             }
12429             else {
12430                 value = value1;
12431             }
12432         }
12433         else {
12434             if (bin > 0) {
12435                 bin--;
12436                 lambda2 = lambda1;
12437                 value2  = value1;
12438                 lambda1 = sky_wave[bin];
12439                 value1  = sky_spectrum[bin];
12440                 if (lambda2 - lambda1 < 0.1) {
12441                     value = (value1 + value2) / 2;
12442                 }
12443                 else {
12444                     frac = (lambda - lambda1) / (lambda2 - lambda1);
12445                     value = frac * value2 + (1 - frac) * value1;
12446                 }
12447             }
12448             else {
12449                 value = value1;
12450             }
12451         }
12452         kdata[i] = value;
12453     }
12454 
12455     if (first_valid)
12456         cpl_table_erase_window(sky, 0, first_valid);
12457 
12458     return sky;
12459 
12460 }
12461 
12462 
12496 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
12497                        double dispersion, cpl_image *skymap)
12498 {
12499     const char *func = "mos_sky_map";
12500 
12501     cpl_vector **vector;
12502     double       firstLambda, lastLambda;
12503     double       lambda, lambda1, lambda2;
12504     double       value, value1, value2;
12505     float        min, max;
12506     int         *count;
12507     int          nbin, bin;
12508     int          nx, ny, npix;
12509     int          i, j;
12510 
12511     cpl_table   *sky;
12512     double      *sky_spectrum;
12513     float       *data;
12514     float       *sdata;
12515     float       *kdata;
12516     double      *wdata;
12517 
12518 
12519     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
12520         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12521         return NULL;
12522     }
12523     
12524     if (dispersion <= 0.0) {
12525         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12526         return NULL;
12527     }
12528 
12529     nx = cpl_image_get_size_x(spectra);
12530     ny = cpl_image_get_size_y(spectra);
12531     npix = nx * ny;
12532 
12533     if (nx != cpl_image_get_size_x(wavemap) ||
12534         ny != cpl_image_get_size_y(wavemap) ||
12535         nx != cpl_image_get_size_x(skymap) ||
12536         ny != cpl_image_get_size_y(skymap)) {
12537         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12538         return NULL;
12539     }
12540 
12541 
12542     /*
12543      * Find bluest and reddest wavelengths in the whole image
12544      */
12545 
12546     data = cpl_image_get_data(wavemap);
12547 
12548     for (i = 0; i < npix; i++) {
12549         if (data[i] > 1.0) {
12550             min = max = data[i];
12551             j = i+1;
12552             break;
12553         }
12554     }
12555 
12556     for (i = j; i < npix; i++) {
12557         if (data[i] < 1.0)      /* Impossible wavelength */
12558             continue;
12559         if (min > data[i])
12560             min = data[i];
12561         if (max < data[i])
12562             max = data[i];
12563     }
12564 
12565     firstLambda = min;
12566     lastLambda = max;
12567 
12568 
12569     /*
12570      * Determine length of median spectrum
12571      */
12572 
12573     nbin = (lastLambda - firstLambda) / dispersion;
12574 
12575     /*
12576      * Count how many values will be found for each spectral bin.
12577      * The ith bin has a wavelength range from firstLambda + i*dispersion
12578      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12579      * it is assigned to its central wavelength.
12580      */
12581 
12582     count = cpl_calloc(nbin, sizeof(int));
12583 
12584     data = cpl_image_get_data(wavemap);
12585 
12586     for (i = 0; i < npix; i++) {
12587         if (data[i] < 1.0)
12588             continue;
12589         bin = (data[i] - firstLambda) / dispersion;
12590         if (bin < nbin)                               /* Safer */
12591             count[bin]++;
12592     }
12593 
12594 
12595     /*
12596      * Allocate an array of vectors with the appropriate size, to
12597      * contain a list of all the spectral pixels values. At the same
12598      * time, reset the array of counters (because we will have to
12599      * count again...).
12600      */
12601 
12602     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12603     for (i = 0; i < nbin; i++) {
12604         if (count[i])
12605             vector[i] = cpl_vector_new(count[i]);
12606         else
12607             vector[i] = NULL;
12608         count[i] = 0;
12609     }
12610 
12611 
12612     /*
12613      * Read the wavemap and the spectral images, and add the data values
12614      * to the appropriate wavelength bins
12615      */
12616 
12617     data  = cpl_image_get_data(wavemap);
12618     sdata = cpl_image_get_data(spectra);
12619 
12620     for (i = 0; i < npix; i++) {
12621         if (data[i] < 1.0)
12622             continue;
12623         bin = (data[i] - firstLambda) / dispersion;
12624         if (bin < nbin) {                             /* Safer */
12625             cpl_vector_set(vector[bin], count[bin], sdata[i]);
12626             count[bin]++;
12627         }
12628     }
12629 
12630 
12631     /*
12632      * Compute the median flux for each wavelength bin, and destroy
12633      * at the same time the used vectors
12634      */
12635 
12636     sky_spectrum = cpl_calloc(nbin, sizeof(double));
12637     for (i = 0; i < nbin; i++) {
12638         if (vector[i]) {
12639             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12640             cpl_vector_delete(vector[i]);
12641         }
12642     }
12643 
12644     cpl_free(vector);
12645 
12646 
12647     /*
12648      * Here possible gaps in the final spectrum should be filled
12649      * by interpolation
12650      */
12651 
12652     /* ... */
12653 
12654     /*
12655      * Create the output table
12656      */
12657 
12658     sky = cpl_table_new(nbin);
12659     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12660     cpl_table_set_column_unit(sky, "wavelength", "pixel");
12661     cpl_table_wrap_double(sky, sky_spectrum, "sky");
12662     cpl_table_wrap_int(sky, count, "npoints");
12663     for (i = 0; i < nbin; i++)
12664         cpl_table_set_double(sky, "wavelength", i, 
12665                              firstLambda + (i+0.5)*dispersion);
12666 
12667 
12668     /*
12669      * Fill the sky map
12670      */
12671 
12672     data  = cpl_image_get_data(wavemap);
12673     sdata = cpl_image_get_data(spectra);
12674     kdata = cpl_image_get_data(skymap);
12675     wdata = cpl_table_get_data_double(sky, "wavelength");
12676 
12677     for (i = 0; i < npix; i++) {
12678 
12679         /*
12680          * Currently based on linear interpolation
12681          */
12682 
12683         lambda = data[i];
12684         if (lambda < 1.0)
12685             continue;
12686         bin = (lambda - firstLambda) / dispersion;
12687         lambda1 = wdata[bin];
12688         value1 = sky_spectrum[bin];
12689         if (lambda1 < lambda) {
12690             bin++;
12691             if (bin < nbin) {
12692                 lambda2 = wdata[bin];
12693                 value2  = sky_spectrum[bin];
12694                 value   = ((lambda2 - lambda)*value1 
12695                         +  (lambda - lambda1)*value2) / dispersion;
12696             }
12697             else {
12698                 value = value1;
12699             }
12700         }
12701         else {
12702             if (bin > 0) {
12703                 bin--;
12704                 lambda2 = lambda1;
12705                 value2  = value1;
12706                 lambda1 = wdata[bin];
12707                 value1  = sky_spectrum[bin];
12708                 value   = ((lambda2 - lambda)*value1 
12709                         +  (lambda - lambda1)*value2)/dispersion;
12710             }
12711             else {
12712                 value = value1;
12713             }
12714         }
12715         kdata[i] = value;
12716     }
12717 
12718     return sky;
12719 
12720 }
12721 
12722 
12738 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12739 {
12740     const char *func = "mos_sky_local_old";
12741 
12742     cpl_image *exslit;
12743     cpl_image *sky;
12744     cpl_image *skymap;
12745     float     *data;
12746     float     *sdata;
12747     int        nx, ny;
12748     int        xlow, ylow, xhig, yhig;
12749     int        nslits;
12750     int       *slit_id;
12751     int       *position;
12752     int       *length;
12753     int        i, j, k;
12754 
12755 
12756     if (spectra == NULL) {
12757         cpl_msg_error(func, 
12758                       "A scientific rectified spectral image must be given");
12759         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12760         return NULL;
12761     }
12762 
12763     if (slits == NULL) {
12764         cpl_msg_error(func, "A slits position table must be given");
12765         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12766         return NULL;
12767     }
12768 
12769     nslits   = cpl_table_get_nrow(slits);
12770     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12771     position = cpl_table_get_data_int(slits, "position");
12772     length   = cpl_table_get_data_int(slits, "length");
12773 
12774     nx = cpl_image_get_size_x(spectra);
12775     ny = cpl_image_get_size_y(spectra);
12776 
12777     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12778 
12779     xlow = 1;
12780     xhig = nx;
12781     for (i = 0; i < nslits; i++) {
12782 
12783         if (length[i] == 0)
12784             continue;
12785 
12786         /*
12787          * Define the extraction boundaries. We DON'T write:
12788          *
12789          * ylow = position[i];
12790          * yhig = ylow + length[i];
12791          *
12792          * because the cpl_image pixels are counted from 1, and because in
12793          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12794          */
12795 
12796         ylow = position[i] + 1;
12797         yhig = ylow + length[i] - 1;
12798 
12799         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12800         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12801         cpl_image_delete(exslit);
12802 
12803         data   = cpl_image_get_data(skymap);
12804         data  += nx * position[i];
12805 
12806         for (j = 0; j < length[i]; j++) {
12807             sdata  = cpl_image_get_data(sky);
12808             for (k = 0; k < nx; k++) {
12809                 *data++ = *sdata++;
12810             }
12811         }
12812 
12813         cpl_image_delete(sky);
12814     }
12815 
12816     return skymap;
12817 
12818 }
12819 
12820 
12840 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12841 {
12842     const char *func = "mos_sky_local";
12843 
12844     char        name[MAX_COLNAME];
12845 
12846     cpl_polynomial *fit;
12847     cpl_vector     *points;
12848     cpl_vector     *values;
12849     cpl_vector     *keep_points;
12850     cpl_vector     *keep_values;
12851     cpl_image      *exslit;
12852     cpl_image      *sky;
12853     cpl_image      *subtracted;
12854     cpl_image      *profile;
12855     cpl_image      *skymap;
12856     cpl_table      *objects;
12857     float          *data;
12858     float          *sdata;
12859     float          *xdata;
12860     double         *vdata;
12861     double         *pdata;
12862     double          median;
12863     int             nx, ny;
12864     int             xlow, ylow, xhig, yhig;
12865     int             nslits;
12866     int            *slit_id;
12867     int            *position;
12868     int            *length;
12869     int            *is_sky;
12870     int             nsky, nbad;
12871     int             maxobjects;
12872     int             margin = 3;
12873     int             radius = 6;
12874     int             i, j, k;
12875 
12876 
12877     if (spectra == NULL) {
12878         cpl_msg_error(func, 
12879                       "A scientific rectified spectral image must be given");
12880         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12881         return NULL;
12882     }
12883 
12884     if (slits == NULL) {
12885         cpl_msg_error(func, "A slits position table must be given");
12886         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12887         return NULL;
12888     }
12889 
12890     if (order < 0) {
12891         cpl_msg_error(func, "Invalid fit order");
12892         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12893         return NULL;
12894     }
12895 
12896     nslits   = cpl_table_get_nrow(slits);
12897     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12898     position = cpl_table_get_data_int(slits, "position");
12899     length   = cpl_table_get_data_int(slits, "length");
12900 
12901     nx = cpl_image_get_size_x(spectra);
12902     ny = cpl_image_get_size_y(spectra);
12903 
12904     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12905 
12906     xlow = 1;
12907     xhig = nx;
12908     for (i = 0; i < nslits; i++) {
12909 
12910         if (length[i] == 0)
12911             continue;
12912 
12913         /*
12914          * Define the extraction boundaries. We DON'T write:
12915          *
12916          * ylow = position[i];
12917          * yhig = ylow + length[i];
12918          *
12919          * because the cpl_image pixels are counted from 1, and because in
12920          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12921          */
12922 
12923         ylow = position[i] + 1;
12924         yhig = ylow + length[i] - 1;
12925 
12926         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12927         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12928         cpl_image_delete(exslit);
12929 
12930         data   = cpl_image_get_data(skymap);
12931         data  += nx * position[i];
12932 
12933         for (j = 0; j < length[i]; j++) {
12934             sdata  = cpl_image_get_data(sky);
12935             for (k = 0; k < nx; k++) {
12936                 *data++ = *sdata++;
12937             }
12938         }
12939 
12940         cpl_image_delete(sky);
12941     }
12942 
12943 
12944     /*
12945      * Preliminary sky-subtracted image
12946      */
12947 
12948     subtracted = cpl_image_duplicate(spectra);
12949     cpl_image_subtract(subtracted, skymap);
12950     cpl_image_delete(skymap);
12951 
12952 
12953     /*
12954      * Detect objects positions in all slits
12955      */
12956 
12957     objects = cpl_table_duplicate(slits);
12958     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12959     cpl_image_delete(profile);
12960     cpl_image_delete(subtracted);
12961 
12962 
12963     /*
12964      * Flag the sky pixels. Note that maxobjects is intentionally 
12965      * the max number of objects increased by one.
12966      */
12967 
12968     maxobjects = 1;
12969     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12970     while (cpl_table_has_column(objects, name)) {
12971         maxobjects++;
12972         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12973     }
12974 
12975     is_sky = cpl_calloc(ny, sizeof(int));
12976 
12977     for (i = 0; i < nslits; i++) {
12978 
12979         if (length[i] == 0)
12980             continue;
12981 
12982         ylow = position[i] + margin;
12983         yhig = position[i] + length[i] - margin;
12984 
12985         for (j = ylow; j < yhig; j++)
12986             is_sky[j] = 1;
12987 
12988         for (j = 1; j < maxobjects; j++) {
12989             snprintf(name, MAX_COLNAME, "object_%d", j);
12990             if (cpl_table_is_valid(objects, name, i)) {
12991                 snprintf(name, MAX_COLNAME, "start_%d", j);
12992                 ylow = cpl_table_get_int(objects, name, i, NULL);
12993                 snprintf(name, MAX_COLNAME, "end_%d", j);
12994                 yhig = cpl_table_get_int(objects, name, i, NULL);
12995                 for (k = ylow; k <= yhig; k++)
12996                     is_sky[k] = 0;
12997             }
12998         }
12999 
13000 
13001         /*
13002          * Eliminate isolated sky points
13003          */
13004 
13005         ylow = position[i] + margin + 1;
13006         yhig = position[i] + length[i] - margin - 1;
13007 
13008         for (j = ylow; j < yhig; j++)
13009             if (is_sky[j])
13010                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
13011                     is_sky[j] = 0;
13012 
13013     }
13014 
13015 
13016     /*
13017      * Determination of the sky map
13018      */
13019 
13020     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13021 
13022     for (i = 0; i < nslits; i++) {
13023 
13024         if (length[i] == 0)
13025             continue;
13026 
13027         ylow = position[i];
13028         yhig = ylow + length[i];
13029 
13030         nsky = 0;
13031         for (j = ylow; j < yhig; j++)
13032             if (is_sky[j])
13033                 nsky++;
13034 
13035         if (nsky > order + 1) {
13036             if (order) {
13037                 points = cpl_vector_new(nsky);
13038                 nsky = 0;
13039                 for (j = ylow; j < yhig; j++) {
13040                     if (is_sky[j]) {
13041                         cpl_vector_set(points, nsky, j);
13042                         nsky++;
13043                     }
13044                 }
13045 
13046                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13047                 xdata = cpl_image_get_data(exslit);
13048                 values = cpl_vector_new(nsky);
13049 
13050                 for (j = 0; j < nx; j++) {
13051                     nsky = 0;
13052                     for (k = ylow; k < yhig; k++) {
13053                         if (is_sky[k]) {
13054                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13055                             nsky++;
13056                         }
13057                     }
13058 
13059                     /*
13060                      * Eliminate obvious outliers
13061                      */
13062 
13063                     median = cpl_vector_get_median_const(values);
13064                     vdata = cpl_vector_get_data(values);
13065                     pdata = cpl_vector_get_data(points);
13066                     nbad = 0;
13067                     for (k = 0; k < nsky; k++) {
13068                         if (fabs(vdata[k] - median) < 100) {
13069                             if (nbad) {
13070                                 vdata[k-nbad] = vdata[k];
13071                                 pdata[k-nbad] = pdata[k];
13072                             }
13073                         }
13074                         else
13075                             nbad++;
13076                     }
13077 
13078                     if (nsky == nbad)
13079                         continue;
13080 
13081                     if (nbad && nsky - nbad > order + 1) {
13082                         keep_values = values;
13083                         keep_points = points;
13084                         values = cpl_vector_wrap(nsky-nbad, vdata);
13085                         points = cpl_vector_wrap(nsky-nbad, pdata);
13086                     }
13087 
13088                     if (nsky - nbad > order + 1) {
13089 
13090                         fit = cpl_polynomial_fit_1d_create(points, values, 
13091                                                            order, NULL);
13092 
13093                         if (fit) {
13094                             for (k = ylow; k < yhig; k++) {
13095                                 xdata[j+(k-ylow)*nx] = 
13096                                          cpl_polynomial_eval_1d(fit, k, NULL);
13097                             }
13098 
13099                             cpl_polynomial_delete(fit);
13100                         }
13101                         else
13102                             cpl_error_reset();
13103                     }
13104                     else {
13105                         for (k = 0; k < nsky; k++) {
13106                             xdata[j+k*nx] = median;
13107                         }
13108                     }
13109 
13110                     if (nbad && nsky - nbad > order + 1) {
13111                         cpl_vector_unwrap(values);
13112                         cpl_vector_unwrap(points);
13113                         values = keep_values;
13114                         points = keep_points;
13115                     }
13116 
13117                     if (nbad) {
13118                         nsky = 0;
13119                         for (k = ylow; k < yhig; k++) {
13120                             if (is_sky[k]) {
13121                                 cpl_vector_set(points, nsky, k);
13122                                 nsky++;
13123                             }
13124                         }
13125                     }
13126 
13127                 }
13128 
13129                 cpl_vector_delete(values);
13130                 cpl_vector_delete(points);
13131 
13132                 cpl_image_copy(skymap, exslit, 1, ylow+1);
13133                 cpl_image_delete(exslit);
13134 
13135             }
13136             else {
13137                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
13138                 xdata = cpl_image_get_data(exslit);
13139                 values = cpl_vector_new(nsky);
13140 
13141                 for (j = 0; j < nx; j++) {
13142                     nsky = 0;
13143                     for (k = ylow; k < yhig; k++) {
13144                         if (is_sky[k]) {
13145                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
13146                             nsky++;
13147                         }
13148                     }
13149 
13150                     median = cpl_vector_get_median_const(values);
13151 
13152                     for (k = ylow; k < yhig; k++)
13153                         xdata[j+(k-ylow)*nx] = median;
13154 
13155                 }
13156 
13157                 cpl_vector_delete(values);
13158 
13159                 cpl_image_copy(skymap, exslit, 1, ylow+1);
13160                 cpl_image_delete(exslit);
13161             }
13162         }
13163         else
13164             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
13165     }
13166 
13167     cpl_free(is_sky);
13168 
13169     return skymap;
13170 
13171 }
13172 
13173 
13195 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
13196                                  float threshold, float ratio)
13197 {
13198     const char *func = "mos_clean_cosmics";
13199 
13200     cpl_image  *smoothImage;
13201     cpl_table  *table;
13202     cpl_matrix *kernel;
13203     int        *xdata;
13204     int        *ydata;
13205     float      *idata;
13206     float      *sdata;
13207     float       sigma, sum, value, smoothValue;
13208     double      noise;
13209     int         count;
13210     float       fMax;
13211     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
13212     int         xLen;
13213     int         yLen;
13214     int         nPix;
13215     int         first = 1;  /* position of first cosmic ray candidate
13216                                encountered while scanning the image */
13217     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
13218     int         numCosmic = 0;
13219     int         found, foundContiguousCandidate;
13220     int        *cosmic;
13221   
13222 
13223     if (image == NULL)
13224         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13225 
13226 
13227     /*
13228      *  "cosmic" is a flags holder (initialized to zero):
13229      *
13230      *           -1 = candidate for cosmic ray
13231      *            0 = not a cosmic
13232      *            1 = a cosmic ray
13233      *            2 = member of current group of contiguous candidates
13234      *            3 = examined member of current group
13235      */
13236 
13237     xLen = cpl_image_get_size_x(image);
13238     yLen = cpl_image_get_size_y(image);
13239 
13240     if (xLen < 4 || yLen < 4)
13241         return CPL_ERROR_NONE;
13242 
13243     nPix = xLen * yLen;
13244 
13245     /*
13246      * Noise estimation from negative offsets in image. Note that this
13247      * assumes that the background level (skyLevel) has already been 
13248      * subtracted from the data. In this way we estimate the noise due 
13249      * to detector readout and to the background signal level (before 
13250      * it were removed). Theoretically this is given by 
13251      *
13252      *        noise = sqrt(ron^2 + skyLevel/gain)
13253      *
13254      * where ron is the read-out-noise. To this we will sum the noise 
13255      * contribution due to any increase of the signal above the background
13256      * by an amount scienceLevel. Theoretically the total noise is given by
13257      *
13258      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
13259      *
13260      * that is
13261      *
13262      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
13263      *
13264      */
13265 
13266     idata = cpl_image_get_data(image);
13267     noise = 0.0;
13268     count = 0;
13269 
13270     for (i = 0; i < nPix; i++) {
13271         if (idata[i] < -0.00001) {
13272             noise -= idata[i];
13273             count++;
13274         }
13275     }
13276 
13277     noise /= count;
13278     noise *= 1.25;       /* Factor to convert average deviation to sigma */
13279 
13280     cosmic = cpl_calloc(nPix, sizeof(int));
13281 
13282     if (threshold < 0.)
13283         threshold = 4.0;
13284     if (ratio < 0.)
13285         ratio = 2.0;
13286 
13287     kernel = cpl_matrix_new(3, 3);
13288     cpl_matrix_fill(kernel, 1.0);
13289     cpl_matrix_set(kernel, 1, 1, 0.0);
13290     smoothImage = cpl_image_filter_median(image, kernel);
13291     cpl_matrix_delete(kernel);
13292     
13293     /*
13294      *  Loop on images pixels, searching for cosmic rays candidates.
13295      *  Border pixels are currently excluded (they cannot contain
13296      *  candidates), to avoid that the search for groups of contiguous
13297      *  pixels would ever go out of image boundaries. In future we may
13298      *  overcome this limit, adding an appropriate check when contiguous
13299      *  pixels are searched.
13300      */
13301 
13302     sdata = cpl_image_get_data(smoothImage);
13303 
13304     for (j = 1; j < yLen - 1; j++) {
13305         for (i = 1; i < xLen - 1; i++) {
13306             value = idata[i + j * xLen];
13307             smoothValue = sdata[i + j * xLen];
13308             if (smoothValue < 1.0)
13309                 smoothValue = 1.0;
13310             sigma = sqrt(noise * noise + smoothValue / gain);
13311             if (value - smoothValue >= threshold * sigma) 
13312                 cosmic[i + j * xLen] = -1;
13313         }
13314     }
13315 
13316     cpl_image_delete(smoothImage);
13317 
13318 
13319     /*
13320      *  Search for groups of contiguous cosmic rays candidates.
13321      */
13322 
13323     do {
13324         found = 0;
13325         for (pos = first; pos < nPix; pos++) {
13326             if (cosmic[pos] == -1) {
13327                 cosmic[pos] = 2;         /*  Candidate found.  */
13328                 i = pos % xLen;          /*  Its coordinates.  */
13329                 j = pos / xLen;
13330                 first = pos;
13331                 first++;      /* ???  really necessary? */
13332                 found = 1;
13333                 break;
13334             }
13335         }
13336 
13337         if (found) {
13338 
13339             /*
13340              *  Determine new group of contiguous cosmic rays candidates.
13341              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
13342              *  and the value of the max pixel and its position, fMax, iPosMax,
13343              *  jPosMax.
13344              */
13345 
13346             iMin = iMax = iPosMax = i;
13347             jMin = jMax = jPosMax = j;
13348             fMax = idata[i + j * xLen];
13349 
13350             do {
13351                 foundContiguousCandidate = 0;
13352                 for (l = 0; l <= 1; l++) {
13353                     for (k = 0; k <= 1; k++) {
13354 
13355                         /*
13356                          *  Looping on 4 pixels to North, East, South and West
13357                          */
13358 
13359                         ii = i + k - l;
13360                         jj = j + k + l - 1;
13361                         if (cosmic[ii + jj * xLen] == -1) {
13362                             foundContiguousCandidate = 1;
13363                             cosmic[ii + jj * xLen] = 2;
13364                                         /* Candidate belongs to current group */
13365                             iii = ii;   /* Keep its position */
13366                             jjj = jj;
13367 
13368                             /*
13369                              * Upgrade search box
13370                              */
13371 
13372                             if (ii < iMin)
13373                                 iMin = ii;
13374                             if (ii > iMax)
13375                                 iMax = ii;
13376                             if (jj < jMin)
13377                                 jMin = jj;
13378                             if (jj > jMax)
13379                                 jMax = jj;
13380 
13381                             if (idata[ii + jj * xLen] > fMax) {
13382                                 fMax = idata[ii + jj * xLen];
13383                                 iPosMax = ii;
13384                                 jPosMax = jj;
13385                             }
13386                         }
13387                     }
13388                 }
13389 
13390                 /*
13391                  *  We are done exploring the "cross". Now mark as "examined"
13392                  *  the current candidate (at the center of the cross):
13393                  */
13394 
13395                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
13396 
13397                 if (foundContiguousCandidate) {
13398 
13399                     /*
13400                      * Pass (arbitrarily) the coordinates of the LAST found 
13401                      * candidate
13402                      */
13403 
13404                     i = iii;
13405                     j = jjj;
13406 
13407                     /* 
13408                      * Skip the rest, continue loop on new candidate 
13409                      */
13410 
13411                     continue; 
13412                 }
13413 
13414 
13415                 /*
13416                  *  Look for leftovers in the (growing!) search box
13417                  */
13418 
13419                 for (l = jMin; l <= jMax; l++) {
13420                     for (k = iMin; k <= iMax; k++) {
13421                         if (cosmic[k + l * xLen] == 2) {
13422                             i = k;
13423                             j = l;
13424                             foundContiguousCandidate = 1;
13425                             break;
13426                         }
13427                     }
13428                     if (foundContiguousCandidate) 
13429                         break;
13430                 }
13431             } while (foundContiguousCandidate);
13432 
13433 
13434             /*
13435              *  No more contiguous candidates are found. Decide now
13436              *  whether the current group is a cosmic ray or not.
13437              */
13438 
13439             sum = 0.;                /* Sum of 8 pixels around max position */
13440             for (l = -1; l <= 1; l++) {
13441                 for (k = -1; k <= 1; k++) {
13442                     if (l != 0 || k != 0) {
13443                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
13444                     }
13445                 }
13446             }
13447 
13448             sum /= 8.;
13449             if (fMax > ratio * sum) {
13450                 for (l = jMin - 1; l <= jMax + 1; l++) {
13451                     for (k = iMin - 1; k <= iMax + 1; k++) {
13452                         if (cosmic[k + l * xLen] == 3) {
13453                             cosmic[k + l * xLen] = 1;
13454                             numCosmic++;
13455                         }
13456                     }
13457                 }
13458             }
13459             else {
13460                 for (l = jMin - 1; l <= jMax + 1; l++) {
13461                     for (k = iMin - 1; k <= iMax + 1; k++) {
13462                         if (cosmic[k + l * xLen] != -1) {
13463                             if (cosmic[k + l * xLen] == 1) 
13464                                 numCosmic--;
13465                             cosmic[k + l * xLen] = 0;
13466                         }
13467                     }
13468                 }
13469             }
13470         }
13471     } while (found);
13472 
13473 
13474     /*
13475      *  Prepare table containing cosmic rays coordinates. 
13476      */
13477 
13478     table = cpl_table_new(numCosmic);
13479     cpl_table_new_column(table, "x", CPL_TYPE_INT);
13480     cpl_table_new_column(table, "y", CPL_TYPE_INT);
13481     cpl_table_set_column_unit(table, "x", "pixel");
13482     cpl_table_set_column_unit(table, "y", "pixel");
13483     xdata = cpl_table_get_data_int(table, "x");
13484     ydata = cpl_table_get_data_int(table, "y");
13485 
13486     for (pos = 0, i = 0; pos < nPix; pos++) {
13487         if (cosmic[pos] == 1) {
13488             xdata[i] = (pos % xLen);
13489             ydata[i] = (pos / xLen);
13490             i++;
13491         }
13492     }
13493 
13494     mos_clean_bad_pixels(image, table, 1);
13495 
13496     cpl_free(cosmic);
13497     cpl_table_delete(table);
13498 
13499     return CPL_ERROR_NONE;
13500 
13501 }
13502 
13503 
13504 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
13505                                     int spectral)
13506 {
13507     const char *func = "mos_clean_cosmics";
13508  
13509     float       *idata;
13510     int         *isBadPix;
13511     int          i, j, k, d;
13512     int          xlen, ylen, totPix;
13513     int          nBadPixels = 0;
13514     int          sign, foundFirst;
13515     int         *xValue = NULL;
13516     int         *yValue = NULL;
13517     float        save = 0.;
13518     double       sumd;
13519     int          cx, cy;
13520     int          nPairs;
13521     float        estimate[4];
13522     int          sx[] = {0, 1, 1, 1};
13523     int          sy[] = {1,-1, 0, 1};
13524     int          searchHorizon = 100;
13525     int          percent = 15;
13526 
13527 
13528     if (image == NULL || table == NULL)
13529         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13530 
13531     if (1 != cpl_table_has_column(table, "x"))
13532         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13533 
13534     if (1 != cpl_table_has_column(table, "y"))
13535         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13536 
13537     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
13538         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13539 
13540     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13541         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13542 
13543     nBadPixels = cpl_table_get_nrow(table);
13544 
13545     if (nBadPixels) {
13546         xlen = cpl_image_get_size_x(image);
13547         ylen = cpl_image_get_size_y(image);
13548         idata = cpl_image_get_data(image);
13549         totPix = xlen * ylen;
13550         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13551             isBadPix = cpl_calloc(totPix, sizeof(int));
13552         }
13553         else {
13554             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13555                             "skip bad pixel correction", percent);
13556             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13557         }
13558     }
13559     else {
13560         cpl_msg_debug(func, "No pixel values to interpolate");
13561         return CPL_ERROR_NONE;
13562     }
13563 
13564     xValue = cpl_table_get_data_int(table, "x");
13565     yValue = cpl_table_get_data_int(table, "y");
13566 
13567     for (i = 0; i < nBadPixels; i++)
13568         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13569 
13570     for (i = 0; i < nBadPixels; i++) {
13571 
13572         /*
13573          *  Search for the closest good pixel along the 4 fundamental 
13574          *  directions (in both senses):
13575          *                            \ | /
13576          *                             \|/
13577          *                           --- ---
13578          *                             /|\
13579          *                            / | \
13580          *
13581          *  Then collect pairs of values to interpolate linearly.
13582          */
13583 
13584         nPairs = 0;
13585         for (j = 0; j < 4; j++) {
13586 
13587             if (spectral) /* Just horizontal interpolation for spectral data */
13588                 if (j != 2)
13589                     continue;
13590 
13591             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
13592             sumd = 0.;
13593             foundFirst = 0;
13594             for (k = 0; k < 2; k++) {
13595                 sign = 2 * k - 1;
13596                 d = 0;
13597                 cx = xValue[i];
13598                 cy = yValue[i];
13599                 do {
13600                     cx += sign * sx[j];
13601                     cy += sign * sy[j];
13602                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
13603                         break;
13604                     d++;
13605                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13606 
13607                 if (cx >= 0 && cx < xlen && 
13608                     cy >= 0 && cy < ylen && d < searchHorizon) {
13609 
13610                     /*
13611                      *  In this block is cripted the linear interpolation...
13612                      */
13613 
13614                     save = idata[cx + cy * xlen];
13615                     estimate[nPairs] += save / d;
13616                     sumd += 1. / (double) d;
13617                     if (k) {
13618                         estimate[nPairs] /= sumd;
13619                         nPairs++;
13620                     }
13621                     else {
13622                         foundFirst = 1;
13623                     }
13624                 }
13625                 else {
13626 
13627                     /*
13628                      * Image borders were crossed, incomplete pair of values
13629                      */
13630 
13631                     if (k) {
13632                         if (foundFirst) {
13633                             estimate[nPairs] = save;
13634                             nPairs++;
13635                         }
13636                     }
13637                 }
13638             }
13639         }
13640 
13641         /*
13642          * Replace pixel value of the input image, corresponding to
13643          * the current bad pixel, with the median of the estimates
13644          * resulted from the 4 linear interpolations.
13645          */
13646 
13647         if (nPairs > 2) {
13648             idata[xValue[i] + yValue[i] * xlen] = 
13649                                cpl_tools_get_median_float(estimate, nPairs);
13650         }
13651         else if (nPairs == 2) {
13652             idata[xValue[i] + yValue[i] * xlen] =
13653                                             (estimate[0] + estimate[1]) / 2.;
13654         }
13655         else if (nPairs == 1) {
13656             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13657         }
13658         else {
13659             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13660                           xValue[i], yValue[i]);
13661         }
13662     }
13663 
13664     cpl_free(isBadPix);
13665 
13666     return CPL_ERROR_NONE;
13667 }
13668 
13669 
13699 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13700                            cpl_table *polytraces, double reference,
13701                            double blue, double red, double dispersion)
13702 {
13703     const char *func = "mos_spatial_map";
13704 
13705     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13706                                                  /* Max order is 5 */
13707     cpl_polynomial *polytop;
13708     cpl_polynomial *polybot;
13709     cpl_image      *calibration;
13710     float          *data;
13711     double          top, bot;
13712     double          coeff;
13713     double          ytop, ybot;
13714     double          ypos, yfra;
13715     double          factor;
13716     int             yint, yprev;
13717     int             nslits;
13718     int             npseudo;
13719     int            *slit_id;
13720     int            *length;
13721     int             nx, ny;
13722     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13723     int             missing_top, missing_bot;
13724     int             null;
13725     int             order;
13726     int             i, j;
13727     cpl_size        k;
13728 
13729 
13730     if (spectra == NULL || slits == NULL || polytraces == NULL) {
13731         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13732         return NULL;
13733     }
13734 
13735     if (dispersion <= 0.0) {
13736         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13737         return NULL;
13738     }
13739 
13740     if (red - blue < dispersion) {
13741         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13742         return NULL;
13743     }
13744 
13745     nx = cpl_image_get_size_x(spectra);
13746     ny = cpl_image_get_size_y(spectra);
13747 
13748     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13749     data = cpl_image_get_data(calibration);
13750 
13751     length  = cpl_table_get_data_int(slits, "length");
13752     nslits  = cpl_table_get_nrow(slits);
13753     slit_id = cpl_table_get_data_int(slits, "slit_id");
13754     order   = cpl_table_get_ncol(polytraces) - 2;
13755 
13756     /*
13757      * The spatial resampling is performed for a certain number of 
13758      * pixels above and below the position of the reference wavelength:
13759      */
13760 
13761     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13762     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13763 
13764     for (i = 0; i < nslits; i++) {
13765         
13766         if (length[i] == 0)
13767             continue;
13768 
13769         /*
13770          * Note that the x coordinate of the reference pixels on the CCD
13771          * is taken arbitrarily at the top end of each slit. This wouldn't
13772          * be entirely correct in case of curved slits, or in presence of
13773          * heavy distortions: in such cases the spatial resampling is
13774          * really performed across a wide range of wavelengths. But
13775          * the lag between top and bottom spectral curvature models 
13776          * would introduce even in such cases negligible effects on
13777          * the spectral spatial resampling.
13778          */
13779 
13780         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13781 
13782         start_pixel = refpixel - pixel_below;
13783         if (start_pixel < 0)
13784             start_pixel = 0;
13785 
13786         end_pixel = refpixel + pixel_above;
13787         if (end_pixel > nx)
13788             end_pixel = nx;
13789 
13790         /*
13791          * Recover from the table of spectral curvature coefficients
13792          * the curvature polynomials.
13793          */
13794 
13795         missing_top = 0;
13796         polytop = cpl_polynomial_new(1);
13797         for (k = 0; k <= order; k++) {
13798             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13799             if (null) {
13800                 cpl_polynomial_delete(polytop);
13801                 missing_top = 1;
13802                 break;
13803             }
13804             cpl_polynomial_set_coeff(polytop, &k, coeff);
13805         }
13806 
13807         missing_bot = 0;
13808         polybot = cpl_polynomial_new(1);
13809         for (k = 0; k <= order; k++) {
13810             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13811             if (null) {
13812                 cpl_polynomial_delete(polybot);
13813                 missing_bot = 1;
13814                 break;
13815             }
13816             cpl_polynomial_set_coeff(polybot, &k, coeff);
13817         }
13818 
13819         if (missing_top && missing_bot) {
13820             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
13821                             slit_id[i]);
13822             continue;
13823         }
13824 
13825         /*
13826          * In case just one of the two edges was not traced, the other
13827          * edge curvature model is duplicated and shifted to the other
13828          * end of the slit: better than nothing!
13829          */
13830 
13831         if (missing_top) {
13832             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13833                             "the spectral curvature of the lower edge "
13834                             "is used instead.", slit_id[i]);
13835             polytop = cpl_polynomial_duplicate(polybot);
13836             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13837             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13838             k = 0;
13839             coeff = cpl_polynomial_get_coeff(polybot, &k);
13840             coeff += ytop - ybot;
13841             cpl_polynomial_set_coeff(polytop, &k, coeff);
13842         }
13843 
13844         if (missing_bot) {
13845             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13846                             "the spectral curvature of the upper edge "
13847                             "is used instead.", slit_id[i]);
13848             polybot = cpl_polynomial_duplicate(polytop);
13849             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13850             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13851             k = 0;
13852             coeff = cpl_polynomial_get_coeff(polytop, &k);
13853             coeff -= ytop - ybot;
13854             cpl_polynomial_set_coeff(polybot, &k, coeff);
13855         }
13856 
13857         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13858         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13859         npseudo = ceil(top-bot) + 1;
13860 
13861         if (npseudo < 1) {
13862             cpl_polynomial_delete(polytop);
13863             cpl_polynomial_delete(polybot);
13864             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13865                             slit_id[i]);
13866             continue;
13867         }
13868 
13869         for (j = start_pixel; j < end_pixel; j++) {
13870             top = cpl_polynomial_eval_1d(polytop, j, NULL);
13871             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13872             factor = (top-bot)/npseudo;
13873             for (k = 0; k <= npseudo; k++) {
13874                 ypos = top - k*factor;
13875                 yint = ypos;
13876                 yfra = ypos - yint;
13877                 if (yint >= 0 && yint < ny-1) {
13878                     data[j + nx*yint] = (top-yint)/factor;
13879                     if (k) {
13880 
13881                         /*
13882                          * This is added to recover lost pixels on
13883                          * the CCD image (pixels are lost because
13884                          * the CCD pixels are less than npseudo+1).
13885                          */
13886 
13887                         if (yprev - yint > 1) {
13888                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
13889                         }
13890                     }
13891                 }
13892                 yprev = yint;
13893             }
13894         }
13895         cpl_polynomial_delete(polytop);
13896         cpl_polynomial_delete(polybot);
13897     }
13898 
13899     return calibration;
13900 }
13901 
13902 
13965 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13966                               int maxradius, int conradius)
13967 {
13968     const char *func = "mos_detect_objects";
13969 
13970     cpl_image  *profile;
13971     float      *pdata;
13972     float      *p;
13973 
13974     char        name[MAX_COLNAME];
13975 
13976     int         nslits;
13977     int         npeaks;
13978     int         nobjects, objpos, totobj;
13979     int         maxobjects;
13980     int        *position;
13981     int        *length;
13982     int        *reject;
13983     double     *place;
13984     double     *bright;
13985     double      mindistance;
13986     int         pos, count;
13987     int         up;
13988     int         low, hig;
13989     int         row;
13990     int         i, j, k;
13991 
13992     const int   min_pixels = 10;
13993 
13994     
13995     if (cpl_error_get_code() != CPL_ERROR_NONE)
13996         return NULL;
13997  
13998     if (image == NULL || slits == NULL) { 
13999         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14000         return NULL;
14001     }
14002 
14003     if (margin < 0)
14004         margin = 0;
14005 
14006     if (maxradius < 0) {
14007         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14008         return NULL;
14009     }
14010 
14011     if (conradius < 0) {
14012         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14013         return NULL;
14014     }
14015 
14016     nslits   = cpl_table_get_nrow(slits);
14017     position = cpl_table_get_data_int(slits, "position");
14018     length   = cpl_table_get_data_int(slits, "length");
14019 
14020     profile = cpl_image_collapse_create(image, 1);
14021     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
14022     pdata = cpl_image_get_data(profile);
14023 
14024     row = 1;
14025     maxobjects = 0;
14026     totobj = 0;
14027     for (i = 0; i < nslits; i++) {
14028 
14029         if (length[i] == 0)
14030             continue;
14031 
14032         pos = position[i] + margin;
14033         count = length[i] - 2*margin;
14034 
14035         if (count < min_pixels)
14036             continue;
14037 
14038         p = pdata + pos;
14039 
14040 
14041         /*
14042          * Count peaks candidates
14043          */
14044 
14045         npeaks = 0;
14046         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14047             npeaks++;
14048         }
14049 
14050         up = 0;
14051         for (j = 0; j < count - 3; j++) {
14052             if (p[j] > 0) {
14053                 if (p[j+1] > p[j]) {
14054                     up++;
14055                 }
14056                 else {
14057                     if (up > 2) {
14058                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
14059                             if (p[j] > 5)
14060                                 npeaks++;
14061                         }
14062                     }
14063                     else if (up > 1) {
14064                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14065                             if (p[j] > 5)
14066                                 npeaks++;
14067                         }
14068                     }
14069                     up = 0;
14070                 }
14071             }
14072             else {
14073                 up = 0;
14074             }
14075         }
14076 
14077         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
14078             && p[count-3] > p[count-4] && p[count-4] > 0) {
14079             npeaks++;
14080         }
14081 
14082         if (npeaks == 0)
14083             continue;
14084 
14085 
14086         /*
14087          * Get candidates parameters
14088          */
14089 
14090         reject = cpl_calloc(npeaks, sizeof(int));
14091         bright = cpl_calloc(npeaks, sizeof(double));
14092         place  = cpl_calloc(npeaks, sizeof(double));
14093 
14094         npeaks = 0;
14095         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
14096             bright[0] = p[0];
14097             place[0] = position[i] + margin;
14098             npeaks++;
14099         }
14100 
14101         up = 0;
14102         for (j = 0; j < count - 3; j++) {
14103             if (p[j] > 0) {
14104                 if (p[j+1] > p[j]) {
14105                     up++;
14106                 }
14107                 else {
14108                     if (up > 2) {
14109                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
14110                             if (p[j] > 5) {
14111                                 bright[npeaks] = p[j];
14112                                 place[npeaks] = position[i] + margin + j + 1
14113                                        + values_to_dx(p[j-1], p[j], p[j+1]);
14114                                 npeaks++;
14115                             }
14116                         }
14117                     }
14118                     else if (up > 1) {
14119                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
14120                             if (p[j] > 5) {
14121                                 bright[npeaks] = p[j];
14122                                 place[npeaks] = position[i] + margin + j + 1
14123                                        + values_to_dx(p[j-1], p[j], p[j+1]);
14124                                 npeaks++;
14125                             }
14126                         }
14127                     }
14128                     up = 0;
14129                 }
14130             }
14131             else {
14132                 up = 0;
14133             }
14134         }
14135 
14136         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
14137             && p[count-3] > p[count-4] && p[count-4] > 0) {
14138             bright[npeaks] = p[count-1];
14139             place[npeaks] = position[i] + count;
14140             npeaks++;
14141         }
14142 
14143 
14144         /*
14145          * Now select the uncontaminated peaks
14146          */
14147 
14148         if (fabs(place[0] - pos) < 1.0)
14149             reject[0] = 1;
14150         if (fabs(place[npeaks-1] - pos - count) < 1.0)
14151             reject[npeaks-1] = 1;
14152         for (j = 0; j < npeaks; j++) {
14153             for (k = 0; k < npeaks; k++) {
14154                 if (k == j)
14155                     continue;
14156                 mindistance = conradius * bright[k] / bright[j] 
14157                                         * bright[k] / bright[j];
14158                 if (fabs(place[j] - place[k]) < mindistance)
14159                     reject[j] = 1;
14160             }
14161         }
14162 
14163 /* new part */
14164         for (j = 0; j < npeaks; j++) {
14165             if (reject[j])
14166                 continue;
14167             if (j) {
14168                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14169                     / (bright[j-1] + bright[j]) + 1;
14170             }
14171             else {
14172                 low = pos;
14173             }
14174             if (j < npeaks - 1) {
14175                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14176                     / (bright[j+1] + bright[j]) + 1;
14177             }
14178             else {
14179                 hig = pos + count;
14180             }
14181 
14182             if (low < pos)
14183                 low = pos;
14184             if (hig > pos + count)
14185                 hig = pos + count;
14186             if (place[j] - low > maxradius)
14187                 low = place[j] - maxradius;
14188             if (hig - place[j] > maxradius)
14189                 hig = place[j] + maxradius;
14190             if (hig == low)
14191                 reject[j] = 1;
14192         }
14193 /* end new part */
14194 
14195         nobjects = npeaks;
14196         for (j = 0; j < npeaks; j++)
14197             if (reject[j])
14198                 nobjects--;
14199 
14200         for (j = 0; j < nobjects; j++) {
14201             snprintf(name, MAX_COLNAME, "object_%d", j+1);
14202             if (cpl_table_has_column(slits, name))
14203                 continue;
14204             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
14205             snprintf(name, MAX_COLNAME, "start_%d", j+1);
14206             cpl_table_new_column(slits, name, CPL_TYPE_INT);
14207             cpl_table_set_column_unit(slits, name, "pixel");
14208             snprintf(name, MAX_COLNAME, "end_%d", j+1);
14209             cpl_table_new_column(slits, name, CPL_TYPE_INT);
14210             cpl_table_set_column_unit(slits, name, "pixel");
14211             snprintf(name, MAX_COLNAME, "row_%d", j+1);
14212             cpl_table_new_column(slits, name, CPL_TYPE_INT);
14213             cpl_table_set_column_unit(slits, name, "pixel");
14214         }
14215 
14216         objpos = nobjects;
14217         for (j = 0; j < npeaks; j++) {
14218             if (reject[j])
14219                 continue;
14220             if (j) {
14221                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
14222                     / (bright[j-1] + bright[j]) + 1;
14223             }
14224             else {
14225                low = pos;
14226             }
14227             if (j < npeaks - 1) {
14228                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
14229                     / (bright[j+1] + bright[j]) + 1;
14230             }
14231             else {
14232                 hig = pos + count;
14233             }
14234 
14235             if (low < pos)
14236                 low = pos;
14237             if (hig > pos + count)
14238                 hig = pos + count;
14239             if (place[j] - low > maxradius)
14240                 low = place[j] - maxradius;
14241             if (hig - place[j] > maxradius)
14242                 hig = place[j] + maxradius;
14243 
14244             snprintf(name, MAX_COLNAME, "object_%d", objpos);
14245             cpl_table_set_double(slits, name, i, place[j]);
14246             snprintf(name, MAX_COLNAME, "start_%d", objpos);
14247             cpl_table_set_int(slits, name, i, low);
14248             snprintf(name, MAX_COLNAME, "end_%d", objpos);
14249             cpl_table_set_int(slits, name, i, hig);
14250             snprintf(name, MAX_COLNAME, "row_%d", objpos);
14251             cpl_table_set_int(slits, name, i, row + objpos - 1);
14252             totobj++;
14253             objpos--;
14254         }
14255 
14256         row += nobjects;
14257 
14258         if (maxobjects < nobjects)
14259             maxobjects = nobjects;
14260 
14261         cpl_free(reject);
14262         cpl_free(bright);
14263         cpl_free(place);
14264 
14265     }
14266 
14267 /*    nobjects = row - nobjects;     A bug, I think... */
14268     row = cpl_table_get_nrow(slits);
14269 
14270     for (i = 0; i < row; i++) {
14271         for (j = 0; j < maxobjects; j++) {
14272             snprintf(name, MAX_COLNAME, "row_%d", j+1);
14273             if (cpl_table_is_valid(slits, name, i))
14274                 cpl_table_set_int(slits, name, i, totobj -
14275                                   cpl_table_get_int(slits, name, i, NULL));
14276         }
14277     }
14278 
14279     for (i = 0; i < maxobjects; i++) {
14280         snprintf(name, MAX_COLNAME, "start_%d", i+1);
14281         cpl_table_fill_invalid_int(slits, name, -1);
14282         snprintf(name, MAX_COLNAME, "end_%d", i+1);
14283         cpl_table_fill_invalid_int(slits, name, -1);
14284         snprintf(name, MAX_COLNAME, "row_%d", i+1);
14285         cpl_table_fill_invalid_int(slits, name, -1);
14286     }
14287 
14288     return profile;
14289 }
14290 
14291 
14316 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *science_var, 
14317                                 cpl_image *sky,
14318                                 cpl_table *objects, int extraction, double ron,
14319                                 double gain, int ncombined)
14320 {
14321     const char *func = "mos_extract_objects";
14322 
14323     char        name[MAX_COLNAME];
14324 
14325     cpl_image **output;
14326     cpl_image  *extracted;
14327     cpl_image  *extr_sky;
14328     cpl_image  *error;
14329     cpl_image  *sciwin;
14330     cpl_image  *sci_var_win = NULL;
14331     cpl_image  *skywin;
14332     int         nslits;
14333     int         nobjects;
14334     int         maxobjects;
14335     int         nx;
14336     int         ylow, yhig;
14337     int         i, j;
14338 
14339 
14340     if (science == NULL || sky == NULL) {
14341         cpl_msg_error(func, "Both scientific exposures are required in input");
14342         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14343         return NULL;
14344     }
14345 
14346     if (objects == NULL) {
14347         cpl_msg_error(func, "An object table is required in input");
14348         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14349         return NULL;
14350     }
14351 
14352     if (extraction < 0 || extraction > 1) {
14353         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
14354                       "either 0 or 1", extraction); 
14355         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14356         return NULL;
14357     }
14358 
14359     if (ron < 0.0) {
14360         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
14361         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14362         return NULL;
14363     }
14364 
14365     if (gain < 0.1) {
14366         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
14367         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14368         return NULL;
14369     }
14370 
14371     if (ncombined < 1) {
14372         cpl_msg_error(func, "Invalid number of combined frames (%d): "
14373                       "it should be at least 1", ncombined);
14374         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14375         return NULL;
14376     }
14377 
14378 
14379     /*
14380      * Count the max number of objects per slit. Note that maxobjects 
14381      * is intentionally the max number of objects increased by one.
14382      */
14383 
14384     maxobjects = 1;
14385     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14386     while (cpl_table_has_column(objects, name)) {
14387         maxobjects++;
14388         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
14389     }
14390 
14391 
14392     /*
14393      * Count objects to extract
14394      */
14395 
14396     nobjects = 0;
14397     nslits = cpl_table_get_nrow(objects);
14398 
14399     for (i = 0; i < nslits; i++) {
14400         for (j = 1; j < maxobjects; j++) {
14401             snprintf(name, MAX_COLNAME, "object_%d", j);
14402             if (cpl_table_is_valid(objects, name, i))
14403                 nobjects++;
14404         }
14405     }
14406 
14407     if (nobjects == 0)
14408         return NULL;
14409 
14410     nx = cpl_image_get_size_x(science);
14411 
14412     output = cpl_calloc(3, sizeof(cpl_image *));
14413     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14414     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14415     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
14416 
14417 
14418     /*
14419      * Extract objects
14420      */
14421 
14422     nobjects = 0;
14423     for (i = 0; i < nslits; i++) {
14424         for (j = 1; j < maxobjects; j++) {
14425             snprintf(name, MAX_COLNAME, "object_%d", j);
14426             if (cpl_table_is_valid(objects, name, i)) {
14427                 snprintf(name, MAX_COLNAME, "start_%d", j);
14428                 ylow = cpl_table_get_int(objects, name, i, NULL);
14429                 snprintf(name, MAX_COLNAME, "end_%d", j);
14430                 yhig = cpl_table_get_int(objects, name, i, NULL);
14431                 snprintf(name, MAX_COLNAME, "row_%d", j);
14432                 nobjects = cpl_table_get_int(objects, name, i, NULL);
14433                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
14434                 if(science_var != NULL)
14435                     sci_var_win = cpl_image_extract(science_var, 1, ylow+1, nx, yhig);
14436                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
14437 /*
14438  * Cleaning the cosmics locally was really NOT a good idea...
14439  * I leave it here, commented out, to never forget this mistake!
14440 
14441                 if (extraction) {
14442                     mos_clean_cosmics(sciwin, gain, -1., -1.);
14443                 }
14444  */
14445                 mos_extraction(sciwin, sci_var_win, skywin, extracted, extr_sky, error, 
14446                                nobjects, extraction, ron, gain, ncombined);
14447 
14448                 /*
14449                  * Hidden check whether the spectrum was saturated or not
14450                  */
14451 
14452                 {
14453                     cpl_image *total = cpl_image_add_create(sciwin, skywin);
14454                     float     *data  = cpl_image_get_data_float(total);
14455                     int        size  = cpl_image_get_size_x(total)
14456                                      * cpl_image_get_size_y(total);
14457                     int        k;
14458                     char      *saturation_level = getenv("SATURATION_LEVEL");
14459                     float      saturation = 62000.0;
14460                     char      *max_saturated = getenv("MAX_SATURATED");
14461                     int        max_satur = 10;
14462                     int        saturated;
14463 
14464                     if (saturation_level)
14465                         saturation = atof(saturation_level);
14466 
14467                     if (max_saturated)
14468                         max_satur = atoi(max_saturated);
14469 
14470                     saturated = 0;
14471                     for (k = 0; k < size; k++) {
14472                         if (data[k] > saturation) {
14473                             saturated++;
14474                             if (saturated > max_satur) {
14475                                 break;
14476                             }
14477                         }
14478                     }
14479 
14480                     if (saturated > max_satur)
14481                         saturated = 1;
14482                     else
14483                         saturated = 0;
14484 
14485                     data = cpl_image_get_data(extracted);
14486                     data[nobjects * nx] = saturated;
14487                 }
14488 
14489                 cpl_image_delete(sciwin);
14490                 cpl_image_delete(skywin);
14491                 nobjects++;
14492             }
14493         }
14494     }
14495 
14496     return output;
14497 
14498 }
14499 
14500 
14523 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
14524                             double dispersion, int saturation, 
14525                             double *mfwhm, double *rmsfwhm,
14526                             double *resolution, double *rmsres, int *nlines)
14527 {
14528     cpl_vector *vector;
14529 
14530     int     i, j, n, m;
14531     int     position, maxpos;
14532     int     xlen, ylen;
14533     int     sp, ep;
14534     int     radius;
14535     int     sradius = 40;
14536     int     threshold = 250;    /* Peak must be so many ADUs above min */
14537 
14538     int     ifwhm;
14539     double  fwhm;
14540     double *buffer;
14541     double  min, max, halfmax;
14542     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
14543     double  value, rms;
14544 
14545     float  *data;
14546 
14547 
14548     *resolution = 0.0;
14549     *rmsres = 0.0;
14550     *nlines = 0;
14551 
14552     xlen = cpl_image_get_size_x(image);
14553     ylen = cpl_image_get_size_y(image);
14554     data = cpl_image_get_data(image);
14555 
14556     buffer = cpl_malloc(ylen * sizeof(double));
14557 
14558     /*
14559      *  Closest pixel to specified wavelength.
14560      */
14561 
14562     position = floor((lambda - startwave) / dispersion + 0.5);
14563 
14564     sp = position - sradius;
14565     ep = position + sradius;
14566 
14567     if (sp < 0 || ep > xlen) {
14568         cpl_free(buffer);
14569         return 0;
14570     }
14571 
14572     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
14573 
14574         /*
14575          *  Search interval for peak. Abort if too close to image border.
14576          */
14577 
14578         radius = mos_lines_width(data + i*xlen + position - sradius, 
14579                                  2*sradius + 1);
14580         if (radius < 5)
14581             radius = 5;
14582 
14583         sp = position - radius;
14584         ep = position + radius;
14585 
14586         if (sp < 0 || ep > xlen) {
14587             cpl_free(buffer);
14588             return 0;
14589         }
14590 
14591 
14592         /*
14593          *  Determine min-max value and position.
14594          */
14595 
14596         maxpos = sp;
14597         min = max = data[sp + i * xlen];
14598         for (j = sp; j < ep; j++) {
14599             if (data[j + i * xlen] > max) {
14600                 max = data[j + i * xlen];
14601                 maxpos = j;
14602             }
14603             if (data[j + i * xlen] < min) {
14604                 min = data[j + i * xlen];
14605             }
14606         }
14607 
14608         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
14609             continue;
14610 
14611         if (max - min < threshold)        /* Low signal... */
14612             continue;
14613 
14614         if (max > saturation)             /* Saturation */
14615             continue;
14616 
14617         /*
14618          *  Determine FWHM counting pixels with value greater than
14619          *  half of the max value, to the right and to the left of
14620          *  the max. Linear interpolation between the pixels where
14621          *  the transition happens.
14622          */
14623 
14624         halfmax = (max + min)/ 2.0;
14625 
14626         fwhm = 0.0;
14627         ifwhm = 0;
14628         for (j = maxpos; j < maxpos + radius; j++) {
14629             if (j < xlen) {
14630                 if (data[j + i * xlen] < halfmax) {
14631                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14632                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14633                     break;
14634                 }
14635                 ifwhm++;
14636             }
14637         }
14638 
14639         ifwhm = 0;
14640         for (j = maxpos; j > maxpos - radius; j--) {
14641             if (j >= 0) {
14642                 if (data[j + i * xlen] < halfmax) {
14643                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14644                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14645                     break;
14646                 }
14647                 ifwhm++;
14648             }
14649         }
14650 
14651         if (fwhm > 3.0) {
14652             buffer[n] = fwhm - 2.0;
14653             n++;
14654         }
14655 
14656     }
14657 
14658     if (n == 0) {
14659         cpl_free(buffer);
14660         return 0;
14661     }
14662 
14663     vector = cpl_vector_wrap(n, buffer);
14664     value = cpl_vector_get_median_const(vector);
14665     cpl_vector_unwrap(vector);
14666 
14667     rms = 0.0;
14668     for (i = 0, m = 0; i < n; i++) {
14669         if (fabs(buffer[i] - value) < cut) {
14670             rms += fabs(buffer[i] - value);
14671             m++;
14672         }
14673     }
14674 
14675     cpl_free(buffer);
14676 
14677     if (m < 3)
14678         return 0;
14679 
14680     rms /= m;
14681     rms *= 1.25;       /* Factor to convert average deviation to sigma */
14682 
14683     value *= dispersion;
14684     rms *= dispersion;
14685 
14686     *mfwhm = value;
14687     *rmsfwhm = rms;
14688 
14689     *resolution = lambda / value;
14690     *rmsres = *resolution * rms / value;
14691 
14692     *nlines = m;
14693 
14694     return 1;
14695 }
14696 
14697 
14719 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
14720                                 double dispersion, int saturation, 
14721                                 cpl_vector *lines)
14722 {
14723 
14724     cpl_table *table;
14725     double    *line;
14726     double     fwhm;
14727     double     rmsfwhm;
14728     double     resolution;
14729     double     rmsres;
14730     int        nref;
14731     int        nlines;
14732     int        i;
14733 
14734 
14735     nref = cpl_vector_get_size(lines);
14736     line = cpl_vector_get_data(lines);
14737 
14738     table = cpl_table_new(nref);
14739     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14740     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14741     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14742     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14743     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14744     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14745     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14746     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14747     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14748 
14749     for (i = 0; i < nref; i++) {
14750         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
14751                                     saturation, &fwhm, &rmsfwhm, 
14752                                     &resolution, &rmsres, &nlines)) {
14753             cpl_table_set_double(table, "wavelength", i, line[i]);
14754             cpl_table_set_double(table, "fwhm", i, fwhm);
14755             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14756             cpl_table_set_double(table, "resolution", i, resolution);
14757             cpl_table_set_double(table, "resolution_rms", i, rmsres);
14758             cpl_table_set_int(table, "nlines", i, nlines);
14759         }
14760         else
14761             cpl_table_set_int(table, "nlines", i, 0);
14762     }
14763 
14764     if (cpl_table_has_valid(table, "wavelength"))
14765         return table;
14766 
14767     cpl_table_delete(table);
14768 
14769     return NULL;
14770     
14771 }
14772 
14773 
14791 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14792                             int ystart, int yend, double wstart, double wend)
14793 {
14794     const char *func = "mos_integrate_signal";
14795 
14796     double sum;
14797     float *sdata;
14798     float *wdata;
14799     int    nx, ny;
14800     int    x, y;
14801     
14802 
14803     if (image == NULL || wavemap == NULL) { 
14804         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14805         return 0.0;
14806     }
14807 
14808     if (ystart > yend || wstart >= wend) {
14809         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14810         return 0.0;
14811     }
14812 
14813     nx = cpl_image_get_size_x(image);
14814     ny = cpl_image_get_size_y(image);
14815 
14816     if (!(nx == cpl_image_get_size_x(wavemap) 
14817         && ny == cpl_image_get_size_y(wavemap))) {
14818         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14819         return 0.0;
14820     }
14821 
14822     if (ystart < 0 || yend > ny) {
14823         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14824         return 0.0;
14825     }
14826 
14827     sdata = cpl_image_get_data(image);
14828     wdata = cpl_image_get_data(wavemap);
14829 
14830     sdata += ystart*nx;
14831     wdata += ystart*nx;
14832 
14833     sum = 0.0;
14834     for (y = ystart; y < yend; y++) {
14835         for (x = 0; x < nx; x++) {
14836             if (wdata[x] < wstart || wdata[x] > wend)
14837                 continue;
14838             sum += sdata[x];
14839         }
14840         sdata += nx;
14841         wdata += nx;
14842     }
14843 
14844     return sum;
14845 
14846 }
14847 
14848 /****************************************************************************
14849  * From this point on, the instrument dependent functions are added:
14850  * they are functions that retrieve information that is stored in
14851  * the data headers in some instrument specific way, such as the
14852  * location of overscans, the slits positions on the telescope
14853  * focal plane, the gain factor, etc.
14854  */
14855 
14856 
14879 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14880 {
14881     const char *func = "mos_load_slits_fors_mxu";
14882 
14883     cpl_table  *slits;
14884     char        keyname[MAX_COLNAME];
14885     const char *instrume;
14886     const char *target_name;
14887     float       slit_x;
14888     float       slit_y;
14889     float       length;
14890     float       slit_width;
14891 /*    double      arc2mm = 0.53316;         */
14892     double      arc2mm = 0.528;
14893     int         nslits;
14894     int         slit_id;
14895     int         fors;
14896     int         chip;
14897     int         found;
14898 
14899     /*
14900      * The limits below are used to exclude from the loaded slit list
14901      * any slit that surely doesn't belong to the used chip. This is
14902      * a way to reduce the chance of ambiguous slit identification.
14903      */
14904 
14905     float      low_limit1 = 10.0;
14906     float      hig_limit2 = 30.0;
14907 
14908 
14909     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14910         return NULL;
14911     }
14912 
14913     if (header == NULL) {
14914         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14915         return NULL;
14916     }
14917 
14918 
14919     /*
14920      * See if this is FORS1 or FORS2;
14921      */
14922 
14923     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14924 
14925     fors = 0;
14926     if (instrume[4] == '1')
14927         fors = 1;
14928     if (instrume[4] == '2')
14929         fors = 2;
14930 
14931     if (fors != 2) {
14932         cpl_msg_error(func, "Wrong instrument: %s\n"
14933                       "FORS2 is expected for MXU data", instrume);
14934         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14935         return NULL;
14936     }
14937 
14938 
14939     /*
14940      * The master and slave chips can be identified by their positions
14941      * in the chip array in the case of FORS2 data (with fors1 the chip
14942      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14943      */
14944 
14945     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14946 
14947     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14948         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14949                       "in FITS header");
14950         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14951         return NULL;
14952     }
14953 
14954     if (chip != 1 && chip != 2) {
14955         cpl_msg_error(func, "Unexpected chip position in keyword "
14956                       "ESO DET CHIP1 Y: %d", chip);
14957         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14958         return NULL;
14959     }
14960 
14961 
14962     /*
14963      * Count slits in header (excluding reference slits, and the slits
14964      * that _surely_ belong to the other chip)
14965      */
14966 
14967     nslits = 0;
14968     slit_id = 0;
14969     found = 1;
14970 
14971     while (found) {
14972         slit_id++;
14973         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14974         if (cpl_propertylist_has(header, keyname)) {
14975             slit_y = cpl_propertylist_get_double(header, keyname);
14976 
14977             if (chip == 1)
14978                 if (slit_y < low_limit1)
14979                     continue;
14980             if (chip == 2)
14981                 if (slit_y > hig_limit2)
14982                     continue;
14983                 
14984             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14985                      slit_id + 100);
14986             if (cpl_propertylist_has(header, keyname)) {
14987                 target_name = cpl_propertylist_get_string(header, keyname);
14988                 if (strncmp(target_name, "refslit", 7))
14989                     nslits++;
14990             }
14991             else
14992                 nslits++;
14993         }
14994         else
14995             found = 0;
14996     }
14997 
14998     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14999         cpl_msg_error(func, "%s while loading slits coordinates from "
15000                       "FITS header", cpl_error_get_message());
15001         cpl_error_set_where(func);
15002         return NULL;
15003     }
15004 
15005     if (nslits == 0)  {
15006         cpl_msg_error(func, "No slits coordinates found in header");
15007         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15008         return NULL;
15009     }
15010 
15011     slits = cpl_table_new(nslits);
15012     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15013     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15014     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15015     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15016     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15017     cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15018     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15019     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15020     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15021     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15022     cpl_table_set_column_unit(slits, "xwidth" , "mm");
15023 
15024     nslits = 0;
15025     slit_id = 0; 
15026     found = 1;
15027     while (found) {
15028         slit_id++;
15029         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
15030         if (cpl_propertylist_has(header, keyname)) {
15031             slit_y = cpl_propertylist_get_double(header, keyname);
15032 
15033             if (chip == 1) 
15034                 if (slit_y < low_limit1)
15035                     continue;
15036             if (chip == 2)
15037                 if (slit_y > hig_limit2)
15038                     continue;
15039 
15040             /*
15041              * Y-flip the slit position, to match CCD pixel coordinate
15042              * convention
15043              */
15044 
15045             slit_y = -slit_y;
15046 
15047             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
15048             slit_x = cpl_propertylist_get_double(header, keyname);
15049             if (cpl_error_get_code() != CPL_ERROR_NONE) {
15050                 cpl_table_delete(slits);
15051                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
15052                               keyname);
15053                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15054                 return NULL;
15055             }
15056 
15057             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
15058             length = cpl_propertylist_get_double(header, keyname);
15059             if (cpl_error_get_code() != CPL_ERROR_NONE) {
15060                 cpl_table_delete(slits);
15061                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
15062                               keyname);
15063                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15064                 return NULL;
15065             }
15066 
15067             length *= arc2mm;
15068 
15069             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id + 100);
15070             slit_width = cpl_propertylist_get_double(header, keyname);
15071             if (cpl_error_get_code() != CPL_ERROR_NONE) {
15072                 cpl_table_delete(slits);
15073                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
15074                               keyname);
15075                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15076                 return NULL;
15077             }
15078 
15079             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
15080                      slit_id + 100);
15081             if (cpl_propertylist_has(header, keyname)) {
15082                 target_name = cpl_propertylist_get_string(header, keyname);
15083                 if (strncmp(target_name, "refslit", 7)) {
15084                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15085                     cpl_table_set(slits, "xtop", nslits, slit_x);
15086                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15087                     cpl_table_set(slits, "xbottom", nslits, slit_x);
15088                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15089                     cpl_table_set(slits, "xwidth", nslits, slit_width);
15090                     nslits++;
15091                 }
15092             }
15093             else {
15094                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15095                 cpl_table_set(slits, "xtop", nslits, slit_x);
15096                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
15097                 cpl_table_set(slits, "xbottom", nslits, slit_x);
15098                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
15099                 cpl_table_set(slits, "xwidth", nslits, slit_width);
15100                 nslits++;
15101             }
15102         }
15103         else
15104             found = 0;
15105     }
15106 
15107     return slits;
15108 }
15109 
15110 
15134 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header, 
15135                                    int * nslits_out_det)
15136 {
15137     const char *func = "mos_load_slits_fors_mos";
15138 
15139     cpl_table  *slits;
15140     char        keyname[MAX_COLNAME];
15141     const char *instrume;
15142     const char *chipname;
15143     float       slit_x;
15144     int         first_slit, last_slit;
15145     cpl_size    nslits;
15146     int         slit_id;
15147     int         fors;
15148     int         chip;
15149     int         fors_is_old;
15150 
15151     /*
15152      * The Y coordinates of the slits are fixed
15153      */
15154 
15155     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
15156                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
15157                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
15158                                -102.1 };
15159     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
15160                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
15161                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
15162                                -113.9 };
15163 
15164 
15165     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15166         return NULL;
15167     }
15168 
15169     if (header == NULL) {
15170         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15171         return NULL;
15172     }
15173 
15174 
15175     /*
15176      * See if this is FORS1 or FORS2;
15177      */
15178 
15179     instrume = cpl_propertylist_get_string(header, "INSTRUME");
15180 
15181     fors = 0;
15182     if (instrume[4] == '1')
15183         fors = 1;
15184     if (instrume[4] == '2')
15185         fors = 2;
15186 
15187     if (fors == 0) {
15188         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
15189                       instrume);
15190         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15191         return NULL;
15192     }
15193 
15194     /* FIXME:
15195      * This is the way FORS1 data belong to the upgraded chips,
15196      * named "Marlene" and "Norma III". It's a quick solution,
15197      * there are hardcoded values here!!!
15198      */
15199 
15200     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
15201 
15202     if (chipname[0] == 'M' || chipname[0] == 'N')
15203         fors_is_old = 0;
15204     else
15205         fors_is_old = 1;
15206 
15207     if (fors == 1 && fors_is_old) {
15208         first_slit = 1;
15209         last_slit = 19;
15210     }
15211     else {
15212 
15213         /*
15214          * The master and slave chips can be identified by their positions
15215          * in the chip array in the case of FORS2 data: chip = 2 is the 
15216          * master, chip = 1 is the slave.
15217          */
15218 
15219         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15220 
15221         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15222             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15223                           "in FITS header");
15224             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15225             return NULL;
15226         }
15227 
15228         if (chip != 1 && chip != 2) {
15229             cpl_msg_error(func, "Unexpected chip position in keyword "
15230                           "ESO DET CHIP1 Y: %d", chip);
15231             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15232             return NULL;
15233         }
15234 
15235         if (chip == 1) {
15236             first_slit = 12;
15237             last_slit = 19;
15238         }
15239         else {
15240             first_slit = 1;
15241             last_slit = 11;
15242         }
15243     }
15244 
15245 
15246     /*
15247      * Count slits in header (excluding closed slits - i.e. those with
15248      * offsets greater than 115 mm - and the slits that do not belong 
15249      * to this chip)
15250      */
15251 
15252     nslits = 0;
15253     slit_id = 0;
15254     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15255         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15256         if (cpl_propertylist_has(header, keyname)) {
15257             slit_x = cpl_propertylist_get_double(header, keyname);
15258             if (fabs(slit_x) < 115.0)
15259                 nslits++;
15260             else
15261                 (*nslits_out_det)++;        
15262         }
15263         else {
15264             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
15265             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15266             return NULL;
15267         }
15268     }
15269 
15270     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15271         cpl_msg_error(func, "%s while loading slits coordinates from "
15272                       "FITS header", cpl_error_get_message());
15273         cpl_error_set_where(func);
15274         return NULL;
15275     }
15276 
15277     if (nslits == 0)  {
15278         cpl_msg_error(func, "No slits coordinates found in header");
15279         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15280         return NULL;
15281     }
15282 
15283     slits = cpl_table_new(nslits);
15284     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15285     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15286     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15287     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15288     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15289     cpl_table_new_column(slits, "xwidth",  CPL_TYPE_DOUBLE);
15290     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15291     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15292     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15293     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15294     cpl_table_set_column_unit(slits, "xwidth" , "mm");
15295 
15296     nslits = 0;
15297     slit_id = 0;
15298     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
15299         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
15300         slit_x = cpl_propertylist_get_double(header, keyname);
15301         if (fabs(slit_x) < 115.0) {
15302             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
15303             cpl_table_set(slits, "xtop", nslits, slit_x);
15304             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
15305             cpl_table_set(slits, "xbottom", nslits, slit_x);
15306             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
15307             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d WIDTH", slit_id);
15308             double slit_width = cpl_propertylist_get_double(header, keyname);
15309             cpl_table_set(slits, "xwidth", nslits, slit_width);
15310             nslits++;
15311         }
15312     }
15313 
15314     return slits;
15315 }
15316 
15317 
15341 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
15342 {
15343     const char *func = "mos_load_slits_fors_lss";
15344 
15345     cpl_table  *slits;
15346     char       *slit_name;
15347     const char *instrume;
15348     int         fors;
15349     int         chip;
15350     float       ytop;
15351     float       ybottom;
15352 
15353     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15354         return NULL;
15355     }
15356 
15357     if (header == NULL) {
15358         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15359         return NULL;
15360     }
15361 
15362 
15363     /*
15364      * See if this is FORS1 or FORS2;
15365      */
15366 
15367     instrume = cpl_propertylist_get_string(header, "INSTRUME");
15368 
15369     fors = 0;
15370     if (instrume[4] == '1')
15371         fors = 1;
15372     if (instrume[4] == '2')
15373         fors = 2;
15374 
15375     if (fors == 0) {
15376         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
15377                       instrume);
15378         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15379         return NULL;
15380     }
15381 
15382     if (fors == 1) {
15383         ytop = 109.94;
15384         ybottom = -109.94;
15385     }
15386     else {
15387 
15388         /*
15389          * The master and slave chips can be identified by their positions
15390          * in the chip array in the case of FORS2 data: chip = 2 is the 
15391          * master, chip = 1 is the slave.
15392          */
15393 
15394         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
15395 
15396         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15397             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
15398                           "in FITS header");
15399             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15400             return NULL;
15401         }
15402 
15403         if (chip != 1 && chip != 2) {
15404             cpl_msg_error(func, "Unexpected chip position in keyword "
15405                           "ESO DET CHIP1 Y: %d", chip);
15406             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15407             return NULL;
15408         }
15409 
15410         if (chip == 1) {
15411             ytop = 30.0;
15412             ybottom = -109.94;
15413         }
15414         else {
15415             ytop = 109.94;
15416             ybottom = -20.0;
15417         }
15418     }
15419 
15420 
15421     slits = cpl_table_new(1);
15422     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15423     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15424     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15425     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15426     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15427     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15428     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15429     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15430     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15431 
15432     slit_name = (char *)cpl_propertylist_get_string(header, 
15433                                                     "ESO INS SLIT NAME");
15434 
15435     cpl_table_set(slits, "ytop", 0, ytop);
15436     cpl_table_set(slits, "ybottom", 0, ybottom);
15437 
15438     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
15439         cpl_table_set_int(slits, "slit_id", 0, 1);
15440         cpl_table_set(slits, "xbottom", 0, -0.075);
15441         cpl_table_set(slits, "xtop", 0, 0.075);
15442     }
15443     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
15444         cpl_table_set_int(slits, "slit_id", 0, 2);
15445         cpl_table_set(slits, "xbottom", 0, 5.895);
15446         cpl_table_set(slits, "xtop", 0, 6.105);
15447     }
15448     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
15449         cpl_table_set_int(slits, "slit_id", 0, 3);
15450         cpl_table_set(slits, "xbottom", 0, -6.135);
15451         cpl_table_set(slits, "xtop", 0, -5.865);
15452     }
15453     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
15454         cpl_table_set_int(slits, "slit_id", 0, 4);
15455         cpl_table_set(slits, "xbottom", 0, 11.815);
15456         cpl_table_set(slits, "xtop", 0, 12.185);
15457     }
15458     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
15459         cpl_table_set_int(slits, "slit_id", 0, 5);
15460         cpl_table_set(slits, "xbottom", 0, -12.265);
15461         cpl_table_set(slits, "xtop", 0, -11.735);
15462     }
15463     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
15464         cpl_table_set_int(slits, "slit_id", 0, 6);
15465         cpl_table_set(slits, "xbottom", 0, 17.655);
15466         cpl_table_set(slits, "xtop", 0, 18.345);
15467     }
15468     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
15469         cpl_table_set_int(slits, "slit_id", 0, 7);
15470         cpl_table_set(slits, "xbottom", 0, -18.425);
15471         cpl_table_set(slits, "xtop", 0, -17.575);
15472     }
15473     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
15474         cpl_table_set_int(slits, "slit_id", 0, 8);
15475         cpl_table_set(slits, "xbottom", 0, 23.475);
15476         cpl_table_set(slits, "xtop", 0, 24.525);
15477     }
15478     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
15479         cpl_table_set_int(slits, "slit_id", 0, 9);
15480         cpl_table_set(slits, "xbottom", 0, -24.66);
15481         cpl_table_set(slits, "xtop", 0, -23.34);
15482     }
15483     else {
15484         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
15485                       slit_name);
15486         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15487         cpl_table_delete(slits);
15488         return NULL;
15489     }
15490 
15491     return slits;
15492 }
15493 
15494 
15509 double mos_get_gain_vimos(cpl_propertylist *header)
15510 {
15511     const char *func = "mos_get_gain_vimos";
15512 
15513     double gain = -1.0;
15514 
15515 
15516     if (cpl_error_get_code() != CPL_ERROR_NONE)
15517         return gain;
15518 
15519     if (header == NULL) {
15520         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15521         return gain;
15522     }
15523 
15524     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
15525     if (cpl_error_get_code()) {
15526         cpl_error_set_where(func);
15527         gain = -1.0;
15528     }
15529 
15530     return gain;
15531 
15532 }
15533 
15534 
15554 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
15555 {
15556     const char *func = "mos_load_slits_vimos";
15557 
15558     cpl_table *slits;
15559     char       keyname[MAX_COLNAME];
15560     float      slit_x;
15561     float      slit_y;
15562     float      dim_x;
15563     float      dim_y;
15564     int        nslits;
15565     int        slit_id;
15566     int        curved;
15567     int        i;
15568 
15569 
15570     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15571         return NULL;
15572     }
15573 
15574     if (header == NULL) {
15575         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15576         return NULL;
15577     }
15578 
15579     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15580 
15581     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15582         cpl_error_set_where(func);
15583         return NULL;
15584     }
15585 
15586     slits = cpl_table_new(nslits);
15587     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15588     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15589     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15590     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15591     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15592     cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15593     cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15594     cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15595     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15596     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15597     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15598     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15599     cpl_table_set_column_unit(slits, "xwidth", "mm");
15600     cpl_table_set_column_unit(slits, "ywidth", "mm");
15601 
15602     for (i = 0; i < nslits; i++) {
15603         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15604         slit_id = cpl_propertylist_get_int(header, keyname);
15605         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15606             cpl_error_set_where(func);
15607             return NULL;
15608         }
15609         sprintf(keyname, "ESO INS SLIT%d X", i+1);
15610         slit_x = cpl_propertylist_get_double(header, keyname);
15611         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15612             cpl_error_set_where(func);
15613             return NULL;
15614         }
15615         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15616         slit_y = cpl_propertylist_get_double(header, keyname);
15617         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15618             cpl_error_set_where(func);
15619             return NULL;
15620         }
15621         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15622         dim_x = cpl_propertylist_get_double(header, keyname);
15623         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15624             cpl_error_set_where(func);
15625             return NULL;
15626         }
15627 
15628         sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15629         if (cpl_propertylist_has(header, keyname)) {
15630             curved = 1;
15631         }
15632         else {
15633             sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15634             curved = 0;
15635         }
15636         dim_y = cpl_propertylist_get_double(header, keyname);
15637         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15638             cpl_error_set_where(func);
15639             return NULL;
15640         }
15641 
15642         cpl_table_set_int(slits, "slit_id", i, slit_id);
15643         cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15644         cpl_table_set(slits, "ytop", i, slit_y);
15645         cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15646         cpl_table_set(slits, "ybottom", i, slit_y);
15647         cpl_table_set(slits, "xwidth", i, dim_x);
15648         cpl_table_set(slits, "ywidth", i, dim_y);
15649         cpl_table_set_int(slits, "curved", i, curved);
15650     }
15651 
15652     return slits;
15653 }
15654 
15655 
15665 int mos_check_multiplex(cpl_table *slits)
15666 {
15667     cpl_propertylist *sort;
15668     int               nrow;
15669     int               i, multiplex, xprev, xcur;
15670     double            prev, cur;
15671     double            tolerance = 1.0; // About spatially aligned slits (mm)
15672 
15673 
15674     /*
15675      * Create an auxiliary column containing a sort of integer
15676      * x coordinate of the slit, to guarantee that slits at the
15677      * same spatial offset are recognised immediately as in spectral 
15678      * multiplexing.
15679      */
15680 
15681     sort = cpl_propertylist_new();
15682     cpl_propertylist_append_bool(sort, "xtop", 0);
15683     cpl_table_sort(slits, sort);
15684     cpl_propertylist_delete(sort);
15685 
15686     prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15687     cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15688     cpl_table_set_int(slits, "xind", 0, prev);   // cast to int is intentional
15689     nrow = cpl_table_get_nrow(slits);
15690     for (i = 1; i < nrow; i++) {
15691         cur = cpl_table_get_double(slits, "xtop", i, NULL);
15692         if (fabs(prev - cur) > tolerance)
15693             prev = cur;
15694         cpl_table_set_int(slits, "xind", i, prev);
15695     }
15696 
15697     /*
15698      * Now sort according to increasing (integer) x positions, and when
15699      * those are equal (multiplexed) according to the increasing y position.
15700      */
15701 
15702     sort = cpl_propertylist_new();
15703     cpl_propertylist_append_bool(sort, "xind", 0);
15704     cpl_propertylist_append_bool(sort, "ytop", 0);
15705     cpl_table_sort(slits, sort);
15706     cpl_propertylist_delete(sort);
15707 
15708     /*
15709      * Now assign to each slit its multiplex order.
15710      */
15711 
15712     multiplex = 0;
15713     cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15714     xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15715     cpl_table_set_int(slits, "multiplex", 0, multiplex);
15716     nrow = cpl_table_get_nrow(slits);
15717     for (i = 1; i < nrow; i++) {
15718         xcur = cpl_table_get_int(slits, "xind", i, NULL);
15719         if (xcur == xprev) {
15720             multiplex++;
15721         }
15722         else {
15723             xprev = xcur;
15724             multiplex = 0;
15725         }
15726         cpl_table_set_int(slits, "multiplex", i, multiplex);
15727     }
15728 
15729     cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15730 
15731     cpl_table_erase_column(slits, "xind");
15732 
15733     return 1 + cpl_table_get_column_max(slits, "multiplex");
15734 
15735 }
15736 
15737 
15764 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
15765                                     int check_consistency)
15766 {
15767     const char *func = "mos_load_overscans_vimos";
15768 
15769     int        nx = 0;
15770     int        ny = 0;
15771     int        px = 0;
15772     int        py = 0;
15773     int        ox = 0;
15774     int        oy = 0;
15775     int        vx = 0;
15776     int        vy = 0;
15777     int        nrows;
15778     cpl_table *overscans;
15779 
15780 
15781     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15782         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15783         return NULL;
15784     }
15785 
15786     if (header == NULL) {
15787         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15788         return NULL;
15789     }
15790 
15791     if (cpl_propertylist_has(header, "NAXIS1"))
15792         nx = cpl_propertylist_get_int(header, "NAXIS1");
15793     if (cpl_propertylist_has(header, "NAXIS2"))
15794         ny = cpl_propertylist_get_int(header, "NAXIS2");
15795     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15796         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15797     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15798         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15799     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15800         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15801     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15802         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15803     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15804         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15805     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15806         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15807 
15808     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15809         cpl_msg_error(func, "Missing overscan keywords in header");
15810         cpl_error_set_where(func);
15811         return NULL;
15812     }
15813 
15814     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15815         cpl_msg_error(func, "Missing overscan keywords in header");
15816         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15817         return NULL;
15818     }
15819 
15820     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15821         if (check_consistency) {
15822             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15823             return NULL;
15824         }
15825         else {
15826             cpl_msg_debug(func, "Overscans description conflicts with "
15827                           "reported image sizes, "
15828                           "%d + %d + %d != %d or "
15829                           "%d + %d + %d != %d",
15830                           px, vx, ox, nx,
15831                           py, vy, oy, ny);
15832         }
15833     }
15834 
15835     nrows = 0;
15836     if (px > 0)
15837         nrows++;
15838     if (ox > 0)
15839         nrows++;
15840     if (py > 0)
15841         nrows++;
15842     if (oy > 0)
15843         nrows++;
15844 
15845     if (nrows > 2) {
15846         cpl_msg_error(func, "Unexpected overscan regions "
15847                       "(both in X and Y direction)");
15848         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15849         return NULL;
15850     }
15851 
15852 
15853     /*
15854      * A row is added for the description of the valid region of the
15855      * exposure the input header belongs to.
15856      */
15857 
15858     nrows++;
15859 
15860     overscans = cpl_table_new(nrows);
15861     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15862     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15863     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15864     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15865 
15866     nrows = 0;
15867 
15868     cpl_table_set_int(overscans, "xlow", nrows, px);
15869     cpl_table_set_int(overscans, "ylow", nrows, py);
15870     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15871     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15872     nrows++;
15873 
15874     if (px > 0) {
15875         cpl_table_set_int(overscans, "xlow", nrows, 0);
15876         cpl_table_set_int(overscans, "ylow", nrows, 0);
15877         cpl_table_set_int(overscans, "xhig", nrows, px);
15878         cpl_table_set_int(overscans, "yhig", nrows, ny);
15879         nrows++;
15880     }
15881 
15882     if (ox > 0) {
15883         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15884         cpl_table_set_int(overscans, "ylow", nrows, 0);
15885         cpl_table_set_int(overscans, "xhig", nrows, nx);
15886         cpl_table_set_int(overscans, "yhig", nrows, ny);
15887         nrows++;
15888     }
15889 
15890     if (py > 0) {
15891         cpl_table_set_int(overscans, "xlow", nrows, 0);
15892         cpl_table_set_int(overscans, "ylow", nrows, 0);
15893         cpl_table_set_int(overscans, "xhig", nrows, nx);
15894         cpl_table_set_int(overscans, "yhig", nrows, py);
15895         nrows++;
15896     }
15897 
15898     if (oy > 0) {
15899         cpl_table_set_int(overscans, "xlow", nrows, 0);
15900         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15901         cpl_table_set_int(overscans, "xhig", nrows, nx);
15902         cpl_table_set_int(overscans, "yhig", nrows, ny);
15903         nrows++;
15904     }
15905 
15906     return overscans;
15907 
15908 }
15909 
15910 
15911 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15912 {
15913     const char *func = "mos_load_overscans_fors";
15914 
15915     int        nports;
15916     int        nx = 0;
15917     int        ny = 0;
15918     int        px = 0;
15919     int        py = 0;
15920     int        ox = 0;
15921     int        oy = 0;
15922     int        rebin;
15923     int        nrows;
15924     cpl_table *overscans;
15925 
15926 
15927     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15928         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15929         return NULL;
15930     }
15931 
15932     if (header == NULL) {
15933         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15934         return NULL;
15935     }
15936 
15937     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15938         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15939 
15940     if (nports == 4                                        && 
15941         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15942         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15943 
15944         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15945 
15946         overscans = cpl_table_new(3);
15947         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15948         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15949         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15950         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15951 
15952         px = 16 / rebin;
15953         ox = 16 / rebin;
15954         nx = 2080 / rebin;
15955         ny = 2048 / rebin;
15956         nrows = 0;
15957 
15958         cpl_table_set_int(overscans, "xlow", nrows, px);
15959         cpl_table_set_int(overscans, "ylow", nrows, py);
15960         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15961         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15962         nrows++;
15963 
15964         cpl_table_set_int(overscans, "xlow", nrows, 0);
15965         cpl_table_set_int(overscans, "ylow", nrows, 0);
15966         cpl_table_set_int(overscans, "xhig", nrows, px);
15967         cpl_table_set_int(overscans, "yhig", nrows, ny);
15968         nrows++;
15969 
15970         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15971         cpl_table_set_int(overscans, "ylow", nrows, 0);
15972         cpl_table_set_int(overscans, "xhig", nrows, nx);
15973         cpl_table_set_int(overscans, "yhig", nrows, ny);
15974         nrows++;
15975     }
15976     else {
15977         overscans = mos_load_overscans_vimos(header, 0);
15978     }
15979 
15980     return overscans;
15981 
15982 }
15983 
16015 #define READY 1
16016 #ifdef READY
16017 
16018 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
16019                                        int samples, int order)
16020 {
16021 
16022     const char *func = "mos_montecarlo_polyfit";
16023 
16024     cpl_polynomial *p;
16025     cpl_polynomial *q;
16026     cpl_vector     *listx;
16027     cpl_vector     *listy;
16028     double          err;
16029     double         *x;
16030     double         *px;
16031     double         *x_eval;
16032     double         *px_eval;
16033     double         *sigma;
16034     double         *vy;
16035     double         *dy;
16036     int             npoints, nevaluate;
16037     int             i, j;
16038 
16039 
16040     if (points == NULL || evaluate == NULL) {
16041         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
16042         return NULL;
16043     }
16044 
16045     if (!cpl_table_has_column(points, "x")) {
16046         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16047         return NULL;
16048     }
16049 
16050     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
16051         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16052         return NULL;
16053     }
16054 
16055     if (cpl_table_has_invalid(points, "x")) {
16056         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16057         return NULL;
16058     }
16059 
16060     if (!cpl_table_has_column(points, "y")) {
16061         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16062         return NULL;
16063     }
16064 
16065     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
16066         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16067         return NULL;
16068     }
16069 
16070     if (cpl_table_has_invalid(points, "y")) {
16071         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16072         return NULL;
16073     }
16074 
16075     if (cpl_table_has_column(points, "y_err")) {
16076 
16077         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
16078             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16079             return NULL;
16080         }
16081     
16082         if (cpl_table_has_invalid(points, "y_err")) {
16083             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16084             return NULL;
16085         }
16086     }
16087 
16088     if (!cpl_table_has_column(evaluate, "x")) {
16089         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
16090         return NULL;
16091     }
16092 
16093     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
16094         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
16095         return NULL;
16096     }
16097 
16098     if (cpl_table_has_invalid(evaluate, "x")) {
16099         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16100         return NULL;
16101     }
16102 
16103     if (samples < 2 || order < 0) {
16104         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
16105         return NULL;
16106     }
16107 
16108     npoints = cpl_table_get_nrow(points);
16109     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
16110     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
16111 
16112     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
16113 
16114     if (!cpl_table_has_column(points, "y_err")) {
16115         err = sqrt(err);
16116         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
16117         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
16118         cpl_msg_info(func, "Error column not found - set to %f\n", err);
16119     }
16120 
16121     /*
16122      * Create columns containing modeled values at each x
16123      */
16124 
16125     if (cpl_table_has_column(points, "px"))
16126         cpl_table_erase_column(points, "px");
16127     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
16128     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
16129     x = cpl_table_get_data_double(points, "x");
16130     px = cpl_table_get_data_double(points, "px");
16131     for (i = 0; i < npoints; i++)
16132         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
16133 
16134     nevaluate = cpl_table_get_nrow(evaluate);
16135 
16136     if (cpl_table_has_column(evaluate, "px"))
16137         cpl_table_erase_column(evaluate, "px");
16138     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
16139     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
16140     x_eval = cpl_table_get_data_double(evaluate, "x");
16141     px_eval = cpl_table_get_data_double(evaluate, "px");
16142     for (i = 0; i < nevaluate; i++)
16143         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
16144 
16145     /*
16146      * Initialise column with sigma
16147      */
16148 
16149     if (cpl_table_has_column(evaluate, "sigma"))
16150         cpl_table_erase_column(evaluate, "sigma");
16151     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
16152     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
16153     sigma = cpl_table_get_data_double(evaluate, "sigma");
16154 
16155     /*
16156      * Compute varied y cordinates to fit
16157      */
16158 
16159     if (cpl_table_has_column(points, "vy"))
16160         cpl_table_erase_column(points, "vy");
16161     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
16162     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
16163     vy = cpl_table_get_data_double(points, "vy");
16164     dy = cpl_table_get_data_double(points, "y_err");
16165     cpl_vector_unwrap(listy);
16166     listy = cpl_vector_wrap(npoints, vy);
16167 
16168     for (i = 0; i < samples; i++) {
16169         for (j = 0; j < npoints; j++)
16170             vy[j] = px[j] + dy[j] * mos_randg(1);
16171         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
16172         for (j = 0; j < nevaluate; j++)
16173             sigma[j] += fabs(px_eval[j] 
16174                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
16175         cpl_polynomial_delete(q);
16176     }
16177 
16178     /* 
16179      * Factor 1.25 to convert average deviation to sigma 
16180      */
16181 
16182     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
16183     cpl_table_divide_scalar(evaluate, "sigma", samples);
16184 
16185     cpl_vector_unwrap(listx);
16186     cpl_vector_unwrap(listy);
16187 
16188     return p;
16189 }
16190 
16191 #endif
16192 
16215 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 
16216                                    double gain, double bias)
16217 {
16218     float *data;
16219     int    npix, i;
16220 
16221 
16222     if (image == NULL)
16223         return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16224 
16225     if (ron < 0.0 || gain <= FLT_EPSILON)
16226         return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
16227 
16228     data = cpl_image_get_data_float(image);
16229     npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
16230     ron *= ron;
16231 
16232     for (i = 0; i < npix; i++) {
16233         if (data[i] < bias) {
16234             data[i] += sqrt(ron) * mos_randg(1);
16235         }
16236         else {
16237             data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
16238         }
16239     }
16240 
16241     return CPL_ERROR_NONE;
16242 }
16243 
16244 
16259 cpl_error_code mos_refmask_find_gaps(cpl_mask  *refmask,
16260                                      cpl_image *master_flat,
16261                                      double     level)
16262 {
16263     int          nx     = cpl_mask_get_size_x(refmask);
16264     int          ny     = cpl_mask_get_size_y(refmask);
16265 
16266     int        * xpos   = cpl_calloc(sizeof(int), ny);
16267 
16268     cpl_image  * filtered = cpl_image_duplicate(master_flat);
16269     cpl_mask   * kernel = cpl_mask_new(9, 3);
16270     cpl_vector * v      = cpl_vector_new(ny);
16271     cpl_vector * truev;
16272     int          nvalid = 0;
16273     double     * flats  = cpl_vector_get_data(v);
16274 
16275     double       median, stdev, delta;
16276 
16277     int          i, kill;
16278 
16279     cpl_mask_not(kernel);
16280     cpl_image_filter_mask(filtered, master_flat, kernel, 
16281                           CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
16282     cpl_mask_delete(kernel);
16283 
16284     for (i = 1; i <= ny; i++) {
16285         int j = 0;
16286 
16287         do j++;
16288         while (!cpl_mask_get(refmask, j, i) && j < nx);
16289 
16290         if (j < nx) {
16291             int rejected;
16292 
16293             xpos[i - 1] = j;
16294             flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
16295             nvalid++;
16296         }
16297         else {
16298             xpos[i - 1] = -1;
16299         }
16300     }
16301 
16302     if (nvalid == 0)
16303         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16304 
16305     truev = cpl_vector_wrap(nvalid, flats);
16306 
16307     median = cpl_vector_get_median(truev);
16308 
16309     if (level < 0.0)
16310        stdev = cpl_vector_get_stdev(truev);
16311 
16312     cpl_vector_unwrap(truev);
16313     cpl_vector_delete(v);
16314 
16315     for (i = 1; i <= ny; i++) {
16316         if (xpos[i - 1] > 0) {
16317             int    rejected;
16318             double kappa = 1.5;
16319 
16320             delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
16321 
16322             if (level < 0.0)
16323                 kill = fabs(delta) > stdev * kappa;
16324             else
16325                 kill = delta < level;
16326 
16327             if (kill) {
16328                 int j = 0;
16329             
16330                 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
16331                     cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
16332                     j++;
16333                 }
16334             }
16335         }
16336     }
16337 
16338     cpl_image_delete(filtered);
16339     cpl_free(xpos);
16340 
16341     return cpl_error_get_code();
16342 }
16343 
16351 cpl_error_code mos_saturation_process(cpl_image * image)
16352 {
16353     int     nx    = cpl_image_get_size_x(image);
16354     int     ny    = cpl_image_get_size_y(image);
16355     int     npix  = nx * ny;
16356     float * sdata = cpl_image_get_data_float(image);
16357 
16358     int count, i, j, k;
16359 
16360     /*
16361      * This is used to avoid saturation level coded with pixel value zero
16362      * To make it more robust against random 0.0 values, check that also
16363      * next pixel along the spatial direction is 0.0.
16364      */
16365 
16366     //This could be applied only to raw images, but it is being applied
16367     //to already bias/overscan processed images, which doesn't make sense.
16368 //    for (i = 0; i < npix - nx; i++)
16369 //        if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
16370 //            sdata[i] = 65535.0;
16371 
16372 //    for (i = npix - nx; i < npix; i++)
16373 //        if (sdata[i] == 0.0) 
16374 //            sdata[i] = 65535.0;
16375 
16376     /*
16377      * This is a dirty trick to overcome saturations (making up a false
16378      * tip on their flat tops). This should be useless with a better
16379      * peak detection algorithm.
16380      */
16381 
16382     for (i = 0; i < npix; i++) {
16383         if (sdata[i] >= 65535.0) {
16384             count = 0;
16385             for (j = i; j < npix; j++) {
16386                 if (sdata[j] < 65535.0) {
16387                     break;
16388                 }
16389                 else {
16390                     count++;
16391                 }
16392             }
16393             if (count < 30 && count > 2) {
16394                 for (j = i; j < i + count/2; j++)
16395                     sdata[j] = sdata[i] + 1000.0 * (j - i);
16396                 if (count % 2 != 0) {
16397                     sdata[j] = sdata[j-1] + 1000.0;
16398                     j++;
16399                 }
16400                 for (k = j; k <= i + count; k++)
16401                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
16402                 i = k;
16403             }
16404         }
16405     }
16406 
16407     return cpl_error_get_code();
16408 }
16409 
16410 
16419 cpl_error_code mos_subtract_background(cpl_image * image)
16420 {
16421     /*
16422      * Create and subtract background
16423      */
16424 
16425     cpl_image * bimage = mos_arc_background(image, 15, 15);
16426     cpl_image_subtract(image, bimage);
16427     cpl_image_delete(bimage);
16428 
16429     return cpl_error_get_code();
16430 }
16431 
16432 
16449 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 
16450                                     int nscience, float tolerance)
16451 {
16452     int i, j;
16453 
16454     cpl_table *summary;
16455     int summary_nobjs = 0;
16456  
16457     int nobjs;
16458 
16459     int nmatches;
16460     int nslits = cpl_table_get_nrow(slitss[0]);
16461 
16462     int maxobjs;
16463     int k, m;
16464     int nstokes, sstokes;
16465 
16466     cpl_table **work;
16467 
16468     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
16469 
16470 
16471     /* 
16472      * First we build a table listing the offset of each detected
16473      * object at each angle and each beam, from the bottom of each 
16474      * slit spectrum, and the pair that slit spectrum belongs to.
16475      * This summary table will have as many rows as objects found 
16476      * in total at all angles.
16477      */
16478 
16479     for (j = 0; j < nscience; j++) {
16480         int c_nobjs = mos_get_nobjects(slitss[j]);
16481         if (!c_nobjs) 
16482             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
16483         summary_nobjs += c_nobjs;
16484     }
16485 
16486     summary = cpl_table_new(summary_nobjs);
16487 
16488     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
16489     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
16490     cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
16491     cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
16492 
16493     /*
16494      * Fill the summary table with data from all objects:
16495      */
16496 
16497     nobjs = 0;
16498 
16499     /* Loop on all object tables (one for each angle) */
16500     for (j = 0; j < nscience; j++) {
16501         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
16502 
16503         /* Loop on all slits found on first - i.e., ALL - object table */
16504         for (k = 0; k < nslits; k++) {
16505 
16506             /* Loop on all objects found on each object table */
16507             for (m = 0; m < c_maxobjs; m++) {
16508                 int null;
16509                 char *name = cpl_sprintf("object_%d", m + 1);
16510                 double obj = cpl_table_get_double(slitss[j], name, k, &null);
16511                 int pos;
16512                 int pair;
16513 
16514                 cpl_free(name);
16515 
16516                 if (null) 
16517                     break;  /* No object #m+1 in this slit - go to next slit */
16518 
16519                 /*
16520                  * Copy necessary object data to summary table. Note 
16521                  * that the absolute object position (row) in the
16522                  * rectified image is made relative to the bottom
16523                  * position (row) of the current slit.
16524                  */ 
16525         
16526                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
16527                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
16528                 cpl_table_set(summary, "absolute", nobjs, obj);
16529                 cpl_table_set(summary, "pos", nobjs, pos);
16530                 cpl_table_set(summary, "offset", nobjs, obj - pos);
16531                 cpl_table_set(summary, "pair", nobjs, pair);
16532 
16533                 nobjs++;
16534             }
16535         }
16536     }
16537 
16538 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
16539 
16540     /* 
16541      * Perform the intersection: what are the objects belonging
16542      * to the same slit (same pair ordinary + extraordinary) which 
16543      * are observed at the same offset at all angles? Those are
16544      * the polarimetric objects.
16545      */
16546 
16547     nmatches = 0;
16548     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
16549 
16550     /*
16551      * We loop on the objects of the first-angle object table as 
16552      * reference, and check whether those objects are present also
16553      * at *all* other angles. Note that the loop advances by pairs.
16554      * If the top (k = 0) slit spectrum is not an ordinary beam,
16555      * it is ignored. The loop advances by pairs, starting at the
16556      * first complete pair. It is implicitely assumed that the 
16557      * slit spectrum on top is always from the ordinary beam, and 
16558      * the spectrum below (k+1) its extraordinary match.
16559      */
16560 
16561     for (k = 0; k < nslits; k+=2) {
16562         int slitmatches = 0;
16563 
16564         if (k + 1 < nslits ) {
16565             if (cpl_table_get_int(slitss[0], "pair_id",  k, NULL) !=
16566                 cpl_table_get_int(slitss[0], "pair_id",  k + 1, NULL)) {
16567 
16568                 /*
16569                  * This is not an ordinary beam - advance to next slit.
16570                  */
16571 
16572                 /* It will be incremented by two, so the effect is like k++ */
16573                 k--;
16574 
16575                 continue;
16576             }
16577         }
16578 
16579         for (m = 0; m < maxobjs; m++) {
16580             int null;
16581             char *name = cpl_sprintf("object_%d", m + 1);
16582             double obj = cpl_table_get_double(slitss[0], name, k, &null);
16583             double pos;
16584             int pair;
16585 
16586             char *name_obj = NULL;
16587             char *name_start = NULL;
16588             char *name_end = NULL;
16589             char *name_row = NULL;
16590             char *name_row_s = NULL;
16591 
16592             char *name_start_o = NULL;
16593             char *name_end_o = NULL;
16594             char *name_row_o = NULL;
16595             char *name_start_v = NULL;
16596             char *name_end_v = NULL;
16597             char *name_obj_v = NULL;
16598 
16599             int start, end;
16600             int length;
16601  
16602             int selected;
16603             int v, start_v, end_v;
16604             double min_v, obj_v;
16605 
16606 
16607             cpl_free(name);
16608 
16609             if (null) 
16610                 break;
16611 
16612             /*
16613              * Each object of the first object table belongs to a
16614              * slit spectrum (k). This slit spectrum has a position
16615              * in the rectified image, and it belongs to a given 
16616              * ordinary + extraordinary pair.
16617              */
16618      
16619             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
16620             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
16621 
16622             /*
16623              * Now from the summary table we can select all objects
16624              * which have the same offset (obj - pos) within all slit
16625              * spectra belonging to the same ordinary + extraordinary 
16626              * pair (at all angles).
16627              */
16628 
16629             cpl_table_select_all(summary);  /* Reset selection */
16630 
16631             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16632             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16633                                           obj - pos + tolerance);
16634             selected = 
16635             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16636                                           obj - pos - tolerance);
16637 
16638 
16639             /*
16640              * If this object were observed at all angles (nscience) and 
16641              * at all beams (2), we should have selected exactly 2*nscience
16642              * objects. If not, this is not a polarimetric object, and it
16643              * is discarded from the intersection.
16644              */
16645             
16646             if (selected != nscience * 2) 
16647                 continue;
16648 
16649             /*
16650              * If we reach this point we have found one valid polarimetric
16651              * object, that must be inserted in the intersection object
16652              * table.
16653              */
16654  
16655             slitmatches++;
16656 
16657             /*
16658              * Names of the columns of the output table where the
16659              * object information needs to be copied. Note that a
16660              * new column is created, the "row_stokes_#", where the
16661              * row number of the extracted polarimetric signal is
16662              * also computed. For the moment this column will be 
16663              * left empty - it will be filled only when all matches 
16664              * are collected.
16665              */
16666 
16667             name_obj   = cpl_sprintf("object_%d",     slitmatches);
16668             name_start = cpl_sprintf("start_%d",      slitmatches);
16669             name_end   = cpl_sprintf("end_%d",        slitmatches);
16670             name_row   = cpl_sprintf("row_%d",        slitmatches);
16671             name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16672 
16673             /*
16674              * Names of the columns of the input table where the
16675              * object information is available.
16676              */
16677 
16678             name_start_o = cpl_sprintf("start_%d",  m + 1);
16679             name_end_o   = cpl_sprintf("end_%d",    m + 1);
16680             name_row_o   = cpl_sprintf("row_%d",    m + 1);
16681 
16682             /*
16683              * If the output columns do not exist yet, create them.
16684              */
16685  
16686             if (!cpl_table_has_column(origslits, name_obj)) {
16687                 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16688                 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16689                 cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
16690                 cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
16691                 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16692             }
16693 
16694             /*
16695              * The current slit spectrum is k. The slit spectrum immediately
16696              * below (in the rectified image) is k+1. We need the length of
16697              * the spectrum below for computing the _absolute_ coordinates
16698              * of the objects in the rectified image in both beams.
16699              */
16700  
16701             length = cpl_table_get_int(origslits, "length", k + 1, &null);
16702 
16703             /* NEW:
16704              * Names of the columns of the input table where
16705              * the information of the corresponding object of 
16706              * the next beam is available.
16707              */
16708 
16709             for (v = 0; v < maxobjs; v++) {
16710                 char *name_v = cpl_sprintf("object_%d", v + 1);
16711                 double obj_v = cpl_table_get_double(slitss[0], name_v, 
16712                                                     k + 1, &null);
16713 
16714                 cpl_free(name_v);
16715 
16716                 if (null) 
16717                     break;
16718 
16719                 if (v) {
16720                     if (fabs(obj - length - obj_v) < min_v) {
16721                         min_v = fabs(obj - length - obj_v);
16722                         cpl_free(name_start_v);
16723                         cpl_free(name_end_v);
16724                         cpl_free(name_obj_v);
16725                         name_start_v = cpl_sprintf("start_%d", v + 1);
16726                         name_end_v   = cpl_sprintf("end_%d",   v + 1);
16727                         name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16728                     }
16729                 }
16730                 else {
16731                     min_v = fabs(obj - length - obj_v);
16732                     name_start_v = cpl_sprintf("start_%d", v + 1);
16733                     name_end_v   = cpl_sprintf("end_%d",   v + 1);
16734                     name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16735                 }
16736             }
16737 
16738             /*
16739              * Read from the first input object table (first angle)
16740              * the spatial window enclosing the object.
16741              */
16742 
16743             start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16744             end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
16745 
16746             /* NEW:
16747              * Spatial window of the matching object in the next beam.
16748              */
16749 
16750             start_v = cpl_table_get_int(slitss[0], name_start_v,  k + 1, &null);
16751             end_v   = cpl_table_get_int(slitss[0], name_end_v,    k + 1, &null);
16752             obj_v   = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16753 
16754             /*
16755              * Write the object coordinates in the same slit, and in the
16756              * slit below. Note that here we assume that all slits were
16757              * traced perfectly, and we compute the theoretical coords
16758              * (obj - length) within the next slit spectrum (k + 1). In
16759              * principle we should read them as well from the input
16760              * table!
16761              */
16762 
16763             cpl_table_set_double(origslits, name_obj,   k,     obj);
16764             cpl_table_set_double(origslits, name_obj,   k + 1, obj_v);
16765          // cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
16766 
16767             cpl_table_set_int(origslits,    name_start, k,     start);
16768             cpl_table_set_int(origslits,    name_start, k + 1, start_v);
16769          // cpl_table_set_int(origslits,    name_start, k + 1, start - length);
16770 
16771             cpl_table_set_int(origslits,    name_end,   k,     end);
16772             cpl_table_set_int(origslits,    name_end,   k + 1, end_v);
16773          // cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
16774 
16775             /*
16776              * "nmatches" is counting at what "reduced" image row the
16777              * extracted spectra are. Note that this is s preliminary
16778              * numbering - which is wrong: other objects may be found
16779              * in the same slit, and then the indeces would not be in
16780              * sequence. What is important is that at the end of this
16781              * loop "nmatches" would be the total number of matching 
16782              * objects. The two cpl_table_set_int() calls made here
16783              * cannot be removed - they "validate" those table elements
16784              * (see ahead). 
16785              */
16786 
16787             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
16788             nmatches++;
16789             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
16790             nmatches++;
16791 
16792             cpl_free(name_obj);
16793             cpl_free(name_start);
16794             cpl_free(name_end);
16795             cpl_free(name_row);
16796             cpl_free(name_row_s);
16797 
16798             cpl_free(name_start_o);
16799             cpl_free(name_end_o);
16800             cpl_free(name_row_o);
16801 
16802             cpl_free(name_start_v); name_start_v = NULL;
16803             cpl_free(name_end_v); name_end_v = NULL;
16804             cpl_free(name_obj_v); name_obj_v = NULL;
16805         }
16806     }
16807 
16808     /*
16809      * The summary table has fulfilled its function. If no matching 
16810      * objects are found, the function returns with an error.
16811      */
16812 
16813     cpl_table_delete(summary);
16814 
16815     if (!nmatches)
16816         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
16817 
16818     /*
16819      * Now we consider the resulting intersection object table,
16820      * listing all matches. As seen, the image row number reported
16821      * in the columns "row_#" was not really performed sequentially.
16822      * We need to renumber sequentially...
16823      * We need also to fill the "row_stokes_#" column the way the
16824      * extracted polarimetric signal will be stored in the 
16825      * reduced_pol_images...
16826      */
16827  
16828     maxobjs = mos_get_maxobjs_per_slit(origslits);
16829     nstokes = nmatches / 2;         /* nmatches is always an even number     */
16830 
16831     for (k = 0; k < nslits; k++) {
16832         if (k % 2) { /* Extraordinary beam */
16833             nstokes = sstokes;      /* Use same start value as for ordinary  */
16834         }
16835         else {       /* Ordinary beam      */
16836             sstokes = nstokes;      /* Memorise start value at ordinary beam */
16837         }
16838 
16839         for (m = 0; m < maxobjs; m++) {
16840             char *name       = cpl_sprintf("row_%d",        m + 1);
16841             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16842 
16843             if (!cpl_table_is_valid(origslits, name, k)) {
16844                 cpl_free(name);
16845                 cpl_free(namestokes);
16846                 break;
16847             }
16848             else { 
16849                 nmatches--;
16850                 nstokes--;
16851                 cpl_table_set_int(origslits, name, k, nmatches);
16852                 cpl_table_set_int(origslits, namestokes, k, nstokes);
16853             }
16854 
16855             cpl_free(name);
16856             cpl_free(namestokes);
16857         }
16858     }
16859 
16860 
16861     /*
16862      * This is done to avoid the NULL value is zero (it would invalidate
16863      * also the row_# = 0 or start_# = 0 for an object), and to enable 
16864      * working directly with the column data buffers, when using this 
16865      * table afterwards.
16866      */
16867 
16868     for (j = 0; j < maxobjs; j++) {
16869         char *name = cpl_sprintf("object_%d", j + 1);
16870         cpl_table_fill_invalid_double(origslits, name, -1);
16871         cpl_free(name);
16872 
16873         name       = cpl_sprintf("start_%d", j + 1);
16874         cpl_table_fill_invalid_int(origslits, name, -1);
16875         cpl_free(name);
16876 
16877         name       = cpl_sprintf("end_%d", j + 1);
16878         cpl_table_fill_invalid_int(origslits, name, -1);
16879         cpl_free(name);
16880 
16881         name       = cpl_sprintf("row_%d", j + 1);
16882         cpl_table_fill_invalid_int(origslits, name, -1);
16883         cpl_free(name);
16884 
16885         name       = cpl_sprintf("row_stokes_%d", j + 1);
16886         cpl_table_fill_invalid_int(origslits, name, -1);
16887         cpl_free(name);
16888     }
16889 
16890     /*********************************************************************
16891      * This tail has been added to propagate the selection of valid
16892      * objects also to the input slitss[] tables. Just eliminate all
16893      * this final part to suppress this behaviour.
16894      */
16895 
16896     /*
16897      * First of all, make a working copy and remove all columns related 
16898      * to objects from the input object tables. 
16899      */
16900 
16901     for (i = 0; i < nscience; i++) {
16902         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16903 
16904         work[i] = cpl_table_duplicate(slitss[i]);
16905 
16906         for (m = 0; m < c_maxobjs; m++) {
16907             char *object_o = cpl_sprintf("object_%d", m + 1);
16908             char *start_o  = cpl_sprintf("start_%d",  m + 1);
16909             char *end_o    = cpl_sprintf("end_%d",    m + 1);
16910             char *row_o    = cpl_sprintf("row_%d",    m + 1);
16911 
16912             cpl_table_erase_column(slitss[i], object_o);
16913             cpl_table_erase_column(slitss[i], start_o);
16914             cpl_table_erase_column(slitss[i], end_o);
16915             cpl_table_erase_column(slitss[i], row_o);
16916         }
16917     }
16918 
16919     /* 
16920      * Now just consider all the objects in the intersection table.
16921      */
16922 
16923     for (k = 0; k < nslits; k++) {
16924         for (j = 0; j < maxobjs; j++) {
16925             double object_w, object_r;
16926             int    row_w;
16927 
16928             char  *object_i = cpl_sprintf("object_%d", j + 1);
16929             char  *start_i  = cpl_sprintf("start_%d",  j + 1);
16930             char  *end_i    = cpl_sprintf("end_%d",    j + 1);
16931             char  *row_i    = cpl_sprintf("row_%d",    j + 1);
16932 
16933 
16934             if (!cpl_table_is_valid(origslits, object_i, k))
16935                 break;
16936 
16937             /* 
16938              * We have found a valid object (valid because it belongs
16939              * to the intersection). Now we look for this object in each
16940              * one of the original tables, we get its parameters, and
16941              * copy them at the right position (i.e., same position as
16942              * in intersection table). The object will be the one closest
16943              * to the object position (column object_i) in the intersection
16944              * table. Note that we examine the same row, k, in all tables.
16945              */
16946 
16947             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16948             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
16949 
16950             for (i = 0; i < nscience; i++) {
16951                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16952                 int        minpos;
16953                 double     mindiff, diff;
16954                 char      *object_o;
16955                 char      *start_o;
16956                 char      *end_o;
16957                 char      *row_o;
16958 
16959                 for (m = 0; m < c_maxobjs; m++) {
16960                     object_o = cpl_sprintf("object_%d", m + 1);
16961                     start_o  = cpl_sprintf("start_%d",  m + 1);
16962                     end_o    = cpl_sprintf("end_%d",    m + 1);
16963                     row_o    = cpl_sprintf("row_%d",    m + 1);
16964 
16965                     if (!cpl_table_is_valid(work[i], object_o, k))
16966                         break;
16967 
16968                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16969                     //row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
16970 
16971                     diff = fabs(object_w - object_r);
16972                     if (m) {
16973                         if (mindiff > diff) {
16974                             mindiff = diff;
16975                             minpos = m;
16976                         }
16977                     }
16978                     else {
16979                         mindiff = diff;
16980                         minpos = 0;
16981                     }
16982 
16983                     cpl_free(object_o);
16984                     cpl_free(start_o);
16985                     cpl_free(end_o);
16986                     cpl_free(row_o);
16987                 }
16988 
16989                 object_o = cpl_sprintf("object_%d", minpos + 1);
16990                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
16991                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
16992                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
16993 
16994                 if (!cpl_table_has_column(slitss[i], object_i)) {
16995                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16996                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
16997                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
16998                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
16999                     cpl_table_fill_invalid_double(slitss[i], object_i, -1);
17000                     cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
17001                     cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
17002                     cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
17003                 }
17004 
17005                 cpl_table_set_double(slitss[i], object_i, k,
17006                                      cpl_table_get_double(work[i], object_o, 
17007                                                           k, NULL));
17008                 cpl_table_set_int(slitss[i], start_i , k,
17009                                   cpl_table_get_int(work[i], start_o, k, NULL));
17010                 cpl_table_set_int(slitss[i], end_i , k,
17011                                   cpl_table_get_int(work[i], end_o, k, NULL));
17012                 cpl_table_set_int(slitss[i], row_i , k, row_w);
17013 
17014                 cpl_free(object_o);
17015                 cpl_free(start_o);
17016                 cpl_free(end_o);
17017                 cpl_free(row_o);
17018             }
17019 
17020             cpl_free(object_i);
17021             cpl_free(start_i);
17022             cpl_free(end_i);
17023             cpl_free(row_i);
17024         }
17025     }
17026 
17027     for (i = 0; i < nscience; i++)
17028         cpl_table_delete(work[i]);
17029 
17030     cpl_free(work);
17031 
17032 
17033     return cpl_error_get_code();
17034 }
17035 
17036 
17044 int mos_get_maxobjs_per_slit(cpl_table * slits)
17045 {
17046     int maxobjs = 1;
17047 
17048     char * colname = cpl_sprintf("object_%d", maxobjs);
17049     
17050     while (cpl_table_has_column(slits, colname)) {
17051         maxobjs++;
17052         cpl_free(colname);
17053         colname = cpl_sprintf("object_%d", maxobjs);
17054     }
17055     
17056     cpl_free(colname);
17057 
17058     maxobjs--;
17059 
17060     return maxobjs;
17061 }
17062 
17070 int mos_get_nobjects(cpl_table * slits)
17071 {
17072     int nobjs = 0;
17073 
17074     int nslits  = cpl_table_get_nrow(slits);
17075     int maxobjs = mos_get_maxobjs_per_slit(slits);
17076 
17077     int k, m;
17078 
17079     for (k = 0; k < nslits; k++) {
17080         for (m = 0; m < maxobjs; m++) {
17081             char * name = cpl_sprintf("object_%d", m + 1);
17082             int    null = !cpl_table_is_valid(slits, name, k);
17083 
17084             cpl_free(name);
17085 
17086             if (null)  break;
17087             else nobjs++;
17088         }
17089     }
17090 
17091     return nobjs;
17092 }
17093 
17101 int mos_check_slits(cpl_table *slits, float rescale)
17102 {
17103 
17104     cpl_propertylist *sort;
17105 
17106     int nslits  = cpl_table_get_nrow(slits);
17107 
17108     int k, null;
17109 
17110     const float interval = 90.0 * rescale;
17111     const float offset   = (90.0 - 5) * rescale;
17112 
17113 
17114     for (k = 0; k < nslits; k++) {
17115         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
17116         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17117 
17118         double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
17119         double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
17120 
17121         int nmiss = (int)((ytop - ybottom) / interval + 0.5);
17122 
17123         if (nmiss > 1) {
17124             cpl_msg_warning(cpl_func, 
17125                             "Some slits could not be properly detected. "
17126                             "There might be accountable inaccuracies.");
17127             while (nmiss > 1) {
17128                 cpl_table_set_size(slits, nslits + 1);
17129 
17130                 /* Fill in new slit 'cut' */
17131 
17132                 /* x coordinates be the same (acceptable approximation) */
17133                 cpl_table_set_double(slits, "xtop",    nslits, xtop);
17134                 cpl_table_set_double(slits, "xbottom", nslits, xbottom);
17135 
17136                 /* Cut */
17137                 if (k == 0) {
17138                     cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
17139                     cpl_table_set_double(slits, "ytop",    nslits, ybottom
17140                                                                    + offset);
17141                     ybottom += interval;
17142                     cpl_table_set_double(slits, "ybottom", k,      ybottom);
17143                 } else {
17144                     cpl_table_set_double(slits, "ytop",    nslits, ytop);
17145                     cpl_table_set_double(slits, "ybottom", nslits, ytop 
17146                                                                    - offset);
17147                     ytop -= interval;
17148                     cpl_table_set_double(slits, "ytop",     k,     ytop);
17149                 }
17150 
17151                 nslits++; nmiss--;
17152             }
17153         }
17154     }
17155 
17156     sort = cpl_propertylist_new();
17157     cpl_propertylist_append_bool(sort, "ytop", 1);
17158     cpl_table_sort(slits, sort);
17159     cpl_propertylist_delete(sort);
17160 
17161     /*
17162      * Add here an ad hoc check on the last slit: is it too long 
17163      * (by more than 10%)? Then shorten it...
17164      */
17165 
17166     k = cpl_table_get_nrow(slits) - 1;
17167 
17168     {
17169         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
17170         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
17171         double length  = (ytop - ybottom) / interval;
17172 
17173         if (length > 1.1) {
17174             cpl_table_set_double(slits, "ybottom", k, ytop - offset);
17175         }
17176   
17177     }
17178 
17179     return 0;
17180 }
17181 
17204 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header, 
17205                                     int * nslits_out_det)
17206 {
17207     int m, null;
17208     int halfsize;
17209 
17210     cpl_propertylist * sort;
17211     cpl_table        * slits; 
17212 
17213     slits    = mos_load_slits_fors_mos(header, nslits_out_det);
17214     halfsize = cpl_table_get_nrow(slits);
17215 
17216     cpl_table_set_size(slits, 2 * halfsize);
17217 
17218     for (m = 0; m < halfsize; m++) {
17219 
17220         double gap = 1.4;
17221 
17222         double length = 
17223             cpl_table_get(slits, "ytop",    m, &null) -
17224             cpl_table_get(slits, "ybottom", m, &null);
17225 
17226         if (m) {
17227             double interval = 
17228                 cpl_table_get(slits, "ybottom", m - 1, &null) -
17229                 cpl_table_get(slits, "ytop",    m,     &null);
17230 
17231             gap = (interval - length) / 2;
17232         }
17233 
17234         cpl_table_set(slits, "slit_id", m + halfsize,
17235                       cpl_table_get(slits, "slit_id", m, &null) - 1);
17236 
17237         cpl_table_set(slits, "xtop",    m + halfsize,
17238                       cpl_table_get(slits, "xtop",    m, &null));
17239 
17240         cpl_table_set(slits, "xbottom", m + halfsize,
17241                       cpl_table_get(slits, "xbottom", m, &null));
17242 
17243         cpl_table_set(slits, "ytop",    m + halfsize, 
17244                       cpl_table_get(slits, "ytop", m, &null) + gap + length);
17245 
17246         cpl_table_set(slits, "ybottom", m + halfsize,
17247                       cpl_table_get(slits, "ytop", m, &null) + gap);
17248     }
17249 
17250     for (m = 0; m < 2 * halfsize; m++) {
17251         cpl_table_set(slits, "ytop",    m, 
17252                       cpl_table_get(slits, "ytop",    m, &null) - 5.3);
17253 
17254         cpl_table_set(slits, "ybottom", m,
17255                       cpl_table_get(slits, "ybottom", m, &null) - 5.3);
17256 
17257     }
17258 
17259     sort = cpl_propertylist_new();
17260     cpl_propertylist_append_bool(sort, "ytop", 1);
17261     cpl_table_sort(slits, sort);
17262 
17263     cpl_propertylist_delete(sort);
17264 
17265     return slits;
17266 }
17267 
17268 int * fors_get_nobjs_perslit(cpl_table * slits)
17269 {
17270     int nslits  = cpl_table_get_nrow(slits);
17271     int maxobjs = mos_get_maxobjs_per_slit(slits);
17272 
17273     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
17274 
17275     int k, m;
17276 
17277     for (k = 0; k < nslits; k++) {
17278         int nobjs = 0;
17279         for (m = 0; m < maxobjs; m++) {
17280             char * name = cpl_sprintf("object_%d", m + 1);
17281             int    null = !cpl_table_is_valid(slits, name, k);
17282 
17283             cpl_free(name);
17284 
17285             if (null)  break;
17286             else nobjs++;
17287         }
17288         
17289         nobjs_per_slit[k] = nobjs;
17290     }
17291 
17292     return nobjs_per_slit;
17293 }
17294 
17295 double fors_get_object_position(cpl_table *slits, int slit, int object)
17296 {
17297     char   *name = cpl_sprintf("object_%d", object);
17298     double  position;
17299 
17300     position = cpl_table_get_double(slits, name, slit, NULL)
17301              - cpl_table_get_int(slits, "position", slit, NULL);
17302 
17303     cpl_free(name);
17304 
17305     return position;
17306 }
17307 
17308 int mos_rebin_signal(cpl_image **image, int rebin)
17309 {
17310     cpl_image *rebinned;
17311 
17312 
17313     if (*image == NULL)
17314         return 1;
17315 
17316     if (rebin == 1)
17317         return 0;
17318 
17319     rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
17320 
17321     cpl_image_delete(*image);
17322 
17323     *image = rebinned;
17324 
17325     return 0;
17326 }
17327 
17328 int mos_rebin_error(cpl_image **image, int rebin)
17329 {
17330     if (*image == NULL)
17331         return 1;
17332 
17333     if (rebin == 1)
17334         return 0;
17335 
17336     cpl_image_power(*image, 2);
17337     mos_rebin_signal(image, rebin);
17338     cpl_image_power(*image, 0.5);
17339 
17340     return 0;
17341 }
17342 
17343 /*
17344  * @brief
17345  *   Map table values into a 1D image
17346  *  
17347  * @param image       Target image
17348  * @param start       Coordinate of first pixel in image
17349  * @param step        Coordinate step for one pixel in image
17350  * @param table       Source table
17351  * @param xname       Name of coordinate column
17352  * @param yname       Name of values column
17353  *
17354  * @return 0 on success
17355  *
17356  * The values in @em yname are linearly interpolated at the @em image 
17357  * pixel coordinates. The @em image must have Nx1 size.
17358  */
17359 
17360 int map_table(cpl_image *image, double start, double step,
17361               cpl_table *table, const char *xname, const char *yname)
17362 {
17363     int      length = cpl_image_get_size_x(image);
17364     int      nrows  = cpl_table_get_nrow(table);
17365     float   *data   = cpl_image_get_data_float(image);
17366     float   *fdata  = NULL;
17367     double  *xdata  = NULL;
17368     double  *ydata  = NULL;
17369     cpl_type xtype  = cpl_table_get_column_type(table, xname);
17370     cpl_type ytype  = cpl_table_get_column_type(table, yname);
17371     double   xzero, pos;
17372     int      i, j, n;
17373 
17374 
17375     /*
17376      * Initialization of output image at 0.0 - this value is left 
17377      * on non-overlapping portions.
17378      */
17379 
17380     for (i = 0; i < length; i++)
17381         data[i] = 0.0;
17382 
17383 
17384     /*
17385      * Do everything in double precision
17386      */
17387 
17388     if (xtype == CPL_TYPE_FLOAT) {
17389         fdata = cpl_table_get_data_float(table, xname);
17390         xdata = cpl_malloc(nrows * sizeof(double));
17391         for (i = 0; i < nrows; i++) {
17392            xdata[i] = fdata[i];
17393         }
17394     }
17395     else {
17396         xdata = cpl_table_get_data_double(table, xname);
17397     }
17398 
17399     if (ytype == CPL_TYPE_FLOAT) {
17400         fdata = cpl_table_get_data_float(table, yname);
17401         ydata = cpl_malloc(nrows * sizeof(double));
17402         for (i = 0; i < nrows; i++) {
17403            ydata[i] = fdata[i];
17404         }
17405     }
17406     else {
17407         ydata = cpl_table_get_data_double(table, yname);
17408     }
17409 
17410     /*
17411      * Mapping
17412      */
17413 
17414     n = 0;
17415     xzero = xdata[n];
17416 
17417     for (i = 0; i < length; i++) {
17418         pos = start + step * i;
17419         if (pos < xzero)
17420             continue;
17421         for (j = n; j < nrows; j++) {
17422             if (xdata[j] > pos) {
17423                 n = j;
17424                 data[i] = ydata[j-1]
17425                         + (ydata[j] - ydata[j-1])
17426                         * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
17427                 break;
17428             }
17429         }
17430     }
17431 
17432     if (xtype == CPL_TYPE_FLOAT)
17433         cpl_free(xdata);
17434 
17435     if (ytype == CPL_TYPE_FLOAT)
17436         cpl_free(ydata);
17437 
17438     return 0;
17439 }
17440 
17441 
17442 /*
17443  * @brief
17444  *   Fit overall trend of a Nx1 image
17445  *  
17446  * @param image       Values to smooth
17447  * @param order       Order of fitting polynomial
17448  * @param hw          Half width of smoothing window
17449  *
17450  * @return Smoothed image, or NULL on failure.
17451  *
17452  * Heavily smooth and fit data in the input Nx1 size @em image.
17453  */
17454 
17455 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
17456 {
17457     int             npoints;
17458     cpl_vector     *x;
17459     cpl_vector     *y;
17460     double         *xdata;
17461     double         *ydata;
17462     cpl_polynomial *poly;
17463     cpl_vector     *ysmooth;
17464     cpl_image      *smoothed;
17465     float          *sdata;
17466     int             i;
17467 
17468 
17469     npoints = cpl_image_get_size_x(image);
17470 
17471     if (2 * hw + 1 > npoints)
17472         return NULL;
17473 
17474     x       = cpl_vector_new(npoints);
17475     y       = cpl_vector_new(npoints);
17476     xdata   = cpl_vector_get_data(x);
17477     ydata   = cpl_vector_get_data(y);
17478 
17479     smoothed = cpl_image_duplicate(image);
17480     sdata = cpl_image_get_data_float(smoothed);
17481 
17482     for (i = 0; i < npoints; i++) {
17483         xdata[i] = i;
17484         ydata[i] = sdata[i];
17485     }
17486 
17487     ysmooth = cpl_vector_filter_median_create(y, hw);
17488     cpl_vector_delete(y);
17489 
17490     poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
17491     cpl_vector_delete(x);
17492     cpl_vector_delete(ysmooth);
17493 
17494     if (poly) {
17495         for (i = 0; i < npoints; i++)
17496             sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
17497     
17498         cpl_polynomial_delete(poly);
17499     }
17500     else {
17501         cpl_image_delete(smoothed);
17502         return NULL;
17503     }
17504 
17505     return smoothed;
17506 }
17507 
17508 #undef cleanup
17509 #define cleanup                       \
17510 do {                                  \
17511     cpl_image_delete(spectrum);       \
17512     cpl_image_delete(flux);           \
17513     cpl_image_delete(efficiency);     \
17514     cpl_image_delete(smo_efficiency); \
17515     cpl_image_delete(extinction);     \
17516     cpl_image_delete(response);       \
17517     cpl_image_delete(smo_response);   \
17518     cpl_image_delete(physical);       \
17519 } while (0)
17520 
17544 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 
17545                                  double dispersion, double gain,
17546                                  double exptime, cpl_table *ext_table,
17547                                  double airmass, cpl_table *flux_table,
17548                                  int order)
17549 {
17550 
17551     cpl_image *spectrum       = NULL; // Extracted standard star spectrum
17552     float     *data;
17553     cpl_image *extinction     = NULL; // Extinction binned as "spectrum"
17554     float     *ext_data;
17555     cpl_image *flux           = NULL; // Standard star flux binned as "spectrum"
17556     float     *flux_data;
17557     cpl_image *physical       = NULL; // Physical units of above
17558     float     *phys_data;
17559     cpl_image *efficiency     = NULL; // Raw efficiency curve
17560     float     *eff_data;
17561     cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17562     float     *smo_eff_data;
17563     cpl_image *response       = NULL; // Raw response curve
17564     float     *res_data;
17565     cpl_image *smo_response   = NULL; // Smoothed response curve
17566     float     *smo_res_data;
17567     cpl_image *image;
17568     cpl_image *smo_image;
17569     cpl_table *table;
17570     float      lambda;
17571     int        nx, ny;
17572     int        ext_count, ext_pos;
17573     int        eff_count, eff_pos;
17574     int        flux_count, flux_pos;
17575     int        start, end;
17576     int        i;
17577 
17578 
17579     if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17580         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17581         return NULL;
17582     }
17583 
17584     if (!cpl_table_has_column(ext_table, "WAVE")) {
17585         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17586                               "Column WAVE in atmospheric extinction table");
17587         return NULL;
17588     }
17589 
17590     if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17591         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17592                         "Column EXTINCTION in atmospheric extinction table");
17593         return NULL;
17594     }
17595 
17596     if (!cpl_table_has_column(flux_table, "WAVE")) {
17597         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17598                               "Column WAVE in standard star flux table");
17599         return NULL;
17600     }
17601 
17602     if (!cpl_table_has_column(flux_table, "FLUX")) {
17603         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17604                               "Column FLUX in standard star flux table");
17605         return NULL;
17606     }
17607 
17608     if (gain < 0.1) {
17609         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17610                               "Invalid gain factor (%.2f)", gain);
17611         return NULL;
17612     }
17613 
17614     if (exptime < 0.001) {
17615         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17616                               "Invalid exposure time (%.2f)", exptime);
17617         return NULL;
17618     }
17619 
17620     if (dispersion < 0.001) {
17621         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17622                               "Invalid dispersion (%.2f)", dispersion);
17623         return NULL;
17624     }
17625 
17626     if (order < 2) {
17627         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17628                               "Order of the polynomial fitting the "
17629                               "instrument response must be at least 2");
17630         return NULL;
17631     }
17632 
17633     nx = cpl_image_get_size_x(spectra);
17634     ny = cpl_image_get_size_y(spectra);
17635 
17636 
17637     /*
17638      * Find brightest spectrum and duplicate it.
17639      */
17640 
17641     if (ny == 1) {
17642         spectrum = cpl_image_duplicate(spectra);
17643     }
17644     else {
17645         cpl_size        x, y;
17646         cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17647 
17648         cpl_image_get_maxpos(brights, &x, &y);
17649         cpl_image_delete(brights);
17650         spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17651     }
17652 
17653 
17654     /*
17655      * Convert standard star spectrum in electrons per second per Angstrom.
17656      */
17657 
17658     cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17659 
17660 
17661     /*
17662      * Map the atmospheric extinction factors to the same lambda sampling
17663      * of the extracted spectrum.
17664      */
17665 
17666     extinction = cpl_image_duplicate(spectrum);
17667     map_table(extinction, startwave + dispersion/2, dispersion, 
17668               ext_table, "WAVE", "EXTINCTION");
17669 
17670 
17671     /*
17672      * Convert from magnitudes to actual flux loss.
17673      */
17674 
17675     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17676     cpl_image_exponential(extinction, 10.);
17677 
17678 
17679     /*
17680      * Correct the scientific spectrum to airmass 0
17681      */
17682 
17683     cpl_image_multiply(spectrum, extinction);
17684 
17685 
17686     /*
17687      * Find in what pixel interval (start at "ext_pos", for "ext_count" 
17688      * pixels) the atmospheric extinction is available.
17689      */
17690     
17691     ext_data = cpl_image_get_data_float(extinction);
17692 
17693     ext_count = 0;
17694     ext_pos = 0;
17695     for (i = 0; i < nx; i++) {
17696         if (ext_data[i] > 0.0) {
17697             if (ext_count == 0) {
17698                 ext_pos = i;
17699             }
17700             ext_count++;
17701         }
17702         else {
17703             if (ext_count) {
17704                 break;
17705             }
17706         }
17707     }
17708 
17709     cpl_image_delete(extinction); extinction = NULL;
17710 
17711 
17712     /*
17713      * Map the standard star catalog flux to the same lambda sampling
17714      * of the extracted spectrum.
17715      */
17716 
17717     flux = cpl_image_duplicate(spectrum);
17718     map_table(flux, startwave + dispersion/2, dispersion, 
17719               flux_table, "WAVE", "FLUX");
17720 
17721 
17722     /*
17723      * Find in what pixel interval (start at "flux_pos", for "flux_count" 
17724      * pixels) the standard star flux is available.
17725      */
17726     
17727     flux_data = cpl_image_get_data_float(flux);
17728 
17729     flux_count = 0;
17730     flux_pos = 0;
17731     for (i = 0; i < nx; i++) {
17732         if (flux_data[i] > 0.0) {
17733             if (flux_count == 0) {
17734                 flux_pos = i;
17735             }
17736             flux_count++;
17737         }
17738         else {
17739             if (flux_count) {
17740                 break;
17741             }
17742         }
17743     }
17744 
17745 
17746     /*
17747      * Intersection with previous selection
17748      */
17749 
17750     start      = ext_pos > flux_pos ? ext_pos : flux_pos;
17751     end        = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17752                  (ext_pos + ext_count) : (flux_pos + flux_count);
17753     flux_pos   = start;
17754     flux_count = end - start;
17755 
17756 
17757     /*
17758      * Convert the flux to photons (per second per Angstrom).
17759      * std_flux is in units of erg / cm^2 / s / Angstrom. This
17760      * must be multiplied by the efficient area of the telescope,
17761      * 5.18E+5 cm^2, and divided by hv (v = frequency). With 
17762      * hc = 1.98E-8 erg*Angstrom one obtains the following:
17763      */
17764 
17765     physical = cpl_image_duplicate(spectrum);
17766     phys_data = cpl_image_get_data_float(physical);
17767 
17768     for (i = 0; i < nx; i++) {
17769         lambda = startwave + dispersion * (i + 0.5);
17770         phys_data[i] = 0.0026 * lambda * flux_data[i];
17771     }
17772 
17773     efficiency = cpl_image_duplicate(spectrum);
17774     eff_data = cpl_image_get_data_float(efficiency);
17775     data = cpl_image_get_data_float(spectrum);
17776 
17777     for (i = 0; i < nx; i++) {
17778         if (phys_data[i] > 0.0)
17779             eff_data[i] = data[i] / phys_data[i];
17780         else
17781             eff_data[i] = 0.0;
17782     }
17783 
17784     cpl_image_delete(physical); physical = NULL;
17785 
17786 
17787     /*
17788      * Find interval (longer than 300 pixels) where efficiency is 
17789      * greater than 1%
17790      */
17791 
17792     eff_count = 0;
17793     eff_pos = 0;
17794     for (i = 0; i < nx; i++) {
17795         if (eff_data[i] > 0.01) {
17796             if (eff_count == 0) {
17797                 eff_pos = i; 
17798             }
17799             eff_count++;
17800         }
17801         else {
17802             if (eff_count > 300) {
17803                 break;
17804             }
17805         }
17806     }
17807 
17808 
17809     /*
17810      * Intersection with previous selection
17811      */
17812 
17813     start      = eff_pos > flux_pos ? eff_pos : flux_pos;
17814     end        = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17815                  (eff_pos + eff_count) : (flux_pos + flux_count);
17816     eff_pos    = start;
17817     eff_count  = end - start;
17818 
17819     if (eff_count < 1) {
17820         cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17821                               "No overlap between catalog and spectrum");
17822         cleanup;
17823         return NULL;
17824     }
17825 
17826 
17827     /*
17828      * Extract only data to fit, i.e., where the efficiency is available.
17829      */
17830 
17831     image = cpl_image_extract(efficiency, eff_pos + 1, 1, 
17832                               eff_pos + eff_count, 1);
17833 
17834     smo_image = polysmooth(image, order, 50);
17835     cpl_image_delete(image);
17836 
17837     smo_efficiency = cpl_image_duplicate(efficiency);
17838     smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17839     cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17840 
17841     cpl_image_delete(smo_image);
17842 
17843 
17844     /*
17845      * Compute instrument response as the ratio between the catalog
17846      * spectrum and the observed spectrum (converted in physical units).
17847      * The polynomial smoothing, however, is performed on the inverse
17848      * of this ration, for obvious reasons (i.e., no divergence at zero
17849      * efficiency).
17850      */
17851 
17852     response = cpl_image_duplicate(spectrum);
17853     res_data = cpl_image_get_data_float(response);
17854 
17855     for (i = 0; i < nx; i++) {
17856         if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17857             res_data[i] = data[i] / flux_data[i];
17858         else
17859             res_data[i] = 0.0;
17860     }
17861 
17862 
17863     /*
17864      * Extract only data to fit, i.e., where the response is available.
17865      */
17866 
17867     image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17868 
17869     smo_image = polysmooth(image, order, 50);
17870     cpl_image_delete(image);
17871 
17872     smo_response = cpl_image_duplicate(response);
17873     smo_res_data = cpl_image_get_data_float(smo_response);
17874     cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17875 
17876     cpl_image_delete(smo_image);
17877 
17878     for (i = 0; i < nx; i++) {
17879         if (eff_data[i] > 0.01) {
17880             res_data[i] = 1 / res_data[i];
17881             smo_res_data[i] = 1 / smo_res_data[i];
17882         }
17883         else {
17884             res_data[i] = 0.0;
17885             smo_res_data[i] = 0.0;
17886         }
17887     }
17888 
17889 
17890     /*
17891      * Assemble the product spectrophotometric table.
17892      */
17893 
17894     table = cpl_table_new(nx);
17895 
17896     cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17897     cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17898 
17899     for (i = 0; i < nx; i++)
17900         cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17901 
17902     cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17903     cpl_table_set_column_unit(table, "STD_FLUX", 
17904                               "10^(-16) erg/(cm^2 s Angstrom)");
17905     cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17906     cpl_image_delete(flux); flux = NULL;
17907 
17908     cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17909     cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17910     cpl_table_copy_data_float(table, "OBS_FLUX", data);
17911     cpl_image_delete(spectrum); spectrum = NULL;
17912 
17913     cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17914     cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17915     cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17916     cpl_image_delete(efficiency); efficiency = NULL;
17917 
17918     cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17919     cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17920     cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17921     cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17922 
17923     cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17924     cpl_table_set_column_unit(table, "RAW_RESPONSE", 
17925                               "10^(-16) erg/(cm^2 electron)");
17926     cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17927     cpl_image_delete(response); response = NULL;
17928 
17929     cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17930     cpl_table_set_column_unit(table, 
17931                               "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17932     cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17933     cpl_image_delete(smo_response); smo_response = NULL;
17934 
17935     cleanup;
17936 
17937     return table;
17938 }
17939 
17940 static double ksigma_vector(cpl_vector *values, 
17941                             double klow, double khigh, int kiter, int *good)
17942 {
17943     cpl_vector *accepted;
17944     double  mean  = 0.0;
17945     double  sigma = 0.0;
17946     double *data  = cpl_vector_get_data(values);
17947     int     n     = cpl_vector_get_size(values);
17948     int     ngood = n;
17949     int     count = 0;
17950     int     i;
17951 
17952 
17953     /*
17954      * At first iteration the mean is taken as the median, and the
17955      * standard deviation relative to this value is computed.
17956      */
17957 
17958     mean = cpl_vector_get_median(values);
17959 
17960     for (i = 0; i < n; i++) 
17961         sigma += (mean - data[i]) * (mean - data[i]);
17962 
17963     sigma = sqrt(sigma / (n - 1));
17964 
17965     while (kiter) {
17966         count = 0;
17967         for (i = 0; i < ngood; i++) {
17968             if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17969                 data[count] = data[i];
17970                 ++count;
17971             }
17972         }
17973 
17974         if (count == 0) // This cannot happen at first iteration.
17975             break;      // So we can break: we have already computed a mean.
17976 
17977         /*
17978          * The mean must be computed even if no element was rejected
17979          * (count == ngood), because at first iteration median instead 
17980          * of mean was computed.
17981          */
17982 
17983         accepted = cpl_vector_wrap(count, data);
17984         mean = cpl_vector_get_mean(accepted);
17985         if (count > 1)
17986             sigma = cpl_vector_get_stdev(accepted);
17987         cpl_vector_unwrap(accepted);
17988 
17989         if (count == ngood || count == 1)
17990             break;
17991 
17992         ngood = count;
17993         --kiter;
17994     }
17995 
17996     if (good)
17997         *good = ngood;
17998 
17999     return mean;
18000 }
18001 
18002 
18021 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 
18022                             double klow, double khigh, int kiter,
18023                             cpl_image **good)
18024 {
18025     int         ni, nx, ny, npix;
18026     cpl_image  *out_ima;
18027     float      *pout_ima;
18028     float      *good_ima;
18029     cpl_image  *image;
18030     float     **data;
18031     cpl_vector *time_line;
18032     double     *ptime_line;
18033     int         ngood;
18034     int         i, j;
18035 
18036 
18037     ni         = cpl_imagelist_get_size(imlist);
18038 
18039     image      = cpl_imagelist_get(imlist, 0);
18040     nx         = cpl_image_get_size_x(image);
18041     ny         = cpl_image_get_size_y(image);
18042     npix       = nx * ny;
18043     
18044     out_ima    = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18045     pout_ima   = cpl_image_get_data_float(out_ima);
18046 
18047     if (good) {
18048         *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
18049         good_ima = cpl_image_get_data_float(*good);
18050     }
18051 
18052     time_line  = cpl_vector_new(ni);
18053     ptime_line = cpl_vector_get_data(time_line);
18054 
18055     data = cpl_calloc(sizeof(float *), ni);
18056     
18057     for (i = 0; i < ni; i++) {
18058         image = cpl_imagelist_get(imlist, i);
18059         data[i] = cpl_image_get_data_float(image);
18060     }
18061 
18062     for (i = 0; i < npix; i++) {
18063         for (j = 0; j < ni; j++) {
18064             ptime_line[j] = data[j][i];
18065         }
18066         pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
18067         if (good) {
18068             good_ima[i] = ngood;
18069         }
18070     }
18071 
18072     cpl_free(data);
18073     cpl_vector_delete(time_line);
18074 
18075     return out_ima;
18076 
18077 }
18078 
18079 
18096 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
18097                                 cpl_table *ext_table, double startwave,
18098                                 double dispersion, double gain,
18099                                 double exptime, double airmass)
18100 {
18101     cpl_image *extinction;
18102     cpl_image *outspectra;
18103     cpl_image *mapresponse;
18104     float     *res_data;
18105     float     *out_data;
18106     float     *ext_data;
18107     int        tlength, xlength, ylength;
18108     int        i, j, k;
18109     double     resp_startwave;
18110     double     resp_endwave;
18111     int        null;
18112 
18113 
18114     if (spectra == NULL || ext_table == NULL || response == NULL) {
18115         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18116         return NULL;
18117     }
18118     
18119     /* Use the normal response if available. If not, use the flat corrected
18120      * response. Usually only one of the two is available in the interpolated
18121      * response.
18122      */
18123     if(cpl_table_has_column(response, "RESPONSE"))
18124         cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18125     else if(cpl_table_has_column(response, "RESPONSE_FFSED"))
18126         cpl_table_cast_column(response, "RESPONSE_FFSED", "RESPONSE_F", CPL_TYPE_FLOAT);
18127     else
18128         return NULL;
18129     
18130     res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18131 
18132     if (res_data == NULL) {
18133         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18134         return NULL;
18135     }
18136 
18137     tlength = cpl_table_get_nrow(response);
18138     xlength = cpl_image_get_size_x(spectra);
18139     ylength = cpl_image_get_size_y(spectra);
18140 
18141     /* Map the response */
18142     mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18143     map_table(mapresponse, startwave + dispersion/2, dispersion,
18144               response, "WAVE", "RESPONSE_F");
18145     res_data = cpl_image_get_data_float(mapresponse);
18146 
18147     /*
18148      * Map the atmospheric extinction factors to the same lambda sampling
18149      * of the extracted spectrum.
18150      */
18151 
18152     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18153     map_table(extinction, startwave + dispersion/2, dispersion,
18154               ext_table, "WAVE", "EXTINCTION");
18155 
18156 
18157     /*
18158      * Convert from magnitudes to actual flux loss.
18159      */
18160 
18161     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18162     cpl_image_exponential(extinction, 10.);
18163 
18164     outspectra = cpl_image_duplicate(spectra);
18165 
18166     ext_data = cpl_image_get_data_float(extinction);
18167     out_data = cpl_image_get_data_float(outspectra);
18168 
18169     for (k = 0, i = 0; i < ylength; i++) {
18170         for (j = 0; j < xlength; j++, k++) 
18171             out_data[k] *= ext_data[j] * res_data[j];
18172     }
18173 
18174     cpl_image_delete(extinction);
18175     cpl_image_delete(mapresponse);
18176 
18177     cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
18178 
18179     /* 
18180      * Set to -1 the extrapolated values
18181      */
18182     resp_startwave = cpl_table_get(response, "WAVE", 0, &null);
18183     resp_endwave = cpl_table_get(response, "WAVE", 
18184                                  cpl_table_get_nrow(response) -1, &null);
18185     for (j = 0; j < xlength; j++) {
18186         double this_wave = startwave + j * dispersion;
18187         if(this_wave < resp_startwave ||this_wave > resp_endwave)
18188         {
18189             for (i = 0; i < ylength; i++) 
18190                 out_data[j + xlength * i] = -1;
18191         }
18192     }
18193 
18194     cpl_table_erase_column(response, "RESPONSE_F");
18195 
18196     return outspectra;
18197 }
18198 
18199 
18216 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 
18217                                           cpl_image *errors, 
18218                                           cpl_table *response,
18219                                           cpl_table *ext_table, 
18220                                           double startwave,
18221                                           double dispersion, double gain,
18222                                           double exptime, double airmass)
18223 {
18224     cpl_image *extinction;
18225     cpl_image *outerrors;
18226     cpl_image *mapresponse;
18227     cpl_image *maperror;
18228     float     *err_data;
18229     float     *out_data;
18230     float     *ext_data;
18231     float     *res_data;
18232     float     *spe_data;
18233     int        tlength, xlength, ylength;
18234     int        i, j, k;
18235 
18236 
18237     if (errors == NULL || ext_table == NULL || response == NULL) {
18238         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
18239         return NULL;
18240     }
18241 
18242     if (!cpl_table_has_column(response, "ERROR")) {
18243         return mos_apply_photometry(errors, response, ext_table, startwave,
18244                                     dispersion, gain, exptime, airmass);
18245     }
18246 
18247     cpl_table_cast_column(response, "RESPONSE", "RESPONSE_F", CPL_TYPE_FLOAT);
18248     res_data = cpl_table_get_data_float(response, "RESPONSE_F");
18249 
18250     if (res_data == NULL) {
18251         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18252         return NULL;
18253     }
18254 
18255     err_data = cpl_table_get_data_float(response, "ERROR");
18256 
18257     if (err_data == NULL) {
18258         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
18259         return NULL;
18260     }
18261 
18262     tlength = cpl_table_get_nrow(response);
18263     xlength = cpl_image_get_size_x(errors);
18264     ylength = cpl_image_get_size_y(errors);
18265 
18266     if (xlength != tlength) {
18267         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18268         map_table(mapresponse, startwave + dispersion/2, dispersion,
18269                   response, "WAVE", "RESPONSE_F");
18270         res_data = cpl_image_get_data_float(mapresponse);
18271 
18272         maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18273         map_table(maperror, startwave + dispersion/2, dispersion,
18274                   response, "WAVE", "ERROR");
18275         err_data = cpl_image_get_data_float(maperror);
18276     }
18277 
18278     /*
18279      * Map the atmospheric extinction factors to the same lambda sampling
18280      * of the extracted spectrum.
18281      */
18282 
18283     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
18284     map_table(extinction, startwave + dispersion/2, dispersion,
18285               ext_table, "WAVE", "EXTINCTION");
18286 
18287 
18288     /*
18289      * Convert from magnitudes to actual flux loss.
18290      */
18291 
18292     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
18293     cpl_image_exponential(extinction, 10.);
18294 
18295     outerrors = cpl_image_duplicate(errors);
18296 
18297     ext_data = cpl_image_get_data_float(extinction);
18298     out_data = cpl_image_get_data_float(outerrors);
18299     spe_data = cpl_image_get_data_float(spectra);
18300 
18301     for (k = 0, i = 0; i < ylength; i++) {
18302         for (j = 0; j < xlength; j++, k++) {
18303             out_data[k] = ext_data[j] * 
18304               sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
18305                    res_data[j] * res_data[j] * out_data[k] * out_data[k]);
18306         }
18307     }
18308 
18309     cpl_image_delete(extinction);
18310     if (xlength != tlength) {
18311         cpl_image_delete(maperror);
18312     }
18313 
18314     cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
18315 
18316     cpl_table_erase_column(response, "RESPONSE_F");
18317     return outerrors;
18318 }
18319 
18320 
18396 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
18397                            cpl_image *u_image, cpl_image *u_error,
18398                            double startwave, double dispersion,
18399                            double band, cpl_table *pol_sta,
18400                            double ra, double dec, char *filter, 
18401                            int *polarisation,
18402                            double *p_offset, double *p_error,
18403                            double *a_offset, double *a_error)
18404 {
18405     cpl_table *standard;
18406     cpl_image *q_noise;
18407     cpl_image *q_signal;
18408     cpl_image *u_noise;
18409     cpl_image *u_signal;
18410     cpl_image *noise;
18411     double    *q_ndata;
18412     double    *q_sdata;
18413     double    *u_ndata;
18414     double    *u_sdata;
18415     double     arctol = 0.5;  /* Arc tolerance in degrees */
18416     double     mindist;
18417     double     cwave;
18418     double     bwave[] = {3650., 4450., 5510., 6580., 8060};
18419     char      *bands = "UBVRI";
18420     char       p_label[] = {' ', 'p', '\0'};
18421     char       dp_label[] = {' ', 'd', 'p', '\0'};
18422     char       a_label[] = {' ', 'a', '\0'};
18423     char       da_label[] = {' ', 'd', 'a', '\0'};
18424     int        nbands = strlen(bands);
18425     int        selected;
18426     int        first, last, count, center;
18427     int        nx;
18428     cpl_size   col, row;
18429     int        i, found, closest;
18430     int        pband;
18431     int        polarised;
18432     double     q_obs;
18433     double     q_err;
18434     double     u_obs;
18435     double     u_err;
18436     double     p_obs;
18437     double     p_err;
18438     double     p_ref;
18439     double     dp_ref;
18440     double     a_obs;
18441     double     a_err;
18442     double     a_ref;
18443     double     da_ref;
18444 
18445 
18446     *filter       = '\0';
18447     *polarisation = 0;
18448     *p_offset     = 0.0;
18449     *p_error      = 0.0;
18450     *a_offset     = 0.0;
18451     *a_error      = 0.0;
18452 
18453     /*
18454      * Select reference standard star
18455      */
18456 
18457     cpl_table_select_all(pol_sta);
18458     cpl_table_and_selected_double(pol_sta, "COORD_RA",  CPL_GREATER_THAN, 
18459                                   ra-arctol);
18460     cpl_table_and_selected_double(pol_sta, "COORD_RA",  CPL_LESS_THAN,    
18461                                   ra+arctol);
18462     cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_GREATER_THAN, 
18463                                   dec-arctol);
18464     selected =
18465     cpl_table_and_selected_double(pol_sta, "COORD_DEC", CPL_LESS_THAN,    
18466                                   dec+arctol);
18467 
18468     if (selected == 0) {
18469         cpl_msg_warning(cpl_func, "No standard star found in FOV");
18470         return 1;
18471     }
18472 
18473     if (selected > 1) {
18474         cpl_msg_warning(cpl_func, 
18475                         "Ambiguity: %d standard stars found in FOV", selected);
18476         return 1;
18477     }
18478 
18479     standard = cpl_table_extract_selected(pol_sta);
18480 
18481     cpl_msg_info(cpl_func, "Standard star: %s", 
18482                  cpl_table_get_string(standard, "name", 0));
18483 
18484     /*
18485      * Check whether the star is polarised or not
18486      */
18487 
18488     polarised = cpl_table_get_int(standard,  "polarised", 0, NULL);
18489 
18490     cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
18491                  polarised ? " " : " not ");
18492 
18493 
18494     /*
18495      * Determine the image row with the smallest median noise: this 
18496      * row is assumed to refer to the standard star.
18497      * (note: the higher the S/N ratio of the original spectra, the 
18498      * smaller the noise of the Stokes parameters Q and U).
18499      */
18500 
18501     nx = cpl_image_get_size_x(q_error);
18502 
18503     noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
18504     cpl_image_get_minpos(noise, &col, &row);
18505 
18506     cpl_image_delete(noise);
18507 
18508     if (col != 1) {
18509         cpl_table_delete(standard);
18510         cpl_msg_error(cpl_func, 
18511                       "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
18512         return 1;
18513     }
18514 
18515     q_signal = cpl_image_extract(q_image, 1, row, nx, row);
18516     q_noise  = cpl_image_extract(q_error, 1, row, nx, row);
18517     u_signal = cpl_image_extract(u_image, 1, row, nx, row);
18518     u_noise  = cpl_image_extract(u_error, 1, row, nx, row);
18519 
18520     q_sdata = cpl_image_get_data_double(q_signal);
18521     q_ndata = cpl_image_get_data_double(q_noise);
18522     u_sdata = cpl_image_get_data_double(u_signal);
18523     u_ndata = cpl_image_get_data_double(u_noise);
18524 
18525 
18526     /*
18527      * Determine valid interval in input images (where error is positive).
18528      */
18529 
18530     first = -1;
18531     last = nx = cpl_image_get_size_x(q_signal);
18532     for (i = 0; i < nx; i++) {
18533         if (first < 0) {
18534             if (q_ndata[i] > 0.0) {
18535                 first = i;
18536             }
18537         }
18538         else {
18539             if (q_ndata[i] <= 0.0) {
18540                 last = i - 1;
18541                 break;
18542             }
18543         }
18544     }
18545 
18546     count = last - first + 1;
18547 
18548     if (first < 0 || count < band) {
18549         cpl_table_delete(standard);
18550         cpl_image_delete(q_signal);
18551         cpl_image_delete(q_noise);
18552         cpl_image_delete(u_signal);
18553         cpl_image_delete(u_noise);
18554         cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
18555         return 1;
18556     }
18557 
18558     center = (first + last) / 2;              // Center of valid spectrum
18559     cwave = startwave + dispersion * center;  // Corresponding wavelength
18560 
18561 
18562     /*
18563      * Find the band UBVRI closest to the central wavelength.
18564      */
18565 
18566     found = 0;
18567     for (i = 0; i < nbands; i++) {
18568         p_label[0] = bands[i];
18569         if (cpl_table_is_valid(standard, p_label, 0)) {
18570             if (found == 0) {
18571                 found = 1;
18572                 mindist = fabs(bwave[i] - cwave);
18573                 closest = i;
18574             }
18575             else if (mindist > fabs(bwave[i] - cwave)) {
18576                 mindist = fabs(bwave[i] - cwave);
18577                 closest = i;
18578             }
18579         }
18580     }
18581 
18582     if (!found) {
18583         cpl_table_delete(standard);
18584         cpl_image_delete(q_signal);
18585         cpl_image_delete(q_noise);
18586         cpl_image_delete(u_signal);
18587         cpl_image_delete(u_noise);
18588         cpl_msg_warning(cpl_func, "No reference value available");
18589         return 1;
18590     }
18591 
18592     center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18593     cwave  =  bwave[closest];                           // Wavelength of band
18594 
18595 
18596     /*
18597      * Check that the integration interval is entirely contained
18598      * in the valid interval, or give it up.
18599      */
18600 
18601     pband = floor(band / dispersion);  // Band width in pixels
18602 
18603     if (center - pband/2 < first || center + pband/2 > last) {
18604         cpl_table_delete(standard);
18605         cpl_image_delete(q_signal);
18606         cpl_image_delete(q_noise);
18607         cpl_image_delete(u_signal);
18608         cpl_image_delete(u_noise);
18609         cpl_msg_warning(cpl_func, "No reference value available");
18610         return 1;
18611     }
18612 
18613     first = center - pband/2;
18614     last  = center + pband/2;
18615 
18616     /*
18617      * Collect reference values. Note that if angle info is not available,
18618      * angle stuff is set automaticaly to zero.
18619      */
18620 
18621      p_label[0] = bands[closest];
18622     dp_label[0] = bands[closest];
18623      a_label[0] = bands[closest];
18624     da_label[0] = bands[closest];
18625 
18626      p_ref = cpl_table_get(standard,  p_label, 0, NULL);
18627     dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18628      a_ref = cpl_table_get(standard,  a_label, 0, NULL);
18629     da_ref = cpl_table_get(standard, da_label, 0, NULL);
18630 
18631     cpl_msg_info(cpl_func, 
18632                  "The expected polarisation is %.2f +- %.2f %%", 
18633                  p_ref, dp_ref);
18634 
18635     if (polarised) {
18636         cpl_msg_info(cpl_func, 
18637                      "The expected polarisation angle is %.2f +- %.2f degrees", 
18638                      a_ref, da_ref);
18639     }
18640 
18641     /*
18642      * Find median signal and median error.
18643      */
18644 
18645     q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18646     q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18647     u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18648     u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18649 
18650     /*
18651      * Measured linear polarisation and its error
18652      */
18653 
18654     p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18655     p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18656 
18657     /*
18658      * Measured polarisation angle
18659      */
18660 
18661     a_obs = 0.0;
18662     if (polarised) {
18663         if (fabs(q_obs) < 0.00001) {
18664             if (u_obs > 0.0) {
18665                 a_obs = 45.0;
18666             }
18667             else {
18668                 a_obs = 135.0;
18669             }
18670         }
18671         else {
18672             a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18673             if (q_obs > 0.0) {
18674                 if (u_obs < 0.0) {
18675                     a_obs += 180.;
18676                 }
18677             }
18678             else {
18679                 a_obs += 90.;
18680             }
18681         }
18682     }
18683 
18684     /*
18685      * Error on polarisation angle
18686      */
18687 
18688     a_err = 0.0;
18689     if (polarised) {
18690         a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18691               / (p_obs * p_obs) 
18692               * 90 / CPL_MATH_PI;
18693     }
18694 
18695     p_obs *= 100;
18696     p_err *= 100;
18697     cpl_msg_info(cpl_func, 
18698                  "The measured polarisation is %.2f +- %.2f %%", 
18699                  p_obs, p_err);
18700 
18701     if (polarised) {
18702         cpl_msg_info(cpl_func, 
18703                      "The measured polarisation angle is %.2f +- %.2f degrees", 
18704                      a_obs, a_err);
18705     }
18706 
18707     *filter       = bands[closest];
18708     *polarisation = polarised;
18709 
18710     if (polarised) {
18711         *p_offset = (p_obs - p_ref) / p_ref;
18712         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18713     }
18714     else {
18715         *p_offset = p_obs - p_ref;
18716         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref);
18717     }
18718 
18719     *a_offset     = a_obs - a_ref;
18720     *a_error      = sqrt(a_err*a_err + da_ref*da_ref);
18721 
18722     return 0;
18723 
18724 }
18725 
18726 
18758 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18759 {
18760     cpl_array *offsets;
18761     int        noffset;
18762     int        nslits = cpl_table_get_nrow(reference);
18763     int       *nref;
18764     int       *nobj;
18765     int        corr, maxcorr;
18766     int        best_shift;
18767     int        i, j, k;
18768 
18769     cpl_error_code status = CPL_ERROR_NONE;
18770 
18771 
18772     *offset = 0.0;
18773 
18774     if (objects == NULL)
18775         return CPL_ERROR_NULL_INPUT;
18776 
18777     if (nslits != cpl_table_get_nrow(objects))
18778         return CPL_ERROR_INCOMPATIBLE_INPUT;
18779 
18780     nref = fors_get_nobjs_perslit(reference);
18781     nobj = fors_get_nobjs_perslit(objects);
18782 
18783     noffset = 0;
18784     for (i = 0; i < nslits; i++)
18785         noffset += nobj[i];
18786 
18787     if (noffset == 0) {
18788         cpl_free(nref);
18789         cpl_free(nobj);
18790         return CPL_ERROR_DATA_NOT_FOUND;
18791     }
18792 
18793     noffset = 0;
18794     for (i = 0; i < nslits; i++)
18795         noffset += nref[i];
18796 
18797     if (noffset == 0) {
18798         cpl_free(nref);
18799         cpl_free(nobj);
18800         return CPL_ERROR_DATA_NOT_FOUND;
18801     }
18802 
18803     offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18804 
18805     noffset = 0;    // The real number of offsets found will be counted.
18806 
18807     for (i = 0; i < nslits; i++) {
18808         if (nref[i] > 0 && nobj[i] > 0) {
18809             double shift;
18810             int    length  = cpl_table_get_int(objects, "length", i, NULL);
18811             double ytop    = cpl_table_get_double(objects, "xtop", i, NULL);
18812             double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18813             int   *aref    = cpl_calloc(length, sizeof(int));
18814             int   *aobj    = cpl_calloc(length, sizeof(int));
18815             float *pref    = cpl_calloc(nref[i], sizeof(float));
18816             float *pobj    = cpl_calloc(nobj[i], sizeof(float));
18817             
18818             for (j = 0; j < nref[i]; j++) {
18819                 pref[j] = fors_get_object_position(reference, i, j + 1);
18820                 aref[(int)pref[j]] = 1;
18821             }
18822 
18823             for (j = 0; j < nobj[i]; j++) {
18824                 pobj[j] = fors_get_object_position(objects, i, j + 1);
18825                 aobj[(int)pobj[j]] = 1;
18826             }
18827 
18828             /*
18829              * Do not consider objects at border
18830              */
18831 
18832             aref[0] = 0;
18833             aref[length - 1] = 0;
18834             aobj[0] = 0;
18835             aobj[length - 1] = 0;
18836 
18837 //for (j = 0; j < nref[i]; j++)
18838 //printf("references: %f, ", pref[j]);
18839 //printf("\n");
18840 //for (j = 0; j < nref[i]; j++)
18841 //printf("objects   : %f, ", pobj[j]);
18842 //printf("\n");
18843 //for (j = 0; j < length; j++)
18844 //printf("%d", aref[j]);
18845 //printf("\n");
18846 //for (j = 0; j < length; j++)
18847 //printf("%d", aobj[j]);
18848 //printf("\n");
18849 
18850             /*
18851              * Object matching by correlation
18852              */
18853 
18854             maxcorr = 0;
18855             best_shift = length;
18856 
18857             for (shift = length/2, j = 0; j <= length; shift--, j++) {
18858                 int rstart, ostart, count;
18859 
18860                 if (shift > 0) {
18861                    rstart = shift;
18862                    ostart = 0;
18863                    count  = length - shift;
18864                 }
18865                 else {
18866                    rstart = 0;
18867                    ostart = -shift;
18868                    count  = length + shift;
18869                 }
18870 
18871                 corr = 0;
18872                 for (k = 0; k < count; k++) {
18873                     corr += aref[rstart + k] * aobj[ostart + k];
18874                 }
18875 
18876                 if (maxcorr < corr) {
18877                     maxcorr = corr;
18878                     best_shift = shift;
18879                 }
18880             }
18881 
18882             if (best_shift == length) { // No shift found
18883 //printf("%d: No shift found\n", i);
18884                 cpl_free(aref);
18885                 cpl_free(aobj);
18886                 cpl_free(pref);
18887                 cpl_free(pobj);
18888                 continue;
18889             }
18890 //printf("%d: Integer shift found = %d\n", i, best_shift);
18891 
18892             for (j = 0; j < nref[i]; j++) {
18893                 for (k = 0; k < nobj[i]; k++) {
18894                     if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18895                        double ccd_offset = (pref[j] - pobj[k]) 
18896                                          * (ytop - ybottom)
18897                                          / length;
18898 
18899 //printf("%d: Match found: %f\n", i, ccd_offset);
18900                        /* 
18901                         * The matching object is found, store the
18902                         * corresponding offset
18903                         */
18904 
18905                        cpl_array_set(offsets, noffset, ccd_offset);
18906                        noffset++;
18907                        break;
18908                     }
18909                 }
18910             }
18911 
18912             cpl_free(aref);
18913             cpl_free(aobj);
18914             cpl_free(pref);
18915             cpl_free(pobj);
18916         }
18917 //else
18918 //printf("%d: No object found\n", i);
18919     }
18920 
18921     cpl_free(nref);
18922     cpl_free(nobj);
18923 
18924 //printf("%d offsets found in total\n", noffset);
18925     if (noffset > 0) {
18926         if (noffset % 2) {
18927             *offset = cpl_array_get_median(offsets);
18928         }
18929         else {
18930             double *a = cpl_malloc(sizeof(double) * noffset);
18931             for (i = 0; i < noffset; i++) {
18932                 a[i] = cpl_array_get_double(offsets, i, NULL);
18933             }
18934             *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18935                        fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18936             cpl_free(a);
18937         }
18938     }
18939     else
18940         status = CPL_ERROR_DATA_NOT_FOUND;
18941 //printf("Median offset: %f\n", *offset);
18942 
18943     cpl_array_delete(offsets);
18944 
18945     return status;
18946 
18947 }
18948 
18949 
18961 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18962 {
18963     cpl_image *source;
18964     int        nx = cpl_image_get_size_x(image);
18965     int        ny = cpl_image_get_size_y(image);
18966     float     *idata;
18967     float     *sdata;
18968     int        i, j, pos;
18969     double     xpos, ypos, xfrac, yfrac;
18970     int        xint, yint;
18971 
18972 
18973     if (fabs(dx) >= nx || fabs(dy) >= ny)
18974         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18975 
18976     source = cpl_image_duplicate(image);
18977     idata = cpl_image_get_data_float(image);
18978     sdata = cpl_image_get_data_float(source);
18979 
18980     /*
18981      * Shift in y
18982      */
18983 
18984     yfrac = - dy - floor(- dy);
18985     xfrac = - dx - floor(- dx);
18986 
18987     for (pos = 0, j = 0; j < ny; j++) {
18988         ypos = j - dy;
18989         yint = floor(ypos);
18990         for (i = 0; i < nx; i++) {
18991             xpos = i - dx;
18992             xint = floor(xpos);
18993             if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18994                 idata[pos] = 0.0;
18995             }
18996             else {
18997                 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18998                            + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18999                            + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
19000                            + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
19001             }
19002             pos++;
19003         }
19004     }
19005 
19006     cpl_image_delete(source);
19007 
19008     return CPL_ERROR_NONE;
19009 }
19010 
19022 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
19023 {
19024 #ifdef CPL_SIZE_FORMAT
19025     cpl_size row;
19026 #else
19027     int row;
19028 #endif
19029 
19030     cpl_table_duplicate_column(slits, "x", slits, "xtop");
19031     cpl_table_add_columns(slits, "x", "xbottom");
19032     cpl_table_divide_scalar(slits, "x", 2);         // Mean x position
19033     cpl_table_subtract_scalar(slits, "x", nx/2);    // Relative to CCD center
19034     cpl_table_multiply_columns(slits, "x", "x");    // Squared
19035 
19036     cpl_table_duplicate_column(slits, "y", slits, "ytop");
19037     cpl_table_add_columns(slits, "y", "ybottom");
19038     cpl_table_divide_scalar(slits, "y", 2);         // Mean y position
19039     cpl_table_subtract_scalar(slits, "y", ny/2);    // Relative to CCD center
19040     cpl_table_multiply_columns(slits, "y", "y");    // Squared
19041 
19042     cpl_table_add_columns(slits, "x", "y");         // Distance from center
19043     cpl_table_get_column_minpos(slits, "x", &row);  // Min distance from center
19044 
19045     cpl_table_erase_column(slits, "x");
19046     cpl_table_erase_column(slits, "y");
19047 
19048     return row;
19049 }
19050 
19070 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 
19071                      double xwidth, double ywidth,
19072                      int dx, double gain, double *o_flux, double *o_err)
19073 {
19074     int    nx      = cpl_image_get_size_x(image);
19075     int    ny      = cpl_image_get_size_y(image);
19076     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
19077     int    ytop    = (int)cpl_table_get(slits, "ytop", slit, NULL);
19078     int    ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
19079     int    dy      = ytop - ybottom;
19080     int    xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
19081                             cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
19082     int    xleft   = xcenter - dx;
19083     int    xright  = xcenter + dx + 1;
19084     double area    = xwidth * ywidth; // squared mm
19085     int    npix    = (2*dx + 1) * dy;
19086     int    count   = 0;
19087     float *data    = cpl_image_get_data_float(image);
19088     double flux    = 0.0;
19089     double error   = 0.0;
19090     int    satur   = 60000;
19091     int    x, y;
19092 
19093 
19094     if (cpl_table_has_column(slits, "ywidth")) {
19095         area    = cpl_table_get(slits, "xwidth", slit, NULL)
19096                 * cpl_table_get(slits, "ywidth", slit, NULL);
19097     }
19098 
19099     *o_flux = 0.0;
19100     *o_err = 0.0;
19101 
19102     if (xleft < 0)
19103         xleft = 0;
19104 
19105     if (xleft > nx)
19106         xleft = nx;
19107 
19108     if (xright < 0)
19109         xright = 0;
19110 
19111     if (xright > nx)
19112         xright = nx;
19113 
19114     if (ytop < 0)
19115         ytop = 0;
19116 
19117     if (ytop > ny)
19118         ytop = ny;
19119 
19120     if (ybottom < 0)
19121         ybottom = 0;
19122 
19123     if (ybottom > ny)
19124         ybottom = ny;
19125 
19126     count = (xright - xleft) * (ytop - ybottom);
19127 
19128     if (count == 0)
19129         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19130 
19131     count = 0;
19132 
19133     for (y = ybottom; y < ytop; y++) {
19134         for (x = xleft; x < xright; x++) {
19135             double value = data[x + y * nx];
19136             if (value < satur) {
19137                 flux += value;
19138                 count++;
19139             }
19140         }
19141     }
19142 
19143     if (count == 0)
19144         return CPL_ERROR_DIVISION_BY_ZERO;
19145 
19146     error = sqrt(flux/gain);
19147 
19148     /*
19149      * Flux correction for lost pixels
19150      */
19151 
19152     flux *= (float)npix / count;
19153     error *= (float)npix / count;
19154 
19155     flux /= area;
19156     error /= area;
19157 
19158     *o_flux = flux;
19159     *o_err = error;
19160 
19161     return CPL_ERROR_NONE;
19162 }
19163 
19164 
19187 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
19188                                        double xwidth, double ywidth,
19189                                        double lambda, double startwave, 
19190                                        double dispersion, int dx, double gain, 
19191                                        double *o_flux, double *o_err)
19192 {
19193     int    nx      = cpl_image_get_size_x(image);
19194     int    ny      = cpl_image_get_size_y(image);
19195     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
19196     int    dy      = (int)cpl_table_get(slits, "length", slit, NULL);
19197     int    ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
19198     int    ytop    = ybottom + dy;
19199     int    xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
19200     int    xleft   = xcenter - dx;
19201     int    xright  = xcenter + dx + 1;
19202     double area    = xwidth * ywidth;
19203     int    npix    = (2*dx + 1) * dy;
19204     int    count   = 0;
19205     float *data    = cpl_image_get_data_float(image);
19206     double flux    = 0.0;
19207     double error   = 0.0;
19208     int    satur   = 60000;
19209     int    x, y;
19210 
19211 
19212     if (cpl_table_has_column(slits, "ywidth")) {
19213         area    = cpl_table_get(slits, "xwidth", slit, NULL)
19214                 * cpl_table_get(slits, "ywidth", slit, NULL);
19215     }
19216 
19217     *o_flux = 0.0;
19218     *o_err = 0.0;
19219 
19220     if (xleft < 0)
19221         xleft = 0;
19222 
19223     if (xleft > nx)
19224         xleft = nx;
19225 
19226     if (xright < 0)
19227         xright = 0;
19228 
19229     if (xright > nx)
19230         xright = nx;
19231 
19232     if (ytop < 0)
19233         ytop = 0;
19234 
19235     if (ytop > ny)
19236         ytop = ny;
19237 
19238     if (ybottom < 0)
19239         ybottom = 0;
19240 
19241     if (ybottom > ny)
19242         ybottom = ny;
19243 
19244     count = (xright - xleft) * (ytop - ybottom);
19245 
19246     if (count == 0)
19247         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
19248 
19249     count = 0;
19250 
19251     for (y = ybottom; y < ytop; y++) {
19252         for (x = xleft; x < xright; x++) {
19253             double value = data[x + y * nx];
19254             if (value < satur) {
19255                 flux += value;
19256                 count++;
19257             }
19258         }
19259     }
19260 
19261     if (count == 0)
19262         return CPL_ERROR_DIVISION_BY_ZERO;
19263 
19264     error = sqrt(flux/gain);
19265 
19266     /*
19267      * Flux correction for lost pixels
19268      */
19269 
19270     flux *= (float)npix / count;
19271     error *= (float)npix / count;
19272     
19273     flux /= area;  
19274     error /= area; 
19275     
19276     *o_flux = flux;
19277     *o_err = error;
19278 
19279     return CPL_ERROR_NONE;
19280 
19281 }
19282 
19283 
19297 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 
19298                        char *label, double *mvalue)
19299 {
19300     int        position   = cpl_table_get_int(slits, "position", slit, NULL);
19301     int        length     = cpl_table_get_int(slits, "length", slit, NULL);
19302     cpl_table *tmp        = cpl_table_extract(table, position, length);
19303 
19304     *mvalue = cpl_table_get_column_median(tmp, label);
19305     cpl_table_delete(tmp);
19306 
19307     if (cpl_error_get_code() != CPL_ERROR_NONE)
19308         return 1;
19309 
19310     return 0;
19311 }
19312 
19313 
19325 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
19326 {
19327       cpl_mask  *kernel   = cpl_mask_new(nx, ny);
19328       cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
19329                                           cpl_image_get_size_y(image),
19330                                           cpl_image_get_type(image));
19331 
19332       cpl_mask_not(kernel);
19333       cpl_image_filter_mask(filtered, image, kernel,
19334                             CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
19335       cpl_mask_delete(kernel);
19336 
19337       return filtered;
19338 }
19339 
19340 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
19341 {
19342     int treat_as_lss = 1;
19343     double mxpos = cpl_table_get_column_median(maskslits, "xtop");
19344     double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
19345     cpl_size nslits = cpl_table_get_nrow(maskslits);
19346 
19347     //If not all the slits are illuminated, then we cannot say that
19348     //it is lss-like (PIPE-4380)
19349     if(nslits_out_det != 0)
19350         return 0;
19351 
19352     for (cpl_size i = 0; i < nslits; i++) {
19353         if (fabs(mxpos-slit_xpos[i]) > 0.01) {
19354             treat_as_lss = 0;
19355             break;
19356         }
19357     }
19358     return treat_as_lss;
19359 }

Generated on 12 Feb 2016 for FORS Pipeline Reference Manual by  doxygen 1.6.1