FORS Pipeline Reference Manual 4.9.9
moses.c
00001 /* $Id: moses.c,v 1.97 2012/02/06 16:17:06 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: 2012/02/06 16:17:06 $
00024  * $Revision: 1.97 $
00025  * $Name: fors-4_9_9 $
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 
00051 float cpl_tools_get_median_float(float *, cpl_size);
00052 
00053 #define MAX_COLNAME      (80)
00054 #define STRETCH_FACTOR   (1.20)
00055 
00056 // Related to mos_identify_peaks(), used in multiplex mode
00057 
00058 static int mos_multiplex   = -1;
00059 static int mos_region_size = 800;
00060 
00061 static double default_lines_hi[] = {   /* Default sky line catalog */
00062                     5577.338,          /* for high res data        */
00063                     5889.953,
00064                     5895.923,
00065                     5915.301,
00066                     5932.862,
00067                     5953.420,
00068                     6257.961,
00069                     6287.434,
00070                     6300.304,
00071                     6306.869,
00072                     6363.780,
00073                     6498.729,
00074                     6533.044,
00075                     6553.617,
00076                     6841.945,
00077                     6863.955,
00078                     6870.994,
00079                     6889.288,
00080                     6900.833,
00081                     6912.623,
00082                     6923.220,
00083                     6939.521,
00084                     6969.930,
00085                     7003.858,
00086                     7244.907,
00087                     7276.405,
00088                     7284.439,
00089                     7316.282,
00090                     7329.148,
00091                     7340.885,
00092                     7358.659,
00093                     7571.746,
00094                     7750.640,
00095                     7759.996,
00096                     7794.112,
00097                     7808.467,
00098                     7821.503,
00099                     7841.266,
00100                     7913.708,
00101                     7949.204,
00102                     7964.650,
00103                     7993.332,
00104                     8014.059,
00105                     8310.719,
00106                     8344.602,
00107                     8382.392,
00108                     8399.170,
00109                     8415.231,
00110                     8430.174,
00111                     8452.250,
00112                     8493.389,
00113                     8791.186,
00114                     8827.096,
00115                     8885.850,
00116                     8903.114,
00117                     8943.395,
00118                     8988.366
00119                     };
00120 
00121 static double default_lines_lo[] = {   /* Default sky line catalog */
00122                     5577.338,          /* for low res data         */
00123                     6300.304,
00124                     6863.955,
00125                     7571.746,
00126                     7964.650,
00127                     7993.332
00128                     };
00129 
00130 
00140 /*
00141  * The following macros and function for finding the k-th smallest
00142  * value on a float array will be accessible from cpl_tools once
00143  * this module will be moved into the CPL.
00144  */
00145 
00146 /****
00147 
00148 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00149 
00150 static float kthSmallest(float a[], int n, int k)
00151 {
00152   register int i,j,l,m;
00153   register float x;
00154 
00155   l = 0;
00156   m = n-1;
00157   while (l < m) {
00158     x = a[k];
00159     i = l;
00160     j = m;
00161     do {
00162       while (a[i] < x) {
00163         i++;
00164       }
00165       while (x < a[j]) {
00166         j--;
00167       }
00168       if (i <= j) {
00169         PIX_SWAP(a[i],a[j]);
00170         i++;
00171         j--;
00172       }
00173     } while (i <= j);
00174 
00175     if (j < k) {
00176       l = i;
00177     }
00178     if (k < i) {
00179       m = j;
00180     }
00181 
00182   }
00183   return(a[k]);
00184 }
00185 
00186 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00187 
00188 ****/
00189 
00190 /* 
00191  * Return random number with gaussian distribution (mean = 0, variance = 1)
00192  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00193  * indicating whether to "seed" or not the sequence of generated random 
00194  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00195  * and at further calls the input argument is ignored. This function
00196  * generates two random numbers at each call, returning the first one
00197  * at odd calls, and the second one at even calls.
00198  */
00199 
00200 static void mos_seed(void)
00201 {
00202     srand((unsigned int)time((time_t *)0));
00203 }
00204 
00205 static double mos_randg(int seme)
00206 {
00207     static int doit = 1;
00208     static int gotit = 1;
00209     double x1, x2, w, y1;
00210     static double y2;
00211 
00212     if (gotit && seme) {
00213         mos_seed();
00214         gotit = 0;
00215     }
00216 
00217     if (doit) {
00218         doit = 0;
00219         do {
00220             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00221             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00222             w = x1 * x1 + x2 * x2;
00223         } while (w >= 1.0 || w == 0.0);
00224     
00225         w = sqrt( (-2.0 * log(w)) / w);
00226     
00227         y1 = x1 * w;
00228         y2 = x2 * w;
00229         return y1;
00230     }
00231 
00232     doit = 1;
00233     return y2;
00234 }
00235 
00236 /* 
00237  * This function contained a dependency on the VIMOS library
00238  * (function medianPixelvalue()): it should be removed as soon as an 
00239  * image median filtering with generic kernel will be implemented
00240  * in the CPL. Currently it has been solved by a direct call to
00241  * a cpl_tool function.
00242  */
00243 
00244 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00245                                             int filtsizey, int refrow,
00246                                             int above, int below, int step)
00247 {
00248 
00249   const char *func = "mos_image_general_median_filter";
00250 
00251   cpl_image  *filt_img = NULL;
00252   int         col, row;
00253   float      *buf = NULL;
00254   float      *data;
00255   float      *fdata;
00256   int         upright_y, loleft_y;
00257   int         j;
00258   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00259   int         f2y;
00260   int         nx = cpl_image_get_size_x(ima_in);
00261   int         ny = cpl_image_get_size_y(ima_in);
00262   int         firstRow;
00263 
00264 
00265   if (yIsEven) filtsizey++;
00266 
00267   if (ny <= filtsizey) {
00268     cpl_msg_error(func, 
00269                   "Median filter size: %d, image size: %d", filtsizey, ny);
00270     return NULL;
00271   }
00272 
00273   f2y = filtsizey / 2;
00274 
00275   filt_img = cpl_image_duplicate(ima_in);
00276   buf = cpl_malloc(filtsizey * sizeof(float));
00277   data = cpl_image_get_data(ima_in);
00278   fdata = cpl_image_get_data(filt_img);
00279 
00280   firstRow = refrow - step * (below / step);
00281   if (firstRow < f2y)
00282     firstRow += step;
00283 
00284   for (col = 0; col < nx; col++) {
00285     for (row = firstRow; row < refrow + above; row += step) {
00286       if (row >= ny - f2y)
00287         break;
00288       loleft_y = row - f2y;
00289       upright_y = row + f2y + 1;
00290       for (j = loleft_y; j < upright_y; j++)
00291         buf[j - loleft_y] = data[col + j * nx];
00292 
00293       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00294     }
00295   }
00296 
00297   cpl_free(buf);
00298 
00299   return filt_img;
00300 
00301 }
00302 
00303 
00304 /*
00305  * The following static function is used to find an accurate position
00306  * of a peak within a short interval (however at least 5 pixels long). 
00307  * The basic idea is to find the baricenter of all the pixel values 
00308  * that pass a threshold level between the median value and the maximum
00309  * value within the examined interval (in case such levels are equal,
00310  * the input is considered flat and no position is returned). At least
00311  * minPoints must pass this threshold, or no position is computed. To
00312  * evaluate the significance of the computed baricenter, the variance 
00313  * of the contributing positions (relative to the found baricenter) is 
00314  * also evaluated, and compared with the expected variance for a uniform 
00315  * distribution of positions. If the observed variance is greater than 
00316  * 80% of the variance of the uniform distribution, the found position 
00317  * is rejected.
00318  */
00319 
00320 static int peakPosition(const float *data, int size, float *position,
00321                         int minPoints)
00322 {
00323   int    i;
00324   int    count = 0;
00325   float *copy;
00326   float  max, median, level, pos, variance, uniformVariance;
00327   double sum, weights;
00328 
00329 
00330   if (data == NULL)
00331       return 1;
00332 
00333   if (size < 5)         /* Hardcoded, I know... */
00334       return 1;
00335 
00336 
00337   /*
00338    *  Find median level
00339    */
00340 
00341   copy = (float *) cpl_malloc(size*sizeof(float));
00342   for (i = 0; i < size; i++)
00343       copy[i] = data[i];
00344   median = cpl_tools_get_median_float(copy, size);
00345   cpl_free(copy);
00346 
00347 
00348   /*
00349    *  Find max
00350    */
00351 
00352   max = data[0];
00353   for (i = 1; i < size; i++)
00354       if (data[i] > max)
00355           max = data[i];
00356 
00357 
00358   /*
00359    *  If the max equals the median we have a flat input, therefore
00360    *  no peak is found.
00361    */
00362 
00363   if (max-median < 0.00001)
00364       return 1;
00365 
00366 
00367   /*
00368    *  Discrimination level: only pixels with values above this
00369    *  level are considered in baricenter calculation.
00370    */
00371 
00372   level = (max + median) / 2;
00373 
00374 
00375   /*
00376    *  Of the values above this level compute the baricenter and
00377    *  then the variance of the positions used. Note that the weights
00378    *  are taken as the difference between the pixels values and
00379    *  the median level (supposedly the background).
00380    */
00381 
00382   count = 0;
00383   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00384       if (data[i] > level) {
00385           count++;
00386           weights += (data[i] - median);
00387           sum     += i * (data[i] - median);
00388       }
00389   }
00390 
00391 
00392   /*
00393    *  If too few values are above threshold, refuse the position
00394    *  as insignificant
00395    */
00396 
00397   if (count < minPoints)
00398       return 1;
00399 
00400   pos = sum / weights;
00401   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00402       if (data[i] > level) {
00403           weights++;
00404           sum += (i - pos) * (i - pos);
00405       }
00406   }
00407   variance = sqrt(sum / weights);
00408 
00409 
00410  /*
00411   *  The "uniform variance" is the variance that should be obtained
00412   *  in the case of uniform distribution of the points positions in
00413   *  the selected interval. If the real variance is comparable with
00414   *  this value, the peak is considered not found.
00415   */
00416 
00417   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00418 
00419   if (variance > 0.8 * uniformVariance)
00420       return 1;
00421 
00422   *position = pos + 0.5;
00423 
00424   return 0;
00425 }
00426 
00427 
00428 /*
00429  *  The following static function determines the quantity dx to be
00430  *  added to the position of the highest pixel of a fiber profile,
00431  *  to get the true position of the profile maximum. All is needed
00432  *  is the maximum observed value v2 in the profile, and the observed
00433  *  values v1 and v3 of the previous and the next pixels in the profile.
00434  *  
00435  *  The following ratio is defined:
00436  *  
00437  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00438  *      
00439  *  This is a conventional ratio that wouldn't diverge for any set of
00440  *  pixel values, and that would not depend on the presence of background
00441  *  (with the assumption that the background level is the same for the 
00442  *  three pixels). R has also been chosen in such a way that its value
00443  *  is already quite close to the real dx. It should be noted that the
00444  *  following condition should be fulfilled:
00445  *
00446  *           v1  <= v2   and   v3  <  v2
00447  *  or
00448  *           v1  <  v2   and   v3  <=  v2
00449  *
00450  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00451  *  boundary cases, one has:
00452  *
00453  *           v2 = v1   and   R = dx = -0.5
00454  *           v2 = v3   and   R = dx =  0.5
00455  *
00456  *  Another special case is when the observed pixel values are perfectly
00457  *  symmetrical:
00458  *
00459  *           v1 = v3   and   R = dx =  0.0
00460  *
00461  *  In all the intermediate cases the relation between R and dx depends
00462  *  on the shape of the fiber profile, that has been determined elsewhere.
00463  *  Using the accurate reconstruction of the fiber profile obtained by 
00464  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00465  *  that R differs from dx always less than 0.01 pixels. If the condition
00466  *
00467  *           v1  <= v2   and   v3  <  v2
00468  *  or
00469  *           v1  <  v2   and   v3  <=  v2
00470  *
00471  *  is not fulfilled, then this function returns the value 2.0.
00472  */
00473 
00474 static double values_to_dx(double v1, double v2, double v3)
00475 {
00476 
00477   static double epsilon = 0.00000001;
00478   double        r       = 2.0;
00479 
00480 
00481   if (v1 > v2 || v3 > v2)
00482     return r;
00483 
00484   if (2 * v2 - v1 - v3 < epsilon)
00485     return r;
00486 
00487   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00488 
00489   return r;
00490 
00491 }
00492 
00493 
00494 /*
00495  * The following static function passes a min filter of given box
00496  * size on the data buffer. The box size must be a positive odd integer.
00497  */
00498 
00499 static float *min_filter(float *buffer, int length, int size)
00500 {
00501     float *minf  = cpl_calloc(length, sizeof(float));
00502     float  min;
00503     int    start = size / 2;
00504     int    end   = length - size / 2;
00505     int    i, j;
00506 
00507 
00508     for (i = start; i < end; i++) {
00509         min = buffer[i-start];
00510         for (j = i - start + 1; j <= i + start; j++)
00511             if (min > buffer[j])
00512                 min = buffer[j];
00513         minf[i] = min;
00514     }
00515 
00516     for (i = 0; i < start; i++)
00517         minf[i] = minf[start];
00518 
00519     for (i = end; i < length; i++)
00520         minf[i] = minf[end-1];
00521 
00522     return minf;
00523 }
00524 
00525 
00526 /*
00527  * The following static function passes a max filter of given box
00528  * size on the data buffer. The box size must be a positive odd integer.
00529  */
00530  
00531 static float *max_filter(float *buffer, int length, int size)
00532 {
00533     float *maxf  = cpl_calloc(length, sizeof(float));
00534     float  max;
00535     int    start = size / 2;
00536     int    end   = length - size / 2;
00537     int    i, j;
00538 
00539 
00540     for (i = start; i < end; i++) {
00541         max = buffer[i-start];
00542         for (j = i - start + 1; j <= i + start; j++)
00543             if (max < buffer[j])
00544                 max = buffer[j];
00545         maxf[i] = max;
00546     }
00547 
00548     for (i = 0; i < start; i++)
00549         maxf[i] = maxf[start];
00550 
00551     for (i = end; i < length; i++)
00552         maxf[i] = maxf[end-1];
00553 
00554     return maxf;
00555 }
00556 
00557 
00558 /*
00559  * The following static function passes a running average of given box
00560  * size on the data buffer. The box size must be a positive odd integer.
00561  */
00562  
00563 static float *smo_filter(float *buffer, int length, int size)
00564 {
00565     float *smof  = cpl_calloc(length, sizeof(float));
00566     double sum;
00567     int    start = size / 2;
00568     int    end   = length - size / 2;
00569     int    i, j;
00570 
00571 
00572     for (i = start; i < end; i++) {
00573         sum = 0.0;
00574         for (j = i - start; j <= i + start; j++)
00575             sum += buffer[j];
00576         smof[i] = sum / size;
00577     }
00578 
00579     for (i = 0; i < start; i++)
00580         smof[i] = smof[start];
00581 
00582     for (i = end; i < length; i++)
00583         smof[i] = smof[end-1];
00584 
00585     return smof;
00586 }
00587 
00588 /*
00589  * The following two static functions are used to read and write from the 
00590  * global distortion table the different model components. Conventionally
00591  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00592  * storage for model coefficients, and these functions guarantee that the
00593  * coefficients are read in and written out correctly, independent on their
00594  * physical meaning. The first 6 table rows are a description of the IDS
00595  * coefficients, followed by a row containing only the used reference 
00596  * wavelength. The remaining 3 are a description of the spectral curvature.
00597  * The first row is a description of coefficient c0, the second of coefficient
00598  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00599  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00600  * bivariate polynomialx on x,y mask coordinates. If the input table
00601  * to the write routine is NULL, it is allocated and initialised. Also
00602  * the input polynomial could be NULL, and nothing would be written to 
00603  * the table. If both pointers are NULL the function is basically a
00604  * constructor of the global distortion table.
00605  */
00606 
00607 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row)
00608 {
00609     cpl_polynomial *poly = NULL;
00610     cpl_size        p[2];
00611     cpl_size        degree = 2;
00612     int             null;
00613     double          coeff;
00614 
00615     char   name[MAX_COLNAME];
00616 
00617 
00618     for (p[0] = 0; p[0] <= degree; p[0]++) {
00619         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00620             snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00621             coeff = cpl_table_get_double(global, name, row, &null);
00622             if (null)
00623                 continue;
00624             if (poly == NULL)
00625                 poly = cpl_polynomial_new(2);
00626             cpl_polynomial_set_coeff(poly, p, coeff);
00627         }
00628     }
00629 
00630     return poly;
00631 }
00632 
00633 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00634                                           cpl_polynomial *poly)
00635 {
00636     cpl_table *table;
00637     cpl_size   p[2];
00638     cpl_size   degree = 2;
00639     int        nrow = 10;
00640 
00641     char       name[MAX_COLNAME];
00642 
00643 
00644     if (global) {
00645         table = global;
00646     }
00647     else {
00648         table = cpl_table_new(nrow);
00649         for (p[0] = 0; p[0] <= degree; p[0]++) {
00650             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00651                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00652                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00653             }
00654         }
00655     }
00656 
00657     if (poly) {
00658         for (p[0] = 0; p[0] <= degree; p[0]++) {
00659             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00660                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00661                 cpl_table_set_double(table, name, row, 
00662                                      cpl_polynomial_get_coeff(poly, p));
00663             }
00664         }
00665     }
00666 
00667     return table;
00668 }
00669 
00670 
00671 /*
00672  * The following static function is performing a robust linear fit
00673  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00674  *
00675  *  ----> y = a + b * x
00676  *
00677  * This function return 0 on success.
00678  */
00679 
00680 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00681 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00682                            double *abdev)
00683 {
00684     cpl_vector *vx;
00685     cpl_vector *vy;
00686     cpl_vector *va;
00687 
00688     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00689     double  sx, sy, sxy, sxx, chisq;
00690     double *arr;
00691     double  aa_ls, bb_ls;
00692     double *x;
00693     double *y;
00694     int     np;
00695     int     iter;
00696     int     max_iterate = 30;
00697     int     i;
00698 
00699 
00700     np = cpl_bivector_get_size(list);
00701     vx = cpl_bivector_get_x(list);
00702     vy = cpl_bivector_get_y(list);
00703     x = cpl_vector_get_data(vx);
00704     y = cpl_vector_get_data(vy);
00705 
00706     sx = sy = sxx = sxy = 0.00;
00707     for (i = 0; i < np; i++) {
00708         sx  += x[i];
00709         sy  += y[i];
00710         sxy += x[i] * y[i];
00711         sxx += x[i] * x[i];
00712     }
00713 
00714     del = np * sxx - sx * sx;
00715     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00716     bb_ls = bb = (np * sxy - sx * sy) / del;
00717 
00718     chisq = 0.00;
00719     for (i = 0; i < np; i++) {
00720         temp = y[i] - (aa+bb*x[i]);
00721         temp *= temp;
00722         chisq += temp;
00723     }
00724 
00725     va = cpl_vector_new(np);
00726     arr = cpl_vector_get_data(va);
00727     sigb = sqrt(chisq/del);
00728     b1 = bb;
00729 
00730     bcomp = b1;
00731     sum = 0.00;
00732     for (i = 0; i < np; i++) {
00733         arr[i] = y[i] - bcomp * x[i];
00734     }
00735     aa = cpl_vector_get_median_const(va);
00736     abdevt = 0.0;
00737     for (i = 0; i < np; i++) {
00738         d = y[i] - (bcomp * x[i] + aa);
00739         abdevt += fabs(d);
00740         if (y[i] != 0.0) 
00741             d /= fabs(y[i]);
00742         if (fabs(d) > 1e-7) 
00743             sum += (d >= 0.0 ? x[i] : -x[i]);
00744     }
00745     f1 = sum;
00746 
00747     b2 = bb + SEGNO(3.0 * sigb, f1);
00748 
00749     bcomp = b2;
00750     sum = 0.00;
00751     for (i = 0; i < np; i++) {
00752         arr[i] = y[i] - bcomp * x[i];
00753     }
00754     aa = cpl_vector_get_median_const(va);
00755     abdevt = 0.0;
00756     for (i = 0; i < np; i++) {
00757         d = y[i] - (bcomp * x[i] + aa);
00758         abdevt += fabs(d);
00759         if (y[i] != 0.0) 
00760             d /= fabs(y[i]);
00761         if (fabs(d) > 1e-7) 
00762             sum += (d >= 0.0 ? x[i] : -x[i]);
00763     }
00764     f2 = sum;
00765 
00766     if (fabs(b2-b1)<1e-7) {
00767         *a = aa;
00768         *b = bb;
00769         *abdev = abdevt / (double)np;
00770         cpl_vector_delete(va);
00771         return 0;
00772     }
00773 
00774     iter = 0;
00775     while (f1*f2 > 0.0) {
00776         bb = 2.0*b2-b1;
00777         b1 = b2;
00778         f1 = f2;
00779         b2 = bb;
00780 
00781         bcomp = b2;
00782         sum = 0.00;
00783         for (i = 0; i < np; i++) {
00784             arr[i] = y[i] - bcomp * x[i];
00785         }
00786         aa = cpl_vector_get_median_const(va);
00787         abdevt = 0.0;
00788         for (i = 0; i < np; i++) {
00789             d = y[i] - (bcomp * x[i] + aa);
00790             abdevt += fabs(d);
00791             if (y[i] != 0.0) 
00792                 d /= fabs(y[i]);
00793             if (fabs(d) > 1e-7) 
00794                 sum += (d >= 0.0 ? x[i] : -x[i]);
00795         }
00796         f2 = sum;
00797         iter++;
00798         if (iter >= max_iterate) 
00799             break;
00800     }
00801     if (iter >= max_iterate) {
00802         *a = aa_ls;
00803         *b = bb_ls;
00804         *abdev = -1.0;
00805         cpl_vector_delete(va);
00806         return 1;
00807     }
00808 
00809     sigb = 0.01 * sigb;
00810     while (fabs(b2-b1) > sigb) {
00811         bb = 0.5 * (b1 + b2);
00812         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00813             break;
00814         bcomp = bb;
00815         sum = 0.0;
00816         for (i = 0; i < np; i++) {
00817             arr[i] = y[i] - bcomp * x[i];
00818         }
00819         aa = cpl_vector_get_median_const(va);
00820         abdevt = 0.0;
00821         for (i = 0; i < np; i++) {
00822             d = y[i] - (bcomp * x[i] + aa);
00823             abdevt += fabs(d);
00824             if (y[i] != 0.0) 
00825                 d /= fabs(y[i]);
00826             if (fabs(d) > 1e-7) 
00827                 sum += (d >= 0.0 ? x[i] : -x[i]);
00828         }
00829         f = sum;
00830 
00831         if (f*f1 >= 0.0) {
00832             f1=f;
00833             b1=bb;
00834         } 
00835         else {
00836             f2=f;
00837             b2=bb;
00838         }
00839     }
00840     cpl_vector_delete(va);
00841     *a = aa;
00842     *b = bb;
00843     *abdev = abdevt / np;
00844     return 0;
00845 }
00846 #undef SEGNO
00847 
00848 /*      
00849  * The following static function applies the Hough transform from a table
00850  * of points to another table of points. Given the points p_i = (x_i,y_i)
00851  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00852  * and Y = y_i - X*x_i is computed and added to the output table for each
00853  * p_i, p_j pair. This means that if the input table has N points, the
00854  * output table has N*(N-1)/2 points.
00855  */
00856     
00857 /* static */
00858 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00859 {
00860     cpl_table *output;
00861     double    *xdata;
00862     double    *ydata;
00863     double    *xodata;
00864     double    *yodata;
00865     int        npoints;
00866     int        opoints;
00867     int        i, j, k;
00868 
00869 
00870     npoints = cpl_table_get_nrow(table);
00871     opoints = npoints*(npoints-1)/2;
00872 
00873     output = cpl_table_new(opoints);
00874     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00875     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00876     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00877     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00878 
00879     xodata = cpl_table_get_data_double(output, "m");
00880     yodata = cpl_table_get_data_double(output, "q");
00881     
00882     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00883     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00884 
00885     xdata = cpl_table_get_data_double(table, "x");
00886     ydata = cpl_table_get_data_double(table, "y");
00887 
00888     k = 0;
00889     for (i = 0; i < npoints; i++) {
00890         for (j = i+1; j < npoints; j++) {
00891             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00892             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00893             k++;
00894         }
00895     }
00896 
00897     if (k != opoints)
00898         printf("Assert k = %d, expected %d\n", k, opoints);
00899 
00900     cpl_table_erase_column(table, "x");
00901     cpl_table_erase_column(table, "y");
00902 
00903     return output;
00904 }
00905 
00906 
00907 /*
00908  * The following static function is performing the spectral
00909  * extraction for the function mos_extract_objects()
00910  */
00911 
00912 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00913                            cpl_image *extracted, cpl_image *sky, 
00914                            cpl_image *error, int nobjects, int extraction, 
00915                            double ron, double conad, int ncomb)
00916 {
00917 
00918   cpl_vector *vprofile;
00919   cpl_matrix *kernel;
00920   cpl_image  *smowin;
00921 
00922   int i, j;
00923   int specLen;
00924   int numRows;
00925   int index;
00926   int iter;
00927   int maxIter   = 2;         /* Not less than 2 !!! */
00928   int smoothBox = 31;        /* Not less than 5 !!! */
00929   double nsigma = 5.0;
00930 
00931   double sumWeight, sum, sumSky, sumProf, variance, weight;
00932   double *profile;
00933   double *buffer;
00934   float  *edata;
00935   float  *ekdata;
00936   float  *endata;
00937   float  *sdata;
00938   float  *kdata;
00939   float  *fdata;
00940 
00941   double value;
00942 
00943 
00944   specLen = cpl_image_get_size_x(sciwin);
00945   numRows = cpl_image_get_size_y(sciwin);
00946 
00947   edata = cpl_image_get_data(extracted);
00948   edata += nobjects * specLen;
00949 
00950   ekdata = cpl_image_get_data(sky);
00951   ekdata += nobjects * specLen;
00952 
00953   endata = cpl_image_get_data(error);
00954   endata += nobjects * specLen;
00955 
00956   sdata = cpl_image_get_data(sciwin);
00957   kdata = cpl_image_get_data(skywin);
00958 
00959 
00960   /*
00961    * Initial spectrum estimate
00962       if (sdata[i + j * specLen] > 0.0)
00963    */
00964 
00965   if (extraction && numRows > 5) {
00966       smowin = mos_image_filter_median(sciwin, 3, 3);
00967       fdata = cpl_image_get_data(smowin);
00968       for (i = 0; i < specLen; i++)
00969         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00970             edata[i] += fdata[i + j * specLen];
00971       cpl_image_delete(smowin);
00972   }
00973   else {
00974       for (i = 0; i < specLen; i++)
00975         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00976             edata[i] += sdata[i + j * specLen];
00977   }
00978 
00979   if (extraction) {
00980 
00981     profile = cpl_calloc(specLen * numRows, sizeof(double));
00982     buffer  = cpl_calloc(specLen, sizeof(double));
00983 
00984     for (iter = 0; iter < maxIter; iter++) {
00985 
00986       /*
00987        * Normalised spatial profile
00988        */
00989 
00990       for (i = 0; i < specLen; i++) {
00991         for (j = 0; j < numRows; j++) {
00992           index = i + j * specLen;
00993 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00994           if (fabs(edata[i]) > 0.00001)
00995             profile[index] = sdata[index] / edata[i];
00996           else
00997             profile[index] = 0.0;
00998         }
00999       }
01000 
01001       for (j = 0; j < numRows; j++) {
01002 
01003         /*
01004          * Smooth each row in the dispersion direction, and enforce positivity
01005          */
01006 
01007         for (i = 0; i < specLen - smoothBox; i++) {
01008           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01009           value = cpl_vector_get_median_const(vprofile);
01010           cpl_vector_unwrap(vprofile);
01011           if (value < 0)
01012             value = 0.0;
01013           buffer[i + smoothBox / 2] = value;
01014         }
01015 
01016         /*
01017          * Replace the end portions (i.e., not median filtered) with a mean
01018          */
01019 
01020         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01021         value = cpl_vector_get_mean(vprofile);
01022         cpl_vector_unwrap(vprofile);
01023 
01024         if (value < 0)
01025             value = 0.0;
01026 
01027         for (i = 0; i < smoothBox / 2; i++)
01028           buffer[i] = value;
01029 
01030         vprofile = cpl_vector_wrap(smoothBox / 2, 
01031                                    profile + specLen - smoothBox/2 + j*specLen);
01032         value = cpl_vector_get_mean(vprofile);
01033         cpl_vector_unwrap(vprofile);
01034 
01035         if (value < 0)
01036             value = 0.0;
01037 
01038         for (i = 0; i < smoothBox / 2; i++)
01039           buffer[i + specLen - smoothBox / 2] = value;
01040 
01041         for (i = 0; i < specLen; i++)
01042           profile[i + j * specLen] = buffer[i];
01043 
01044       }
01045 
01046       /*
01047        * Enforce normalization of spatial profile after smoothing
01048        */
01049 
01050       for (i = 0; i < specLen; i++) {
01051         for (j = 0, value = 0.0; j < numRows; j++)
01052           value += profile[i + j * specLen];
01053         if (value > 0.00001)
01054           for (j = 0; j < numRows; j++)
01055             profile[i + j * specLen] /= value;
01056         else
01057           for (j = 0; j < numRows; j++)
01058             profile[i + j * specLen] = 0.0;
01059       }
01060 
01061 
01062       /*
01063        * Optimal extraction
01064        */
01065 
01066       for (i = 0; i < specLen; i++) {
01067         sum = 0.0;
01068         sumSky = 0.0;
01069         sumWeight = 0.0;
01070         sumProf = 0.0;
01071         for (j = 0; j < numRows; j++) {
01072           index = i + j * specLen;
01073 /*        
01074 if (sdata[index] > 0.0) {
01075 */
01076             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01077                      / conad;
01078             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01079             value = sdata[index] - edata[i] * profile[index];
01080             if (fabs(value) / sqrt(variance) < nsigma) {
01081               weight = 1000000 * profile[index] / variance;
01082               sum += weight * sdata[index];
01083               sumSky += weight * kdata[index];
01084               sumWeight += weight * profile[index];
01085               sumProf += profile[index];
01086             }
01087 /*
01088 }      
01089 */
01090         }
01091 
01092         if (sumWeight > 0.00001) {
01093           edata[i] = sum / sumWeight;
01094           ekdata[i] = sumSky / sumWeight;
01095           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01096         }
01097         else {
01098 /*
01099           edata[i] = 0.0;
01100           ekdata[i] = 0.0;
01101           endata[i] = 0.0;
01102 */
01103           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01104         }
01105       }
01106     }
01107     cpl_free(profile);
01108     cpl_free(buffer);
01109   }
01110   else {
01111 
01112     /*
01113      * Add sky estimation for the simple aperture extraction.
01114         if (kdata[i + j * specLen] > 0.0)
01115      */
01116 
01117     for (i = 0; i < specLen; i++)
01118       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01119           ekdata[i] += kdata[i + j * specLen];
01120 
01121     /*
01122      * Add error estimation for the simple aperture extraction.
01123      */
01124 
01125     for (i = 0; i < specLen; i++)
01126       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01127 
01128   }
01129 
01130 }
01131 
01132 
01179 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01180                                  cpl_table *ids, cpl_table *crv, 
01181                                  double reference)
01182 {
01183     const char *func = "mos_global_distortion";
01184 
01185     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01186 
01187     cpl_table      *global = NULL;
01188     cpl_table      *coeff;
01189     cpl_table      *dummy;
01190     cpl_vector     *ci;
01191     cpl_vector     *xmask;
01192     cpl_vector     *ymask;
01193     cpl_bivector   *mask;
01194     cpl_vector     *xccd;
01195     cpl_vector     *yccd;
01196     cpl_bivector   *ccd;
01197     cpl_polynomial *poly;
01198     double         *xtop;
01199     double         *ytop;
01200     double         *xbottom;
01201     double         *ybottom;
01202     double         *mxtop;
01203     double         *mytop;
01204     double         *mxbottom;
01205     double         *mybottom;
01206     int            *position;
01207     int            *length;
01208     int            *slit_id;
01209     int            *mslit_id;
01210     int             nslits, nmaskslits, npoints;
01211     int             order;
01212     int             i, j;
01213     int             minslit = 6;    // 12;
01214 
01215 
01216 /* *+
01217 printf("error1: %s\n", cpl_error_get_message());
01218 +* */
01219     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01220         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01221         return NULL;
01222     }
01223 /* *+
01224 printf("error1a: %s\n", cpl_error_get_message());
01225 +* */
01226 
01227     nslits = cpl_table_get_nrow(slits);
01228 /* *+
01229 printf("error1b: %s\n", cpl_error_get_message());
01230 +* */
01231 
01232     if (nslits < minslit) {
01233         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01234                         "distortion model determination", nslits, minslit);
01235         return NULL;
01236     }
01237 /* *+
01238 printf("error1c: %s\n", cpl_error_get_message());
01239 +* */
01240 
01241     nmaskslits = cpl_table_get_nrow(maskslits);
01242 
01243     length   = cpl_table_get_data_int(slits, "length");
01244     position = cpl_table_get_data_int(slits, "position");
01245     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01246     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01247     xtop     = cpl_table_get_data_double(slits, "xtop");
01248     ytop     = cpl_table_get_data_double(slits, "ytop");
01249     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01250     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01251     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01252     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01253     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01254     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01255 
01256 
01257     /*
01258      * Global IDS
01259      */
01260 
01261     coeff = cpl_table_new(nslits);
01262     cpl_table_copy_structure(coeff, ids);
01263     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01264     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01265     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01266     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01267 
01268 /* *+
01269 printf("error2: %s\n", cpl_error_get_message());
01270 +* */
01271     for (i = 0; i < nslits; i++) {
01272         for (j = 0; j < nmaskslits; j++) {
01273             if (slit_id[i] == mslit_id[j]) {
01274                 cpl_table_set_double(coeff, "xmask", i,
01275                                      (mxtop[j] + mxbottom[j]) / 2);
01276                 cpl_table_set_double(coeff, "ymask", i,
01277                                      (mytop[j] + mybottom[j]) / 2);
01278             }
01279         }
01280     }
01281 
01282     if (cpl_table_has_invalid(coeff, "xmask")) {
01283         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01284         cpl_table_delete(coeff);
01285         return NULL;
01286     }
01287 
01288     for (i = 0; i < nslits; i++) {
01289         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01290         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01291     }
01292 
01293 /* *+
01294 printf("error3: %s\n", cpl_error_get_message());
01295 +* */
01296     for (i = 0; i < nslits; i++) {
01297 
01298         if (length[i] == 0)
01299             continue;
01300 
01301         cpl_table_and_selected_window(ids, position[i], length[i]);
01302         dummy = cpl_table_extract_selected(ids);
01303         for (j = 0; j < 6; j++) {
01304             if (cpl_table_has_column(dummy, clab[j])) {
01305                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01306                     cpl_table_set_double(coeff, clab[j], i, 
01307                          cpl_table_get_column_median(dummy, clab[j]));
01308                 }
01309             }
01310         }
01311 
01312         cpl_table_delete(dummy);
01313         cpl_table_select_all(ids);
01314             
01315     }
01316 
01317 /* *+
01318 printf("error4: %s\n", cpl_error_get_message());
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 /* *+
01389 printf("error5: %s\n", cpl_error_get_message());
01390 +* */
01391     cpl_table_delete(coeff);
01392 /* *+
01393 printf("error6: %s\n", cpl_error_get_message());
01394 +* */
01395 
01396 
01397     /*
01398      * Add model's reference wavelength
01399      */
01400 
01401     cpl_table_set_double(global, "a00", 6, reference);
01402 
01403 
01404     /*
01405      * Global curvature model
01406      */
01407 
01408     coeff = cpl_table_duplicate(crv);
01409     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01410     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01411     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01412     npoints = cpl_table_get_nrow(coeff);
01413 
01414 /* *+
01415 printf("error7: %s\n", cpl_error_get_message());
01416 +* */
01417     for (i = 0; i < npoints; i++) {
01418         for (j = 0; j < nmaskslits; j++) {
01419             if (slit_id[i] == mslit_id[j]) {
01420                 if (i%2) {
01421                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01422                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01423                 }
01424                 else {
01425                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01426                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01427                 }
01428             }
01429         }
01430     }
01431 
01432 /* *+
01433 printf("error8: %s\n", cpl_error_get_message());
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 /* *+
01442 printf("error9: %s\n", cpl_error_get_message());
01443 +* */
01444     for (j = 0; j < 3; j++) {
01445         if (cpl_table_has_column(coeff, clab[j])) {
01446             cpl_table_and_selected_invalid(coeff, clab[j]);
01447 
01448             if (cpl_table_not_selected(coeff))
01449                 dummy = cpl_table_extract_selected(coeff);
01450             else
01451                 break;
01452 
01453             npoints = cpl_table_get_nrow(dummy);
01454 
01455             if (npoints >= 6) {
01456 
01457                 if (npoints >= 12)
01458                     order = 2;
01459                 else
01460                     order = 1;
01461 
01462                 ci = cpl_vector_wrap(npoints,
01463                                      cpl_table_get_data_double(dummy, clab[j]));
01464                 xmask = cpl_vector_wrap(npoints,
01465                                      cpl_table_get_data_double(dummy, "xmask"));
01466                 ymask = cpl_vector_wrap(npoints,
01467                                      cpl_table_get_data_double(dummy, "ymask"));
01468                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01469 
01470                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01471 
01472                 cpl_bivector_unwrap_vectors(mask);
01473                 cpl_vector_unwrap(ci);
01474                 cpl_vector_unwrap(xmask);
01475                 cpl_vector_unwrap(ymask);
01476             }
01477             else {
01478                 cpl_size p[2] = {0, 0};
01479                 poly = cpl_polynomial_new(2);
01480                 cpl_polynomial_set_coeff(poly, p,
01481                                cpl_table_get_column_median(dummy, clab[j]));
01482             }
01483 
01484             cpl_table_delete(dummy);
01485 
01486             global = write_global_distortion(global, j + 7, poly);
01487 
01488             cpl_polynomial_delete(poly);
01489             cpl_table_select_all(coeff);
01490         }
01491     }
01492 
01493 /* *+
01494 printf("error10: %s\n", cpl_error_get_message());
01495 +* */
01496     cpl_table_delete(coeff);
01497 /* *+
01498 printf("error11: %s\n", cpl_error_get_message());
01499 +* */
01500 
01501     return global;
01502 
01503 }
01504 
01505 
01543 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01544                                    int ysize)
01545 {
01546     const char *func = "mos_build_slit_location";
01547 
01548     cpl_propertylist *sort_col;
01549     cpl_polynomial   *ids0;
01550     cpl_polynomial   *crv[3];
01551     cpl_polynomial   *loc_crv;
01552     cpl_vector       *point;
01553     cpl_table        *slits;
01554     cpl_size         nslits;
01555     int              *slit_id;
01556     double           *dpoint;
01557     double           *xtop;
01558     double           *ytop;
01559     double           *xbottom;
01560     double           *ybottom;
01561     double           *mxtop;
01562     double           *mytop;
01563     double           *mxbottom;
01564     double           *mybottom;
01565     cpl_size          i;
01566     cpl_size          j;
01567 
01568 
01569     if (global == NULL || maskslits == NULL) {
01570         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01571         return NULL;
01572     }
01573 
01574     nslits   = cpl_table_get_nrow(maskslits);
01575     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01576     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01577     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01578     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01579     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01580 
01581     slits = cpl_table_duplicate(maskslits);
01582 
01583     xtop    = cpl_table_get_data_double(slits, "xtop");
01584     ytop    = cpl_table_get_data_double(slits, "ytop");
01585     xbottom = cpl_table_get_data_double(slits, "xbottom");
01586     ybottom = cpl_table_get_data_double(slits, "ybottom");
01587 
01588     ids0 = read_global_distortion(global, 0);
01589     crv[0] = read_global_distortion(global, 7);
01590     crv[1] = read_global_distortion(global, 8);
01591     crv[2] = read_global_distortion(global, 9);
01592 
01593     loc_crv = cpl_polynomial_new(1);
01594 
01595     point = cpl_vector_new(2);
01596     dpoint = cpl_vector_get_data(point);
01597 
01598     for (i = 0; i < nslits; i++) {
01599         dpoint[0] = mxtop[i];
01600         dpoint[1] = mytop[i];
01601 
01602         xtop[i] = cpl_polynomial_eval(ids0, point);
01603 
01604         for (j = 0; j < 3; j++)
01605             if (crv[j])
01606                 cpl_polynomial_set_coeff(loc_crv, &j, 
01607                                          cpl_polynomial_eval(crv[j], point));
01608 
01609         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01610 
01611         dpoint[0] = mxbottom[i];
01612         dpoint[1] = mybottom[i];
01613         xbottom[i] = cpl_polynomial_eval(ids0, point);
01614 
01615         for (j = 0; j < 3; j++)
01616             if (crv[j])
01617                 cpl_polynomial_set_coeff(loc_crv, &j,
01618                                          cpl_polynomial_eval(crv[j], point));
01619 
01620         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01621     }
01622 
01623     cpl_vector_delete(point);
01624     cpl_polynomial_delete(ids0);
01625     cpl_polynomial_delete(loc_crv);
01626     for (j = 0; j < 3; j++)
01627         cpl_polynomial_delete(crv[j]);
01628 
01629     sort_col = cpl_propertylist_new();
01630     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01631     cpl_table_sort(slits, sort_col);
01632     cpl_table_sort(maskslits, sort_col);
01633     cpl_propertylist_delete(sort_col);
01634 
01635     /*
01636      * Eliminate slits which are _entirely_ outside the CCD
01637      */
01638 
01639     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01640     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01641     cpl_table_erase_selected(slits);
01642 
01643     nslits = cpl_table_get_nrow(slits);
01644 
01645     if (nslits == 0) {
01646         cpl_msg_warning(func, "No slits found on the CCD");
01647         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01648         cpl_table_delete(slits);
01649         return NULL;
01650     }
01651 
01652     if (nslits > 1)
01653         cpl_msg_info(func, "Slit location: %d slits are entirely or partially "
01654                      "contained in CCD", nslits);
01655     else
01656         cpl_msg_info(func, "Slit location: %d slit is entirely or partially "
01657                      "contained in CCD", nslits);
01658 
01659     return slits;
01660 
01661 }
01662 
01663 
01690 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01691                                 cpl_table *slits)
01692 {
01693     const char *func = "mos_build_curv_coeff";
01694 
01695     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01696                                                  /* Max order is 5 */
01697 
01698     cpl_polynomial *crv[3];
01699     cpl_vector     *point;
01700     cpl_table      *polytraces;
01701     double         *dpoint;
01702     double         *xtop;
01703     double         *ytop;
01704     double         *xbottom;
01705     double         *ybottom;
01706     int            *slit_id;
01707     int            *valid_id;
01708     int             nslits, nvalid;
01709     int             found;
01710     int             i, j, k;
01711 
01712 
01713     if (global == NULL || slits == NULL || maskslits == NULL) {
01714         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01715         return NULL;
01716     }
01717 
01718     nslits  = cpl_table_get_nrow(maskslits);
01719     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01720     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01721     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01722     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01723     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01724 
01725     polytraces = cpl_table_new(2*nslits);
01726     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01727     for (i = 0; i < 3; i++)
01728         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01729 
01730     crv[0] = read_global_distortion(global, 7);
01731     crv[1] = read_global_distortion(global, 8);
01732     crv[2] = read_global_distortion(global, 9);
01733 
01734     point = cpl_vector_new(2);
01735     dpoint = cpl_vector_get_data(point);
01736 
01737     for (i = 0; i < nslits; i++) {
01738         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01739 
01740             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01741 
01742             if (j) {
01743                 dpoint[0] = xbottom[i];
01744                 dpoint[1] = ybottom[i];                
01745             }
01746             else {
01747                 dpoint[0] = xtop[i];
01748                 dpoint[1] = ytop[i];                
01749             }
01750 
01751             for (k = 0; k < 3; k++)
01752                 if (crv[j])
01753                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01754                                          cpl_polynomial_eval(crv[k], point));
01755         }
01756     }
01757 
01758     cpl_vector_delete(point);
01759     for (j = 0; j < 3; j++)
01760         cpl_polynomial_delete(crv[j]);
01761 
01762     /*
01763      * Eliminate slits which are _entirely_ outside the CCD
01764      */
01765  
01766     nvalid  = cpl_table_get_nrow(slits);
01767     valid_id = cpl_table_get_data_int(slits, "slit_id");
01768     cpl_table_unselect_all(polytraces);
01769     for (i = 0; i < nslits; i++) {
01770         found = 0;
01771         for (j = 0; j < nvalid; j++) {
01772             if (slit_id[i] == valid_id[j]) {
01773                 found = 1;
01774                 break;
01775             }
01776         }
01777         if (!found) {
01778             cpl_table_select_row(polytraces, 2*i);
01779             cpl_table_select_row(polytraces, 2*i + 1);
01780         }
01781     }
01782     cpl_table_erase_selected(polytraces);
01783  
01784     nslits = cpl_table_get_nrow(polytraces);
01785 
01786     if (nslits == 0) {
01787         cpl_msg_warning(func, "No slits found on the CCD");
01788         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01789         cpl_table_delete(polytraces);
01790         return NULL;
01791     }
01792 
01793     if (nslits > 2) 
01794         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01795                      "partially contained in CCD", nslits / 2);
01796     else
01797         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01798                      "partially contained in CCD", nslits / 2);
01799 
01800     return polytraces;
01801 }
01802 
01803 
01845 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01846 {
01847     const char *func = "mos_build_disp_coeff";
01848 
01849     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01850 
01851     cpl_polynomial *ids[6];
01852     cpl_vector     *point;
01853     cpl_table      *idscoeff;
01854     double         *dpoint;
01855     double         *xtop;
01856     double         *ytop;
01857     double         *xbottom;
01858     double         *ybottom;
01859     int            *position;
01860     int            *length;
01861     int             nslits;
01862     int             nrows;
01863     int             order;
01864     int             ylow, yhig;
01865     int             i, j, k;
01866 
01867 
01868     if (global == NULL || slits == NULL) {
01869         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01870         return NULL;
01871     }
01872     
01873     nslits   = cpl_table_get_nrow(slits);
01874     position = cpl_table_get_data_int(slits, "position");
01875     length   = cpl_table_get_data_int(slits, "length");
01876     xtop     = cpl_table_get_data_double(slits, "xtop");
01877     ytop     = cpl_table_get_data_double(slits, "ytop");
01878     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01879     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01880 
01881     for (i = 0; i < 6; i++)
01882         ids[i] = read_global_distortion(global, i);
01883 
01884     for (i = 0; i < 6; i++)
01885         if (ids[i] == NULL)
01886             break;
01887 
01888     order = i - 1;
01889 
01890     if (order < 1) {
01891         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01892         return NULL;
01893     }
01894 
01895     nrows = 0;
01896     for (i = 0; i < nslits; i++)
01897         nrows += length[i]; 
01898 
01899     idscoeff = cpl_table_new(nrows);
01900 
01901     for (j = 0; j <= order; j++)
01902         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01903 
01904     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01905     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01906     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01907     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01908 
01909     point = cpl_vector_new(2);
01910     dpoint = cpl_vector_get_data(point);
01911 
01912     for (i = 0; i < nslits; i++) {
01913 
01914         if (length[i] == 0)
01915             continue;
01916 
01917         ylow = position[i];
01918         yhig = ylow + length[i];
01919 
01920         for (j = 0; j <= order; j++) {
01921             if (j) {
01922                 for (k = 0; k < length[i]; k++) {
01923                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01924                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01925                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01926                                          cpl_polynomial_eval(ids[j], point));
01927                 }
01928             }
01929             else {
01930                 for (k = 0; k < length[i]; k++) {
01931                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01932                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01933                 }
01934             }
01935         }
01936     }
01937 
01938     cpl_vector_delete(point);
01939     for (j = 0; j < 6; j++)
01940         cpl_polynomial_delete(ids[j]);
01941 
01942     return idscoeff;
01943 
01944 }
01945 
01946 
01969 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01970                             cpl_table *polytraces, double reference, 
01971                             double blue, double red, double dispersion)
01972 {
01973     const char     *func = "mos_subtract_sky";
01974 
01975     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01976                                                  /* Max order is 5 */
01977 
01978     cpl_image      *sky;
01979     cpl_bivector   *list;
01980     cpl_vector     *listx;
01981     cpl_vector     *listy;
01982     cpl_polynomial *polytop;
01983     cpl_polynomial *polybot;
01984     cpl_polynomial *trend;
01985 
01986     int            *slit_id;
01987     double         *dlistx;
01988     double         *dlisty;
01989     float          *sdata;
01990     float          *kdata;
01991     double          top, bot;
01992     int             itop, ibot;
01993     double          coeff;
01994     double          ytop, ybot;
01995     double          m, q, err;
01996     int             npix;
01997 
01998     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
01999     int             nx, ny;
02000     int             nslits;
02001     int            *length;
02002     int             missing_top, missing_bot;
02003     int             order;
02004     int             null;
02005     int             window = 50;  /* Longer slits have polynomial sky model */
02006     int             count;
02007     int             i, j;
02008     cpl_size        k;
02009 
02010 
02011     if (science == NULL || slits == NULL || polytraces == NULL) {
02012         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02013         return NULL;
02014     }
02015  
02016     if (dispersion <= 0.0) {
02017         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02018         return NULL;
02019     }
02020 
02021     if (red - blue < dispersion) {
02022         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02023         return NULL;
02024     }
02025 
02026     nx = cpl_image_get_size_x(science);
02027     ny = cpl_image_get_size_y(science);
02028 
02029     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02030 
02031     sdata = cpl_image_get_data(science);
02032     kdata = cpl_image_get_data(sky);
02033 
02034     nslits   = cpl_table_get_nrow(slits);
02035     order    = cpl_table_get_ncol(polytraces) - 2;
02036     length   = cpl_table_get_data_int(slits, "length");
02037     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02038 
02039     /*
02040      * The spatial resampling is performed for a certain number of
02041      * pixels above and below the position of the reference wavelength:
02042      */
02043     
02044     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02045     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02046 
02047     for (i = 0; i < nslits; i++) {
02048 
02049         if (length[i] == 0)
02050             continue;
02051 
02052         
02053         /*
02054          * Recover from the table of spectral curvature coefficients
02055          * the curvature polynomials.
02056          */
02057 
02058         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02059 
02060         start_pixel = refpixel - pixel_below;
02061         if (start_pixel < 0)
02062             start_pixel = 0;
02063 
02064         end_pixel = refpixel + pixel_above;
02065         if (end_pixel > nx)
02066             end_pixel = nx;
02067 
02068         missing_top = 0;
02069         polytop = cpl_polynomial_new(1);
02070         for (k = 0; k <= order; k++) {
02071             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02072             if (null) {
02073                 cpl_polynomial_delete(polytop);
02074                 missing_top = 1;
02075                 break;
02076             }
02077             cpl_polynomial_set_coeff(polytop, &k, coeff);
02078         }
02079 
02080         missing_bot = 0;
02081         polybot = cpl_polynomial_new(1);
02082         for (k = 0; k <= order; k++) {
02083             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02084             if (null) {
02085                 cpl_polynomial_delete(polybot);
02086                 missing_bot = 1;
02087                 break;
02088             }
02089             cpl_polynomial_set_coeff(polybot, &k, coeff);
02090         }
02091 
02092         if (missing_top && missing_bot) {
02093             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02094                           slit_id[i]);
02095             continue;
02096         }
02097 
02098         /*
02099          * In case just one of the two edges was not traced, the other
02100          * edge curvature model is duplicated and shifted to the other
02101          * end of the slit: better than nothing!
02102          */
02103 
02104         if (missing_top) {
02105             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02106                           "the spectral curvature of the lower edge "
02107                           "is used instead.", slit_id[i]);
02108             polytop = cpl_polynomial_duplicate(polybot);
02109             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02110             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02111             k = 0;
02112             coeff = cpl_polynomial_get_coeff(polybot, &k);
02113             coeff += ytop - ybot;
02114             cpl_polynomial_set_coeff(polytop, &k, coeff);
02115         }
02116 
02117         if (missing_bot) {
02118             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02119                           "the spectral curvature of the upper edge "
02120                           "is used instead.", slit_id[i]);
02121             polybot = cpl_polynomial_duplicate(polytop);
02122             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02123             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02124             k = 0;
02125             coeff = cpl_polynomial_get_coeff(polytop, &k);
02126             coeff -= ytop - ybot;
02127             cpl_polynomial_set_coeff(polybot, &k, coeff);
02128         }
02129 
02130 
02131         /*
02132          * Now read pixel values along spatial direction, and fit them.
02133          */
02134 
02135         for (j = start_pixel; j < end_pixel; j++) {
02136             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02137             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02138             itop = floor(top + 0.5) + 1;
02139             ibot = floor(bot + 0.5);
02140             if (itop > ny)
02141                 itop = ny;
02142             if (ibot < 0)
02143                 ibot = 0;
02144             npix = itop - ibot;
02145             if (npix < 5)
02146                 break;
02147 
02148             list = cpl_bivector_new(npix);
02149             listx = cpl_bivector_get_x(list);
02150             listy = cpl_bivector_get_y(list);
02151             dlistx = cpl_vector_get_data(listx);
02152             dlisty = cpl_vector_get_data(listy);
02153 
02154             for (k = 0; k < npix; k++) {
02155                 dlistx[k] = k;
02156                 dlisty[k] = sdata[j + (ibot + k)*nx];
02157             }
02158 
02159             if (robustLinearFit(list, &q, &m, &err)) {
02160                 cpl_bivector_delete(list);
02161                 continue;
02162             }
02163 
02164             cpl_bivector_delete(list);
02165 
02166             for (k = 0; k < npix; k++) {
02167                 kdata[j + (ibot + k)*nx] = m*k + q;
02168             }
02169 
02170             if (npix > window) {
02171 
02172                 /*
02173                  * Polynomial iteration
02174                  */
02175 
02176                 err = 3*sqrt(err);
02177 
02178                 count = 0;
02179                 for (k = 0; k < npix; k++)
02180                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02181                         count++;
02182 
02183                 if (count < 10)
02184                     continue;
02185 
02186                 list = cpl_bivector_new(count);
02187                 listx = cpl_bivector_get_x(list);
02188                 listy = cpl_bivector_get_y(list);
02189                 dlistx = cpl_vector_get_data(listx);
02190                 dlisty = cpl_vector_get_data(listy);
02191 
02192                 count = 0;
02193                 for (k = 0; k < npix; k++) {
02194                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02195                         dlistx[count] = k;
02196                         dlisty[count] = sdata[j + (ibot + k)*nx];
02197                         count++;
02198                     }
02199                 }
02200 
02201                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02202  
02203                 cpl_bivector_delete(list);
02204 
02205                 err = 3*sqrt(err);
02206 
02207                 count = 0;
02208                 for (k = 0; k < npix; k++)
02209                     if (fabs(sdata[j + (ibot + k)*nx] 
02210                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02211                         count++;
02212 
02213                 if (count < 10) {
02214                     cpl_polynomial_delete(trend);
02215                     continue;
02216                 }
02217 
02218                 list = cpl_bivector_new(count);
02219                 listx = cpl_bivector_get_x(list);
02220                 listy = cpl_bivector_get_y(list);
02221                 dlistx = cpl_vector_get_data(listx);
02222                 dlisty = cpl_vector_get_data(listy);
02223 
02224                 count = 0;
02225                 for (k = 0; k < npix; k++) {
02226                     if (fabs(sdata[j + (ibot + k)*nx] 
02227                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02228                         dlistx[count] = k;
02229                         dlisty[count] = sdata[j + (ibot + k)*nx];
02230                         count++;
02231                     }
02232                 }
02233 
02234                 cpl_polynomial_delete(trend);
02235 
02236                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02237 
02238                 cpl_bivector_delete(list);
02239  
02240                 for (k = 0; k < npix; k++) {
02241                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02242                                                k, NULL);
02243                 }
02244 
02245                 cpl_polynomial_delete(trend);
02246             }
02247         }
02248         cpl_polynomial_delete(polytop);
02249         cpl_polynomial_delete(polybot);
02250     }
02251 
02252     cpl_image_subtract(science, sky);
02253 
02254     return sky;
02255 }
02256 
02257 
02290 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02291                               cpl_table *slits, cpl_table *polytraces, 
02292                               double reference, double blue, double red, 
02293                               double dispersion, int sradius, int polyorder)
02294 {
02295     const char     *func = "mos_normalise_flat";
02296 
02297     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02298                                                  /* Max order is 5 */
02299 
02300     cpl_image      *rectified;
02301     cpl_image      *smo_flat;
02302     cpl_image      *exslit;
02303     cpl_vector     *positions;
02304     cpl_vector     *flux;
02305     cpl_vector     *smo_flux;
02306     cpl_polynomial *trend;
02307     cpl_polynomial *polytop;
02308     cpl_polynomial *polybot;
02309 
02310     int            *slit_id;
02311     float          *p;
02312     float          *data;
02313     double         *fdata;
02314     double         *pdata;
02315     float          *sdata;
02316     float          *xdata;
02317     float          *wdata;
02318     double          vtop, vbot, value;
02319     double          top, bot;
02320     double          coeff;
02321     double          ytop, ybot;
02322     double          ypos;
02323     double          fvalue;
02324     int             ivalue;
02325     int             yint, yprev;
02326     int             npseudo;
02327 
02328     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02329     int             nx, ny, nsubx, nsuby;
02330     int             xlow, ylow, xhig, yhig;
02331     int             nslits;
02332     int            *position;
02333     int            *length;
02334     int             missing_top, missing_bot;
02335     int             order;
02336     int             npoints;
02337     int             uradius;
02338     int             null;
02339     int             i, j;
02340     cpl_size        k;
02341 
02342 /*    int             exclude = 5;     Number of excluded pixels at edges */
02343 
02344     /* For debug puposes only: cpl_image      *smo_rectified; */
02345 
02346 
02347     if (flat == NULL || slits == NULL || polytraces == NULL) {
02348         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02349         return NULL;
02350     }
02351  
02352     if (dispersion <= 0.0) {
02353         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02354         return NULL;
02355     }
02356 
02357     if (red - blue < dispersion) {
02358         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02359         return NULL;
02360     }
02361 
02362     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02363                                         blue, red, dispersion, 0, NULL);
02364 
02365     nx = cpl_image_get_size_x(rectified);
02366     ny = cpl_image_get_size_y(rectified);
02367 
02368     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02369                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02370     wdata = cpl_image_get_data(smo_flat);
02371 
02372     nslits   = cpl_table_get_nrow(slits);
02373     order    = cpl_table_get_ncol(polytraces) - 2;
02374     position = cpl_table_get_data_int(slits, "position");
02375     length   = cpl_table_get_data_int(slits, "length");
02376     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02377 
02378     /*
02379      * The spatial resampling is performed for a certain number of
02380      * pixels above and below the position of the reference wavelength:
02381      */
02382     
02383     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02384     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02385 
02386     xlow = 1;
02387     xhig = nx;
02388     for (i = 0; i < nslits; i++) {
02389 
02390         if (length[i] == 0)
02391             continue;
02392 
02393         /*
02394          * We DON'T write:
02395          *
02396          * ylow = position[i];
02397          * yhig = ylow + length[i];
02398          *
02399          * because the cpl_image pixels are counted from 1, and because in 
02400          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02401          */
02402 
02403         ylow = position[i] + 1;
02404         yhig = ylow + length[i] - 1;
02405 
02406         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02407 
02408         if (polyorder < 0) {
02409 
02410             cpl_image_turn(exslit, -1);   /* For faster memory access */
02411     
02412             nsubx = cpl_image_get_size_x(exslit);
02413             nsuby = cpl_image_get_size_y(exslit);
02414             data = cpl_image_get_data(exslit);
02415             flux = cpl_vector_new(nsubx);
02416 
02417             uradius = nsubx / 2;
02418             if (uradius > sradius)
02419                 uradius = sradius;
02420 
02421             for (j = 0; j < nsuby; j++) {
02422                 fdata = cpl_vector_get_data(flux);
02423                 p = data;
02424                 for (k = 0; k < nsubx; k++)
02425                     *fdata++ = *p++;
02426                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02427                 fdata = cpl_vector_get_data(smo_flux);
02428                 p = data;
02429                 for (k = 0; k < nsubx; k++)
02430                     *p++ = *fdata++;
02431                 cpl_vector_delete(smo_flux);
02432                 data += nsubx;
02433             }
02434 
02435             cpl_vector_delete(flux);
02436 
02437 
02438             /*
02439              * First fit fluxes along the spatial direction with a low-degree
02440              * polynomial (excluding the first and the last pixels, close to
02441              * the edges)
02442              */
02443 /*
02444             if (nsubx-2*exclude > 10) {
02445                 flux = cpl_vector_new(nsubx-2*exclude);
02446                 fdata = cpl_vector_get_data(flux);
02447                 positions = cpl_vector_new(nsubx-2*exclude);
02448                 for (j = 0; j < nsubx-2*exclude; j++)
02449                     cpl_vector_set(positions, j, j+exclude);
02450         
02451                 for (k = 0; k < nsuby; k++) {
02452                     for (j = 0; j < nsubx-2*exclude; j++)
02453                         fdata[j] = data[j+exclude];
02454                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02455                                                          1, NULL);
02456                     for (j = 0; j < nsubx; j++)
02457                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02458                     cpl_polynomial_delete(trend);
02459                     data += nsubx;
02460                 }
02461 
02462                 cpl_vector_delete(flux);
02463                 cpl_vector_delete(positions);
02464             }
02465 */
02466 
02467             /*
02468              * Now smooth along the dispersion direction 
02469              */
02470 
02471             cpl_image_turn(exslit, 1);   /* For faster memory access */
02472             nsubx = cpl_image_get_size_x(exslit);
02473             nsuby = cpl_image_get_size_y(exslit);
02474             data = cpl_image_get_data(exslit);
02475 
02476             for (j = 0; j < nsuby; j++) {
02477                 flux = cpl_vector_new(nsubx);
02478                 fdata = cpl_vector_get_data(flux);
02479                 p = data;
02480                 for (k = 0; k < nsubx; k++)
02481                     *fdata++ = *p++;
02482                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02483                 cpl_vector_delete(flux);
02484                 fdata = cpl_vector_get_data(smo_flux);
02485                 p = data;
02486                 for (k = 0; k < nsubx; k++)
02487                     *p++ = *fdata++;
02488                 cpl_vector_delete(smo_flux);
02489                 data += nsubx;
02490             }
02491         }
02492         else {
02493 
02494             /*
02495              * Fit with a polynomial the flat field trend row by row.
02496              */
02497 
02498             nsubx = cpl_image_get_size_x(exslit);
02499             nsuby = cpl_image_get_size_y(exslit);
02500             data = cpl_image_get_data(exslit);
02501 
02502             for (j = 0; j < nsuby; j++) {
02503 
02504                 /*
02505                  * First get the size of the vectors to allocate:
02506                  */
02507 
02508                 npoints = 0;
02509                 p = data + j*nsubx;
02510                 for (k = 0; k < nsubx; k++)
02511                     if (p[k] > 1.0)
02512                         npoints++;
02513 
02514                 if (npoints > polyorder + 1) {
02515 
02516                     /*
02517                      * Fill the vectors for the fitting
02518                      */
02519 
02520                     flux = cpl_vector_new(npoints);
02521                     fdata = cpl_vector_get_data(flux);
02522                     positions = cpl_vector_new(npoints);
02523                     pdata = cpl_vector_get_data(positions);
02524 
02525                     npoints = 0;
02526                     p = data + j*nsubx;
02527                     for (k = 0; k < nsubx; k++) {
02528                         if (p[k] > 1.0) {
02529                             fdata[npoints] = p[k];
02530                             pdata[npoints] = k;
02531                             npoints++;
02532                         }
02533                     }
02534     
02535                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02536                                                          polyorder, NULL);
02537 
02538                     cpl_vector_delete(flux);
02539                     cpl_vector_delete(positions);
02540 
02541                     if (trend) {
02542                         p = data + j*nsubx;
02543                         for (k = 0; k < nsubx; k++)
02544                             if (p[k] > 1.0)
02545                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02546                         cpl_polynomial_delete(trend);
02547                     }
02548                     else {
02549                         cpl_msg_warning(func, "Invalid flat field flux fit "
02550                                         "(ignored)");
02551                     }
02552                 }
02553             }
02554         }
02555 
02556         
02557         /*
02558          * Recover from the table of spectral curvature coefficients
02559          * the curvature polynomials.
02560          */
02561 
02562         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02563 
02564         start_pixel = refpixel - pixel_below;
02565         if (start_pixel < 0)
02566             start_pixel = 0;
02567 
02568         end_pixel = refpixel + pixel_above;
02569         if (end_pixel > nx)
02570             end_pixel = nx;
02571 
02572         missing_top = 0;
02573         polytop = cpl_polynomial_new(1);
02574         for (k = 0; k <= order; k++) {
02575             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02576             if (null) {
02577                 cpl_polynomial_delete(polytop);
02578                 missing_top = 1;
02579                 break;
02580             }
02581             cpl_polynomial_set_coeff(polytop, &k, coeff);
02582         }
02583 
02584         missing_bot = 0;
02585         polybot = cpl_polynomial_new(1);
02586         for (k = 0; k <= order; k++) {
02587             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02588             if (null) {
02589                 cpl_polynomial_delete(polybot);
02590                 missing_bot = 1;
02591                 break;
02592             }
02593             cpl_polynomial_set_coeff(polybot, &k, coeff);
02594         }
02595 
02596         if (missing_top && missing_bot) {
02597             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02598                           slit_id[i]);
02599             continue;
02600         }
02601 
02602         /*
02603          * In case just one of the two edges was not traced, the other
02604          * edge curvature model is duplicated and shifted to the other
02605          * end of the slit: better than nothing!
02606          */
02607 
02608         if (missing_top) {
02609             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02610                           "the spectral curvature of the lower edge "
02611                           "is used instead.", slit_id[i]);
02612             polytop = cpl_polynomial_duplicate(polybot);
02613             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02614             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02615             k = 0;
02616             coeff = cpl_polynomial_get_coeff(polybot, &k);
02617             coeff += ytop - ybot;
02618             cpl_polynomial_set_coeff(polytop, &k, coeff);
02619         }
02620 
02621         if (missing_bot) {
02622             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02623                           "the spectral curvature of the upper edge "
02624                           "is used instead.", slit_id[i]);
02625             polybot = cpl_polynomial_duplicate(polytop);
02626             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02627             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02628             k = 0;
02629             coeff = cpl_polynomial_get_coeff(polytop, &k);
02630             coeff -= ytop - ybot;
02631             cpl_polynomial_set_coeff(polybot, &k, coeff);
02632         }
02633 
02634 
02635         /*
02636          * Now map smoothed image to CCD.
02637          * Note that the npseudo value related to this slit is equal
02638          * to the number of spatial pseudo-pixels decreased by 1
02639          * (compare with function mos_spatial_calibration()).
02640          */
02641 
02642         nx = cpl_image_get_size_x(flat);
02643         ny = cpl_image_get_size_y(flat);
02644 
02645         sdata = cpl_image_get_data(spatial);
02646         xdata = cpl_image_get_data(exslit);
02647         npseudo = cpl_image_get_size_y(exslit) - 1;
02648 
02649         /*
02650          * Write interpolated smoothed values to CCD image
02651          */
02652 
02653         for (j = start_pixel; j < end_pixel; j++) {
02654             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02655             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02656             for (k = 0; k <= npseudo; k++) {
02657                 ypos = top - k*(top-bot)/npseudo;
02658                 yint = ypos;
02659 
02660                 /*
02661                  * The line:
02662                  *     value = sdata[j + nx*yint];
02663                  * should be equivalent to:
02664                  *     value = npseudo*(top-yint)/(top-bot);
02665                  */
02666 
02667                 if (yint < 0 || yint >= ny-1) {
02668                     yprev = yint;
02669                     continue;
02670                 }
02671 
02672                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02673                 ivalue = value;               /* Nearest spatial pixels:   */
02674                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02675                 if (ivalue < npseudo && ivalue >= 0) {
02676                     vtop = xdata[j + nx*(npseudo-ivalue)];
02677                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02678                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02679 
02680                     if (k) {
02681 
02682                         /*
02683                          * This is added to recover lost pixels on
02684                          * the CCD image (pixels are lost because
02685                          * the CCD pixels are less than npseudo+1).
02686                          */
02687 
02688                         if (yprev - yint > 1) {
02689                             value = sdata[j + nx*(yint+1)];
02690                             ivalue = value;
02691                             fvalue = value - ivalue;
02692                             if (ivalue < npseudo && ivalue >= 0) {
02693                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02694                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02695                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02696                                                        + vbot*fvalue;
02697                             }
02698                         }
02699                     }
02700                 }
02701                 yprev = yint;
02702             }
02703         }
02704         cpl_polynomial_delete(polytop);
02705         cpl_polynomial_delete(polybot);
02706         cpl_image_delete(exslit);
02707     }
02708 
02709     cpl_image_delete(rectified);
02710 
02711     cpl_image_divide(flat, smo_flat);
02712 
02713     return smo_flat;
02714 }
02715 
02716 
02741 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02742                                   int polyorder)
02743 {
02744     const char     *func = "mos_normalise_longflat";
02745 
02746     cpl_image      *smo_flat;
02747     cpl_image      *profile;
02748     cpl_vector     *flux;
02749     cpl_vector     *smo_flux;
02750     cpl_vector     *positions;
02751     cpl_polynomial *trend;
02752 
02753     float          *level;
02754     float          *p;
02755     float          *data;
02756     double         *fdata;
02757     double         *pdata;
02758 
02759     int             nx, ny;
02760     int             npoints;
02761     int             i, j;
02762 
02763 
02764     if (flat == NULL) {
02765         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02766         return NULL;
02767     }
02768  
02769     if (sradius < 1 || dradius < 1) {
02770         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02771         return NULL;
02772     }
02773 
02774     smo_flat = cpl_image_duplicate(flat);
02775 
02776     if (polyorder < 0) {
02777 
02778         /*
02779          * First smooth along the spatial direction
02780          */
02781 
02782         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02783 
02784         nx = cpl_image_get_size_x(smo_flat);
02785         ny = cpl_image_get_size_y(smo_flat);
02786         data = cpl_image_get_data(smo_flat);
02787     
02788         for (i = 0; i < ny; i++) {
02789             flux = cpl_vector_new(nx);
02790             fdata = cpl_vector_get_data(flux);
02791             p = data;
02792             for (j = 0; j < nx; j++)
02793                 *fdata++ = *p++;
02794             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02795             cpl_vector_delete(flux);
02796             fdata = cpl_vector_get_data(smo_flux);
02797             p = data;
02798             for (j = 0; j < nx; j++)
02799                 *p++ = *fdata++;
02800             cpl_vector_delete(smo_flux);
02801             data += nx;
02802         }
02803 
02804         /*
02805          * Second smooth along the dispersion direction
02806          */
02807 
02808         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02809 
02810         nx = cpl_image_get_size_x(smo_flat);
02811         ny = cpl_image_get_size_y(smo_flat);
02812         data = cpl_image_get_data(smo_flat);
02813 
02814         for (i = 0; i < ny; i++) {
02815             flux = cpl_vector_new(nx);
02816             fdata = cpl_vector_get_data(flux);
02817             p = data;
02818             for (j = 0; j < nx; j++)
02819                 *fdata++ = *p++;
02820             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02821             cpl_vector_delete(flux);
02822             fdata = cpl_vector_get_data(smo_flux);
02823             p = data;
02824             for (j = 0; j < nx; j++)
02825                 *p++ = *fdata++;
02826             cpl_vector_delete(smo_flux);
02827             data += nx;
02828         }
02829     }
02830     else {
02831 
02832         /*
02833          * Fit with a polynomial the flat field trend column by column.
02834          */
02835 
02836         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02837 
02838         nx = cpl_image_get_size_x(smo_flat);
02839         ny = cpl_image_get_size_y(smo_flat);
02840         data = cpl_image_get_data(smo_flat);
02841 
02842         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02843         level = cpl_image_get_data(profile);
02844 
02845         for (i = 0; i < ny; i++) {
02846 
02847             /*
02848              * First get the size of the vectors to allocate:
02849              * eliminate from fit any value more than 20% away
02850              * from median level in current column.
02851              */
02852 
02853             npoints = 0;
02854             p = data + i*nx;
02855             for (j = 0; j < nx; j++)
02856                 if (fabs(p[j]/level[i] - 1) < 0.20)
02857                     npoints++;
02858 
02859             if (npoints > polyorder + 1) {
02860 
02861                 /*
02862                  * Fill the vectors for the fitting
02863                  */
02864 
02865                 flux = cpl_vector_new(npoints);
02866                 fdata = cpl_vector_get_data(flux);
02867                 positions = cpl_vector_new(npoints);
02868                 pdata = cpl_vector_get_data(positions);
02869 
02870                 npoints = 0;
02871                 p = data + i*nx;
02872                 for (j = 0; j < nx; j++) {
02873                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02874                         fdata[npoints] = p[j];
02875                         pdata[npoints] = j;
02876                         npoints++;
02877                     }
02878                 }
02879     
02880                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02881                                                      polyorder, NULL);
02882 
02883                 cpl_vector_delete(flux);
02884                 cpl_vector_delete(positions);
02885 
02886                 if (trend) {
02887                     p = data + i*nx;
02888                     for (j = 0; j < nx; j++)
02889                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02890                     cpl_polynomial_delete(trend);
02891                 }
02892                 else {
02893                     cpl_msg_warning(func, 
02894                                     "Invalid flat field flux fit (ignored)");
02895                 }
02896             }
02897         }
02898 
02899         cpl_image_delete(profile);
02900         cpl_image_turn(smo_flat, 1);
02901 
02902     }
02903 
02904     cpl_image_divide(flat, smo_flat);
02905 
02906     return smo_flat;
02907 }
02908 
02909 
02932 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
02933                                               cpl_table *slits, 
02934                                               int order, int global)
02935 {
02936     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02937                                                  /* Max order is 5 */
02938     int nrow = cpl_table_get_nrow(slits);
02939     int i, j;
02940 
02941     
02942     if (order < 0)
02943         return CPL_ERROR_NONE;
02944 
02945     cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
02946     cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
02947 
02948     for (i = 0; i < nrow; i++) {
02949         int        position = cpl_table_get_int   (slits, "position", i, NULL);
02950         int        length   = cpl_table_get_int   (slits, "length",   i, NULL);
02951         double     xtop     = cpl_table_get_double(slits, "xtop",     i, NULL);
02952         double     xbot     = cpl_table_get_double(slits, "xbottom",  i, NULL);
02953         double     ytop     = cpl_table_get_double(slits, "ytop",     i, NULL);
02954         double     ybot     = cpl_table_get_double(slits, "ybottom",  i, NULL);
02955         double     dx       = xtop - xbot;
02956         double     dy       = ytop - ybot;
02957         cpl_table *table    = cpl_table_extract(idscoeff, position, length);
02958 
02959         if (mos_interpolate_wavecalib(table, NULL, 2, order))
02960             continue;
02961 
02962         cpl_table_erase_window(idscoeff, position, length);
02963         cpl_table_insert(idscoeff, table, position);
02964 
02965         cpl_table_delete(table);
02966 
02967         for (j = 0; j < length; j++) {
02968             cpl_table_set_double(idscoeff, "x", j + position,
02969                                  xbot + j*(dx/length));
02970             cpl_table_set_double(idscoeff, "y", j + position,
02971                                  ybot + j*(dy/length));
02972         }
02973     }
02974 
02975     if (global) {
02976 
02977         /*
02978          * Now fit a global solution
02979          */
02980 
02981         nrow = cpl_table_get_nrow(idscoeff);
02982 
02983         for (i = 0; i < 6; i++) {
02984             cpl_table      *dummy;
02985             cpl_vector     *x;
02986             cpl_vector     *y;
02987             cpl_bivector   *z;
02988             cpl_vector     *c;
02989             cpl_polynomial *p;
02990             cpl_vector     *point;
02991             double         *dpoint;
02992             int             npoints;
02993 
02994             if (!cpl_table_has_column(idscoeff, clab[i]))
02995                 break;
02996 
02997             npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
02998             if (npoints < 18)
02999                 break;
03000 
03001             dummy = cpl_table_new(nrow);
03002             cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
03003             cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
03004             cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
03005             cpl_table_erase_invalid(dummy);
03006 
03007             x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
03008             y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
03009             z = cpl_bivector_wrap_vectors(x, y);
03010             c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 
03011                                                                    clab[i]));
03012             p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
03013             cpl_bivector_unwrap_vectors(z);
03014             cpl_vector_unwrap(x);
03015             cpl_vector_unwrap(y);
03016             cpl_vector_unwrap(c);
03017             cpl_table_delete(dummy);
03018 
03019             point  = cpl_vector_new(2);
03020             dpoint = cpl_vector_get_data(point);
03021             for (j = 0; j < nrow; j++) {
03022                 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
03023                 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
03024                 cpl_table_set_double(idscoeff, clab[i], j,
03025                                      cpl_polynomial_eval(p, point));
03026             }
03027             cpl_vector_delete(point);
03028             cpl_polynomial_delete(p);
03029         }
03030     }
03031 
03032     return CPL_ERROR_NONE;
03033 }
03034 
03035 
03061 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
03062                                          cpl_image *wavemap, int mode,
03063                                          int degree)
03064 {
03065     const char *func = "mos_interpolate_wavecalib";
03066 
03067     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
03068                                                  /* Max order is 5 */
03069 
03070     cpl_vector     *wave;
03071     cpl_vector     *positions;
03072     cpl_polynomial *trend;
03073 
03074     float          *p;
03075     float          *data;
03076     double         *wdata;
03077     double         *pdata;
03078 
03079     double          c;
03080     double          mse, ksigma;
03081 
03082     int             order;
03083     int             nrows, first_row, last_row;
03084     int             nx, ny;
03085     int             npoints, rpoints;
03086     int             null;
03087     int             i, j, k;
03088 
03089     int             polyorder = 4;  /* Candidate input argument */
03090 
03091 
03092     if (idscoeff == NULL)
03093         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03094 
03095     if (mode < 0 || mode > 2)
03096         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03097 
03098     if (mode == 0 || degree < 0)
03099         return CPL_ERROR_NONE;
03100 
03101     if (wavemap) {
03102 
03103         /*
03104          * Fit with a polynomial the wavelength trend column by column.
03105          */
03106 
03107         cpl_image_turn(wavemap, -1);   /* For faster memory access */
03108 
03109         nx = cpl_image_get_size_x(wavemap);
03110         ny = cpl_image_get_size_y(wavemap);
03111         data = cpl_image_get_data(wavemap);
03112 
03113         for (i = 0; i < ny; i++) {
03114 
03115             /*
03116              * First get the size of the vectors to allocate:
03117              * eliminate from fit any value with "impossible" wavelength.
03118              */
03119 
03120             npoints = 0;
03121             p = data + i*nx;
03122             for (j = 0; j < nx; j++)
03123                 if (p[j] > 1.0)
03124                     npoints++;
03125 
03126             if (npoints > polyorder + 1) {
03127 
03128                 /*
03129                  * Fill the vectors for the fitting
03130                  */
03131 
03132                 wave = cpl_vector_new(npoints);
03133                 wdata = cpl_vector_get_data(wave);
03134                 positions = cpl_vector_new(npoints);
03135                 pdata = cpl_vector_get_data(positions);
03136 
03137                 npoints = 0;
03138                 p = data + i*nx;
03139                 for (j = 0; j < nx; j++) {
03140                     if (p[j] > 1.0) {
03141                         wdata[npoints] = p[j];
03142                         pdata[npoints] = j;
03143                         npoints++;
03144                     }
03145                 }
03146     
03147                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03148                                                      polyorder, &mse);
03149 
03150                 ksigma = 3*sqrt(mse);
03151 
03152                 cpl_vector_delete(wave);
03153                 cpl_vector_delete(positions);
03154 
03155                 if (trend) {
03156 
03157                     /*
03158                      * Apply 3-sigma rejection
03159                      */
03160 
03161                     rpoints = 0;
03162                     p = data + i*nx;
03163                     for (j = 0; j < nx; j++)
03164                         if (p[j] > 1.0)
03165                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03166                                                     - p[j]) < ksigma)
03167                                 rpoints++;
03168 
03169                     if (rpoints < npoints && rpoints > polyorder + 1) {
03170 
03171                         wave = cpl_vector_new(rpoints);
03172                         wdata = cpl_vector_get_data(wave);
03173                         positions = cpl_vector_new(rpoints);
03174                         pdata = cpl_vector_get_data(positions);
03175 
03176                         npoints = 0;
03177                         p = data + i*nx;
03178                         for (j = 0; j < nx; j++) {
03179                             if (p[j] > 1.0) {
03180                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03181                                                                 j, NULL) - p[j])
03182                                                                 < ksigma) {
03183                                     wdata[npoints] = p[j];
03184                                     pdata[npoints] = j;
03185                                     npoints++;
03186                                 }
03187                             }
03188                         }
03189         
03190                         cpl_polynomial_delete(trend);
03191                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03192                                                              polyorder, NULL);
03193 
03194                         cpl_vector_delete(wave);
03195                         cpl_vector_delete(positions);
03196                     }
03197                 }
03198 
03199                 if (trend) {
03200                     p = data + i*nx;
03201                     if (mode == 1) {
03202                         for (j = 0; j < nx; j++)
03203                             if (p[j] < 1.0)
03204                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03205                     }
03206                     else if (mode == 2) {
03207                         for (j = 0; j < nx; j++)
03208                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03209                     }
03210                     cpl_polynomial_delete(trend);
03211                 }
03212                 else {
03213                     cpl_msg_warning(func, 
03214                                     "Invalid wavelength field fit (ignored)");
03215                 }
03216             }
03217     
03218         }
03219 
03220         cpl_image_turn(wavemap, 1);
03221 
03222     }
03223 
03224 
03225     /*
03226      * Interpolating the IDS coefficients
03227      */
03228 
03229     nrows = cpl_table_get_nrow(idscoeff);
03230 
03231     order = 0;
03232     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03233         ++order;
03234     --order;
03235 
03236     if (degree == 0) {
03237         for (k = 0; k <= order; k++) {
03238             double m;
03239             if (cpl_table_has_column(idscoeff, clab[k])) {
03240                 m = cpl_table_get_column_median(idscoeff, clab[k]);
03241                 cpl_table_fill_column_window_double(idscoeff, clab[k], 
03242                                                     0, nrows, m);
03243             }
03244         }
03245 
03246         return CPL_ERROR_NONE;
03247     }
03248 
03249     first_row = 0;
03250     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03251         first_row++;
03252 
03253     last_row = nrows - 1;
03254     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03255         last_row--;
03256 
03257     for (k = 0; k <= order; k++) {
03258 
03259         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03260         wave = cpl_vector_new(npoints);
03261         wdata = cpl_vector_get_data(wave);
03262         positions = cpl_vector_new(npoints);
03263         pdata = cpl_vector_get_data(positions);
03264 
03265         npoints = 0;
03266         for (i = first_row; i <= last_row; i++) {
03267             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03268             if (null == 0) {
03269                 wdata[npoints] = c;
03270                 pdata[npoints] = i;
03271                 npoints++;
03272             }
03273         }
03274 
03275 // This doesn't seem to provide good results, I have not understood why.
03276 // Restore for robust linear fitting.
03277 //
03278         if (degree == 1) {
03279             cpl_vector   *p;
03280             cpl_vector   *w;
03281             cpl_bivector *list;
03282             double        q, m;
03283 
03284             if (npoints > 4) {
03285                 p = cpl_vector_extract(positions, 2, npoints - 2, 1);
03286                 w = cpl_vector_extract(wave, 2, npoints - 2, 1);
03287             }
03288             else {
03289                 p = positions;
03290                 w = wave;
03291             }
03292 
03293             list = cpl_bivector_wrap_vectors(p, w);
03294 
03295             robustLinearFit(list, &q, &m, &mse);
03296             cpl_bivector_unwrap_vectors(list);
03297             for (i = first_row; i <= last_row; i++)
03298                  cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
03299 
03300             if (npoints > 4) {
03301                 cpl_vector_delete(p);
03302                 cpl_vector_delete(w);
03303             }
03304 
03305             continue;
03306         }
03307 
03308 // End robust linear fitting
03309 
03310         trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
03311 
03312         ksigma = 3*sqrt(mse);
03313 
03314         cpl_vector_delete(wave);
03315         cpl_vector_delete(positions);
03316 
03317         /*
03318          * Iteration
03319          */
03320 
03321         if (trend) {
03322             rpoints = 0;
03323             for (i = first_row; i <= last_row; i++) {
03324                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03325                 if (null == 0) {
03326                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03327                                                                  < ksigma) {
03328                         rpoints++;
03329                     }
03330                 }
03331             }
03332 
03333             if (rpoints > 0 && rpoints < npoints) {
03334                 cpl_msg_debug(func, "%d points rejected from "
03335                               "wavelength calibration fit", 
03336                               npoints - rpoints);
03337 
03338                 wave = cpl_vector_new(rpoints);
03339                 wdata = cpl_vector_get_data(wave);
03340                 positions = cpl_vector_new(rpoints);
03341                 pdata = cpl_vector_get_data(positions);
03342 
03343                 npoints = 0;
03344                 for (i = first_row; i <= last_row; i++) {
03345                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03346                     if (null == 0) {
03347                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03348                                                                  < ksigma) {
03349                             wdata[npoints] = c;
03350                             pdata[npoints] = i;
03351                             npoints++;
03352                         }
03353                     }
03354                 }
03355 
03356                 if (npoints) {
03357                     cpl_polynomial_delete(trend);
03358                     trend = cpl_polynomial_fit_1d_create(positions, 
03359                                                          wave, degree, NULL);
03360                 }
03361 
03362                 cpl_vector_delete(wave);
03363                 cpl_vector_delete(positions);
03364 
03365             }
03366         }
03367 
03368         if (trend) {
03369             for (i = first_row; i <= last_row; i++) {
03370                 if (mode == 1) {
03371                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03372                         cpl_table_set_double(idscoeff, clab[k], i, 
03373                                              cpl_polynomial_eval_1d(trend, i,
03374                                                                     NULL));
03375                     }
03376                 }
03377                 else if (mode == 2) {
03378                     cpl_table_set_double(idscoeff, clab[k], i, 
03379                                      cpl_polynomial_eval_1d(trend, i, NULL));
03380                 }
03381             }
03382             cpl_polynomial_delete(trend);
03383         }
03384         else {
03385             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03386         }
03387 
03388     }
03389 
03390     return CPL_ERROR_NONE;
03391 }
03392 
03393 
03394 
03420 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03421                            cpl_table *overscans)
03422 {
03423     const char *func = "mos_remove_bias";
03424 
03425     cpl_image *unbiased;
03426     cpl_image *overscan;
03427     double     mean_bias_level;
03428     double     mean_overscans_level;
03429     int        count;
03430     int        nrows;
03431     int        xlow, ylow, xhig, yhig;
03432     int        i;
03433 
03434 
03435     if (image == NULL || overscans == NULL) {
03436         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03437         return NULL;
03438     }
03439 
03440     nrows = cpl_table_get_nrow(overscans);
03441 
03442     if (nrows == 0) {
03443         cpl_msg_error(func, "Empty overscan table");
03444         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03445         return NULL;
03446     }
03447 
03448     if (bias) {
03449         if (nrows == 1) {
03450             unbiased = cpl_image_subtract_create(image, bias);
03451             if (unbiased == NULL) {
03452                 cpl_msg_error(func, "Incompatible master bias");
03453                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03454             }
03455             return unbiased;
03456         }
03457         mean_bias_level = cpl_image_get_mean(bias);
03458     }
03459     else {
03460         if (nrows == 1) {
03461             cpl_msg_error(func, "No master bias in input, and no overscan "
03462                           "regions in input image: bias subtraction "
03463                           "cannot be performed!");
03464             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03465             return NULL;
03466         }
03467         mean_bias_level = 0.0;
03468     }
03469 
03470     mean_overscans_level = 0.0;
03471     count = 0;
03472     for (i = 0; i < nrows; i++) {
03473         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03474         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03475         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03476         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03477 
03478         if (i == 0) {
03479             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03480             if (unbiased == NULL) {
03481                 cpl_msg_error(func, "Incompatible overscan table");
03482                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03483                 return NULL;
03484             }
03485             if (bias) {
03486                 if (cpl_image_subtract(unbiased, bias)) {
03487                     cpl_msg_error(func, "Incompatible master bias");
03488                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03489                     cpl_image_delete(unbiased);
03490                     return NULL;
03491                 }
03492             }
03493         }
03494         else {
03495             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03496             if (overscan == NULL) {
03497                 cpl_msg_error(func, "Incompatible overscan table");
03498                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03499                 cpl_image_delete(unbiased);
03500                 return NULL;
03501             }
03502 
03503             mean_overscans_level += cpl_image_get_median(overscan);
03504             count++;
03505 
03506 /***
03507  * Here the mean level was used: not very robust...
03508 
03509             mean_overscans_level += cpl_image_get_flux(overscan);
03510             count += cpl_image_get_size_x(overscan)
03511                    * cpl_image_get_size_y(overscan);
03512 ***/
03513             cpl_image_delete(overscan);
03514         }
03515     }
03516 
03517     /*
03518      * Overscan correction
03519      */
03520 
03521     mean_overscans_level /= count;
03522 
03523     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03524 
03525     cpl_msg_info(cpl_func, 
03526                  "Ratio between mean overscans level and mean bias level: %.2f",
03527                   mean_overscans_level / mean_bias_level);
03528 
03529     return unbiased;
03530 
03531 }
03532 
03533 
03592 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03593                                      int length, int msize, int fsize) 
03594 {
03595     const char *func = "mos_arc_background_1D";
03596 
03597     float  *minf;
03598     float  *maxf;
03599     float  *smof;
03600     int     i;
03601 
03602 
03603     if (spectrum == NULL || back == NULL)
03604         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03605 
03606     if (msize % 2 == 0)
03607         msize++;
03608 
03609     if (fsize % 2 == 0)
03610         fsize++;
03611 
03612     if (msize < 3 || fsize < msize || length < 2*fsize)
03613         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03614 
03615 
03616     minf = min_filter(spectrum, length, msize);
03617     smof = smo_filter(minf, length, fsize);
03618     cpl_free(minf);
03619     maxf = max_filter(smof, length, 2*msize+1);
03620     cpl_free(smof);
03621     smof = smo_filter(maxf, length, 2*fsize+1);
03622     cpl_free(maxf);
03623     minf = min_filter(smof, length, 2*msize+1);
03624     cpl_free(smof);
03625     smof = smo_filter(minf, length, 2*fsize+1);
03626     cpl_free(minf);
03627 
03628     for (i = 0; i < length; i++)
03629         back[i] = smof[i];
03630 
03631     cpl_free(smof);
03632 
03633     return CPL_ERROR_NONE;
03634 
03635 }
03636 
03637 
03694 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03695 {
03696     const char *func = "mos_arc_background";
03697 
03698     cpl_image  *fimage;
03699     cpl_image  *bimage;
03700     cpl_matrix *kernel;
03701     float      *data;
03702     float      *bdata;
03703     float      *row;
03704     float      *brow;
03705     int         nx, ny;
03706     int         i;
03707 
03708 
03709     if (image == NULL) {
03710         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03711         return NULL;
03712     }
03713 
03714     if (msize % 2 == 0)
03715         msize++;
03716 
03717     if (fsize % 2 == 0)
03718         fsize++;
03719 
03720     nx = cpl_image_get_size_x(image);
03721     ny = cpl_image_get_size_y(image);
03722 
03723     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03724 
03725     fimage = mos_image_filter_median(image, 3, 3);
03726 
03727     data = cpl_image_get_data_float(fimage);
03728     bdata = cpl_image_get_data_float(bimage);
03729 
03730     for (i = 0; i < ny; i++) {
03731         row = data + i * nx;
03732         brow = bdata + i * nx;
03733         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03734             cpl_error_set_where(func); 
03735             cpl_image_delete(fimage);
03736             cpl_image_delete(bimage);
03737             return NULL;
03738         }
03739     }
03740 
03741     cpl_image_delete(fimage);
03742 
03743     return bimage;
03744 }
03745 
03746 
03767 int mos_lines_width(const float *spectrum, int length)
03768 {
03769 
03770   const char *func = "mos_lines_width";
03771 
03772   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03773   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03774 
03775   double  norm, value, max;
03776   int     radius = 20;
03777   int     short_length = length - 2*radius - 1;
03778   int     width;
03779   int     i, j, k;
03780 
03781 
03782   /*
03783    * Derivative, and separation of positive and negative derivatives
03784    */
03785 
03786   for (j = 0, i = 1; i < length; j++, i++) {
03787       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03788       if (profile1[j] < 0)
03789           profile1[j] = 0;
03790       if (profile2[j] > 0)
03791           profile2[j] = 0;
03792       else
03793           profile2[j] = -profile2[j];
03794   }
03795 
03796 
03797   /*
03798    * Profiles normalisation
03799    */
03800 
03801   length--;
03802 
03803   norm = 0;
03804   for (i = 0; i < length; i++)
03805       if (norm < profile1[i])
03806           norm = profile1[i];
03807 
03808   for (i = 0; i < length; i++) {
03809       profile1[i] /= norm;
03810       profile2[i] /= norm;
03811   }
03812 
03813 
03814   /*
03815    * Cross-correlation
03816    */
03817 
03818   max = -1;
03819   for (i = 0; i <= radius; i++) {
03820       value = 0;
03821       for (j = 0; j < short_length; j++) {
03822           k = radius+j;
03823           value += profile1[k] * profile2[k+i];
03824       }
03825       if (max < value) {
03826           max = value;
03827           width = i;
03828       }
03829   }
03830 
03831   cpl_free(profile1);
03832   cpl_free(profile2);
03833 
03834   if (max < 0.0) {
03835       cpl_msg_debug(func, "Cannot estimate line width");
03836       width = 1;
03837   }
03838 
03839   return width;
03840 
03841 }
03842 
03843 
03870 cpl_vector *mos_peak_candidates(const float *spectrum, 
03871                                 int length, float level, 
03872                                 float exp_width)
03873 { 
03874 
03875   const char *func = "mos_peak_candidates";
03876 
03877   int     i, j;
03878   int     nint   = length - 1;
03879   int     n      = 0;
03880   int     width  = 2 * ceil(exp_width / 2) + 1;
03881   int     start  = width / 2;
03882   int     end    = length - width / 2;
03883   int     step;
03884   float  *smo;
03885   double *data   = cpl_calloc(length/2, sizeof(double));
03886 
03887 
03888   if (spectrum == NULL) {
03889       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03890       return NULL;
03891   }
03892 
03893 
03894   /*
03895    * If lines have a flat top (as in the case of broad slit), smooth
03896    * before determining the max.
03897    */
03898 
03899   if (width > 7) {
03900     smo = cpl_calloc(length, sizeof(float));
03901     start = width / 2;
03902     end = length - width / 2;
03903     for (i = 0; i < start; i++)
03904       smo[i] = spectrum[i];
03905     for (i = start; i < end; i++) {
03906       for (j = i - start; j <= i + start; j++)
03907         smo[i] += spectrum[j];
03908       smo[i] /= width;
03909     }
03910     for (i = end; i < length; i++)
03911       smo[i] = spectrum[i];
03912   }
03913   else {
03914       smo = (float *)spectrum;
03915   }
03916 
03917   /*
03918    * Collect all relative maxima along spectrum, that are higher than the
03919    * specified level.
03920    */
03921 
03922   if (width > 20)
03923     step = width / 2;
03924   else
03925     step = 1;
03926 
03927   for (i = step; i < nint - step + 1; i += step) {
03928     if (smo[i] > level) {
03929       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03930         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03931           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03932           ++n;
03933         }
03934       }
03935     }
03936   }
03937 
03938   if (width > 7) {
03939     cpl_free(smo);
03940   }
03941 
03942   if (n == 0) {
03943     cpl_free(data);
03944     return NULL;
03945   }
03946 
03947   return cpl_vector_wrap(n, data);
03948 
03949 }
03950 
03951 
03973 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
03974                              cpl_vector *peaks, int sradius)
03975 {
03976 
03977     const char *func = "mos_refine_peaks";
03978 
03979     double *data;
03980     float   pos;
03981     int     npeaks;
03982     int     startPos, endPos;
03983     int     window = 2*sradius+1;
03984     int     i, j;
03985 
03986 
03987     if (peaks == NULL || spectrum == NULL) {
03988         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03989         return NULL;
03990     }
03991 
03992     npeaks = cpl_vector_get_size(peaks);
03993     data = cpl_vector_unwrap(peaks);
03994 
03995     for (i = 0; i < npeaks; i++) {
03996         startPos = data[i] - window/2;
03997         endPos   = startPos + window;
03998         if (startPos < 0 || endPos >= length)
03999             continue;
04000 
04001         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
04002             pos += startPos;
04003             data[i] = pos;
04004         }
04005     }
04006 
04007     for (i = 1; i < npeaks; i++)
04008         if (data[i] - data[i-1] < 0.5)
04009             data[i-1] = -1.0;
04010 
04011     for (i = 0, j = 0; i < npeaks; i++) {
04012         if (data[i] > 0.0) {
04013             if (i != j)
04014                 data[j] = data[i];
04015             j++;
04016         }
04017     }
04018 
04019     return cpl_vector_wrap(j, data);
04020 
04021 }
04022 
04023 
04024 void mos_set_multiplex(int multiplex)
04025 {
04026     mos_multiplex = multiplex;
04027 }
04028 
04082 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
04083                                  double min_disp, double max_disp,
04084                                  double tolerance)
04085 {
04086 
04087   int      i, j, k, l;
04088   int      nlint, npint;
04089   int      minpos;
04090   float    min;
04091   double   lratio, pratio;
04092   double   lo_start, lo_end, hi_start, hi_end, denom;
04093   double   disp, variation, prev_variation;
04094   int      max, maxpos, minl, mink;
04095   int      ambiguous;
04096   int      npeaks_lo, npeaks_hi;
04097   int     *peak_lo;
04098   int     *peak_hi;
04099   int    **ident;
04100   int     *nident;
04101   int     *lident;
04102 
04103   double  *peak;
04104   double  *line;
04105   int      npeaks, nlines;
04106 
04107   double  *xpos;
04108   double  *lambda;
04109   int     *ilambda;
04110   double  *tmp_xpos;
04111   double  *tmp_lambda;
04112   int     *tmp_ilambda;
04113   int     *flag;
04114   int      n = 0;
04115   int      nn;
04116   int      nseq = 0;
04117   int      gap;
04118   int     *seq_length;
04119   int      found;
04120 
04121   peak        = cpl_vector_get_data(peaks);
04122   npeaks      = cpl_vector_get_size(peaks);
04123   line        = cpl_vector_get_data(lines);
04124   nlines      = cpl_vector_get_size(lines);
04125 
04126   if (npeaks < 4)
04127       return NULL;
04128 
04129   peak_lo     = cpl_malloc(npeaks * sizeof(int));
04130   peak_hi     = cpl_malloc(npeaks * sizeof(int));
04131   nident      = cpl_calloc(npeaks, sizeof(int));
04132   lident      = cpl_calloc(nlines, sizeof(int));
04133   xpos        = cpl_calloc(npeaks, sizeof(double));
04134   lambda      = cpl_calloc(npeaks, sizeof(double));
04135   ilambda     = cpl_calloc(npeaks, sizeof(int));
04136   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
04137   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
04138   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
04139   flag        = cpl_calloc(npeaks, sizeof(int));
04140   seq_length  = cpl_calloc(npeaks, sizeof(int));
04141   ident       = cpl_malloc(npeaks * sizeof(int *));
04142   for (i = 0; i < npeaks; i++)
04143     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
04144 
04145   /*
04146    * This is just the number of intervals - one less than the number
04147    * of points (catalog wavelengths, or detected peaks).
04148    */
04149 
04150   nlint = nlines - 1;
04151   npint = npeaks - 1;
04152 
04153 
04154   /*
04155    * Here the big loops on catalog lines begins.
04156    */
04157 
04158   for (i = 1; i < nlint; i++) {
04159 
04160 
04161     /*
04162      * For each catalog wavelength I take the previous and the next one, 
04163      * and compute the ratio of the corresponding wavelength intervals.
04164      * This ratio will be compared to all the ratios obtained doing the
04165      * same with all the detected peaks positions.
04166      */
04167 
04168     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
04169 
04170 
04171     /*
04172      * Here the loop on detected peaks positions begins.
04173      */
04174 
04175     for (j = 1; j < npint; j++) {
04176 
04177       /*
04178        * Not all peaks are used for computing ratios: just the ones
04179        * that are compatible with the expected spectral dispersion
04180        * are taken into consideration. Therefore, I define the pixel
04181        * intervals before and after any peak that are compatible with
04182        * the specified dispersion interval, and select just the peaks
04183        * within such intervals. If either of the two intervals doesn't
04184        * contain any peak, then I skip the current peak and continue
04185        * with the next.
04186        */
04187 
04188       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
04189       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
04190       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
04191       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
04192 
04193       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04194         if (peak[k] > lo_end)
04195           break;
04196         if (peak[k] > lo_start) {
04197           peak_lo[npeaks_lo] = k;
04198           ++npeaks_lo;
04199         }
04200       }
04201 
04202       if (npeaks_lo == 0)
04203         continue;
04204 
04205       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04206         if (peak[k] > hi_end)
04207           break;
04208         if (peak[k] > hi_start) {
04209           peak_hi[npeaks_hi] = k;
04210           ++npeaks_hi;
04211         }
04212       }
04213 
04214       if (npeaks_hi == 0)
04215         continue;
04216 
04217 
04218       /*
04219        * Now I have all peaks that may help for a local identification.
04220        * peak_lo[k] is the sequence number of the k-th peak of the lower
04221        * interval; peak_hi[l] is the sequence number of the l-th peak of
04222        * the higher interval. j is, of course, the sequence number of the
04223        * current peak (second big loop).
04224        */
04225 
04226       prev_variation = 1000.0;
04227       minl = mink = 0;
04228 
04229       for (k = 0; k < npeaks_lo; k++) {
04230         denom = peak[j] - peak[peak_lo[k]];
04231         for (l = 0; l < npeaks_hi; l++) {
04232 
04233           /*
04234            * For any pair of peaks - one from the lower and the other
04235            * from the higher interval - I compute the same ratio that
04236            * was computed with the current line catalog wavelength.
04237            */
04238 
04239           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04240 
04241           /*
04242            * If the two ratios are compatible within the specified
04243            * tolerance, we have a preliminary identification. This
04244            * will be marked in the matrix ident[][], where the first
04245            * index corresponds to a peak sequence number, and the second
04246            * index is the counter of the identifications made during
04247            * this whole process. The array of counters is nident[].
04248            * If more than one interval pair fulfills the specified
04249            * tolerance, the closest to the expected ratio is selected.
04250            */
04251 
04252           variation = fabs(lratio-pratio) / pratio;
04253 
04254           if (variation < tolerance) {
04255             if (variation < prev_variation) {
04256               prev_variation = variation;
04257               minl = l;
04258               mink = k;
04259             }
04260           }
04261         }
04262       }
04263       if (prev_variation < tolerance) {
04264         ident[j][nident[j]]                         = i;
04265         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04266         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04267         ++nident[j];
04268         ++nident[peak_hi[minl]];
04269         ++nident[peak_lo[mink]];
04270       }
04271     }   /* End loop on positions */
04272   }    /* End loop on lines     */
04273 
04274 
04275   /*
04276    * At this point I have filled the ident matrix with all my preliminary
04277    * identifications. Ambiguous identifications must be eliminated.
04278    */
04279 
04280 
04281   for (i = 0; i < npeaks; i++) {
04282 
04283 
04284     /*
04285      * I don't take into consideration peaks that were never identified.
04286      * They are likely contaminations, or emission lines that were not
04287      * listed in the input wavelength catalog.
04288      */
04289 
04290     if (nident[i] > 1) {
04291 
04292 
04293       /*
04294        * Initialise the histogram of wavelengths assigned to the i-th peak.
04295        */
04296 
04297       for (j = 0; j < nlines; j++)
04298         lident[j] = 0;
04299 
04300 
04301       /*
04302        * Count how many times each catalog wavelength was assigned
04303        * to the i-th peak.
04304        */
04305 
04306       for (j = 0; j < nident[i]; j++)
04307         ++lident[ident[i][j]];
04308 
04309 
04310       /*
04311        * What wavelength was most frequently assigned to the i-th peak?
04312        */
04313 
04314       max = 0;
04315       maxpos = 0;
04316       for (j = 0; j < nlines; j++) {
04317         if (max < lident[j]) {
04318           max = lident[j];
04319           maxpos = j;
04320         }
04321       }
04322 
04323 
04324       /*
04325        * Were there other wavelengths assigned with the same frequency?
04326        * This would be the case of an ambiguous identification. It is
04327        * safer to reject this peak...
04328        */
04329 
04330       ambiguous = 0;
04331 
04332       for (k = maxpos + 1; k < nlines; k++) {
04333         if (lident[k] == max) {
04334           ambiguous = 1;
04335           break;
04336         }
04337       }
04338 
04339       if (ambiguous)
04340         continue;
04341 
04342 
04343       /*
04344        * Otherwise, I assign to the i-th peak the wavelength that was
04345        * most often assigned to it.
04346        */
04347 
04348       tmp_xpos[n]   = peak[i];
04349       tmp_lambda[n] = line[maxpos];
04350       tmp_ilambda[n] = maxpos;
04351 
04352       ++n;
04353 
04354     }
04355 
04356   }
04357 
04358 
04359   /*
04360    * Check on identified peaks. Contaminations from other spectra might 
04361    * be present and should be excluded: this type of contamination 
04362    * consists of peaks that have been _correctly_ identified! The non-
04363    * spectral type of light contamination should have been almost all 
04364    * removed already in the previous steps, but it may still be present.
04365    * Here, the self-consistent sequences of identified peaks are
04366    * separated one from the other. At the moment, just the longest of
04367    * such sequences is selected (in other words, spectral multiplexing
04368    * is ignored).
04369    */
04370 
04371   if (n > 1) {
04372     nn = 0;                  /* Number of peaks in the list of sequences */
04373     nseq = 0;                /* Current sequence */
04374     for (k = 0; k < n; k++) {
04375       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04376         flag[k] = 1;
04377         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04378         lambda[nn] = tmp_lambda[k];
04379         ilambda[nn] = tmp_ilambda[k];
04380         ++seq_length[nseq];
04381         ++nn;
04382 
04383         /*
04384          * Now look for all the following peaks that are compatible
04385          * with the expected spectral dispersion, and add them in 
04386          * sequence to xpos. Note that missing peaks are not a problem...
04387          */
04388          
04389         i = k;
04390         while (i < n - 1) {
04391           found = 0;
04392           for (j = i + 1; j < n; j++) {
04393             if (flag[j] == 0) {
04394               disp = (tmp_lambda[j] - tmp_lambda[i])
04395                    / (tmp_xpos[j] - tmp_xpos[i]);
04396               if (disp >= min_disp && disp <= max_disp) {
04397                 flag[j] = 1;
04398                 xpos[nn] = tmp_xpos[j];
04399                 lambda[nn] = tmp_lambda[j];
04400                 ilambda[nn] = tmp_ilambda[j];
04401                 ++seq_length[nseq];
04402                 ++nn;
04403                 i = j;
04404                 found = 1;
04405                 break;
04406               }
04407             }
04408           }
04409           if (!found)
04410             break;
04411         }
04412 
04413         /*
04414          * Current sequence is completed: begin new sequence on the
04415          * excluded peaks, starting the loop on peaks again.
04416          */
04417 
04418         ++nseq;
04419         k = 0;
04420       }
04421     }
04422 
04423 
04424     /*
04425      * Find the longest sequence of self-consistent peaks.
04426      */
04427 
04428     maxpos = max = 0;
04429 
04430     if (mos_multiplex < 0) {
04431       for (i = 0; i < nseq; i++) {
04432         if (seq_length[i] > max) {
04433           max = seq_length[i];
04434           maxpos = i;
04435         }
04436       }
04437     }
04438     else {
04439 
04440       /*
04441        * Now consider the sequence which lays in the specified 
04442        * CCD region (indicated by mos_multiplex): that is, _most_ 
04443        * of its lines (more than half) must be in that region...
04444        */
04445 
04446       nn = 0;
04447       found = 0;
04448 
04449       for (i = 0; i < nseq; i++) {
04450         n = seq_length[i];
04451         if (n > 5) {
04452           cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
04453           int        region;
04454 
04455           for (j = 0; j < n; j++)
04456             cpl_array_set_int(regions, j, 
04457                               ((int)floor(xpos[nn + j])) / mos_region_size);
04458 
04459           region = (int)cpl_array_get_median(regions);
04460           cpl_array_delete(regions);
04461 
04462           if (mos_multiplex == region) {
04463             if (found) {
04464               cpl_msg_debug(cpl_func, "More than one spectrum found in "
04465                             "region %d (only the first one is extracted)", 
04466                             mos_multiplex);
04467               break;
04468             }
04469             found = 1;
04470             max = seq_length[i];
04471             maxpos = i;
04472           }
04473         }
04474         nn += seq_length[i];
04475       }
04476     }
04477 
04478     /*
04479      * Find where this sequence starts in the whole peak position
04480      * storage.
04481      */
04482 
04483     nn = 0;
04484     for (i = 0; i < maxpos; i++)
04485       nn += seq_length[i];
04486 
04487     /*
04488      * Move the longest sequence at the beginning of the returned lists
04489      */
04490 
04491     n = max;
04492     for (i = 0; i < n; i++, nn++) {
04493       xpos[i] = xpos[nn];
04494       lambda[i] = lambda[nn];
04495       ilambda[i] = ilambda[nn];
04496     }
04497 
04498 
04499     /*
04500      * Are some wavelengths missing? Recover them.
04501      */
04502 
04503     for (i = 1; i < n; i++) {
04504       gap = ilambda[i] - ilambda[i-1];
04505       for (j = 1; j < gap; j++) {
04506 
04507         if (j == 1) {
04508 
04509           /*
04510            * Determine the local dispersion from the current pair of peaks
04511            */
04512   
04513           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04514         }
04515 
04516         /*
04517          * With this, find the expected position of the missing
04518          * peak by linear interpolation.
04519          */
04520 
04521         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04522 
04523         /*
04524          * Is there a peak at that position? Here a peak from the
04525          * original list is searched, that is closer than 2 pixels
04526          * to the expected position. If it is found, insert it at
04527          * the current position on the list of identified peaks,
04528          * and leave immediately the loop (taking the new position
04529          * for the following linear interpolation, in case more
04530          * than one peak is missing in the current interval).
04531          * If it is not found, stay in the loop, looking for 
04532          * the following missing peaks in this interval.
04533          */
04534 
04535         found = 0;
04536         for (k = 0; k < npeaks; k++) {
04537           if (fabs(peak[k] - hi_start) < 2) {
04538             for (l = n; l > i; l--) {
04539               xpos[l] = xpos[l-1];
04540               lambda[l] = lambda[l-1];
04541               ilambda[l] = ilambda[l-1];
04542             }
04543             xpos[i] = peak[k];
04544             lambda[i] = line[ilambda[i-1] + j];
04545             ilambda[i] = ilambda[i-1] + j;
04546             ++n;
04547             found = 1;
04548             break;
04549           }
04550         }
04551         if (found)
04552           break;
04553       }
04554     }
04555 
04556 
04557     /*
04558      * Try to extrapolate forward
04559      */
04560 
04561     found = 1;
04562     while (ilambda[n-1] < nlines - 1 && found) {
04563 
04564       /*
04565        * Determine the local dispersion from the last pair of 
04566        * identified peaks
04567        */
04568 
04569       if (n > 1)
04570           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04571       else
04572           disp = 0.0;
04573 
04574       if (disp > max_disp || disp < min_disp)
04575         break;
04576 
04577 
04578       /*
04579        * With this, find the expected position of the missing
04580        * peak by linear interpolation.
04581        */
04582 
04583       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04584 
04585       /*
04586        * Is there a peak at that position? Here a peak from the
04587        * original list is searched, that is closer than 6 pixels
04588        * to the expected position. If it is found, insert it at
04589        * the end of the list of identified peaks. If it is not
04590        * found, leave the loop.
04591        */
04592 
04593       found = 0;
04594       min = fabs(peak[0] - hi_start);
04595       minpos = 0;
04596       for (k = 1; k < npeaks; k++) {
04597         if (min > fabs(peak[k] - hi_start)) {
04598             min = fabs(peak[k] - hi_start);
04599             minpos = k;
04600         }
04601       }
04602       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04603         xpos[n] = peak[minpos];
04604         lambda[n] = line[ilambda[n-1] + 1];
04605         ilambda[n] = ilambda[n-1] + 1;
04606         ++n;
04607         found = 1;
04608       }
04609     }
04610 
04611 
04612     /*
04613      * Try to extrapolate backward
04614      */
04615 
04616     found = 1;
04617     while (ilambda[0] > 0 && found) {
04618 
04619       /*
04620        * Determine the local dispersion from the first pair of
04621        * identified peaks
04622        */
04623 
04624       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04625 
04626       if (disp > max_disp || disp < min_disp)
04627         break;
04628 
04629 
04630       /*
04631        * With this, find the expected position of the missing
04632        * peak by linear interpolation.
04633        */
04634 
04635       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04636 
04637 
04638       /*
04639        * Is there a peak at that position? Here a peak from the
04640        * original list is searched, that is closer than 6 pixels
04641        * to the expected position. If it is found, insert it at
04642        * the beginning of the list of identified peaks. If it is not
04643        * found, leave the loop.
04644        */
04645 
04646       found = 0;
04647       min = fabs(peak[0] - hi_start);
04648       minpos = 0;
04649       for (k = 1; k < npeaks; k++) {
04650         if (min > fabs(peak[k] - hi_start)) {
04651             min = fabs(peak[k] - hi_start);
04652             minpos = k;
04653         }
04654       }
04655       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04656         for (j = n; j > 0; j--) {
04657           xpos[j] = xpos[j-1];
04658           lambda[j] = lambda[j-1];
04659           ilambda[j] = ilambda[j-1];
04660         }
04661         xpos[0] = peak[minpos];
04662         lambda[0] = line[ilambda[0] - 1];
04663         ilambda[0] = ilambda[0] - 1;
04664         ++n;
04665         found = 1;
04666       }
04667     }
04668   }
04669 
04670 
04671   /*
04672    * At this point all peaks are processed. Free memory, and return
04673    * the result.
04674    */
04675 
04676 /************************************************+
04677   for (i = 0; i < npeaks; i++) {
04678     printf("Peak %d:\n   ", i);
04679     for (j = 0; j < nident[i]; j++)
04680       printf("%.2f, ", line[ident[i][j]]);
04681     printf("\n");
04682   }
04683 
04684   printf("\n");
04685 
04686   for (i = 0; i < n; i++)
04687     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04688 +************************************************/
04689   for (i = 0; i < npeaks; i++)
04690     cpl_free(ident[i]);
04691   cpl_free(ident);
04692   cpl_free(nident);
04693   cpl_free(lident);
04694   cpl_free(ilambda);
04695   cpl_free(tmp_xpos);
04696   cpl_free(tmp_lambda);
04697   cpl_free(tmp_ilambda);
04698   cpl_free(peak_lo);
04699   cpl_free(flag);
04700   cpl_free(seq_length);
04701   cpl_free(peak_hi);
04702 
04703   if (n == 0) {
04704     cpl_free(xpos);
04705     cpl_free(lambda);
04706     return NULL;
04707   }
04708 
04709   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04710                                    cpl_vector_wrap(n, lambda));
04711 }
04712 
04713 
04731 /*
04732 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04733                     double refwave, double pixel)
04734 {
04735     double yellow;
04736     double cpixel;
04737     double tolerance = 0.02;
04738     int    max_iter = 20;
04739     int    iter = 0;
04740 
04741     
04742     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04743         return 0.0;
04744     
04745     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04746         return 0.0;
04747 
04748     yellow = (blue + red) / 2;
04749 
04750     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04751 
04752     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04753 
04754         if (cpixel > pixel)
04755             red = yellow;
04756         else
04757             blue = yellow;
04758 
04759         yellow = (blue + red) / 2;
04760         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04761 
04762         iter++;
04763 
04764     }
04765 
04766     return yellow;
04767 
04768 }
04769 */
04770 
04771 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04772                     double refwave, double pixel)
04773 {
04774     double     yellow;
04775     double     coeff;
04776     cpl_size   zero = 0;
04777 
04778     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04779         return 0.0;
04780 
04781     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04782         return 0.0;
04783 
04784     yellow = (blue + red) / 2 - refwave;
04785 
04786     coeff = cpl_polynomial_get_coeff(ids, &zero);
04787     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04788 
04789     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04790     if (cpl_error_get_code() != CPL_ERROR_NONE) {
04791         cpl_error_reset();
04792         return 0.0;
04793     }
04794 
04795     cpl_polynomial_set_coeff(ids, &zero, coeff);
04796 
04797     return yellow + refwave;
04798 
04799 }
04800 
04826 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04827                                  double reject, int minlines, 
04828                                  int *nlines, double *err)
04829 {
04830     const char   *func = "mos_poly_wav2pix";
04831 
04832     cpl_bivector *pixwav2;
04833     cpl_vector   *wavel;
04834     cpl_vector   *pixel;
04835     double       *d_wavel;
04836     double       *d_pixel;
04837     double        pixpos;
04838     int           fitlines;
04839     int           rejection = 0;
04840     int           i, j;
04841 
04842     cpl_polynomial *ids;
04843 
04844 
04845     *nlines = 0;
04846     *err = 0;
04847 
04848     if (pixwav == NULL) {
04849         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04850         return NULL;
04851     }
04852 
04853     fitlines = cpl_bivector_get_size(pixwav);
04854 
04855     if (fitlines < minlines) {
04856         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04857         return NULL;
04858     }
04859 
04860 
04861     /*
04862      * If outliers rejection was requested, allocate a working
04863      * vector (that can be modified as soon as outliers are removed)
04864      */
04865 
04866     if (reject > 0.0)
04867         rejection = 1;
04868 
04869     if (rejection)
04870         pixwav2 = cpl_bivector_duplicate(pixwav);
04871     else
04872         pixwav2 = pixwav;
04873 
04874 
04875     /*
04876      * The single vectors are extracted just because the fitting routine
04877      * requires it
04878      */
04879 
04880     pixel = cpl_bivector_get_x(pixwav2);
04881     wavel = cpl_bivector_get_y(pixwav2);
04882 
04883 
04884     /*
04885      * Get rid of the wrapper, in case of duplication
04886      */
04887 
04888     if (rejection)
04889         cpl_bivector_unwrap_vectors(pixwav2);
04890 
04891 
04892     /*
04893      * Here begins the iterative fit of identified lines
04894      */
04895 
04896     while (fitlines >= minlines) {
04897 
04898         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04899         *err = sqrt(*err);
04900     
04901         if (ids == NULL) {
04902             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04903             cpl_msg_debug(func, "Fitting IDS");
04904             cpl_error_set_where(func);
04905             if (rejection) {
04906                 cpl_vector_delete(wavel);
04907                 cpl_vector_delete(pixel);
04908             }
04909             return NULL;
04910         }
04911 
04912         if (rejection) {
04913 
04914 
04915             /*
04916              * Now work directly with the vector data buffers...
04917              */
04918 
04919             d_pixel = cpl_vector_unwrap(pixel);
04920             d_wavel = cpl_vector_unwrap(wavel);
04921 
04922             for (i = 0, j = 0; i < fitlines; i++) {
04923                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04924                 if (fabs(pixpos - d_pixel[i]) < reject) {
04925                     d_pixel[j] = d_pixel[i];
04926                     d_wavel[j] = d_wavel[i];
04927                     j++;
04928                 }
04929             }
04930     
04931             if (j == fitlines) {       /* No rejection in last iteration */
04932                 cpl_free(d_wavel);
04933                 cpl_free(d_pixel);
04934                 *nlines = fitlines;
04935                 return ids;
04936             }
04937             else {                     /* Some lines were rejected       */
04938                 fitlines = j;
04939                 cpl_polynomial_delete(ids);
04940                 if (fitlines >= minlines) {
04941                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04942                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04943                 }
04944                 else {                 /* Too few lines: failure         */
04945                     cpl_free(d_wavel);
04946                     cpl_free(d_pixel);
04947                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04948                     return NULL;
04949                 }
04950             }
04951         }
04952         else {
04953             *nlines = fitlines;
04954             return ids;       /* Exit at first iteration if no rejection */
04955         }
04956     }
04957 
04958     return ids;               /* To avoid compiler warnings */
04959 }
04960 
04961 
04986 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04987                                  double reject, int minlines, 
04988                                  int *nlines, double *err)
04989 {
04990 
04991     cpl_bivector *wavpix;
04992     cpl_vector   *wavel;
04993     cpl_vector   *pixel;
04994 
04995     cpl_polynomial *dds;
04996 
04997 
04998     /*
04999      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
05000      */
05001 
05002     pixel = cpl_bivector_get_x(pixwav);
05003     wavel = cpl_bivector_get_y(pixwav);
05004 
05005     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
05006 
05007     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
05008 
05009     cpl_bivector_unwrap_vectors(wavpix);
05010 
05011     return dds;
05012 
05013 }
05014 
05015 
05038 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 
05039                              cpl_vector *lines, cpl_polynomial *ids, 
05040                              double refwave, int sradius)
05041 {
05042     const char   *func = "mos_find_peaks";
05043 
05044     double       *data;
05045     double       *d_pixel;
05046     double       *d_wavel;
05047     float         pos;
05048     int           nlines;
05049     int           pixel;
05050     int           i, j;
05051 
05052 
05053     if (spectrum == NULL || lines == NULL || ids == NULL) {
05054         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05055         return NULL;
05056     }
05057 
05058     nlines = cpl_vector_get_size(lines);
05059 
05060     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
05061         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05062         return NULL;
05063     }
05064 
05065     d_wavel = cpl_malloc(nlines * sizeof(double));
05066     d_pixel = cpl_malloc(nlines * sizeof(double));
05067 
05068     data = cpl_vector_get_data(lines);
05069 
05070     for (i = 0, j = 0; i < nlines; i++) {
05071         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
05072         if (pixel - sradius < 0 || pixel + sradius >= length)
05073             continue;
05074         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
05075             pos += pixel - sradius;
05076             d_pixel[j] = pos;
05077             d_wavel[j] = data[i];
05078             j++;
05079         }
05080     }
05081 
05082     if (j > 0) {
05083         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
05084                                          cpl_vector_wrap(j, d_wavel));
05085     }
05086     else {
05087         cpl_free(d_wavel);
05088         cpl_free(d_pixel);
05089         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
05090         return NULL;
05091     }
05092 }
05093 
05094 
05212 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
05213                                           cpl_vector *lines,
05214                                           double dispersion, float level,
05215                                           int sradius, int order,
05216                                           double reject, double refwave, 
05217                                           double *wavestart, double *waveend,
05218                                           int *nlines, double *error, 
05219                                           cpl_table *idscoeff,
05220                                           cpl_image *calibration,
05221                                           cpl_image *residuals, 
05222                                           cpl_table *restable,
05223                                           cpl_mask *refmask)
05224 {
05225 
05226     const char *func = "mos_wavelength_calibration_raw";
05227 
05228     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
05229                                                  /* Max order is 5 */
05230 
05231     double  tolerance = 20.0;     /* Probably forever...                */
05232     int     step      = 10;       /* Compute restable every "step" rows */
05233 
05234     char            name[MAX_COLNAME];
05235     cpl_image      *resampled;
05236     cpl_bivector   *output;
05237     cpl_bivector   *new_output;
05238     cpl_vector     *peaks;
05239     cpl_vector     *wavel;
05240     cpl_polynomial *ids;
05241     cpl_polynomial *lin;
05242     cpl_matrix     *kernel;
05243     double          ids_err;
05244     double          max_disp, min_disp;
05245     double         *line;
05246     double          firstLambda, lastLambda, lambda;
05247     double          value, wave, pixe;
05248     cpl_binary     *mdata;
05249     const float    *sdata;
05250     float          *rdata;
05251     float          *idata;
05252     float          *ddata;
05253     float           v1, v2, vi;
05254     float           fpixel;
05255     int            *have_it;
05256     int             pixstart, pixend;
05257     int             extrapolation;
05258     int             nref;
05259     int             nl, nx, ny, pixel;
05260     int             countLines, usedLines;
05261     int             uorder;
05262     int             in, first, last;
05263     int             width, uradius;
05264     int             i, j;
05265     cpl_size        k;
05266 
05267 
05268     if (dispersion == 0.0) {
05269         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05270         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05271         return NULL;
05272     }
05273 
05274     if (dispersion < 0.0) {
05275         cpl_msg_error(func, "The expected dispersion must be positive");
05276         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05277         return NULL;
05278     }
05279 
05280     max_disp = dispersion + dispersion * tolerance / 100;
05281     min_disp = dispersion - dispersion * tolerance / 100;
05282 
05283     if (order < 1) {
05284         cpl_msg_error(func, "The order of the fitting polynomial "
05285                       "must be at least 1");
05286         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05287         return NULL;
05288     }
05289 
05290     if (image == NULL || lines == NULL) {
05291         cpl_msg_error(func, "Both spectral exposure and reference line "
05292                       "catalog are required in input");
05293         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05294         return NULL;
05295     }
05296 
05297     nx = cpl_image_get_size_x(image);
05298     ny = cpl_image_get_size_y(image);
05299     sdata = cpl_image_get_data_float_const(image);
05300 
05301     nref = cpl_vector_get_size(lines);
05302     line = cpl_vector_get_data(lines);
05303 
05304     if (*wavestart < 1.0 && *waveend < 1.0) {
05305         firstLambda = line[0];
05306         lastLambda = line[nref-1];
05307         extrapolation = (lastLambda - firstLambda) / 10;
05308         firstLambda -= extrapolation;
05309         lastLambda += extrapolation;
05310         *wavestart = firstLambda;
05311         *waveend = lastLambda;
05312     }
05313     else {
05314         firstLambda = *wavestart;
05315         lastLambda = *waveend;
05316     }
05317 
05318     nl = (lastLambda - firstLambda) / dispersion;
05319     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05320     rdata = cpl_image_get_data_float(resampled);
05321 
05322     if (calibration)
05323         idata = cpl_image_get_data_float(calibration);
05324 
05325     if (residuals)
05326         ddata = cpl_image_get_data_float(residuals);
05327 
05328     if (idscoeff)
05329         for (j = 0; j <= order; j++)
05330             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05331 
05332     if (restable) {
05333         cpl_table_set_size(restable, nref);
05334         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05335         cpl_table_copy_data_double(restable, "wavelength", line);
05336         for (i = 0; i < ny; i += step) {
05337              snprintf(name, MAX_COLNAME, "r%d", i);
05338              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05339              snprintf(name, MAX_COLNAME, "d%d", i);
05340              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05341              snprintf(name, MAX_COLNAME, "p%d", i);
05342              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05343         }
05344     }
05345 
05346     /*
05347      * Here is the real thing: detecting and identifying peaks,
05348      * and then fit the transformation from wavelength to pixel
05349      * and from pixel to wavelength.
05350      */
05351 
05352     for (i = 0; i < ny; i++) {
05353         width = mos_lines_width(sdata + i*nx, nx);
05354         if (sradius > 0) {
05355             if (width > sradius) {
05356                 uradius = width;
05357             }
05358             else {
05359                 uradius = sradius;
05360             }
05361         }
05362         if (width < 5)
05363             width = 5;
05364         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05365         if (peaks) {
05366             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05367         }
05368         if (peaks) {
05369             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05370             if (output) {
05371                 countLines = cpl_bivector_get_size(output);
05372                 if (countLines < 4) {
05373                     cpl_bivector_delete(output);
05374                     cpl_vector_delete(peaks);
05375                     if (nlines)
05376                         nlines[i] = 0;
05377                     if (error)
05378                         error[i] = 0.0;
05379                     continue;
05380                 }
05381 
05382                 /*
05383                  * Set reference wavelength as zero point
05384                  */
05385 
05386                 wavel = cpl_bivector_get_y(output);
05387                 cpl_vector_subtract_scalar(wavel, refwave);
05388 
05389                 uorder = countLines / 2 - 1;
05390                 if (uorder > order)
05391                     uorder = order;
05392 
05393 /* This part is now commented out. In case the first-guess iteration
05394  * was requested, the first fit was made with a lower polynomial degree:
05395  * more robust, and still accurate enough to be used as a first-guess.
05396 
05397                 if (sradius > 0 && uorder > 2)
05398                     --uorder;
05399 
05400  * End of commented part */
05401 
05402                 ids = mos_poly_wav2pix(output, uorder, reject,
05403                                        2 * (uorder + 1), &usedLines,
05404                                        &ids_err);
05405 
05406                 if (ids == NULL) {
05407                     cpl_bivector_delete(output);
05408                     cpl_vector_delete(peaks);
05409                     if (nlines)
05410                         nlines[i] = 0;
05411                     if (error)
05412                         error[i] = 0.0;
05413                     cpl_error_reset();
05414                     continue;
05415                 }
05416 
05417                 if (idscoeff) {
05418 
05419                     /*
05420                      * Write it anyway, even in case a first-guess based
05421                      * solution will be searched afterwards: in case of
05422                      * failure, the "blind" solution is kept.
05423                      */
05424 
05425                     for (k = 0; k <= order; k++) {
05426                         if (k > uorder) {
05427                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05428                         }
05429                         else {
05430                             cpl_table_set_double(idscoeff, clab[k], i,
05431                                       cpl_polynomial_get_coeff(ids, &k));
05432                         }
05433                     }
05434                 }
05435 
05436                 if (sradius > 0) {
05437 
05438                     /*
05439                      * Use ids as a first-guess
05440                      */
05441 
05442                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05443                                                 ids, refwave, uradius);
05444 
05445                     if (new_output) {
05446                         cpl_bivector_delete(output);
05447                         output = new_output;
05448                     }
05449                     else
05450                         cpl_error_reset();
05451 
05452 
05453                     cpl_polynomial_delete(ids);
05454 
05455                     countLines = cpl_bivector_get_size(output);
05456 
05457                     if (countLines < 4) {
05458                         cpl_bivector_delete(output);
05459                         cpl_vector_delete(peaks);
05460 
05461                         /* 
05462                          * With the following code a decision is taken:
05463                          * if using the first-guess gives no results,
05464                          * then also the "blind" solution is rejected.
05465                          */
05466 
05467                         if (nlines)
05468                             nlines[i] = 0;
05469                         if (error)
05470                             error[i] = 0.0;
05471                         if (idscoeff)
05472                             for (k = 0; k <= order; k++)
05473                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05474                         continue;
05475                     }
05476 
05477                     wavel = cpl_bivector_get_y(output);
05478                     cpl_vector_subtract_scalar(wavel, refwave);
05479 
05480                     uorder = countLines / 2 - 1;
05481                     if (uorder > order)
05482                         uorder = order;
05483 
05484                     ids = mos_poly_wav2pix(output, uorder, reject,
05485                                            2 * (uorder + 1), &usedLines,
05486                                            &ids_err);
05487 
05488                     if (ids == NULL) {
05489                         cpl_bivector_delete(output);
05490                         cpl_vector_delete(peaks);
05491 
05492                         /* 
05493                          * With the following code a decision is taken:
05494                          * if using the first-guess gives no results,
05495                          * then also the "blind" solution is rejected.
05496                          */
05497 
05498                         if (nlines)
05499                             nlines[i] = 0;
05500                         if (error)
05501                             error[i] = 0.0;
05502                         if (idscoeff)
05503                             for (k = 0; k <= order; k++)
05504                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05505                         cpl_error_reset();
05506                         continue;
05507                     }
05508 
05509                     if (idscoeff) {
05510                         for (k = 0; k <= order; k++) {
05511                             if (k > uorder) {
05512                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05513                             }
05514                             else {
05515                                 cpl_table_set_double(idscoeff, clab[k], i,
05516                                             cpl_polynomial_get_coeff(ids, &k));
05517                             }
05518                         }
05519                     }
05520 
05521                 } /* End of "use ids as a first-guess" */
05522 
05523                 if (nlines)
05524                     nlines[i] = usedLines;
05525                 if (error)
05526                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05527 
05528                 pixstart = cpl_polynomial_eval_1d(ids, 
05529                     cpl_bivector_get_y_data(output)[0], NULL);
05530                 pixend = cpl_polynomial_eval_1d(ids,
05531                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05532                 extrapolation = (pixend - pixstart) / 5;
05533                 pixstart -= extrapolation;
05534                 pixend += extrapolation;
05535                 if (pixstart < 0)
05536                     pixstart = 0;
05537                 if (pixend > nx)
05538                     pixend = nx;
05539 
05540                 /*
05541                  * Wavelength calibrated image (if requested):
05542                  */
05543 
05544                 if (calibration) {
05545                     for (j = pixstart; j < pixend; j++) {
05546                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05547                                                          lastLambda, refwave, 
05548                                                          j);
05549                     }
05550                 }
05551 
05552                 /*
05553                  * Resampled image:
05554                  */
05555 
05556                 for (j = 0; j < nl; j++) {
05557                     lambda = firstLambda + j * dispersion;
05558                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05559                                                     NULL);
05560                     pixel = fpixel;
05561                     if (pixel >= 0 && pixel < nx-1) {
05562                         v1 = (sdata + i*nx)[pixel];
05563                         v2 = (sdata + i*nx)[pixel+1];
05564                         vi = v1 + (v2-v1)*(fpixel-pixel);
05565                         (rdata + i*nl)[j] = vi;
05566                     }
05567                 }
05568 
05569                 /*
05570                  * Residuals image
05571                  */
05572 
05573                 if (residuals || (restable && !(i%step))) {
05574                     if (restable && !(i%step)) {
05575                         lin = cpl_polynomial_new(1);
05576                         for (k = 0; k < 2; k++)
05577                             cpl_polynomial_set_coeff(lin, &k, 
05578                                           cpl_polynomial_get_coeff(ids, &k));
05579                     }
05580                     for (j = 0; j < countLines; j++) {
05581                         pixe = cpl_bivector_get_x_data(output)[j];
05582                         wave = cpl_bivector_get_y_data(output)[j];
05583                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05584                         if (residuals) {
05585                             pixel = pixe + 0.5;
05586                             (ddata + i*nx)[pixel] = value;
05587                         }
05588                         if (restable && !(i%step)) {
05589                             for (k = 0; k < nref; k++) {
05590                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05591                                     snprintf(name, MAX_COLNAME, "r%d", i);
05592                                     cpl_table_set_double(restable, name, 
05593                                                          k, value);
05594                                     value = pixe
05595                                           - cpl_polynomial_eval_1d(lin, wave,
05596                                                                    NULL);
05597                                     snprintf(name, MAX_COLNAME, "d%d", i);
05598                                     cpl_table_set_double(restable, name, 
05599                                                          k, value);
05600                                     snprintf(name, MAX_COLNAME, "p%d", i);
05601                                     cpl_table_set_double(restable, name,
05602                                                          k, pixe);
05603                                     break;
05604                                 }
05605                             }
05606                         }
05607                     }
05608                     if (restable && !(i%step)) {
05609                         cpl_polynomial_delete(lin);
05610                     }
05611                 }
05612 
05613                 /*
05614                  * Mask at reference wavelength
05615                  */
05616 
05617                 if (refmask) {
05618                     mdata = cpl_mask_get_data(refmask);
05619                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05620                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05621                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05622                         mdata[pixel + i*nx] = CPL_BINARY_1;
05623                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05624                     }
05625                 }
05626 
05627                 cpl_polynomial_delete(ids);
05628                 cpl_bivector_delete(output);
05629             }
05630             cpl_vector_delete(peaks);
05631         }
05632     }
05633 
05634     if (refmask) {
05635         kernel = cpl_matrix_new(3, 3);
05636         cpl_matrix_set(kernel, 0, 1, 1.0);
05637         cpl_matrix_set(kernel, 1, 1, 1.0);
05638         cpl_matrix_set(kernel, 2, 1, 1.0);
05639 
05640         cpl_mask_dilation(refmask, kernel);
05641         cpl_mask_erosion(refmask, kernel);
05642         cpl_mask_erosion(refmask, kernel);
05643         cpl_mask_dilation(refmask, kernel);
05644 
05645         cpl_matrix_delete(kernel);
05646 
05647         /*
05648          *  Fill possible gaps
05649          */
05650 
05651         mdata = cpl_mask_get_data(refmask);
05652         have_it = cpl_calloc(ny, sizeof(int));
05653 
05654         for (i = 0; i < ny; i++, mdata += nx) {
05655             for (j = 0; j < nx; j++) {
05656                 if (mdata[j] == CPL_BINARY_1) {
05657                     have_it[i] = j;
05658                     break;
05659                 }
05660             }
05661         }
05662 
05663         mdata = cpl_mask_get_data(refmask);
05664         in = 0;
05665         first = last = 0;
05666 
05667         for (i = 0; i < ny; i++) {
05668             if (have_it[i]) {
05669                 if (!in) {
05670                     in = 1;
05671                     if (first) {
05672                         last = i;
05673                         if (abs(have_it[first] - have_it[last]) < 3) {
05674                             for (j = first; j < last; j++) {
05675                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05676                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05677                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05678                             }
05679                         }
05680                     }
05681                 }
05682             }
05683             else {
05684                 if (in) {
05685                     in = 0;
05686                     first = i - 1;
05687                 }
05688             }
05689         }
05690 
05691         cpl_free(have_it);
05692 
05693     }
05694 
05695 /*
05696     for (i = 0; i < ny; i++) {
05697         if (nlines[i] == 0) {
05698             for (k = 0; k <= order; k++) {
05699                 cpl_table_set_invalid(idscoeff, clab[k], i);
05700             }
05701         }
05702     }
05703 */
05704 
05705     return resampled;
05706 }
05707 
05708 
05730 /*
05731 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05732 {
05733     const char *func = "mos_locate_spectra_bis";
05734 
05735     cpl_apertures    *slits;
05736     cpl_image        *labimage;
05737     cpl_image        *refimage;
05738     cpl_binary       *mdata;
05739     cpl_table        *slitpos;
05740     cpl_propertylist *sort_col;
05741     int               nslits;
05742     int              *have_it;
05743     int               in, first, last;
05744     int               i, j;
05745 
05746 
05747     if (mask == NULL) {
05748         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05749         return NULL;
05750     }
05751 
05752     nx = cpl_mask_get_size_x(mask);
05753     ny = cpl_mask_get_size_y(mask);
05754 
05755     mdata = cpl_mask_get_data(refmask);
05756     have_it = cpl_calloc(ny, sizeof(int));
05757 
05758     for (i = 0; i < ny; i++, mdata += nx) {
05759         for (j = 0; j < nx; j++) {
05760             if (mdata[j] == CPL_BINARY_1) {
05761                 have_it[i] = j + 1;
05762                 break;
05763             }
05764         }
05765     }
05766 
05767     mdata = cpl_mask_get_data(refmask);
05768     in = 0;
05769     first = last = 0;
05770     nslits = 0;
05771 
05772     for (i = 0; i < ny; i++) {
05773         if (have_it[i]) {
05774             if (in) {
05775                 if (i) {
05776                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05777                         nslits++;
05778                     }
05779                 }
05780             }
05781             else {
05782                 in = 1;
05783                 nslits++;
05784             }
05785         }
05786         else {
05787             if (in) {
05788                 in = 0;
05789             }
05790         }
05791     }
05792 }
05793 */
05794 
05795 
05817 cpl_table *mos_locate_spectra(cpl_mask *mask)
05818 {
05819     const char *func = "mos_locate_spectra";
05820 
05821     cpl_apertures    *slits;
05822     cpl_image        *labimage;
05823     cpl_image        *refimage;
05824     cpl_table        *slitpos;
05825     cpl_propertylist *sort_col;
05826     cpl_size          nslits;
05827     int               i;
05828 
05829 
05830     if (mask == NULL) {
05831         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05832         return NULL;
05833     }
05834 
05835     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05836 
05837     if (nslits < 1) {
05838         cpl_image_delete(labimage);
05839         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05840         return NULL;
05841     }
05842 
05843     refimage = cpl_image_new_from_mask(mask);
05844 
05845     slits = cpl_apertures_new_from_image(refimage, labimage);
05846 
05847     cpl_image_delete(labimage);
05848     cpl_image_delete(refimage);
05849 
05850     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05851     if (nslits < 1) {
05852         cpl_apertures_delete(slits);
05853         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05854         return NULL;
05855     }
05856 
05857     slitpos = cpl_table_new(nslits);
05858     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05859     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05860     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05861     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05862     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05863     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05864     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05865     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05866 
05867     for (i = 0; i < nslits; i++) {
05868         cpl_table_set_double(slitpos, "xtop", i, 
05869                              cpl_apertures_get_top_x(slits, i+1) - 1);
05870         cpl_table_set_double(slitpos, "ytop", i, 
05871                              cpl_apertures_get_top(slits, i+1));
05872         cpl_table_set_double(slitpos, "xbottom", i, 
05873                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05874         cpl_table_set_double(slitpos, "ybottom", i, 
05875                              cpl_apertures_get_bottom(slits, i+1));
05876     }
05877 
05878     cpl_apertures_delete(slits);
05879 
05880     sort_col = cpl_propertylist_new();
05881     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05882     cpl_table_sort(slitpos, sort_col);
05883     cpl_propertylist_delete(sort_col);
05884 
05885     return slitpos;
05886 
05887 }
05888 
05889 
05905 cpl_error_code mos_validate_slits(cpl_table *slits) 
05906 {
05907     const char *func = "mos_validate_slits";
05908 
05909 
05910     if (slits == NULL)
05911         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05912 
05913     if (1 != cpl_table_has_column(slits, "xtop"))
05914         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05915 
05916     if (1 != cpl_table_has_column(slits, "ytop"))
05917         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05918 
05919     if (1 != cpl_table_has_column(slits, "xbottom"))
05920         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05921 
05922     if (1 != cpl_table_has_column(slits, "ybottom"))
05923         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05924 
05925     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05926         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05927 
05928     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05929         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05930 
05931     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05932         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05933 
05934     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05935         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05936 
05937     return CPL_ERROR_NONE;
05938 }
05939 
05940 
05969 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05970 {
05971     const char *func = "mos_rotate_slits";
05972 
05973     cpl_error_code error;
05974     char aux_name[] = "_0";
05975     int i;
05976 
05977 
05978     rotation %= 4;
05979     if (rotation < 0)
05980         rotation += 4;
05981 
05982     if (rotation == 0)
05983         return CPL_ERROR_NONE;
05984 
05985     error = mos_validate_slits(slits);
05986     if (error)
05987         return cpl_error_set(func, error);
05988 
05989     if (rotation == 1 || rotation == 3) {
05990 
05991         /*
05992          * Swap x and y column names
05993          */
05994 
05995         for (i = 0; i < 77; i++)
05996             if (1 == cpl_table_has_column(slits, aux_name))
05997                 aux_name[1]++;
05998         if (1 == cpl_table_has_column(slits, aux_name))
05999             return cpl_error_set(func, CPL_ERROR_CONTINUE);
06000         cpl_table_name_column(slits, "xtop", aux_name);
06001         cpl_table_name_column(slits, "ytop", "xtop");
06002         cpl_table_name_column(slits, aux_name, "ytop");
06003         cpl_table_name_column(slits, "xbottom", aux_name);
06004         cpl_table_name_column(slits, "ybottom", "xbottom");
06005         cpl_table_name_column(slits, aux_name, "ybottom");
06006     }
06007 
06008     if (rotation == 1 || rotation == 2) {
06009         cpl_table_multiply_scalar(slits, "xtop", -1.0);
06010         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
06011         cpl_table_add_scalar(slits, "xtop", nx);
06012         cpl_table_add_scalar(slits, "xbottom", nx);
06013     }
06014 
06015     if (rotation == 3 || rotation == 2) {
06016         cpl_table_multiply_scalar(slits, "ytop", -1.0);
06017         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
06018         cpl_table_add_scalar(slits, "ytop", ny);
06019         cpl_table_add_scalar(slits, "ybottom", ny);
06020     }
06021 
06022     return CPL_ERROR_NONE;
06023 }
06024 
06025 
06083 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
06084                               cpl_table *global)
06085 {
06086     cpl_array        *top_ident = NULL;;
06087     cpl_array        *bot_ident = NULL;;
06088     cpl_matrix       *mdata;
06089     cpl_matrix       *mpattern;
06090     cpl_matrix       *top_data;
06091     cpl_matrix       *top_pattern;
06092     cpl_matrix       *top_mdata;
06093     cpl_matrix       *top_mpattern;
06094     cpl_matrix       *bot_data;
06095     cpl_matrix       *bot_pattern;
06096     cpl_matrix       *bot_mdata;
06097     cpl_matrix       *bot_mpattern;
06098     cpl_propertylist *sort_col;
06099     double           *xtop;
06100     double           *ytop;
06101     double           *xmtop;
06102     double           *ymtop;
06103     double           *xbot;
06104     double           *ybot;
06105     double           *xmbot;
06106     double           *ymbot;
06107     double            top_scale, bot_scale;
06108     double            angle, top_angle, bot_angle;
06109     double            xmse, ymse;
06110     double            xrms, top_xrms, bot_xrms;
06111     double            yrms, top_yrms, bot_yrms;
06112     int               nslits;
06113     int               nmaskslits, use_pattern;
06114     int               found_slits, found_slits_top, found_slits_bot;
06115     int               degree;
06116     int               i;
06117     cpl_table        *positions;
06118     cpl_error_code    error;
06119 
06120     cpl_vector       *point;
06121     double           *dpoint;
06122     cpl_vector       *xpos;
06123     cpl_vector       *ypos;
06124     cpl_vector       *xmpos;
06125     cpl_vector       *ympos;
06126     cpl_bivector     *mpos;
06127     cpl_polynomial   *xpoly     = NULL;
06128     cpl_polynomial   *ypoly     = NULL;
06129     cpl_polynomial   *top_xpoly = NULL;
06130     cpl_polynomial   *top_ypoly = NULL;
06131     cpl_polynomial   *bot_xpoly = NULL;
06132     cpl_polynomial   *bot_ypoly = NULL;
06133 
06134     char *msg_multiplex = " ";
06135 
06136 
06137     error = mos_validate_slits(slits);
06138     if (error) {
06139         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
06140                       cpl_error_get_message());
06141         cpl_error_set(cpl_func, error);
06142         return NULL;
06143     }
06144 
06145     error = mos_validate_slits(maskslits);
06146     if (error) {
06147         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
06148                       cpl_error_get_message());
06149         cpl_error_set(cpl_func, error);
06150         return NULL;
06151     }
06152 
06153     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06154         cpl_msg_error(cpl_func, "Missing slits identifiers");
06155         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
06156         return NULL;
06157     }
06158 
06159     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06160         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
06161         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
06162         return NULL;
06163     }
06164 
06165     nslits = cpl_table_get_nrow(slits);
06166     nmaskslits = cpl_table_get_nrow(maskslits);
06167 
06168     if (nslits == 0 || nmaskslits == 0) {
06169         cpl_msg_error(cpl_func, "Empty slits table");
06170         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
06171         return NULL;
06172     }
06173 
06174     if (nslits > 25 && mos_multiplex < 0) {
06175         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
06176         positions = mos_identify_slits_fast(slits, maskslits, global);
06177         if (positions == NULL)
06178             cpl_error_set_where(cpl_func);
06179         return positions;
06180     }
06181 
06182     /*
06183      * Guarantee that both input tables are sorted in the same way
06184      */
06185 
06186     sort_col = cpl_propertylist_new();
06187     cpl_propertylist_append_bool(sort_col, "ytop", 1);
06188     cpl_table_sort(slits, sort_col);
06189     cpl_table_sort(maskslits, sort_col);
06190     cpl_propertylist_delete(sort_col);
06191 
06192     /*
06193      * First we handle all the special cases (too few slits...)
06194      */
06195 
06196     if (nslits < 3 && nmaskslits > nslits) {
06197 
06198         /*
06199          * If there are just 1 or 2 slits on the CCD, and more on the
06200          * mask, the ambiguity cannot be solved, and an error is returned.
06201          * This is a case that must be solved with a first-guess relation
06202          * between mask and CCD.
06203          */
06204 
06205         if (nslits > 1)
06206             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
06207                             "with the %d mask slits: process will continue "
06208                             "using the detected CCD slits positions", nslits,
06209                             nmaskslits);
06210         else
06211             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
06212                             "the %d mask slits: process will continue using "
06213                             "the detected CCD slit position", nmaskslits);
06214         return NULL;
06215     }
06216 
06217     if (nmaskslits < 3 && nslits > nmaskslits) {
06218 
06219         /*
06220          * If there are less than 3 slits on the mask the ambiguity cannot
06221          * be solved, and an error is returned. This is a case that must
06222          * be solved with a first-guess relation between mask and CCD.
06223          */
06224 
06225         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
06226                         "the %d mask slits: process will continue using "
06227                         "the detected CCD slits positions", nslits,
06228                         nmaskslits);
06229         return NULL;
06230     }
06231 
06232     /*
06233      * Pattern matching related operations begin here. Two pattern
06234      * matching will be run, one based on the "top" and another one
06235      * based on the "bottom" slit coordinates. The one with the
06236      * smallest rms in the Y coordinate will be chosen.
06237      */
06238 
06239     xtop  = cpl_table_get_data_double(slits, "xtop");
06240     ytop  = cpl_table_get_data_double(slits, "ytop");
06241     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06242     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06243 
06244     xbot  = cpl_table_get_data_double(slits, "xbottom");
06245     ybot  = cpl_table_get_data_double(slits, "ybottom");
06246     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06247     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06248 
06249     top_data    = cpl_matrix_new(2, nslits);
06250     top_pattern = cpl_matrix_new(2, nmaskslits);
06251     bot_data    = cpl_matrix_new(2, nslits);
06252     bot_pattern = cpl_matrix_new(2, nmaskslits);
06253 
06254     for (i = 0; i < nslits; i++)
06255         cpl_matrix_set(top_data, 0, i, xtop[i]);
06256 
06257     for (i = 0; i < nslits; i++)
06258         cpl_matrix_set(top_data, 1, i, ytop[i]);
06259 
06260     for (i = 0; i < nmaskslits; i++)
06261         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06262 
06263     for (i = 0; i < nmaskslits; i++)
06264         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06265 
06266     for (i = 0; i < nslits; i++)
06267         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06268 
06269     for (i = 0; i < nslits; i++)
06270         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06271 
06272     for (i = 0; i < nmaskslits; i++)
06273         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06274 
06275     for (i = 0; i < nmaskslits; i++)
06276         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06277 
06278     if (nmaskslits > nslits)
06279         use_pattern = nslits;
06280     else
06281         use_pattern = nmaskslits;
06282 
06283     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06284                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06285                                      &top_mpattern, &top_scale, &top_angle);
06286 
06287     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06288                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06289                                      &bot_mpattern, &bot_scale, &bot_angle);
06290 
06291     if (top_ident == NULL && bot_ident == NULL) {
06292         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06293                         "the %d found CCD slits with the %d mask slits: "
06294                         "process will continue using the detected CCD "
06295                         "slits positions", nslits, nmaskslits);
06296         return NULL;
06297     }
06298 
06299     found_slits_top = 0;
06300     found_slits_bot = 0;
06301     if (top_ident && bot_ident) {
06302         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06303                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06304         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06305                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06306         if (fabs(top_angle) < fabs(bot_angle))
06307             angle = fabs(top_angle);
06308         else
06309             angle = fabs(bot_angle);
06310         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06311         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06312     }
06313     else if (top_ident) {
06314         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06315         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06316         angle = fabs(top_angle);
06317         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06318     }
06319     else {
06320         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06321         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06322         angle = fabs(bot_angle);
06323         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06324     }
06325 
06326     cpl_array_delete(top_ident);
06327     cpl_array_delete(bot_ident);
06328 
06329     if (angle > 4.0) {
06330         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06331                         "angle is expected to be around zero. This match is "
06332                         "rejected: the process will continue using the %d "
06333                         "detected CCD slits positions", nslits);
06334         return NULL;
06335     }
06336 
06337     found_slits = found_slits_top;
06338     if (found_slits < found_slits_bot)
06339         found_slits = found_slits_bot;     /* Max value */
06340 
06341     if (found_slits < 4) {
06342         cpl_msg_warning(cpl_func,
06343                         "Too few safely identified slits: %d out of %d "
06344                         "candidates (%d expected). Process will continue "
06345                         "using the detected CCD slits positions", found_slits,
06346                         nslits, nmaskslits);
06347         return NULL;
06348     }
06349 
06350     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06351                  "candidates\n(%d expected)", found_slits, nslits,
06352                  nmaskslits);
06353 
06354     if (found_slits_top < 4)
06355         found_slits_top = 0;
06356 
06357     if (found_slits_bot < 4)
06358         found_slits_bot = 0;
06359 
06360     /*
06361      * Now for each set select the points of the identified slits, and fit
06362      * two bivariate polynomials to determine a first approximate relation
06363      * between positions on the mask and positions on the CCD.
06364      */
06365 
06366     for (i = 0; i < 2; i++) {
06367         if (i) {
06368             found_slits = found_slits_top;
06369             mdata = top_mdata;
06370             mpattern = top_mpattern;
06371         }
06372         else {
06373             found_slits = found_slits_bot;
06374             mdata = bot_mdata;
06375             mpattern = bot_mpattern;
06376         }
06377 
06378         if (found_slits == 0)
06379             continue;
06380         else if (found_slits < 10)
06381             degree = 1;
06382         else
06383             degree = 2;
06384 
06385         xpos  = cpl_vector_wrap(found_slits,
06386                                 cpl_matrix_get_data(mdata)              );
06387         ypos  = cpl_vector_wrap(found_slits,
06388                                 cpl_matrix_get_data(mdata) + found_slits);
06389         xmpos = cpl_vector_wrap(found_slits,
06390                                 cpl_matrix_get_data(mpattern)              );
06391         ympos = cpl_vector_wrap(found_slits,
06392                                 cpl_matrix_get_data(mpattern) + found_slits);
06393         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06394         xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06395         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06396 
06397         cpl_bivector_unwrap_vectors(mpos);
06398         cpl_vector_unwrap(xpos);
06399         cpl_vector_unwrap(ypos);
06400         cpl_vector_unwrap(xmpos);
06401         cpl_vector_unwrap(ympos);
06402         cpl_matrix_delete(mdata);
06403         cpl_matrix_delete(mpattern);
06404 
06405         if (i) {
06406             top_xpoly = xpoly;
06407             top_ypoly = ypoly;
06408             top_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06409             top_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06410         }
06411         else {
06412             bot_xpoly = xpoly;
06413             bot_ypoly = ypoly;
06414             bot_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06415             bot_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06416         }
06417     }
06418 
06419     if (top_xpoly && bot_xpoly) {
06420         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06421             xrms = top_xrms;
06422             xpoly = top_xpoly;
06423             cpl_polynomial_delete(bot_xpoly);
06424         }
06425         else {                      /* bottom X solution wins... */
06426             xrms = bot_xrms;
06427             xpoly = bot_xpoly;
06428             cpl_polynomial_delete(top_xpoly);
06429         }
06430     }
06431     else if (top_xpoly) {
06432         xrms = top_xrms;
06433         xpoly = top_xpoly;
06434     }
06435     else {
06436         xrms = bot_xrms;
06437         xpoly = bot_xpoly;
06438     }
06439 
06440     if (top_ypoly && bot_ypoly) {
06441         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06442             yrms = top_yrms;
06443             ypoly = top_ypoly;
06444             cpl_polynomial_delete(bot_ypoly);
06445         }
06446         else {                      /* bottom Y solution wins... */
06447             yrms = bot_yrms;
06448             ypoly = bot_ypoly;
06449             cpl_polynomial_delete(top_ypoly);
06450         }
06451     }
06452     else if (top_ypoly) {
06453         yrms = top_yrms;
06454         ypoly = top_ypoly;
06455     }
06456     else {
06457         yrms = bot_yrms;
06458         ypoly = bot_ypoly;
06459     }
06460 
06461     if (xpoly == NULL || ypoly == NULL) {
06462         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06463                         "identified slits positions cannot be improved.");
06464         cpl_polynomial_delete(xpoly);
06465         cpl_polynomial_delete(ypoly);
06466         cpl_error_reset();
06467         return NULL;
06468     }
06469 
06470     cpl_msg_info(cpl_func,
06471                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06472                  xrms, yrms);
06473 
06474     if (global) {
06475         write_global_distortion(global, 0, xpoly);
06476         write_global_distortion(global, 7, ypoly);
06477     }
06478 
06479     /*
06480      * The fit was successful: use the polynomials to obtain a new
06481      * position table with the improved positions of the slits
06482      */
06483 
06484     positions = cpl_table_duplicate(maskslits);
06485     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06486     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06487     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06488     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06489 
06490     point = cpl_vector_new(2);
06491     dpoint = cpl_vector_get_data(point);
06492 
06493     for (i = 0; i < nmaskslits; i++) {
06494         double position;
06495 
06496         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06497         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06498         position  = cpl_polynomial_eval(xpoly, point);
06499 //        if (mos_multiplex >= 0) {
06500 //            if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
06501 //                cpl_table_unselect_row(positions, i);
06502 //                continue;
06503 //            }
06504 //        }
06505         cpl_table_set_double(positions, "xtop", i, position);
06506         position  = cpl_polynomial_eval(ypoly, point);
06507         cpl_table_set_double(positions, "ytop", i, position);
06508         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06509         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06510         position  = cpl_polynomial_eval(xpoly, point);
06511         cpl_table_set_double(positions, "xbottom", i, position);
06512         position  = cpl_polynomial_eval(ypoly, point);
06513         cpl_table_set_double(positions, "ybottom", i, position);
06514     }
06515 
06516 //    if (mos_multiplex >= 0) {
06517 //        cpl_table_not_selected(positions);
06518 //        cpl_table_erase_selected(positions);
06519 //        nmaskslits = cpl_table_get_nrow(positions);
06520 //    }
06521 
06522     cpl_vector_delete(point);
06523     cpl_polynomial_delete(xpoly);
06524     cpl_polynomial_delete(ypoly);
06525 
06526     cpl_table_erase_column(positions, "xmtop");
06527     cpl_table_erase_column(positions, "ymtop");
06528     cpl_table_erase_column(positions, "xmbottom");
06529     cpl_table_erase_column(positions, "ymbottom");
06530 
06531     if (mos_multiplex >= 0) {
06532         msg_multiplex = 
06533         cpl_sprintf("in the CCD section between %d and %d pixel", 
06534                     mos_multiplex * mos_region_size, 
06535                     (mos_multiplex + 1) * mos_region_size);
06536     }
06537 
06538     if (nmaskslits > nslits)
06539         cpl_msg_info(cpl_func,
06540                      "Finally identified slits: %d out of %d expected %s\n"
06541                      "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
06542                      nmaskslits - nslits);
06543     else if (nmaskslits < nslits)
06544         cpl_msg_info(cpl_func,
06545                      "Finally identified slits: %d out of %d expected %s\n"
06546                      "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
06547                      nslits - nmaskslits);
06548     else
06549         cpl_msg_info(cpl_func,
06550                      "Finally identified slits: %d out of %d expected %s",
06551                      nmaskslits, nmaskslits, msg_multiplex);
06552 
06553     if (mos_multiplex >= 0) {
06554         cpl_free(msg_multiplex);
06555     }
06556 
06557     return positions;
06558 
06559 }
06560 
06561 
06562 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06563                                    cpl_table *global)
06564 {
06565     const char *func = "mos_identify_slits_fast";
06566 
06567     cpl_propertylist *sort_col;
06568     cpl_table        *positions;
06569     cpl_vector       *scales;
06570     cpl_vector       *angles;
06571     cpl_vector       *point;
06572     cpl_vector       *xpos;
06573     cpl_vector       *ypos;
06574     cpl_vector       *xmpos;
06575     cpl_vector       *ympos;
06576     cpl_bivector     *mpos;
06577     cpl_polynomial   *xpoly = NULL;
06578     cpl_polynomial   *ypoly = NULL;
06579     cpl_error_code    error;
06580     int nslits;
06581     int nmaskslits;
06582     int found_slits;
06583     int i, j, k;
06584 
06585     double  dist1, dist2, dist3, dist, mindist;
06586     double  scale, minscale, maxscale;
06587     double  angle, minangle, maxangle;
06588     double *dscale;
06589     double *dangle;
06590     double *dpoint;
06591     double *xtop;
06592     double *ytop;
06593     double *xbottom;
06594     double *ybottom;
06595     double *xcenter;
06596     double *ycenter;
06597     double *xpseudo;
06598     double *ypseudo;
06599     int    *slit_id;
06600     double *xmtop;
06601     double *ymtop;
06602     double *xmbottom;
06603     double *ymbottom;
06604     double *xmcenter;
06605     double *ymcenter;
06606     double *xmpseudo;
06607     double *ympseudo;
06608     double  xmse, ymse;
06609     int    *mslit_id;
06610     int    *good;
06611     int     minpos;
06612     int     degree;
06613 
06614     double  sradius = 0.01;   /* Candidate input argument... */
06615     int     in_sradius;
06616 
06617     double pi = 3.14159265358979323846;
06618 
06619 
06620     error = mos_validate_slits(slits);
06621     if (error) {
06622         cpl_msg_error(func, "CCD slits table validation: %s", 
06623                       cpl_error_get_message());
06624         cpl_error_set(func, error);
06625         return NULL;
06626     }
06627 
06628     error = mos_validate_slits(maskslits);
06629     if (error) {
06630         cpl_msg_error(func, "Mask slits table validation: %s", 
06631                       cpl_error_get_message());
06632         cpl_error_set(func, error);
06633         return NULL;
06634     }
06635 
06636     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06637         cpl_msg_error(func, "Missing slits identifiers");
06638         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06639         return NULL;
06640     }
06641 
06642     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06643         cpl_msg_error(func, "Wrong type used for slits identifiers");
06644         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06645         return NULL;
06646     }
06647 
06648     nslits = cpl_table_get_nrow(slits);
06649     nmaskslits = cpl_table_get_nrow(maskslits);
06650 
06651     if (nslits == 0 || nmaskslits == 0) {
06652         cpl_msg_error(func, "Empty slits table");
06653         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06654         return NULL;
06655     }
06656 
06657 
06658     /*
06659      * Compute middle point coordinates for each slit listed in both
06660      * input tables.
06661      */
06662 
06663     if (cpl_table_has_column(slits, "xcenter"))
06664         cpl_table_erase_column(slits, "xcenter");
06665 
06666     if (cpl_table_has_column(slits, "ycenter"))
06667         cpl_table_erase_column(slits, "ycenter");
06668 
06669     if (cpl_table_has_column(maskslits, "xcenter"))
06670         cpl_table_erase_column(maskslits, "xcenter");
06671 
06672     if (cpl_table_has_column(maskslits, "ycenter"))
06673         cpl_table_erase_column(maskslits, "ycenter");
06674 
06675     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06676     cpl_table_add_columns(slits, "xcenter", "xbottom");
06677     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06678     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06679     cpl_table_add_columns(slits, "ycenter", "ybottom");
06680     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06681 
06682     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06683     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06684     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06685     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06686     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06687     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06688 
06689 
06690     /*
06691      * Guarantee that both input tables are sorted in the same way
06692      */
06693 
06694     sort_col = cpl_propertylist_new();
06695     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06696     cpl_table_sort(slits, sort_col);
06697     cpl_table_sort(maskslits, sort_col);
06698     cpl_propertylist_delete(sort_col);
06699 
06700 
06701     /*
06702      * First we handle all the special cases (too few slits...)
06703      */
06704 
06705     if (nslits < 3 && nmaskslits > nslits) {
06706 
06707         /*
06708          * If there are just 1 or 2 slits on the CCD, and more on the
06709          * mask, the ambiguity cannot be solved, and an error is returned.
06710          * This is a case that must be solved with a first-guess relation
06711          * between mask and CCD.
06712          */
06713 
06714         if (nslits > 1)
06715             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06716                             "%d mask slits: process will continue using the "
06717                             "detected CCD slit position", nmaskslits);
06718         else
06719             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06720                             "the %d mask slits: process will continue using "
06721                             "the detected CCD slits positions", nslits, 
06722                             nmaskslits);
06723         return NULL;
06724     }
06725 
06726     if (nslits <= 3 && nslits == nmaskslits) {
06727 
06728         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06729         cpl_msg_warning(func, "Their detected positions are left unchanged");
06730 
06731         /*
06732          * If there are just up to 3 slits, both on the mask and on the CCD,
06733          * we can reasonably hope that those slits were found, and accept 
06734          * that their positions on the CCD cannot be improved. We prepare
06735          * therefore an output position table containing the slits with
06736          * their original positions. We can however give an estimate of
06737          * the platescale if there is more than one slit.
06738          */
06739 
06740         positions = cpl_table_duplicate(slits);
06741         cpl_table_erase_column(slits, "xcenter");
06742         cpl_table_erase_column(slits, "ycenter");
06743         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06744         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06745         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06746         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06747         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06748         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06749         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06750         cpl_table_erase_column(maskslits, "xcenter");
06751         cpl_table_erase_column(maskslits, "ycenter");
06752 
06753         if (nslits > 1) {
06754             xcenter = cpl_table_get_data_double(positions, "xcenter");
06755             ycenter = cpl_table_get_data_double(positions, "ycenter");
06756             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06757             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06758 
06759             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06760                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06761             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06762                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06763             scale = sqrt(dist1/dist2);
06764 
06765             if (nslits == 3) {
06766                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06767                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06768                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06769                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06770                 scale += sqrt(dist1/dist2);
06771                 scale /= 2;
06772             }
06773 
06774             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06775         }
06776 
06777         return positions;
06778     }
06779 
06780     if (nmaskslits < 3 && nslits > nmaskslits) {
06781 
06782         /*
06783          * If there are less than 3 slits on the mask the ambiguity cannot 
06784          * be solved, and an error is returned. This is a case that must 
06785          * be solved with a first-guess relation between mask and CCD.
06786          */
06787 
06788         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06789                         "the %d mask slits: process will continue using "
06790                         "the detected CCD slits positions", nslits, 
06791                         nmaskslits);
06792         return NULL;
06793     }
06794 
06795 
06796     /*
06797      * At this point of the program all the region of the plane
06798      * (nslits, nmaskslits) where either or both mask and CCD display
06799      * less than 3 slits are handled in some way. It would be better
06800      * to add in this place a special handling for identifying slits
06801      * in case of a very reduced number of slits (say, below 6).
06802      * It is also clear that if there are many more slits on the
06803      * mask than on the CCD, or many more on the CCD than on the
06804      * mask, something went deeply wrong with the preliminary 
06805      * wavelength calibration. Such cases should be handled with
06806      * a _complete_ pattern-recognition algorithm based on the
06807      * construction of all possible triangles. For the moment, 
06808      * we go directly to the limited pattern-recognition applied
06809      * below, based on triangles build only for consecutive slits.
06810      * This is reasonably safe, since the preliminary wavelength
06811      * calibration performed by mos_identify_peaks() is generally
06812      * robust.
06813      */
06814 
06815 
06816     /*
06817      * Compute (X, Y) coordinates on pseudo-plane describing the
06818      * different position ratios of successive slits, in both
06819      * input tables.
06820      */
06821 
06822     if (cpl_table_has_column(slits, "xpseudo"))
06823         cpl_table_erase_column(slits, "xpseudo");
06824 
06825     if (cpl_table_has_column(slits, "ypseudo"))
06826         cpl_table_erase_column(slits, "ypseudo");
06827 
06828     if (cpl_table_has_column(maskslits, "xpseudo"))
06829         cpl_table_erase_column(maskslits, "xpseudo");
06830 
06831     if (cpl_table_has_column(maskslits, "ypseudo"))
06832         cpl_table_erase_column(maskslits, "ypseudo");
06833 
06834     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06835     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06836 
06837     xcenter = cpl_table_get_data_double(slits, "xcenter");
06838     ycenter = cpl_table_get_data_double(slits, "ycenter");
06839     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06840     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06841 
06842     for (i = 1; i < nslits - 1; i++) {
06843         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06844               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06845         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06846               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06847         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06848               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06849         xpseudo[i] = sqrt(dist1/dist2);
06850         ypseudo[i] = sqrt(dist3/dist2);
06851     }
06852 
06853     cpl_table_set_invalid(slits, "xpseudo", 0);
06854     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06855     cpl_table_set_invalid(slits, "ypseudo", 0);
06856     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06857 
06858     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06859     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06860 
06861     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06862     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06863     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06864     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06865 
06866     for (i = 1; i < nmaskslits - 1; i++) {
06867         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06868               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06869         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06870               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06871         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06872               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06873         xmpseudo[i] = sqrt(dist1/dist2);
06874         ympseudo[i] = sqrt(dist3/dist2);
06875     }
06876     
06877     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06878     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06879     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06880     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06881 
06882 
06883     /*
06884      * For each (X, Y) on the pseudo-plane related to the mask positions,
06885      * find the closest (X, Y) on the pseudo-plane related to the CCD
06886      * positions. If the closest point is closer than a given search
06887      * radius, a triangle has been found, and 3 slits are identified.
06888      * However, if more than one point is found within the search
06889      * radius, we have an ambiguity and this is rejected.
06890      */
06891 
06892     if (cpl_table_has_column(slits, "slit_id"))
06893         cpl_table_erase_column(slits, "slit_id");
06894     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06895     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06896 
06897     for (i = 1; i < nmaskslits - 1; i++) {
06898         in_sradius = 0;
06899         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06900                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06901         minpos = 1;
06902         if (mindist < sradius*sradius)
06903             in_sradius++;
06904         for (j = 2; j < nslits - 1; j++) {
06905             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06906                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06907             if (dist < sradius*sradius)
06908                 in_sradius++;
06909             if (in_sradius > 1)    /* More than one triangle within radius */
06910                 break;
06911             if (mindist > dist) {
06912                 mindist = dist;
06913                 minpos = j;
06914             }
06915         }
06916 
06917         mindist = sqrt(mindist);
06918 
06919         if (mindist < sradius && in_sradius == 1) {
06920             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06921             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06922             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06923         }
06924     }
06925 
06926 
06927     /*
06928      * At this point, the slit_id column contains invalid elements 
06929      * corresponding to unidentified slits.
06930      */
06931 
06932     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06933 
06934     if (found_slits < 3) {
06935         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06936                         "%d out of %d", found_slits, nslits);
06937         if (nslits == nmaskslits) {
06938             cpl_msg_warning(func, "(this is not an error, it could be caused "
06939                             "by a mask with regularly located slits)");
06940             cpl_msg_warning(func, "The detected slits positions are left "
06941                             "unchanged");
06942 
06943             /*
06944              * If there are less than 3 identified slits, this is probably 
06945              * a mask with regularly spaced slits (leading to an ambiguous
06946              * pattern). Only in the case all expected slits appear to have 
06947              * been found on the CCD we can proceed...
06948              */
06949 
06950             cpl_table_erase_column(slits, "slit_id");
06951             cpl_table_erase_column(slits, "xpseudo");
06952             cpl_table_erase_column(slits, "ypseudo");
06953             positions = cpl_table_duplicate(slits);
06954             cpl_table_erase_column(slits, "xcenter");
06955             cpl_table_erase_column(slits, "ycenter");
06956 
06957             cpl_table_erase_column(maskslits, "xpseudo");
06958             cpl_table_erase_column(maskslits, "ypseudo");
06959             cpl_table_duplicate_column(positions, "xmtop", 
06960                                        maskslits, "xtop");
06961             cpl_table_duplicate_column(positions, "ymtop", 
06962                                        maskslits, "ytop");
06963             cpl_table_duplicate_column(positions, "xmbottom", 
06964                                        maskslits, "xbottom");
06965             cpl_table_duplicate_column(positions, "ymbottom", 
06966                                        maskslits, "ybottom");
06967             cpl_table_duplicate_column(positions, "xmcenter", 
06968                                        maskslits, "xcenter");
06969             cpl_table_duplicate_column(positions, "ymcenter", 
06970                                        maskslits, "ycenter");
06971             cpl_table_duplicate_column(positions, "slit_id", 
06972                                        maskslits, "slit_id");
06973             cpl_table_erase_column(maskslits, "xcenter");
06974             cpl_table_erase_column(maskslits, "ycenter");
06975             return positions;
06976         }
06977         else {
06978             cpl_table_erase_column(slits, "slit_id");
06979             cpl_table_erase_column(slits, "xpseudo");
06980             cpl_table_erase_column(slits, "ypseudo");
06981             positions = cpl_table_duplicate(slits);
06982             cpl_table_erase_column(slits, "xcenter");
06983             cpl_table_erase_column(slits, "ycenter");
06984             cpl_msg_warning(func, "(the failure could be caused "
06985                             "by a mask with regularly located slits)");
06986             return NULL;
06987         }
06988     }
06989     else {
06990         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06991                      "candidates (%d expected)", found_slits, nslits, 
06992                      nmaskslits);
06993     }
06994 
06995 
06996     /*
06997      * Create a table with the coordinates of the preliminarily identified 
06998      * slits, both on CCD and mask. The original order of the slits positions 
06999      * is preserved.
07000      */
07001 
07002     positions = cpl_table_new(found_slits);
07003     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
07004     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
07005     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
07006     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
07007     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
07008     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
07009     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
07010     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
07011     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
07012     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
07013     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
07014     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
07015     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
07016     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
07017     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
07018 
07019     slit_id = cpl_table_get_data_int   (slits, "slit_id");
07020     xtop    = cpl_table_get_data_double(slits, "xtop");
07021     ytop    = cpl_table_get_data_double(slits, "ytop");
07022     xbottom = cpl_table_get_data_double(slits, "xbottom");
07023     ybottom = cpl_table_get_data_double(slits, "ybottom");
07024     xcenter = cpl_table_get_data_double(slits, "xcenter");
07025     ycenter = cpl_table_get_data_double(slits, "ycenter");
07026 
07027     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
07028     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
07029     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
07030     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
07031     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
07032     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
07033     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
07034 
07035 
07036     /*
07037      * Transferring the valid slits information to the new table.
07038      * Note that invalid elements are coded as 0 in the internal
07039      * buffer, and this is the way they are recognised and excluded.
07040      */
07041 
07042     k = 0;
07043     cpl_table_fill_invalid_int(slits, "slit_id", 0);
07044     for (i = 0; i < nmaskslits; i++) {
07045         for (j = 0; j < nslits; j++) {
07046             if (slit_id[j] == 0)
07047                 continue; /* Skip invalid slit */
07048             if (mslit_id[i] == slit_id[j]) {
07049                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
07050 
07051                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
07052                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
07053                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
07054                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
07055                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
07056                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
07057 
07058                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
07059                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
07060                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
07061                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
07062                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
07063                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
07064 
07065                 k++;
07066 
07067                 break;
07068             }
07069         }
07070     }
07071 
07072     found_slits = k;
07073 
07074     cpl_table_erase_column(slits, "slit_id");
07075     cpl_table_erase_column(slits, "xpseudo");
07076     cpl_table_erase_column(slits, "ypseudo");
07077     cpl_table_erase_column(slits, "xcenter");
07078     cpl_table_erase_column(slits, "ycenter");
07079     cpl_table_erase_column(maskslits, "xpseudo");
07080     cpl_table_erase_column(maskslits, "ypseudo");
07081     cpl_table_erase_column(maskslits, "xcenter");
07082     cpl_table_erase_column(maskslits, "ycenter");
07083 
07084 
07085     /*
07086      * Find the median platescale and rotation angle from the identified 
07087      * slits, and then exclude slits outlaying more than 10% from the 
07088      * median platescale, and more than 2 degrees from the median
07089      * rotation angle.
07090      */
07091 
07092     ytop    = cpl_table_get_data_double(positions, "ytop");
07093     ybottom = cpl_table_get_data_double(positions, "ybottom");
07094     xcenter = cpl_table_get_data_double(positions, "xcenter");
07095     ycenter = cpl_table_get_data_double(positions, "ycenter");
07096     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
07097     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
07098 
07099     scales = cpl_vector_new(found_slits - 1);
07100     dscale = cpl_vector_get_data(scales);
07101     angles = cpl_vector_new(found_slits - 1);
07102     dangle = cpl_vector_get_data(angles);
07103 
07104     for (i = 1; i < found_slits; i++) {
07105         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
07106               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
07107         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
07108               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
07109         dscale[i-1] = sqrt(dist1/dist2);
07110         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
07111                             xcenter[i-1] - xcenter[i])
07112                     - atan2(ymcenter[i-1] - ymcenter[i], 
07113                             xmcenter[i-1] - xmcenter[i]);
07114         dangle[i-1] *= 180;
07115         dangle[i-1] /= pi;
07116     }
07117 
07118     minscale = cpl_vector_get_min(scales);
07119     scale = cpl_vector_get_median_const(scales);
07120     maxscale = cpl_vector_get_max(scales);
07121 
07122     minangle = cpl_vector_get_min(angles);
07123     angle = cpl_vector_get_median_const(angles);
07124     maxangle = cpl_vector_get_max(angles);
07125 
07126     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
07127     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
07128                  minscale, maxscale);
07129 
07130     cpl_msg_info(func, "Median rotation: %f degrees", angle);
07131     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
07132                  minangle, maxangle);
07133 
07134     good = cpl_table_get_data_int(positions, "good");
07135 
07136     good[0] = good[found_slits - 1] = 1;
07137     for (i = 1; i < found_slits; i++) {
07138         if (fabs((dscale[i-1] - scale)/scale) < 0.10
07139          && fabs(dangle[i-1] - angle) < 2) {
07140             good[i-1]++;
07141             good[i]++;
07142         }
07143     }
07144 
07145     for (i = 0; i < found_slits; i++) {
07146         if (good[i] < 2)
07147             good[i] = 0;
07148         else
07149             good[i] = 1;
07150     }
07151 
07152 /*
07153     for (i = 1; i < found_slits; i++)
07154         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
07155             good[i-1] = good[i] = 1;
07156 */
07157 
07158 /* DEBUG ************+
07159     for (i = 0; i < found_slits; i++) {
07160         if (good[i]) {
07161             if (i == found_slits - 1)
07162                 printf("include slit %d, prev = %f, %f\n", 
07163                        i, dscale[i-1], dangle[i-1]);
07164             else if (i == 0)
07165                 printf("include slit %d, next %f, %f\n", 
07166                        i, dscale[i], dangle[i]);
07167             else
07168                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
07169                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07170         }
07171         else {
07172             if (i == found_slits - 1)
07173                 printf("EXclude slit %d, prev = %f, %f\n", 
07174                        i, dscale[i-1], dangle[i-1]);
07175             else if (i == 0)
07176                 printf("EXclude slit %d, next %f, %f\n", 
07177                        i, dscale[i], dangle[i]);
07178             else
07179                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
07180                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07181         }
07182     }
07183 +*********** DEBUG */
07184 
07185     cpl_vector_delete(scales);
07186     cpl_vector_delete(angles);
07187 
07188     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
07189     cpl_table_erase_selected(positions);
07190     cpl_table_erase_column(positions, "good");
07191     found_slits = cpl_table_get_nrow(positions);
07192 
07193     if (found_slits < 4) {
07194 
07195         /*
07196          * If the self-consistency check gives such a poor result,
07197          * something must have gone really wrong in the preliminary
07198          * wavelength calibration... Nothing can be done.
07199          */
07200 
07201         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
07202                         "candidates (%d expected). Process will continue "
07203                         "using the detected CCD slits positions", found_slits, 
07204                         nslits, nmaskslits);
07205         cpl_table_delete(positions);
07206         return NULL;
07207     }
07208     else {
07209         cpl_msg_info(func, "Safely identified slits: %d out of %d "
07210                      "candidates\n(%d expected)", found_slits, nslits,
07211                      nmaskslits);
07212     }
07213 
07214 
07215     /*
07216      * Now select the central points of the identified slits, and
07217      * fit two bivariate polynomials to determine a first approximate
07218      * relation between positions on the mask and positions on the CCD.
07219      */
07220 
07221     xpos = cpl_vector_wrap(found_slits, 
07222                            cpl_table_get_data_double(positions, "xcenter"));
07223     ypos = cpl_vector_wrap(found_slits, 
07224                            cpl_table_get_data_double(positions, "ycenter"));
07225     xmpos = cpl_vector_wrap(found_slits, 
07226                             cpl_table_get_data_double(positions, "xmcenter"));
07227     ympos = cpl_vector_wrap(found_slits, 
07228                             cpl_table_get_data_double(positions, "ymcenter"));
07229     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
07230 
07231     if (found_slits < 10)
07232         degree = 1;
07233     else
07234         degree = 2;
07235 
07236     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
07237     if (xpoly != NULL)
07238         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
07239     cpl_bivector_unwrap_vectors(mpos);
07240     cpl_vector_unwrap(xpos);
07241     cpl_vector_unwrap(ypos);
07242     cpl_vector_unwrap(xmpos);
07243     cpl_vector_unwrap(ympos);
07244     if (ypoly == NULL) {
07245         if (found_slits == nmaskslits) {
07246             cpl_msg_warning(func, "Fit failure: the accuracy of the "
07247                             "identified slits positions is not improved.");
07248 
07249             /*
07250              * The determination of the transformation from mask to CCD
07251              * failed, but since all slits have been already identified
07252              * this is not a problem: data can be reduced also without
07253              * such transformation. Simply the accuracy of the slits 
07254              * positions cannot be improved.
07255              */ 
07256 
07257         } else {
07258             cpl_msg_info(func, "Fit failure: not all slits have been "
07259                          "identified. Process will continue using "
07260                          "the detected CCD slits positions");
07261         }
07262 
07263         cpl_polynomial_delete(xpoly);
07264         return positions;
07265     }
07266 
07267     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07268                  sqrt(xmse), sqrt(ymse));
07269 
07270     if (global) {
07271         write_global_distortion(global, 0, xpoly);
07272         write_global_distortion(global, 7, ypoly);
07273     }
07274 
07275     /*
07276      * The fit was successful: use the polynomials to obtain a new 
07277      * position table with the improved positions of the slits
07278      */
07279 
07280     cpl_table_delete(positions);
07281 
07282     positions = cpl_table_duplicate(maskslits);
07283     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07284     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07285     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07286     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07287 
07288     point = cpl_vector_new(2);
07289     dpoint = cpl_vector_get_data(point);
07290 
07291     for (i = 0; i < nmaskslits; i++) {
07292         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07293         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07294         cpl_table_set_double(positions, "xtop", i, 
07295                              cpl_polynomial_eval(xpoly, point));
07296         cpl_table_set_double(positions, "ytop", i, 
07297                              cpl_polynomial_eval(ypoly, point));
07298         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07299         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07300         cpl_table_set_double(positions, "xbottom", i, 
07301                              cpl_polynomial_eval(xpoly, point));
07302         cpl_table_set_double(positions, "ybottom", i, 
07303                              cpl_polynomial_eval(ypoly, point));
07304     }
07305 
07306     cpl_vector_delete(point);
07307     cpl_polynomial_delete(xpoly);
07308     cpl_polynomial_delete(ypoly);
07309 
07310     cpl_table_erase_column(positions, "xmtop");
07311     cpl_table_erase_column(positions, "ymtop");
07312     cpl_table_erase_column(positions, "xmbottom");
07313     cpl_table_erase_column(positions, "ymbottom");
07314 
07315     if (nmaskslits > nslits)
07316         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07317                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07318     else if (nmaskslits < nslits)
07319         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07320                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07321     else
07322         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07323                  nmaskslits, nmaskslits);
07324 
07325     return positions;
07326 }
07327 
07328 
07370 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07371                           double blue, double red, double dispersion)
07372 {
07373 
07374     const char  *func = "mos_trace_flat";
07375 
07376     cpl_image   *gradient;
07377     cpl_image   *sgradient;
07378     float       *dgradient;
07379     float        level = 500;   /* It was 250... */
07380     cpl_vector  *row;
07381     cpl_vector  *srow;
07382     cpl_vector **peaks;
07383     double      *peak;
07384     int         *slit_id;
07385     float       *g;
07386     double      *r;
07387     double      *xtop;
07388     double      *ytop;
07389     double      *xbottom;
07390     double      *ybottom;
07391     double       min, dist;
07392     double       sradius;
07393     double       tolerance;
07394     double       start_y, prev_y;
07395     int          minpos;
07396     int          nslits;
07397     int          nrows;
07398     int          step = 10;
07399     int          filtbox = 15;
07400     int          nx, ny, npix;
07401     int          pos, ypos;
07402     int          npeaks;
07403     int          pixel_above, pixel_below;
07404     int          i, j, k, l;
07405     char         trace_id[MAX_COLNAME];
07406 
07407     cpl_table   *traces;
07408 
07409 
07410     if (flat == NULL || slits == NULL) {
07411         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07412         return NULL;
07413     }
07414 
07415     if (dispersion <= 0.0) {
07416         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07417         return NULL;
07418     }
07419 
07420     if (red - blue < dispersion) {
07421         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07422         return NULL;
07423     }
07424 
07425     /*
07426      * Create a dummy slit_id column if it is missing in the
07427      * input slits table
07428      */
07429 
07430     nslits  = cpl_table_get_nrow(slits);
07431     if (1 != cpl_table_has_column(slits, "slit_id")) {
07432         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07433         for (i = 0; i < nslits; i++)
07434             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07435     }
07436 
07437     slit_id = cpl_table_get_data_int(slits, "slit_id");
07438 
07439     nx = cpl_image_get_size_x(flat);
07440     ny = cpl_image_get_size_y(flat);
07441     npix = nx * ny;
07442 
07443     gradient = cpl_image_duplicate(flat);
07444     dgradient = cpl_image_get_data_float(gradient);
07445 
07446     for (i = 0; i < ny - 1; i++) {
07447         k = i * nx;
07448         for (j = 0; j < nx; j++) {
07449             l = k + j;
07450             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07451         }
07452     }
07453 
07454     npix--;
07455     for (j = 0; j < nx; j++)
07456         dgradient[npix - j] = 0.0;
07457 
07458     cpl_image_turn(gradient, -1);
07459     nx = cpl_image_get_size_x(gradient);
07460     ny = cpl_image_get_size_y(gradient);
07461     sgradient = mos_image_vertical_median_filter(gradient, 
07462                                                  filtbox, 0, ny, 0, step);
07463     cpl_image_delete(gradient);
07464 
07465 
07466     /*
07467      * Remove background from processed image rows
07468      */
07469 
07470     dgradient = cpl_image_get_data_float(sgradient);
07471 
07472     for (i = 1; i <= ny; i += step) {
07473         row = cpl_vector_new_from_image_row(sgradient, i);
07474         srow = cpl_vector_filter_median_create(row, filtbox);
07475         cpl_vector_subtract(row, srow);
07476         cpl_vector_delete(srow);
07477         g = dgradient + (i-1)*nx;
07478         r = cpl_vector_get_data(row);
07479         for (j = 0; j < nx; j++)
07480             g[j] = r[j];
07481         cpl_vector_delete(row);
07482     }
07483 
07484 
07485     /*
07486      * Rotate (temporarily) the input slits table, to get coordinates
07487      * compatible with the rotated gradient image.
07488      */
07489 
07490     mos_rotate_slits(slits, 1, nx, ny);
07491     xtop    = cpl_table_get_data_double(slits, "xtop");
07492     ytop    = cpl_table_get_data_double(slits, "ytop");
07493     xbottom = cpl_table_get_data_double(slits, "xbottom");
07494     ybottom = cpl_table_get_data_double(slits, "ybottom");
07495 
07496 
07497     /*
07498      * Get positions of peaks candidates for each processed gradient
07499      * image row
07500      */
07501 
07502     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07503 
07504     for (i = 0; i < ny; i += step) {
07505         g = dgradient + i*nx;
07506         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07507 
07508         /* I thought this would be required, but apparently I was wrong.
07509          * Check twice... */
07510         if (peaks[i])
07511             cpl_vector_subtract_scalar(peaks[i], 0.5);
07512          
07513     }
07514 
07515     cpl_image_delete(sgradient);
07516 
07517 
07518     /*
07519      * Tracing the flat field spectra edges, starting from the
07520      * slits positions obtained at reference wavelength. The 
07521      * gradient maximum closest to each slits ends is found and
07522      * accepted only within a given search radius:
07523      */
07524 
07525     sradius = 5.0;  /* Pixel */
07526 
07527     /*
07528      * The tracing proceeds along the processed gradient image rows,
07529      * above and below the start position, finding the closest peak
07530      * to the previous obtained position, accepting the new position
07531      * only if it is closer than a given tolerance:
07532      */
07533 
07534     tolerance = 0.9;  /* Pixel */
07535 
07536     /*
07537      * The trace is attempted for a certain number of pixels above
07538      * and below the position of the reference wavelength:
07539      */
07540 
07541     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
07542     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
07543 
07544 
07545     /*
07546      * Prepare the structure of the output table:
07547      */
07548 
07549     nrows = (ny-1)/step + 1;
07550     traces = cpl_table_new(nrows);
07551     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07552     cpl_table_set_column_unit(traces, "x", "pixel");
07553     for (i = 0, j = 0; i < ny; i += step, j++)
07554         cpl_table_set(traces, "x", j, i);
07555 
07556     for (i = 0; i < nslits; i++) {
07557 
07558         /*
07559          * Find the closest processed gradient image row
07560          */
07561 
07562         ypos = ytop[i];
07563 
07564         if (ypos < 0)
07565             ypos = 0;
07566         if (ypos >= ny)
07567             ypos = ny - 1;
07568 
07569         pos = ypos / step;
07570         pos *= step;
07571 
07572         /*
07573          * Find the peak in that row that is closest to xtop[i]
07574          */
07575 
07576         if (peaks[pos]) {
07577             peak = cpl_vector_get_data(peaks[pos]);
07578             npeaks = cpl_vector_get_size(peaks[pos]);
07579 
07580             min = fabs(peak[0] - xtop[i]);
07581             minpos = 0;
07582             for (j = 1; j < npeaks; j++) {
07583                 dist = fabs(peak[j] - xtop[i]);
07584                 if (min > dist) {
07585                     min = dist;
07586                     minpos = j;
07587                 }
07588             }
07589         }
07590         else {
07591             npeaks = 0;
07592         }
07593 
07594         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07595         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07596 
07597         if (min > sradius || npeaks == 0) {
07598             cpl_msg_warning(func, "Cannot find spectrum edge for "
07599                             "top (or left) end of slit %d", slit_id[i]);
07600         }
07601         else {
07602 
07603             /*
07604              * Add to output table the start y position. Note that
07605              * y positions are written in coordinates of the unrotated
07606              * image, i.e., with horizontal dispersion. Currently nx
07607              * is the x size of the temporarily rotated image (for
07608              * faster memory access), but it has the sense of a y size.
07609              */
07610 
07611             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07612             start_y = peak[minpos];
07613 
07614             /*
07615              * Perform the tracing of current edge. Above:
07616              */
07617 
07618             prev_y = start_y;
07619 
07620             for (j = pos + step; j < ny; j += step) {
07621                 if (j - pos > pixel_above)
07622                     break;
07623                 if (peaks[j]) {
07624                     peak = cpl_vector_get_data(peaks[j]);
07625                     npeaks = cpl_vector_get_size(peaks[j]);
07626                     min = fabs(peak[0] - prev_y);
07627                     minpos = 0;
07628                     for (k = 1; k < npeaks; k++) {
07629                         dist = fabs(peak[k] - prev_y);
07630                         if (min > dist) {
07631                             min = dist;
07632                             minpos = k;
07633                         }
07634                     }
07635                     if (min < tolerance) {
07636                         cpl_table_set(traces, trace_id, j/step, 
07637                                       nx - peak[minpos]);
07638                         prev_y = peak[minpos];
07639                     }
07640                 }
07641             }
07642 
07643             /*
07644              * Perform the tracing of current edge. Below:
07645              */
07646 
07647             prev_y = start_y;
07648 
07649             for (j = pos - step; j >= 0; j -= step) {
07650                 if (pos - j > pixel_below)
07651                     break;
07652                 if (peaks[j]) {
07653                     peak = cpl_vector_get_data(peaks[j]);
07654                     npeaks = cpl_vector_get_size(peaks[j]);
07655                     min = fabs(peak[0] - prev_y);
07656                     minpos = 0;
07657                     for (k = 1; k < npeaks; k++) {
07658                         dist = fabs(peak[k] - prev_y);
07659                         if (min > dist) {
07660                             min = dist;
07661                             minpos = k;
07662                         }
07663                     }
07664                     if (min < tolerance) {
07665                         cpl_table_set(traces, trace_id, j/step, 
07666                                       nx - peak[minpos]);
07667                         prev_y = peak[minpos];
07668                     }
07669                 }
07670             }
07671         }
07672 
07673 
07674         /*
07675          * Find the peak in that row that is closest to xbottom[i]
07676          */
07677 
07678         if (peaks[pos]) {
07679             peak = cpl_vector_get_data(peaks[pos]);
07680             npeaks = cpl_vector_get_size(peaks[pos]);
07681     
07682             min = fabs(peak[0] - xbottom[i]);
07683             minpos = 0;
07684             for (j = 1; j < npeaks; j++) {
07685                 dist = fabs(peak[j] - xbottom[i]);
07686                 if (min > dist) {
07687                     min = dist;
07688                     minpos = j;
07689                 }
07690             }
07691         }
07692         else {
07693             npeaks = 0;
07694         }
07695 
07696         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07697         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07698 
07699         if (min > sradius || npeaks == 0) {
07700             cpl_msg_warning(func, "Cannot find spectrum edge for "
07701                             "bottom (or right) end of slit %d", slit_id[i]);
07702         }
07703         else {
07704 
07705             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07706             start_y = peak[minpos]; 
07707 
07708             /*
07709              * Perform the tracing of current edge. Above:
07710              */
07711 
07712             prev_y = start_y;
07713 
07714             for (j = pos + step; j < ny; j += step) {
07715                 if (j - pos > pixel_above)
07716                     break;
07717                 if (peaks[j]) {
07718                     peak = cpl_vector_get_data(peaks[j]);
07719                     npeaks = cpl_vector_get_size(peaks[j]);
07720                     min = fabs(peak[0] - prev_y);
07721                     minpos = 0;
07722                     for (k = 1; k < npeaks; k++) {
07723                         dist = fabs(peak[k] - prev_y);
07724                         if (min > dist) {
07725                             min = dist;
07726                             minpos = k;
07727                         }
07728                     }
07729                     if (min < tolerance) {
07730                         cpl_table_set(traces, trace_id, j/step, 
07731                                       nx - peak[minpos]);
07732                         prev_y = peak[minpos];
07733                     }
07734                 }
07735             }
07736 
07737             /*
07738              * Perform the tracing of current edge. Below:
07739              */
07740 
07741             prev_y = start_y;
07742 
07743             for (j = pos - step; j >= 0; j -= step) {
07744                 if (pos - j > pixel_below)
07745                     break;
07746                 if (peaks[j]) {
07747                     peak = cpl_vector_get_data(peaks[j]);
07748                     npeaks = cpl_vector_get_size(peaks[j]);
07749                     min = fabs(peak[0] - prev_y);
07750                     minpos = 0;
07751                     for (k = 1; k < npeaks; k++) {
07752                         dist = fabs(peak[k] - prev_y);
07753                         if (min > dist) {
07754                             min = dist;
07755                             minpos = k;
07756                         }
07757                     }
07758                     if (min < tolerance) {
07759                         cpl_table_set(traces, trace_id, j/step, 
07760                                       nx - peak[minpos]);
07761                         prev_y = peak[minpos];
07762                     }
07763                 }
07764             }
07765         }
07766 
07767     }   /* End of loop on slits */
07768 
07769     for (i = 0; i < ny; i += step)
07770         cpl_vector_delete(peaks[i]);
07771     cpl_free(peaks);
07772 
07773     /*
07774      * Restore original orientation of slits positions table
07775      */
07776 
07777     mos_rotate_slits(slits, -1, ny, nx);
07778 
07779     return traces;
07780 
07781 }
07782 
07783 
07804 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07805 {
07806     const char *func = "mos_poly_trace";
07807 
07808     cpl_table      *polytraces;
07809     cpl_table      *dummy;
07810     cpl_vector     *x;
07811     cpl_vector     *trace;
07812     cpl_polynomial *polytrace;
07813     char            trace_id[MAX_COLNAME];
07814     char            trace_res[MAX_COLNAME];
07815     char            trace_mod[MAX_COLNAME];
07816     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07817                                                  /* Max order is 5 */
07818     double         *xdata;
07819     int            *slit_id;
07820     int             nslits;
07821     int             nrows;
07822     int             npoints;
07823     int             i, j;
07824     cpl_size        k;
07825 
07826 
07827     if (traces == NULL || slits == NULL) {
07828         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07829         return NULL;
07830     }
07831 
07832     if (order > 5) {
07833         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07834         return NULL;
07835     }
07836 
07837     nrows   = cpl_table_get_nrow(traces);
07838     xdata   = cpl_table_get_data_double(traces, "x");
07839     nslits  = cpl_table_get_nrow(slits);
07840     slit_id = cpl_table_get_data_int(slits, "slit_id");
07841 
07842     polytraces = cpl_table_new(2*nslits);
07843     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07844     for (i = 0; i <= order; i++)
07845         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07846 
07847     for (i = 0; i < nslits; i++) {
07848         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07849 
07850             if (j) {
07851                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07852                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07853                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07854             }
07855             else {
07856                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07857                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07858                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07859             }
07860 
07861             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07862 
07863             /*
07864              * The "dummy" table is just a tool for eliminating invalid
07865              * points from the vectors to be fitted.
07866              */
07867 
07868             dummy = cpl_table_new(nrows);
07869             cpl_table_duplicate_column(dummy, "x", traces, "x");
07870             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07871             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07872             if (npoints < 2 * order) {
07873                 cpl_table_delete(dummy);
07874                 continue;
07875             }
07876             cpl_table_erase_invalid(dummy);
07877             x     = cpl_vector_wrap(npoints, 
07878                                     cpl_table_get_data_double(dummy, "x"));
07879             trace = cpl_vector_wrap(npoints, 
07880                                     cpl_table_get_data_double(dummy, trace_id));
07881             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07882             cpl_vector_unwrap(x);
07883             cpl_vector_unwrap(trace);
07884             cpl_table_delete(dummy);
07885 
07886             /*
07887              * Screen bad solutions. At the moment, a primitive screening
07888              * consists in excluding solutions displaying excessive
07889              * curvature (larger than 1E-5 / pixel)
07890              */
07891 
07892             k = 2;
07893             if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) >  1.E-4) {
07894                 cpl_polynomial_delete(polytrace);
07895                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07896                 cpl_table_duplicate_column(traces, trace_res, traces, 
07897                                            trace_mod);
07898                 if (j) 
07899                     cpl_msg_warning(func, "Exclude bad curvature solution "
07900                            "for bottom (right) edge of slit %d", slit_id[i]);
07901                 else
07902                     cpl_msg_warning(func, "Exclude bad curvature solution "
07903                                 "for top (left) edge of slit %d", slit_id[i]);
07904                 continue;
07905             }
07906 
07907             /*
07908              * Write polynomial coefficients to the output table,
07909              * tagged with the appropriate slit_id
07910              */
07911 
07912             for (k = 0; k <= order; k++)
07913                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07914                                      cpl_polynomial_get_coeff(polytrace, &k));
07915 
07916             /*
07917              * Add column of residuals to input traces table
07918              */
07919 
07920             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07921             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07922 
07923             for (k = 0; k < nrows; k++) {
07924                 cpl_table_set_double(traces, trace_mod, k,
07925                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07926             }
07927 
07928             cpl_polynomial_delete(polytrace);
07929 
07930             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07931             cpl_table_subtract_columns(traces, trace_res, trace_id);
07932             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07933 
07934         }
07935     }
07936 
07937     return polytraces;
07938 
07939 }
07940 
07941 
07965 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07966                                 int mode)
07967 {
07968     const char *func = "mos_global_trace";
07969 
07970     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07971                                                  /* Max order is 5 */
07972     cpl_table      *table;
07973     cpl_vector     *c0;
07974     cpl_vector     *cn;
07975     cpl_bivector   *list;
07976 /* alternative (not robust)
07977     cpl_polynomial *poly;
07978 */
07979 
07980     double *offset;
07981     double  rms, q, m;
07982 
07983     int order, nrows, nslits;
07984     int i, j;
07985 
07986 
07987     if (polytraces == NULL) {
07988         cpl_msg_error(func, "Missing spectral curvature table");
07989         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07990     }
07991 
07992     if (slits == NULL) {
07993         cpl_msg_error(func, "Missing slits positions table");
07994         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07995     }
07996 
07997     nslits = cpl_table_get_nrow(slits);
07998 
07999     table = cpl_table_duplicate(polytraces);
08000     cpl_table_erase_invalid(table);
08001 
08002     nrows = cpl_table_get_nrow(table);
08003 
08004     if (nrows < 4) {
08005         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
08006                       "(%d): the determination of a global curvature model "
08007                       "failed", nrows);
08008         return CPL_ERROR_NONE;
08009     }
08010     
08011     order = cpl_table_get_ncol(polytraces) - 2;
08012 
08013     for (i = 0; i <= order; i++) {
08014         if (!cpl_table_has_column(table, clab[i])) {
08015             cpl_msg_error(func, "Wrong spectral curvature table");
08016             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08017         }
08018     }
08019 
08020 
08021     /*
08022      * Fill in advance the missing offset terms
08023      */
08024 
08025     for (i = 0; i < nslits; i++) {
08026         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
08027             cpl_table_set_double(polytraces, clab[0], 2*i, 
08028                                 cpl_table_get_double(slits, "ytop", i, NULL));
08029         }
08030         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
08031             cpl_table_set_double(polytraces, clab[0], 2*i+1,
08032                              cpl_table_get_double(slits, "ybottom", i, NULL));
08033         }
08034     }
08035 
08036     offset = cpl_table_get_data_double(polytraces, clab[0]);
08037 
08038 
08039     /*
08040      * Fit the global model and modify polytraces table accordingly
08041      */
08042 
08043     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
08044 
08045     for (i = 1; i <= order; i++) {
08046         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
08047         list = cpl_bivector_wrap_vectors(c0, cn);
08048         robustLinearFit(list, &q, &m, &rms);
08049 /* alternative (not robust)
08050         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
08051 */
08052         for (j = 0; j < 2*nslits; j++) {
08053             if (mode == 1)
08054                 if (cpl_table_is_valid(polytraces, clab[i], j))
08055                     continue;
08056             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
08057 /* alternative (not robust)
08058             cpl_table_set_double(polytraces, clab[i], j, 
08059                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
08060 */
08061         }
08062         cpl_bivector_unwrap_vectors(list);
08063 /* alternative (not robust)
08064         cpl_polynomial_delete(poly);
08065 */
08066         cpl_vector_unwrap(cn);
08067     }
08068 
08069     cpl_vector_unwrap(c0);
08070     cpl_table_delete(table);
08071 
08072     return CPL_ERROR_NONE;
08073 
08074 }
08075 
08076 
08146 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
08147                                    cpl_table *polytraces, double reference,
08148                                    double blue, double red, double dispersion,
08149                                    int flux, cpl_image *calibration)
08150 {
08151     const char *func = "mos_spatial_calibration";
08152 
08153     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08154                                                  /* Max order is 5 */
08155     cpl_polynomial *polytop;
08156     cpl_polynomial *polybot;
08157     cpl_image     **exslit;
08158     cpl_image      *resampled;
08159     float          *data;
08160     float          *sdata;
08161     float          *xdata;
08162     double          vtop, vbot, value;
08163     double          top, bot;
08164     double          coeff;
08165     double          ytop, ybot;
08166     double          ypos, yfra;
08167     double          factor;
08168     int             yint, ysize, yprev;
08169     int             nslits;
08170     int             npseudo;
08171     int            *slit_id;
08172     int            *length;
08173     int             nx, ny;
08174     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
08175     int             missing_top, missing_bot;
08176     int             null;
08177     int             order;
08178     int             i, j; 
08179     cpl_size        k;
08180 
08181     int             create_position = 1;
08182 
08183 
08184     if (spectra == NULL || slits == NULL || polytraces == NULL) {
08185         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08186         return NULL;
08187     }
08188 
08189     if (dispersion <= 0.0) {
08190         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08191         return NULL;
08192     }
08193 
08194     if (red - blue < dispersion) {
08195         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08196         return NULL;
08197     }
08198 
08199     nx = cpl_image_get_size_x(spectra);
08200     ny = cpl_image_get_size_y(spectra);
08201     sdata = cpl_image_get_data(spectra);
08202     if (calibration)
08203         data = cpl_image_get_data(calibration);
08204 
08205     if (cpl_table_has_column(slits, "position"))
08206         create_position = 0;
08207 
08208     if (create_position) {
08209         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
08210         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
08211         cpl_table_set_column_unit(slits, "position", "pixel");
08212         cpl_table_set_column_unit(slits, "length", "pixel");
08213     }
08214     else
08215         length = cpl_table_get_data_int(slits, "length");
08216 
08217     nslits   = cpl_table_get_nrow(slits);
08218     slit_id  = cpl_table_get_data_int(slits, "slit_id");
08219     order    = cpl_table_get_ncol(polytraces) - 2;
08220 
08221     /*
08222      * The spatial resampling is performed for a certain number of 
08223      * pixels above and below the position of the reference wavelength:
08224      */
08225 
08226     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
08227     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
08228 
08229     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
08230 
08231     for (i = 0; i < nslits; i++) {
08232         
08233         if (create_position == 0)
08234             if (length[i] == 0)
08235                 continue;
08236 
08237         /*
08238          * Note that the x coordinate of the reference pixels on the CCD
08239          * is taken arbitrarily at the top end of each slit. This wouldn't
08240          * be entirely correct in case of curved slits, or in presence of
08241          * heavy distortions: in such cases the spatial resampling is
08242          * really performed across a wide range of wavelengths. But
08243          * the lag between top and bottom spectral curvature models 
08244          * would introduce even in such cases negligible effects on
08245          * the spectral spatial resampling.
08246          */
08247 
08248         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
08249 
08250         start_pixel = refpixel - pixel_below;
08251         if (start_pixel < 0)
08252             start_pixel = 0;
08253 
08254         end_pixel = refpixel + pixel_above;
08255         if (end_pixel > nx)
08256             end_pixel = nx;
08257 
08258         /*
08259          * Recover from the table of spectral curvature coefficients
08260          * the curvature polynomials.
08261          */
08262 
08263         missing_top = 0;
08264         polytop = cpl_polynomial_new(1);
08265         for (k = 0; k <= order; k++) {
08266             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08267             if (null) {
08268                 cpl_polynomial_delete(polytop);
08269                 missing_top = 1;
08270                 break;
08271             }
08272             cpl_polynomial_set_coeff(polytop, &k, coeff);
08273         }
08274 
08275         missing_bot = 0;
08276         polybot = cpl_polynomial_new(1);
08277         for (k = 0; k <= order; k++) {
08278             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08279             if (null) {
08280                 cpl_polynomial_delete(polybot);
08281                 missing_bot = 1;
08282                 break;
08283             }
08284             cpl_polynomial_set_coeff(polybot, &k, coeff);
08285         }
08286 
08287         if (missing_top && missing_bot) {
08288             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08289                             "traced: no extraction!", 
08290                             slit_id[i]);
08291             continue;
08292         }
08293 
08294         /*
08295          * In case just one of the two edges was not traced, the other
08296          * edge curvature model is duplicated and shifted to the other
08297          * end of the slit: better than nothing!
08298          */
08299 
08300         if (missing_top) {
08301             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08302                             "the spectral curvature of the lower edge "
08303                             "is used instead.", slit_id[i]);
08304             polytop = cpl_polynomial_duplicate(polybot);
08305             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08306             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08307             k = 0;
08308             coeff = cpl_polynomial_get_coeff(polybot, &k);
08309             coeff += ytop - ybot;
08310             cpl_polynomial_set_coeff(polytop, &k, coeff);
08311         }
08312 
08313         if (missing_bot) {
08314             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08315                             "the spectral curvature of the upper edge "
08316                             "is used instead.", slit_id[i]);
08317             polybot = cpl_polynomial_duplicate(polytop);
08318             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08319             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08320             k = 0;
08321             coeff = cpl_polynomial_get_coeff(polytop, &k);
08322             coeff -= ytop - ybot;
08323             cpl_polynomial_set_coeff(polybot, &k, coeff);
08324         }
08325 
08326         /*
08327          * Allocate image for current extracted slit
08328          */
08329 
08330         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08331         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08332         npseudo = ceil(top-bot) + 1;
08333 
08334         if (npseudo < 1) {
08335             cpl_polynomial_delete(polytop);
08336             cpl_polynomial_delete(polybot);
08337             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08338                             slit_id[i]);
08339             continue;
08340         }
08341 
08342         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08343         xdata = cpl_image_get_data(exslit[i]);
08344 
08345         /*
08346          * Write interpolated values to slit image.
08347          */
08348 
08349         for (j = start_pixel; j < end_pixel; j++) {
08350             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08351             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08352             factor = (top-bot)/npseudo;
08353             for (k = 0; k <= npseudo; k++) {
08354                 ypos = top - k*factor;
08355                 yint = ypos;
08356                 yfra = ypos - yint;
08357                 if (yint >= 0 && yint < ny-1) {
08358                     vtop = sdata[j + nx*yint];
08359                     vbot = sdata[j + nx*(yint+1)];
08360                     value = vtop*(1-yfra) + vbot*yfra;
08361                     if (flux)
08362                         value *= factor;
08363                     xdata[j + nx*(npseudo-k)] = value;
08364                     if (calibration) {
08365                         data[j + nx*yint] = (top-yint)/factor;
08366                         if (k) {
08367 
08368                             /*
08369                              * This is added to recover lost pixels on
08370                              * the CCD image (pixels are lost because
08371                              * the CCD pixels are less than npseudo+1).
08372                              */
08373 
08374                             if (yprev - yint > 1) {
08375                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08376                             }
08377                         }
08378                     }
08379                 }
08380                 yprev = yint;
08381             }
08382         }
08383         cpl_polynomial_delete(polytop);
08384         cpl_polynomial_delete(polybot);
08385     }
08386 
08387     /*
08388      * Now all the slits images are copied to a single image
08389      */
08390 
08391     ysize = 0;
08392     for (i = 0; i < nslits; i++)
08393         if (exslit[i])
08394             ysize += cpl_image_get_size_y(exslit[i]);
08395 
08396     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08397 
08398     yint = -1;
08399     for (i = 0; i < nslits; i++) {
08400         if (exslit[i]) {
08401             yint += cpl_image_get_size_y(exslit[i]);
08402             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08403             if (create_position) {
08404                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08405                 cpl_table_set_int(slits, "length", i, 
08406                                   cpl_image_get_size_y(exslit[i]));
08407             }
08408             cpl_image_delete(exslit[i]);
08409         }
08410         else if (create_position) {
08411             cpl_table_set_int(slits, "position", i, -1);
08412             cpl_table_set_int(slits, "length", i, 0);
08413         }
08414     }
08415 
08416 
08417     /*
08418      * Elimination of non-traced slits from slit position table: we cannot do
08419      * it because we would lose sync with polytraces and other slit-oriented
08420      * tables. COMMENTED OUT.
08421      * 
08422 
08423     if (create_position) {
08424 
08425         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08426             cpl_table_erase_selected(slits);
08427 
08428     }
08429 
08430     */
08431 
08432     cpl_free(exslit);
08433 
08434     return resampled;
08435 
08436 }
08437 
08438 
08545 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08546                                             cpl_vector *lines,
08547                                             double dispersion, float level,
08548                                             int sradius, int order,
08549                                             double reject, double refwave,
08550                                             double *wavestart, double *waveend,
08551                                             int *nlines, double *error, 
08552                                             cpl_table *idscoeff,
08553                                             cpl_image *calibration,
08554                                             cpl_image *residuals,
08555                                             cpl_table *restable)
08556 {
08557 
08558     const char *func = "mos_wavelength_calibration_final";
08559 
08560     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08561                                                  /* Max order is 5 */
08562 
08563     double  tolerance = 20.0;    /* Probably forever...                */
08564     int     step = 10;           /* Compute restable every "step" rows */
08565 
08566     char            name[MAX_COLNAME];
08567 
08568     cpl_image      *resampled;
08569     cpl_bivector   *output;
08570     cpl_vector     *wavel;
08571     cpl_vector     *peaks;
08572     cpl_polynomial *ids;
08573     cpl_polynomial *lin;
08574     cpl_polynomial *fguess;
08575     cpl_table      *coeff;
08576     double          ids_err;
08577     double          max_disp, min_disp;
08578     double         *line;
08579     double          firstLambda, lastLambda, lambda;
08580     double          wave, pixe, value;
08581     double          c;
08582     float          *sdata;
08583     float          *rdata;
08584     float          *idata;
08585     float          *ddata;
08586     float           v1, v2, vi;
08587     float           fpixel;
08588     int            *length;
08589     int             pixstart, pixend;
08590     int             row_top, row_bot;
08591     int             extrapolation;
08592     int             nref;
08593     int             nslits;
08594     int             nfits;
08595     int             nl, nx, ny, pixel;
08596     int             countLines, usedLines;
08597     int             uorder;
08598     int             missing;
08599     int             null;
08600     int             width, uradius;
08601     int             i, j, s;
08602     cpl_size        k;
08603 
08604 
08605     if (dispersion == 0.0) {
08606         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
08607         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08608         return NULL;
08609     }
08610 
08611     if (dispersion < 0.0) {
08612         cpl_msg_error(func, "The expected dispersion must be positive");
08613         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08614         return NULL;
08615     }
08616 
08617     if (idscoeff == NULL) {
08618         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
08619         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08620         return NULL;
08621     }
08622 
08623     max_disp = dispersion + dispersion * tolerance / 100;
08624     min_disp = dispersion - dispersion * tolerance / 100;
08625 
08626     if (order < 1) {
08627         cpl_msg_error(func, "The order of the fitting polynomial "
08628                       "must be at least 1");
08629         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08630         return NULL;
08631     }
08632 
08633     if (image == NULL || lines == NULL) {
08634         cpl_msg_error(func, "Both spectral exposure and reference line "
08635                       "catalog are required in input");
08636         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08637         return NULL;
08638     }
08639 
08640     nx = cpl_image_get_size_x(image);
08641     ny = cpl_image_get_size_y(image);
08642     sdata = cpl_image_get_data_float(image);
08643 
08644     nref = cpl_vector_get_size(lines);
08645     line = cpl_vector_get_data(lines);
08646 
08647     if (*wavestart < 1.0 && *waveend < 1.0) {
08648         firstLambda = line[0];
08649         lastLambda = line[nref-1];
08650         extrapolation = (lastLambda - firstLambda) / 10;
08651         firstLambda -= extrapolation;
08652         lastLambda += extrapolation;
08653         *wavestart = firstLambda;
08654         *waveend = lastLambda;
08655     }
08656     else {
08657         firstLambda = *wavestart;
08658         lastLambda = *waveend;
08659     }
08660 
08661     nl = (lastLambda - firstLambda) / dispersion;
08662     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08663     rdata = cpl_image_get_data_float(resampled);
08664 
08665     /*
08666      * Allocate total output table of IDS coefficients
08667      */
08668 
08669     for (j = 0; j <= order; j++)
08670         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08671 
08672     if (calibration)
08673         idata = cpl_image_get_data_float(calibration);
08674 
08675     if (residuals)
08676         ddata = cpl_image_get_data_float(residuals);
08677 
08678     if (restable) {
08679         cpl_table_set_size(restable, nref);
08680         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08681         cpl_table_copy_data_double(restable, "wavelength", line);
08682         for (i = 0; i < ny; i += step) {
08683              snprintf(name, MAX_COLNAME, "r%d", i);
08684              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08685              snprintf(name, MAX_COLNAME, "d%d", i);
08686              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08687              snprintf(name, MAX_COLNAME, "p%d", i);
08688              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08689         }
08690     }
08691 
08692 
08693     /*
08694      * Process all slits separately.
08695      */
08696 
08697     nslits   = cpl_table_get_nrow(slits);
08698     length   = cpl_table_get_data_int(slits, "length");
08699 
08700     row_top = ny;
08701     for (s = 0; s < nslits; s++) {
08702 
08703         if (length[s] == 0)
08704             continue;
08705 
08706         /*
08707          * row_top and row_bot define the boundaries of the current slit.
08708          * Here we begin (arbitrarily...) from the top slit.
08709          */
08710 
08711         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08712 
08713         if (sradius > 0) {
08714 
08715             /*
08716              * If a search radius was defined, allocate the table of
08717              * the fitting polynomials coefficients. This table is
08718              * just used to generate the first-guess polynomial made
08719              * of the median coefficients of all polynomials found
08720              * for this slit.
08721              */
08722 
08723             coeff = cpl_table_new(row_top - row_bot);
08724             for (j = 0; j <= order; j++)
08725                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08726         }
08727 
08728         /*
08729          * Here is the loop on all rows of the current slit. They are
08730          * wavelength calibrated one by one.
08731          */
08732 
08733         for (i = row_bot; i < row_top; i++) {
08734             width = mos_lines_width(sdata + i*nx, nx);
08735             if (width < 5)
08736                 width = 5;
08737             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08738             if (peaks) {
08739                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08740             }
08741             if (peaks) {
08742                 int keep_multiplex = mos_multiplex;
08743                 mos_multiplex = -1;
08744                 output = mos_identify_peaks(peaks, lines, 
08745                                             min_disp, max_disp, 0.05);
08746                 mos_multiplex = keep_multiplex;
08747                 if (output) {
08748                     countLines = cpl_bivector_get_size(output);
08749                     if (countLines < 4) {
08750                         cpl_bivector_delete(output);
08751                         cpl_vector_delete(peaks);
08752                         if (nlines)
08753                             nlines[i] = 0;
08754                         if (error)
08755                             error[i] = 0.0;
08756                         continue;
08757                     }
08758 
08759                     /*
08760                      * Set reference wavelength as zero point
08761                      */
08762 
08763                     wavel = cpl_bivector_get_y(output);
08764                     cpl_vector_subtract_scalar(wavel, refwave);
08765 
08766                     uorder = countLines / 2 - 1;
08767                     if (uorder > order)
08768                         uorder = order;
08769 
08770                     ids = mos_poly_wav2pix(output, uorder, reject,
08771                                            2 * (uorder + 1), &usedLines,
08772                                            &ids_err);
08773 
08774                     if (ids == NULL) {
08775                         cpl_bivector_delete(output);
08776                         cpl_vector_delete(peaks);
08777                         if (nlines)
08778                             nlines[i] = 0;
08779                         if (error)
08780                             error[i] = 0.0;
08781                         cpl_error_reset();
08782                         continue;
08783                     }
08784 
08785                     if (sradius > 0) {
08786                         for (k = 0; k <= order; k++) {
08787                             if (k > uorder) {
08788                                 cpl_table_set_double(coeff, clab[k], 
08789                                 i - row_bot, 0.0);
08790                             }
08791                             else {
08792                                 cpl_table_set_double(coeff, clab[k], 
08793                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08794                             }
08795                         }
08796                     }
08797                /*   else {   */
08798                         if (calibration) {
08799                             pixstart = cpl_polynomial_eval_1d(ids,
08800                               cpl_bivector_get_y_data(output)[0], 
08801                               NULL);
08802                             pixend = cpl_polynomial_eval_1d(ids,
08803                               cpl_bivector_get_y_data(output)[countLines-1],
08804                               NULL);
08805                             extrapolation = (pixend - pixstart) / 5;
08806                             pixstart -= extrapolation;
08807                             pixend += extrapolation;
08808                             if (pixstart < 0)
08809                                 pixstart = 0;
08810                             if (pixend > nx)
08811                                 pixend = nx;
08812    
08813                             for (j = pixstart; j < pixend; j++) {
08814                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08815                                      firstLambda, lastLambda, refwave, j);
08816                             }
08817                         }
08818 
08819                         /*
08820                          * Residuals image
08821                          */
08822         
08823                         if (residuals || (restable && !(i%step))) {
08824                             if (restable && !(i%step)) {
08825                                 lin = cpl_polynomial_new(1);
08826                                 for (k = 0; k < 2; k++)
08827                                     cpl_polynomial_set_coeff(lin, &k,
08828                                           cpl_polynomial_get_coeff(ids, &k));
08829                             }
08830                             for (j = 0; j < countLines; j++) {
08831                                 pixe = cpl_bivector_get_x_data(output)[j];
08832                                 wave = cpl_bivector_get_y_data(output)[j];
08833                                 value = pixe 
08834                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08835                                 if (residuals) {
08836                                     pixel = pixe + 0.5;
08837                                     (ddata + i*nx)[pixel] = value;
08838                                 }
08839                                 if (restable && !(i%step)) {
08840                                     for (k = 0; k < nref; k++) {
08841                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08842                                             snprintf(name, MAX_COLNAME, 
08843                                                      "r%d", i);
08844                                             cpl_table_set_double(restable, name,
08845                                                                  k, value);
08846                                             value = pixe
08847                                                   - cpl_polynomial_eval_1d(lin,
08848                                                               wave, NULL);
08849                                             snprintf(name, MAX_COLNAME, 
08850                                                      "d%d", i);
08851                                             cpl_table_set_double(restable, name,
08852                                                                  k, value);
08853                                             snprintf(name, MAX_COLNAME,
08854                                                      "p%d", i);
08855                                             cpl_table_set_double(restable, name,
08856                                                                  k, pixe);
08857                                             break;
08858                                         }
08859                                     }
08860                                 }
08861                             }
08862                             if (restable && !(i%step)) {
08863                                 cpl_polynomial_delete(lin);
08864                             }
08865 /***
08866                             for (j = 0; j < countLines; j++) {
08867                                 pixel = cpl_bivector_get_x_data(output)[j] 
08868                                       + 0.5;
08869                                 (ddata + i*nx)[pixel] =
08870                                 cpl_bivector_get_x_data(output)[j]
08871                               - cpl_polynomial_eval_1d(ids,
08872                                 cpl_bivector_get_y_data(output)[j], 
08873                                 NULL);
08874                             }
08875 ***/
08876                         }
08877                 /*  }   */
08878 
08879                     /*
08880                      * Write it anyway, even in case a first-guess based
08881                      * solution will be searched afterwards: in case of
08882                      * failure, the "blind" solution is kept.
08883                      */
08884 
08885                     if (nlines)
08886                         nlines[i] = usedLines;
08887                     if (error)
08888                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08889 
08890                     for (k = 0; k <= order; k++) {
08891                         if (k > uorder) {
08892                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08893                         }
08894                         else {
08895                             cpl_table_set_double(idscoeff, clab[k], i,
08896                                       cpl_polynomial_get_coeff(ids, &k));
08897                         }
08898                     }
08899 
08900                     cpl_polynomial_delete(ids);
08901                     cpl_bivector_delete(output);
08902                 }
08903                 cpl_vector_delete(peaks);
08904             }
08905         }       /* End of loop on current slit's rows */
08906 
08907 
08908         if (sradius > 0) {
08909 
08910             /*
08911              * See whether there are valid fits at all...
08912              */
08913     
08914             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08915     
08916             if (nfits) {
08917                 int slope = 0;
08918 
08919                 fguess = cpl_polynomial_new(1);
08920 
08921                 if (mos_interpolate_wavecalib(coeff, NULL, 2, 1)) {
08922 
08923                     slope = 0;
08924 
08925                     /*
08926                      * Compute a median IDS polynomial for the current slit
08927                      */
08928 
08929                     for (k = 0; k <= order; k++) {
08930                         c = cpl_table_get_column_median(coeff, clab[k]);
08931                         cpl_polynomial_set_coeff(fguess, &k, c);
08932                     }
08933                 }
08934                 else {
08935                     slope = 1;
08936                 }
08937 
08938                 for (i = row_bot; i < row_top; i++) {
08939 
08940                     /*
08941                      * Use first-guess to find the reference lines again
08942                      */
08943 
08944                     width = mos_lines_width(sdata + i*nx, nx);
08945                     if (width > sradius) {
08946                         uradius = width; 
08947                     }
08948                     else {
08949                         uradius = sradius;
08950                     }
08951 
08952                     if (slope) {
08953                         for (k = 0; k <= order; k++) {
08954                             c = cpl_table_get_double(coeff, clab[k], 
08955                                                      i - row_bot, NULL);
08956                             cpl_polynomial_set_coeff(fguess, &k, c);
08957                         }
08958                     }
08959 
08960                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08961                                             fguess, refwave, uradius);
08962 
08963                     if (output == NULL) {
08964                         cpl_error_reset();
08965                         continue;
08966                     }
08967 
08968                     countLines = cpl_bivector_get_size(output);
08969 
08970                     if (countLines < 4) {
08971                         cpl_bivector_delete(output);
08972                         continue;
08973                     }
08974 
08975                     /*
08976                      * Set reference wavelength as zero point
08977                      */
08978 
08979                     wavel = cpl_bivector_get_y(output);
08980                     cpl_vector_subtract_scalar(wavel, refwave);
08981 
08982                     uorder = countLines / 2 - 1;
08983                     if (uorder > order)
08984                         uorder = order;
08985 
08986                     ids = mos_poly_wav2pix(output, uorder, reject,
08987                                            2 * (uorder + 1), &usedLines,
08988                                            &ids_err);
08989 
08990                     if (ids == NULL) {
08991                         cpl_error_reset();
08992                         cpl_bivector_delete(output);
08993                         continue;
08994                     }
08995 
08996                     if (nlines)
08997                         nlines[i] = usedLines;
08998                     if (error)
08999                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
09000 
09001                     if (calibration) {
09002                         pixstart = cpl_polynomial_eval_1d(ids,
09003                            cpl_bivector_get_y_data(output)[0], 
09004                            NULL);
09005                         pixend = cpl_polynomial_eval_1d(ids,
09006                            cpl_bivector_get_y_data(output)[countLines-1], 
09007                            NULL);
09008                         extrapolation = (pixend - pixstart) / 5;
09009                         pixstart -= extrapolation;
09010                         pixend += extrapolation;
09011                         if (pixstart < 0)
09012                             pixstart = 0;
09013                         if (pixend > nx)
09014                             pixend = nx;
09015 
09016                         for (j = pixstart; j < pixend; j++) {
09017                             (idata + i*nx)[j] = mos_eval_dds(ids,
09018                                      firstLambda, lastLambda, refwave, j);
09019                         }
09020                     }
09021 
09022                     /*
09023                      * Residuals image
09024                      */
09025 
09026                     if (residuals || (restable && !(i%step))) {
09027                         if (restable && !(i%step)) {
09028                             lin = cpl_polynomial_new(1);
09029                             for (k = 0; k < 2; k++)
09030                                 cpl_polynomial_set_coeff(lin, &k,
09031                                       cpl_polynomial_get_coeff(ids, &k));
09032                         }
09033                         for (j = 0; j < countLines; j++) {
09034                             pixe = cpl_bivector_get_x_data(output)[j];
09035                             wave = cpl_bivector_get_y_data(output)[j];
09036                             value = pixe
09037                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
09038                             if (residuals) {
09039                                 pixel = pixe + 0.5;
09040                                 (ddata + i*nx)[pixel] = value;
09041                             }
09042                             if (restable && !(i%step)) {
09043                                 for (k = 0; k < nref; k++) {
09044                                     if (fabs(line[k]-refwave-wave) < 0.1) {
09045                                         snprintf(name, MAX_COLNAME,
09046                                                  "r%d", i);
09047                                         cpl_table_set_double(restable, name,
09048                                                              k, value);
09049                                         value = pixe
09050                                               - cpl_polynomial_eval_1d(lin,
09051                                                           wave, NULL);
09052                                         snprintf(name, MAX_COLNAME,
09053                                                  "d%d", i);
09054                                         cpl_table_set_double(restable, name,
09055                                                              k, value);
09056                                         snprintf(name, MAX_COLNAME,
09057                                                  "p%d", i);
09058                                         cpl_table_set_double(restable, name,
09059                                                              k, pixe);
09060                                         break;
09061                                     }
09062                                 }
09063                             }
09064                         }
09065                         if (restable && !(i%step)) {
09066                             cpl_polynomial_delete(lin);
09067                         }
09068 /***
09069                         for (j = 0; j < countLines; j++) {
09070                             pixel = cpl_bivector_get_x_data(output)[j]
09071                                   + 0.5; 
09072                             (ddata + i*nx)[pixel] =
09073                             cpl_bivector_get_x_data(output)[j]
09074                           - cpl_polynomial_eval_1d(ids,
09075                             cpl_bivector_get_y_data(output)[j], 
09076                             NULL);
09077                         }
09078 ***/
09079                     }
09080 
09081                     for (k = 0; k <= order; k++) {
09082                         if (k > uorder) {
09083                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
09084                         }
09085                         else {
09086                             cpl_table_set_double(idscoeff, clab[k], i,
09087                                         cpl_polynomial_get_coeff(ids, &k));
09088                         }
09089                     }
09090 
09091                     cpl_bivector_delete(output);
09092                     cpl_polynomial_delete(ids);
09093 
09094                 } /* End of loop "use ids as a first-guess" */
09095 
09096                 cpl_polynomial_delete(fguess);
09097             }
09098 
09099             cpl_table_delete(coeff);
09100 
09101         }
09102 
09103         row_top = row_bot;
09104 
09105     } /* End of loop on slits */
09106 
09107 
09108     /*
09109      * At this point the idscoeff table has been filled with all the 
09110      * fits coefficients obtained for all the rows of the input image.
09111      * Now we apply these coefficients to resample the input image
09112      * at constant wavelength step.
09113      */
09114 
09115     for (i = 0; i < ny; i++) {
09116 
09117         missing = 0;
09118         ids = cpl_polynomial_new(1);
09119         for (k = 0; k <= order; k++) {
09120             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09121             if (null) {
09122                 cpl_polynomial_delete(ids);
09123                 missing = 1;
09124                 break;
09125             }
09126             cpl_polynomial_set_coeff(ids, &k, c);
09127         }
09128         if (missing)
09129             continue;
09130 
09131         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09132         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09133         if (pixstart < 0)
09134             pixstart = 0;
09135         if (pixend > nx)
09136             pixend = nx;
09137 
09138         /*
09139          * Resampled image:
09140          */
09141 
09142         for (j = 0; j < nl; j++) {
09143             lambda = firstLambda + j * dispersion;
09144             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
09145             pixel = fpixel;
09146             if (pixel >= 0 && pixel < nx-1) {
09147                 v1 = (sdata + i*nx)[pixel];
09148                 v2 = (sdata + i*nx)[pixel+1];
09149                 vi = v1 + (v2-v1)*(fpixel-pixel);
09150                 (rdata + i*nl)[j] = vi;
09151             }
09152         }
09153 
09154         cpl_polynomial_delete(ids);
09155     }
09156 
09157     return resampled;
09158 }
09159 
09160 
09187 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
09188                                       double firstLambda, double lastLambda, 
09189                                       double dispersion, cpl_table *idscoeff, 
09190                                       int flux)
09191 {
09192 
09193     const char *func = "mos_wavelength_calibration";
09194 
09195     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09196                                                  /* Max order is 5 */
09197 
09198     cpl_image      *resampled;
09199     cpl_polynomial *ids;
09200     double          pixel_per_lambda;
09201     double          lambda;
09202     double          c;
09203     float          *sdata;
09204     float          *rdata;
09205     float           v0, v1, v2, v3, vi;
09206     float           fpixel;
09207     int             order;
09208     int             pixstart, pixend;
09209     int             nl, nx, ny, pixel;
09210     int             missing;
09211     int             null;
09212     int             i, j;
09213     cpl_size        k;
09214 
09215 
09216     if (dispersion <= 0.0) {
09217         cpl_msg_error(func, "The resampling step must be positive");
09218         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09219         return NULL;
09220     }
09221 
09222     if (lastLambda - firstLambda < dispersion) {
09223         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
09224                       firstLambda, lastLambda);
09225         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09226         return NULL;
09227     }
09228 
09229     if (idscoeff == NULL) {
09230         cpl_msg_error(func, "An IDS coeff table must be given");
09231         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09232         return NULL;
09233     }
09234 
09235     if (image == NULL) {
09236         cpl_msg_error(func, "A scientific spectral image must be given");
09237         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09238         return NULL;
09239     }
09240 
09241     nx = cpl_image_get_size_x(image);
09242     ny = cpl_image_get_size_y(image);
09243     sdata = cpl_image_get_data_float(image);
09244 
09245     nl = (lastLambda - firstLambda) / dispersion;
09246     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
09247     rdata = cpl_image_get_data_float(resampled);
09248 
09249     order = 0;
09250     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
09251         ++order;
09252     --order;
09253 
09254     for (i = 0; i < ny; i++) {
09255 
09256         missing = 0;
09257         ids = cpl_polynomial_new(1);
09258         for (k = 0; k <= order; k++) {
09259             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09260             if (null) {
09261                 cpl_polynomial_delete(ids);
09262                 missing = 1;
09263                 break;
09264             }
09265             cpl_polynomial_set_coeff(ids, &k, c);
09266         }
09267         if (missing)
09268             continue;
09269 
09270         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09271         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09272         if (pixstart < 0)
09273             pixstart = 0;
09274         if (pixend > nx)
09275             pixend = nx;
09276 
09277         /*
09278          * Resampled image:
09279          */
09280 
09281         for (j = 0; j < nl; j++) {
09282             lambda = firstLambda + j * dispersion;
09283             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09284                                             &pixel_per_lambda);
09285 
09286             /*
09287              * The local dispersion is 1 / pixel_per_lambda
09288              * and this factor is used for applying the flux
09289              * conservation correction (if requested).
09290              */
09291 
09292             pixel = fpixel;
09293 
09294          // if (dispersion * pixel_per_lambda < 2.0) {
09295             if (1) {  /* Old behaviour: this is safe. */
09296 
09297                 /*
09298                  * In this case we just sample interpolating the
09299                  * signal of nearby pixels.
09300                  */
09301 
09302                if (pixel >= 1 && pixel < nx-2) {
09303                    v0 = (sdata + i*nx)[pixel-1];
09304                    v1 = (sdata + i*nx)[pixel];
09305                    v2 = (sdata + i*nx)[pixel+1];
09306                    v3 = (sdata + i*nx)[pixel+2];
09307                    vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09308                       + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09309                       + 2*v1;
09310                    vi /= 2;
09311                    if (v1 > v2) {
09312                        if (vi > v1) { 
09313                            vi = v1;
09314                        }
09315                        else if (vi < v2) {
09316                            vi = v2;
09317                        }
09318                    }
09319                    else {
09320                        if (vi > v2) { 
09321                            vi = v2;
09322                        }
09323                        else if (vi < v1) {
09324                            vi = v1;
09325                        }
09326                    }
09327                    if (flux)
09328                        vi *= dispersion * pixel_per_lambda;
09329                    (rdata + i*nl)[j] = vi;
09330                }
09331                else if (pixel >= 0 && pixel < nx-1) {
09332                    v1 = (sdata + i*nx)[pixel];
09333                    v2 = (sdata + i*nx)[pixel+1];
09334                    vi = v1 + (v2-v1)*(fpixel-pixel);
09335                    if (flux)
09336                        vi *= dispersion * pixel_per_lambda;
09337                    (rdata + i*nl)[j] = vi;
09338                }
09339            }
09340            else {
09341 
09342                /*
09343                 * Here instead we integrate the pixel values in
09344                 * the interval centered at the interpolation point.
09345                 * This interval is long dispersion * pixel_per_lambda
09346                 * of the original pixels, and is centered at fpixel.
09347                 * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
09348                 * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
09349                 */
09350 
09351                double spos = fpixel - dispersion * pixel_per_lambda / 2;
09352                double epos = fpixel + dispersion * pixel_per_lambda / 2;
09353 
09354                /*
09355                 * Brutal sum over all involved pixels
09356                 */
09357 
09358                int spix = spos;
09359                int epix = epos + 1;
09360 
09361                if (spix < 0)
09362                    spix = 0;
09363 
09364                if (epix > nx)
09365                    epix = nx;
09366 
09367                vi = 0.0;
09368                for (k = spix; k < epix; k++) {
09369                    if (pixel >= 0 && pixel < nx) {
09370                        vi += (sdata + i*nx)[k];
09371                    }
09372                }
09373 
09374                /*
09375                 * Correct integrated flux by true length
09376                 * of interval. This is clearly an approximation,
09377                 * but it's good enough if the PSF is much larger
09378                 * than the original pix.
09379                 */
09380 
09381                vi *= dispersion * pixel_per_lambda / (epix - spix);
09382 
09383                 /*
09384                  * Flux conservation is a geometric factor that is applied
09385                  * always in the same way...
09386                  */
09387 
09388                if (flux)
09389                    vi *= dispersion * pixel_per_lambda;
09390 
09391                (rdata + i*nl)[j] = vi;
09392             }
09393         }
09394 
09395         cpl_polynomial_delete(ids);
09396     }
09397 
09398     return resampled;
09399 }
09400 
09401 
09468 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
09469                                 double refwave, double firstLambda, 
09470                                 double lastLambda, cpl_table *idscoeff,
09471                                 cpl_vector *skylines, int highres, int order,
09472                                 cpl_image *calibration, int sradius)
09473 {
09474     const char *func = "mos_wavelength_align";
09475 
09476     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09477                                                  /* Max order is 5 */
09478     double         *line;
09479     double         *data;
09480     double          expPos, offset;
09481     double          c;
09482     double          lambda1, lambda2;
09483     double          rms;
09484     float           pos;
09485     float          *sdata;
09486     float          *cdata;
09487     int            *idata;
09488     int             startPos, endPos;
09489     int             window = 2*sradius + 1;
09490     int             nlines;
09491     int             nslits;
09492     int             npoints;
09493     int             nrows;
09494     int             nx, ny;
09495     int             xlow, ylow, xhig, yhig;
09496     int             idsorder, uorder;
09497     int            *slit_id;
09498     int            *position;
09499     int            *length;
09500     int             missing;
09501     int             null;
09502     int             i;
09503     cpl_size        j, k;
09504 
09505     char            offname[MAX_COLNAME];
09506     char            name[MAX_COLNAME];
09507 
09508     cpl_polynomial *ids;
09509     cpl_polynomial *polycorr;
09510     cpl_image      *exslit;
09511     cpl_image      *sky;
09512     cpl_table      *offsets;
09513     cpl_table      *dummy;
09514     cpl_vector     *wave;
09515     cpl_vector     *offs;
09516     
09517 
09518     if (idscoeff == NULL) {
09519         cpl_msg_error(func, "An IDS coeff table must be given");
09520         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09521         return NULL;
09522     }
09523 
09524     if (image == NULL) {
09525         cpl_msg_error(func, "A scientific spectral image must be given");
09526         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09527         return NULL;
09528     }
09529 
09530     if (slits == NULL) {
09531         cpl_msg_error(func, "A slit position table must be given");
09532         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09533         return NULL;
09534     }
09535 
09536     if (skylines) {
09537         line = cpl_vector_get_data(skylines);
09538         nlines = cpl_vector_get_size(skylines);
09539     }
09540     else {
09541         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09542                         "given: using internal list of reference sky lines");
09543         if (highres) {
09544            line = default_lines_hi;
09545            nlines = sizeof(default_lines_hi) / sizeof(double);
09546         }
09547         else {
09548            line = default_lines_lo;
09549            nlines = sizeof(default_lines_lo) / sizeof(double);
09550         }
09551     }
09552 
09553     if (calibration)
09554         cdata = cpl_image_get_data(calibration);
09555 
09556     nx = cpl_image_get_size_x(image);
09557     ny = cpl_image_get_size_y(image);
09558 
09559     nslits   = cpl_table_get_nrow(slits);
09560     slit_id  = cpl_table_get_data_int(slits, "slit_id");
09561     position = cpl_table_get_data_int(slits, "position");
09562     length   = cpl_table_get_data_int(slits, "length");
09563 
09564 
09565     /*
09566      * Define the output table of offsets
09567      */
09568 
09569     nrows = 0;
09570     for (i = 0; i < nlines; i++)
09571         if (line[i] > firstLambda && line[i] < lastLambda)
09572             nrows++;
09573 
09574     offsets = cpl_table_new(nrows);
09575     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09576     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09577 
09578     nrows = 0;
09579     for (i = 0; i < nlines; i++) {
09580         if (line[i] > firstLambda && line[i] < lastLambda) {
09581             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09582             nrows++;
09583         }
09584     }
09585 
09586     /*
09587      * Here "line" is made to point to the new list of selected wavelengths
09588      */
09589 
09590     line = cpl_table_get_data_double(offsets, "wave");
09591     nlines = nrows;
09592 
09593     idsorder = 0;
09594     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09595         ++idsorder;
09596     --idsorder;
09597 
09598     xlow = 1;
09599     xhig = nx;
09600     for (i = 0; i < nslits; i++) {
09601 
09602         if (length[i] == 0)
09603             continue;
09604 
09605         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09606         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
09607 
09608         /* 
09609          * Define the extraction boundaries. We DON'T write:
09610          *
09611          * ylow = position[i];
09612          * yhig = ylow + length[i];
09613          *
09614          * because the cpl_image pixels are counted from 1, and because in
09615          * cpl_image_extract() the coordinates of the last pixel are inclusive.
09616          */
09617 
09618         ylow = position[i] + 1;
09619         yhig = ylow + length[i] - 1;
09620 
09621         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
09622         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
09623         sdata  = cpl_image_get_data(sky);
09624 
09625         cpl_image_delete(exslit);
09626 
09627         /* 
09628          * Return here to a decent way of counting pixels (i.e., starting
09629          * from 0)
09630          */
09631          
09632         ylow--;
09633 
09634         /*
09635          * Allocate a dummy table for collecting all the offsets
09636          * for all the lines: this is only needed for the computation
09637          * of the median offset for each sky line
09638          */
09639 
09640         dummy = cpl_table_new(yhig - ylow);
09641         for (j = 0; j < nlines; j++) {
09642             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
09643             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09644         }
09645 
09646         for (j = ylow; j < yhig; j++) {
09647 
09648             /*
09649              * Get the IDS polynomial for the current slit row
09650              */
09651 
09652             missing = 0;
09653             ids = cpl_polynomial_new(1);
09654             for (k = 0; k <= idsorder; k++) {
09655                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
09656                 if (null) {
09657                     cpl_polynomial_delete(ids);
09658                     missing = 1;
09659                     break;
09660                 }
09661                 cpl_polynomial_set_coeff(ids, &k, c);
09662             }
09663             if (missing)
09664                 continue;
09665 
09666             for (k = 0; k < nlines; k++) {
09667                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09668                 startPos = expPos - sradius;
09669                 endPos   = startPos + window;
09670                 if (startPos < 0 || endPos >= nx)
09671                     continue;
09672            
09673                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09674                     pos += startPos;
09675                     offset = pos - expPos;
09676                     snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
09677                     cpl_table_set_double(dummy, name, j - ylow, offset);
09678                 }
09679             }
09680 
09681             cpl_polynomial_delete(ids);
09682         }
09683 
09684         cpl_image_delete(sky);
09685 
09686         for (j = 0; j < nlines; j++) {
09687             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
09688             if (cpl_table_has_valid(dummy, name)) {
09689                 offset = cpl_table_get_column_median(dummy, name);
09690                 cpl_table_set_double(offsets, offname, j, offset);
09691             }
09692         }
09693 
09694         cpl_table_delete(dummy);
09695 
09696     }
09697 
09698 
09699     /*
09700      * In the following the input idscoeff table is modified by simply
09701      * adding the coefficients of the polynomial used to fit the sky
09702      * line residuals to the coefficients of the IDS polynomials.
09703      */
09704 
09705     for (i = 0; i < nslits; i++) {
09706 
09707         if (length[i] == 0)
09708             continue;
09709 
09710         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09711 
09712         /*
09713          * In the following, the "dummy" table is just a tool for
09714          * eliminating invalid points from the vectors to be fitted.
09715          */
09716 
09717         dummy = cpl_table_new(nlines);
09718         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
09719         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
09720 
09721         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
09722         if (npoints == 0) {
09723             cpl_msg_warning(func, "No sky lines alignment was possible "
09724                             "for slit ID=%d: no sky line found", slit_id[i]);
09725             cpl_table_delete(dummy);
09726             continue;
09727         }
09728 
09729         uorder = order;
09730         if (npoints <= uorder) {
09731             uorder = npoints - 1;
09732             if (uorder) {
09733                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09734                                 "ID=%d, while a polynomial order %d was "
09735                                 "requested. Using polynomial order %d for "
09736                                 "this slit!", npoints, slit_id[i], order, 
09737                                 uorder);
09738             }
09739             else {
09740                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09741                                 "ID=%d, while a polynomial order %d was "
09742                                 "requested. Computing a median offset for "
09743                                 "this slit!", npoints, slit_id[i], order);
09744             }
09745         }
09746 
09747         cpl_table_erase_invalid(dummy);
09748 
09749         if (uorder > 1) {
09750 
09751             /*
09752              * Model offsets with polynomial fitting
09753              */
09754 
09755             wave = cpl_vector_wrap(npoints,
09756                                    cpl_table_get_data_double(dummy, "wave"));
09757             offs = cpl_vector_wrap(npoints,
09758                                    cpl_table_get_data_double(dummy, "offset"));
09759 
09760             /*
09761              * Set reference wavelength as zero point
09762              */
09763 
09764             cpl_vector_subtract_scalar(wave, refwave);
09765 
09766             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09767 
09768             rms = sqrt(rms * (uorder + 1) / npoints);
09769 
09770             cpl_vector_unwrap(wave);
09771             cpl_vector_unwrap(offs);
09772             cpl_table_delete(dummy);
09773 
09774             /*
09775              * Now correct the coefficients of the corresponding IDS
09776              * polynomials related to this slit:
09777              */
09778 
09779             ylow = position[i];
09780             yhig = ylow + length[i];
09781 
09782             for (j = 0; j <= uorder; j++) {
09783                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09784                 c = cpl_polynomial_get_coeff(polycorr, &j);
09785                 for (k = ylow; k < yhig; k++)
09786                     data[k] += c;
09787             }
09788 
09789             data = cpl_table_get_data_double(idscoeff, "error");
09790             for (k = ylow; k < yhig; k++)
09791                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09792 
09793             idata = cpl_table_get_data_int(idscoeff, "nlines");
09794             for (k = ylow; k < yhig; k++)
09795                  idata[k] = npoints;
09796 
09797             /*
09798              * If a wavelengths map was provided, correct it to keep
09799              * into account the alignment to skylines:
09800              */
09801 
09802             if (calibration) {
09803                 for (j = ylow; j < yhig; j++) {
09804                     for (k = 1; k < nx; k++) {
09805                         lambda1 = cdata[k - 1 + j*nx];
09806                         lambda2 = cdata[k + j*nx];
09807                         if (lambda1 < 1.0 || lambda2 < 1.0)
09808                             continue;
09809                         offset = cpl_polynomial_eval_1d(polycorr, 
09810                                                         lambda1-refwave, NULL);
09811                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09812                     }
09813                 }
09814             }
09815     
09816             cpl_polynomial_delete(polycorr);
09817         }
09818         else if (uorder == 1) {
09819 
09820             /*
09821              * Model offsets with robust linear fitting
09822              */
09823 
09824             double        q, m;
09825             cpl_bivector *list;
09826 
09827 
09828             wave = cpl_vector_wrap(npoints,
09829                                    cpl_table_get_data_double(dummy, "wave"));
09830             offs = cpl_vector_wrap(npoints,
09831                                    cpl_table_get_data_double(dummy, "offset"));
09832 
09833             list = cpl_bivector_wrap_vectors(wave, offs);
09834 
09835             /*
09836              * Set reference wavelength as zero point
09837              */
09838 
09839             cpl_vector_subtract_scalar(wave, refwave);
09840 
09841             robustLinearFit(list, &q, &m, &rms);
09842 
09843             rms = sqrt(rms * (uorder + 1) / npoints);
09844 
09845             cpl_bivector_unwrap_vectors(list);
09846             cpl_vector_unwrap(wave);
09847             cpl_vector_unwrap(offs);
09848             cpl_table_delete(dummy);
09849 
09850             /*
09851              * Now correct the coefficients of the corresponding IDS
09852              * polynomials related to this slit:
09853              */
09854 
09855             ylow = position[i];
09856             yhig = ylow + length[i];
09857 
09858             for (j = 0; j <= uorder; j++) {
09859                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09860                 if (j)
09861                     c = m;
09862                 else
09863                     c = q;
09864                 for (k = ylow; k < yhig; k++)
09865                     data[k] += c;
09866             }
09867 
09868             data = cpl_table_get_data_double(idscoeff, "error");
09869             for (k = ylow; k < yhig; k++)
09870                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09871 
09872             idata = cpl_table_get_data_int(idscoeff, "nlines");
09873             for (k = ylow; k < yhig; k++)
09874                  idata[k] = npoints;
09875 
09876             /*
09877              * If a wavelengths map was provided, correct it to keep
09878              * into account the alignment to skylines:
09879              */
09880 
09881             if (calibration) {
09882                 for (j = ylow; j < yhig; j++) {
09883                     for (k = 1; k < nx; k++) {
09884                         lambda1 = cdata[k - 1 + j*nx];
09885                         lambda2 = cdata[k + j*nx];
09886                         if (lambda1 < 1.0 || lambda2 < 1.0)
09887                             continue;
09888                         offset = q + m*(lambda1-refwave);
09889                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09890                     }
09891                 }
09892             }
09893         }
09894         else {
09895 
09896             /*
09897              * Just compute median offset
09898              */
09899 
09900             offs = cpl_vector_wrap(npoints,
09901                                    cpl_table_get_data_double(dummy, "offset"));
09902 
09903             offset = cpl_vector_get_median_const(offs);
09904 
09905             if (npoints > 1)
09906                 rms = cpl_table_get_column_stdev(dummy, "offset");
09907             else
09908                 rms = 0.0;
09909 
09910             rms /= sqrt(npoints);
09911 
09912             cpl_vector_unwrap(offs);
09913             cpl_table_delete(dummy);
09914 
09915             /*
09916              * Now correct the constant term of the corresponding IDS
09917              * polynomials related to this slit:
09918              */
09919 
09920             ylow = position[i];
09921             yhig = ylow + length[i];
09922 
09923             data = cpl_table_get_data_double(idscoeff, clab[0]);
09924             for (k = ylow; k < yhig; k++)
09925                 data[k] += offset;
09926 
09927             data = cpl_table_get_data_double(idscoeff, "error");
09928             for (k = ylow; k < yhig; k++)
09929                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09930 
09931             idata = cpl_table_get_data_int(idscoeff, "nlines");
09932             for (k = ylow; k < yhig; k++)
09933                  idata[k] = npoints;
09934 
09935             /*
09936              * If a wavelengths map was provided, correct it to keep
09937              * into account the alignment to skylines. Note that 
09938              * the offset must be converted from pixels to wavelengths.
09939              */
09940 
09941             if (calibration) {
09942                 for (j = ylow; j < yhig; j++) {
09943                     for (k = 1; k < nx; k++) {
09944                         lambda1 = cdata[k - 1 + j*nx];
09945                         lambda2 = cdata[k + j*nx];
09946                         if (lambda1 < 1.0 || lambda2 < 1.0)
09947                             continue; 
09948                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09949                     }
09950                 }
09951             }
09952         }
09953     }
09954 
09955     return offsets;
09956 
09957 }
09958 
09959 
10021 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
10022                                     double firstLambda, double lastLambda, 
10023                                     cpl_table *idscoeff, cpl_vector *skylines, 
10024                                     int highres, int order, 
10025                                     cpl_image *calibration, int sradius)
10026 {
10027     const char *func = "mos_wavelength_align_lss";
10028 
10029     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10030                                                  /* Max order is 5 */
10031     double         *line;
10032     double         *data;
10033     double         *wdata;
10034     double         *odata;
10035     double          expPos, offset;
10036     double          c;
10037     double          lambda1, lambda2;
10038     double          rms;
10039     float           pos;
10040     float          *sdata;
10041     float          *cdata;
10042     int            *idata;
10043     int             startPos, endPos;
10044     int             window = 2*sradius + 1;
10045     int             nlines;
10046     int             npoints;
10047     int             nrows;
10048     int             nx, ny;
10049     int             idsorder, uorder;
10050     int             missing;
10051     int             i;
10052     cpl_size        j, k;
10053 
10054     char            name[MAX_COLNAME];
10055     char            fname[MAX_COLNAME];
10056 
10057     cpl_polynomial *ids;
10058     cpl_polynomial *polycorr;
10059     cpl_table      *offsets;
10060     cpl_table      *fittable;
10061     cpl_table      *dummy;
10062     cpl_vector     *wave;
10063     cpl_vector     *offs;
10064     cpl_vector     *row;
10065     
10066 
10067     if (idscoeff == NULL) {
10068         cpl_msg_error(func, "An IDS coeff table must be given");
10069         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10070         return NULL;
10071     }
10072 
10073     if (image == NULL) {
10074         cpl_msg_error(func, "A scientific spectral image must be given");
10075         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10076         return NULL;
10077     }
10078 
10079     if (skylines) {
10080         line = cpl_vector_get_data(skylines);
10081         nlines = cpl_vector_get_size(skylines);
10082     }
10083     else {
10084         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10085                         "given: using internal list of reference sky lines");
10086         if (highres) {
10087            line = default_lines_hi;
10088            nlines = sizeof(default_lines_hi) / sizeof(double);
10089         }
10090         else {
10091            line = default_lines_lo;
10092            nlines = sizeof(default_lines_lo) / sizeof(double);
10093         }
10094     }
10095 
10096     if (calibration)
10097         cdata = cpl_image_get_data(calibration);
10098 
10099     nx = cpl_image_get_size_x(image);
10100     ny = cpl_image_get_size_y(image);
10101 
10102     sdata = cpl_image_get_data(image);
10103 
10104     if (ny != cpl_table_get_nrow(idscoeff)) {
10105         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10106         return NULL;
10107     }
10108     
10109 
10110     /*FIXME: This is a remnant of the adaptation of the function
10111      * mos_wavelength_align(), where an offset table was created.
10112      * I leave it here because I am in a hurry, it is just used to
10113      * hold the list of selected sky lines.
10114      *
10115      * Define table of wavelengths
10116      */
10117 
10118     nrows = 0;
10119     for (i = 0; i < nlines; i++)
10120         if (line[i] > firstLambda && line[i] < lastLambda)
10121             nrows++;
10122 
10123     offsets = cpl_table_new(nrows);
10124     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10125     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10126 
10127     nrows = 0;
10128     for (i = 0; i < nlines; i++) {
10129         if (line[i] > firstLambda && line[i] < lastLambda) {
10130             cpl_table_set_double(offsets, "wave", nrows, line[i]);
10131             nrows++;
10132         }
10133     }
10134 
10135     /*
10136      * Here "line" is made to point to the new list of selected wavelengths
10137      */
10138 
10139     line = cpl_table_get_data_double(offsets, "wave");
10140     nlines = nrows;
10141 
10142     idsorder = 0;
10143     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10144         ++idsorder;
10145     --idsorder;
10146 
10147 
10148     /*
10149      * Allocate a dummy table for collecting all the offsets
10150      * for all the lines
10151      */
10152 
10153     dummy = cpl_table_new(ny);
10154     for (j = 0; j < nlines; j++) {
10155         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10156         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10157         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10158         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10159     }
10160 
10161     for (j = 0; j < ny; j++, sdata += nx) {
10162 
10163         /*
10164          * Get the IDS polynomial for the current slit row
10165          */
10166 
10167         missing = 0;
10168         ids = cpl_polynomial_new(1);
10169         for (k = 0; k <= idsorder; k++) {
10170             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10171             if (missing) {
10172                 cpl_polynomial_delete(ids);
10173                 break;
10174             }
10175             cpl_polynomial_set_coeff(ids, &k, c);
10176         }
10177         if (missing)
10178             continue;
10179 
10180         for (k = 0; k < nlines; k++) {
10181             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10182             startPos = expPos - sradius;
10183             endPos   = startPos + window;
10184             if (startPos < 0 || endPos >= nx)
10185                 continue;
10186            
10187             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10188                 pos += startPos;
10189                 offset = pos - expPos;
10190                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10191                 cpl_table_set_double(dummy, name, j, offset);
10192             }
10193         }
10194 
10195         cpl_polynomial_delete(ids);
10196     }
10197 
10198 
10199     /*
10200      * At this point for each sky line we model its offset along
10201      * the image rows using a robust linear fitting
10202      */
10203 
10204     for (j = 0; j < nlines; j++) {
10205         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10206         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10207         if (cpl_table_has_valid(dummy, name)) {
10208 
10209             /*
10210              * In the following, the "fittable" is just a tool for
10211              * eliminating invalid points from the vectors to be fitted.
10212              */
10213 
10214             double        q, m;
10215             cpl_bivector *list;
10216 
10217             fittable = cpl_table_new(ny);
10218             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10219             cpl_table_set_column_unit(fittable, "row", "pixel");
10220             for (k = 0; k < ny; k++)
10221                  cpl_table_set_double(fittable, "row", k, k);
10222             cpl_table_duplicate_column(fittable, "offset", dummy, name);
10223             npoints = ny - cpl_table_count_invalid(fittable, "offset");
10224             cpl_table_erase_invalid(fittable);
10225             row = cpl_vector_wrap(npoints,
10226                                cpl_table_get_data_double(fittable, "row"));
10227             offs = cpl_vector_wrap(npoints,
10228                                cpl_table_get_data_double(fittable, "offset"));
10229             list = cpl_bivector_wrap_vectors(row, offs);
10230             robustLinearFit(list, &q, &m, &rms);
10231             cpl_bivector_unwrap_vectors(list);
10232             cpl_vector_unwrap(row);
10233             cpl_vector_unwrap(offs);
10234             cpl_table_delete(fittable);
10235             for (k = 0; k < ny; k++)
10236                  cpl_table_set_double(dummy, fname, k, q + m*k);
10237         }
10238     }
10239 
10240 
10241     /*
10242      * Now each dummy table row consists of a sequence of offsets,
10243      * one for each wavelength. A table row corresponds to an image row.
10244      * We must fit a polynomial to each one of these rows, in order to
10245      * express the offsets as a function of wavelength. The obtained 
10246      * polynomial coefficients are used to correct the IDS coefficients.
10247      */
10248 
10249     for (i = 0; i < ny; i++) {
10250 
10251         if (!cpl_table_is_valid(idscoeff, clab[0], i))
10252             continue;
10253 
10254         npoints = 0;
10255         for (j = 0; j < nlines; j++) {
10256             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10257             if (cpl_table_is_valid(dummy, name, i))
10258                 npoints++;
10259         }
10260 
10261         if (npoints == 0)
10262             continue;
10263 
10264         uorder = order;
10265         if (npoints <= uorder)
10266             uorder = npoints - 1;
10267 
10268         if (uorder > 1) {
10269 
10270             /*
10271              * Model offsets with polynomial fitting
10272              */
10273 
10274             wave = cpl_vector_new(npoints);
10275             wdata = cpl_vector_get_data(wave);
10276             offs = cpl_vector_new(npoints);
10277             odata = cpl_vector_get_data(offs);
10278 
10279             npoints = 0;
10280             for (j = 0; j < nlines; j++) {
10281                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10282                 if (cpl_table_is_valid(dummy, name, i)) {
10283                     wdata[npoints] = line[j] - refwave;
10284                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10285                     npoints++;
10286                 }
10287             }
10288 
10289             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10290 
10291             rms = sqrt(rms * (uorder + 1) / npoints);
10292 
10293             cpl_vector_delete(wave);
10294             cpl_vector_delete(offs);
10295 
10296             /*
10297              * Now correct the coefficients of the corresponding IDS
10298              * polynomials related to this slit:
10299              */
10300 
10301             for (j = 0; j <= uorder; j++) {
10302                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10303                 c = cpl_polynomial_get_coeff(polycorr, &j);
10304                 data[i] += c;
10305             }
10306 
10307             data = cpl_table_get_data_double(idscoeff, "error");
10308             data[i] = sqrt(data[i]*data[i] + rms*rms);
10309 
10310             idata = cpl_table_get_data_int(idscoeff, "nlines");
10311             idata[i] = npoints;
10312 
10313             /*
10314              * If a wavelengths map was provided, correct it to keep
10315              * into account the alignment to skylines:
10316              */
10317 
10318             if (calibration) {
10319                 for (k = 1; k < nx; k++) {
10320                     lambda1 = cdata[k - 1 + i*nx];
10321                     lambda2 = cdata[k + i*nx];
10322                     if (lambda1 < 1.0 || lambda2 < 1.0)
10323                         continue;
10324                     offset = cpl_polynomial_eval_1d(polycorr,
10325                                                     lambda1-refwave, NULL);
10326                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10327                 }
10328             }
10329 
10330             cpl_polynomial_delete(polycorr);
10331 
10332         }
10333         else if (uorder == 1) {
10334 
10335             /*
10336              * Model offsets with robust linear fitting
10337              */
10338 
10339             cpl_bivector *list;
10340             double        q, m;
10341 
10342             wave = cpl_vector_new(npoints);
10343             wdata = cpl_vector_get_data(wave);
10344             offs = cpl_vector_new(npoints);
10345             odata = cpl_vector_get_data(offs);
10346 
10347             npoints = 0;
10348             for (j = 0; j < nlines; j++) {
10349                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10350                 if (cpl_table_is_valid(dummy, name, i)) {
10351                     wdata[npoints] = line[j] - refwave;
10352                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10353                     npoints++;
10354                 }
10355             }
10356 
10357             list = cpl_bivector_wrap_vectors(wave, offs);
10358             robustLinearFit(list, &q, &m, &rms);
10359 
10360             rms = sqrt(rms * (uorder + 1) / npoints);
10361 
10362             cpl_bivector_unwrap_vectors(list);
10363             cpl_vector_delete(wave);
10364             cpl_vector_delete(offs);
10365 
10366             /*
10367              * Now correct the coefficients of the corresponding IDS
10368              * polynomials related to this row:
10369              */
10370 
10371             for (j = 0; j <= uorder; j++) {
10372                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10373                 if (j)
10374                     c = m;
10375                 else
10376                     c = q;
10377                 data[i] += c;
10378             }
10379 
10380             data = cpl_table_get_data_double(idscoeff, "error");
10381             data[i] = sqrt(data[i]*data[i] + rms*rms);
10382 
10383             idata = cpl_table_get_data_int(idscoeff, "nlines");
10384             idata[i] = npoints;
10385 
10386             /*
10387              * If a wavelengths map was provided, correct it to keep
10388              * into account the alignment to skylines:
10389              */
10390 
10391             if (calibration) {
10392                 for (k = 1; k < nx; k++) {
10393                     lambda1 = cdata[k - 1 + i*nx];
10394                     lambda2 = cdata[k + i*nx];
10395                     if (lambda1 < 1.0 || lambda2 < 1.0)
10396                         continue;
10397                     offset = q + m*(lambda1-refwave);
10398                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10399                 }
10400             }
10401         }
10402         else {
10403 
10404             /*
10405              * Just compute median offset
10406              */
10407 
10408             offs = cpl_vector_new(npoints);
10409             odata = cpl_vector_get_data(offs);
10410 
10411             npoints = 0;
10412             for (j = 0; j < nlines; j++) {
10413                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10414                 if (cpl_table_is_valid(dummy, name, i)) {
10415                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10416                     npoints++;
10417                 }
10418             }
10419 
10420             offset = cpl_vector_get_median_const(offs);
10421 
10422             if (npoints > 1) {
10423                 rms = cpl_vector_get_stdev(offs);
10424             }
10425             else if (npoints == 1) {
10426                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10427                 if (cpl_table_has_valid(dummy, name)) {
10428                     rms = cpl_table_get_column_stdev(dummy, name);
10429                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10430                 }
10431                 else {
10432                     rms = 0.0;
10433                 }
10434             }
10435             else {
10436                 rms = 0.0;
10437             }
10438 
10439             rms /= sqrt(npoints);
10440 
10441             cpl_vector_delete(offs);
10442 
10443             /*
10444              * Now correct the constant term of the corresponding IDS
10445              * polynomials related to this slit:
10446              */
10447 
10448             data = cpl_table_get_data_double(idscoeff, clab[0]);
10449             data[i] += offset;
10450 
10451             data = cpl_table_get_data_double(idscoeff, "error");
10452             data[i] = sqrt(data[i]*data[i] + rms*rms);
10453 
10454             idata = cpl_table_get_data_int(idscoeff, "nlines");
10455             idata[i] = npoints;
10456 
10457             /*
10458              * If a wavelengths map was provided, correct it to keep
10459              * into account the alignment to skylines. Note that
10460              * the offset must be converted from pixels to wavelengths.
10461              */
10462 
10463             if (calibration) {
10464                 for (k = 1; k < nx; k++) {
10465                     lambda1 = cdata[k - 1 + i*nx];
10466                     lambda2 = cdata[k + i*nx];
10467                     if (lambda1 < 1.0 || lambda2 < 1.0)
10468                         continue;
10469                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10470                 }
10471             }
10472         }
10473     }
10474 
10475     missing = 1;
10476     for (j = 0; j < nlines; j++) {
10477         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10478         if (cpl_table_has_valid(dummy, name)) {
10479             missing = 0;
10480             offset = cpl_table_get_column_median(dummy, name);
10481             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
10482                          line[j], offset);
10483         }
10484         else {
10485             cpl_msg_info(func, 
10486                          "Median offset for %.2f: not available", line[j]);
10487         }
10488     }
10489 
10490     cpl_table_delete(offsets);
10491 
10492     if (missing) {
10493         cpl_table_delete(dummy);
10494         dummy = NULL;
10495     }
10496 
10497     return dummy;
10498 
10499 }
10500 
10501 
10529 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
10530                            double wavestart, double dispersion, int radius,
10531                            int highres)
10532 {
10533 
10534     const char *func = "mos_distortions_rms";
10535 
10536     int xlen;
10537     int ylen;
10538     int numLines;
10539     int cpix, npix, nzero;
10540     int sp, ep;
10541     int i, j, k;
10542     int npeaks, allPeaks;
10543 
10544     float *profile;
10545     float  peak, expectPeak, offset;
10546     double lambda;
10547 
10548     double  average;
10549     double  rms, oneRms;
10550 
10551     float  *sdata;
10552     double *wdata;
10553 
10554   
10555     xlen = cpl_image_get_size_x(rectified);
10556     ylen = cpl_image_get_size_y(rectified);
10557     sdata = cpl_image_get_data(rectified);
10558 
10559     if (lines) {
10560         wdata = cpl_vector_get_data(lines);
10561         numLines = cpl_vector_get_size(lines);
10562     }
10563     else {
10564         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10565                         "given: using internal list of reference sky lines");
10566         if (highres) {
10567            wdata = default_lines_hi;
10568            numLines = sizeof(default_lines_hi) / sizeof(double);
10569         }
10570         else {
10571            wdata = default_lines_lo;
10572            numLines = sizeof(default_lines_lo) / sizeof(double);
10573         }
10574     }
10575 
10576     npix = 2 * radius + 1;
10577     profile = cpl_calloc(npix, sizeof(float));
10578 
10579     rms = 0.0;
10580     allPeaks = 0;
10581 
10582     for (i = 0; i < numLines; i++) {
10583 
10584         /*
10585          *  Expected peak and closest pixel to specified wavelength.
10586          */
10587 
10588         lambda = wdata[i];
10589         expectPeak = (lambda - wavestart) / dispersion;
10590         cpix = floor(expectPeak + 0.5);
10591 
10592         /*
10593          *  Search interval for peak. Abort if too close to image border.
10594          */
10595 
10596         sp = cpix - radius;
10597         ep = cpix + radius;
10598 
10599         if (sp < 0 || ep > xlen)
10600             continue;
10601 
10602         average = 0.0;
10603         npeaks = 0;
10604         oneRms = 0.0;
10605 
10606         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
10607             nzero = 0;
10608             for (k = 0; k < npix; k++) {
10609                 profile[k] = sdata[sp + k + j * xlen];
10610                 if (fabs(profile[k]) < 0.0001)
10611                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
10612             }
10613             if (nzero > 0)
10614                 continue;
10615 
10616             if (peakPosition(profile, npix, &peak, 1) == 0) {
10617                 offset = (sp + peak) - expectPeak;
10618                 average += offset;
10619                 rms += fabs(offset);
10620                 oneRms += fabs(offset);
10621                 npeaks++;
10622                 allPeaks++;
10623             }
10624         }
10625 
10626         if (npeaks)
10627             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
10628                          lambda, oneRms / npeaks * 1.25, npeaks);
10629         else
10630             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
10631     }
10632 
10633     cpl_free(profile);
10634 
10635     if (allPeaks < 10)
10636         return 0.0;
10637 
10638     rms /= allPeaks;
10639     rms *= 1.25;       /* Factor to convert average deviation to sigma */
10640 
10641     return rms;
10642 
10643 }
10644 
10645 
10666 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
10667                          double blue, double red, double dispersion, int trend)
10668 {
10669     const char *func = "mos_map_pixel";
10670 
10671     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10672                                                  /* Max order is 5 */
10673 
10674     cpl_polynomial *ids;
10675     cpl_image      *map;
10676     float          *mdata;
10677     double          lambda;
10678     double          c;
10679     int             order;
10680     int             xsize, ysize;
10681     int             missing;
10682     int             i, j;
10683     cpl_size        k;
10684 
10685 
10686     if (idscoeff == NULL) {
10687         cpl_msg_error(func, "An IDS coeff table must be given");
10688         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10689         return NULL;
10690     }
10691 
10692     xsize = (red - blue) / dispersion;
10693     ysize = cpl_table_get_nrow(idscoeff);
10694     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10695     mdata = cpl_image_get_data(map);
10696 
10697     order = 0;
10698     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10699         ++order;
10700     --order;
10701 
10702     for (i = 0; i < ysize; i++, mdata += xsize) {
10703 
10704         missing = 0;
10705         ids = cpl_polynomial_new(1);
10706         for (k = trend; k <= order; k++) {
10707             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10708             if (missing) {
10709                 cpl_polynomial_delete(ids);
10710                 break;
10711             }
10712             cpl_polynomial_set_coeff(ids, &k, c);
10713         }
10714         if (missing)
10715             continue;
10716 
10717         for (j = 0; j < xsize; j++) {
10718             lambda = blue + j*dispersion;
10719             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
10720         }
10721 
10722         cpl_polynomial_delete(ids);
10723     }
10724 
10725     return map;
10726 
10727 }
10728 
10729 
10751 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
10752                             double blue, double red)
10753 {
10754     const char *func = "mos_map_idscoeff";
10755 
10756     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10757                                                  /* Max order is 5 */
10758 
10759     cpl_polynomial *ids;
10760     cpl_image      *map;
10761     float          *mdata;
10762     double          lambda;
10763     double          c;
10764     int             order;
10765     int             ysize;
10766     int             missing;
10767     int             i, j;
10768     cpl_size        k;
10769 
10770 
10771     if (idscoeff == NULL) {
10772         cpl_msg_error(func, "An IDS coeff table must be given");
10773         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10774         return NULL;
10775     }
10776 
10777     if (xsize < 1) {
10778         cpl_msg_error(func, "Invalid image size");
10779         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10780         return NULL;
10781     }
10782 
10783     if (xsize < 20 || xsize > 5000) {
10784         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10785                         xsize);
10786     }
10787 
10788     ysize = cpl_table_get_nrow(idscoeff);
10789     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10790     mdata = cpl_image_get_data(map);
10791 
10792     order = 0;
10793     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10794         ++order;
10795     --order;
10796 
10797     for (i = 0; i < ysize; i++, mdata += xsize) {
10798 
10799         missing = 0;
10800         ids = cpl_polynomial_new(1);
10801         for (k = 0; k <= order; k++) {
10802             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10803             if (missing) {
10804                 cpl_polynomial_delete(ids);
10805                 break;
10806             }
10807             cpl_polynomial_set_coeff(ids, &k, c);
10808         }
10809         if (missing)
10810             continue;
10811 
10812         for (j = 0; j < xsize; j++) {
10813             lambda = mos_eval_dds(ids, blue, red, reference, j);
10814 
10815             if (lambda >= blue && lambda <= red) {
10816                 mdata[j] = lambda;
10817             }
10818         }
10819 
10820         cpl_polynomial_delete(ids);
10821     }
10822 
10823     return map;
10824 
10825 }
10826 
10827 
10862 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10863                                cpl_table *slits, cpl_table *polytraces, 
10864                                double reference, double blue, double red, 
10865                                double dispersion)
10866 {
10867     const char *func = "mos_map_wavelengths";
10868 
10869     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10870                                                  /* Max order is 5 */
10871     cpl_polynomial *polytop;
10872     cpl_polynomial *polybot;
10873     cpl_image      *remapped;
10874     float          *data;
10875     float          *wdata;
10876     float          *sdata;
10877     float          *xdata;
10878     double          vtop, vbot, value;
10879     double          top, bot;
10880     double          coeff;
10881     double          ytop, ybot;
10882     double          ypos;
10883     double          fvalue;
10884     int             ivalue;
10885     int             yint, ysize, yprev;
10886     int             nslits;
10887     int             npseudo;
10888     int            *slit_id;
10889     int            *position;
10890     int            *length;
10891     int             nx, ny;
10892     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10893     int             missing_top, missing_bot;
10894     int             null;
10895     int             order;
10896     int             i, j;
10897     cpl_size        k;
10898 
10899 
10900     if (spatial == NULL || calibration == NULL || 
10901         slits == NULL || polytraces == NULL) {
10902         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10903         return NULL;
10904     }
10905 
10906     if (dispersion <= 0.0) {
10907         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10908         return NULL;
10909     }
10910 
10911     if (red - blue < dispersion) {
10912         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10913         return NULL;
10914     }
10915 
10916     nx = cpl_image_get_size_x(spatial);
10917     ny = cpl_image_get_size_y(spatial);
10918     ysize = cpl_image_get_size_y(calibration);
10919     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10920     data  = cpl_image_get_data(remapped);
10921     sdata = cpl_image_get_data(spatial);
10922     wdata = cpl_image_get_data(calibration);
10923 
10924     nslits   = cpl_table_get_nrow(slits);
10925     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10926     order    = cpl_table_get_ncol(polytraces) - 2;
10927     position = cpl_table_get_data_int(slits, "position");
10928     length   = cpl_table_get_data_int(slits, "length");
10929 
10930     /*
10931      * The spatial resampling is performed for a certain number of 
10932      * pixels above and below the position of the reference wavelength:
10933      */
10934 
10935     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
10936     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
10937 
10938     for (i = 0; i < nslits; i++) {
10939 
10940         if (length[i] == 0)
10941             continue;
10942 
10943         /*
10944          * Note that the x coordinate of the reference pixels on the CCD
10945          * is taken arbitrarily at the top end of each slit. This wouldn't
10946          * be entirely correct in case of curved slits, or in presence of
10947          * heavy distortions: in such cases the spatial resampling is
10948          * really performed across a wide range of wavelengths. But
10949          * the lag between top and bottom spectral curvature models 
10950          * would introduce even in such cases negligible effects on
10951          * the spectral spatial resampling.
10952          */
10953 
10954         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10955 
10956         start_pixel = refpixel - pixel_below;
10957         if (start_pixel < 0)
10958             start_pixel = 0;
10959 
10960         end_pixel = refpixel + pixel_above;
10961         if (end_pixel > nx)
10962             end_pixel = nx;
10963 
10964         /*
10965          * Recover from the table of spectral curvature coefficients
10966          * the curvature polynomials.
10967          */
10968 
10969         missing_top = 0;
10970         polytop = cpl_polynomial_new(1);
10971         for (k = 0; k <= order; k++) {
10972             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10973             if (null) {
10974                 cpl_polynomial_delete(polytop);
10975                 missing_top = 1;
10976                 break;
10977             }
10978             cpl_polynomial_set_coeff(polytop, &k, coeff);
10979         }
10980 
10981         missing_bot = 0;
10982         polybot = cpl_polynomial_new(1);
10983         for (k = 0; k <= order; k++) {
10984             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10985             if (null) {
10986                 cpl_polynomial_delete(polybot);
10987                 missing_bot = 1;
10988                 break;
10989             }
10990             cpl_polynomial_set_coeff(polybot, &k, coeff);
10991         }
10992 
10993         if (missing_top && missing_bot) {
10994             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10995                           slit_id[i]);
10996             continue;
10997         }
10998 
10999         /*
11000          * In case just one of the two edges was not traced, the other
11001          * edge curvature model is duplicated and shifted to the other
11002          * end of the slit: better than nothing!
11003          */
11004 
11005         if (missing_top) {
11006             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11007                           "the spectral curvature of the lower edge "
11008                           "is used instead.", slit_id[i]);
11009             polytop = cpl_polynomial_duplicate(polybot);
11010             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11011             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11012             k = 0;
11013             coeff = cpl_polynomial_get_coeff(polybot, &k);
11014             coeff += ytop - ybot;
11015             cpl_polynomial_set_coeff(polytop, &k, coeff);
11016         }
11017 
11018         if (missing_bot) {
11019             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11020                           "the spectral curvature of the upper edge "
11021                           "is used instead.", slit_id[i]);
11022             polybot = cpl_polynomial_duplicate(polytop);
11023             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11024             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11025             k = 0;
11026             coeff = cpl_polynomial_get_coeff(polytop, &k);
11027             coeff -= ytop - ybot;
11028             cpl_polynomial_set_coeff(polybot, &k, coeff);
11029         }
11030 
11031         /*
11032          * Point to current slit on wavelength calibration image.
11033          * Note that the npseudo value related to this slit is equal 
11034          * to the number of spatial pseudo-pixels decreased by 1 
11035          * (compare with function mos_spatial_calibration()).
11036          */
11037 
11038         xdata = wdata + nx*position[i];
11039         npseudo = length[i] - 1;
11040 
11041         /*
11042          * Write interpolated wavelengths to CCD image
11043          */
11044 
11045         for (j = start_pixel; j < end_pixel; j++) {
11046             top = cpl_polynomial_eval_1d(polytop, j, NULL);
11047             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11048             for (k = 0; k <= npseudo; k++) {
11049                 ypos = top - k*(top-bot)/npseudo;
11050                 yint = ypos;
11051 
11052                 /* 
11053                  * The line:
11054                  *     value = sdata[j + nx*yint];
11055                  * should be equivalent to:
11056                  *     value = npseudo*(top-yint)/(top-bot);
11057                  */
11058 
11059                 if (yint < 0 || yint >= ny-1) {
11060                     yprev = yint;
11061                     continue;
11062                 }
11063 
11064                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
11065                 ivalue = value;               /* Nearest spatial pixels:   */
11066                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
11067                 if (ivalue < npseudo && ivalue >= 0) {
11068                     vtop = xdata[j + nx*(npseudo-ivalue)];
11069                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
11070                     if (vtop < 1.0) {  /* Impossible wavelength */
11071                         if (vbot < 1.0) {
11072                             value = 0.0;
11073                         }
11074                         else {
11075                             value = vbot;
11076                         }
11077                     }
11078                     else if (vbot < 1.0) {
11079                         if (k)
11080                             value = vtop;
11081                         else
11082                             value = 0.0;
11083                     }
11084                     else if (fabs(vbot-vtop) > 10*dispersion) {
11085                         value = 0.0;
11086                     }
11087                     else {
11088                         value = vtop*(1-fvalue) + vbot*fvalue;
11089                     }
11090                     data[j + nx*yint] = value;
11091 
11092                     if (k) {
11093 
11094                         /*
11095                          * This is added to recover lost pixels on
11096                          * the CCD image (pixels are lost because
11097                          * the CCD pixels are less than npseudo+1).
11098                          */
11099 
11100                         if (yprev - yint > 1) {
11101                             value = sdata[j + nx*(yint+1)];
11102                             ivalue = value;
11103                             fvalue = value - ivalue;
11104                             if (ivalue < npseudo && ivalue >= 0) {
11105                                 vtop = xdata[j + nx*(npseudo-ivalue)];
11106                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
11107                                 if (vtop < 1.0) {
11108                                     if (vbot < 1.0) {
11109                                         value = data[j + nx*(yint+1)];
11110                                     }
11111                                     else {
11112                                         value = vbot;
11113                                     }
11114                                 }
11115                                 else if (vbot < 1.0) {
11116                                     value = vtop;
11117                                 }
11118                                 else if (fabs(vbot-vtop) > 2*dispersion) {
11119                                     value = vtop;
11120                                 }
11121                                 else {
11122                                     value = vtop*(1-fvalue) + vbot*fvalue;
11123                                 }
11124                                 data[j + nx*(yint+1)] = value;
11125                             }
11126                         }
11127                     }
11128                 }
11129                 yprev = yint;
11130             }
11131         }
11132         cpl_polynomial_delete(polytop);
11133         cpl_polynomial_delete(polybot);
11134     }
11135 
11136     return remapped;
11137 }
11138 
11212 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
11213                             cpl_image *spatial, cpl_table *slits,
11214                             cpl_table *polytraces, double reference,
11215                             double blue, double red, double dispersion,
11216                             int flux)
11217 {
11218     const char *func = "mos_map_spectrum";
11219     
11220     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11221                                                  /* Max order is 5 */
11222     cpl_polynomial *polytop;
11223     cpl_polynomial *polybot;
11224     cpl_image      *remapped;
11225     cpl_image     **exslit;
11226     float          *data;
11227     float          *wdata;
11228     float          *sdata;
11229     float          *xdata;
11230     double          lambda00, lambda01, lambda10, lambda11, lambda;
11231     double          space00, space01, space10, space11, space;
11232     double          value00, value01, value10, value11, value0, value1, value;
11233     double          dL, dS;
11234     double          top, bot;
11235     double          coeff;
11236     double          ytop, ybot;
11237     double          xfrac, yfrac;
11238     int             yint, ysize;
11239     int             itop, ibot;
11240     int             shift;
11241     int             L, S;
11242     int             nslits;
11243     int             npseudo;
11244     int            *slit_id;
11245     int            *position;
11246     int            *length;
11247     int             nx, ny;
11248     int             x, y;
11249     int             nlambda;
11250     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11251     int             missing_top, missing_bot; 
11252     int             null;
11253     int             order;
11254     int             i; 
11255     cpl_size        k;
11256     
11257 
11258     flux += flux;
11259 
11260     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11261         slits == NULL || polytraces == NULL) { 
11262         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11263         return NULL;
11264     }
11265 
11266     if (dispersion <= 0.0) {
11267         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11268         return NULL;
11269     }
11270 
11271     if (red - blue < dispersion) {
11272         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11273         return NULL;
11274     }
11275     
11276     nx = cpl_image_get_size_x(spectra);
11277     ny = cpl_image_get_size_y(spectra);
11278 
11279     if (nx != cpl_image_get_size_x(spatial) ||
11280         ny != cpl_image_get_size_y(spatial) ||
11281         nx != cpl_image_get_size_x(wavecalib) ||
11282         ny != cpl_image_get_size_y(wavecalib)) {
11283         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11284         return NULL;
11285     }
11286 
11287     nlambda     = STRETCH_FACTOR * (red - blue) / dispersion;
11288     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11289     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11290 
11291     data  = cpl_image_get_data(spectra);
11292     sdata = cpl_image_get_data(spatial);
11293     wdata = cpl_image_get_data(wavecalib);
11294     
11295     nslits   = cpl_table_get_nrow(slits);
11296     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11297     order    = cpl_table_get_ncol(polytraces) - 2;
11298     position = cpl_table_get_data_int(slits, "position");
11299     length   = cpl_table_get_data_int(slits, "length");
11300     
11301     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11302 
11303     for (i = 0; i < nslits; i++) {
11304 
11305          if (length == 0)
11306              continue;
11307 
11308         /*
11309          * Note that the x coordinate of the reference pixels on the CCD
11310          * is taken arbitrarily at the top end of each slit. This wouldn't
11311          * be entirely correct in case of curved slits, or in presence of
11312          * heavy distortions: in such cases the spatial resampling is
11313          * really performed across a wide range of wavelengths. But
11314          * the lag between top and bottom spectral curvature models
11315          * would introduce even in such cases negligible effects on
11316          * the spectral spatial resampling.
11317          */
11318 
11319         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11320 
11321         start_pixel = refpixel - pixel_below;
11322         if (start_pixel < 1)
11323             start_pixel = 1;
11324 
11325         end_pixel = refpixel + pixel_above;
11326         if (end_pixel > nx)
11327             end_pixel = nx;
11328 
11329         /*
11330          * Recover from the table of spectral curvature coefficients
11331          * the curvature polynomials.
11332          */
11333 
11334         missing_top = 0;
11335         polytop = cpl_polynomial_new(1);
11336         for (k = 0; k <= order; k++) {
11337             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11338             if (null) {
11339                 cpl_polynomial_delete(polytop);
11340                 missing_top = 1;
11341                 break;
11342             }
11343             cpl_polynomial_set_coeff(polytop, &k, coeff);
11344         }
11345 
11346         missing_bot = 0;
11347         polybot = cpl_polynomial_new(1);
11348         for (k = 0; k <= order; k++) {
11349             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11350             if (null) {
11351                 cpl_polynomial_delete(polybot);
11352                 missing_bot = 1;
11353                 break;
11354             }
11355             cpl_polynomial_set_coeff(polybot, &k, coeff);
11356         }
11357 
11358         if (missing_top && missing_bot) {
11359             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11360                           slit_id[i]);
11361             continue;
11362         }
11363 
11364         /*
11365          * In case just one of the two edges was not traced, the other
11366          * edge curvature model is duplicated and shifted to the other
11367          * end of the slit: better than nothing!
11368          */
11369 
11370         if (missing_top) {
11371             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11372                           "the spectral curvature of the lower edge "
11373                           "is used instead.", slit_id[i]);
11374             polytop = cpl_polynomial_duplicate(polybot);
11375             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11376             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11377             k = 0;
11378             coeff = cpl_polynomial_get_coeff(polybot, &k);
11379             coeff += ytop - ybot;
11380             cpl_polynomial_set_coeff(polytop, &k, coeff);
11381         }
11382 
11383         if (missing_bot) {
11384             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11385                           "the spectral curvature of the upper edge "
11386                           "is used instead.", slit_id[i]);
11387             polybot = cpl_polynomial_duplicate(polytop);
11388             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11389             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11390             k = 0;
11391             coeff = cpl_polynomial_get_coeff(polytop, &k);
11392             coeff -= ytop - ybot;
11393             cpl_polynomial_set_coeff(polybot, &k, coeff);
11394         }
11395 
11396         /*
11397          * Allocate image for current extracted slit
11398          */
11399 
11400         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11401         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11402         npseudo = ceil(top-bot) + 1;
11403 
11404         if (npseudo < 1) {
11405             cpl_polynomial_delete(polytop);
11406             cpl_polynomial_delete(polybot);
11407             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11408                           slit_id[i]);
11409             continue;
11410         }
11411 
11412         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11413         xdata = cpl_image_get_data(exslit[i]);
11414 
11415         /*
11416          * Write interpolated spectral values to remapped slit spectrum.
11417          */
11418 
11419         for (x = start_pixel; x < end_pixel; x++) {
11420             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11421             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11422             itop = top + 1;
11423             ibot = bot;
11424             if (itop < 0)
11425                 itop = 0;
11426             if (itop > ny - 1)
11427                 itop = ny - 1;
11428             if (ibot < 0)
11429                 ibot = 0;
11430             if (ibot > ny - 1)
11431                 ibot = ny - 1;
11432             for (y = ibot; y < itop; y++) {
11433                  lambda11 = wdata[x + y*nx];
11434                  if (lambda11 < 1.0)        /* Impossible wavelength */
11435                      continue;
11436                  space11 = sdata[x + y*nx];
11437                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11438                      continue;
11439                  lambda01 = wdata[x - 1 + y*nx];
11440                  if (lambda01 < 1.0)        /* Impossible wavelength */
11441                      continue;
11442                  space01 = sdata[x - 1 + y*nx];
11443                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11444                      continue;
11445 
11446                  shift = 0;
11447 
11448 /****+
11449                  if (wdata[x + (y+1)*nx] > 1.0) {
11450                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11451                          shift = -1;
11452                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11453                              shift--;
11454                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11455                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11456                              shift++;
11457                          }
11458                      }
11459                      else {
11460                          shift = 1;
11461                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11462                              shift++;
11463                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
11464                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11465                              shift--;
11466                          }
11467                      }
11468                  }
11469 ****/
11470 
11471 /****
11472 printf("y = %d, shift = %d\n", y, shift);
11473 ****/
11474 
11475                  lambda10 = wdata[x + shift + (y+1)*nx];
11476                  if (lambda10 < 1.0)        /* Impossible wavelength */
11477                      continue;
11478                  space10 = sdata[x + shift + (y+1)*nx];
11479                  if (space10 < 0.0)         /* Impossible spatial coordinate */
11480                      continue;
11481                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
11482                  if (lambda00 < 1.0)        /* Impossible wavelength */
11483                      continue;
11484                  space00 = sdata[x - 1 + shift + (y+1)*nx];
11485                  if (space00 < 0.0)         /* Impossible spatial coordinate */
11486                      continue;
11487                  
11488                  /*
11489                   * Find the variation in lambda and space in this
11490                   * position for each CCD pixel (both quantities are 
11491                   * expected to be positive).
11492                   */
11493 
11494                  dL = lambda11 - lambda01;
11495                  dS = space11 - space10;
11496 
11497                  /*
11498                   * Find the position (L,S) of the output pixel 
11499                   * (by integer truncation).
11500                   */
11501 
11502                  L = (lambda11 - blue)/dispersion + 0.5;
11503                  S = space11 + 0.5;                   /* Counted from top! */
11504 
11505                  if (L < 0 || L >= nlambda)
11506                      continue;
11507                  if (S < 0 || S > npseudo)
11508                      continue;
11509 
11510                  /*
11511                   * Find the coordinate of pixel (L,S)
11512                   */
11513 
11514                  lambda = blue + L*dispersion;
11515                  space  = S;
11516 
11517                  /*
11518                   * Find the interpolation point on the CCD: it is
11519                   * defined as the (positive) distance from current
11520                   * CCD pixel (x,y) of the interpolation point (x',y'),
11521                   * measured in CCD pixels. The interpolation point
11522                   * is located between the four CCD pixels selected
11523                   * above.
11524                   */
11525 
11526                  xfrac = (lambda11-lambda)/dL;
11527                  yfrac = (space11-space)/dS;
11528 
11529 /*
11530 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
11531 printf("xyfrac = %f, %f\n", xfrac, yfrac);
11532 */
11533 
11534                  /*
11535                   * Get the four values to interpolate
11536                   */
11537 
11538                  value11 = data[x + y*nx];
11539                  value01 = data[x - 1 + y*nx];
11540                  value10 = data[x + shift + (y+1)*nx];
11541                  value00 = data[x + shift - 1 + (y+1)*nx];
11542 
11543                  /*
11544                   * Interpolation
11545                   */
11546 
11547                  value1 = (1-xfrac)*value11 + xfrac*value01;
11548                  value0 = (1-xfrac)*value10 + xfrac*value00;
11549                  value  = (1-yfrac)*value1  + yfrac*value0;
11550 
11551                  /*
11552                   * Write this value to the appropriate (L,S) coordinate
11553                   * on output slit
11554                   */
11555 
11556                  xdata[L + nlambda*(npseudo-S)] = value;
11557                  
11558             }
11559         }
11560         cpl_polynomial_delete(polytop);
11561         cpl_polynomial_delete(polybot);
11562     }
11563 
11564     /*
11565      * Now all the slits images are copied to a single image
11566      */
11567 
11568     ysize = 0;
11569     for (i = 0; i < nslits; i++)
11570         if (exslit[i])
11571             ysize += cpl_image_get_size_y(exslit[i]);
11572 
11573     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
11574 
11575     yint = -1;
11576     for (i = 0; i < nslits; i++) {
11577         if (exslit[i]) {
11578             yint += cpl_image_get_size_y(exslit[i]);
11579             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
11580             cpl_image_delete(exslit[i]);
11581             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
11582         }
11583     }
11584 
11585     cpl_free(exslit);
11586 
11587     return remapped;
11588 
11589 }
11590 
11591 
11624 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
11625                              double dispersion, double factor, int minpoints,
11626                              cpl_image *skymap)
11627 {
11628     const char *func = "mos_sky_map_super";
11629 
11630     cpl_vector **vector;
11631     cpl_vector **wvector;
11632     double       firstLambda, lastLambda;
11633     double       lambda, lambda1, lambda2;
11634     double       value, value1, value2;
11635     double       frac;
11636     float        min, max;
11637     int         *count;
11638     int          nbin, bin;
11639     int          nx, ny, npix;
11640     int          first_valid, valid_bins;
11641     int          i, j;
11642 
11643     cpl_table   *sky;
11644     double      *sky_spectrum;
11645     double      *sky_wave;
11646     float       *data;
11647     float       *sdata;
11648     float       *kdata;
11649 
11650 
11651     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11652         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11653         return NULL;
11654     }
11655     
11656     if (dispersion <= 0.0) {
11657         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11658         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
11659         return NULL;
11660     }
11661 
11662     nx = cpl_image_get_size_x(spectra);
11663     ny = cpl_image_get_size_y(spectra);
11664     npix = nx * ny;
11665 
11666     if (nx != cpl_image_get_size_x(wavemap) ||
11667         ny != cpl_image_get_size_y(wavemap) ||
11668         nx != cpl_image_get_size_x(skymap) ||
11669         ny != cpl_image_get_size_y(skymap)) {
11670         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11671         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
11672         return NULL;
11673     }
11674 
11675     if (factor < 1.0) {
11676         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11677         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
11678                       cpl_error_get_message());
11679         return NULL;
11680     }
11681 
11682     if (minpoints < 0) {
11683         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11684         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
11685         return NULL;
11686     }
11687 
11688     dispersion /= factor;
11689 
11690 
11691     /*
11692      * Find bluest and reddest wavelengths in the whole image
11693      */
11694 
11695     data = cpl_image_get_data(wavemap);
11696 
11697     for (i = 0; i < npix; i++) {
11698         if (data[i] > 1.0) {
11699             min = max = data[i];
11700             j = i+1;
11701             break;
11702         }
11703     }
11704 
11705     for (i = j; i < npix; i++) {
11706         if (data[i] < 1.0)      /* Impossible wavelength */
11707             continue;
11708         if (min > data[i])
11709             min = data[i];
11710         if (max < data[i])
11711             max = data[i];
11712     }
11713 
11714     firstLambda = min;
11715     lastLambda = max;
11716 
11717 
11718     /*
11719      * Determine length of median spectrum
11720      */
11721 
11722     nbin = (lastLambda - firstLambda) / dispersion;
11723 
11724     /*
11725      * Count how many values will be found for each spectral bin.
11726      * The ith bin has a wavelength range from firstLambda + i*dispersion
11727      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11728      * it is assigned to its central wavelength.
11729      */
11730 
11731     count = cpl_calloc(nbin, sizeof(int));
11732 
11733     data = cpl_image_get_data(wavemap);
11734 
11735     for (i = 0; i < npix; i++) {
11736         if (data[i] < 1.0)
11737             continue;
11738         bin = (data[i] - firstLambda) / dispersion;
11739         if (bin < nbin)                               /* Safer */
11740             count[bin]++;
11741     }
11742 
11743     valid_bins = 0;
11744     for (i = 0; i < nbin; i++)
11745         if (count[i] >= minpoints)
11746             valid_bins++;
11747 
11748     if (valid_bins < nbin/3) {
11749         cpl_msg_warning(func, "Cannot determine a good global sky "
11750                         "spectrum from input data");
11751         return NULL;
11752     }
11753 
11754 
11755     /*
11756      * Allocate an array of vectors with the appropriate size, to
11757      * contain a list of all the spectral pixels values. At the same
11758      * time, reset the array of counters (because we will have to
11759      * count again...).
11760      */
11761 
11762     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11763     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11764     for (i = 0; i < nbin; i++) {
11765         if (count[i] >= minpoints) {
11766             vector[i] = cpl_vector_new(count[i]);
11767             wvector[i] = cpl_vector_new(count[i]);
11768         }
11769         count[i] = 0;
11770     }
11771 
11772 
11773     /*
11774      * Read the wavemap and the spectral images, and add the data values
11775      * to the appropriate wavelength bins
11776      */
11777 
11778     data  = cpl_image_get_data(wavemap);
11779     sdata = cpl_image_get_data(spectra);
11780 
11781     for (i = 0; i < npix; i++) {
11782         if (data[i] < 1.0)
11783             continue;
11784         bin = (data[i] - firstLambda) / dispersion;
11785         if (bin < nbin) {                             /* Safer */
11786             if (vector[bin]) {
11787                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11788                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11789             }
11790             count[bin]++;
11791         }
11792     }
11793 
11794 
11795     /*
11796      * Compute the median flux for each wavelength bin, and destroy
11797      * at the same time the used vectors
11798      */
11799 
11800     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11801     sky_wave = cpl_calloc(nbin, sizeof(double));
11802     for (i = 0; i < nbin; i++) {
11803         if (vector[i]) {
11804             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11805             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11806             cpl_vector_delete(vector[i]);
11807             cpl_vector_delete(wvector[i]);
11808         }
11809     }
11810 
11811     cpl_free(vector);
11812     cpl_free(wvector);
11813 
11814 
11815     /*
11816      * Here possible gaps in the final spectrum are filled by interpolation
11817      */
11818 
11819     for (i = 0; i < nbin; i++) {
11820         if (count[i] >= minpoints) {
11821             first_valid = i;
11822             break;
11823         }
11824     }
11825     
11826     for (i = first_valid; i < nbin; i++) {
11827         if (count[i] < minpoints) {
11828             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11829             for (j = i+1; j < nbin; j++) {
11830                 if (count[j] >= minpoints) {
11831                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11832                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11833                                         / 2;
11834                     }
11835                     else {
11836                         frac = (sky_wave[i] - sky_wave[i-1]) 
11837                              / (sky_wave[j] - sky_wave[i-1]);
11838                         sky_spectrum[i] = frac * sky_spectrum[j]
11839                                         + (1 - frac) * sky_spectrum[i-1];
11840                     }
11841                 }
11842             }
11843         }
11844     }
11845 
11846 
11847     /*
11848      * Create the output table
11849      */
11850 
11851     sky = cpl_table_new(nbin);
11852     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11853     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11854     cpl_table_wrap_int(sky, count, "npoints");
11855 
11856 
11857     /*
11858      * Fill the sky map
11859      */
11860 
11861     data  = cpl_image_get_data(wavemap);
11862     sdata = cpl_image_get_data(spectra);
11863     kdata = cpl_image_get_data(skymap);
11864 
11865     for (i = 0; i < npix; i++) {
11866 
11867         /*
11868          * Currently based on linear interpolation
11869          */
11870 
11871         lambda = data[i];
11872         if (lambda < 1.0)
11873             continue;
11874         bin = (lambda - firstLambda) / dispersion;
11875         lambda1 = sky_wave[bin];
11876         value1 = sky_spectrum[bin];
11877         if (lambda1 < lambda) {
11878             bin++;
11879             if (bin < nbin) {
11880                 lambda2 = sky_wave[bin];
11881                 value2  = sky_spectrum[bin];
11882                 if (lambda2 - lambda1 < 0.1) {
11883                     value = (value1 + value2) / 2;
11884                 }
11885                 else {
11886                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11887                     value = frac * value2 + (1 - frac) * value1;
11888                 }
11889             }
11890             else {
11891                 value = value1;
11892             }
11893         }
11894         else {
11895             if (bin > 0) {
11896                 bin--;
11897                 lambda2 = lambda1;
11898                 value2  = value1;
11899                 lambda1 = sky_wave[bin];
11900                 value1  = sky_spectrum[bin];
11901                 if (lambda2 - lambda1 < 0.1) {
11902                     value = (value1 + value2) / 2;
11903                 }
11904                 else {
11905                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11906                     value = frac * value2 + (1 - frac) * value1;
11907                 }
11908             }
11909             else {
11910                 value = value1;
11911             }
11912         }
11913         kdata[i] = value;
11914     }
11915 
11916     if (first_valid)
11917         cpl_table_erase_window(sky, 0, first_valid);
11918 
11919     return sky;
11920 
11921 }
11922 
11923 
11957 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11958                        double dispersion, cpl_image *skymap)
11959 {
11960     const char *func = "mos_sky_map";
11961 
11962     cpl_vector **vector;
11963     double       firstLambda, lastLambda;
11964     double       lambda, lambda1, lambda2;
11965     double       value, value1, value2;
11966     float        min, max;
11967     int         *count;
11968     int          nbin, bin;
11969     int          nx, ny, npix;
11970     int          i, j;
11971 
11972     cpl_table   *sky;
11973     double      *sky_spectrum;
11974     float       *data;
11975     float       *sdata;
11976     float       *kdata;
11977     double      *wdata;
11978 
11979 
11980     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11981         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11982         return NULL;
11983     }
11984     
11985     if (dispersion <= 0.0) {
11986         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11987         return NULL;
11988     }
11989 
11990     nx = cpl_image_get_size_x(spectra);
11991     ny = cpl_image_get_size_y(spectra);
11992     npix = nx * ny;
11993 
11994     if (nx != cpl_image_get_size_x(wavemap) ||
11995         ny != cpl_image_get_size_y(wavemap) ||
11996         nx != cpl_image_get_size_x(skymap) ||
11997         ny != cpl_image_get_size_y(skymap)) {
11998         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11999         return NULL;
12000     }
12001 
12002 
12003     /*
12004      * Find bluest and reddest wavelengths in the whole image
12005      */
12006 
12007     data = cpl_image_get_data(wavemap);
12008 
12009     for (i = 0; i < npix; i++) {
12010         if (data[i] > 1.0) {
12011             min = max = data[i];
12012             j = i+1;
12013             break;
12014         }
12015     }
12016 
12017     for (i = j; i < npix; i++) {
12018         if (data[i] < 1.0)      /* Impossible wavelength */
12019             continue;
12020         if (min > data[i])
12021             min = data[i];
12022         if (max < data[i])
12023             max = data[i];
12024     }
12025 
12026     firstLambda = min;
12027     lastLambda = max;
12028 
12029 
12030     /*
12031      * Determine length of median spectrum
12032      */
12033 
12034     nbin = (lastLambda - firstLambda) / dispersion;
12035 
12036     /*
12037      * Count how many values will be found for each spectral bin.
12038      * The ith bin has a wavelength range from firstLambda + i*dispersion
12039      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12040      * it is assigned to its central wavelength.
12041      */
12042 
12043     count = cpl_calloc(nbin, sizeof(int));
12044 
12045     data = cpl_image_get_data(wavemap);
12046 
12047     for (i = 0; i < npix; i++) {
12048         if (data[i] < 1.0)
12049             continue;
12050         bin = (data[i] - firstLambda) / dispersion;
12051         if (bin < nbin)                               /* Safer */
12052             count[bin]++;
12053     }
12054 
12055 
12056     /*
12057      * Allocate an array of vectors with the appropriate size, to
12058      * contain a list of all the spectral pixels values. At the same
12059      * time, reset the array of counters (because we will have to
12060      * count again...).
12061      */
12062 
12063     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12064     for (i = 0; i < nbin; i++) {
12065         if (count[i])
12066             vector[i] = cpl_vector_new(count[i]);
12067         else
12068             vector[i] = NULL;
12069         count[i] = 0;
12070     }
12071 
12072 
12073     /*
12074      * Read the wavemap and the spectral images, and add the data values
12075      * to the appropriate wavelength bins
12076      */
12077 
12078     data  = cpl_image_get_data(wavemap);
12079     sdata = cpl_image_get_data(spectra);
12080 
12081     for (i = 0; i < npix; i++) {
12082         if (data[i] < 1.0)
12083             continue;
12084         bin = (data[i] - firstLambda) / dispersion;
12085         if (bin < nbin) {                             /* Safer */
12086             cpl_vector_set(vector[bin], count[bin], sdata[i]);
12087             count[bin]++;
12088         }
12089     }
12090 
12091 
12092     /*
12093      * Compute the median flux for each wavelength bin, and destroy
12094      * at the same time the used vectors
12095      */
12096 
12097     sky_spectrum = cpl_calloc(nbin, sizeof(double));
12098     for (i = 0; i < nbin; i++) {
12099         if (vector[i]) {
12100             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12101             cpl_vector_delete(vector[i]);
12102         }
12103     }
12104 
12105     cpl_free(vector);
12106 
12107 
12108     /*
12109      * Here possible gaps in the final spectrum should be filled
12110      * by interpolation
12111      */
12112 
12113     /* ... */
12114 
12115     /*
12116      * Create the output table
12117      */
12118 
12119     sky = cpl_table_new(nbin);
12120     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12121     cpl_table_set_column_unit(sky, "wavelength", "pixel");
12122     cpl_table_wrap_double(sky, sky_spectrum, "sky");
12123     cpl_table_wrap_int(sky, count, "npoints");
12124     for (i = 0; i < nbin; i++)
12125         cpl_table_set_double(sky, "wavelength", i, 
12126                              firstLambda + (i+0.5)*dispersion);
12127 
12128 
12129     /*
12130      * Fill the sky map
12131      */
12132 
12133     data  = cpl_image_get_data(wavemap);
12134     sdata = cpl_image_get_data(spectra);
12135     kdata = cpl_image_get_data(skymap);
12136     wdata = cpl_table_get_data_double(sky, "wavelength");
12137 
12138     for (i = 0; i < npix; i++) {
12139 
12140         /*
12141          * Currently based on linear interpolation
12142          */
12143 
12144         lambda = data[i];
12145         if (lambda < 1.0)
12146             continue;
12147         bin = (lambda - firstLambda) / dispersion;
12148         lambda1 = wdata[bin];
12149         value1 = sky_spectrum[bin];
12150         if (lambda1 < lambda) {
12151             bin++;
12152             if (bin < nbin) {
12153                 lambda2 = wdata[bin];
12154                 value2  = sky_spectrum[bin];
12155                 value   = ((lambda2 - lambda)*value1 
12156                         +  (lambda - lambda1)*value2) / dispersion;
12157             }
12158             else {
12159                 value = value1;
12160             }
12161         }
12162         else {
12163             if (bin > 0) {
12164                 bin--;
12165                 lambda2 = lambda1;
12166                 value2  = value1;
12167                 lambda1 = wdata[bin];
12168                 value1  = sky_spectrum[bin];
12169                 value   = ((lambda2 - lambda)*value1 
12170                         +  (lambda - lambda1)*value2)/dispersion;
12171             }
12172             else {
12173                 value = value1;
12174             }
12175         }
12176         kdata[i] = value;
12177     }
12178 
12179     return sky;
12180 
12181 }
12182 
12183 
12199 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12200 {
12201     const char *func = "mos_sky_local_old";
12202 
12203     cpl_image *exslit;
12204     cpl_image *sky;
12205     cpl_image *skymap;
12206     float     *data;
12207     float     *sdata;
12208     int        nx, ny;
12209     int        xlow, ylow, xhig, yhig;
12210     int        nslits;
12211     int       *slit_id;
12212     int       *position;
12213     int       *length;
12214     int        i, j, k;
12215 
12216 
12217     if (spectra == NULL) {
12218         cpl_msg_error(func, 
12219                       "A scientific rectified spectral image must be given");
12220         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12221         return NULL;
12222     }
12223 
12224     if (slits == NULL) {
12225         cpl_msg_error(func, "A slits position table must be given");
12226         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12227         return NULL;
12228     }
12229 
12230     nslits   = cpl_table_get_nrow(slits);
12231     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12232     position = cpl_table_get_data_int(slits, "position");
12233     length   = cpl_table_get_data_int(slits, "length");
12234 
12235     nx = cpl_image_get_size_x(spectra);
12236     ny = cpl_image_get_size_y(spectra);
12237 
12238     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12239 
12240     xlow = 1;
12241     xhig = nx;
12242     for (i = 0; i < nslits; i++) {
12243 
12244         if (length[i] == 0)
12245             continue;
12246 
12247         /*
12248          * Define the extraction boundaries. We DON'T write:
12249          *
12250          * ylow = position[i];
12251          * yhig = ylow + length[i];
12252          *
12253          * because the cpl_image pixels are counted from 1, and because in
12254          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12255          */
12256 
12257         ylow = position[i] + 1;
12258         yhig = ylow + length[i] - 1;
12259 
12260         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12261         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12262         cpl_image_delete(exslit);
12263 
12264         data   = cpl_image_get_data(skymap);
12265         data  += nx * position[i];
12266 
12267         for (j = 0; j < length[i]; j++) {
12268             sdata  = cpl_image_get_data(sky);
12269             for (k = 0; k < nx; k++) {
12270                 *data++ = *sdata++;
12271             }
12272         }
12273 
12274         cpl_image_delete(sky);
12275     }
12276 
12277     return skymap;
12278 
12279 }
12280 
12281 
12301 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12302 {
12303     const char *func = "mos_sky_local";
12304 
12305     char        name[MAX_COLNAME];
12306 
12307     cpl_polynomial *fit;
12308     cpl_vector     *points;
12309     cpl_vector     *values;
12310     cpl_vector     *keep_points;
12311     cpl_vector     *keep_values;
12312     cpl_image      *exslit;
12313     cpl_image      *sky;
12314     cpl_image      *subtracted;
12315     cpl_image      *profile;
12316     cpl_image      *skymap;
12317     cpl_table      *objects;
12318     float          *data;
12319     float          *sdata;
12320     float          *xdata;
12321     double         *vdata;
12322     double         *pdata;
12323     double          median;
12324     int             nx, ny;
12325     int             xlow, ylow, xhig, yhig;
12326     int             nslits;
12327     int            *slit_id;
12328     int            *position;
12329     int            *length;
12330     int            *is_sky;
12331     int             nsky, nbad;
12332     int             maxobjects;
12333     int             margin = 3;
12334     int             radius = 6;
12335     int             i, j, k;
12336 
12337 
12338     if (spectra == NULL) {
12339         cpl_msg_error(func, 
12340                       "A scientific rectified spectral image must be given");
12341         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12342         return NULL;
12343     }
12344 
12345     if (slits == NULL) {
12346         cpl_msg_error(func, "A slits position table must be given");
12347         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12348         return NULL;
12349     }
12350 
12351     if (order < 0) {
12352         cpl_msg_error(func, "Invalid fit order");
12353         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12354         return NULL;
12355     }
12356 
12357     nslits   = cpl_table_get_nrow(slits);
12358     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12359     position = cpl_table_get_data_int(slits, "position");
12360     length   = cpl_table_get_data_int(slits, "length");
12361 
12362     nx = cpl_image_get_size_x(spectra);
12363     ny = cpl_image_get_size_y(spectra);
12364 
12365     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12366 
12367     xlow = 1;
12368     xhig = nx;
12369     for (i = 0; i < nslits; i++) {
12370 
12371         if (length[i] == 0)
12372             continue;
12373 
12374         /*
12375          * Define the extraction boundaries. We DON'T write:
12376          *
12377          * ylow = position[i];
12378          * yhig = ylow + length[i];
12379          *
12380          * because the cpl_image pixels are counted from 1, and because in
12381          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12382          */
12383 
12384         ylow = position[i] + 1;
12385         yhig = ylow + length[i] - 1;
12386 
12387         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12388         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12389         cpl_image_delete(exslit);
12390 
12391         data   = cpl_image_get_data(skymap);
12392         data  += nx * position[i];
12393 
12394         for (j = 0; j < length[i]; j++) {
12395             sdata  = cpl_image_get_data(sky);
12396             for (k = 0; k < nx; k++) {
12397                 *data++ = *sdata++;
12398             }
12399         }
12400 
12401         cpl_image_delete(sky);
12402     }
12403 
12404 
12405     /*
12406      * Preliminary sky-subtracted image
12407      */
12408 
12409     subtracted = cpl_image_duplicate(spectra);
12410     cpl_image_subtract(subtracted, skymap);
12411     cpl_image_delete(skymap);
12412 
12413 
12414     /*
12415      * Detect objects positions in all slits
12416      */
12417 
12418     objects = cpl_table_duplicate(slits);
12419     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12420     cpl_image_delete(profile);
12421     cpl_image_delete(subtracted);
12422 
12423 
12424     /*
12425      * Flag the sky pixels. Note that maxobjects is intentionally 
12426      * the max number of objects increased by one.
12427      */
12428 
12429     maxobjects = 1;
12430     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12431     while (cpl_table_has_column(objects, name)) {
12432         maxobjects++;
12433         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12434     }
12435 
12436     is_sky = cpl_calloc(ny, sizeof(int));
12437 
12438     for (i = 0; i < nslits; i++) {
12439 
12440         if (length[i] == 0)
12441             continue;
12442 
12443         ylow = position[i] + margin;
12444         yhig = position[i] + length[i] - margin;
12445 
12446         for (j = ylow; j < yhig; j++)
12447             is_sky[j] = 1;
12448 
12449         for (j = 1; j < maxobjects; j++) {
12450             snprintf(name, MAX_COLNAME, "object_%d", j);
12451             if (cpl_table_is_valid(objects, name, i)) {
12452                 snprintf(name, MAX_COLNAME, "start_%d", j);
12453                 ylow = cpl_table_get_int(objects, name, i, NULL);
12454                 snprintf(name, MAX_COLNAME, "end_%d", j);
12455                 yhig = cpl_table_get_int(objects, name, i, NULL);
12456                 for (k = ylow; k <= yhig; k++)
12457                     is_sky[k] = 0;
12458             }
12459         }
12460 
12461 
12462         /*
12463          * Eliminate isolated sky points
12464          */
12465 
12466         ylow = position[i] + margin + 1;
12467         yhig = position[i] + length[i] - margin - 1;
12468 
12469         for (j = ylow; j < yhig; j++)
12470             if (is_sky[j])
12471                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12472                     is_sky[j] = 0;
12473 
12474     }
12475 
12476 
12477     /*
12478      * Determination of the sky map
12479      */
12480 
12481     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12482 
12483     for (i = 0; i < nslits; i++) {
12484 
12485         if (length[i] == 0)
12486             continue;
12487 
12488         ylow = position[i];
12489         yhig = ylow + length[i];
12490 
12491         nsky = 0;
12492         for (j = ylow; j < yhig; j++)
12493             if (is_sky[j])
12494                 nsky++;
12495 
12496         if (nsky > order + 1) {
12497             if (order) {
12498                 points = cpl_vector_new(nsky);
12499                 nsky = 0;
12500                 for (j = ylow; j < yhig; j++) {
12501                     if (is_sky[j]) {
12502                         cpl_vector_set(points, nsky, j);
12503                         nsky++;
12504                     }
12505                 }
12506 
12507                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12508                 xdata = cpl_image_get_data(exslit);
12509                 values = cpl_vector_new(nsky);
12510 
12511                 for (j = 0; j < nx; j++) {
12512                     nsky = 0;
12513                     for (k = ylow; k < yhig; k++) {
12514                         if (is_sky[k]) {
12515                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12516                             nsky++;
12517                         }
12518                     }
12519 
12520                     /*
12521                      * Eliminate obvious outliers
12522                      */
12523 
12524                     median = cpl_vector_get_median_const(values);
12525                     vdata = cpl_vector_get_data(values);
12526                     pdata = cpl_vector_get_data(points);
12527                     nbad = 0;
12528                     for (k = 0; k < nsky; k++) {
12529                         if (fabs(vdata[k] - median) < 100) {
12530                             if (nbad) {
12531                                 vdata[k-nbad] = vdata[k];
12532                                 pdata[k-nbad] = pdata[k];
12533                             }
12534                         }
12535                         else
12536                             nbad++;
12537                     }
12538 
12539                     if (nsky == nbad)
12540                         continue;
12541 
12542                     if (nbad && nsky - nbad > order + 1) {
12543                         keep_values = values;
12544                         keep_points = points;
12545                         values = cpl_vector_wrap(nsky-nbad, vdata);
12546                         points = cpl_vector_wrap(nsky-nbad, pdata);
12547                     }
12548 
12549                     if (nsky - nbad > order + 1) {
12550 
12551                         fit = cpl_polynomial_fit_1d_create(points, values, 
12552                                                            order, NULL);
12553 
12554                         if (fit) {
12555                             for (k = ylow; k < yhig; k++) {
12556                                 xdata[j+(k-ylow)*nx] = 
12557                                          cpl_polynomial_eval_1d(fit, k, NULL);
12558                             }
12559 
12560                             cpl_polynomial_delete(fit);
12561                         }
12562                         else
12563                             cpl_error_reset();
12564                     }
12565                     else {
12566                         for (k = 0; k < nsky; k++) {
12567                             xdata[j+k*nx] = median;
12568                         }
12569                     }
12570 
12571                     if (nbad && nsky - nbad > order + 1) {
12572                         cpl_vector_unwrap(values);
12573                         cpl_vector_unwrap(points);
12574                         values = keep_values;
12575                         points = keep_points;
12576                     }
12577 
12578                     if (nbad) {
12579                         nsky = 0;
12580                         for (k = ylow; k < yhig; k++) {
12581                             if (is_sky[k]) {
12582                                 cpl_vector_set(points, nsky, k);
12583                                 nsky++;
12584                             }
12585                         }
12586                     }
12587 
12588                 }
12589 
12590                 cpl_vector_delete(values);
12591                 cpl_vector_delete(points);
12592 
12593                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12594                 cpl_image_delete(exslit);
12595 
12596             }
12597             else {
12598                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12599                 xdata = cpl_image_get_data(exslit);
12600                 values = cpl_vector_new(nsky);
12601 
12602                 for (j = 0; j < nx; j++) {
12603                     nsky = 0;
12604                     for (k = ylow; k < yhig; k++) {
12605                         if (is_sky[k]) {
12606                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12607                             nsky++;
12608                         }
12609                     }
12610 
12611                     median = cpl_vector_get_median_const(values);
12612 
12613                     for (k = ylow; k < yhig; k++)
12614                         xdata[j+(k-ylow)*nx] = median;
12615 
12616                 }
12617 
12618                 cpl_vector_delete(values);
12619 
12620                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12621                 cpl_image_delete(exslit);
12622             }
12623         }
12624         else
12625             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
12626     }
12627 
12628     cpl_free(is_sky);
12629 
12630     return skymap;
12631 
12632 }
12633 
12634 
12656 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
12657                                  float threshold, float ratio)
12658 {
12659     const char *func = "mos_clean_cosmics";
12660 
12661     cpl_image  *smoothImage;
12662     cpl_table  *table;
12663     cpl_matrix *kernel;
12664     int        *xdata;
12665     int        *ydata;
12666     float      *idata;
12667     float      *sdata;
12668     float       sigma, sum, value, smoothValue;
12669     double      noise;
12670     int         count;
12671     float       fMax;
12672     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
12673     int         xLen;
12674     int         yLen;
12675     int         nPix;
12676     int         first = 1;  /* position of first cosmic ray candidate
12677                                encountered while scanning the image */
12678     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
12679     int         numCosmic = 0;
12680     int         found, foundContiguousCandidate;
12681     int        *cosmic;
12682   
12683 
12684     if (image == NULL)
12685         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12686 
12687 
12688     /*
12689      *  "cosmic" is a flags holder (initialized to zero):
12690      *
12691      *           -1 = candidate for cosmic ray
12692      *            0 = not a cosmic
12693      *            1 = a cosmic ray
12694      *            2 = member of current group of contiguous candidates
12695      *            3 = examined member of current group
12696      */
12697 
12698     xLen = cpl_image_get_size_x(image);
12699     yLen = cpl_image_get_size_y(image);
12700 
12701     if (xLen < 4 || yLen < 4)
12702         return CPL_ERROR_NONE;
12703 
12704     nPix = xLen * yLen;
12705 
12706     /*
12707      * Noise estimation from negative offsets in image. Note that this
12708      * assumes that the background level (skyLevel) has already been 
12709      * subtracted from the data. In this way we estimate the noise due 
12710      * to detector readout and to the background signal level (before 
12711      * it were removed). Theoretically this is given by 
12712      *
12713      *        noise = sqrt(ron^2 + skyLevel/gain)
12714      *
12715      * where ron is the read-out-noise. To this we will sum the noise 
12716      * contribution due to any increase of the signal above the background
12717      * by an amount scienceLevel. Theoretically the total noise is given by
12718      *
12719      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
12720      *
12721      * that is
12722      *
12723      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
12724      *
12725      */
12726 
12727     idata = cpl_image_get_data(image);
12728     noise = 0.0;
12729     count = 0;
12730 
12731     for (i = 0; i < nPix; i++) {
12732         if (idata[i] < -0.00001) {
12733             noise -= idata[i];
12734             count++;
12735         }
12736     }
12737 
12738     noise /= count;
12739     noise *= 1.25;       /* Factor to convert average deviation to sigma */
12740 
12741     cosmic = cpl_calloc(nPix, sizeof(int));
12742 
12743     if (threshold < 0.)
12744         threshold = 4.0;
12745     if (ratio < 0.)
12746         ratio = 2.0;
12747 
12748     kernel = cpl_matrix_new(3, 3);
12749     cpl_matrix_fill(kernel, 1.0);
12750     cpl_matrix_set(kernel, 1, 1, 0.0);
12751     smoothImage = cpl_image_filter_median(image, kernel);
12752     cpl_matrix_delete(kernel);
12753     
12754     /*
12755      *  Loop on images pixels, searching for cosmic rays candidates.
12756      *  Border pixels are currently excluded (they cannot contain
12757      *  candidates), to avoid that the search for groups of contiguous
12758      *  pixels would ever go out of image boundaries. In future we may
12759      *  overcome this limit, adding an appropriate check when contiguous
12760      *  pixels are searched.
12761      */
12762 
12763     sdata = cpl_image_get_data(smoothImage);
12764 
12765     for (j = 1; j < yLen - 1; j++) {
12766         for (i = 1; i < xLen - 1; i++) {
12767             value = idata[i + j * xLen];
12768             smoothValue = sdata[i + j * xLen];
12769             if (smoothValue < 1.0)
12770                 smoothValue = 1.0;
12771             sigma = sqrt(noise * noise + smoothValue / gain);
12772             if (value - smoothValue >= threshold * sigma) 
12773                 cosmic[i + j * xLen] = -1;
12774         }
12775     }
12776 
12777     cpl_image_delete(smoothImage);
12778 
12779 
12780     /*
12781      *  Search for groups of contiguous cosmic rays candidates.
12782      */
12783 
12784     do {
12785         found = 0;
12786         for (pos = first; pos < nPix; pos++) {
12787             if (cosmic[pos] == -1) {
12788                 cosmic[pos] = 2;         /*  Candidate found.  */
12789                 i = pos % xLen;          /*  Its coordinates.  */
12790                 j = pos / xLen;
12791                 first = pos;
12792                 first++;      /* ???  really necessary? */
12793                 found = 1;
12794                 break;
12795             }
12796         }
12797 
12798         if (found) {
12799 
12800             /*
12801              *  Determine new group of contiguous cosmic rays candidates.
12802              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12803              *  and the value of the max pixel and its position, fMax, iPosMax,
12804              *  jPosMax.
12805              */
12806 
12807             iMin = iMax = iPosMax = i;
12808             jMin = jMax = jPosMax = j;
12809             fMax = idata[i + j * xLen];
12810 
12811             do {
12812                 foundContiguousCandidate = 0;
12813                 for (l = 0; l <= 1; l++) {
12814                     for (k = 0; k <= 1; k++) {
12815 
12816                         /*
12817                          *  Looping on 4 pixels to North, East, South and West
12818                          */
12819 
12820                         ii = i + k - l;
12821                         jj = j + k + l - 1;
12822                         if (cosmic[ii + jj * xLen] == -1) {
12823                             foundContiguousCandidate = 1;
12824                             cosmic[ii + jj * xLen] = 2;
12825                                         /* Candidate belongs to current group */
12826                             iii = ii;   /* Keep its position */
12827                             jjj = jj;
12828 
12829                             /*
12830                              * Upgrade search box
12831                              */
12832 
12833                             if (ii < iMin)
12834                                 iMin = ii;
12835                             if (ii > iMax)
12836                                 iMax = ii;
12837                             if (jj < jMin)
12838                                 jMin = jj;
12839                             if (jj > jMax)
12840                                 jMax = jj;
12841 
12842                             if (idata[ii + jj * xLen] > fMax) {
12843                                 fMax = idata[ii + jj * xLen];
12844                                 iPosMax = ii;
12845                                 jPosMax = jj;
12846                             }
12847                         }
12848                     }
12849                 }
12850 
12851                 /*
12852                  *  We are done exploring the "cross". Now mark as "examined"
12853                  *  the current candidate (at the center of the cross):
12854                  */
12855 
12856                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12857 
12858                 if (foundContiguousCandidate) {
12859 
12860                     /*
12861                      * Pass (arbitrarily) the coordinates of the LAST found 
12862                      * candidate
12863                      */
12864 
12865                     i = iii;
12866                     j = jjj;
12867 
12868                     /* 
12869                      * Skip the rest, continue loop on new candidate 
12870                      */
12871 
12872                     continue; 
12873                 }
12874 
12875 
12876                 /*
12877                  *  Look for leftovers in the (growing!) search box
12878                  */
12879 
12880                 for (l = jMin; l <= jMax; l++) {
12881                     for (k = iMin; k <= iMax; k++) {
12882                         if (cosmic[k + l * xLen] == 2) {
12883                             i = k;
12884                             j = l;
12885                             foundContiguousCandidate = 1;
12886                             break;
12887                         }
12888                     }
12889                     if (foundContiguousCandidate) 
12890                         break;
12891                 }
12892             } while (foundContiguousCandidate);
12893 
12894 
12895             /*
12896              *  No more contiguous candidates are found. Decide now
12897              *  whether the current group is a cosmic ray or not.
12898              */
12899 
12900             sum = 0.;                /* Sum of 8 pixels around max position */
12901             for (l = -1; l <= 1; l++) {
12902                 for (k = -1; k <= 1; k++) {
12903                     if (l != 0 || k != 0) {
12904                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12905                     }
12906                 }
12907             }
12908 
12909             sum /= 8.;
12910             if (fMax > ratio * sum) {
12911                 for (l = jMin - 1; l <= jMax + 1; l++) {
12912                     for (k = iMin - 1; k <= iMax + 1; k++) {
12913                         if (cosmic[k + l * xLen] == 3) {
12914                             cosmic[k + l * xLen] = 1;
12915                             numCosmic++;
12916                         }
12917                     }
12918                 }
12919             }
12920             else {
12921                 for (l = jMin - 1; l <= jMax + 1; l++) {
12922                     for (k = iMin - 1; k <= iMax + 1; k++) {
12923                         if (cosmic[k + l * xLen] != -1) {
12924                             if (cosmic[k + l * xLen] == 1) 
12925                                 numCosmic--;
12926                             cosmic[k + l * xLen] = 0;
12927                         }
12928                     }
12929                 }
12930             }
12931         }
12932     } while (found);
12933 
12934 
12935     /*
12936      *  Prepare table containing cosmic rays coordinates. 
12937      */
12938 
12939     table = cpl_table_new(numCosmic);
12940     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12941     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12942     cpl_table_set_column_unit(table, "x", "pixel");
12943     cpl_table_set_column_unit(table, "y", "pixel");
12944     xdata = cpl_table_get_data_int(table, "x");
12945     ydata = cpl_table_get_data_int(table, "y");
12946 
12947     for (pos = 0, i = 0; pos < nPix; pos++) {
12948         if (cosmic[pos] == 1) {
12949             xdata[i] = (pos % xLen);
12950             ydata[i] = (pos / xLen);
12951             i++;
12952         }
12953     }
12954 
12955     mos_clean_bad_pixels(image, table, 1);
12956 
12957     cpl_free(cosmic);
12958     cpl_table_delete(table);
12959 
12960     return CPL_ERROR_NONE;
12961 
12962 }
12963 
12964 
12965 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12966                                     int spectral)
12967 {
12968     const char *func = "mos_clean_cosmics";
12969  
12970     float       *idata;
12971     int         *isBadPix;
12972     int          i, j, k, d;
12973     int          xlen, ylen, totPix;
12974     int          nBadPixels = 0;
12975     int          sign, foundFirst;
12976     int         *xValue = NULL;
12977     int         *yValue = NULL;
12978     float        save = 0.;
12979     double       sumd;
12980     int          cx, cy;
12981     int          nPairs;
12982     float        estimate[4];
12983     int          sx[] = {0, 1, 1, 1};
12984     int          sy[] = {1,-1, 0, 1};
12985     int          searchHorizon = 100;
12986     int          percent = 15;
12987 
12988 
12989     if (image == NULL || table == NULL)
12990         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12991 
12992     if (1 != cpl_table_has_column(table, "x"))
12993         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12994 
12995     if (1 != cpl_table_has_column(table, "y"))
12996         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12997 
12998     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
12999         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13000 
13001     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13002         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13003 
13004     nBadPixels = cpl_table_get_nrow(table);
13005 
13006     if (nBadPixels) {
13007         xlen = cpl_image_get_size_x(image);
13008         ylen = cpl_image_get_size_y(image);
13009         idata = cpl_image_get_data(image);
13010         totPix = xlen * ylen;
13011         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13012             isBadPix = cpl_calloc(totPix, sizeof(int));
13013         }
13014         else {
13015             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13016                             "skip bad pixel correction", percent);
13017             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13018         }
13019     }
13020     else {
13021         cpl_msg_debug(func, "No pixel values to interpolate");
13022         return CPL_ERROR_NONE;
13023     }
13024 
13025     xValue = cpl_table_get_data_int(table, "x");
13026     yValue = cpl_table_get_data_int(table, "y");
13027 
13028     for (i = 0; i < nBadPixels; i++)
13029         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13030 
13031     for (i = 0; i < nBadPixels; i++) {
13032 
13033         /*
13034          *  Search for the closest good pixel along the 4 fundamental 
13035          *  directions (in both senses):
13036          *                            \ | /
13037          *                             \|/
13038          *                           --- ---
13039          *                             /|\
13040          *                            / | \
13041          *
13042          *  Then collect pairs of values to interpolate linearly.
13043          */
13044 
13045         nPairs = 0;
13046         for (j = 0; j < 4; j++) {
13047 
13048             if (spectral) /* Just horizontal interpolation for spectral data */
13049                 if (j != 2)
13050                     continue;
13051 
13052             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
13053             sumd = 0.;
13054             foundFirst = 0;
13055             for (k = 0; k < 2; k++) {
13056                 sign = 2 * k - 1;
13057                 d = 0;
13058                 cx = xValue[i];
13059                 cy = yValue[i];
13060                 do {
13061                     cx += sign * sx[j];
13062                     cy += sign * sy[j];
13063                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
13064                         break;
13065                     d++;
13066                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13067 
13068                 if (cx >= 0 && cx < xlen && 
13069                     cy >= 0 && cy < ylen && d < searchHorizon) {
13070 
13071                     /*
13072                      *  In this block is cripted the linear interpolation...
13073                      */
13074 
13075                     save = idata[cx + cy * xlen];
13076                     estimate[nPairs] += save / d;
13077                     sumd += 1. / (double) d;
13078                     if (k) {
13079                         estimate[nPairs] /= sumd;
13080                         nPairs++;
13081                     }
13082                     else {
13083                         foundFirst = 1;
13084                     }
13085                 }
13086                 else {
13087 
13088                     /*
13089                      * Image borders were crossed, incomplete pair of values
13090                      */
13091 
13092                     if (k) {
13093                         if (foundFirst) {
13094                             estimate[nPairs] = save;
13095                             nPairs++;
13096                         }
13097                     }
13098                 }
13099             }
13100         }
13101 
13102         /*
13103          * Replace pixel value of the input image, corresponding to
13104          * the current bad pixel, with the median of the estimates
13105          * resulted from the 4 linear interpolations.
13106          */
13107 
13108         if (nPairs > 2) {
13109             idata[xValue[i] + yValue[i] * xlen] = 
13110                                cpl_tools_get_median_float(estimate, nPairs);
13111         }
13112         else if (nPairs == 2) {
13113             idata[xValue[i] + yValue[i] * xlen] =
13114                                             (estimate[0] + estimate[1]) / 2.;
13115         }
13116         else if (nPairs == 1) {
13117             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13118         }
13119         else {
13120             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13121                           xValue[i], yValue[i]);
13122         }
13123     }
13124 
13125     cpl_free(isBadPix);
13126 
13127     return CPL_ERROR_NONE;
13128 }
13129 
13130 
13160 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13161                            cpl_table *polytraces, double reference,
13162                            double blue, double red, double dispersion)
13163 {
13164     const char *func = "mos_spatial_map";
13165 
13166     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13167                                                  /* Max order is 5 */
13168     cpl_polynomial *polytop;
13169     cpl_polynomial *polybot;
13170     cpl_image      *calibration;
13171     float          *data;
13172     double          top, bot;
13173     double          coeff;
13174     double          ytop, ybot;
13175     double          ypos, yfra;
13176     double          factor;
13177     int             yint, yprev;
13178     int             nslits;
13179     int             npseudo;
13180     int            *slit_id;
13181     int            *length;
13182     int             nx, ny;
13183     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13184     int             missing_top, missing_bot;
13185     int             null;
13186     int             order;
13187     int             i, j;
13188     cpl_size        k;
13189 
13190 
13191     if (spectra == NULL || slits == NULL || polytraces == NULL) {
13192         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13193         return NULL;
13194     }
13195 
13196     if (dispersion <= 0.0) {
13197         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13198         return NULL;
13199     }
13200 
13201     if (red - blue < dispersion) {
13202         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13203         return NULL;
13204     }
13205 
13206     nx = cpl_image_get_size_x(spectra);
13207     ny = cpl_image_get_size_y(spectra);
13208 
13209     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13210     data = cpl_image_get_data(calibration);
13211 
13212     length  = cpl_table_get_data_int(slits, "length");
13213     nslits  = cpl_table_get_nrow(slits);
13214     slit_id = cpl_table_get_data_int(slits, "slit_id");
13215     order   = cpl_table_get_ncol(polytraces) - 2;
13216 
13217     /*
13218      * The spatial resampling is performed for a certain number of 
13219      * pixels above and below the position of the reference wavelength:
13220      */
13221 
13222     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13223     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13224 
13225     for (i = 0; i < nslits; i++) {
13226         
13227         if (length[i] == 0)
13228             continue;
13229 
13230         /*
13231          * Note that the x coordinate of the reference pixels on the CCD
13232          * is taken arbitrarily at the top end of each slit. This wouldn't
13233          * be entirely correct in case of curved slits, or in presence of
13234          * heavy distortions: in such cases the spatial resampling is
13235          * really performed across a wide range of wavelengths. But
13236          * the lag between top and bottom spectral curvature models 
13237          * would introduce even in such cases negligible effects on
13238          * the spectral spatial resampling.
13239          */
13240 
13241         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13242 
13243         start_pixel = refpixel - pixel_below;
13244         if (start_pixel < 0)
13245             start_pixel = 0;
13246 
13247         end_pixel = refpixel + pixel_above;
13248         if (end_pixel > nx)
13249             end_pixel = nx;
13250 
13251         /*
13252          * Recover from the table of spectral curvature coefficients
13253          * the curvature polynomials.
13254          */
13255 
13256         missing_top = 0;
13257         polytop = cpl_polynomial_new(1);
13258         for (k = 0; k <= order; k++) {
13259             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13260             if (null) {
13261                 cpl_polynomial_delete(polytop);
13262                 missing_top = 1;
13263                 break;
13264             }
13265             cpl_polynomial_set_coeff(polytop, &k, coeff);
13266         }
13267 
13268         missing_bot = 0;
13269         polybot = cpl_polynomial_new(1);
13270         for (k = 0; k <= order; k++) {
13271             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13272             if (null) {
13273                 cpl_polynomial_delete(polybot);
13274                 missing_bot = 1;
13275                 break;
13276             }
13277             cpl_polynomial_set_coeff(polybot, &k, coeff);
13278         }
13279 
13280         if (missing_top && missing_bot) {
13281             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
13282                             slit_id[i]);
13283             continue;
13284         }
13285 
13286         /*
13287          * In case just one of the two edges was not traced, the other
13288          * edge curvature model is duplicated and shifted to the other
13289          * end of the slit: better than nothing!
13290          */
13291 
13292         if (missing_top) {
13293             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13294                             "the spectral curvature of the lower edge "
13295                             "is used instead.", slit_id[i]);
13296             polytop = cpl_polynomial_duplicate(polybot);
13297             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13298             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13299             k = 0;
13300             coeff = cpl_polynomial_get_coeff(polybot, &k);
13301             coeff += ytop - ybot;
13302             cpl_polynomial_set_coeff(polytop, &k, coeff);
13303         }
13304 
13305         if (missing_bot) {
13306             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13307                             "the spectral curvature of the upper edge "
13308                             "is used instead.", slit_id[i]);
13309             polybot = cpl_polynomial_duplicate(polytop);
13310             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13311             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13312             k = 0;
13313             coeff = cpl_polynomial_get_coeff(polytop, &k);
13314             coeff -= ytop - ybot;
13315             cpl_polynomial_set_coeff(polybot, &k, coeff);
13316         }
13317 
13318         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13319         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13320         npseudo = ceil(top-bot) + 1;
13321 
13322         if (npseudo < 1) {
13323             cpl_polynomial_delete(polytop);
13324             cpl_polynomial_delete(polybot);
13325             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13326                             slit_id[i]);
13327             continue;
13328         }
13329 
13330         for (j = start_pixel; j < end_pixel; j++) {
13331             top = cpl_polynomial_eval_1d(polytop, j, NULL);
13332             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13333             factor = (top-bot)/npseudo;
13334             for (k = 0; k <= npseudo; k++) {
13335                 ypos = top - k*factor;
13336                 yint = ypos;
13337                 yfra = ypos - yint;
13338                 if (yint >= 0 && yint < ny-1) {
13339                     data[j + nx*yint] = (top-yint)/factor;
13340                     if (k) {
13341 
13342                         /*
13343                          * This is added to recover lost pixels on
13344                          * the CCD image (pixels are lost because
13345                          * the CCD pixels are less than npseudo+1).
13346                          */
13347 
13348                         if (yprev - yint > 1) {
13349                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
13350                         }
13351                     }
13352                 }
13353                 yprev = yint;
13354             }
13355         }
13356         cpl_polynomial_delete(polytop);
13357         cpl_polynomial_delete(polybot);
13358     }
13359 
13360     return calibration;
13361 }
13362 
13363 
13426 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13427                               int maxradius, int conradius)
13428 {
13429     const char *func = "mos_detect_objects";
13430 
13431     cpl_image  *profile;
13432     float      *pdata;
13433     float      *p;
13434 
13435     char        name[MAX_COLNAME];
13436 
13437     int         nslits;
13438     int         npeaks;
13439     int         nobjects, objpos, totobj;
13440     int         maxobjects;
13441     int        *position;
13442     int        *length;
13443     int        *reject;
13444     double     *place;
13445     double     *bright;
13446     double      mindistance;
13447     int         pos, count;
13448     int         up;
13449     int         low, hig;
13450     int         row;
13451     int         i, j, k;
13452 
13453     const int   min_pixels = 10;
13454 
13455     
13456     if (cpl_error_get_code() != CPL_ERROR_NONE)
13457         return NULL;
13458  
13459     if (image == NULL || slits == NULL) { 
13460         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13461         return NULL;
13462     }
13463 
13464     if (margin < 0)
13465         margin = 0;
13466 
13467     if (maxradius < 0) {
13468         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13469         return NULL;
13470     }
13471 
13472     if (conradius < 0) {
13473         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13474         return NULL;
13475     }
13476 
13477     nslits   = cpl_table_get_nrow(slits);
13478     position = cpl_table_get_data_int(slits, "position");
13479     length   = cpl_table_get_data_int(slits, "length");
13480 
13481     profile = cpl_image_collapse_create(image, 1);
13482     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
13483     pdata = cpl_image_get_data(profile);
13484 
13485     row = 1;
13486     maxobjects = 0;
13487     totobj = 0;
13488     for (i = 0; i < nslits; i++) {
13489 
13490         if (length[i] == 0)
13491             continue;
13492 
13493         pos = position[i] + margin;
13494         count = length[i] - 2*margin;
13495 
13496         if (count < min_pixels)
13497             continue;
13498 
13499         p = pdata + pos;
13500 
13501 
13502         /*
13503          * Count peaks candidates
13504          */
13505 
13506         npeaks = 0;
13507         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13508             npeaks++;
13509         }
13510 
13511         up = 0;
13512         for (j = 0; j < count - 3; j++) {
13513             if (p[j] > 0) {
13514                 if (p[j+1] > p[j]) {
13515                     up++;
13516                 }
13517                 else {
13518                     if (up > 2) {
13519                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13520                             if (p[j] > 5)
13521                                 npeaks++;
13522                         }
13523                     }
13524                     else if (up > 1) {
13525                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13526                             if (p[j] > 5)
13527                                 npeaks++;
13528                         }
13529                     }
13530                     up = 0;
13531                 }
13532             }
13533             else {
13534                 up = 0;
13535             }
13536         }
13537 
13538         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
13539             && p[count-3] > p[count-4] && p[count-4] > 0) {
13540             npeaks++;
13541         }
13542 
13543         if (npeaks == 0)
13544             continue;
13545 
13546 
13547         /*
13548          * Get candidates parameters
13549          */
13550 
13551         reject = cpl_calloc(npeaks, sizeof(int));
13552         bright = cpl_calloc(npeaks, sizeof(double));
13553         place  = cpl_calloc(npeaks, sizeof(double));
13554 
13555         npeaks = 0;
13556         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13557             bright[0] = p[0];
13558             place[0] = position[i] + margin;
13559             npeaks++;
13560         }
13561 
13562         up = 0;
13563         for (j = 0; j < count - 3; j++) {
13564             if (p[j] > 0) {
13565                 if (p[j+1] > p[j]) {
13566                     up++;
13567                 }
13568                 else {
13569                     if (up > 2) {
13570                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13571                             if (p[j] > 5) {
13572                                 bright[npeaks] = p[j];
13573                                 place[npeaks] = position[i] + margin + j + 1
13574                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13575                                 npeaks++;
13576                             }
13577                         }
13578                     }
13579                     else if (up > 1) {
13580                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13581                             if (p[j] > 5) {
13582                                 bright[npeaks] = p[j];
13583                                 place[npeaks] = position[i] + margin + j + 1
13584                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13585                                 npeaks++;
13586                             }
13587                         }
13588                     }
13589                     up = 0;
13590                 }
13591             }
13592             else {
13593                 up = 0;
13594             }
13595         }
13596 
13597         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
13598             && p[count-3] > p[count-4] && p[count-4] > 0) {
13599             bright[npeaks] = p[count-1];
13600             place[npeaks] = position[i] + count;
13601             npeaks++;
13602         }
13603 
13604 
13605         /*
13606          * Now select the uncontaminated peaks
13607          */
13608 
13609         if (fabs(place[0] - pos) < 1.0)
13610             reject[0] = 1;
13611         if (fabs(place[npeaks-1] - pos - count) < 1.0)
13612             reject[npeaks-1] = 1;
13613         for (j = 0; j < npeaks; j++) {
13614             for (k = 0; k < npeaks; k++) {
13615                 if (k == j)
13616                     continue;
13617                 mindistance = conradius * bright[k] / bright[j] 
13618                                         * bright[k] / bright[j];
13619                 if (fabs(place[j] - place[k]) < mindistance)
13620                     reject[j] = 1;
13621             }
13622         }
13623 
13624 /* new part */
13625         for (j = 0; j < npeaks; j++) {
13626             if (reject[j])
13627                 continue;
13628             if (j) {
13629                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13630                     / (bright[j-1] + bright[j]) + 1;
13631             }
13632             else {
13633                 low = pos;
13634             }
13635             if (j < npeaks - 1) {
13636                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13637                     / (bright[j+1] + bright[j]) + 1;
13638             }
13639             else {
13640                 hig = pos + count;
13641             }
13642 
13643             if (low < pos)
13644                 low = pos;
13645             if (hig > pos + count)
13646                 hig = pos + count;
13647             if (place[j] - low > maxradius)
13648                 low = place[j] - maxradius;
13649             if (hig - place[j] > maxradius)
13650                 hig = place[j] + maxradius;
13651             if (hig == low)
13652                 reject[j] = 1;
13653         }
13654 /* end new part */
13655 
13656         nobjects = npeaks;
13657         for (j = 0; j < npeaks; j++)
13658             if (reject[j])
13659                 nobjects--;
13660 
13661         for (j = 0; j < nobjects; j++) {
13662             snprintf(name, MAX_COLNAME, "object_%d", j+1);
13663             if (cpl_table_has_column(slits, name))
13664                 continue;
13665             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
13666             snprintf(name, MAX_COLNAME, "start_%d", j+1);
13667             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13668             cpl_table_set_column_unit(slits, name, "pixel");
13669             snprintf(name, MAX_COLNAME, "end_%d", j+1);
13670             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13671             cpl_table_set_column_unit(slits, name, "pixel");
13672             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13673             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13674             cpl_table_set_column_unit(slits, name, "pixel");
13675         }
13676 
13677         objpos = nobjects;
13678         for (j = 0; j < npeaks; j++) {
13679             if (reject[j])
13680                 continue;
13681             if (j) {
13682                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13683                     / (bright[j-1] + bright[j]) + 1;
13684             }
13685             else {
13686                low = pos;
13687             }
13688             if (j < npeaks - 1) {
13689                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13690                     / (bright[j+1] + bright[j]) + 1;
13691             }
13692             else {
13693                 hig = pos + count;
13694             }
13695 
13696             if (low < pos)
13697                 low = pos;
13698             if (hig > pos + count)
13699                 hig = pos + count;
13700             if (place[j] - low > maxradius)
13701                 low = place[j] - maxradius;
13702             if (hig - place[j] > maxradius)
13703                 hig = place[j] + maxradius;
13704 
13705             snprintf(name, MAX_COLNAME, "object_%d", objpos);
13706             cpl_table_set_double(slits, name, i, place[j]);
13707             snprintf(name, MAX_COLNAME, "start_%d", objpos);
13708             cpl_table_set_int(slits, name, i, low);
13709             snprintf(name, MAX_COLNAME, "end_%d", objpos);
13710             cpl_table_set_int(slits, name, i, hig);
13711             snprintf(name, MAX_COLNAME, "row_%d", objpos);
13712             cpl_table_set_int(slits, name, i, row + objpos - 1);
13713             totobj++;
13714             objpos--;
13715         }
13716 
13717         row += nobjects;
13718 
13719         if (maxobjects < nobjects)
13720             maxobjects = nobjects;
13721 
13722         cpl_free(reject);
13723         cpl_free(bright);
13724         cpl_free(place);
13725 
13726     }
13727 
13728 /*    nobjects = row - nobjects;     A bug, I think... */
13729     row = cpl_table_get_nrow(slits);
13730 
13731     for (i = 0; i < row; i++) {
13732         for (j = 0; j < maxobjects; j++) {
13733             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13734             if (cpl_table_is_valid(slits, name, i))
13735                 cpl_table_set_int(slits, name, i, totobj -
13736                                   cpl_table_get_int(slits, name, i, NULL));
13737         }
13738     }
13739 
13740     for (i = 0; i < maxobjects; i++) {
13741         snprintf(name, MAX_COLNAME, "start_%d", i+1);
13742         cpl_table_fill_invalid_int(slits, name, -1);
13743         snprintf(name, MAX_COLNAME, "end_%d", i+1);
13744         cpl_table_fill_invalid_int(slits, name, -1);
13745         snprintf(name, MAX_COLNAME, "row_%d", i+1);
13746         cpl_table_fill_invalid_int(slits, name, -1);
13747     }
13748 
13749     return profile;
13750 }
13751 
13752 
13777 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13778                                 cpl_table *objects, int extraction, double ron,
13779                                 double gain, int ncombined)
13780 {
13781     const char *func = "mos_extract_objects";
13782 
13783     char        name[MAX_COLNAME];
13784 
13785     cpl_image **output;
13786     cpl_image  *extracted;
13787     cpl_image  *extr_sky;
13788     cpl_image  *error;
13789     cpl_image  *sciwin;
13790     cpl_image  *skywin;
13791     int         nslits;
13792     int         nobjects;
13793     int         maxobjects;
13794     int         nx, ny;
13795     int         ylow, yhig;
13796     int         i, j;
13797 
13798 
13799     if (science == NULL || sky == NULL) {
13800         cpl_msg_error(func, "Both scientific exposures are required in input");
13801         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13802         return NULL;
13803     }
13804 
13805     if (objects == NULL) {
13806         cpl_msg_error(func, "An object table is required in input");
13807         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13808         return NULL;
13809     }
13810 
13811     if (extraction < 0 || extraction > 1) {
13812         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13813                       "either 0 or 1", extraction); 
13814         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13815         return NULL;
13816     }
13817 
13818     if (ron < 0.0) {
13819         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13820         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13821         return NULL;
13822     }
13823 
13824     if (gain < 0.1) {
13825         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13826         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13827         return NULL;
13828     }
13829 
13830     if (ncombined < 1) {
13831         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13832                       "it should be at least 1", ncombined);
13833         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13834         return NULL;
13835     }
13836 
13837 
13838     /*
13839      * Count the max number of objects per slit. Note that maxobjects 
13840      * is intentionally the max number of objects increased by one.
13841      */
13842 
13843     maxobjects = 1;
13844     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13845     while (cpl_table_has_column(objects, name)) {
13846         maxobjects++;
13847         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13848     }
13849 
13850 
13851     /*
13852      * Count objects to extract
13853      */
13854 
13855     nobjects = 0;
13856     nslits = cpl_table_get_nrow(objects);
13857 
13858     for (i = 0; i < nslits; i++) {
13859         for (j = 1; j < maxobjects; j++) {
13860             snprintf(name, MAX_COLNAME, "object_%d", j);
13861             if (cpl_table_is_valid(objects, name, i))
13862                 nobjects++;
13863         }
13864     }
13865 
13866     if (nobjects == 0)
13867         return NULL;
13868 
13869     nx = cpl_image_get_size_x(science);
13870     ny = cpl_image_get_size_x(science);
13871 
13872     output = cpl_calloc(3, sizeof(cpl_image *));
13873     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13874     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13875     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13876 
13877 
13878     /*
13879      * Extract objects
13880      */
13881 
13882     nobjects = 0;
13883     for (i = 0; i < nslits; i++) {
13884         for (j = 1; j < maxobjects; j++) {
13885             snprintf(name, MAX_COLNAME, "object_%d", j);
13886             if (cpl_table_is_valid(objects, name, i)) {
13887                 snprintf(name, MAX_COLNAME, "start_%d", j);
13888                 ylow = cpl_table_get_int(objects, name, i, NULL);
13889                 snprintf(name, MAX_COLNAME, "end_%d", j);
13890                 yhig = cpl_table_get_int(objects, name, i, NULL);
13891                 snprintf(name, MAX_COLNAME, "row_%d", j);
13892                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13893                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13894                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13895 /*
13896  * Cleaning the cosmics locally was really NOT a good idea...
13897  * I leave it here, commented out, to never forget this mistake!
13898 
13899                 if (extraction) {
13900                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13901                 }
13902  */
13903                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13904                                nobjects, extraction, ron, gain, ncombined);
13905 
13906                 /*
13907                  * Hidden check whether the spectrum was saturated or not
13908                  */
13909 
13910                 {
13911                     cpl_image *total = cpl_image_add_create(sciwin, skywin);
13912                     float     *data  = cpl_image_get_data_float(total);
13913                     int        size  = cpl_image_get_size_x(total)
13914                                      * cpl_image_get_size_y(total);
13915                     int        k;
13916                     char      *saturation_level = getenv("SATURATION_LEVEL");
13917                     float      saturation = 62000.0;
13918                     char      *max_saturated = getenv("MAX_SATURATED");
13919                     int        max_satur = 10;
13920                     int        saturated;
13921 
13922                     if (saturation_level)
13923                         saturation = atof(saturation_level);
13924 
13925                     if (max_saturated)
13926                         max_satur = atoi(max_saturated);
13927 
13928                     saturated = 0;
13929                     for (k = 0; k < size; k++) {
13930                         if (data[k] > saturation) {
13931                             saturated++;
13932                             if (saturated > max_satur) {
13933                                 break;
13934                             }
13935                         }
13936                     }
13937 
13938                     if (saturated > max_satur)
13939                         saturated = 1;
13940                     else
13941                         saturated = 0;
13942 
13943                     data = cpl_image_get_data(extracted);
13944                     data[nobjects * nx] = saturated;
13945                 }
13946 
13947                 cpl_image_delete(sciwin);
13948                 cpl_image_delete(skywin);
13949                 nobjects++;
13950             }
13951         }
13952     }
13953 
13954     return output;
13955 
13956 }
13957 
13958 
13981 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13982                             double dispersion, int saturation, 
13983                             double *mfwhm, double *rmsfwhm,
13984                             double *resolution, double *rmsres, int *nlines)
13985 {
13986     cpl_vector *vector;
13987 
13988     int     i, j, n, m;
13989     int     position, maxpos;
13990     int     xlen, ylen;
13991     int     sp, ep;
13992     int     radius;
13993     int     sradius = 40;
13994     int     threshold = 250;    /* Peak must be so many ADUs above min */
13995 
13996     int     ifwhm;
13997     double  fwhm;
13998     double *buffer;
13999     double  min, max, halfmax;
14000     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
14001     double  value, rms;
14002 
14003     float  *data;
14004 
14005 
14006     *resolution = 0.0;
14007     *rmsres = 0.0;
14008     *nlines = 0;
14009 
14010     xlen = cpl_image_get_size_x(image);
14011     ylen = cpl_image_get_size_y(image);
14012     data = cpl_image_get_data(image);
14013 
14014     buffer = cpl_malloc(ylen * sizeof(double));
14015 
14016     /*
14017      *  Closest pixel to specified wavelength.
14018      */
14019 
14020     position = floor((lambda - startwave) / dispersion + 0.5);
14021 
14022     sp = position - sradius;
14023     ep = position + sradius;
14024 
14025     if (sp < 0 || ep > xlen) {
14026         cpl_free(buffer);
14027         return 0;
14028     }
14029 
14030     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
14031 
14032         /*
14033          *  Search interval for peak. Abort if too close to image border.
14034          */
14035 
14036         radius = mos_lines_width(data + i*xlen + position - sradius, 
14037                                  2*sradius + 1);
14038         if (radius < 5)
14039             radius = 5;
14040 
14041         sp = position - radius;
14042         ep = position + radius;
14043 
14044         if (sp < 0 || ep > xlen) {
14045             cpl_free(buffer);
14046             return 0;
14047         }
14048 
14049 
14050         /*
14051          *  Determine min-max value and position.
14052          */
14053 
14054         maxpos = sp;
14055         min = max = data[sp + i * xlen];
14056         for (j = sp; j < ep; j++) {
14057             if (data[j + i * xlen] > max) {
14058                 max = data[j + i * xlen];
14059                 maxpos = j;
14060             }
14061             if (data[j + i * xlen] < min) {
14062                 min = data[j + i * xlen];
14063             }
14064         }
14065 
14066         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
14067             continue;
14068 
14069         if (max - min < threshold)        /* Low signal... */
14070             continue;
14071 
14072         if (max > saturation)             /* Saturation */
14073             continue;
14074 
14075         /*
14076          *  Determine FWHM counting pixels with value greater than
14077          *  half of the max value, to the right and to the left of
14078          *  the max. Linear interpolation between the pixels where
14079          *  the transition happens.
14080          */
14081 
14082         halfmax = (max + min)/ 2.0;
14083 
14084         fwhm = 0.0;
14085         ifwhm = 0;
14086         for (j = maxpos; j < maxpos + radius; j++) {
14087             if (j < xlen) {
14088                 if (data[j + i * xlen] < halfmax) {
14089                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14090                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14091                     break;
14092                 }
14093                 ifwhm++;
14094             }
14095         }
14096 
14097         ifwhm = 0;
14098         for (j = maxpos; j > maxpos - radius; j--) {
14099             if (j >= 0) {
14100                 if (data[j + i * xlen] < halfmax) {
14101                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14102                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14103                     break;
14104                 }
14105                 ifwhm++;
14106             }
14107         }
14108 
14109         if (fwhm > 3.0) {
14110             buffer[n] = fwhm - 2.0;
14111             n++;
14112         }
14113 
14114     }
14115 
14116     if (n == 0) {
14117         cpl_free(buffer);
14118         return 0;
14119     }
14120 
14121     vector = cpl_vector_wrap(n, buffer);
14122     value = cpl_vector_get_median_const(vector);
14123     cpl_vector_unwrap(vector);
14124 
14125     rms = 0.0;
14126     for (i = 0, m = 0; i < n; i++) {
14127         if (fabs(buffer[i] - value) < cut) {
14128             rms += fabs(buffer[i] - value);
14129             m++;
14130         }
14131     }
14132 
14133     cpl_free(buffer);
14134 
14135     if (m < 3)
14136         return 0;
14137 
14138     rms /= m;
14139     rms *= 1.25;       /* Factor to convert average deviation to sigma */
14140 
14141     value *= dispersion;
14142     rms *= dispersion;
14143 
14144     *mfwhm = value;
14145     *rmsfwhm = rms;
14146 
14147     *resolution = lambda / value;
14148     *rmsres = *resolution * rms / value;
14149 
14150     *nlines = m;
14151 
14152     return 1;
14153 }
14154 
14155 
14177 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
14178                                 double dispersion, int saturation, 
14179                                 cpl_vector *lines)
14180 {
14181 
14182     cpl_table *table;
14183     double    *line;
14184     double     fwhm;
14185     double     rmsfwhm;
14186     double     resolution;
14187     double     rmsres;
14188     int        nref;
14189     int        nlines;
14190     int        i;
14191 
14192 
14193     nref = cpl_vector_get_size(lines);
14194     line = cpl_vector_get_data(lines);
14195 
14196     table = cpl_table_new(nref);
14197     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14198     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14199     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14200     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14201     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14202     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14203     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14204     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14205     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14206 
14207     for (i = 0; i < nref; i++) {
14208         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
14209                                     saturation, &fwhm, &rmsfwhm, 
14210                                     &resolution, &rmsres, &nlines)) {
14211             cpl_table_set_double(table, "wavelength", i, line[i]);
14212             cpl_table_set_double(table, "fwhm", i, fwhm);
14213             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14214             cpl_table_set_double(table, "resolution", i, resolution);
14215             cpl_table_set_double(table, "resolution_rms", i, rmsres);
14216             cpl_table_set_int(table, "nlines", i, nlines);
14217         }
14218         else
14219             cpl_table_set_int(table, "nlines", i, 0);
14220     }
14221 
14222     if (cpl_table_has_valid(table, "wavelength"))
14223         return table;
14224 
14225     cpl_table_delete(table);
14226 
14227     return NULL;
14228     
14229 }
14230 
14231 
14249 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14250                             int ystart, int yend, double wstart, double wend)
14251 {
14252     const char *func = "mos_integrate_signal";
14253 
14254     double sum;
14255     float *sdata;
14256     float *wdata;
14257     int    nx, ny;
14258     int    x, y;
14259     
14260 
14261     if (image == NULL || wavemap == NULL) { 
14262         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14263         return 0.0;
14264     }
14265 
14266     if (ystart > yend || wstart >= wend) {
14267         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14268         return 0.0;
14269     }
14270 
14271     nx = cpl_image_get_size_x(image);
14272     ny = cpl_image_get_size_y(image);
14273 
14274     if (!(nx == cpl_image_get_size_x(wavemap) 
14275         && ny == cpl_image_get_size_y(wavemap))) {
14276         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14277         return 0.0;
14278     }
14279 
14280     if (ystart < 0 || yend > ny) {
14281         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14282         return 0.0;
14283     }
14284 
14285     sdata = cpl_image_get_data(image);
14286     wdata = cpl_image_get_data(wavemap);
14287 
14288     sdata += ystart*nx;
14289     wdata += ystart*nx;
14290 
14291     sum = 0.0;
14292     for (y = ystart; y < yend; y++) {
14293         for (x = 0; x < nx; x++) {
14294             if (wdata[x] < wstart || wdata[x] > wend)
14295                 continue;
14296             sum += sdata[x];
14297         }
14298         sdata += nx;
14299         wdata += nx;
14300     }
14301 
14302     return sum;
14303 
14304 }
14305 
14306 /****************************************************************************
14307  * From this point on, the instrument dependent functions are added:
14308  * they are functions that retrieve information that is stored in
14309  * the data headers in some instrument specific way, such as the
14310  * location of overscans, the slits positions on the telescope
14311  * focal plane, the gain factor, etc.
14312  */
14313 
14314 
14337 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14338 {
14339     const char *func = "mos_load_slits_fors_mxu";
14340 
14341     cpl_table  *slits;
14342     char        keyname[MAX_COLNAME];
14343     const char *instrume;
14344     const char *target_name;
14345     float       slit_x;
14346     float       slit_y;
14347     float       length;
14348 /*    double      arc2mm = 0.53316;         */
14349     double      arc2mm = 0.528;
14350     int         nslits;
14351     int         slit_id;
14352     int         fors;
14353     int         chip;
14354     int         found;
14355 
14356     /*
14357      * The limits below are used to exclude from the loaded slit list
14358      * any slit that surely doesn't belong to the used chip. This is
14359      * a way to reduce the chance of ambiguous slit identification.
14360      */
14361 
14362     float      low_limit1 = 10.0;
14363     float      hig_limit2 = 30.0;
14364 
14365 
14366     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14367         return NULL;
14368     }
14369 
14370     if (header == NULL) {
14371         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14372         return NULL;
14373     }
14374 
14375 
14376     /*
14377      * See if this is FORS1 or FORS2;
14378      */
14379 
14380     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14381 
14382     fors = 0;
14383     if (instrume[4] == '1')
14384         fors = 1;
14385     if (instrume[4] == '2')
14386         fors = 2;
14387 
14388     if (fors != 2) {
14389         cpl_msg_error(func, "Wrong instrument: %s\n"
14390                       "FORS2 is expected for MXU data", instrume);
14391         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14392         return NULL;
14393     }
14394 
14395 
14396     /*
14397      * The master and slave chips can be identified by their positions
14398      * in the chip array in the case of FORS2 data (with fors1 the chip
14399      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14400      */
14401 
14402     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14403 
14404     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14405         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14406                       "in FITS header");
14407         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14408         return NULL;
14409     }
14410 
14411     if (chip != 1 && chip != 2) {
14412         cpl_msg_error(func, "Unexpected chip position in keyword "
14413                       "ESO DET CHIP1 Y: %d", chip);
14414         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14415         return NULL;
14416     }
14417 
14418 
14419     /*
14420      * Count slits in header (excluding reference slits, and the slits
14421      * that _surely_ belong to the other chip)
14422      */
14423 
14424     nslits = 0;
14425     slit_id = 0;
14426     found = 1;
14427 
14428     while (found) {
14429         slit_id++;
14430         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14431         if (cpl_propertylist_has(header, keyname)) {
14432             slit_y = cpl_propertylist_get_double(header, keyname);
14433 
14434             if (chip == 1)
14435                 if (slit_y < low_limit1)
14436                     continue;
14437             if (chip == 2)
14438                 if (slit_y > hig_limit2)
14439                     continue;
14440                 
14441             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14442                      slit_id + 100);
14443             if (cpl_propertylist_has(header, keyname)) {
14444                 target_name = cpl_propertylist_get_string(header, keyname);
14445                 if (strncmp(target_name, "refslit", 7))
14446                     nslits++;
14447             }
14448             else
14449                 nslits++;
14450         }
14451         else
14452             found = 0;
14453     }
14454 
14455     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14456         cpl_msg_error(func, "%s while loading slits coordinates from "
14457                       "FITS header", cpl_error_get_message());
14458         cpl_error_set_where(func);
14459         return NULL;
14460     }
14461 
14462     if (nslits == 0)  {
14463         cpl_msg_error(func, "No slits coordinates found in header");
14464         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14465         return NULL;
14466     }
14467 
14468     slits = cpl_table_new(nslits);
14469     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14470     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14471     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14472     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14473     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14474     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14475     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14476     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14477     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14478 
14479     nslits = 0;
14480     slit_id = 0; 
14481     found = 1;
14482     while (found) {
14483         slit_id++;
14484         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14485         if (cpl_propertylist_has(header, keyname)) {
14486             slit_y = cpl_propertylist_get_double(header, keyname);
14487 
14488             if (chip == 1) 
14489                 if (slit_y < low_limit1)
14490                     continue;
14491             if (chip == 2)
14492                 if (slit_y > hig_limit2)
14493                     continue;
14494 
14495             /*
14496              * Y-flip the slit position, to match CCD pixel coordinate
14497              * convention
14498              */
14499 
14500             slit_y = -slit_y;
14501 
14502             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
14503             slit_x = cpl_propertylist_get_double(header, keyname);
14504             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14505                 cpl_table_delete(slits);
14506                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14507                               keyname);
14508                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14509                 return NULL;
14510             }
14511 
14512             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
14513             length = cpl_propertylist_get_double(header, keyname);
14514             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14515                 cpl_table_delete(slits);
14516                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14517                               keyname);
14518                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14519                 return NULL;
14520             }
14521 
14522             length *= arc2mm;
14523 
14524             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14525                      slit_id + 100);
14526             if (cpl_propertylist_has(header, keyname)) {
14527                 target_name = cpl_propertylist_get_string(header, keyname);
14528                 if (strncmp(target_name, "refslit", 7)) {
14529                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14530                     cpl_table_set(slits, "xtop", nslits, slit_x);
14531                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14532                     cpl_table_set(slits, "xbottom", nslits, slit_x);
14533                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14534                     nslits++;
14535                 }
14536             }
14537             else {
14538                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14539                 cpl_table_set(slits, "xtop", nslits, slit_x);
14540                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14541                 cpl_table_set(slits, "xbottom", nslits, slit_x);
14542                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14543                 nslits++;
14544             }
14545         }
14546         else
14547             found = 0;
14548     }
14549 
14550     return slits;
14551 }
14552 
14553 
14576 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header)
14577 {
14578     const char *func = "mos_load_slits_fors_mos";
14579 
14580     cpl_table  *slits;
14581     char        keyname[MAX_COLNAME];
14582     const char *instrume;
14583     const char *chipname;
14584     float       slit_x;
14585     int         first_slit, last_slit;
14586     cpl_size    nslits;
14587     int         slit_id;
14588     int         fors;
14589     int         chip;
14590     int         fors_is_old;
14591 
14592     /*
14593      * The Y coordinates of the slits are fixed
14594      */
14595 
14596     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
14597                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
14598                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
14599                                -102.1 };
14600     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
14601                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
14602                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
14603                                -113.9 };
14604 
14605 
14606     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14607         return NULL;
14608     }
14609 
14610     if (header == NULL) {
14611         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14612         return NULL;
14613     }
14614 
14615 
14616     /*
14617      * See if this is FORS1 or FORS2;
14618      */
14619 
14620     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14621 
14622     fors = 0;
14623     if (instrume[4] == '1')
14624         fors = 1;
14625     if (instrume[4] == '2')
14626         fors = 2;
14627 
14628     if (fors == 0) {
14629         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14630                       instrume);
14631         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14632         return NULL;
14633     }
14634 
14635     /* FIXME:
14636      * This is the way FORS1 data belong to the upgraded chips,
14637      * named "Marlene" and "Norma III". It's a quick solution,
14638      * there are hardcoded values here!!!
14639      */
14640 
14641     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
14642 
14643     if (chipname[0] == 'M' || chipname[0] == 'N')
14644         fors_is_old = 0;
14645     else
14646         fors_is_old = 1;
14647 
14648     if (fors == 1 && fors_is_old) {
14649         first_slit = 1;
14650         last_slit = 19;
14651     }
14652     else {
14653 
14654         /*
14655          * The master and slave chips can be identified by their positions
14656          * in the chip array in the case of FORS2 data: chip = 2 is the 
14657          * master, chip = 1 is the slave.
14658          */
14659 
14660         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14661 
14662         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14663             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14664                           "in FITS header");
14665             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14666             return NULL;
14667         }
14668 
14669         if (chip != 1 && chip != 2) {
14670             cpl_msg_error(func, "Unexpected chip position in keyword "
14671                           "ESO DET CHIP1 Y: %d", chip);
14672             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14673             return NULL;
14674         }
14675 
14676         if (chip == 1) {
14677             first_slit = 12;
14678             last_slit = 19;
14679         }
14680         else {
14681             first_slit = 1;
14682             last_slit = 11;
14683         }
14684     }
14685 
14686 
14687     /*
14688      * Count slits in header (excluding closed slits - i.e. those with
14689      * offsets greater than 115 mm - and the slits that do not belong 
14690      * to this chip)
14691      */
14692 
14693     nslits = 0;
14694     slit_id = 0;
14695     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14696         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14697         if (cpl_propertylist_has(header, keyname)) {
14698             slit_x = cpl_propertylist_get_double(header, keyname);
14699             if (fabs(slit_x) < 115.0)
14700                 nslits++;
14701         }
14702         else {
14703             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
14704             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14705             return NULL;
14706         }
14707     }
14708 
14709     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14710         cpl_msg_error(func, "%s while loading slits coordinates from "
14711                       "FITS header", cpl_error_get_message());
14712         cpl_error_set_where(func);
14713         return NULL;
14714     }
14715 
14716     if (nslits == 0)  {
14717         cpl_msg_error(func, "No slits coordinates found in header");
14718         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14719         return NULL;
14720     }
14721 
14722     slits = cpl_table_new(nslits);
14723     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14724     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14725     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14726     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14727     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14728     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14729     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14730     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14731     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14732 
14733     nslits = 0;
14734     slit_id = 0;
14735     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14736         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14737         slit_x = cpl_propertylist_get_double(header, keyname);
14738         if (fabs(slit_x) < 115.0) {
14739             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14740             cpl_table_set(slits, "xtop", nslits, slit_x);
14741             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
14742             cpl_table_set(slits, "xbottom", nslits, slit_x);
14743             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
14744             nslits++;
14745         }
14746     }
14747 
14748     return slits;
14749 }
14750 
14751 
14775 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
14776 {
14777     const char *func = "mos_load_slits_fors_lss";
14778 
14779     cpl_table  *slits;
14780     char       *slit_name;
14781     const char *instrume;
14782     int         fors;
14783     int         chip;
14784     float       ytop;
14785     float       ybottom;
14786 
14787     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14788         return NULL;
14789     }
14790 
14791     if (header == NULL) {
14792         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14793         return NULL;
14794     }
14795 
14796 
14797     /*
14798      * See if this is FORS1 or FORS2;
14799      */
14800 
14801     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14802 
14803     fors = 0;
14804     if (instrume[4] == '1')
14805         fors = 1;
14806     if (instrume[4] == '2')
14807         fors = 2;
14808 
14809     if (fors == 0) {
14810         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14811                       instrume);
14812         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14813         return NULL;
14814     }
14815 
14816     if (fors == 1) {
14817         ytop = 109.94;
14818         ybottom = -109.94;
14819     }
14820     else {
14821 
14822         /*
14823          * The master and slave chips can be identified by their positions
14824          * in the chip array in the case of FORS2 data: chip = 2 is the 
14825          * master, chip = 1 is the slave.
14826          */
14827 
14828         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14829 
14830         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14831             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14832                           "in FITS header");
14833             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14834             return NULL;
14835         }
14836 
14837         if (chip != 1 && chip != 2) {
14838             cpl_msg_error(func, "Unexpected chip position in keyword "
14839                           "ESO DET CHIP1 Y: %d", chip);
14840             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14841             return NULL;
14842         }
14843 
14844         if (chip == 1) {
14845             ytop = 30.0;
14846             ybottom = -109.94;
14847         }
14848         else {
14849             ytop = 109.94;
14850             ybottom = -20.0;
14851         }
14852     }
14853 
14854 
14855     slits = cpl_table_new(1);
14856     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14857     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14858     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14859     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14860     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14861     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14862     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14863     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14864     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14865 
14866     slit_name = (char *)cpl_propertylist_get_string(header, 
14867                                                     "ESO INS SLIT NAME");
14868 
14869     cpl_table_set(slits, "ytop", 0, ytop);
14870     cpl_table_set(slits, "ybottom", 0, ybottom);
14871 
14872     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14873         cpl_table_set_int(slits, "slit_id", 0, 1);
14874         cpl_table_set(slits, "xbottom", 0, -0.075);
14875         cpl_table_set(slits, "xtop", 0, 0.075);
14876     }
14877     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14878         cpl_table_set_int(slits, "slit_id", 0, 2);
14879         cpl_table_set(slits, "xbottom", 0, 5.895);
14880         cpl_table_set(slits, "xtop", 0, 6.105);
14881     }
14882     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14883         cpl_table_set_int(slits, "slit_id", 0, 3);
14884         cpl_table_set(slits, "xbottom", 0, -6.135);
14885         cpl_table_set(slits, "xtop", 0, -5.865);
14886     }
14887     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14888         cpl_table_set_int(slits, "slit_id", 0, 4);
14889         cpl_table_set(slits, "xbottom", 0, 11.815);
14890         cpl_table_set(slits, "xtop", 0, 12.185);
14891     }
14892     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14893         cpl_table_set_int(slits, "slit_id", 0, 5);
14894         cpl_table_set(slits, "xbottom", 0, -12.265);
14895         cpl_table_set(slits, "xtop", 0, -11.735);
14896     }
14897     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14898         cpl_table_set_int(slits, "slit_id", 0, 6);
14899         cpl_table_set(slits, "xbottom", 0, 17.655);
14900         cpl_table_set(slits, "xtop", 0, 18.345);
14901     }
14902     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14903         cpl_table_set_int(slits, "slit_id", 0, 7);
14904         cpl_table_set(slits, "xbottom", 0, -18.425);
14905         cpl_table_set(slits, "xtop", 0, -17.575);
14906     }
14907     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14908         cpl_table_set_int(slits, "slit_id", 0, 8);
14909         cpl_table_set(slits, "xbottom", 0, 23.475);
14910         cpl_table_set(slits, "xtop", 0, 24.525);
14911     }
14912     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14913         cpl_table_set_int(slits, "slit_id", 0, 9);
14914         cpl_table_set(slits, "xbottom", 0, -24.66);
14915         cpl_table_set(slits, "xtop", 0, -23.34);
14916     }
14917     else {
14918         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14919                       slit_name);
14920         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14921         cpl_table_delete(slits);
14922         return NULL;
14923     }
14924 
14925     return slits;
14926 }
14927 
14928 
14943 double mos_get_gain_vimos(cpl_propertylist *header)
14944 {
14945     const char *func = "mos_get_gain_vimos";
14946 
14947     double gain = -1.0;
14948 
14949 
14950     if (cpl_error_get_code() != CPL_ERROR_NONE)
14951         return gain;
14952 
14953     if (header == NULL) {
14954         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14955         return gain;
14956     }
14957 
14958     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14959     if (cpl_error_get_code()) {
14960         cpl_error_set_where(func);
14961         gain = -1.0;
14962     }
14963 
14964     return gain;
14965 
14966 }
14967 
14968 
14988 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14989 {
14990     const char *func = "mos_load_slits_vimos";
14991 
14992     cpl_table *slits;
14993     char       keyname[MAX_COLNAME];
14994     float      slit_x;
14995     float      slit_y;
14996     float      dim_x;
14997     float      dim_y;
14998     int        nslits;
14999     int        slit_id;
15000     int        curved;
15001     int        i;
15002 
15003 
15004     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15005         return NULL;
15006     }
15007 
15008     if (header == NULL) {
15009         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15010         return NULL;
15011     }
15012 
15013     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15014 
15015     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15016         cpl_error_set_where(func);
15017         return NULL;
15018     }
15019 
15020     slits = cpl_table_new(nslits);
15021     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15022     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15023     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15024     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15025     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15026     cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15027     cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15028     cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15029     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15030     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15031     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15032     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15033     cpl_table_set_column_unit(slits, "xwidth", "mm");
15034     cpl_table_set_column_unit(slits, "ywidth", "mm");
15035 
15036     for (i = 0; i < nslits; i++) {
15037         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15038         slit_id = cpl_propertylist_get_int(header, keyname);
15039         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15040             cpl_error_set_where(func);
15041             return NULL;
15042         }
15043         sprintf(keyname, "ESO INS SLIT%d X", i+1);
15044         slit_x = cpl_propertylist_get_double(header, keyname);
15045         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15046             cpl_error_set_where(func);
15047             return NULL;
15048         }
15049         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15050         slit_y = cpl_propertylist_get_double(header, keyname);
15051         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15052             cpl_error_set_where(func);
15053             return NULL;
15054         }
15055         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15056         dim_x = cpl_propertylist_get_double(header, keyname);
15057         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15058             cpl_error_set_where(func);
15059             return NULL;
15060         }
15061 
15062         sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15063         if (cpl_propertylist_has(header, keyname)) {
15064             curved = 1;
15065         }
15066         else {
15067             sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15068             curved = 0;
15069         }
15070         dim_y = cpl_propertylist_get_double(header, keyname);
15071         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15072             cpl_error_set_where(func);
15073             return NULL;
15074         }
15075 
15076         cpl_table_set_int(slits, "slit_id", i, slit_id);
15077         cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15078         cpl_table_set(slits, "ytop", i, slit_y);
15079         cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15080         cpl_table_set(slits, "ybottom", i, slit_y);
15081         cpl_table_set(slits, "xwidth", i, dim_x);
15082         cpl_table_set(slits, "ywidth", i, dim_y);
15083         cpl_table_set_int(slits, "curved", i, curved);
15084     }
15085 
15086     return slits;
15087 }
15088 
15089 
15099 int mos_check_multiplex(cpl_table *slits)
15100 {
15101     cpl_propertylist *sort;
15102     int               nrow;
15103     int               i, multiplex, xprev, xcur;
15104     double            prev, cur;
15105     double            tolerance = 1.0; // About spatially aligned slits (mm)
15106 
15107 
15108     /*
15109      * Create an auxiliary column containing a sort of integer
15110      * x coordinate of the slit, to guarantee that slits at the
15111      * same spatial offset are recognised immediately as in spectral 
15112      * multiplexing.
15113      */
15114 
15115     sort = cpl_propertylist_new();
15116     cpl_propertylist_append_bool(sort, "xtop", 0);
15117     cpl_table_sort(slits, sort);
15118     cpl_propertylist_delete(sort);
15119 
15120     prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15121     cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15122     cpl_table_set_int(slits, "xind", 0, prev);   // cast to int is intentional
15123     nrow = cpl_table_get_nrow(slits);
15124     for (i = 1; i < nrow; i++) {
15125         cur = cpl_table_get_double(slits, "xtop", i, NULL);
15126         if (fabs(prev - cur) > tolerance)
15127             prev = cur;
15128         cpl_table_set_int(slits, "xind", i, prev);
15129     }
15130 
15131     /*
15132      * Now sort according to increasing (integer) x positions, and when
15133      * those are equal (multiplexed) according to the increasing y position.
15134      */
15135 
15136     sort = cpl_propertylist_new();
15137     cpl_propertylist_append_bool(sort, "xind", 0);
15138     cpl_propertylist_append_bool(sort, "ytop", 0);
15139     cpl_table_sort(slits, sort);
15140     cpl_propertylist_delete(sort);
15141 
15142     /*
15143      * Now assign to each slit its multiplex order.
15144      */
15145 
15146     multiplex = 0;
15147     cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15148     xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15149     cpl_table_set_int(slits, "multiplex", 0, multiplex);
15150     nrow = cpl_table_get_nrow(slits);
15151     for (i = 1; i < nrow; i++) {
15152         xcur = cpl_table_get_int(slits, "xind", i, NULL);
15153         if (xcur == xprev) {
15154             multiplex++;
15155         }
15156         else {
15157             xprev = xcur;
15158             multiplex = 0;
15159         }
15160         cpl_table_set_int(slits, "multiplex", i, multiplex);
15161     }
15162 
15163     cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15164 
15165     cpl_table_erase_column(slits, "xind");
15166 
15167     return 1 + cpl_table_get_column_max(slits, "multiplex");
15168 
15169 }
15170 
15171 
15198 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
15199                                     int check_consistency)
15200 {
15201     const char *func = "mos_load_overscans_vimos";
15202 
15203     int        nx = 0;
15204     int        ny = 0;
15205     int        px = 0;
15206     int        py = 0;
15207     int        ox = 0;
15208     int        oy = 0;
15209     int        vx = 0;
15210     int        vy = 0;
15211     int        nrows;
15212     cpl_table *overscans;
15213 
15214 
15215     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15216         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15217         return NULL;
15218     }
15219 
15220     if (header == NULL) {
15221         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15222         return NULL;
15223     }
15224 
15225     if (cpl_propertylist_has(header, "NAXIS1"))
15226         nx = cpl_propertylist_get_int(header, "NAXIS1");
15227     if (cpl_propertylist_has(header, "NAXIS2"))
15228         ny = cpl_propertylist_get_int(header, "NAXIS2");
15229     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15230         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15231     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15232         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15233     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15234         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15235     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15236         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15237     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15238         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15239     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15240         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15241 
15242     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15243         cpl_msg_error(func, "Missing overscan keywords in header");
15244         cpl_error_set_where(func);
15245         return NULL;
15246     }
15247 
15248     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15249         cpl_msg_error(func, "Missing overscan keywords in header");
15250         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15251         return NULL;
15252     }
15253 
15254     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15255         if (check_consistency) {
15256             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15257             return NULL;
15258         }
15259         else {
15260             cpl_msg_debug(func, "Overscans description conflicts with "
15261                           "reported image sizes, "
15262                           "%d + %d + %d != %d or "
15263                           "%d + %d + %d != %d",
15264                           px, vx, ox, nx,
15265                           py, vy, oy, ny);
15266         }
15267     }
15268 
15269     nrows = 0;
15270     if (px > 0)
15271         nrows++;
15272     if (ox > 0)
15273         nrows++;
15274     if (py > 0)
15275         nrows++;
15276     if (oy > 0)
15277         nrows++;
15278 
15279     if (nrows > 2) {
15280         cpl_msg_error(func, "Unexpected overscan regions "
15281                       "(both in X and Y direction)");
15282         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15283         return NULL;
15284     }
15285 
15286 
15287     /*
15288      * A row is added for the description of the valid region of the
15289      * exposure the input header belongs to.
15290      */
15291 
15292     nrows++;
15293 
15294     overscans = cpl_table_new(nrows);
15295     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15296     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15297     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15298     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15299 
15300     nrows = 0;
15301 
15302     cpl_table_set_int(overscans, "xlow", nrows, px);
15303     cpl_table_set_int(overscans, "ylow", nrows, py);
15304     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15305     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15306     nrows++;
15307 
15308     if (px > 0) {
15309         cpl_table_set_int(overscans, "xlow", nrows, 0);
15310         cpl_table_set_int(overscans, "ylow", nrows, 0);
15311         cpl_table_set_int(overscans, "xhig", nrows, px);
15312         cpl_table_set_int(overscans, "yhig", nrows, ny);
15313         nrows++;
15314     }
15315 
15316     if (ox > 0) {
15317         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15318         cpl_table_set_int(overscans, "ylow", nrows, 0);
15319         cpl_table_set_int(overscans, "xhig", nrows, nx);
15320         cpl_table_set_int(overscans, "yhig", nrows, ny);
15321         nrows++;
15322     }
15323 
15324     if (py > 0) {
15325         cpl_table_set_int(overscans, "xlow", nrows, 0);
15326         cpl_table_set_int(overscans, "ylow", nrows, 0);
15327         cpl_table_set_int(overscans, "xhig", nrows, nx);
15328         cpl_table_set_int(overscans, "yhig", nrows, py);
15329         nrows++;
15330     }
15331 
15332     if (oy > 0) {
15333         cpl_table_set_int(overscans, "xlow", nrows, 0);
15334         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15335         cpl_table_set_int(overscans, "xhig", nrows, nx);
15336         cpl_table_set_int(overscans, "yhig", nrows, ny);
15337         nrows++;
15338     }
15339 
15340     return overscans;
15341 
15342 }
15343 
15344 
15345 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15346 {
15347     const char *func = "mos_load_overscans_fors";
15348 
15349     int        nports;
15350     int        nx = 0;
15351     int        ny = 0;
15352     int        px = 0;
15353     int        py = 0;
15354     int        ox = 0;
15355     int        oy = 0;
15356     int        rebin;
15357     int        nrows;
15358     cpl_table *overscans;
15359 
15360 
15361     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15362         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15363         return NULL;
15364     }
15365 
15366     if (header == NULL) {
15367         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15368         return NULL;
15369     }
15370 
15371     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15372         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15373 
15374     if (nports == 4                                        && 
15375         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15376         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15377 
15378         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15379 
15380         overscans = cpl_table_new(3);
15381         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15382         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15383         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15384         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15385 
15386         px = 16 / rebin;
15387         ox = 16 / rebin;
15388         nx = 2080 / rebin;
15389         ny = 2048 / rebin;
15390         nrows = 0;
15391 
15392         cpl_table_set_int(overscans, "xlow", nrows, px);
15393         cpl_table_set_int(overscans, "ylow", nrows, py);
15394         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15395         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15396         nrows++;
15397 
15398         cpl_table_set_int(overscans, "xlow", nrows, 0);
15399         cpl_table_set_int(overscans, "ylow", nrows, 0);
15400         cpl_table_set_int(overscans, "xhig", nrows, px);
15401         cpl_table_set_int(overscans, "yhig", nrows, ny);
15402         nrows++;
15403 
15404         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15405         cpl_table_set_int(overscans, "ylow", nrows, 0);
15406         cpl_table_set_int(overscans, "xhig", nrows, nx);
15407         cpl_table_set_int(overscans, "yhig", nrows, ny);
15408         nrows++;
15409     }
15410     else {
15411         overscans = mos_load_overscans_vimos(header, 0);
15412     }
15413 
15414     return overscans;
15415 
15416 }
15417 
15449 #define READY 1
15450 #ifdef READY
15451 
15452 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
15453                                        int samples, int order)
15454 {
15455 
15456     const char *func = "mos_montecarlo_polyfit";
15457 
15458     cpl_polynomial *p;
15459     cpl_polynomial *q;
15460     cpl_vector     *listx;
15461     cpl_vector     *listy;
15462     double          err;
15463     double         *x;
15464     double         *px;
15465     double         *x_eval;
15466     double         *px_eval;
15467     double         *sigma;
15468     double         *vy;
15469     double         *dy;
15470     int             npoints, nevaluate;
15471     int             i, j;
15472 
15473 
15474     if (points == NULL || evaluate == NULL) {
15475         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15476         return NULL;
15477     }
15478 
15479     if (!cpl_table_has_column(points, "x")) {
15480         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15481         return NULL;
15482     }
15483 
15484     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
15485         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15486         return NULL;
15487     }
15488 
15489     if (cpl_table_has_invalid(points, "x")) {
15490         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15491         return NULL;
15492     }
15493 
15494     if (!cpl_table_has_column(points, "y")) {
15495         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15496         return NULL;
15497     }
15498 
15499     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
15500         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15501         return NULL;
15502     }
15503 
15504     if (cpl_table_has_invalid(points, "y")) {
15505         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15506         return NULL;
15507     }
15508 
15509     if (cpl_table_has_column(points, "y_err")) {
15510 
15511         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
15512             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15513             return NULL;
15514         }
15515     
15516         if (cpl_table_has_invalid(points, "y_err")) {
15517             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15518             return NULL;
15519         }
15520     }
15521 
15522     if (!cpl_table_has_column(evaluate, "x")) {
15523         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15524         return NULL;
15525     }
15526 
15527     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
15528         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15529         return NULL;
15530     }
15531 
15532     if (cpl_table_has_invalid(evaluate, "x")) {
15533         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15534         return NULL;
15535     }
15536 
15537     if (samples < 2 || order < 0) {
15538         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15539         return NULL;
15540     }
15541 
15542     npoints = cpl_table_get_nrow(points);
15543     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
15544     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
15545 
15546     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
15547 
15548     if (!cpl_table_has_column(points, "y_err")) {
15549         err = sqrt(err);
15550         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
15551         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
15552         cpl_msg_info(func, "Error column not found - set to %f\n", err);
15553     }
15554 
15555     /*
15556      * Create columns containing modeled values at each x
15557      */
15558 
15559     if (cpl_table_has_column(points, "px"))
15560         cpl_table_erase_column(points, "px");
15561     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
15562     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
15563     x = cpl_table_get_data_double(points, "x");
15564     px = cpl_table_get_data_double(points, "px");
15565     for (i = 0; i < npoints; i++)
15566         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
15567 
15568     nevaluate = cpl_table_get_nrow(evaluate);
15569 
15570     if (cpl_table_has_column(evaluate, "px"))
15571         cpl_table_erase_column(evaluate, "px");
15572     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
15573     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
15574     x_eval = cpl_table_get_data_double(evaluate, "x");
15575     px_eval = cpl_table_get_data_double(evaluate, "px");
15576     for (i = 0; i < nevaluate; i++)
15577         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
15578 
15579     /*
15580      * Initialise column with sigma
15581      */
15582 
15583     if (cpl_table_has_column(evaluate, "sigma"))
15584         cpl_table_erase_column(evaluate, "sigma");
15585     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
15586     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
15587     sigma = cpl_table_get_data_double(evaluate, "sigma");
15588 
15589     /*
15590      * Compute varied y cordinates to fit
15591      */
15592 
15593     if (cpl_table_has_column(points, "vy"))
15594         cpl_table_erase_column(points, "vy");
15595     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
15596     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
15597     vy = cpl_table_get_data_double(points, "vy");
15598     dy = cpl_table_get_data_double(points, "y_err");
15599     cpl_vector_unwrap(listy);
15600     listy = cpl_vector_wrap(npoints, vy);
15601 
15602     for (i = 0; i < samples; i++) {
15603         for (j = 0; j < npoints; j++)
15604             vy[j] = px[j] + dy[j] * mos_randg(1);
15605         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
15606         for (j = 0; j < nevaluate; j++)
15607             sigma[j] += fabs(px_eval[j] 
15608                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
15609         cpl_polynomial_delete(q);
15610     }
15611 
15612     /* 
15613      * Factor 1.25 to convert average deviation to sigma 
15614      */
15615 
15616     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
15617     cpl_table_divide_scalar(evaluate, "sigma", samples);
15618 
15619     cpl_vector_unwrap(listx);
15620     cpl_vector_unwrap(listy);
15621 
15622     return p;
15623 }
15624 
15625 #endif
15626 
15649 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 
15650                                    double gain, double bias)
15651 {
15652     float *data;
15653     int    npix, i;
15654 
15655 
15656     if (image == NULL)
15657         return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
15658 
15659     if (ron < 0.0 || gain <= FLT_EPSILON)
15660         return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
15661 
15662     data = cpl_image_get_data_float(image);
15663     npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
15664     ron *= ron;
15665 
15666     for (i = 0; i < npix; i++) {
15667         if (data[i] < bias) {
15668             data[i] += sqrt(ron) * mos_randg(1);
15669         }
15670         else {
15671             data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
15672         }
15673     }
15674 
15675     return CPL_ERROR_NONE;
15676 }
15677 
15678 
15693 cpl_error_code mos_refmask_find_gaps(cpl_mask  *refmask,
15694                                      cpl_image *master_flat,
15695                                      double     level)
15696 {
15697     int          nx     = cpl_mask_get_size_x(refmask);
15698     int          ny     = cpl_mask_get_size_y(refmask);
15699 
15700     int        * xpos   = cpl_calloc(sizeof(int), ny);
15701 
15702     cpl_image  * filtered = cpl_image_duplicate(master_flat);
15703     cpl_mask   * kernel = cpl_mask_new(9, 9);
15704     cpl_vector * v      = cpl_vector_new(ny);
15705     cpl_vector * truev;
15706     int          nvalid = 0;
15707     double     * flats  = cpl_vector_get_data(v);
15708 
15709     double       median, stdev, delta;
15710 
15711     int          i, kill;
15712 
15713 
15714     cpl_mask_not(kernel);
15715     cpl_image_filter_mask(filtered, master_flat, kernel, 
15716                           CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
15717     cpl_mask_delete(kernel);
15718 
15719     for (i = 1; i <= ny; i++) {
15720         int j = 0;
15721 
15722         do j++;
15723         while (!cpl_mask_get(refmask, j, i) && j < nx);
15724 
15725         if (j < nx) {
15726             int rejected;
15727 
15728             xpos[i - 1] = j;
15729             flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
15730             nvalid++;
15731         }
15732         else {
15733             xpos[i - 1] = -1;
15734         }
15735     }
15736 
15737     if (nvalid == 0)
15738         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15739 
15740     truev = cpl_vector_wrap(nvalid, flats);
15741 
15742     median = cpl_vector_get_median(truev);
15743 
15744     if (level < 0.0)
15745        stdev = cpl_vector_get_stdev(truev);
15746 
15747     cpl_vector_unwrap(truev);
15748     cpl_vector_delete(v);
15749 
15750     for (i = 1; i <= ny; i++) {
15751         if (xpos[i - 1] > 0) {
15752             int    rejected;
15753             double kappa = 1.0;
15754 
15755             delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
15756 
15757             if (level < 0.0)
15758                 kill = fabs(delta) > stdev * kappa;
15759             else
15760                 kill = delta < level;
15761 
15762             if (kill) {
15763                 int j = 0;
15764             
15765                 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
15766                     cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
15767                     j++;
15768                 }
15769             }
15770         }
15771     }
15772 
15773     cpl_image_delete(filtered);
15774     cpl_free(xpos);
15775 
15776     return cpl_error_get_code();
15777 }
15778 
15786 cpl_error_code mos_saturation_process(cpl_image * image)
15787 {
15788     int     nx    = cpl_image_get_size_x(image);
15789     int     ny    = cpl_image_get_size_y(image);
15790     int     npix  = nx * ny;
15791     float * sdata = cpl_image_get_data_float(image);
15792 
15793     int count, i, j, k;
15794 
15795     /*
15796      * This is used to avoid saturation level coded with pixel value zero
15797      * To make it more robust against random 0.0 values, check that also
15798      * next pixel along the spatial direction is 0.0.
15799      */
15800 
15801     for (i = 0; i < npix - nx; i++)
15802         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
15803             sdata[i] = 65535.0;
15804 
15805     for (i = npix - nx; i < npix; i++)
15806         if (sdata[i] == 0.0) 
15807             sdata[i] = 65535.0;
15808 
15809     /*
15810      * This is a dirty trick to overcome saturations (making up a false
15811      * tip on their flat tops). This should be useless with a better
15812      * peak detection algorithm.
15813      */
15814 
15815     for (i = 0; i < npix; i++) {
15816         if (sdata[i] >= 65535.0) {
15817             count = 0;
15818             for (j = i; j < npix; j++) {
15819                 if (sdata[j] < 65535.0) {
15820                     break;
15821                 }
15822                 else {
15823                     count++;
15824                 }
15825             }
15826             if (count < 30 && count > 2) {
15827                 for (j = i; j < i + count/2; j++)
15828                     sdata[j] = sdata[i] + 1000.0 * (j - i);
15829                 if (count % 2 != 0) {
15830                     sdata[j] = sdata[j-1] + 1000.0;
15831                     j++;
15832                 }
15833                 for (k = j; k <= i + count; k++)
15834                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
15835                 i = k;
15836             }
15837         }
15838     }
15839 
15840     return cpl_error_get_code();
15841 }
15842 
15843 
15852 cpl_error_code mos_subtract_background(cpl_image * image)
15853 {
15854     /*
15855      * Create and subtract background
15856      */
15857 
15858     cpl_image * bimage = mos_arc_background(image, 15, 15);
15859     cpl_image_subtract(image, bimage);
15860     cpl_image_delete(bimage);
15861 
15862     return cpl_error_get_code();
15863 }
15864 
15865 
15882 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 
15883                                     int nscience, float tolerance)
15884 {
15885     int i, j;
15886 
15887     cpl_table *summary;
15888     int summary_nobjs = 0;
15889  
15890     int nobjs;
15891 
15892     int nmatches;
15893     int nslits = cpl_table_get_nrow(slitss[0]);
15894 
15895     int maxobjs;
15896     int k, m;
15897     int nstokes, sstokes;
15898 
15899     cpl_table **work;
15900 
15901     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
15902 
15903 
15904     /* 
15905      * First we build a table listing the offset of each detected
15906      * object at each angle and each beam, from the bottom of each 
15907      * slit spectrum, and the pair that slit spectrum belongs to.
15908      * This summary table will have as many rows as objects found 
15909      * in total at all angles.
15910      */
15911 
15912     for (j = 0; j < nscience; j++) {
15913         int c_nobjs = mos_get_nobjects(slitss[j]);
15914         if (!c_nobjs) 
15915             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15916         summary_nobjs += c_nobjs;
15917     }
15918 
15919     summary = cpl_table_new(summary_nobjs);
15920 
15921     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
15922     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
15923     cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
15924     cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
15925 
15926     /*
15927      * Fill the summary table with data from all objects:
15928      */
15929 
15930     nobjs = 0;
15931 
15932     /* Loop on all object tables (one for each angle) */
15933     for (j = 0; j < nscience; j++) {
15934         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
15935 
15936         /* Loop on all slits found on first - i.e., ALL - object table */
15937         for (k = 0; k < nslits; k++) {
15938 
15939             /* Loop on all objects found on each object table */
15940             for (m = 0; m < c_maxobjs; m++) {
15941                 int null;
15942                 char *name = cpl_sprintf("object_%d", m + 1);
15943                 double obj = cpl_table_get_double(slitss[j], name, k, &null);
15944                 int pos;
15945                 int pair;
15946 
15947                 cpl_free(name);
15948 
15949                 if (null) 
15950                     break;  /* No object #m+1 in this slit - go to next slit */
15951 
15952                 /*
15953                  * Copy necessary object data to summary table. Note 
15954                  * that the absolute object position (row) in the
15955                  * rectified image is made relative to the bottom
15956                  * position (row) of the current slit.
15957                  */ 
15958         
15959                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
15960                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
15961                 cpl_table_set(summary, "absolute", nobjs, obj);
15962                 cpl_table_set(summary, "pos", nobjs, pos);
15963                 cpl_table_set(summary, "offset", nobjs, obj - pos);
15964                 cpl_table_set(summary, "pair", nobjs, pair);
15965 
15966                 nobjs++;
15967             }
15968         }
15969     }
15970 
15971 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
15972 
15973     /* 
15974      * Perform the intersection: what are the objects belonging
15975      * to the same slit (same pair ordinary + extraordinary) which 
15976      * are observed at the same offset at all angles? Those are
15977      * the polarimetric objects.
15978      */
15979 
15980     nmatches = 0;
15981     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
15982 
15983     /*
15984      * We loop on the objects of the first-angle object table as 
15985      * reference, and check whether those objects are present also
15986      * at *all* other angles. Note that the loop advances by pairs.
15987      * If the top (k = 0) slit spectrum is not an ordinary beam,
15988      * it is ignored. The loop advances by pairs, starting at the
15989      * first complete pair. It is implicitely assumed that the 
15990      * slit spectrum on top is always from the ordinary beam, and 
15991      * the spectrum below (k+1) its extraordinary match.
15992      */
15993 
15994     for (k = 0; k < nslits; k+=2) {
15995         int slitmatches = 0;
15996 
15997         if (k == 0) {
15998             if (cpl_table_get_int(slitss[0], "pair_id",  0, NULL) !=
15999                 cpl_table_get_int(slitss[0], "pair_id",  1, NULL)) {
16000 
16001                 /*
16002                  * This is not an ordinary beam - advance to next slit.
16003                  */
16004 
16005                 k++;
16006                 continue;
16007             }
16008         }
16009 
16010         for (m = 0; m < maxobjs; m++) {
16011             int null;
16012             char *name = cpl_sprintf("object_%d", m + 1);
16013             double obj = cpl_table_get_double(slitss[0], name, k, &null);
16014             double pos;
16015             int pair;
16016 
16017             char *name_obj = NULL;
16018             char *name_start = NULL;
16019             char *name_end = NULL;
16020             char *name_row = NULL;
16021             char *name_row_s = NULL;
16022 
16023             char *name_start_o = NULL;
16024             char *name_end_o = NULL;
16025             char *name_row_o = NULL;
16026             char *name_start_v = NULL;
16027             char *name_end_v = NULL;
16028             char *name_obj_v = NULL;
16029 
16030             int start, end;
16031             int length;
16032  
16033             int selected;
16034             int v, start_v, end_v;
16035             double min_v, obj_v;
16036 
16037 
16038             cpl_free(name);
16039 
16040             if (null) 
16041                 break;
16042 
16043             /*
16044              * Each object of the first object table belongs to a
16045              * slit spectrum (k). This slit spectrum has a position
16046              * in the rectified image, and it belongs to a given 
16047              * ordinary + extraordinary pair.
16048              */
16049      
16050             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
16051             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
16052 
16053             /*
16054              * Now from the summary table we can select all objects
16055              * which have the same offset (obj - pos) within all slit
16056              * spectra belonging to the same ordinary + extraordinary 
16057              * pair (at all angles).
16058              */
16059 
16060             cpl_table_select_all(summary);  /* Reset selection */
16061 
16062             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16063             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16064                                           obj - pos + tolerance);
16065             selected = 
16066             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16067                                           obj - pos - tolerance);
16068 
16069 
16070             /*
16071              * If this object were observed at all angles (nscience) and 
16072              * at all beams (2), we should have selected exactly 2*nscience
16073              * objects. If not, this is not a polarimetric object, and it
16074              * is discarded from the intersection.
16075              */
16076             
16077             if (selected != nscience * 2) 
16078                 continue;
16079 
16080             /*
16081              * If we reach this point we have found one valid polarimetric
16082              * object, that must be inserted in the intersection object
16083              * table.
16084              */
16085  
16086             slitmatches++;
16087 
16088             /*
16089              * Names of the columns of the output table where the
16090              * object information needs to be copied. Note that a
16091              * new column is created, the "row_stokes_#", where the
16092              * row number of the extracted polarimetric signal is
16093              * also computed. For the moment this column will be 
16094              * left empty - it will be filled only when all matches 
16095              * are collected.
16096              */
16097 
16098             name_obj   = cpl_sprintf("object_%d",     slitmatches);
16099             name_start = cpl_sprintf("start_%d",      slitmatches);
16100             name_end   = cpl_sprintf("end_%d",        slitmatches);
16101             name_row   = cpl_sprintf("row_%d",        slitmatches);
16102             name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16103 
16104             /*
16105              * Names of the columns of the input table where the
16106              * object information is available.
16107              */
16108 
16109             name_start_o = cpl_sprintf("start_%d",  m + 1);
16110             name_end_o   = cpl_sprintf("end_%d",    m + 1);
16111             name_row_o   = cpl_sprintf("row_%d",    m + 1);
16112 
16113             /*
16114              * If the output columns do not exist yet, create them.
16115              */
16116  
16117             if (!cpl_table_has_column(origslits, name_obj)) {
16118                 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16119                 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16120                 cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
16121                 cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
16122                 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16123             }
16124 
16125             /*
16126              * The current slit spectrum is k. The slit spectrum immediately
16127              * below (in the rectified image) is k+1. We need the length of
16128              * the spectrum below for computing the _absolute_ coordinates
16129              * of the objects in the rectified image in both beams.
16130              */
16131  
16132             length = cpl_table_get_int(origslits, "length", k + 1, &null);
16133 
16134             /* NEW:
16135              * Names of the columns of the input table where
16136              * the information of the corresponding object of 
16137              * the next beam is available.
16138              */
16139 
16140             for (v = 0; v < maxobjs; v++) {
16141                 char *name_v = cpl_sprintf("object_%d", v + 1);
16142                 double obj_v = cpl_table_get_double(slitss[0], name_v, 
16143                                                     k + 1, &null);
16144 
16145                 cpl_free(name_v);
16146 
16147                 if (null) 
16148                     break;
16149 
16150                 if (v) {
16151                     if (fabs(obj - length - obj_v) < min_v) {
16152                         min_v = fabs(obj - length - obj_v);
16153                         cpl_free(name_start_v);
16154                         cpl_free(name_end_v);
16155                         cpl_free(name_obj_v);
16156                         name_start_v = cpl_sprintf("start_%d", v + 1);
16157                         name_end_v   = cpl_sprintf("end_%d",   v + 1);
16158                         name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16159                     }
16160                 }
16161                 else {
16162                     min_v = fabs(obj - length - obj_v);
16163                     name_start_v = cpl_sprintf("start_%d", v + 1);
16164                     name_end_v   = cpl_sprintf("end_%d",   v + 1);
16165                     name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16166                 }
16167             }
16168 
16169             /*
16170              * Read from the first input object table (first angle)
16171              * the spatial window enclosing the object.
16172              */
16173 
16174             start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16175             end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
16176 
16177             /* NEW:
16178              * Spatial window of the matching object in the next beam.
16179              */
16180 
16181             start_v = cpl_table_get_int(slitss[0], name_start_v,  k + 1, &null);
16182             end_v   = cpl_table_get_int(slitss[0], name_end_v,    k + 1, &null);
16183             obj_v   = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16184 
16185             /*
16186              * Write the object coordinates in the same slit, and in the
16187              * slit below. Note that here we assume that all slits were
16188              * traced perfectly, and we compute the theoretical coords
16189              * (obj - length) within the next slit spectrum (k + 1). In
16190              * principle we should read them as well from the input
16191              * table!
16192              */
16193 
16194             cpl_table_set_double(origslits, name_obj,   k,     obj);
16195             cpl_table_set_double(origslits, name_obj,   k + 1, obj_v);
16196          // cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
16197 
16198             cpl_table_set_int(origslits,    name_start, k,     start);
16199             cpl_table_set_int(origslits,    name_start, k + 1, start_v);
16200          // cpl_table_set_int(origslits,    name_start, k + 1, start - length);
16201 
16202             cpl_table_set_int(origslits,    name_end,   k,     end);
16203             cpl_table_set_int(origslits,    name_end,   k + 1, end_v);
16204          // cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
16205 
16206             /*
16207              * "nmatches" is counting at what "reduced" image row the
16208              * extracted spectra are. Note that this is s preliminary
16209              * numbering - which is wrong: other objects may be found
16210              * in the same slit, and then the indeces would not be in
16211              * sequence. What is important is that at the end of this
16212              * loop "nmatches" would be the total number of matching 
16213              * objects. The two cpl_table_set_int() calls made here
16214              * cannot be removed - they "validate" those table elements
16215              * (see ahead). 
16216              */
16217 
16218             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
16219             nmatches++;
16220             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
16221             nmatches++;
16222 
16223             cpl_free(name_obj);
16224             cpl_free(name_start);
16225             cpl_free(name_end);
16226             cpl_free(name_row);
16227             cpl_free(name_row_s);
16228 
16229             cpl_free(name_start_o);
16230             cpl_free(name_end_o);
16231             cpl_free(name_row_o);
16232 
16233             cpl_free(name_start_v); name_start_v = NULL;
16234             cpl_free(name_end_v); name_end_v = NULL;
16235             cpl_free(name_obj_v); name_obj_v = NULL;
16236         }
16237     }
16238 
16239     /*
16240      * The summary table has fulfilled its function. If no matching 
16241      * objects are found, the function returns with an error.
16242      */
16243 
16244     cpl_table_delete(summary);
16245 
16246     if (!nmatches)
16247         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
16248 
16249     /*
16250      * Now we consider the resulting intersection object table,
16251      * listing all matches. As seen, the image row number reported
16252      * in the columns "row_#" was not really performed sequentially.
16253      * We need to renumber sequentially...
16254      * We need also to fill the "row_stokes_#" column the way the
16255      * extracted polarimetric signal will be stored in the 
16256      * reduced_pol_images...
16257      */
16258  
16259     maxobjs = mos_get_maxobjs_per_slit(origslits);
16260     nstokes = nmatches / 2;         /* nmatches is always an even number     */
16261 
16262     for (k = 0; k < nslits; k++) {
16263         if (k % 2) { /* Extraordinary beam */
16264             nstokes = sstokes;      /* Use same start value as for ordinary  */
16265         }
16266         else {       /* Ordinary beam      */
16267             sstokes = nstokes;      /* Memorise start value at ordinary beam */
16268         }
16269 
16270         for (m = 0; m < maxobjs; m++) {
16271             char *name       = cpl_sprintf("row_%d",        m + 1);
16272             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16273 
16274             if (!cpl_table_is_valid(origslits, name, k)) {
16275                 cpl_free(name);
16276                 cpl_free(namestokes);
16277                 break;
16278             }
16279             else { 
16280                 nmatches--;
16281                 nstokes--;
16282                 cpl_table_set_int(origslits, name, k, nmatches);
16283                 cpl_table_set_int(origslits, namestokes, k, nstokes);
16284             }
16285 
16286             cpl_free(name);
16287             cpl_free(namestokes);
16288         }
16289     }
16290 
16291 
16292     /*
16293      * This is done to avoid the NULL value is zero (it would invalidate
16294      * also the row_# = 0 or start_# = 0 for an object), and to enable 
16295      * working directly with the column data buffers, when using this 
16296      * table afterwards.
16297      */
16298 
16299     for (j = 0; j < maxobjs; j++) {
16300         char *name = cpl_sprintf("object_%d", j + 1);
16301         cpl_table_fill_invalid_double(origslits, name, -1);
16302         cpl_free(name);
16303 
16304         name       = cpl_sprintf("start_%d", j + 1);
16305         cpl_table_fill_invalid_int(origslits, name, -1);
16306         cpl_free(name);
16307 
16308         name       = cpl_sprintf("end_%d", j + 1);
16309         cpl_table_fill_invalid_int(origslits, name, -1);
16310         cpl_free(name);
16311 
16312         name       = cpl_sprintf("row_%d", j + 1);
16313         cpl_table_fill_invalid_int(origslits, name, -1);
16314         cpl_free(name);
16315 
16316         name       = cpl_sprintf("row_stokes_%d", j + 1);
16317         cpl_table_fill_invalid_int(origslits, name, -1);
16318         cpl_free(name);
16319     }
16320 
16321     /*********************************************************************
16322      * This tail has been added to propagate the selection of valid
16323      * objects also to the input slitss[] tables. Just eliminate all
16324      * this final part to suppress this behaviour.
16325      */
16326 
16327     /*
16328      * First of all, make a working copy and remove all columns related 
16329      * to objects from the input object tables. 
16330      */
16331 
16332     for (i = 0; i < nscience; i++) {
16333         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16334 
16335         work[i] = cpl_table_duplicate(slitss[i]);
16336 
16337         for (m = 0; m < c_maxobjs; m++) {
16338             char *object_o = cpl_sprintf("object_%d", m + 1);
16339             char *start_o  = cpl_sprintf("start_%d",  m + 1);
16340             char *end_o    = cpl_sprintf("end_%d",    m + 1);
16341             char *row_o    = cpl_sprintf("row_%d",    m + 1);
16342 
16343             cpl_table_erase_column(slitss[i], object_o);
16344             cpl_table_erase_column(slitss[i], start_o);
16345             cpl_table_erase_column(slitss[i], end_o);
16346             cpl_table_erase_column(slitss[i], row_o);
16347         }
16348     }
16349 
16350     /* 
16351      * Now just consider all the objects in the intersection table.
16352      */
16353 
16354     for (k = 0; k < nslits; k++) {
16355         for (j = 0; j < maxobjs; j++) {
16356             double object_w, object_r;
16357             int    start_w, start_r;
16358             int    end_w, end_r;
16359             int    row_w, row_r;
16360 
16361             char  *object_i = cpl_sprintf("object_%d", j + 1);
16362             char  *start_i  = cpl_sprintf("start_%d",  j + 1);
16363             char  *end_i    = cpl_sprintf("end_%d",    j + 1);
16364             char  *row_i    = cpl_sprintf("row_%d",    j + 1);
16365 
16366 
16367             if (!cpl_table_is_valid(origslits, object_i, k))
16368                 break;
16369 
16370             /* 
16371              * We have found a valid object (valid because it belongs
16372              * to the intersection). Now we look for this object in each
16373              * one of the original tables, we get its parameters, and
16374              * copy them at the right position (i.e., same position as
16375              * in intersection table). The object will be the one closest
16376              * to the object position (column object_i) in the intersection
16377              * table. Note that we examine the same row, k, in all tables.
16378              */
16379 
16380             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16381             start_w  = cpl_table_get_int   (origslits, start_i,  k, NULL);
16382             end_w    = cpl_table_get_int   (origslits, end_i,    k, NULL);
16383             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
16384 
16385             for (i = 0; i < nscience; i++) {
16386                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16387                 int        minpos;
16388                 double     mindiff, diff;
16389                 char      *object_o;
16390                 char      *start_o;
16391                 char      *end_o;
16392                 char      *row_o;
16393 
16394                 for (m = 0; m < c_maxobjs; m++) {
16395                     object_o = cpl_sprintf("object_%d", m + 1);
16396                     start_o  = cpl_sprintf("start_%d",  m + 1);
16397                     end_o    = cpl_sprintf("end_%d",    m + 1);
16398                     row_o    = cpl_sprintf("row_%d",    m + 1);
16399 
16400                     if (!cpl_table_is_valid(work[i], object_o, k))
16401                         break;
16402 
16403                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16404                     start_r  = cpl_table_get_int   (work[i], start_o,  k, NULL);
16405                     end_r    = cpl_table_get_int   (work[i], end_o,    k, NULL);
16406                     row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
16407 
16408                     diff = fabs(object_w - object_r);
16409                     if (m) {
16410                         if (mindiff > diff) {
16411                             mindiff = diff;
16412                             minpos = m;
16413                         }
16414                     }
16415                     else {
16416                         mindiff = diff;
16417                         minpos = 0;
16418                     }
16419 
16420                     cpl_free(object_o);
16421                     cpl_free(start_o);
16422                     cpl_free(end_o);
16423                     cpl_free(row_o);
16424                 }
16425 
16426                 object_o = cpl_sprintf("object_%d", minpos + 1);
16427                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
16428                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
16429                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
16430 
16431                 if (!cpl_table_has_column(slitss[i], object_i)) {
16432                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16433                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
16434                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
16435                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
16436                     cpl_table_fill_invalid_double(slitss[i], object_i, -1);
16437                     cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
16438                     cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
16439                     cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
16440                 }
16441 
16442                 cpl_table_set_double(slitss[i], object_i, k,
16443                                      cpl_table_get_double(work[i], object_o, 
16444                                                           k, NULL));
16445                 cpl_table_set_int(slitss[i], start_i , k,
16446                                   cpl_table_get_int(work[i], start_o, k, NULL));
16447                 cpl_table_set_int(slitss[i], end_i , k,
16448                                   cpl_table_get_int(work[i], end_o, k, NULL));
16449                 cpl_table_set_int(slitss[i], row_i , k, row_w);
16450 
16451                 cpl_free(object_o);
16452                 cpl_free(start_o);
16453                 cpl_free(end_o);
16454                 cpl_free(row_o);
16455             }
16456 
16457             cpl_free(object_i);
16458             cpl_free(start_i);
16459             cpl_free(end_i);
16460             cpl_free(row_i);
16461         }
16462     }
16463 
16464     for (i = 0; i < nscience; i++)
16465         cpl_table_delete(work[i]);
16466 
16467     cpl_free(work);
16468 
16469 
16470     return cpl_error_get_code();
16471 }
16472 
16473 
16481 int mos_get_maxobjs_per_slit(cpl_table * slits)
16482 {
16483     int maxobjs = 1;
16484 
16485     char * colname = cpl_sprintf("object_%d", maxobjs);
16486     
16487     while (cpl_table_has_column(slits, colname)) {
16488         maxobjs++;
16489         cpl_free(colname);
16490         colname = cpl_sprintf("object_%d", maxobjs);
16491     }
16492     
16493     cpl_free(colname);
16494 
16495     maxobjs--;
16496 
16497     return maxobjs;
16498 }
16499 
16507 int mos_get_nobjects(cpl_table * slits)
16508 {
16509     int nobjs = 0;
16510 
16511     int nslits  = cpl_table_get_nrow(slits);
16512     int maxobjs = mos_get_maxobjs_per_slit(slits);
16513 
16514     int k, m;
16515 
16516     for (k = 0; k < nslits; k++) {
16517         for (m = 0; m < maxobjs; m++) {
16518             char * name = cpl_sprintf("object_%d", m + 1);
16519             int    null = !cpl_table_is_valid(slits, name, k);
16520 
16521             cpl_free(name);
16522 
16523             if (null)  break;
16524             else nobjs++;
16525         }
16526     }
16527 
16528     return nobjs;
16529 }
16530 
16538 int mos_check_slits(cpl_table *slits, float rescale)
16539 {
16540 
16541     cpl_propertylist *sort;
16542 
16543     int nslits  = cpl_table_get_nrow(slits);
16544 
16545     int k, null;
16546 
16547     const float interval = 90.0 * rescale;
16548     const float offset   = (90.0 - 5) * rescale;
16549 
16550 
16551     for (k = 0; k < nslits; k++) {
16552         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16553         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16554 
16555         double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
16556         double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
16557 
16558         int nmiss = (int)((ytop - ybottom) / interval + 0.5);
16559 
16560         if (nmiss > 1) {
16561             cpl_msg_warning(cpl_func, 
16562                             "Some slits could not be properly detected. "
16563                             "There might be accountable inaccuracies.");
16564             while (nmiss > 1) {
16565                 cpl_table_set_size(slits, nslits + 1);
16566 
16567                 /* Fill in new slit 'cut' */
16568 
16569                 /* x coordinates be the same (acceptable approximation) */
16570                 cpl_table_set_double(slits, "xtop",    nslits, xtop);
16571                 cpl_table_set_double(slits, "xbottom", nslits, xbottom);
16572 
16573                 /* Cut */
16574                 if (k == 0) {
16575                     cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
16576                     cpl_table_set_double(slits, "ytop",    nslits, ybottom
16577                                                                    + offset);
16578                     ybottom += interval;
16579                     cpl_table_set_double(slits, "ybottom", k,      ybottom);
16580                 } else {
16581                     cpl_table_set_double(slits, "ytop",    nslits, ytop);
16582                     cpl_table_set_double(slits, "ybottom", nslits, ytop 
16583                                                                    - offset);
16584                     ytop -= interval;
16585                     cpl_table_set_double(slits, "ytop",     k,     ytop);
16586                 }
16587 
16588                 nslits++; nmiss--;
16589             }
16590         }
16591     }
16592 
16593     sort = cpl_propertylist_new();
16594     cpl_propertylist_append_bool(sort, "ytop", 1);
16595     cpl_table_sort(slits, sort);
16596     cpl_propertylist_delete(sort);
16597 
16598     /*
16599      * Add here an ad hoc check on the last slit: is it too long 
16600      * (by more than 10%)? Then shorten it...
16601      */
16602 
16603     k = cpl_table_get_nrow(slits) - 1;
16604 
16605     {
16606         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16607         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16608         double length  = (ytop - ybottom) / interval;
16609 
16610         if (length > 1.1) {
16611             cpl_table_set_double(slits, "ybottom", k, ytop - offset);
16612         }
16613   
16614     }
16615 
16616     return 0;
16617 }
16618 
16641 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header)
16642 {
16643     int m, null;
16644     int halfsize;
16645 
16646     cpl_propertylist * sort;
16647     cpl_table        * slits; 
16648 
16649     slits    = mos_load_slits_fors_mos(header);
16650     halfsize = cpl_table_get_nrow(slits);
16651 
16652     cpl_table_set_size(slits, 2 * halfsize);
16653 
16654     for (m = 0; m < halfsize; m++) {
16655 
16656         double gap = 1.4;
16657 
16658         double length = 
16659             cpl_table_get(slits, "ytop",    m, &null) -
16660             cpl_table_get(slits, "ybottom", m, &null);
16661 
16662         if (m) {
16663             double interval = 
16664                 cpl_table_get(slits, "ybottom", m - 1, &null) -
16665                 cpl_table_get(slits, "ytop",    m,     &null);
16666 
16667             gap = (interval - length) / 2;
16668         }
16669 
16670         cpl_table_set(slits, "slit_id", m + halfsize,
16671                       cpl_table_get(slits, "slit_id", m, &null) - 1);
16672 
16673         cpl_table_set(slits, "xtop",    m + halfsize,
16674                       cpl_table_get(slits, "xtop",    m, &null));
16675 
16676         cpl_table_set(slits, "xbottom", m + halfsize,
16677                       cpl_table_get(slits, "xbottom", m, &null));
16678 
16679         cpl_table_set(slits, "ytop",    m + halfsize, 
16680                       cpl_table_get(slits, "ytop", m, &null) + gap + length);
16681 
16682         cpl_table_set(slits, "ybottom", m + halfsize,
16683                       cpl_table_get(slits, "ytop", m, &null) + gap);
16684     }
16685 
16686     for (m = 0; m < 2 * halfsize; m++) {
16687         cpl_table_set(slits, "ytop",    m, 
16688                       cpl_table_get(slits, "ytop",    m, &null) - 5.3);
16689 
16690         cpl_table_set(slits, "ybottom", m,
16691                       cpl_table_get(slits, "ybottom", m, &null) - 5.3);
16692 
16693     }
16694 
16695     sort = cpl_propertylist_new();
16696     cpl_propertylist_append_bool(sort, "ytop", 1);
16697     cpl_table_sort(slits, sort);
16698 
16699     cpl_propertylist_delete(sort);
16700 
16701     return slits;
16702 }
16703 
16704 int * fors_get_nobjs_perslit(cpl_table * slits)
16705 {
16706     int nslits  = cpl_table_get_nrow(slits);
16707     int maxobjs = mos_get_maxobjs_per_slit(slits);
16708 
16709     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
16710 
16711     int k, m;
16712 
16713     for (k = 0; k < nslits; k++) {
16714         int nobjs = 0;
16715         for (m = 0; m < maxobjs; m++) {
16716             char * name = cpl_sprintf("object_%d", m + 1);
16717             int    null = !cpl_table_is_valid(slits, name, k);
16718 
16719             cpl_free(name);
16720 
16721             if (null)  break;
16722             else nobjs++;
16723         }
16724         
16725         nobjs_per_slit[k] = nobjs;
16726     }
16727 
16728     return nobjs_per_slit;
16729 }
16730 
16731 double fors_get_object_position(cpl_table *slits, int slit, int object)
16732 {
16733     char   *name = cpl_sprintf("object_%d", object);
16734     double  position;
16735 
16736     position = cpl_table_get_double(slits, name, slit, NULL)
16737              - cpl_table_get_int(slits, "position", slit, NULL);
16738 
16739     cpl_free(name);
16740 
16741     return position;
16742 }
16743 
16744 int mos_rebin_signal(cpl_image **image, int rebin)
16745 {
16746     cpl_image *rebinned;
16747 
16748 
16749     if (*image == NULL)
16750         return 1;
16751 
16752     if (rebin == 1)
16753         return 0;
16754 
16755     rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
16756 
16757     cpl_image_delete(*image);
16758 
16759     *image = rebinned;
16760 
16761     return 0;
16762 }
16763 
16764 int mos_rebin_error(cpl_image **image, int rebin)
16765 {
16766     if (*image == NULL)
16767         return 1;
16768 
16769     if (rebin == 1)
16770         return 0;
16771 
16772     cpl_image_power(*image, 2);
16773     mos_rebin_signal(image, rebin);
16774     cpl_image_power(*image, 0.5);
16775 
16776     return 0;
16777 }
16778 
16779 /*
16780  * @brief
16781  *   Map table values into a 1D image
16782  *  
16783  * @param image       Target image
16784  * @param start       Coordinate of first pixel in image
16785  * @param step        Coordinate step for one pixel in image
16786  * @param table       Source table
16787  * @param xname       Name of coordinate column
16788  * @param yname       Name of values column
16789  *
16790  * @return 0 on success
16791  *
16792  * The values in @em yname are linearly interpolated at the @em image 
16793  * pixel coordinates. The @em image must have Nx1 size.
16794  */
16795 
16796 int map_table(cpl_image *image, double start, double step,
16797               cpl_table *table, char *xname, char *yname)
16798 {
16799     int      length = cpl_image_get_size_x(image);
16800     int      nrows  = cpl_table_get_nrow(table);
16801     float   *data   = cpl_image_get_data_float(image);
16802     float   *fdata  = NULL;
16803     double  *xdata  = NULL;
16804     double  *ydata  = NULL;
16805     cpl_type xtype  = cpl_table_get_column_type(table, xname);
16806     cpl_type ytype  = cpl_table_get_column_type(table, yname);
16807     double   xzero, pos;
16808     int      i, j, n;
16809 
16810 
16811     /*
16812      * Initialization of output image at 0.0 - this value is left 
16813      * on non-overlapping portions.
16814      */
16815 
16816     for (i = 0; i < length; i++)
16817         data[i] = 0.0;
16818 
16819 
16820     /*
16821      * Do everything in double precision
16822      */
16823 
16824     if (xtype == CPL_TYPE_FLOAT) {
16825         fdata = cpl_table_get_data_float(table, xname);
16826         xdata = cpl_malloc(nrows * sizeof(double));
16827         for (i = 0; i < nrows; i++) {
16828            xdata[i] = fdata[i];
16829         }
16830     }
16831     else {
16832         xdata = cpl_table_get_data_double(table, xname);
16833     }
16834 
16835     if (ytype == CPL_TYPE_FLOAT) {
16836         fdata = cpl_table_get_data_float(table, yname);
16837         ydata = cpl_malloc(nrows * sizeof(double));
16838         for (i = 0; i < nrows; i++) {
16839            ydata[i] = fdata[i];
16840         }
16841     }
16842     else {
16843         ydata = cpl_table_get_data_double(table, yname);
16844     }
16845 
16846     /*
16847      * Mapping
16848      */
16849 
16850     n = 0;
16851     xzero = xdata[n];
16852 
16853     for (i = 0; i < length; i++) {
16854         pos = start + step * i;
16855         if (pos < xzero)
16856             continue;
16857         for (j = n; j < nrows; j++) {
16858             if (xdata[j] > pos) {
16859                 n = j;
16860                 data[i] = ydata[j-1]
16861                         + (ydata[j] - ydata[j-1])
16862                         * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
16863                 break;
16864             }
16865         }
16866     }
16867 
16868     if (xtype == CPL_TYPE_FLOAT)
16869         cpl_free(xdata);
16870 
16871     if (ytype == CPL_TYPE_FLOAT)
16872         cpl_free(ydata);
16873 
16874     return 0;
16875 }
16876 
16877 
16878 /*
16879  * @brief
16880  *   Fit overall trend of a Nx1 image
16881  *  
16882  * @param image       Values to smooth
16883  * @param order       Order of fitting polynomial
16884  * @param hw          Half width of smoothing window
16885  *
16886  * @return Smoothed image, or NULL on failure.
16887  *
16888  * Heavily smooth and fit data in the input Nx1 size @em image.
16889  */
16890 
16891 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
16892 {
16893     int             npoints;
16894     cpl_vector     *x;
16895     cpl_vector     *y;
16896     double         *xdata;
16897     double         *ydata;
16898     cpl_polynomial *poly;
16899     cpl_vector     *ysmooth;
16900     cpl_image      *smoothed;
16901     float          *sdata;
16902     int             i;
16903 
16904 
16905     npoints = cpl_image_get_size_x(image);
16906 
16907     if (2 * hw + 1 > npoints)
16908         return NULL;
16909 
16910     x       = cpl_vector_new(npoints);
16911     y       = cpl_vector_new(npoints);
16912     xdata   = cpl_vector_get_data(x);
16913     ydata   = cpl_vector_get_data(y);
16914 
16915     smoothed = cpl_image_duplicate(image);
16916     sdata = cpl_image_get_data_float(smoothed);
16917 
16918     for (i = 0; i < npoints; i++) {
16919         xdata[i] = i;
16920         ydata[i] = sdata[i];
16921     }
16922 
16923     ysmooth = cpl_vector_filter_median_create(y, hw);
16924     cpl_vector_delete(y);
16925 
16926     poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
16927     cpl_vector_delete(x);
16928     cpl_vector_delete(ysmooth);
16929 
16930     if (poly) {
16931         for (i = 0; i < npoints; i++)
16932             sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
16933     
16934         cpl_polynomial_delete(poly);
16935     }
16936     else {
16937         cpl_image_delete(smoothed);
16938         return NULL;
16939     }
16940 
16941     return smoothed;
16942 }
16943 
16944 #undef cleanup
16945 #define cleanup                       \
16946 do {                                  \
16947     cpl_image_delete(spectrum);       \
16948     cpl_image_delete(flux);           \
16949     cpl_image_delete(efficiency);     \
16950     cpl_image_delete(smo_efficiency); \
16951     cpl_image_delete(extinction);     \
16952     cpl_image_delete(response);       \
16953     cpl_image_delete(smo_response);   \
16954     cpl_image_delete(physical);       \
16955 } while (0)
16956 
16980 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 
16981                                  double dispersion, double gain,
16982                                  double exptime, cpl_table *ext_table,
16983                                  double airmass, cpl_table *flux_table,
16984                                  int order)
16985 {
16986 
16987     cpl_image *spectrum       = NULL; // Extracted standard star spectrum
16988     float     *data;
16989     cpl_image *extinction     = NULL; // Extinction binned as "spectrum"
16990     float     *ext_data;
16991     cpl_image *flux           = NULL; // Standard star flux binned as "spectrum"
16992     float     *flux_data;
16993     cpl_image *physical       = NULL; // Physical units of above
16994     float     *phys_data;
16995     cpl_image *efficiency     = NULL; // Raw efficiency curve
16996     float     *eff_data;
16997     cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
16998     float     *smo_eff_data;
16999     cpl_image *response       = NULL; // Raw response curve
17000     float     *res_data;
17001     cpl_image *smo_response   = NULL; // Smoothed response curve
17002     float     *smo_res_data;
17003     cpl_image *image;
17004     cpl_image *smo_image;
17005     cpl_table *table;
17006     float      lambda;
17007     int        nx, ny;
17008     int        ext_count, ext_pos;
17009     int        eff_count, eff_pos;
17010     int        flux_count, flux_pos;
17011     int        start, end;
17012     int        i;
17013 
17014 
17015     if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17016         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17017         return NULL;
17018     }
17019 
17020     if (!cpl_table_has_column(ext_table, "WAVE")) {
17021         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17022                               "Column WAVE in atmospheric extinction table");
17023         return NULL;
17024     }
17025 
17026     if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17027         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17028                         "Column EXTINCTION in atmospheric extinction table");
17029         return NULL;
17030     }
17031 
17032     if (!cpl_table_has_column(flux_table, "WAVE")) {
17033         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17034                               "Column WAVE in standard star flux table");
17035         return NULL;
17036     }
17037 
17038     if (!cpl_table_has_column(flux_table, "FLUX")) {
17039         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17040                               "Column FLUX in standard star flux table");
17041         return NULL;
17042     }
17043 
17044     if (gain < 0.1) {
17045         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17046                               "Invalid gain factor (%.2f)", gain);
17047         return NULL;
17048     }
17049 
17050     if (exptime < 0.001) {
17051         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17052                               "Invalid exposure time (%.2f)", exptime);
17053         return NULL;
17054     }
17055 
17056     if (dispersion < 0.001) {
17057         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17058                               "Invalid dispersion (%.2f)", dispersion);
17059         return NULL;
17060     }
17061 
17062     if (order < 2) {
17063         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17064                               "Order of the polynomial fitting the "
17065                               "instrument response must be at least 2");
17066         return NULL;
17067     }
17068 
17069     nx = cpl_image_get_size_x(spectra);
17070     ny = cpl_image_get_size_y(spectra);
17071 
17072 
17073     /*
17074      * Find brightest spectrum and duplicate it.
17075      */
17076 
17077     if (ny == 1) {
17078         spectrum = cpl_image_duplicate(spectra);
17079     }
17080     else {
17081         cpl_size        x, y;
17082         cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17083 
17084         cpl_image_get_maxpos(brights, &x, &y);
17085         cpl_image_delete(brights);
17086         spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17087     }
17088 
17089 
17090     /*
17091      * Convert standard star spectrum in electrons per second per Angstrom.
17092      */
17093 
17094     cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17095 
17096 
17097     /*
17098      * Map the atmospheric extinction factors to the same lambda sampling
17099      * of the extracted spectrum.
17100      */
17101 
17102     extinction = cpl_image_duplicate(spectrum);
17103     map_table(extinction, startwave + dispersion/2, dispersion, 
17104               ext_table, "WAVE", "EXTINCTION");
17105 
17106 
17107     /*
17108      * Convert from magnitudes to actual flux loss.
17109      */
17110 
17111     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17112     cpl_image_exponential(extinction, 10.);
17113 
17114 
17115     /*
17116      * Correct the scientific spectrum to airmass 0
17117      */
17118 
17119     cpl_image_multiply(spectrum, extinction);
17120 
17121 
17122     /*
17123      * Find in what pixel interval (start at "ext_pos", for "ext_count" 
17124      * pixels) the atmospheric extinction is available.
17125      */
17126     
17127     ext_data = cpl_image_get_data_float(extinction);
17128 
17129     ext_count = 0;
17130     ext_pos = 0;
17131     for (i = 0; i < nx; i++) {
17132         if (ext_data[i] > 0.0) {
17133             if (ext_count == 0) {
17134                 ext_pos = i;
17135             }
17136             ext_count++;
17137         }
17138         else {
17139             if (ext_count) {
17140                 break;
17141             }
17142         }
17143     }
17144 
17145     cpl_image_delete(extinction); extinction = NULL;
17146 
17147 
17148     /*
17149      * Map the standard star catalog flux to the same lambda sampling
17150      * of the extracted spectrum.
17151      */
17152 
17153     flux = cpl_image_duplicate(spectrum);
17154     map_table(flux, startwave + dispersion/2, dispersion, 
17155               flux_table, "WAVE", "FLUX");
17156 
17157 
17158     /*
17159      * Find in what pixel interval (start at "flux_pos", for "flux_count" 
17160      * pixels) the standard star flux is available.
17161      */
17162     
17163     flux_data = cpl_image_get_data_float(flux);
17164 
17165     flux_count = 0;
17166     flux_pos = 0;
17167     for (i = 0; i < nx; i++) {
17168         if (flux_data[i] > 0.0) {
17169             if (flux_count == 0) {
17170                 flux_pos = i;
17171             }
17172             flux_count++;
17173         }
17174         else {
17175             if (flux_count) {
17176                 break;
17177             }
17178         }
17179     }
17180 
17181 
17182     /*
17183      * Intersection with previous selection
17184      */
17185 
17186     start      = ext_pos > flux_pos ? ext_pos : flux_pos;
17187     end        = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17188                  (ext_pos + ext_count) : (flux_pos + flux_count);
17189     flux_pos   = start;
17190     flux_count = end - start;
17191 
17192 
17193     /*
17194      * Convert the flux to photons (per second per Angstrom).
17195      * std_flux is in units of erg / cm^2 / s / Angstrom. This
17196      * must be multiplied by the efficient area of the telescope,
17197      * 5.18E+5 cm^2, and divided by hv (v = frequency). With 
17198      * hc = 1.98E-8 erg*Angstrom one obtains the following:
17199      */
17200 
17201     physical = cpl_image_duplicate(spectrum);
17202     phys_data = cpl_image_get_data_float(physical);
17203 
17204     for (i = 0; i < nx; i++) {
17205         lambda = startwave + dispersion * (i + 0.5);
17206         phys_data[i] = 0.0026 * lambda * flux_data[i];
17207     }
17208 
17209     efficiency = cpl_image_duplicate(spectrum);
17210     eff_data = cpl_image_get_data_float(efficiency);
17211     data = cpl_image_get_data_float(spectrum);
17212 
17213     for (i = 0; i < nx; i++) {
17214         if (phys_data[i] > 0.0)
17215             eff_data[i] = data[i] / phys_data[i];
17216         else
17217             eff_data[i] = 0.0;
17218     }
17219 
17220     cpl_image_delete(physical); physical = NULL;
17221 
17222 
17223     /*
17224      * Find interval (longer than 300 pixels) where efficiency is 
17225      * greater than 1%
17226      */
17227 
17228     eff_count = 0;
17229     eff_pos = 0;
17230     for (i = 0; i < nx; i++) {
17231         if (eff_data[i] > 0.01) {
17232             if (eff_count == 0) {
17233                 eff_pos = i; 
17234             }
17235             eff_count++;
17236         }
17237         else {
17238             if (eff_count > 300) {
17239                 break;
17240             }
17241         }
17242     }
17243 
17244 
17245     /*
17246      * Intersection with previous selection
17247      */
17248 
17249     start      = eff_pos > flux_pos ? eff_pos : flux_pos;
17250     end        = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17251                  (eff_pos + eff_count) : (flux_pos + flux_count);
17252     eff_pos    = start;
17253     eff_count  = end - start;
17254 
17255     if (eff_count < 1) {
17256         cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17257                               "No overlap between catalog and spectrum");
17258         cleanup;
17259         return NULL;
17260     }
17261 
17262 
17263     /*
17264      * Extract only data to fit, i.e., where the efficiency is available.
17265      */
17266 
17267     image = cpl_image_extract(efficiency, eff_pos + 1, 1, 
17268                               eff_pos + eff_count, 1);
17269 
17270     smo_image = polysmooth(image, order, 50);
17271     cpl_image_delete(image);
17272 
17273     smo_efficiency = cpl_image_duplicate(efficiency);
17274     smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17275     cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17276 
17277     cpl_image_delete(smo_image);
17278 
17279 
17280     /*
17281      * Compute instrument response as the ratio between the catalog
17282      * spectrum and the observed spectrum (converted in physical units).
17283      * The polynomial smoothing, however, is performed on the inverse
17284      * of this ration, for obvious reasons (i.e., no divergence at zero
17285      * efficiency).
17286      */
17287 
17288     response = cpl_image_duplicate(spectrum);
17289     res_data = cpl_image_get_data_float(response);
17290 
17291     for (i = 0; i < nx; i++) {
17292         if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17293             res_data[i] = data[i] / flux_data[i];
17294         else
17295             res_data[i] = 0.0;
17296     }
17297 
17298 
17299     /*
17300      * Extract only data to fit, i.e., where the response is available.
17301      */
17302 
17303     image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17304 
17305     smo_image = polysmooth(image, order, 50);
17306     cpl_image_delete(image);
17307 
17308     smo_response = cpl_image_duplicate(response);
17309     smo_res_data = cpl_image_get_data_float(smo_response);
17310     cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17311 
17312     cpl_image_delete(smo_image);
17313 
17314     for (i = 0; i < nx; i++) {
17315         if (eff_data[i] > 0.01) {
17316             res_data[i] = 1 / res_data[i];
17317             smo_res_data[i] = 1 / smo_res_data[i];
17318         }
17319         else {
17320             res_data[i] = 0.0;
17321             smo_res_data[i] = 0.0;
17322         }
17323     }
17324 
17325 
17326     /*
17327      * Assemble the product spectrophotometric table.
17328      */
17329 
17330     table = cpl_table_new(nx);
17331 
17332     cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17333     cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17334 
17335     for (i = 0; i < nx; i++)
17336         cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17337 
17338     cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17339     cpl_table_set_column_unit(table, "STD_FLUX", 
17340                               "10^(-16) erg/(cm^2 s Angstrom)");
17341     cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17342     cpl_image_delete(flux); flux = NULL;
17343 
17344     cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17345     cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17346     cpl_table_copy_data_float(table, "OBS_FLUX", data);
17347     cpl_image_delete(spectrum); spectrum = NULL;
17348 
17349     cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17350     cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17351     cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17352     cpl_image_delete(efficiency); efficiency = NULL;
17353 
17354     cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17355     cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17356     cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17357     cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17358 
17359     cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17360     cpl_table_set_column_unit(table, "RAW_RESPONSE", 
17361                               "10^(-16) erg/(cm^2 electron)");
17362     cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17363     cpl_image_delete(response); response = NULL;
17364 
17365     cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17366     cpl_table_set_column_unit(table, 
17367                               "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17368     cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17369     cpl_image_delete(smo_response); smo_response = NULL;
17370 
17371     cleanup;
17372 
17373     return table;
17374 }
17375 
17376 static double ksigma_vector(cpl_vector *values, 
17377                             double klow, double khigh, int kiter, int *good)
17378 {
17379     cpl_vector *accepted;
17380     double  mean  = 0.0;
17381     double  sigma = 0.0;
17382     double *data  = cpl_vector_get_data(values);
17383     int     n     = cpl_vector_get_size(values);
17384     int     ngood = n;
17385     int     count = 0;
17386     int     i;
17387 
17388 
17389     /*
17390      * At first iteration the mean is taken as the median, and the
17391      * standard deviation relative to this value is computed.
17392      */
17393 
17394     mean = cpl_vector_get_median(values);
17395 
17396     for (i = 0; i < n; i++) 
17397         sigma += (mean - data[i]) * (mean - data[i]);
17398 
17399     sigma = sqrt(sigma / (n - 1));
17400 
17401     while (kiter) {
17402         count = 0;
17403         for (i = 0; i < ngood; i++) {
17404             if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17405                 data[count] = data[i];
17406                 ++count;
17407             }
17408         }
17409 
17410         if (count == 0) // This cannot happen at first iteration.
17411             break;      // So we can break: we have already computed a mean.
17412 
17413         /*
17414          * The mean must be computed even if no element was rejected
17415          * (count == ngood), because at first iteration median instead 
17416          * of mean was computed.
17417          */
17418 
17419         accepted = cpl_vector_wrap(count, data);
17420         mean = cpl_vector_get_mean(accepted);
17421         if (count > 1)
17422             sigma = cpl_vector_get_stdev(accepted);
17423         cpl_vector_unwrap(accepted);
17424 
17425         if (count == ngood || count == 1)
17426             break;
17427 
17428         ngood = count;
17429         --kiter;
17430     }
17431 
17432     if (good)
17433         *good = ngood;
17434 
17435     return mean;
17436 }
17437 
17438 
17457 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 
17458                             double klow, double khigh, int kiter,
17459                             cpl_image **good)
17460 {
17461     int         ni, nx, ny, npix;
17462     cpl_image  *out_ima;
17463     float      *pout_ima;
17464     float      *good_ima;
17465     cpl_image  *image;
17466     float     **data;
17467     cpl_vector *time_line;
17468     double     *ptime_line;
17469     int         ngood;
17470     int         i, j;
17471 
17472 
17473     ni         = cpl_imagelist_get_size(imlist);
17474 
17475     image      = cpl_imagelist_get(imlist, 0);
17476     nx         = cpl_image_get_size_x(image);
17477     ny         = cpl_image_get_size_y(image);
17478     npix       = nx * ny;
17479     
17480     out_ima    = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17481     pout_ima   = cpl_image_get_data_float(out_ima);
17482 
17483     if (good) {
17484         *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17485         good_ima = cpl_image_get_data_float(*good);
17486     }
17487 
17488     time_line  = cpl_vector_new(ni);
17489     ptime_line = cpl_vector_get_data(time_line);
17490 
17491     data = cpl_calloc(sizeof(float *), ni);
17492     
17493     for (i = 0; i < ni; i++) {
17494         image = cpl_imagelist_get(imlist, i);
17495         data[i] = cpl_image_get_data_float(image);
17496     }
17497 
17498     for (i = 0; i < npix; i++) {
17499         for (j = 0; j < ni; j++) {
17500             ptime_line[j] = data[j][i];
17501         }
17502         pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
17503         if (good) {
17504             good_ima[i] = ngood;
17505         }
17506     }
17507 
17508     cpl_free(data);
17509     cpl_vector_delete(time_line);
17510 
17511     return out_ima;
17512 
17513 }
17514 
17515 
17532 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
17533                                 cpl_table *ext_table, double startwave,
17534                                 double dispersion, double gain,
17535                                 double exptime, double airmass)
17536 {
17537     cpl_image *extinction;
17538     cpl_image *outspectra;
17539     cpl_image *mapresponse;
17540     float     *res_data;
17541     float     *out_data;
17542     float     *ext_data;
17543     int        tlength, xlength, ylength;
17544     int        i, j, k;
17545 
17546 
17547     if (spectra == NULL || ext_table == NULL || response == NULL) {
17548         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17549         return NULL;
17550     }
17551 
17552     res_data = cpl_table_get_data_float(response, "RESPONSE");
17553 
17554     if (res_data == NULL) {
17555         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17556         return NULL;
17557     }
17558 
17559     tlength = cpl_table_get_nrow(response);
17560     xlength = cpl_image_get_size_x(spectra);
17561     ylength = cpl_image_get_size_y(spectra);
17562 
17563     if (xlength != tlength) {
17564         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17565         map_table(mapresponse, startwave + dispersion/2, dispersion,
17566                   response, "WAVE", "RESPONSE");
17567         res_data = cpl_image_get_data_float(mapresponse);
17568     }
17569 
17570     /*
17571      * Map the atmospheric extinction factors to the same lambda sampling
17572      * of the extracted spectrum.
17573      */
17574 
17575     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17576     map_table(extinction, startwave + dispersion/2, dispersion,
17577               ext_table, "WAVE", "EXTINCTION");
17578 
17579 
17580     /*
17581      * Convert from magnitudes to actual flux loss.
17582      */
17583 
17584     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17585     cpl_image_exponential(extinction, 10.);
17586 
17587     outspectra = cpl_image_duplicate(spectra);
17588 
17589     ext_data = cpl_image_get_data_float(extinction);
17590     out_data = cpl_image_get_data_float(outspectra);
17591 
17592     for (k = 0, i = 0; i < ylength; i++) {
17593         for (j = 0; j < xlength; j++, k++) {
17594             out_data[k] *= ext_data[j] * res_data[j];
17595         }
17596     }
17597 
17598     cpl_image_delete(extinction);
17599     if (xlength != tlength) {
17600         cpl_image_delete(mapresponse);
17601     }
17602 
17603     cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
17604 
17605     return outspectra;
17606 }
17607 
17608 
17625 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 
17626                                           cpl_image *errors, 
17627                                           cpl_table *response,
17628                                           cpl_table *ext_table, 
17629                                           double startwave,
17630                                           double dispersion, double gain,
17631                                           double exptime, double airmass)
17632 {
17633     cpl_image *extinction;
17634     cpl_image *outerrors;
17635     cpl_image *mapresponse;
17636     cpl_image *maperror;
17637     float     *err_data;
17638     float     *out_data;
17639     float     *ext_data;
17640     float     *res_data;
17641     float     *spe_data;
17642     int        tlength, xlength, ylength;
17643     int        i, j, k;
17644 
17645 
17646     if (errors == NULL || ext_table == NULL || response == NULL) {
17647         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17648         return NULL;
17649     }
17650 
17651     if (!cpl_table_has_column(response, "ERROR")) {
17652         return mos_apply_photometry(errors, response, ext_table, startwave,
17653                                     dispersion, gain, exptime, airmass);
17654     }
17655 
17656     res_data = cpl_table_get_data_float(response, "RESPONSE");
17657 
17658     if (res_data == NULL) {
17659         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17660         return NULL;
17661     }
17662 
17663     err_data = cpl_table_get_data_float(response, "ERROR");
17664 
17665     if (err_data == NULL) {
17666         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17667         return NULL;
17668     }
17669 
17670     tlength = cpl_table_get_nrow(response);
17671     xlength = cpl_image_get_size_x(errors);
17672     ylength = cpl_image_get_size_y(errors);
17673 
17674     if (xlength != tlength) {
17675         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17676         map_table(mapresponse, startwave + dispersion/2, dispersion,
17677                   response, "WAVE", "RESPONSE");
17678         res_data = cpl_image_get_data_float(mapresponse);
17679 
17680         maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17681         map_table(maperror, startwave + dispersion/2, dispersion,
17682                   response, "WAVE", "ERROR");
17683         err_data = cpl_image_get_data_float(maperror);
17684     }
17685 
17686     /*
17687      * Map the atmospheric extinction factors to the same lambda sampling
17688      * of the extracted spectrum.
17689      */
17690 
17691     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17692     map_table(extinction, startwave + dispersion/2, dispersion,
17693               ext_table, "WAVE", "EXTINCTION");
17694 
17695 
17696     /*
17697      * Convert from magnitudes to actual flux loss.
17698      */
17699 
17700     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17701     cpl_image_exponential(extinction, 10.);
17702 
17703     outerrors = cpl_image_duplicate(errors);
17704 
17705     ext_data = cpl_image_get_data_float(extinction);
17706     out_data = cpl_image_get_data_float(outerrors);
17707     spe_data = cpl_image_get_data_float(spectra);
17708 
17709     for (k = 0, i = 0; i < ylength; i++) {
17710         for (j = 0; j < xlength; j++, k++) {
17711             out_data[k] = ext_data[j] * 
17712               sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
17713                    res_data[j] * res_data[j] * out_data[k] * out_data[k]);
17714         }
17715     }
17716 
17717     cpl_image_delete(extinction);
17718     if (xlength != tlength) {
17719         cpl_image_delete(maperror);
17720     }
17721 
17722     cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
17723 
17724     return outerrors;
17725 }
17726 
17727 
17803 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
17804                            cpl_image *u_image, cpl_image *u_error,
17805                            double startwave, double dispersion,
17806                            double band, cpl_table *pol_sta,
17807                            double ra, double dec, char *filter, 
17808                            int *polarisation,
17809                            double *p_offset, double *p_error,
17810                            double *a_offset, double *a_error)
17811 {
17812     cpl_table *standard;
17813     cpl_image *q_noise;
17814     cpl_image *q_signal;
17815     cpl_image *u_noise;
17816     cpl_image *u_signal;
17817     cpl_image *noise;
17818     double    *q_ndata;
17819     double    *q_sdata;
17820     double    *u_ndata;
17821     double    *u_sdata;
17822     double     arctol = 0.5;  /* Arc tolerance in degrees */
17823     double     mindist;
17824     double     cwave;
17825     double     bwave[] = {3650., 4450., 5510., 6580., 8060};
17826     char      *bands = "UBVRI";
17827     char       p_label[] = {' ', 'p', '\0'};
17828     char       dp_label[] = {' ', 'd', 'p', '\0'};
17829     char       a_label[] = {' ', 'a', '\0'};
17830     char       da_label[] = {' ', 'd', 'a', '\0'};
17831     int        nbands = strlen(bands);
17832     int        selected;
17833     int        first, last, count, center;
17834     int        nx;
17835     cpl_size   col, row;
17836     int        i, found, closest;
17837     int        pband;
17838     int        polarised;
17839     double     q_obs;
17840     double     q_err;
17841     double     u_obs;
17842     double     u_err;
17843     double     p_obs;
17844     double     p_err;
17845     double     p_ref;
17846     double     dp_ref;
17847     double     a_obs;
17848     double     a_err;
17849     double     a_ref;
17850     double     da_ref;
17851 
17852 
17853     *filter       = '\0';
17854     *polarisation = 0;
17855     *p_offset     = 0.0;
17856     *p_error      = 0.0;
17857     *a_offset     = 0.0;
17858     *a_error      = 0.0;
17859 
17860     /*
17861      * Select reference standard star
17862      */
17863 
17864     cpl_table_select_all(pol_sta);
17865     cpl_table_and_selected_double(pol_sta, "RA",  CPL_GREATER_THAN, ra-arctol);
17866     cpl_table_and_selected_double(pol_sta, "RA",  CPL_LESS_THAN,    ra+arctol);
17867     cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol);
17868     selected =
17869     cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN,    dec+arctol);
17870 
17871     if (selected == 0) {
17872         cpl_msg_warning(cpl_func, "No standard star found in FOV");
17873         return 1;
17874     }
17875 
17876     if (selected > 1) {
17877         cpl_msg_warning(cpl_func, 
17878                         "Ambiguity: %d standard stars found in FOV", selected);
17879         return 1;
17880     }
17881 
17882     standard = cpl_table_extract_selected(pol_sta);
17883 
17884     cpl_msg_info(cpl_func, "Standard star: %s", 
17885                  cpl_table_get_string(standard, "name", 0));
17886 
17887     /*
17888      * Check whether the star is polarised or not
17889      */
17890 
17891     polarised = cpl_table_get_int(standard,  "polarised", 0, NULL);
17892 
17893     cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
17894                  polarised ? " " : " not ");
17895 
17896 
17897     /*
17898      * Determine the image row with the smallest median noise: this 
17899      * row is assumed to refer to the standard star.
17900      * (note: the higher the S/N ratio of the original spectra, the 
17901      * smaller the noise of the Stokes parameters Q and U).
17902      */
17903 
17904     nx = cpl_image_get_size_x(q_error);
17905 
17906     noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
17907     cpl_image_get_minpos(noise, &col, &row);
17908 
17909     cpl_image_delete(noise);
17910 
17911     if (col != 1) {
17912         cpl_table_delete(standard);
17913         cpl_msg_error(cpl_func, 
17914                       "Assertion failure!!! col = %d (it should be 1)", col);
17915         return 1;
17916     }
17917 
17918     q_signal = cpl_image_extract(q_image, 1, row, nx, row);
17919     q_noise  = cpl_image_extract(q_error, 1, row, nx, row);
17920     u_signal = cpl_image_extract(u_image, 1, row, nx, row);
17921     u_noise  = cpl_image_extract(u_error, 1, row, nx, row);
17922 
17923     q_sdata = cpl_image_get_data_double(q_signal);
17924     q_ndata = cpl_image_get_data_double(q_noise);
17925     u_sdata = cpl_image_get_data_double(u_signal);
17926     u_ndata = cpl_image_get_data_double(u_noise);
17927 
17928 
17929     /*
17930      * Determine valid interval in input images (where error is positive).
17931      */
17932 
17933     first = -1;
17934     last = nx = cpl_image_get_size_x(q_signal);
17935     for (i = 0; i < nx; i++) {
17936         if (first < 0) {
17937             if (q_ndata[i] > 0.0) {
17938                 first = i;
17939             }
17940         }
17941         else {
17942             if (q_ndata[i] <= 0.0) {
17943                 last = i - 1;
17944                 break;
17945             }
17946         }
17947     }
17948 
17949     count = last - first + 1;
17950 
17951     if (first < 0 || count < band) {
17952         cpl_table_delete(standard);
17953         cpl_image_delete(q_signal);
17954         cpl_image_delete(q_noise);
17955         cpl_image_delete(u_signal);
17956         cpl_image_delete(u_noise);
17957         cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
17958         return 1;
17959     }
17960 
17961     center = (first + last) / 2;              // Center of valid spectrum
17962     cwave = startwave + dispersion * center;  // Corresponding wavelength
17963 
17964 
17965     /*
17966      * Find the band UBVRI closest to the central wavelength.
17967      */
17968 
17969     found = 0;
17970     for (i = 0; i < nbands; i++) {
17971         p_label[0] = bands[i];
17972         if (cpl_table_is_valid(standard, p_label, 0)) {
17973             if (found == 0) {
17974                 found = 1;
17975                 mindist = fabs(bwave[i] - cwave);
17976                 closest = i;
17977             }
17978             else if (mindist > fabs(bwave[i] - cwave)) {
17979                 mindist = fabs(bwave[i] - cwave);
17980                 closest = i;
17981             }
17982         }
17983     }
17984 
17985     if (!found) {
17986         cpl_table_delete(standard);
17987         cpl_image_delete(q_signal);
17988         cpl_image_delete(q_noise);
17989         cpl_image_delete(u_signal);
17990         cpl_image_delete(u_noise);
17991         cpl_msg_warning(cpl_func, "No reference value available");
17992         return 1;
17993     }
17994 
17995     center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
17996     cwave  =  bwave[closest];                           // Wavelength of band
17997 
17998 
17999     /*
18000      * Check that the integration interval is entirely contained
18001      * in the valid interval, or give it up.
18002      */
18003 
18004     pband = floor(band / dispersion);  // Band width in pixels
18005 
18006     if (center - pband/2 < first || center + pband/2 > last) {
18007         cpl_table_delete(standard);
18008         cpl_image_delete(q_signal);
18009         cpl_image_delete(q_noise);
18010         cpl_image_delete(u_signal);
18011         cpl_image_delete(u_noise);
18012         cpl_msg_warning(cpl_func, "No reference value available");
18013         return 1;
18014     }
18015 
18016     first = center - pband/2;
18017     last  = center + pband/2;
18018 
18019     /*
18020      * Collect reference values. Note that if angle info is not available,
18021      * angle stuff is set automaticaly to zero.
18022      */
18023 
18024      p_label[0] = bands[closest];
18025     dp_label[0] = bands[closest];
18026      a_label[0] = bands[closest];
18027     da_label[0] = bands[closest];
18028 
18029      p_ref = cpl_table_get(standard,  p_label, 0, NULL);
18030     dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18031      a_ref = cpl_table_get(standard,  a_label, 0, NULL);
18032     da_ref = cpl_table_get(standard, da_label, 0, NULL);
18033 
18034     cpl_msg_info(cpl_func, 
18035                  "The expected polarisation is %.2f +- %.2f %%", 
18036                  p_ref, dp_ref);
18037 
18038     if (polarised) {
18039         cpl_msg_info(cpl_func, 
18040                      "The expected polarisation angle is %.2f +- %.2f degrees", 
18041                      a_ref, da_ref);
18042     }
18043 
18044     /*
18045      * Find median signal and median error.
18046      */
18047 
18048     q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18049     q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18050     u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18051     u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18052 
18053     /*
18054      * Measured linear polarisation and its error
18055      */
18056 
18057     p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18058     p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18059 
18060     /*
18061      * Measured polarisation angle
18062      */
18063 
18064     a_obs = 0.0;
18065     if (polarised) {
18066         if (fabs(q_obs) < 0.00001) {
18067             if (u_obs > 0.0) {
18068                 a_obs = 45.0;
18069             }
18070             else {
18071                 a_obs = 135.0;
18072             }
18073         }
18074         else {
18075             a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18076             if (q_obs > 0.0) {
18077                 if (u_obs < 0.0) {
18078                     a_obs += 180.;
18079                 }
18080             }
18081             else {
18082                 a_obs += 90.;
18083             }
18084         }
18085     }
18086 
18087     /*
18088      * Error on polarisation angle
18089      */
18090 
18091     a_err = 0.0;
18092     if (polarised) {
18093         a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18094               / (p_obs * p_obs) 
18095               * 90 / CPL_MATH_PI;
18096     }
18097 
18098     p_obs *= 100;
18099     p_err *= 100;
18100     cpl_msg_info(cpl_func, 
18101                  "The measured polarisation is %.2f +- %.2f %%", 
18102                  p_obs, p_err);
18103 
18104     if (polarised) {
18105         cpl_msg_info(cpl_func, 
18106                      "The measured polarisation angle is %.2f +- %.2f degrees", 
18107                      a_obs, a_err);
18108     }
18109 
18110     *filter       = bands[closest];
18111     *polarisation = polarised;
18112 
18113     if (polarised) {
18114         *p_offset = (p_obs - p_ref) / p_ref;
18115         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18116     }
18117     else {
18118         *p_offset = p_obs - p_ref;
18119         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref);
18120     }
18121 
18122     *a_offset     = a_obs - a_ref;
18123     *a_error      = sqrt(a_err*a_err + da_ref*da_ref);
18124 
18125     return 0;
18126 
18127 }
18128 
18129 
18161 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18162 {
18163     cpl_array *offsets;
18164     int        noffset;
18165     int        nslits = cpl_table_get_nrow(reference);
18166     int       *nref;
18167     int       *nobj;
18168     int        corr, maxcorr;
18169     int        shift, best_shift;
18170     int        i, j, k;
18171 
18172     cpl_error_code status = CPL_ERROR_NONE;
18173 
18174 
18175     *offset = 0.0;
18176 
18177     if (nslits != cpl_table_get_nrow(objects))
18178         return CPL_ERROR_INCOMPATIBLE_INPUT;
18179 
18180     nref = fors_get_nobjs_perslit(reference);
18181     nobj = fors_get_nobjs_perslit(objects);
18182 
18183     noffset = 0;
18184     for (i = 0; i < nslits; i++)
18185         noffset += nobj[i];
18186 
18187     if (noffset == 0) {
18188         cpl_free(nref);
18189         cpl_free(nobj);
18190         return CPL_ERROR_DATA_NOT_FOUND;
18191     }
18192 
18193     noffset = 0;
18194     for (i = 0; i < nslits; i++)
18195         noffset += nref[i];
18196 
18197     if (noffset == 0) {
18198         cpl_free(nref);
18199         cpl_free(nobj);
18200         return CPL_ERROR_DATA_NOT_FOUND;
18201     }
18202 
18203     offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18204 
18205     noffset = 0;    // The real number of offsets found will be counted.
18206 
18207     for (i = 0; i < nslits; i++) {
18208         if (nref[i] > 0 && nobj[i] > 0) {
18209             double shift;
18210             int    length  = cpl_table_get_int(objects, "length", i, NULL);
18211             double ytop    = cpl_table_get_double(objects, "xtop", i, NULL);
18212             double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18213             int   *aref    = cpl_calloc(length, sizeof(int));
18214             int   *aobj    = cpl_calloc(length, sizeof(int));
18215             float *pref    = cpl_calloc(nref[i], sizeof(float));
18216             float *pobj    = cpl_calloc(nobj[i], sizeof(float));
18217             
18218             for (j = 0; j < nref[i]; j++) {
18219                 pref[j] = fors_get_object_position(reference, i, j + 1);
18220                 aref[(int)pref[j]] = 1;
18221             }
18222 
18223             for (j = 0; j < nobj[i]; j++) {
18224                 pobj[j] = fors_get_object_position(objects, i, j + 1);
18225                 aobj[(int)pobj[j]] = 1;
18226             }
18227 
18228             /*
18229              * Do not consider objects at border
18230              */
18231 
18232             aref[0] = 0;
18233             aref[length - 1] = 0;
18234             aobj[0] = 0;
18235             aobj[length - 1] = 0;
18236 
18237 //for (j = 0; j < nref[i]; j++)
18238 //printf("references: %f, ", pref[j]);
18239 //printf("\n");
18240 //for (j = 0; j < nref[i]; j++)
18241 //printf("objects   : %f, ", pobj[j]);
18242 //printf("\n");
18243 //for (j = 0; j < length; j++)
18244 //printf("%d", aref[j]);
18245 //printf("\n");
18246 //for (j = 0; j < length; j++)
18247 //printf("%d", aobj[j]);
18248 //printf("\n");
18249 
18250             /*
18251              * Object matching by correlation
18252              */
18253 
18254             maxcorr = 0;
18255             best_shift = length;
18256 
18257             for (shift = length/2, j = 0; j <= length; shift--, j++) {
18258                 int rstart, ostart, count;
18259 
18260                 if (shift > 0) {
18261                    rstart = shift;
18262                    ostart = 0;
18263                    count  = length - shift;
18264                 }
18265                 else {
18266                    rstart = 0;
18267                    ostart = -shift;
18268                    count  = length + shift;
18269                 }
18270 
18271                 corr = 0;
18272                 for (k = 0; k < count; k++) {
18273                     corr += aref[rstart + k] * aobj[ostart + k];
18274                 }
18275 
18276                 if (maxcorr < corr) {
18277                     maxcorr = corr;
18278                     best_shift = shift;
18279                 }
18280             }
18281 
18282             if (best_shift == length) { // No shift found
18283 //printf("%d: No shift found\n", i);
18284                 cpl_free(aref);
18285                 cpl_free(aobj);
18286                 cpl_free(pref);
18287                 cpl_free(pobj);
18288                 continue;
18289             }
18290 //printf("%d: Integer shift found = %d\n", i, best_shift);
18291 
18292             for (j = 0; j < nref[i]; j++) {
18293                 for (k = 0; k < nobj[i]; k++) {
18294                     if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18295                        double ccd_offset = (pref[j] - pobj[k]) 
18296                                          * (ytop - ybottom)
18297                                          / length;
18298 
18299 //printf("%d: Match found: %f\n", i, ccd_offset);
18300                        /* 
18301                         * The matching object is found, store the
18302                         * corresponding offset
18303                         */
18304 
18305                        cpl_array_set(offsets, noffset, ccd_offset);
18306                        noffset++;
18307                        break;
18308                     }
18309                 }
18310             }
18311 
18312             cpl_free(aref);
18313             cpl_free(aobj);
18314             cpl_free(pref);
18315             cpl_free(pobj);
18316         }
18317 //else
18318 //printf("%d: No object found\n", i);
18319     }
18320 
18321     cpl_free(nref);
18322     cpl_free(nobj);
18323 
18324 //printf("%d offsets found in total\n", noffset);
18325     if (noffset > 0) {
18326         if (noffset % 2) {
18327             *offset = cpl_array_get_median(offsets);
18328         }
18329         else {
18330             double *a = cpl_malloc(sizeof(double) * noffset);
18331             for (i = 0; i < noffset; i++) {
18332                 a[i] = cpl_array_get_double(offsets, i, NULL);
18333             }
18334             *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18335                        fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18336             cpl_free(a);
18337         }
18338     }
18339     else
18340         status = CPL_ERROR_DATA_NOT_FOUND;
18341 //printf("Median offset: %f\n", *offset);
18342 
18343     cpl_array_delete(offsets);
18344 
18345     return status;
18346 
18347 }
18348 
18349 
18361 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18362 {
18363     cpl_image *source;
18364     int        nx = cpl_image_get_size_x(image);
18365     int        ny = cpl_image_get_size_y(image);
18366     float     *idata;
18367     float     *sdata;
18368     int        i, j, pos;
18369     double     xpos, ypos, xfrac, yfrac;
18370     int        xint, yint;
18371 
18372 
18373     if (fabs(dx) >= nx || fabs(dy) >= ny)
18374         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18375 
18376     source = cpl_image_duplicate(image);
18377     idata = cpl_image_get_data_float(image);
18378     sdata = cpl_image_get_data_float(source);
18379 
18380     /*
18381      * Shift in y
18382      */
18383 
18384     yfrac = - dy - floor(- dy);
18385     xfrac = - dx - floor(- dx);
18386 
18387     for (pos = 0, j = 0; j < ny; j++) {
18388         ypos = j - dy;
18389         yint = floor(ypos);
18390         for (i = 0; i < nx; i++) {
18391             xpos = i - dx;
18392             xint = floor(xpos);
18393             if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18394                 idata[pos] = 0.0;
18395             }
18396             else {
18397                 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18398                            + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18399                            + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
18400                            + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
18401             }
18402             pos++;
18403         }
18404     }
18405 
18406     cpl_image_delete(source);
18407 
18408     return CPL_ERROR_NONE;
18409 }
18410 
18422 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
18423 {
18424 #ifdef CPL_SIZE_FORMAT
18425     cpl_size row;
18426 #else
18427     int row;
18428 #endif
18429 
18430     cpl_table_duplicate_column(slits, "x", slits, "xtop");
18431     cpl_table_add_columns(slits, "x", "xbottom");
18432     cpl_table_divide_scalar(slits, "x", 2);         // Mean x position
18433     cpl_table_subtract_scalar(slits, "x", nx/2);    // Relative to CCD center
18434     cpl_table_multiply_columns(slits, "x", "x");    // Squared
18435 
18436     cpl_table_duplicate_column(slits, "y", slits, "ytop");
18437     cpl_table_add_columns(slits, "y", "ybottom");
18438     cpl_table_divide_scalar(slits, "y", 2);         // Mean y position
18439     cpl_table_subtract_scalar(slits, "y", ny/2);    // Relative to CCD center
18440     cpl_table_multiply_columns(slits, "y", "y");    // Squared
18441 
18442     cpl_table_add_columns(slits, "x", "y");         // Distance from center
18443     cpl_table_get_column_minpos(slits, "x", &row);  // Min distance from center
18444 
18445     cpl_table_erase_column(slits, "x");
18446     cpl_table_erase_column(slits, "y");
18447 
18448     return row;
18449 }
18450 
18470 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 
18471                      double xwidth, double ywidth,
18472                      int dx, double gain, double *o_flux, double *o_err)
18473 {
18474     int    nx      = cpl_image_get_size_x(image);
18475     int    ny      = cpl_image_get_size_y(image);
18476     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18477     int    ytop    = (int)cpl_table_get(slits, "ytop", slit, NULL);
18478     int    ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
18479     int    dy      = ytop - ybottom;
18480     int    xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
18481                             cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
18482     int    xleft   = xcenter - dx;
18483     int    xright  = xcenter + dx + 1;
18484     double area    = xwidth * ywidth; // squared mm
18485     int    npix    = (2*dx + 1) * dy;
18486     int    count   = 0;
18487     float *data    = cpl_image_get_data_float(image);
18488     double flux    = 0.0;
18489     double error   = 0.0;
18490     int    satur   = 60000;
18491     int    x, y;
18492 
18493 
18494     if (cpl_table_has_column(slits, "ywidth")) {
18495         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18496                 * cpl_table_get(slits, "ywidth", slit, NULL);
18497     }
18498 
18499     *o_flux = 0.0;
18500     *o_err = 0.0;
18501 
18502     if (xleft < 0)
18503         xleft = 0;
18504 
18505     if (xleft > nx)
18506         xleft = nx;
18507 
18508     if (xright < 0)
18509         xright = 0;
18510 
18511     if (xright > nx)
18512         xright = nx;
18513 
18514     if (ytop < 0)
18515         ytop = 0;
18516 
18517     if (ytop > ny)
18518         ytop = ny;
18519 
18520     if (ybottom < 0)
18521         ybottom = 0;
18522 
18523     if (ybottom > ny)
18524         ybottom = ny;
18525 
18526     count = (xright - xleft) * (ytop - ybottom);
18527 
18528     if (count == 0)
18529         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18530 
18531     count = 0;
18532 
18533     for (y = ybottom; y < ytop; y++) {
18534         for (x = xleft; x < xright; x++) {
18535             double value = data[x + y * nx];
18536             if (value < satur) {
18537                 flux += value;
18538                 count++;
18539             }
18540         }
18541     }
18542 
18543     if (count == 0)
18544         return CPL_ERROR_DIVISION_BY_ZERO;
18545 
18546     error = sqrt(flux/gain);
18547 
18548     /*
18549      * Flux correction for lost pixels
18550      */
18551 
18552     flux *= (float)npix / count;
18553     error *= (float)npix / count;
18554 
18555     flux /= area;
18556     error /= area;
18557 
18558     *o_flux = flux;
18559     *o_err = error;
18560 
18561     return CPL_ERROR_NONE;
18562 }
18563 
18564 
18587 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
18588                                        double xwidth, double ywidth,
18589                                        double lambda, double startwave, 
18590                                        double dispersion, int dx, double gain, 
18591                                        double *o_flux, double *o_err)
18592 {
18593     int    nx      = cpl_image_get_size_x(image);
18594     int    ny      = cpl_image_get_size_y(image);
18595     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18596     int    dy      = (int)cpl_table_get(slits, "length", slit, NULL);
18597     int    ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
18598     int    ytop    = ybottom + dy;
18599     int    xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
18600     int    xleft   = xcenter - dx;
18601     int    xright  = xcenter + dx + 1;
18602     double area    = xwidth * ywidth;
18603     int    npix    = (2*dx + 1) * dy;
18604     int    count   = 0;
18605     float *data    = cpl_image_get_data_float(image);
18606     double flux    = 0.0;
18607     double error   = 0.0;
18608     int    satur   = 60000;
18609     int    x, y;
18610 
18611 
18612     if (cpl_table_has_column(slits, "ywidth")) {
18613         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18614                 * cpl_table_get(slits, "ywidth", slit, NULL);
18615     }
18616 
18617     *o_flux = 0.0;
18618     *o_err = 0.0;
18619 
18620     if (xleft < 0)
18621         xleft = 0;
18622 
18623     if (xleft > nx)
18624         xleft = nx;
18625 
18626     if (xright < 0)
18627         xright = 0;
18628 
18629     if (xright > nx)
18630         xright = nx;
18631 
18632     if (ytop < 0)
18633         ytop = 0;
18634 
18635     if (ytop > ny)
18636         ytop = ny;
18637 
18638     if (ybottom < 0)
18639         ybottom = 0;
18640 
18641     if (ybottom > ny)
18642         ybottom = ny;
18643 
18644     count = (xright - xleft) * (ytop - ybottom);
18645 
18646     if (count == 0)
18647         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18648 
18649     count = 0;
18650 
18651     for (y = ybottom; y < ytop; y++) {
18652         for (x = xleft; x < xright; x++) {
18653             double value = data[x + y * nx];
18654             if (value < satur) {
18655                 flux += value;
18656                 count++;
18657             }
18658         }
18659     }
18660 
18661     if (count == 0)
18662         return CPL_ERROR_DIVISION_BY_ZERO;
18663 
18664     error = sqrt(flux/gain);
18665 
18666     /*
18667      * Flux correction for lost pixels
18668      */
18669 
18670     flux *= (float)npix / count;
18671     error *= (float)npix / count;
18672     
18673     flux /= area;  
18674     error /= area; 
18675     
18676     *o_flux = flux;
18677     *o_err = error;
18678 
18679     return CPL_ERROR_NONE;
18680 
18681 }
18682 
18683 
18697 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 
18698                        char *label, double *mvalue)
18699 {
18700     int        position   = cpl_table_get_int(slits, "position", slit, NULL);
18701     int        length     = cpl_table_get_int(slits, "length", slit, NULL);
18702     cpl_table *tmp        = cpl_table_extract(table, position, length);
18703 
18704     *mvalue = cpl_table_get_column_median(tmp, label);
18705     cpl_table_delete(tmp);
18706 
18707     if (cpl_error_get_code() != CPL_ERROR_NONE)
18708         return 1;
18709 
18710     return 0;
18711 }
18712 
18713 
18725 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
18726 {
18727       cpl_mask  *kernel   = cpl_mask_new(nx, ny);
18728       cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
18729                                           cpl_image_get_size_y(image),
18730                                           cpl_image_get_type(image));
18731 
18732       cpl_mask_not(kernel);
18733       cpl_image_filter_mask(filtered, image, kernel,
18734                             CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
18735       cpl_mask_delete(kernel);
18736 
18737       return filtered;
18738 }