FORS Pipeline Reference Manual 4.9.20
moses.c
00001 /* $Id: moses.c,v 1.100 2013/02/28 15:11:12 cgarcia Exp $
00002  *
00003  * This file is part of the MOSES library
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013/02/28 15:11:12 $
00024  * $Revision: 1.100 $
00025  * $Name: fors-4_9_20 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <math.h>
00037 #include <time.h>
00038 
00039 #include <fors_utils.h>
00040 #include <moses.h>
00041 
00042 /* Prototypes */
00043 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row);
00044 
00045 
00046 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
00047  * even if cpl_tool.h is not public. It should be removed as soon as 
00048  * an image median filtering with generic kernel will be implemented
00049  * in the CPL, or as soon as this module will be moved into the CPL. */
00050 /* FIXME: This has to be removed!! */
00051 
00052 float cpl_tools_get_median_float(float *, cpl_size);
00053 
00054 #define MAX_COLNAME      (80)
00055 #define STRETCH_FACTOR   (1.20)
00056 
00057 // Related to mos_identify_peaks(), used in multiplex mode
00058 
00059 static int mos_multiplex   = -1;
00060 static int mos_region_size = 800;
00061 
00062 static double default_lines_hi[] = {   /* Default sky line catalog */
00063                     5577.338,          /* for high res data        */
00064                     5889.953,
00065                     5895.923,
00066                     5915.301,
00067                     5932.862,
00068                     5953.420,
00069                     6257.961,
00070                     6287.434,
00071                     6300.304,
00072                     6306.869,
00073                     6363.780,
00074                     6498.729,
00075                     6533.044,
00076                     6553.617,
00077                     6841.945,
00078                     6863.955,
00079                     6870.994,
00080                     6889.288,
00081                     6900.833,
00082                     6912.623,
00083                     6923.220,
00084                     6939.521,
00085                     6969.930,
00086                     7003.858,
00087                     7244.907,
00088                     7276.405,
00089                     7284.439,
00090                     7316.282,
00091                     7329.148,
00092                     7340.885,
00093                     7358.659,
00094                     7571.746,
00095                     7750.640,
00096                     7759.996,
00097                     7794.112,
00098                     7808.467,
00099                     7821.503,
00100                     7841.266,
00101                     7913.708,
00102                     7949.204,
00103                     7964.650,
00104                     7993.332,
00105                     8014.059,
00106                     8310.719,
00107                     8344.602,
00108                     8382.392,
00109                     8399.170,
00110                     8415.231,
00111                     8430.174,
00112                     8452.250,
00113                     8493.389,
00114                     8791.186,
00115                     8827.096,
00116                     8885.850,
00117                     8903.114,
00118                     8943.395,
00119                     8988.366
00120                     };
00121 
00122 static double default_lines_lo[] = {   /* Default sky line catalog */
00123                     5577.338,          /* for low res data         */
00124                     6300.304,
00125                     6863.955,
00126                     7571.746,
00127                     7964.650,
00128                     7993.332
00129                     };
00130 
00131 
00141 /*
00142  * The following macros and function for finding the k-th smallest
00143  * value on a float array will be accessible from cpl_tools once
00144  * this module will be moved into the CPL.
00145  */
00146 
00147 /****
00148 
00149 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00150 
00151 static float kthSmallest(float a[], int n, int k)
00152 {
00153   register int i,j,l,m;
00154   register float x;
00155 
00156   l = 0;
00157   m = n-1;
00158   while (l < m) {
00159     x = a[k];
00160     i = l;
00161     j = m;
00162     do {
00163       while (a[i] < x) {
00164         i++;
00165       }
00166       while (x < a[j]) {
00167         j--;
00168       }
00169       if (i <= j) {
00170         PIX_SWAP(a[i],a[j]);
00171         i++;
00172         j--;
00173       }
00174     } while (i <= j);
00175 
00176     if (j < k) {
00177       l = i;
00178     }
00179     if (k < i) {
00180       m = j;
00181     }
00182 
00183   }
00184   return(a[k]);
00185 }
00186 
00187 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00188 
00189 ****/
00190 
00191 /* 
00192  * Return random number with gaussian distribution (mean = 0, variance = 1)
00193  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00194  * indicating whether to "seed" or not the sequence of generated random 
00195  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00196  * and at further calls the input argument is ignored. This function
00197  * generates two random numbers at each call, returning the first one
00198  * at odd calls, and the second one at even calls.
00199  */
00200 
00201 static void mos_seed(void)
00202 {
00203     srand((unsigned int)time((time_t *)0));
00204 }
00205 
00206 static double mos_randg(int seme)
00207 {
00208     static int doit = 1;
00209     static int gotit = 1;
00210     double x1, x2, w, y1;
00211     static double y2;
00212 
00213     if (gotit && seme) {
00214         mos_seed();
00215         gotit = 0;
00216     }
00217 
00218     if (doit) {
00219         doit = 0;
00220         do {
00221             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00222             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00223             w = x1 * x1 + x2 * x2;
00224         } while (w >= 1.0 || w == 0.0);
00225     
00226         w = sqrt( (-2.0 * log(w)) / w);
00227     
00228         y1 = x1 * w;
00229         y2 = x2 * w;
00230         return y1;
00231     }
00232 
00233     doit = 1;
00234     return y2;
00235 }
00236 
00237 /* 
00238  * This function contained a dependency on the VIMOS library
00239  * (function medianPixelvalue()): it should be removed as soon as an 
00240  * image median filtering with generic kernel will be implemented
00241  * in the CPL. Currently it has been solved by a direct call to
00242  * a cpl_tool function.
00243  */
00244 
00245 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00246                                             int filtsizey, int refrow,
00247                                             int above, int below, int step)
00248 {
00249 
00250   const char *func = "mos_image_general_median_filter";
00251 
00252   cpl_image  *filt_img = NULL;
00253   int         col, row;
00254   float      *buf = NULL;
00255   float      *data;
00256   float      *fdata;
00257   int         upright_y, loleft_y;
00258   int         j;
00259   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00260   int         f2y;
00261   int         nx = cpl_image_get_size_x(ima_in);
00262   int         ny = cpl_image_get_size_y(ima_in);
00263   int         firstRow;
00264 
00265 
00266   if (yIsEven) filtsizey++;
00267 
00268   if (ny <= filtsizey) {
00269     cpl_msg_error(func, 
00270                   "Median filter size: %d, image size: %d", filtsizey, ny);
00271     return NULL;
00272   }
00273 
00274   f2y = filtsizey / 2;
00275 
00276   filt_img = cpl_image_duplicate(ima_in);
00277   buf = cpl_malloc(filtsizey * sizeof(float));
00278   data = cpl_image_get_data(ima_in);
00279   fdata = cpl_image_get_data(filt_img);
00280 
00281   firstRow = refrow - step * (below / step);
00282   if (firstRow < f2y)
00283     firstRow += step;
00284 
00285   for (col = 0; col < nx; col++) {
00286     for (row = firstRow; row < refrow + above; row += step) {
00287       if (row >= ny - f2y)
00288         break;
00289       loleft_y = row - f2y;
00290       upright_y = row + f2y + 1;
00291       for (j = loleft_y; j < upright_y; j++)
00292         buf[j - loleft_y] = data[col + j * nx];
00293 
00294       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00295     }
00296   }
00297 
00298   cpl_free(buf);
00299 
00300   return filt_img;
00301 
00302 }
00303 
00304 
00305 /*
00306  * The following static function is used to find an accurate position
00307  * of a peak within a short interval (however at least 5 pixels long). 
00308  * The basic idea is to find the baricenter of all the pixel values 
00309  * that pass a threshold level between the median value and the maximum
00310  * value within the examined interval (in case such levels are equal,
00311  * the input is considered flat and no position is returned). At least
00312  * minPoints must pass this threshold, or no position is computed. To
00313  * evaluate the significance of the computed baricenter, the variance 
00314  * of the contributing positions (relative to the found baricenter) is 
00315  * also evaluated, and compared with the expected variance for a uniform 
00316  * distribution of positions. If the observed variance is greater than 
00317  * 80% of the variance of the uniform distribution, the found position 
00318  * is rejected.
00319  */
00320 
00321 static int peakPosition(const float *data, int size, float *position,
00322                         int minPoints)
00323 {
00324   int    i;
00325   int    count = 0;
00326   float *copy;
00327   float  max, median, level, pos, variance, uniformVariance;
00328   double sum, weights;
00329 
00330 
00331   if (data == NULL)
00332       return 1;
00333 
00334   if (size < 5)         /* Hardcoded, I know... */
00335       return 1;
00336 
00337 
00338   /*
00339    *  Find median level
00340    */
00341 
00342   copy = (float *) cpl_malloc(size*sizeof(float));
00343   for (i = 0; i < size; i++)
00344       copy[i] = data[i];
00345   median = cpl_tools_get_median_float(copy, size);
00346   cpl_free(copy);
00347 
00348 
00349   /*
00350    *  Find max
00351    */
00352 
00353   max = data[0];
00354   for (i = 1; i < size; i++)
00355       if (data[i] > max)
00356           max = data[i];
00357 
00358 
00359   /*
00360    *  If the max equals the median we have a flat input, therefore
00361    *  no peak is found.
00362    */
00363 
00364   if (max-median < 0.00001)
00365       return 1;
00366 
00367 
00368   /*
00369    *  Discrimination level: only pixels with values above this
00370    *  level are considered in baricenter calculation.
00371    */
00372 
00373   level = (max + median) / 2;
00374 
00375 
00376   /*
00377    *  Of the values above this level compute the baricenter and
00378    *  then the variance of the positions used. Note that the weights
00379    *  are taken as the difference between the pixels values and
00380    *  the median level (supposedly the background).
00381    */
00382 
00383   count = 0;
00384   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00385       if (data[i] > level) {
00386           count++;
00387           weights += (data[i] - median);
00388           sum     += i * (data[i] - median);
00389       }
00390   }
00391 
00392 
00393   /*
00394    *  If too few values are above threshold, refuse the position
00395    *  as insignificant
00396    */
00397 
00398   if (count < minPoints)
00399       return 1;
00400 
00401   pos = sum / weights;
00402   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00403       if (data[i] > level) {
00404           weights++;
00405           sum += (i - pos) * (i - pos);
00406       }
00407   }
00408   variance = sqrt(sum / weights);
00409 
00410 
00411  /*
00412   *  The "uniform variance" is the variance that should be obtained
00413   *  in the case of uniform distribution of the points positions in
00414   *  the selected interval. If the real variance is comparable with
00415   *  this value, the peak is considered not found.
00416   */
00417 
00418   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00419 
00420   if (variance > 0.8 * uniformVariance)
00421       return 1;
00422 
00423   *position = pos + 0.5;
00424 
00425   return 0;
00426 }
00427 
00428 
00429 /*
00430  *  The following static function determines the quantity dx to be
00431  *  added to the position of the highest pixel of a fiber profile,
00432  *  to get the true position of the profile maximum. All is needed
00433  *  is the maximum observed value v2 in the profile, and the observed
00434  *  values v1 and v3 of the previous and the next pixels in the profile.
00435  *  
00436  *  The following ratio is defined:
00437  *  
00438  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00439  *      
00440  *  This is a conventional ratio that wouldn't diverge for any set of
00441  *  pixel values, and that would not depend on the presence of background
00442  *  (with the assumption that the background level is the same for the 
00443  *  three pixels). R has also been chosen in such a way that its value
00444  *  is already quite close to the real dx. It should be noted that the
00445  *  following condition should be fulfilled:
00446  *
00447  *           v1  <= v2   and   v3  <  v2
00448  *  or
00449  *           v1  <  v2   and   v3  <=  v2
00450  *
00451  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00452  *  boundary cases, one has:
00453  *
00454  *           v2 = v1   and   R = dx = -0.5
00455  *           v2 = v3   and   R = dx =  0.5
00456  *
00457  *  Another special case is when the observed pixel values are perfectly
00458  *  symmetrical:
00459  *
00460  *           v1 = v3   and   R = dx =  0.0
00461  *
00462  *  In all the intermediate cases the relation between R and dx depends
00463  *  on the shape of the fiber profile, that has been determined elsewhere.
00464  *  Using the accurate reconstruction of the fiber profile obtained by 
00465  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00466  *  that R differs from dx always less than 0.01 pixels. If the condition
00467  *
00468  *           v1  <= v2   and   v3  <  v2
00469  *  or
00470  *           v1  <  v2   and   v3  <=  v2
00471  *
00472  *  is not fulfilled, then this function returns the value 2.0.
00473  */
00474 
00475 static double values_to_dx(double v1, double v2, double v3)
00476 {
00477 
00478   static double epsilon = 0.00000001;
00479   double        r       = 2.0;
00480 
00481 
00482   if (v1 > v2 || v3 > v2)
00483     return r;
00484 
00485   if (2 * v2 - v1 - v3 < epsilon)
00486     return r;
00487 
00488   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00489 
00490   return r;
00491 
00492 }
00493 
00494 
00495 /*
00496  * The following static function passes a min filter of given box
00497  * size on the data buffer. The box size must be a positive odd integer.
00498  */
00499 
00500 static float *min_filter(float *buffer, int length, int size)
00501 {
00502     float *minf  = cpl_calloc(length, sizeof(float));
00503     float  min;
00504     int    start = size / 2;
00505     int    end   = length - size / 2;
00506     int    i, j;
00507 
00508 
00509     for (i = start; i < end; i++) {
00510         min = buffer[i-start];
00511         for (j = i - start + 1; j <= i + start; j++)
00512             if (min > buffer[j])
00513                 min = buffer[j];
00514         minf[i] = min;
00515     }
00516 
00517     for (i = 0; i < start; i++)
00518         minf[i] = minf[start];
00519 
00520     for (i = end; i < length; i++)
00521         minf[i] = minf[end-1];
00522 
00523     return minf;
00524 }
00525 
00526 
00527 /*
00528  * The following static function passes a max filter of given box
00529  * size on the data buffer. The box size must be a positive odd integer.
00530  */
00531  
00532 static float *max_filter(float *buffer, int length, int size)
00533 {
00534     float *maxf  = cpl_calloc(length, sizeof(float));
00535     float  max;
00536     int    start = size / 2;
00537     int    end   = length - size / 2;
00538     int    i, j;
00539 
00540 
00541     for (i = start; i < end; i++) {
00542         max = buffer[i-start];
00543         for (j = i - start + 1; j <= i + start; j++)
00544             if (max < buffer[j])
00545                 max = buffer[j];
00546         maxf[i] = max;
00547     }
00548 
00549     for (i = 0; i < start; i++)
00550         maxf[i] = maxf[start];
00551 
00552     for (i = end; i < length; i++)
00553         maxf[i] = maxf[end-1];
00554 
00555     return maxf;
00556 }
00557 
00558 
00559 /*
00560  * The following static function passes a running average of given box
00561  * size on the data buffer. The box size must be a positive odd integer.
00562  */
00563  
00564 static float *smo_filter(float *buffer, int length, int size)
00565 {
00566     float *smof  = cpl_calloc(length, sizeof(float));
00567     double sum;
00568     int    start = size / 2;
00569     int    end   = length - size / 2;
00570     int    i, j;
00571 
00572 
00573     for (i = start; i < end; i++) {
00574         sum = 0.0;
00575         for (j = i - start; j <= i + start; j++)
00576             sum += buffer[j];
00577         smof[i] = sum / size;
00578     }
00579 
00580     for (i = 0; i < start; i++)
00581         smof[i] = smof[start];
00582 
00583     for (i = end; i < length; i++)
00584         smof[i] = smof[end-1];
00585 
00586     return smof;
00587 }
00588 
00589 /*
00590  * The following two static functions are used to read and write from the 
00591  * global distortion table the different model components. Conventionally
00592  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00593  * storage for model coefficients, and these functions guarantee that the
00594  * coefficients are read in and written out correctly, independent on their
00595  * physical meaning. The first 6 table rows are a description of the IDS
00596  * coefficients, followed by a row containing only the used reference 
00597  * wavelength. The remaining 3 are a description of the spectral curvature.
00598  * The first row is a description of coefficient c0, the second of coefficient
00599  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00600  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00601  * bivariate polynomialx on x,y mask coordinates. If the input table
00602  * to the write routine is NULL, it is allocated and initialised. Also
00603  * the input polynomial could be NULL, and nothing would be written to 
00604  * the table. If both pointers are NULL the function is basically a
00605  * constructor of the global distortion table.
00606  */
00607 
00608 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row)
00609 {
00610     cpl_polynomial *poly = NULL;
00611     cpl_size        p[2];
00612     cpl_size        degree = 2;
00613     int             null;
00614     double          coeff;
00615 
00616     char   name[MAX_COLNAME];
00617 
00618 
00619     for (p[0] = 0; p[0] <= degree; p[0]++) {
00620         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00621             snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00622             coeff = cpl_table_get_double(global, name, row, &null);
00623             if (null)
00624                 continue;
00625             if (poly == NULL)
00626                 poly = cpl_polynomial_new(2);
00627             cpl_polynomial_set_coeff(poly, p, coeff);
00628         }
00629     }
00630 
00631     return poly;
00632 }
00633 
00634 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00635                                           cpl_polynomial *poly)
00636 {
00637     cpl_table *table;
00638     cpl_size   p[2];
00639     cpl_size   degree = 2;
00640     int        nrow = 10;
00641 
00642     char       name[MAX_COLNAME];
00643 
00644 
00645     if (global) {
00646         table = global;
00647     }
00648     else {
00649         table = cpl_table_new(nrow);
00650         for (p[0] = 0; p[0] <= degree; p[0]++) {
00651             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00652                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00653                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00654             }
00655         }
00656     }
00657 
00658     if (poly) {
00659         for (p[0] = 0; p[0] <= degree; p[0]++) {
00660             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00661                 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]);
00662                 cpl_table_set_double(table, name, row, 
00663                                      cpl_polynomial_get_coeff(poly, p));
00664             }
00665         }
00666     }
00667 
00668     return table;
00669 }
00670 
00671 
00672 /*
00673  * The following static function is performing a robust linear fit
00674  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00675  *
00676  *  ----> y = a + b * x
00677  *
00678  * This function return 0 on success.
00679  */
00680 
00681 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00682 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00683                            double *abdev)
00684 {
00685     cpl_vector *vx;
00686     cpl_vector *vy;
00687     cpl_vector *va;
00688 
00689     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00690     double  sx, sy, sxy, sxx, chisq;
00691     double *arr;
00692     double  aa_ls, bb_ls;
00693     double *x;
00694     double *y;
00695     int     np;
00696     int     iter;
00697     int     max_iterate = 30;
00698     int     i;
00699 
00700 
00701     np = cpl_bivector_get_size(list);
00702     vx = cpl_bivector_get_x(list);
00703     vy = cpl_bivector_get_y(list);
00704     x = cpl_vector_get_data(vx);
00705     y = cpl_vector_get_data(vy);
00706 
00707     sx = sy = sxx = sxy = 0.00;
00708     for (i = 0; i < np; i++) {
00709         sx  += x[i];
00710         sy  += y[i];
00711         sxy += x[i] * y[i];
00712         sxx += x[i] * x[i];
00713     }
00714 
00715     del = np * sxx - sx * sx;
00716     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00717     bb_ls = bb = (np * sxy - sx * sy) / del;
00718 
00719     chisq = 0.00;
00720     for (i = 0; i < np; i++) {
00721         temp = y[i] - (aa+bb*x[i]);
00722         temp *= temp;
00723         chisq += temp;
00724     }
00725 
00726     va = cpl_vector_new(np);
00727     arr = cpl_vector_get_data(va);
00728     sigb = sqrt(chisq/del);
00729     b1 = bb;
00730 
00731     bcomp = b1;
00732     sum = 0.00;
00733     for (i = 0; i < np; i++) {
00734         arr[i] = y[i] - bcomp * x[i];
00735     }
00736     aa = cpl_vector_get_median_const(va);
00737     abdevt = 0.0;
00738     for (i = 0; i < np; i++) {
00739         d = y[i] - (bcomp * x[i] + aa);
00740         abdevt += fabs(d);
00741         if (y[i] != 0.0) 
00742             d /= fabs(y[i]);
00743         if (fabs(d) > 1e-7) 
00744             sum += (d >= 0.0 ? x[i] : -x[i]);
00745     }
00746     f1 = sum;
00747 
00748     b2 = bb + SEGNO(3.0 * sigb, f1);
00749 
00750     bcomp = b2;
00751     sum = 0.00;
00752     for (i = 0; i < np; i++) {
00753         arr[i] = y[i] - bcomp * x[i];
00754     }
00755     aa = cpl_vector_get_median_const(va);
00756     abdevt = 0.0;
00757     for (i = 0; i < np; i++) {
00758         d = y[i] - (bcomp * x[i] + aa);
00759         abdevt += fabs(d);
00760         if (y[i] != 0.0) 
00761             d /= fabs(y[i]);
00762         if (fabs(d) > 1e-7) 
00763             sum += (d >= 0.0 ? x[i] : -x[i]);
00764     }
00765     f2 = sum;
00766 
00767     if (fabs(b2-b1)<1e-7) {
00768         *a = aa;
00769         *b = bb;
00770         *abdev = abdevt / (double)np;
00771         cpl_vector_delete(va);
00772         return 0;
00773     }
00774 
00775     iter = 0;
00776     while (f1*f2 > 0.0) {
00777         bb = 2.0*b2-b1;
00778         b1 = b2;
00779         f1 = f2;
00780         b2 = bb;
00781 
00782         bcomp = b2;
00783         sum = 0.00;
00784         for (i = 0; i < np; i++) {
00785             arr[i] = y[i] - bcomp * x[i];
00786         }
00787         aa = cpl_vector_get_median_const(va);
00788         abdevt = 0.0;
00789         for (i = 0; i < np; i++) {
00790             d = y[i] - (bcomp * x[i] + aa);
00791             abdevt += fabs(d);
00792             if (y[i] != 0.0) 
00793                 d /= fabs(y[i]);
00794             if (fabs(d) > 1e-7) 
00795                 sum += (d >= 0.0 ? x[i] : -x[i]);
00796         }
00797         f2 = sum;
00798         iter++;
00799         if (iter >= max_iterate) 
00800             break;
00801     }
00802     if (iter >= max_iterate) {
00803         *a = aa_ls;
00804         *b = bb_ls;
00805         *abdev = -1.0;
00806         cpl_vector_delete(va);
00807         return 1;
00808     }
00809 
00810     sigb = 0.01 * sigb;
00811     while (fabs(b2-b1) > sigb) {
00812         bb = 0.5 * (b1 + b2);
00813         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00814             break;
00815         bcomp = bb;
00816         sum = 0.0;
00817         for (i = 0; i < np; i++) {
00818             arr[i] = y[i] - bcomp * x[i];
00819         }
00820         aa = cpl_vector_get_median_const(va);
00821         abdevt = 0.0;
00822         for (i = 0; i < np; i++) {
00823             d = y[i] - (bcomp * x[i] + aa);
00824             abdevt += fabs(d);
00825             if (y[i] != 0.0) 
00826                 d /= fabs(y[i]);
00827             if (fabs(d) > 1e-7) 
00828                 sum += (d >= 0.0 ? x[i] : -x[i]);
00829         }
00830         f = sum;
00831 
00832         if (f*f1 >= 0.0) {
00833             f1=f;
00834             b1=bb;
00835         } 
00836         else {
00837             f2=f;
00838             b2=bb;
00839         }
00840     }
00841     cpl_vector_delete(va);
00842     *a = aa;
00843     *b = bb;
00844     *abdev = abdevt / np;
00845     return 0;
00846 }
00847 #undef SEGNO
00848 
00849 /*      
00850  * The following static function applies the Hough transform from a table
00851  * of points to another table of points. Given the points p_i = (x_i,y_i)
00852  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00853  * and Y = y_i - X*x_i is computed and added to the output table for each
00854  * p_i, p_j pair. This means that if the input table has N points, the
00855  * output table has N*(N-1)/2 points.
00856  */
00857     
00858 /* static */
00859 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00860 {
00861     cpl_table *output;
00862     double    *xdata;
00863     double    *ydata;
00864     double    *xodata;
00865     double    *yodata;
00866     int        npoints;
00867     int        opoints;
00868     int        i, j, k;
00869 
00870 
00871     npoints = cpl_table_get_nrow(table);
00872     opoints = npoints*(npoints-1)/2;
00873 
00874     output = cpl_table_new(opoints);
00875     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00876     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00877     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00878     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00879 
00880     xodata = cpl_table_get_data_double(output, "m");
00881     yodata = cpl_table_get_data_double(output, "q");
00882     
00883     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00884     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00885 
00886     xdata = cpl_table_get_data_double(table, "x");
00887     ydata = cpl_table_get_data_double(table, "y");
00888 
00889     k = 0;
00890     for (i = 0; i < npoints; i++) {
00891         for (j = i+1; j < npoints; j++) {
00892             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00893             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00894             k++;
00895         }
00896     }
00897 
00898     if (k != opoints)
00899         printf("Assert k = %d, expected %d\n", k, opoints);
00900 
00901     cpl_table_erase_column(table, "x");
00902     cpl_table_erase_column(table, "y");
00903 
00904     return output;
00905 }
00906 
00907 
00908 /*
00909  * The following static function is performing the spectral
00910  * extraction for the function mos_extract_objects()
00911  */
00912 
00913 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00914                            cpl_image *extracted, cpl_image *sky, 
00915                            cpl_image *error, int nobjects, int extraction, 
00916                            double ron, double conad, int ncomb)
00917 {
00918 
00919   cpl_vector *vprofile;
00920   cpl_matrix *kernel;
00921   cpl_image  *smowin;
00922 
00923   int i, j;
00924   int specLen;
00925   int numRows;
00926   int index;
00927   int iter;
00928   int maxIter   = 2;         /* Not less than 2 !!! */
00929   int smoothBox = 31;        /* Not less than 5 !!! */
00930   double nsigma = 5.0;
00931 
00932   double sumWeight, sum, sumSky, sumProf, variance, weight;
00933   double *profile;
00934   double *buffer;
00935   float  *edata;
00936   float  *ekdata;
00937   float  *endata;
00938   float  *sdata;
00939   float  *kdata;
00940   float  *fdata;
00941 
00942   double value;
00943 
00944 
00945   specLen = cpl_image_get_size_x(sciwin);
00946   numRows = cpl_image_get_size_y(sciwin);
00947 
00948   edata = cpl_image_get_data(extracted);
00949   edata += nobjects * specLen;
00950 
00951   ekdata = cpl_image_get_data(sky);
00952   ekdata += nobjects * specLen;
00953 
00954   endata = cpl_image_get_data(error);
00955   endata += nobjects * specLen;
00956 
00957   sdata = cpl_image_get_data(sciwin);
00958   kdata = cpl_image_get_data(skywin);
00959 
00960 
00961   /*
00962    * Initial spectrum estimate
00963       if (sdata[i + j * specLen] > 0.0)
00964    */
00965 
00966   if (extraction && numRows > 5) {
00967       smowin = mos_image_filter_median(sciwin, 3, 3);
00968       fdata = cpl_image_get_data(smowin);
00969       for (i = 0; i < specLen; i++)
00970         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00971             edata[i] += fdata[i + j * specLen];
00972       cpl_image_delete(smowin);
00973   }
00974   else {
00975       for (i = 0; i < specLen; i++)
00976         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00977             edata[i] += sdata[i + j * specLen];
00978   }
00979 
00980   if (extraction) {
00981 
00982     profile = cpl_calloc(specLen * numRows, sizeof(double));
00983     buffer  = cpl_calloc(specLen, sizeof(double));
00984 
00985     for (iter = 0; iter < maxIter; iter++) {
00986 
00987       /*
00988        * Normalised spatial profile
00989        */
00990 
00991       for (i = 0; i < specLen; i++) {
00992         for (j = 0; j < numRows; j++) {
00993           index = i + j * specLen;
00994 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00995           if (fabs(edata[i]) > 0.00001)
00996             profile[index] = sdata[index] / edata[i];
00997           else
00998             profile[index] = 0.0;
00999         }
01000       }
01001 
01002       for (j = 0; j < numRows; j++) {
01003 
01004         /*
01005          * Smooth each row in the dispersion direction, and enforce positivity
01006          */
01007 
01008         for (i = 0; i < specLen - smoothBox; i++) {
01009           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01010           value = cpl_vector_get_median_const(vprofile);
01011           cpl_vector_unwrap(vprofile);
01012           if (value < 0)
01013             value = 0.0;
01014           buffer[i + smoothBox / 2] = value;
01015         }
01016 
01017         /*
01018          * Replace the end portions (i.e., not median filtered) with a mean
01019          */
01020 
01021         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01022         value = cpl_vector_get_mean(vprofile);
01023         cpl_vector_unwrap(vprofile);
01024 
01025         if (value < 0)
01026             value = 0.0;
01027 
01028         for (i = 0; i < smoothBox / 2; i++)
01029           buffer[i] = value;
01030 
01031         vprofile = cpl_vector_wrap(smoothBox / 2, 
01032                                    profile + specLen - smoothBox/2 + j*specLen);
01033         value = cpl_vector_get_mean(vprofile);
01034         cpl_vector_unwrap(vprofile);
01035 
01036         if (value < 0)
01037             value = 0.0;
01038 
01039         for (i = 0; i < smoothBox / 2; i++)
01040           buffer[i + specLen - smoothBox / 2] = value;
01041 
01042         for (i = 0; i < specLen; i++)
01043           profile[i + j * specLen] = buffer[i];
01044 
01045       }
01046 
01047       /*
01048        * Enforce normalization of spatial profile after smoothing
01049        */
01050 
01051       for (i = 0; i < specLen; i++) {
01052         for (j = 0, value = 0.0; j < numRows; j++)
01053           value += profile[i + j * specLen];
01054         if (value > 0.00001)
01055           for (j = 0; j < numRows; j++)
01056             profile[i + j * specLen] /= value;
01057         else
01058           for (j = 0; j < numRows; j++)
01059             profile[i + j * specLen] = 0.0;
01060       }
01061 
01062 
01063       /*
01064        * Optimal extraction
01065        */
01066 
01067       for (i = 0; i < specLen; i++) {
01068         sum = 0.0;
01069         sumSky = 0.0;
01070         sumWeight = 0.0;
01071         sumProf = 0.0;
01072         for (j = 0; j < numRows; j++) {
01073           index = i + j * specLen;
01074 /*        
01075 if (sdata[index] > 0.0) {
01076 */
01077             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01078                      / conad;
01079             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01080             value = sdata[index] - edata[i] * profile[index];
01081             if (fabs(value) / sqrt(variance) < nsigma) {
01082               weight = 1000000 * profile[index] / variance;
01083               sum += weight * sdata[index];
01084               sumSky += weight * kdata[index];
01085               sumWeight += weight * profile[index];
01086               sumProf += profile[index];
01087             }
01088 /*
01089 }      
01090 */
01091         }
01092 
01093         if (sumWeight > 0.00001) {
01094           edata[i] = sum / sumWeight;
01095           ekdata[i] = sumSky / sumWeight;
01096           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01097         }
01098         else {
01099 /*
01100           edata[i] = 0.0;
01101           ekdata[i] = 0.0;
01102           endata[i] = 0.0;
01103 */
01104           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01105         }
01106       }
01107     }
01108     cpl_free(profile);
01109     cpl_free(buffer);
01110   }
01111   else {
01112 
01113     /*
01114      * Add sky estimation for the simple aperture extraction.
01115         if (kdata[i + j * specLen] > 0.0)
01116      */
01117 
01118     for (i = 0; i < specLen; i++)
01119       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01120           ekdata[i] += kdata[i + j * specLen];
01121 
01122     /*
01123      * Add error estimation for the simple aperture extraction.
01124      */
01125 
01126     for (i = 0; i < specLen; i++)
01127       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01128 
01129   }
01130 
01131 }
01132 
01133 
01180 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01181                                  cpl_table *ids, cpl_table *crv, 
01182                                  double reference)
01183 {
01184     const char *func = "mos_global_distortion";
01185 
01186     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01187 
01188     cpl_table      *global = NULL;
01189     cpl_table      *coeff;
01190     cpl_table      *dummy;
01191     cpl_vector     *ci;
01192     cpl_vector     *xmask;
01193     cpl_vector     *ymask;
01194     cpl_bivector   *mask;
01195     cpl_vector     *xccd;
01196     cpl_vector     *yccd;
01197     cpl_bivector   *ccd;
01198     cpl_polynomial *poly;
01199     double         *xtop;
01200     double         *ytop;
01201     double         *xbottom;
01202     double         *ybottom;
01203     double         *mxtop;
01204     double         *mytop;
01205     double         *mxbottom;
01206     double         *mybottom;
01207     int            *position;
01208     int            *length;
01209     int            *slit_id;
01210     int            *mslit_id;
01211     int             nslits, nmaskslits, npoints;
01212     int             order;
01213     int             i, j;
01214     int             minslit = 6;    // 12;
01215 
01216 
01217 /* *+
01218 printf("error1: %s\n", cpl_error_get_message());
01219 +* */
01220     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01221         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01222         return NULL;
01223     }
01224 /* *+
01225 printf("error1a: %s\n", cpl_error_get_message());
01226 +* */
01227 
01228     nslits = cpl_table_get_nrow(slits);
01229 /* *+
01230 printf("error1b: %s\n", cpl_error_get_message());
01231 +* */
01232 
01233     if (nslits < minslit) {
01234         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01235                         "distortion model determination", nslits, minslit);
01236         return NULL;
01237     }
01238 /* *+
01239 printf("error1c: %s\n", cpl_error_get_message());
01240 +* */
01241 
01242     nmaskslits = cpl_table_get_nrow(maskslits);
01243 
01244     length   = cpl_table_get_data_int(slits, "length");
01245     position = cpl_table_get_data_int(slits, "position");
01246     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01247     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01248     xtop     = cpl_table_get_data_double(slits, "xtop");
01249     ytop     = cpl_table_get_data_double(slits, "ytop");
01250     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01251     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01252     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01253     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01254     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01255     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01256 
01257 
01258     /*
01259      * Global IDS
01260      */
01261 
01262     coeff = cpl_table_new(nslits);
01263     cpl_table_copy_structure(coeff, ids);
01264     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01265     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01266     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01267     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01268 
01269 /* *+
01270 printf("error2: %s\n", cpl_error_get_message());
01271 +* */
01272     for (i = 0; i < nslits; i++) {
01273         for (j = 0; j < nmaskslits; j++) {
01274             if (slit_id[i] == mslit_id[j]) {
01275                 cpl_table_set_double(coeff, "xmask", i,
01276                                      (mxtop[j] + mxbottom[j]) / 2);
01277                 cpl_table_set_double(coeff, "ymask", i,
01278                                      (mytop[j] + mybottom[j]) / 2);
01279             }
01280         }
01281     }
01282 
01283     if (cpl_table_has_invalid(coeff, "xmask")) {
01284         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01285         cpl_table_delete(coeff);
01286         return NULL;
01287     }
01288 
01289     for (i = 0; i < nslits; i++) {
01290         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01291         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01292     }
01293 
01294 /* *+
01295 printf("error3: %s\n", cpl_error_get_message());
01296 +* */
01297     for (i = 0; i < nslits; i++) {
01298 
01299         if (length[i] == 0)
01300             continue;
01301 
01302         cpl_table_and_selected_window(ids, position[i], length[i]);
01303         dummy = cpl_table_extract_selected(ids);
01304         for (j = 0; j < 6; j++) {
01305             if (cpl_table_has_column(dummy, clab[j])) {
01306                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01307                     cpl_table_set_double(coeff, clab[j], i, 
01308                          cpl_table_get_column_median(dummy, clab[j]));
01309                 }
01310             }
01311         }
01312 
01313         cpl_table_delete(dummy);
01314         cpl_table_select_all(ids);
01315             
01316     }
01317 
01318 /* *+
01319 printf("error4: %s\n", cpl_error_get_message());
01320 +* */
01321     for (j = 0; j < 6; j++) {
01322         if (cpl_table_has_column(coeff, clab[j])) {
01323             cpl_table_and_selected_invalid(coeff, clab[j]);
01324 
01325             if (cpl_table_not_selected(coeff))
01326                 dummy = cpl_table_extract_selected(coeff);
01327             else
01328                 break;
01329 
01330             npoints = cpl_table_get_nrow(dummy);
01331 
01332             if (npoints >= 6) {
01333 
01334                 if (npoints >= 12)
01335                     order = 2;
01336                 else
01337                     order = 1;
01338                    
01339                 ci = cpl_vector_wrap(npoints,
01340                                      cpl_table_get_data_double(dummy, clab[j]));
01341                 if (j) {
01342                     xccd = cpl_vector_wrap(npoints,
01343                                      cpl_table_get_data_double(dummy, "xccd"));
01344                     yccd = cpl_vector_wrap(npoints,
01345                                      cpl_table_get_data_double(dummy, "yccd"));
01346                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01347 
01348 /* %%% */
01349                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01350 
01351                     cpl_bivector_unwrap_vectors(ccd);
01352                     cpl_vector_unwrap(xccd);
01353                     cpl_vector_unwrap(yccd);
01354                     cpl_vector_unwrap(ci);
01355                 }
01356                 else {
01357                     xmask = cpl_vector_wrap(npoints,
01358                                      cpl_table_get_data_double(dummy, "xmask"));
01359                     ymask = cpl_vector_wrap(npoints,
01360                                      cpl_table_get_data_double(dummy, "ymask"));
01361                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01362 
01363 /* %%% */
01364                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01365 
01366                     cpl_bivector_unwrap_vectors(mask);
01367                     cpl_vector_unwrap(xmask);
01368                     cpl_vector_unwrap(ymask);
01369                     cpl_vector_unwrap(ci);
01370                 }
01371             }
01372             else {
01373                 cpl_size p[2] = {0, 0};
01374                 poly = cpl_polynomial_new(2);
01375                 cpl_polynomial_set_coeff(poly, p, 
01376                                cpl_table_get_column_median(dummy, clab[j]));
01377             }
01378 
01379             cpl_table_delete(dummy);
01380 
01381             global = write_global_distortion(global, j, poly);
01382 
01383             cpl_polynomial_delete(poly);
01384 
01385             cpl_table_select_all(coeff);
01386         }
01387     }
01388 
01389 /* *+
01390 printf("error5: %s\n", cpl_error_get_message());
01391 +* */
01392     cpl_table_delete(coeff);
01393 /* *+
01394 printf("error6: %s\n", cpl_error_get_message());
01395 +* */
01396 
01397 
01398     /*
01399      * Add model's reference wavelength
01400      */
01401 
01402     cpl_table_set_double(global, "a00", 6, reference);
01403 
01404 
01405     /*
01406      * Global curvature model
01407      */
01408 
01409     coeff = cpl_table_duplicate(crv);
01410     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01411     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01412     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01413     npoints = cpl_table_get_nrow(coeff);
01414 
01415 /* *+
01416 printf("error7: %s\n", cpl_error_get_message());
01417 +* */
01418     for (i = 0; i < npoints; i++) {
01419         for (j = 0; j < nmaskslits; j++) {
01420             if (slit_id[i] == mslit_id[j]) {
01421                 if (i%2) {
01422                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01423                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01424                 }
01425                 else {
01426                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01427                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01428                 }
01429             }
01430         }
01431     }
01432 
01433 /* *+
01434 printf("error8: %s\n", cpl_error_get_message());
01435 +* */
01436     if (cpl_table_has_invalid(coeff, "xmask")) {
01437         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01438         cpl_table_delete(coeff);
01439         return NULL;
01440     }
01441 
01442 /* *+
01443 printf("error9: %s\n", cpl_error_get_message());
01444 +* */
01445     for (j = 0; j < 3; j++) {
01446         if (cpl_table_has_column(coeff, clab[j])) {
01447             cpl_table_and_selected_invalid(coeff, clab[j]);
01448 
01449             if (cpl_table_not_selected(coeff))
01450                 dummy = cpl_table_extract_selected(coeff);
01451             else
01452                 break;
01453 
01454             npoints = cpl_table_get_nrow(dummy);
01455 
01456             if (npoints >= 6) {
01457 
01458                 if (npoints >= 12)
01459                     order = 2;
01460                 else
01461                     order = 1;
01462 
01463                 ci = cpl_vector_wrap(npoints,
01464                                      cpl_table_get_data_double(dummy, clab[j]));
01465                 xmask = cpl_vector_wrap(npoints,
01466                                      cpl_table_get_data_double(dummy, "xmask"));
01467                 ymask = cpl_vector_wrap(npoints,
01468                                      cpl_table_get_data_double(dummy, "ymask"));
01469                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01470 
01471                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01472 
01473                 cpl_bivector_unwrap_vectors(mask);
01474                 cpl_vector_unwrap(ci);
01475                 cpl_vector_unwrap(xmask);
01476                 cpl_vector_unwrap(ymask);
01477             }
01478             else {
01479                 cpl_size p[2] = {0, 0};
01480                 poly = cpl_polynomial_new(2);
01481                 cpl_polynomial_set_coeff(poly, p,
01482                                cpl_table_get_column_median(dummy, clab[j]));
01483             }
01484 
01485             cpl_table_delete(dummy);
01486 
01487             global = write_global_distortion(global, j + 7, poly);
01488 
01489             cpl_polynomial_delete(poly);
01490             cpl_table_select_all(coeff);
01491         }
01492     }
01493 
01494 /* *+
01495 printf("error10: %s\n", cpl_error_get_message());
01496 +* */
01497     cpl_table_delete(coeff);
01498 /* *+
01499 printf("error11: %s\n", cpl_error_get_message());
01500 +* */
01501 
01502     return global;
01503 
01504 }
01505 
01506 
01544 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01545                                    int ysize)
01546 {
01547     const char *func = "mos_build_slit_location";
01548 
01549     cpl_propertylist *sort_col;
01550     cpl_polynomial   *ids0;
01551     cpl_polynomial   *crv[3];
01552     cpl_polynomial   *loc_crv;
01553     cpl_vector       *point;
01554     cpl_table        *slits;
01555     cpl_size         nslits;
01556     int              *slit_id;
01557     double           *dpoint;
01558     double           *xtop;
01559     double           *ytop;
01560     double           *xbottom;
01561     double           *ybottom;
01562     double           *mxtop;
01563     double           *mytop;
01564     double           *mxbottom;
01565     double           *mybottom;
01566     cpl_size          i;
01567     cpl_size          j;
01568 
01569 
01570     if (global == NULL || maskslits == NULL) {
01571         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01572         return NULL;
01573     }
01574 
01575     nslits   = cpl_table_get_nrow(maskslits);
01576     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01577     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01578     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01579     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01580     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01581 
01582     slits = cpl_table_duplicate(maskslits);
01583 
01584     xtop    = cpl_table_get_data_double(slits, "xtop");
01585     ytop    = cpl_table_get_data_double(slits, "ytop");
01586     xbottom = cpl_table_get_data_double(slits, "xbottom");
01587     ybottom = cpl_table_get_data_double(slits, "ybottom");
01588 
01589     ids0 = read_global_distortion(global, 0);
01590     crv[0] = read_global_distortion(global, 7);
01591     crv[1] = read_global_distortion(global, 8);
01592     crv[2] = read_global_distortion(global, 9);
01593 
01594     loc_crv = cpl_polynomial_new(1);
01595 
01596     point = cpl_vector_new(2);
01597     dpoint = cpl_vector_get_data(point);
01598 
01599     for (i = 0; i < nslits; i++) {
01600         dpoint[0] = mxtop[i];
01601         dpoint[1] = mytop[i];
01602 
01603         xtop[i] = cpl_polynomial_eval(ids0, point);
01604 
01605         for (j = 0; j < 3; j++)
01606             if (crv[j])
01607                 cpl_polynomial_set_coeff(loc_crv, &j, 
01608                                          cpl_polynomial_eval(crv[j], point));
01609 
01610         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01611 
01612         dpoint[0] = mxbottom[i];
01613         dpoint[1] = mybottom[i];
01614         xbottom[i] = cpl_polynomial_eval(ids0, point);
01615 
01616         for (j = 0; j < 3; j++)
01617             if (crv[j])
01618                 cpl_polynomial_set_coeff(loc_crv, &j,
01619                                          cpl_polynomial_eval(crv[j], point));
01620 
01621         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01622     }
01623 
01624     cpl_vector_delete(point);
01625     cpl_polynomial_delete(ids0);
01626     cpl_polynomial_delete(loc_crv);
01627     for (j = 0; j < 3; j++)
01628         cpl_polynomial_delete(crv[j]);
01629 
01630     sort_col = cpl_propertylist_new();
01631     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01632     cpl_table_sort(slits, sort_col);
01633     cpl_table_sort(maskslits, sort_col);
01634     cpl_propertylist_delete(sort_col);
01635 
01636     /*
01637      * Eliminate slits which are _entirely_ outside the CCD
01638      */
01639 
01640     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01641     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01642     cpl_table_erase_selected(slits);
01643 
01644     nslits = cpl_table_get_nrow(slits);
01645 
01646     if (nslits == 0) {
01647         cpl_msg_warning(func, "No slits found on the CCD");
01648         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01649         cpl_table_delete(slits);
01650         return NULL;
01651     }
01652 
01653     if (nslits > 1)
01654         cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slits are entirely or partially "
01655                      "contained in CCD", nslits);
01656     else
01657         cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slit is entirely or partially "
01658                      "contained in CCD", nslits);
01659 
01660     return slits;
01661 
01662 }
01663 
01664 
01691 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01692                                 cpl_table *slits)
01693 {
01694     const char *func = "mos_build_curv_coeff";
01695 
01696     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01697                                                  /* Max order is 5 */
01698 
01699     cpl_polynomial *crv[3];
01700     cpl_vector     *point;
01701     cpl_table      *polytraces;
01702     double         *dpoint;
01703     double         *xtop;
01704     double         *ytop;
01705     double         *xbottom;
01706     double         *ybottom;
01707     int            *slit_id;
01708     int            *valid_id;
01709     int             nslits, nvalid;
01710     int             found;
01711     int             i, j, k;
01712 
01713 
01714     if (global == NULL || slits == NULL || maskslits == NULL) {
01715         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01716         return NULL;
01717     }
01718 
01719     nslits  = cpl_table_get_nrow(maskslits);
01720     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01721     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01722     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01723     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01724     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01725 
01726     polytraces = cpl_table_new(2*nslits);
01727     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01728     for (i = 0; i < 3; i++)
01729         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01730 
01731     crv[0] = read_global_distortion(global, 7);
01732     crv[1] = read_global_distortion(global, 8);
01733     crv[2] = read_global_distortion(global, 9);
01734 
01735     point = cpl_vector_new(2);
01736     dpoint = cpl_vector_get_data(point);
01737 
01738     for (i = 0; i < nslits; i++) {
01739         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01740 
01741             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01742 
01743             if (j) {
01744                 dpoint[0] = xbottom[i];
01745                 dpoint[1] = ybottom[i];                
01746             }
01747             else {
01748                 dpoint[0] = xtop[i];
01749                 dpoint[1] = ytop[i];                
01750             }
01751 
01752             for (k = 0; k < 3; k++)
01753                 if (crv[j])
01754                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01755                                          cpl_polynomial_eval(crv[k], point));
01756         }
01757     }
01758 
01759     cpl_vector_delete(point);
01760     for (j = 0; j < 3; j++)
01761         cpl_polynomial_delete(crv[j]);
01762 
01763     /*
01764      * Eliminate slits which are _entirely_ outside the CCD
01765      */
01766  
01767     nvalid  = cpl_table_get_nrow(slits);
01768     valid_id = cpl_table_get_data_int(slits, "slit_id");
01769     cpl_table_unselect_all(polytraces);
01770     for (i = 0; i < nslits; i++) {
01771         found = 0;
01772         for (j = 0; j < nvalid; j++) {
01773             if (slit_id[i] == valid_id[j]) {
01774                 found = 1;
01775                 break;
01776             }
01777         }
01778         if (!found) {
01779             cpl_table_select_row(polytraces, 2*i);
01780             cpl_table_select_row(polytraces, 2*i + 1);
01781         }
01782     }
01783     cpl_table_erase_selected(polytraces);
01784  
01785     nslits = cpl_table_get_nrow(polytraces);
01786 
01787     if (nslits == 0) {
01788         cpl_msg_warning(func, "No slits found on the CCD");
01789         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01790         cpl_table_delete(polytraces);
01791         return NULL;
01792     }
01793 
01794     if (nslits > 2) 
01795         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01796                      "partially contained in CCD", nslits / 2);
01797     else
01798         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01799                      "partially contained in CCD", nslits / 2);
01800 
01801     return polytraces;
01802 }
01803 
01804 
01846 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01847 {
01848     const char *func = "mos_build_disp_coeff";
01849 
01850     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01851 
01852     cpl_polynomial *ids[6];
01853     cpl_vector     *point;
01854     cpl_table      *idscoeff;
01855     double         *dpoint;
01856     double         *xtop;
01857     double         *ytop;
01858     double         *xbottom;
01859     double         *ybottom;
01860     int            *position;
01861     int            *length;
01862     int             nslits;
01863     int             nrows;
01864     int             order;
01865     int             ylow, yhig;
01866     int             i, j, k;
01867 
01868 
01869     if (global == NULL || slits == NULL) {
01870         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01871         return NULL;
01872     }
01873     
01874     nslits   = cpl_table_get_nrow(slits);
01875     position = cpl_table_get_data_int(slits, "position");
01876     length   = cpl_table_get_data_int(slits, "length");
01877     xtop     = cpl_table_get_data_double(slits, "xtop");
01878     ytop     = cpl_table_get_data_double(slits, "ytop");
01879     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01880     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01881 
01882     for (i = 0; i < 6; i++)
01883         ids[i] = read_global_distortion(global, i);
01884 
01885     for (i = 0; i < 6; i++)
01886         if (ids[i] == NULL)
01887             break;
01888 
01889     order = i - 1;
01890 
01891     if (order < 1) {
01892         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01893         return NULL;
01894     }
01895 
01896     nrows = 0;
01897     for (i = 0; i < nslits; i++)
01898         nrows += length[i]; 
01899 
01900     idscoeff = cpl_table_new(nrows);
01901 
01902     for (j = 0; j <= order; j++)
01903         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01904 
01905     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01906     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01907     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01908     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01909 
01910     point = cpl_vector_new(2);
01911     dpoint = cpl_vector_get_data(point);
01912 
01913     for (i = 0; i < nslits; i++) {
01914 
01915         if (length[i] == 0)
01916             continue;
01917 
01918         ylow = position[i];
01919         yhig = ylow + length[i];
01920 
01921         for (j = 0; j <= order; j++) {
01922             if (j) {
01923                 for (k = 0; k < length[i]; k++) {
01924                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01925                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01926                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01927                                          cpl_polynomial_eval(ids[j], point));
01928                 }
01929             }
01930             else {
01931                 for (k = 0; k < length[i]; k++) {
01932                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01933                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01934                 }
01935             }
01936         }
01937     }
01938 
01939     cpl_vector_delete(point);
01940     for (j = 0; j < 6; j++)
01941         cpl_polynomial_delete(ids[j]);
01942 
01943     return idscoeff;
01944 
01945 }
01946 
01947 
01970 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01971                             cpl_table *polytraces, double reference, 
01972                             double blue, double red, double dispersion)
01973 {
01974     const char     *func = "mos_subtract_sky";
01975 
01976     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01977                                                  /* Max order is 5 */
01978 
01979     cpl_image      *sky;
01980     cpl_bivector   *list;
01981     cpl_vector     *listx;
01982     cpl_vector     *listy;
01983     cpl_polynomial *polytop;
01984     cpl_polynomial *polybot;
01985     cpl_polynomial *trend;
01986 
01987     int            *slit_id;
01988     double         *dlistx;
01989     double         *dlisty;
01990     float          *sdata;
01991     float          *kdata;
01992     double          top, bot;
01993     int             itop, ibot;
01994     double          coeff;
01995     double          ytop, ybot;
01996     double          m, q, err;
01997     int             npix;
01998 
01999     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02000     int             nx, ny;
02001     int             nslits;
02002     int            *length;
02003     int             missing_top, missing_bot;
02004     int             order;
02005     int             null;
02006     int             window = 50;  /* Longer slits have polynomial sky model */
02007     int             count;
02008     int             i, j;
02009     cpl_size        k;
02010 
02011 
02012     if (science == NULL || slits == NULL || polytraces == NULL) {
02013         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02014         return NULL;
02015     }
02016  
02017     if (dispersion <= 0.0) {
02018         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02019         return NULL;
02020     }
02021 
02022     if (red - blue < dispersion) {
02023         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02024         return NULL;
02025     }
02026 
02027     nx = cpl_image_get_size_x(science);
02028     ny = cpl_image_get_size_y(science);
02029 
02030     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02031 
02032     sdata = cpl_image_get_data(science);
02033     kdata = cpl_image_get_data(sky);
02034 
02035     nslits   = cpl_table_get_nrow(slits);
02036     order    = cpl_table_get_ncol(polytraces) - 2;
02037     length   = cpl_table_get_data_int(slits, "length");
02038     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02039 
02040     /*
02041      * The spatial resampling is performed for a certain number of
02042      * pixels above and below the position of the reference wavelength:
02043      */
02044     
02045     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02046     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02047 
02048     for (i = 0; i < nslits; i++) {
02049 
02050         if (length[i] == 0)
02051             continue;
02052 
02053         
02054         /*
02055          * Recover from the table of spectral curvature coefficients
02056          * the curvature polynomials.
02057          */
02058 
02059         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02060 
02061         start_pixel = refpixel - pixel_below;
02062         if (start_pixel < 0)
02063             start_pixel = 0;
02064 
02065         end_pixel = refpixel + pixel_above;
02066         if (end_pixel > nx)
02067             end_pixel = nx;
02068 
02069         missing_top = 0;
02070         polytop = cpl_polynomial_new(1);
02071         for (k = 0; k <= order; k++) {
02072             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02073             if (null) {
02074                 cpl_polynomial_delete(polytop);
02075                 missing_top = 1;
02076                 break;
02077             }
02078             cpl_polynomial_set_coeff(polytop, &k, coeff);
02079         }
02080 
02081         missing_bot = 0;
02082         polybot = cpl_polynomial_new(1);
02083         for (k = 0; k <= order; k++) {
02084             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02085             if (null) {
02086                 cpl_polynomial_delete(polybot);
02087                 missing_bot = 1;
02088                 break;
02089             }
02090             cpl_polynomial_set_coeff(polybot, &k, coeff);
02091         }
02092 
02093         if (missing_top && missing_bot) {
02094             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02095                           slit_id[i]);
02096             continue;
02097         }
02098 
02099         /*
02100          * In case just one of the two edges was not traced, the other
02101          * edge curvature model is duplicated and shifted to the other
02102          * end of the slit: better than nothing!
02103          */
02104 
02105         if (missing_top) {
02106             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02107                           "the spectral curvature of the lower edge "
02108                           "is used instead.", slit_id[i]);
02109             polytop = cpl_polynomial_duplicate(polybot);
02110             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02111             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02112             k = 0;
02113             coeff = cpl_polynomial_get_coeff(polybot, &k);
02114             coeff += ytop - ybot;
02115             cpl_polynomial_set_coeff(polytop, &k, coeff);
02116         }
02117 
02118         if (missing_bot) {
02119             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02120                           "the spectral curvature of the upper edge "
02121                           "is used instead.", slit_id[i]);
02122             polybot = cpl_polynomial_duplicate(polytop);
02123             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02124             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02125             k = 0;
02126             coeff = cpl_polynomial_get_coeff(polytop, &k);
02127             coeff -= ytop - ybot;
02128             cpl_polynomial_set_coeff(polybot, &k, coeff);
02129         }
02130 
02131 
02132         /*
02133          * Now read pixel values along spatial direction, and fit them.
02134          */
02135 
02136         for (j = start_pixel; j < end_pixel; j++) {
02137             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02138             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02139             itop = floor(top + 0.5) + 1;
02140             ibot = floor(bot + 0.5);
02141             if (itop > ny)
02142                 itop = ny;
02143             if (ibot < 0)
02144                 ibot = 0;
02145             npix = itop - ibot;
02146             if (npix < 5)
02147                 break;
02148 
02149             list = cpl_bivector_new(npix);
02150             listx = cpl_bivector_get_x(list);
02151             listy = cpl_bivector_get_y(list);
02152             dlistx = cpl_vector_get_data(listx);
02153             dlisty = cpl_vector_get_data(listy);
02154 
02155             for (k = 0; k < npix; k++) {
02156                 dlistx[k] = k;
02157                 dlisty[k] = sdata[j + (ibot + k)*nx];
02158             }
02159 
02160             if (robustLinearFit(list, &q, &m, &err)) {
02161                 cpl_bivector_delete(list);
02162                 continue;
02163             }
02164 
02165             cpl_bivector_delete(list);
02166 
02167             for (k = 0; k < npix; k++) {
02168                 kdata[j + (ibot + k)*nx] = m*k + q;
02169             }
02170 
02171             if (npix > window) {
02172 
02173                 /*
02174                  * Polynomial iteration
02175                  */
02176 
02177                 err = 3*sqrt(err);
02178 
02179                 count = 0;
02180                 for (k = 0; k < npix; k++)
02181                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02182                         count++;
02183 
02184                 if (count < 10)
02185                     continue;
02186 
02187                 list = cpl_bivector_new(count);
02188                 listx = cpl_bivector_get_x(list);
02189                 listy = cpl_bivector_get_y(list);
02190                 dlistx = cpl_vector_get_data(listx);
02191                 dlisty = cpl_vector_get_data(listy);
02192 
02193                 count = 0;
02194                 for (k = 0; k < npix; k++) {
02195                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02196                         dlistx[count] = k;
02197                         dlisty[count] = sdata[j + (ibot + k)*nx];
02198                         count++;
02199                     }
02200                 }
02201 
02202                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02203  
02204                 cpl_bivector_delete(list);
02205 
02206                 err = 3*sqrt(err);
02207 
02208                 count = 0;
02209                 for (k = 0; k < npix; k++)
02210                     if (fabs(sdata[j + (ibot + k)*nx] 
02211                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02212                         count++;
02213 
02214                 if (count < 10) {
02215                     cpl_polynomial_delete(trend);
02216                     continue;
02217                 }
02218 
02219                 list = cpl_bivector_new(count);
02220                 listx = cpl_bivector_get_x(list);
02221                 listy = cpl_bivector_get_y(list);
02222                 dlistx = cpl_vector_get_data(listx);
02223                 dlisty = cpl_vector_get_data(listy);
02224 
02225                 count = 0;
02226                 for (k = 0; k < npix; k++) {
02227                     if (fabs(sdata[j + (ibot + k)*nx] 
02228                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02229                         dlistx[count] = k;
02230                         dlisty[count] = sdata[j + (ibot + k)*nx];
02231                         count++;
02232                     }
02233                 }
02234 
02235                 cpl_polynomial_delete(trend);
02236 
02237                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02238 
02239                 cpl_bivector_delete(list);
02240  
02241                 for (k = 0; k < npix; k++) {
02242                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02243                                                k, NULL);
02244                 }
02245 
02246                 cpl_polynomial_delete(trend);
02247             }
02248         }
02249         cpl_polynomial_delete(polytop);
02250         cpl_polynomial_delete(polybot);
02251     }
02252 
02253     cpl_image_subtract(science, sky);
02254 
02255     return sky;
02256 }
02257 
02258 
02291 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02292                               cpl_table *slits, cpl_table *polytraces, 
02293                               double reference, double blue, double red, 
02294                               double dispersion, int sradius, int polyorder)
02295 {
02296     const char     *func = "mos_normalise_flat";
02297 
02298     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02299                                                  /* Max order is 5 */
02300 
02301     cpl_image      *rectified;
02302     cpl_image      *smo_flat;
02303     cpl_image      *exslit;
02304     cpl_vector     *positions;
02305     cpl_vector     *flux;
02306     cpl_vector     *smo_flux;
02307     cpl_polynomial *trend;
02308     cpl_polynomial *polytop;
02309     cpl_polynomial *polybot;
02310 
02311     int            *slit_id;
02312     float          *p;
02313     float          *data;
02314     double         *fdata;
02315     double         *pdata;
02316     float          *sdata;
02317     float          *xdata;
02318     float          *wdata;
02319     double          vtop, vbot, value;
02320     double          top, bot;
02321     double          coeff;
02322     double          ytop, ybot;
02323     double          ypos;
02324     double          fvalue;
02325     int             ivalue;
02326     int             yint, yprev;
02327     int             npseudo;
02328 
02329     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02330     int             nx, ny, nsubx, nsuby;
02331     int             xlow, ylow, xhig, yhig;
02332     int             nslits;
02333     int            *position;
02334     int            *length;
02335     int             missing_top, missing_bot;
02336     int             order;
02337     int             npoints;
02338     int             uradius;
02339     int             null;
02340     int             i, j;
02341     cpl_size        k;
02342 
02343 /*    int             exclude = 5;     Number of excluded pixels at edges */
02344 
02345     /* For debug puposes only: cpl_image      *smo_rectified; */
02346 
02347 
02348     if (flat == NULL || slits == NULL || polytraces == NULL) {
02349         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02350         return NULL;
02351     }
02352  
02353     if (dispersion <= 0.0) {
02354         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02355         return NULL;
02356     }
02357 
02358     if (red - blue < dispersion) {
02359         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02360         return NULL;
02361     }
02362 
02363     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02364                                         blue, red, dispersion, 0, NULL);
02365 
02366     nx = cpl_image_get_size_x(rectified);
02367     ny = cpl_image_get_size_y(rectified);
02368 
02369     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02370                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02371     wdata = cpl_image_get_data(smo_flat);
02372 
02373     nslits   = cpl_table_get_nrow(slits);
02374     order    = cpl_table_get_ncol(polytraces) - 2;
02375     position = cpl_table_get_data_int(slits, "position");
02376     length   = cpl_table_get_data_int(slits, "length");
02377     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02378 
02379     /*
02380      * The spatial resampling is performed for a certain number of
02381      * pixels above and below the position of the reference wavelength:
02382      */
02383     
02384     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02385     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02386 
02387     xlow = 1;
02388     xhig = nx;
02389     for (i = 0; i < nslits; i++) {
02390 
02391         if (length[i] == 0)
02392             continue;
02393 
02394         /*
02395          * We DON'T write:
02396          *
02397          * ylow = position[i];
02398          * yhig = ylow + length[i];
02399          *
02400          * because the cpl_image pixels are counted from 1, and because in 
02401          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02402          */
02403 
02404         ylow = position[i] + 1;
02405         yhig = ylow + length[i] - 1;
02406 
02407         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02408 
02409         if (polyorder < 0) {
02410 
02411             cpl_image_turn(exslit, -1);   /* For faster memory access */
02412     
02413             nsubx = cpl_image_get_size_x(exslit);
02414             nsuby = cpl_image_get_size_y(exslit);
02415             data = cpl_image_get_data(exslit);
02416             flux = cpl_vector_new(nsubx);
02417 
02418             uradius = nsubx / 2;
02419             if (uradius > sradius)
02420                 uradius = sradius;
02421 
02422             for (j = 0; j < nsuby; j++) {
02423                 fdata = cpl_vector_get_data(flux);
02424                 p = data;
02425                 for (k = 0; k < nsubx; k++)
02426                     *fdata++ = *p++;
02427                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02428                 fdata = cpl_vector_get_data(smo_flux);
02429                 p = data;
02430                 for (k = 0; k < nsubx; k++)
02431                     *p++ = *fdata++;
02432                 cpl_vector_delete(smo_flux);
02433                 data += nsubx;
02434             }
02435 
02436             cpl_vector_delete(flux);
02437 
02438 
02439             /*
02440              * First fit fluxes along the spatial direction with a low-degree
02441              * polynomial (excluding the first and the last pixels, close to
02442              * the edges)
02443              */
02444 /*
02445             if (nsubx-2*exclude > 10) {
02446                 flux = cpl_vector_new(nsubx-2*exclude);
02447                 fdata = cpl_vector_get_data(flux);
02448                 positions = cpl_vector_new(nsubx-2*exclude);
02449                 for (j = 0; j < nsubx-2*exclude; j++)
02450                     cpl_vector_set(positions, j, j+exclude);
02451         
02452                 for (k = 0; k < nsuby; k++) {
02453                     for (j = 0; j < nsubx-2*exclude; j++)
02454                         fdata[j] = data[j+exclude];
02455                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02456                                                          1, NULL);
02457                     for (j = 0; j < nsubx; j++)
02458                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02459                     cpl_polynomial_delete(trend);
02460                     data += nsubx;
02461                 }
02462 
02463                 cpl_vector_delete(flux);
02464                 cpl_vector_delete(positions);
02465             }
02466 */
02467 
02468             /*
02469              * Now smooth along the dispersion direction 
02470              */
02471 
02472             cpl_image_turn(exslit, 1);   /* For faster memory access */
02473             nsubx = cpl_image_get_size_x(exslit);
02474             nsuby = cpl_image_get_size_y(exslit);
02475             data = cpl_image_get_data(exslit);
02476 
02477             for (j = 0; j < nsuby; j++) {
02478                 flux = cpl_vector_new(nsubx);
02479                 fdata = cpl_vector_get_data(flux);
02480                 p = data;
02481                 for (k = 0; k < nsubx; k++)
02482                     *fdata++ = *p++;
02483                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02484                 cpl_vector_delete(flux);
02485                 fdata = cpl_vector_get_data(smo_flux);
02486                 p = data;
02487                 for (k = 0; k < nsubx; k++)
02488                     *p++ = *fdata++;
02489                 cpl_vector_delete(smo_flux);
02490                 data += nsubx;
02491             }
02492         }
02493         else {
02494 
02495             /*
02496              * Fit with a polynomial the flat field trend row by row.
02497              */
02498 
02499             nsubx = cpl_image_get_size_x(exslit);
02500             nsuby = cpl_image_get_size_y(exslit);
02501             data = cpl_image_get_data(exslit);
02502 
02503             for (j = 0; j < nsuby; j++) {
02504 
02505                 /*
02506                  * First get the size of the vectors to allocate:
02507                  */
02508 
02509                 npoints = 0;
02510                 p = data + j*nsubx;
02511                 for (k = 0; k < nsubx; k++)
02512                     if (p[k] > 1.0)
02513                         npoints++;
02514 
02515                 if (npoints > polyorder + 1) {
02516 
02517                     /*
02518                      * Fill the vectors for the fitting
02519                      */
02520 
02521                     flux = cpl_vector_new(npoints);
02522                     fdata = cpl_vector_get_data(flux);
02523                     positions = cpl_vector_new(npoints);
02524                     pdata = cpl_vector_get_data(positions);
02525 
02526                     npoints = 0;
02527                     p = data + j*nsubx;
02528                     for (k = 0; k < nsubx; k++) {
02529                         if (p[k] > 1.0) {
02530                             fdata[npoints] = p[k];
02531                             pdata[npoints] = k;
02532                             npoints++;
02533                         }
02534                     }
02535     
02536                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02537                                                          polyorder, NULL);
02538 
02539                     cpl_vector_delete(flux);
02540                     cpl_vector_delete(positions);
02541 
02542                     if (trend) {
02543                         p = data + j*nsubx;
02544                         for (k = 0; k < nsubx; k++)
02545                             if (p[k] > 1.0)
02546                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02547                         cpl_polynomial_delete(trend);
02548                     }
02549                     else {
02550                         cpl_msg_warning(func, "Invalid flat field flux fit "
02551                                         "(ignored)");
02552                     }
02553                 }
02554             }
02555         }
02556 
02557         
02558         /*
02559          * Recover from the table of spectral curvature coefficients
02560          * the curvature polynomials.
02561          */
02562 
02563         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02564 
02565         start_pixel = refpixel - pixel_below;
02566         if (start_pixel < 0)
02567             start_pixel = 0;
02568 
02569         end_pixel = refpixel + pixel_above;
02570         if (end_pixel > nx)
02571             end_pixel = nx;
02572 
02573         missing_top = 0;
02574         polytop = cpl_polynomial_new(1);
02575         for (k = 0; k <= order; k++) {
02576             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02577             if (null) {
02578                 cpl_polynomial_delete(polytop);
02579                 missing_top = 1;
02580                 break;
02581             }
02582             cpl_polynomial_set_coeff(polytop, &k, coeff);
02583         }
02584 
02585         missing_bot = 0;
02586         polybot = cpl_polynomial_new(1);
02587         for (k = 0; k <= order; k++) {
02588             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02589             if (null) {
02590                 cpl_polynomial_delete(polybot);
02591                 missing_bot = 1;
02592                 break;
02593             }
02594             cpl_polynomial_set_coeff(polybot, &k, coeff);
02595         }
02596 
02597         if (missing_top && missing_bot) {
02598             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02599                           slit_id[i]);
02600             continue;
02601         }
02602 
02603         /*
02604          * In case just one of the two edges was not traced, the other
02605          * edge curvature model is duplicated and shifted to the other
02606          * end of the slit: better than nothing!
02607          */
02608 
02609         if (missing_top) {
02610             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02611                           "the spectral curvature of the lower edge "
02612                           "is used instead.", slit_id[i]);
02613             polytop = cpl_polynomial_duplicate(polybot);
02614             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02615             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02616             k = 0;
02617             coeff = cpl_polynomial_get_coeff(polybot, &k);
02618             coeff += ytop - ybot;
02619             cpl_polynomial_set_coeff(polytop, &k, coeff);
02620         }
02621 
02622         if (missing_bot) {
02623             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02624                           "the spectral curvature of the upper edge "
02625                           "is used instead.", slit_id[i]);
02626             polybot = cpl_polynomial_duplicate(polytop);
02627             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02628             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02629             k = 0;
02630             coeff = cpl_polynomial_get_coeff(polytop, &k);
02631             coeff -= ytop - ybot;
02632             cpl_polynomial_set_coeff(polybot, &k, coeff);
02633         }
02634 
02635 
02636         /*
02637          * Now map smoothed image to CCD.
02638          * Note that the npseudo value related to this slit is equal
02639          * to the number of spatial pseudo-pixels decreased by 1
02640          * (compare with function mos_spatial_calibration()).
02641          */
02642 
02643         nx = cpl_image_get_size_x(flat);
02644         ny = cpl_image_get_size_y(flat);
02645 
02646         sdata = cpl_image_get_data(spatial);
02647         xdata = cpl_image_get_data(exslit);
02648         npseudo = cpl_image_get_size_y(exslit) - 1;
02649 
02650         /*
02651          * Write interpolated smoothed values to CCD image
02652          */
02653 
02654         for (j = start_pixel; j < end_pixel; j++) {
02655             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02656             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02657             for (k = 0; k <= npseudo; k++) {
02658                 ypos = top - k*(top-bot)/npseudo;
02659                 yint = ypos;
02660 
02661                 /*
02662                  * The line:
02663                  *     value = sdata[j + nx*yint];
02664                  * should be equivalent to:
02665                  *     value = npseudo*(top-yint)/(top-bot);
02666                  */
02667 
02668                 if (yint < 0 || yint >= ny-1) {
02669                     yprev = yint;
02670                     continue;
02671                 }
02672 
02673                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02674                 ivalue = value;               /* Nearest spatial pixels:   */
02675                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02676                 if (ivalue < npseudo && ivalue >= 0) {
02677                     vtop = xdata[j + nx*(npseudo-ivalue)];
02678                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02679                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02680 
02681                     if (k) {
02682 
02683                         /*
02684                          * This is added to recover lost pixels on
02685                          * the CCD image (pixels are lost because
02686                          * the CCD pixels are less than npseudo+1).
02687                          */
02688 
02689                         if (yprev - yint > 1) {
02690                             value = sdata[j + nx*(yint+1)];
02691                             ivalue = value;
02692                             fvalue = value - ivalue;
02693                             if (ivalue < npseudo && ivalue >= 0) {
02694                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02695                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02696                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02697                                                        + vbot*fvalue;
02698                             }
02699                         }
02700                     }
02701                 }
02702                 yprev = yint;
02703             }
02704         }
02705         cpl_polynomial_delete(polytop);
02706         cpl_polynomial_delete(polybot);
02707         cpl_image_delete(exslit);
02708     }
02709 
02710     cpl_image_delete(rectified);
02711 
02712     cpl_image_divide(flat, smo_flat);
02713 
02714     return smo_flat;
02715 }
02716 
02717 
02742 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02743                                   int polyorder)
02744 {
02745     const char     *func = "mos_normalise_longflat";
02746 
02747     cpl_image      *smo_flat;
02748     cpl_image      *profile;
02749     cpl_vector     *flux;
02750     cpl_vector     *smo_flux;
02751     cpl_vector     *positions;
02752     cpl_polynomial *trend;
02753 
02754     float          *level;
02755     float          *p;
02756     float          *data;
02757     double         *fdata;
02758     double         *pdata;
02759 
02760     int             nx, ny;
02761     int             npoints;
02762     int             i, j;
02763 
02764 
02765     if (flat == NULL) {
02766         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02767         return NULL;
02768     }
02769  
02770     if (sradius < 1 || dradius < 1) {
02771         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02772         return NULL;
02773     }
02774 
02775     smo_flat = cpl_image_duplicate(flat);
02776 
02777     if (polyorder < 0) {
02778 
02779         /*
02780          * First smooth along the spatial direction
02781          */
02782 
02783         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02784 
02785         nx = cpl_image_get_size_x(smo_flat);
02786         ny = cpl_image_get_size_y(smo_flat);
02787         data = cpl_image_get_data(smo_flat);
02788     
02789         for (i = 0; i < ny; i++) {
02790             flux = cpl_vector_new(nx);
02791             fdata = cpl_vector_get_data(flux);
02792             p = data;
02793             for (j = 0; j < nx; j++)
02794                 *fdata++ = *p++;
02795             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02796             cpl_vector_delete(flux);
02797             fdata = cpl_vector_get_data(smo_flux);
02798             p = data;
02799             for (j = 0; j < nx; j++)
02800                 *p++ = *fdata++;
02801             cpl_vector_delete(smo_flux);
02802             data += nx;
02803         }
02804 
02805         /*
02806          * Second smooth along the dispersion direction
02807          */
02808 
02809         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02810 
02811         nx = cpl_image_get_size_x(smo_flat);
02812         ny = cpl_image_get_size_y(smo_flat);
02813         data = cpl_image_get_data(smo_flat);
02814 
02815         for (i = 0; i < ny; i++) {
02816             flux = cpl_vector_new(nx);
02817             fdata = cpl_vector_get_data(flux);
02818             p = data;
02819             for (j = 0; j < nx; j++)
02820                 *fdata++ = *p++;
02821             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02822             cpl_vector_delete(flux);
02823             fdata = cpl_vector_get_data(smo_flux);
02824             p = data;
02825             for (j = 0; j < nx; j++)
02826                 *p++ = *fdata++;
02827             cpl_vector_delete(smo_flux);
02828             data += nx;
02829         }
02830     }
02831     else {
02832 
02833         /*
02834          * Fit with a polynomial the flat field trend column by column.
02835          */
02836 
02837         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02838 
02839         nx = cpl_image_get_size_x(smo_flat);
02840         ny = cpl_image_get_size_y(smo_flat);
02841         data = cpl_image_get_data(smo_flat);
02842 
02843         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02844         level = cpl_image_get_data(profile);
02845 
02846         for (i = 0; i < ny; i++) {
02847 
02848             /*
02849              * First get the size of the vectors to allocate:
02850              * eliminate from fit any value more than 20% away
02851              * from median level in current column.
02852              */
02853 
02854             npoints = 0;
02855             p = data + i*nx;
02856             for (j = 0; j < nx; j++)
02857                 if (fabs(p[j]/level[i] - 1) < 0.20)
02858                     npoints++;
02859 
02860             if (npoints > polyorder + 1) {
02861 
02862                 /*
02863                  * Fill the vectors for the fitting
02864                  */
02865 
02866                 flux = cpl_vector_new(npoints);
02867                 fdata = cpl_vector_get_data(flux);
02868                 positions = cpl_vector_new(npoints);
02869                 pdata = cpl_vector_get_data(positions);
02870 
02871                 npoints = 0;
02872                 p = data + i*nx;
02873                 for (j = 0; j < nx; j++) {
02874                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02875                         fdata[npoints] = p[j];
02876                         pdata[npoints] = j;
02877                         npoints++;
02878                     }
02879                 }
02880     
02881                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02882                                                      polyorder, NULL);
02883 
02884                 cpl_vector_delete(flux);
02885                 cpl_vector_delete(positions);
02886 
02887                 if (trend) {
02888                     p = data + i*nx;
02889                     for (j = 0; j < nx; j++)
02890                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02891                     cpl_polynomial_delete(trend);
02892                 }
02893                 else {
02894                     cpl_msg_warning(func, 
02895                                     "Invalid flat field flux fit (ignored)");
02896                 }
02897             }
02898         }
02899 
02900         cpl_image_delete(profile);
02901         cpl_image_turn(smo_flat, 1);
02902 
02903     }
02904 
02905     cpl_image_divide(flat, smo_flat);
02906 
02907     return smo_flat;
02908 }
02909 
02910 
02933 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
02934                                               cpl_table *slits, 
02935                                               int order, int global)
02936 {
02937     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02938                                                  /* Max order is 5 */
02939     int nrow = cpl_table_get_nrow(slits);
02940     int i, j;
02941 
02942     
02943     if (order < 0)
02944         return CPL_ERROR_NONE;
02945 
02946     cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
02947     cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
02948 
02949     for (i = 0; i < nrow; i++) {
02950         int        position = cpl_table_get_int   (slits, "position", i, NULL);
02951         int        length   = cpl_table_get_int   (slits, "length",   i, NULL);
02952         double     xtop     = cpl_table_get_double(slits, "xtop",     i, NULL);
02953         double     xbot     = cpl_table_get_double(slits, "xbottom",  i, NULL);
02954         double     ytop     = cpl_table_get_double(slits, "ytop",     i, NULL);
02955         double     ybot     = cpl_table_get_double(slits, "ybottom",  i, NULL);
02956         double     dx       = xtop - xbot;
02957         double     dy       = ytop - ybot;
02958         cpl_table *table    = cpl_table_extract(idscoeff, position, length);
02959 
02960         if (mos_interpolate_wavecalib(table, NULL, 2, order))
02961             continue;
02962 
02963         cpl_table_erase_window(idscoeff, position, length);
02964         cpl_table_insert(idscoeff, table, position);
02965 
02966         cpl_table_delete(table);
02967 
02968         for (j = 0; j < length; j++) {
02969             cpl_table_set_double(idscoeff, "x", j + position,
02970                                  xbot + j*(dx/length));
02971             cpl_table_set_double(idscoeff, "y", j + position,
02972                                  ybot + j*(dy/length));
02973         }
02974     }
02975 
02976     if (global) {
02977 
02978         /*
02979          * Now fit a global solution
02980          */
02981 
02982         nrow = cpl_table_get_nrow(idscoeff);
02983 
02984         for (i = 0; i < 6; i++) {
02985             cpl_table      *dummy;
02986             cpl_vector     *x;
02987             cpl_vector     *y;
02988             cpl_bivector   *z;
02989             cpl_vector     *c;
02990             cpl_polynomial *p;
02991             cpl_vector     *point;
02992             double         *dpoint;
02993             int             npoints;
02994 
02995             if (!cpl_table_has_column(idscoeff, clab[i]))
02996                 break;
02997 
02998             npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
02999             if (npoints < 18)
03000                 break;
03001 
03002             dummy = cpl_table_new(nrow);
03003             cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
03004             cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
03005             cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
03006             cpl_table_erase_invalid(dummy);
03007 
03008             x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
03009             y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
03010             z = cpl_bivector_wrap_vectors(x, y);
03011             c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 
03012                                                                    clab[i]));
03013             p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
03014             cpl_bivector_unwrap_vectors(z);
03015             cpl_vector_unwrap(x);
03016             cpl_vector_unwrap(y);
03017             cpl_vector_unwrap(c);
03018             cpl_table_delete(dummy);
03019 
03020             point  = cpl_vector_new(2);
03021             dpoint = cpl_vector_get_data(point);
03022             for (j = 0; j < nrow; j++) {
03023                 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
03024                 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
03025                 cpl_table_set_double(idscoeff, clab[i], j,
03026                                      cpl_polynomial_eval(p, point));
03027             }
03028             cpl_vector_delete(point);
03029             cpl_polynomial_delete(p);
03030         }
03031     }
03032 
03033     return CPL_ERROR_NONE;
03034 }
03035 
03036 
03062 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
03063                                          cpl_image *wavemap, int mode,
03064                                          int degree)
03065 {
03066     const char *func = "mos_interpolate_wavecalib";
03067 
03068     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
03069                                                  /* Max order is 5 */
03070 
03071     cpl_vector     *wave;
03072     cpl_vector     *positions;
03073     cpl_polynomial *trend;
03074 
03075     float          *p;
03076     float          *data;
03077     double         *wdata;
03078     double         *pdata;
03079 
03080     double          c;
03081     double          mse, ksigma;
03082 
03083     int             order;
03084     int             nrows, first_row, last_row;
03085     int             nx, ny;
03086     int             npoints, rpoints;
03087     int             null;
03088     int             i, j, k;
03089 
03090     int             polyorder = 4;  /* Candidate input argument */
03091 
03092 
03093     if (idscoeff == NULL)
03094         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03095 
03096     if (mode < 0 || mode > 2)
03097         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03098 
03099     if (mode == 0 || degree < 0)
03100         return CPL_ERROR_NONE;
03101 
03102     if (wavemap) {
03103 
03104         /*
03105          * Fit with a polynomial the wavelength trend column by column.
03106          */
03107 
03108         cpl_image_turn(wavemap, -1);   /* For faster memory access */
03109 
03110         nx = cpl_image_get_size_x(wavemap);
03111         ny = cpl_image_get_size_y(wavemap);
03112         data = cpl_image_get_data(wavemap);
03113 
03114         for (i = 0; i < ny; i++) {
03115 
03116             /*
03117              * First get the size of the vectors to allocate:
03118              * eliminate from fit any value with "impossible" wavelength.
03119              */
03120 
03121             npoints = 0;
03122             p = data + i*nx;
03123             for (j = 0; j < nx; j++)
03124                 if (p[j] > 1.0)
03125                     npoints++;
03126 
03127             if (npoints > polyorder + 1) {
03128 
03129                 /*
03130                  * Fill the vectors for the fitting
03131                  */
03132 
03133                 wave = cpl_vector_new(npoints);
03134                 wdata = cpl_vector_get_data(wave);
03135                 positions = cpl_vector_new(npoints);
03136                 pdata = cpl_vector_get_data(positions);
03137 
03138                 npoints = 0;
03139                 p = data + i*nx;
03140                 for (j = 0; j < nx; j++) {
03141                     if (p[j] > 1.0) {
03142                         wdata[npoints] = p[j];
03143                         pdata[npoints] = j;
03144                         npoints++;
03145                     }
03146                 }
03147     
03148                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03149                                                      polyorder, &mse);
03150 
03151                 ksigma = 3*sqrt(mse);
03152 
03153                 cpl_vector_delete(wave);
03154                 cpl_vector_delete(positions);
03155 
03156                 if (trend) {
03157 
03158                     /*
03159                      * Apply 3-sigma rejection
03160                      */
03161 
03162                     rpoints = 0;
03163                     p = data + i*nx;
03164                     for (j = 0; j < nx; j++)
03165                         if (p[j] > 1.0)
03166                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03167                                                     - p[j]) < ksigma)
03168                                 rpoints++;
03169 
03170                     if (rpoints < npoints && rpoints > polyorder + 1) {
03171 
03172                         wave = cpl_vector_new(rpoints);
03173                         wdata = cpl_vector_get_data(wave);
03174                         positions = cpl_vector_new(rpoints);
03175                         pdata = cpl_vector_get_data(positions);
03176 
03177                         npoints = 0;
03178                         p = data + i*nx;
03179                         for (j = 0; j < nx; j++) {
03180                             if (p[j] > 1.0) {
03181                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03182                                                                 j, NULL) - p[j])
03183                                                                 < ksigma) {
03184                                     wdata[npoints] = p[j];
03185                                     pdata[npoints] = j;
03186                                     npoints++;
03187                                 }
03188                             }
03189                         }
03190         
03191                         cpl_polynomial_delete(trend);
03192                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03193                                                              polyorder, NULL);
03194 
03195                         cpl_vector_delete(wave);
03196                         cpl_vector_delete(positions);
03197                     }
03198                 }
03199 
03200                 if (trend) {
03201                     p = data + i*nx;
03202                     if (mode == 1) {
03203                         for (j = 0; j < nx; j++)
03204                             if (p[j] < 1.0)
03205                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03206                     }
03207                     else if (mode == 2) {
03208                         for (j = 0; j < nx; j++)
03209                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03210                     }
03211                     cpl_polynomial_delete(trend);
03212                 }
03213                 else {
03214                     cpl_msg_warning(func, 
03215                                     "Invalid wavelength field fit (ignored)");
03216                 }
03217             }
03218     
03219         }
03220 
03221         cpl_image_turn(wavemap, 1);
03222 
03223     }
03224 
03225 
03226     /*
03227      * Interpolating the IDS coefficients
03228      */
03229 
03230     nrows = cpl_table_get_nrow(idscoeff);
03231 
03232     order = 0;
03233     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03234         ++order;
03235     --order;
03236 
03237     if (degree == 0) {
03238         for (k = 0; k <= order; k++) {
03239             double m;
03240             if (cpl_table_has_column(idscoeff, clab[k])) {
03241                 m = cpl_table_get_column_median(idscoeff, clab[k]);
03242                 cpl_table_fill_column_window_double(idscoeff, clab[k], 
03243                                                     0, nrows, m);
03244             }
03245         }
03246 
03247         return CPL_ERROR_NONE;
03248     }
03249 
03250     first_row = 0;
03251     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03252         first_row++;
03253 
03254     last_row = nrows - 1;
03255     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03256         last_row--;
03257 
03258     for (k = 0; k <= order; k++) {
03259 
03260         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03261         wave = cpl_vector_new(npoints);
03262         wdata = cpl_vector_get_data(wave);
03263         positions = cpl_vector_new(npoints);
03264         pdata = cpl_vector_get_data(positions);
03265 
03266         npoints = 0;
03267         for (i = first_row; i <= last_row; i++) {
03268             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03269             if (null == 0) {
03270                 wdata[npoints] = c;
03271                 pdata[npoints] = i;
03272                 npoints++;
03273             }
03274         }
03275 
03276 // This doesn't seem to provide good results, I have not understood why.
03277 // Restore for robust linear fitting.
03278 //
03279         if (degree == 1) {
03280             cpl_vector   *p;
03281             cpl_vector   *w;
03282             cpl_bivector *list;
03283             double        q, m;
03284 
03285             if (npoints > 4) {
03286                 p = cpl_vector_extract(positions, 2, npoints - 2, 1);
03287                 w = cpl_vector_extract(wave, 2, npoints - 2, 1);
03288             }
03289             else {
03290                 p = positions;
03291                 w = wave;
03292             }
03293 
03294             list = cpl_bivector_wrap_vectors(p, w);
03295 
03296             robustLinearFit(list, &q, &m, &mse);
03297             cpl_bivector_unwrap_vectors(list);
03298             for (i = first_row; i <= last_row; i++)
03299                  cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
03300 
03301             if (npoints > 4) {
03302                 cpl_vector_delete(p);
03303                 cpl_vector_delete(w);
03304             }
03305 
03306             continue;
03307         }
03308 
03309 // End robust linear fitting
03310 
03311         trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
03312 
03313         ksigma = 3*sqrt(mse);
03314 
03315         cpl_vector_delete(wave);
03316         cpl_vector_delete(positions);
03317 
03318         /*
03319          * Iteration
03320          */
03321 
03322         if (trend) {
03323             rpoints = 0;
03324             for (i = first_row; i <= last_row; i++) {
03325                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03326                 if (null == 0) {
03327                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03328                                                                  < ksigma) {
03329                         rpoints++;
03330                     }
03331                 }
03332             }
03333 
03334             if (rpoints > 0 && rpoints < npoints) {
03335                 cpl_msg_debug(func, "%d points rejected from "
03336                               "wavelength calibration fit", 
03337                               npoints - rpoints);
03338 
03339                 wave = cpl_vector_new(rpoints);
03340                 wdata = cpl_vector_get_data(wave);
03341                 positions = cpl_vector_new(rpoints);
03342                 pdata = cpl_vector_get_data(positions);
03343 
03344                 npoints = 0;
03345                 for (i = first_row; i <= last_row; i++) {
03346                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03347                     if (null == 0) {
03348                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03349                                                                  < ksigma) {
03350                             wdata[npoints] = c;
03351                             pdata[npoints] = i;
03352                             npoints++;
03353                         }
03354                     }
03355                 }
03356 
03357                 if (npoints) {
03358                     cpl_polynomial_delete(trend);
03359                     trend = cpl_polynomial_fit_1d_create(positions, 
03360                                                          wave, degree, NULL);
03361                 }
03362 
03363                 cpl_vector_delete(wave);
03364                 cpl_vector_delete(positions);
03365 
03366             }
03367         }
03368 
03369         if (trend) {
03370             for (i = first_row; i <= last_row; i++) {
03371                 if (mode == 1) {
03372                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03373                         cpl_table_set_double(idscoeff, clab[k], i, 
03374                                              cpl_polynomial_eval_1d(trend, i,
03375                                                                     NULL));
03376                     }
03377                 }
03378                 else if (mode == 2) {
03379                     cpl_table_set_double(idscoeff, clab[k], i, 
03380                                      cpl_polynomial_eval_1d(trend, i, NULL));
03381                 }
03382             }
03383             cpl_polynomial_delete(trend);
03384         }
03385         else {
03386             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03387         }
03388 
03389     }
03390 
03391     return CPL_ERROR_NONE;
03392 }
03393 
03394 
03395 
03421 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03422                            cpl_table *overscans)
03423 {
03424     const char *func = "mos_remove_bias";
03425 
03426     cpl_image *unbiased;
03427     cpl_image *overscan;
03428     double     mean_bias_level;
03429     double     mean_overscans_level;
03430     int        count;
03431     int        nrows;
03432     int        xlow, ylow, xhig, yhig;
03433     int        i;
03434 
03435 
03436     if (image == NULL || overscans == NULL) {
03437         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03438         return NULL;
03439     }
03440 
03441     nrows = cpl_table_get_nrow(overscans);
03442 
03443     if (nrows == 0) {
03444         cpl_msg_error(func, "Empty overscan table");
03445         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03446         return NULL;
03447     }
03448 
03449     if (bias) {
03450         if (nrows == 1) {
03451             unbiased = cpl_image_subtract_create(image, bias);
03452             if (unbiased == NULL) {
03453                 cpl_msg_error(func, "Incompatible master bias");
03454                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03455             }
03456             return unbiased;
03457         }
03458         mean_bias_level = cpl_image_get_mean(bias);
03459     }
03460     else {
03461         if (nrows == 1) {
03462             cpl_msg_error(func, "No master bias in input, and no overscan "
03463                           "regions in input image: bias subtraction "
03464                           "cannot be performed!");
03465             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03466             return NULL;
03467         }
03468         mean_bias_level = 0.0;
03469     }
03470 
03471     mean_overscans_level = 0.0;
03472     count = 0;
03473     for (i = 0; i < nrows; i++) {
03474         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03475         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03476         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03477         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03478 
03479         if (i == 0) {
03480             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03481             if (unbiased == NULL) {
03482                 cpl_msg_error(func, "Incompatible overscan table");
03483                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03484                 return NULL;
03485             }
03486             if (bias) {
03487                 if (cpl_image_subtract(unbiased, bias)) {
03488                     cpl_msg_error(func, "Incompatible master bias");
03489                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03490                     cpl_image_delete(unbiased);
03491                     return NULL;
03492                 }
03493             }
03494         }
03495         else {
03496             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03497             if (overscan == NULL) {
03498                 cpl_msg_error(func, "Incompatible overscan table");
03499                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03500                 cpl_image_delete(unbiased);
03501                 return NULL;
03502             }
03503 
03504             mean_overscans_level += cpl_image_get_median(overscan);
03505             count++;
03506 
03507 /***
03508  * Here the mean level was used: not very robust...
03509 
03510             mean_overscans_level += cpl_image_get_flux(overscan);
03511             count += cpl_image_get_size_x(overscan)
03512                    * cpl_image_get_size_y(overscan);
03513 ***/
03514             cpl_image_delete(overscan);
03515         }
03516     }
03517 
03518     /*
03519      * Overscan correction
03520      */
03521 
03522     mean_overscans_level /= count;
03523 
03524     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03525 
03526     cpl_msg_info(cpl_func, 
03527                  "Ratio between mean overscans level and mean bias level: %.2f",
03528                   mean_overscans_level / mean_bias_level);
03529 
03530     return unbiased;
03531 
03532 }
03533 
03534 
03593 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03594                                      int length, int msize, int fsize) 
03595 {
03596     const char *func = "mos_arc_background_1D";
03597 
03598     float  *minf;
03599     float  *maxf;
03600     float  *smof;
03601     int     i;
03602 
03603 
03604     if (spectrum == NULL || back == NULL)
03605         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03606 
03607     if (msize % 2 == 0)
03608         msize++;
03609 
03610     if (fsize % 2 == 0)
03611         fsize++;
03612 
03613     if (msize < 3 || fsize < msize || length < 2*fsize)
03614         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03615 
03616 
03617     minf = min_filter(spectrum, length, msize);
03618     smof = smo_filter(minf, length, fsize);
03619     cpl_free(minf);
03620     maxf = max_filter(smof, length, 2*msize+1);
03621     cpl_free(smof);
03622     smof = smo_filter(maxf, length, 2*fsize+1);
03623     cpl_free(maxf);
03624     minf = min_filter(smof, length, 2*msize+1);
03625     cpl_free(smof);
03626     smof = smo_filter(minf, length, 2*fsize+1);
03627     cpl_free(minf);
03628 
03629     for (i = 0; i < length; i++)
03630         back[i] = smof[i];
03631 
03632     cpl_free(smof);
03633 
03634     return CPL_ERROR_NONE;
03635 
03636 }
03637 
03638 
03695 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03696 {
03697     const char *func = "mos_arc_background";
03698 
03699     cpl_image  *fimage;
03700     cpl_image  *bimage;
03701     cpl_matrix *kernel;
03702     float      *data;
03703     float      *bdata;
03704     float      *row;
03705     float      *brow;
03706     int         nx, ny;
03707     int         i;
03708 
03709 
03710     if (image == NULL) {
03711         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03712         return NULL;
03713     }
03714 
03715     if (msize % 2 == 0)
03716         msize++;
03717 
03718     if (fsize % 2 == 0)
03719         fsize++;
03720 
03721     nx = cpl_image_get_size_x(image);
03722     ny = cpl_image_get_size_y(image);
03723 
03724     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03725 
03726     fimage = mos_image_filter_median(image, 3, 3);
03727 
03728     data = cpl_image_get_data_float(fimage);
03729     bdata = cpl_image_get_data_float(bimage);
03730 
03731     for (i = 0; i < ny; i++) {
03732         row = data + i * nx;
03733         brow = bdata + i * nx;
03734         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03735             cpl_error_set_where(func); 
03736             cpl_image_delete(fimage);
03737             cpl_image_delete(bimage);
03738             return NULL;
03739         }
03740     }
03741 
03742     cpl_image_delete(fimage);
03743 
03744     return bimage;
03745 }
03746 
03747 
03768 int mos_lines_width(const float *spectrum, int length)
03769 {
03770 
03771   const char *func = "mos_lines_width";
03772 
03773   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03774   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03775 
03776   double  norm, value, max;
03777   int     radius = 20;
03778   int     short_length = length - 2*radius - 1;
03779   int     width;
03780   int     i, j, k;
03781 
03782 
03783   /*
03784    * Derivative, and separation of positive and negative derivatives
03785    */
03786 
03787   for (j = 0, i = 1; i < length; j++, i++) {
03788       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03789       if (profile1[j] < 0)
03790           profile1[j] = 0;
03791       if (profile2[j] > 0)
03792           profile2[j] = 0;
03793       else
03794           profile2[j] = -profile2[j];
03795   }
03796 
03797 
03798   /*
03799    * Profiles normalisation
03800    */
03801 
03802   length--;
03803 
03804   norm = 0;
03805   for (i = 0; i < length; i++)
03806       if (norm < profile1[i])
03807           norm = profile1[i];
03808 
03809   for (i = 0; i < length; i++) {
03810       profile1[i] /= norm;
03811       profile2[i] /= norm;
03812   }
03813 
03814 
03815   /*
03816    * Cross-correlation
03817    */
03818 
03819   max = -1;
03820   for (i = 0; i <= radius; i++) {
03821       value = 0;
03822       for (j = 0; j < short_length; j++) {
03823           k = radius+j;
03824           value += profile1[k] * profile2[k+i];
03825       }
03826       if (max < value) {
03827           max = value;
03828           width = i;
03829       }
03830   }
03831 
03832   cpl_free(profile1);
03833   cpl_free(profile2);
03834 
03835   if (max < 0.0) {
03836       cpl_msg_debug(func, "Cannot estimate line width");
03837       width = 1;
03838   }
03839 
03840   return width;
03841 
03842 }
03843 
03844 
03871 cpl_vector *mos_peak_candidates(const float *spectrum, 
03872                                 int length, float level, 
03873                                 float exp_width)
03874 { 
03875 
03876   const char *func = "mos_peak_candidates";
03877 
03878   int     i, j;
03879   int     nint   = length - 1;
03880   int     n      = 0;
03881   int     width  = 2 * ceil(exp_width / 2) + 1;
03882   int     start  = width / 2;
03883   int     end    = length - width / 2;
03884   int     step;
03885   float  *smo;
03886   double *data   = cpl_calloc(length/2, sizeof(double));
03887 
03888 
03889   if (spectrum == NULL) {
03890       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03891       return NULL;
03892   }
03893 
03894 
03895   /*
03896    * If lines have a flat top (as in the case of broad slit), smooth
03897    * before determining the max.
03898    */
03899 
03900   if (width > 7) {
03901     smo = cpl_calloc(length, sizeof(float));
03902     start = width / 2;
03903     end = length - width / 2;
03904     for (i = 0; i < start; i++)
03905       smo[i] = spectrum[i];
03906     for (i = start; i < end; i++) {
03907       for (j = i - start; j <= i + start; j++)
03908         smo[i] += spectrum[j];
03909       smo[i] /= width;
03910     }
03911     for (i = end; i < length; i++)
03912       smo[i] = spectrum[i];
03913   }
03914   else {
03915       smo = (float *)spectrum;
03916   }
03917 
03918   /*
03919    * Collect all relative maxima along spectrum, that are higher than the
03920    * specified level.
03921    */
03922 
03923   if (width > 20)
03924     step = width / 2;
03925   else
03926     step = 1;
03927 
03928   for (i = step; i < nint - step + 1; i += step) {
03929     if (smo[i] > level) {
03930       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03931         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03932           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03933           ++n;
03934         }
03935       }
03936     }
03937   }
03938 
03939   if (width > 7) {
03940     cpl_free(smo);
03941   }
03942 
03943   if (n == 0) {
03944     cpl_free(data);
03945     return NULL;
03946   }
03947 
03948   return cpl_vector_wrap(n, data);
03949 
03950 }
03951 
03952 
03974 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
03975                              cpl_vector *peaks, int sradius)
03976 {
03977 
03978     const char *func = "mos_refine_peaks";
03979 
03980     double *data;
03981     float   pos;
03982     int     npeaks;
03983     int     startPos, endPos;
03984     int     window = 2*sradius+1;
03985     int     i, j;
03986 
03987 
03988     if (peaks == NULL || spectrum == NULL) {
03989         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03990         return NULL;
03991     }
03992 
03993     npeaks = cpl_vector_get_size(peaks);
03994     data = cpl_vector_unwrap(peaks);
03995 
03996     for (i = 0; i < npeaks; i++) {
03997         startPos = data[i] - window/2;
03998         endPos   = startPos + window;
03999         if (startPos < 0 || endPos >= length)
04000             continue;
04001 
04002         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
04003             pos += startPos;
04004             data[i] = pos;
04005         }
04006     }
04007 
04008     for (i = 1; i < npeaks; i++)
04009         if (data[i] - data[i-1] < 0.5)
04010             data[i-1] = -1.0;
04011 
04012     for (i = 0, j = 0; i < npeaks; i++) {
04013         if (data[i] > 0.0) {
04014             if (i != j)
04015                 data[j] = data[i];
04016             j++;
04017         }
04018     }
04019 
04020     return cpl_vector_wrap(j, data);
04021 
04022 }
04023 
04024 
04025 void mos_set_multiplex(int multiplex)
04026 {
04027     mos_multiplex = multiplex;
04028 }
04029 
04083 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
04084                                  double min_disp, double max_disp,
04085                                  double tolerance)
04086 {
04087 
04088   int      i, j, k, l;
04089   int      nlint, npint;
04090   int      minpos;
04091   float    min;
04092   double   lratio, pratio;
04093   double   lo_start, lo_end, hi_start, hi_end, denom;
04094   double   disp, variation, prev_variation;
04095   int      max, maxpos, minl, mink;
04096   int      ambiguous;
04097   int      npeaks_lo, npeaks_hi;
04098   int     *peak_lo;
04099   int     *peak_hi;
04100   int    **ident;
04101   int     *nident;
04102   int     *lident;
04103 
04104   double  *peak;
04105   double  *line;
04106   int      npeaks, nlines;
04107 
04108   double  *xpos;
04109   double  *lambda;
04110   int     *ilambda;
04111   double  *tmp_xpos;
04112   double  *tmp_lambda;
04113   int     *tmp_ilambda;
04114   int     *flag;
04115   int      n = 0;
04116   int      nn;
04117   int      nseq = 0;
04118   int      gap;
04119   int     *seq_length;
04120   int      found;
04121 
04122   peak        = cpl_vector_get_data(peaks);
04123   npeaks      = cpl_vector_get_size(peaks);
04124   line        = cpl_vector_get_data(lines);
04125   nlines      = cpl_vector_get_size(lines);
04126 
04127   if (npeaks < 4)
04128       return NULL;
04129 
04130   peak_lo     = cpl_malloc(npeaks * sizeof(int));
04131   peak_hi     = cpl_malloc(npeaks * sizeof(int));
04132   nident      = cpl_calloc(npeaks, sizeof(int));
04133   lident      = cpl_calloc(nlines, sizeof(int));
04134   xpos        = cpl_calloc(npeaks, sizeof(double));
04135   lambda      = cpl_calloc(npeaks, sizeof(double));
04136   ilambda     = cpl_calloc(npeaks, sizeof(int));
04137   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
04138   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
04139   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
04140   flag        = cpl_calloc(npeaks, sizeof(int));
04141   seq_length  = cpl_calloc(npeaks, sizeof(int));
04142   ident       = cpl_malloc(npeaks * sizeof(int *));
04143   for (i = 0; i < npeaks; i++)
04144     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
04145 
04146   /*
04147    * This is just the number of intervals - one less than the number
04148    * of points (catalog wavelengths, or detected peaks).
04149    */
04150 
04151   nlint = nlines - 1;
04152   npint = npeaks - 1;
04153 
04154 
04155   /*
04156    * Here the big loops on catalog lines begins.
04157    */
04158 
04159   for (i = 1; i < nlint; i++) {
04160 
04161 
04162     /*
04163      * For each catalog wavelength I take the previous and the next one, 
04164      * and compute the ratio of the corresponding wavelength intervals.
04165      * This ratio will be compared to all the ratios obtained doing the
04166      * same with all the detected peaks positions.
04167      */
04168 
04169     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
04170 
04171 
04172     /*
04173      * Here the loop on detected peaks positions begins.
04174      */
04175 
04176     for (j = 1; j < npint; j++) {
04177 
04178       /*
04179        * Not all peaks are used for computing ratios: just the ones
04180        * that are compatible with the expected spectral dispersion
04181        * are taken into consideration. Therefore, I define the pixel
04182        * intervals before and after any peak that are compatible with
04183        * the specified dispersion interval, and select just the peaks
04184        * within such intervals. If either of the two intervals doesn't
04185        * contain any peak, then I skip the current peak and continue
04186        * with the next.
04187        */
04188 
04189       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
04190       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
04191       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
04192       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
04193 
04194       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04195         if (peak[k] > lo_end)
04196           break;
04197         if (peak[k] > lo_start) {
04198           peak_lo[npeaks_lo] = k;
04199           ++npeaks_lo;
04200         }
04201       }
04202 
04203       if (npeaks_lo == 0)
04204         continue;
04205 
04206       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04207         if (peak[k] > hi_end)
04208           break;
04209         if (peak[k] > hi_start) {
04210           peak_hi[npeaks_hi] = k;
04211           ++npeaks_hi;
04212         }
04213       }
04214 
04215       if (npeaks_hi == 0)
04216         continue;
04217 
04218 
04219       /*
04220        * Now I have all peaks that may help for a local identification.
04221        * peak_lo[k] is the sequence number of the k-th peak of the lower
04222        * interval; peak_hi[l] is the sequence number of the l-th peak of
04223        * the higher interval. j is, of course, the sequence number of the
04224        * current peak (second big loop).
04225        */
04226 
04227       prev_variation = 1000.0;
04228       minl = mink = 0;
04229 
04230       for (k = 0; k < npeaks_lo; k++) {
04231         denom = peak[j] - peak[peak_lo[k]];
04232         for (l = 0; l < npeaks_hi; l++) {
04233 
04234           /*
04235            * For any pair of peaks - one from the lower and the other
04236            * from the higher interval - I compute the same ratio that
04237            * was computed with the current line catalog wavelength.
04238            */
04239 
04240           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04241 
04242           /*
04243            * If the two ratios are compatible within the specified
04244            * tolerance, we have a preliminary identification. This
04245            * will be marked in the matrix ident[][], where the first
04246            * index corresponds to a peak sequence number, and the second
04247            * index is the counter of the identifications made during
04248            * this whole process. The array of counters is nident[].
04249            * If more than one interval pair fulfills the specified
04250            * tolerance, the closest to the expected ratio is selected.
04251            */
04252 
04253           variation = fabs(lratio-pratio) / pratio;
04254 
04255           if (variation < tolerance) {
04256             if (variation < prev_variation) {
04257               prev_variation = variation;
04258               minl = l;
04259               mink = k;
04260             }
04261           }
04262         }
04263       }
04264       if (prev_variation < tolerance) {
04265         ident[j][nident[j]]                         = i;
04266         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04267         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04268         ++nident[j];
04269         ++nident[peak_hi[minl]];
04270         ++nident[peak_lo[mink]];
04271       }
04272     }   /* End loop on positions */
04273   }    /* End loop on lines     */
04274 
04275 
04276   /*
04277    * At this point I have filled the ident matrix with all my preliminary
04278    * identifications. Ambiguous identifications must be eliminated.
04279    */
04280 
04281 
04282   for (i = 0; i < npeaks; i++) {
04283 
04284 
04285     /*
04286      * I don't take into consideration peaks that were never identified.
04287      * They are likely contaminations, or emission lines that were not
04288      * listed in the input wavelength catalog.
04289      */
04290 
04291     if (nident[i] > 1) {
04292 
04293 
04294       /*
04295        * Initialise the histogram of wavelengths assigned to the i-th peak.
04296        */
04297 
04298       for (j = 0; j < nlines; j++)
04299         lident[j] = 0;
04300 
04301 
04302       /*
04303        * Count how many times each catalog wavelength was assigned
04304        * to the i-th peak.
04305        */
04306 
04307       for (j = 0; j < nident[i]; j++)
04308         ++lident[ident[i][j]];
04309 
04310 
04311       /*
04312        * What wavelength was most frequently assigned to the i-th peak?
04313        */
04314 
04315       max = 0;
04316       maxpos = 0;
04317       for (j = 0; j < nlines; j++) {
04318         if (max < lident[j]) {
04319           max = lident[j];
04320           maxpos = j;
04321         }
04322       }
04323 
04324 
04325       /*
04326        * Were there other wavelengths assigned with the same frequency?
04327        * This would be the case of an ambiguous identification. It is
04328        * safer to reject this peak...
04329        */
04330 
04331       ambiguous = 0;
04332 
04333       for (k = maxpos + 1; k < nlines; k++) {
04334         if (lident[k] == max) {
04335           ambiguous = 1;
04336           break;
04337         }
04338       }
04339 
04340       if (ambiguous)
04341         continue;
04342 
04343 
04344       /*
04345        * Otherwise, I assign to the i-th peak the wavelength that was
04346        * most often assigned to it.
04347        */
04348 
04349       tmp_xpos[n]   = peak[i];
04350       tmp_lambda[n] = line[maxpos];
04351       tmp_ilambda[n] = maxpos;
04352 
04353       ++n;
04354 
04355     }
04356 
04357   }
04358 
04359 
04360   /*
04361    * Check on identified peaks. Contaminations from other spectra might 
04362    * be present and should be excluded: this type of contamination 
04363    * consists of peaks that have been _correctly_ identified! The non-
04364    * spectral type of light contamination should have been almost all 
04365    * removed already in the previous steps, but it may still be present.
04366    * Here, the self-consistent sequences of identified peaks are
04367    * separated one from the other. At the moment, just the longest of
04368    * such sequences is selected (in other words, spectral multiplexing
04369    * is ignored).
04370    */
04371 
04372   if (n > 1) {
04373     nn = 0;                  /* Number of peaks in the list of sequences */
04374     nseq = 0;                /* Current sequence */
04375     for (k = 0; k < n; k++) {
04376       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04377         flag[k] = 1;
04378         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04379         lambda[nn] = tmp_lambda[k];
04380         ilambda[nn] = tmp_ilambda[k];
04381         ++seq_length[nseq];
04382         ++nn;
04383 
04384         /*
04385          * Now look for all the following peaks that are compatible
04386          * with the expected spectral dispersion, and add them in 
04387          * sequence to xpos. Note that missing peaks are not a problem...
04388          */
04389          
04390         i = k;
04391         while (i < n - 1) {
04392           found = 0;
04393           for (j = i + 1; j < n; j++) {
04394             if (flag[j] == 0) {
04395               disp = (tmp_lambda[j] - tmp_lambda[i])
04396                    / (tmp_xpos[j] - tmp_xpos[i]);
04397               if (disp >= min_disp && disp <= max_disp) {
04398                 flag[j] = 1;
04399                 xpos[nn] = tmp_xpos[j];
04400                 lambda[nn] = tmp_lambda[j];
04401                 ilambda[nn] = tmp_ilambda[j];
04402                 ++seq_length[nseq];
04403                 ++nn;
04404                 i = j;
04405                 found = 1;
04406                 break;
04407               }
04408             }
04409           }
04410           if (!found)
04411             break;
04412         }
04413 
04414         /*
04415          * Current sequence is completed: begin new sequence on the
04416          * excluded peaks, starting the loop on peaks again.
04417          */
04418 
04419         ++nseq;
04420         k = 0;
04421       }
04422     }
04423 
04424 
04425     /*
04426      * Find the longest sequence of self-consistent peaks.
04427      */
04428 
04429     maxpos = max = 0;
04430 
04431     if (mos_multiplex < 0) {
04432       for (i = 0; i < nseq; i++) {
04433         if (seq_length[i] > max) {
04434           max = seq_length[i];
04435           maxpos = i;
04436         }
04437       }
04438     }
04439     else {
04440 
04441       /*
04442        * Now consider the sequence which lays in the specified 
04443        * CCD region (indicated by mos_multiplex): that is, _most_ 
04444        * of its lines (more than half) must be in that region...
04445        */
04446 
04447       nn = 0;
04448       found = 0;
04449 
04450       for (i = 0; i < nseq; i++) {
04451         n = seq_length[i];
04452         if (n > 5) {
04453           cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
04454           int        region;
04455 
04456           for (j = 0; j < n; j++)
04457             cpl_array_set_int(regions, j, 
04458                               ((int)floor(xpos[nn + j])) / mos_region_size);
04459 
04460           region = (int)cpl_array_get_median(regions);
04461           cpl_array_delete(regions);
04462 
04463           if (mos_multiplex == region) {
04464             if (found) {
04465               cpl_msg_debug(cpl_func, "More than one spectrum found in "
04466                             "region %d (only the first one is extracted)", 
04467                             mos_multiplex);
04468               break;
04469             }
04470             found = 1;
04471             max = seq_length[i];
04472             maxpos = i;
04473           }
04474         }
04475         nn += seq_length[i];
04476       }
04477     }
04478 
04479     /*
04480      * Find where this sequence starts in the whole peak position
04481      * storage.
04482      */
04483 
04484     nn = 0;
04485     for (i = 0; i < maxpos; i++)
04486       nn += seq_length[i];
04487 
04488     /*
04489      * Move the longest sequence at the beginning of the returned lists
04490      */
04491 
04492     n = max;
04493     for (i = 0; i < n; i++, nn++) {
04494       xpos[i] = xpos[nn];
04495       lambda[i] = lambda[nn];
04496       ilambda[i] = ilambda[nn];
04497     }
04498 
04499 
04500     /*
04501      * Are some wavelengths missing? Recover them.
04502      */
04503 
04504     for (i = 1; i < n; i++) {
04505       gap = ilambda[i] - ilambda[i-1];
04506       for (j = 1; j < gap; j++) {
04507 
04508         if (j == 1) {
04509 
04510           /*
04511            * Determine the local dispersion from the current pair of peaks
04512            */
04513   
04514           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04515         }
04516 
04517         /*
04518          * With this, find the expected position of the missing
04519          * peak by linear interpolation.
04520          */
04521 
04522         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04523 
04524         /*
04525          * Is there a peak at that position? Here a peak from the
04526          * original list is searched, that is closer than 2 pixels
04527          * to the expected position. If it is found, insert it at
04528          * the current position on the list of identified peaks,
04529          * and leave immediately the loop (taking the new position
04530          * for the following linear interpolation, in case more
04531          * than one peak is missing in the current interval).
04532          * If it is not found, stay in the loop, looking for 
04533          * the following missing peaks in this interval.
04534          */
04535 
04536         found = 0;
04537         for (k = 0; k < npeaks; k++) {
04538           if (fabs(peak[k] - hi_start) < 2) {
04539             for (l = n; l > i; l--) {
04540               xpos[l] = xpos[l-1];
04541               lambda[l] = lambda[l-1];
04542               ilambda[l] = ilambda[l-1];
04543             }
04544             xpos[i] = peak[k];
04545             lambda[i] = line[ilambda[i-1] + j];
04546             ilambda[i] = ilambda[i-1] + j;
04547             ++n;
04548             found = 1;
04549             break;
04550           }
04551         }
04552         if (found)
04553           break;
04554       }
04555     }
04556 
04557 
04558     /*
04559      * Try to extrapolate forward
04560      */
04561 
04562     found = 1;
04563     while (ilambda[n-1] < nlines - 1 && found) {
04564 
04565       /*
04566        * Determine the local dispersion from the last pair of 
04567        * identified peaks
04568        */
04569 
04570       if (n > 1)
04571           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04572       else
04573           disp = 0.0;
04574 
04575       if (disp > max_disp || disp < min_disp)
04576         break;
04577 
04578 
04579       /*
04580        * With this, find the expected position of the missing
04581        * peak by linear interpolation.
04582        */
04583 
04584       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04585 
04586       /*
04587        * Is there a peak at that position? Here a peak from the
04588        * original list is searched, that is closer than 6 pixels
04589        * to the expected position. If it is found, insert it at
04590        * the end of the list of identified peaks. If it is not
04591        * found, leave the loop.
04592        */
04593 
04594       found = 0;
04595       min = fabs(peak[0] - hi_start);
04596       minpos = 0;
04597       for (k = 1; k < npeaks; k++) {
04598         if (min > fabs(peak[k] - hi_start)) {
04599             min = fabs(peak[k] - hi_start);
04600             minpos = k;
04601         }
04602       }
04603       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04604         xpos[n] = peak[minpos];
04605         lambda[n] = line[ilambda[n-1] + 1];
04606         ilambda[n] = ilambda[n-1] + 1;
04607         ++n;
04608         found = 1;
04609       }
04610     }
04611 
04612 
04613     /*
04614      * Try to extrapolate backward
04615      */
04616 
04617     found = 1;
04618     while (ilambda[0] > 0 && found) {
04619 
04620       /*
04621        * Determine the local dispersion from the first pair of
04622        * identified peaks
04623        */
04624 
04625       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04626 
04627       if (disp > max_disp || disp < min_disp)
04628         break;
04629 
04630 
04631       /*
04632        * With this, find the expected position of the missing
04633        * peak by linear interpolation.
04634        */
04635 
04636       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04637 
04638 
04639       /*
04640        * Is there a peak at that position? Here a peak from the
04641        * original list is searched, that is closer than 6 pixels
04642        * to the expected position. If it is found, insert it at
04643        * the beginning of the list of identified peaks. If it is not
04644        * found, leave the loop.
04645        */
04646 
04647       found = 0;
04648       min = fabs(peak[0] - hi_start);
04649       minpos = 0;
04650       for (k = 1; k < npeaks; k++) {
04651         if (min > fabs(peak[k] - hi_start)) {
04652             min = fabs(peak[k] - hi_start);
04653             minpos = k;
04654         }
04655       }
04656       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04657         for (j = n; j > 0; j--) {
04658           xpos[j] = xpos[j-1];
04659           lambda[j] = lambda[j-1];
04660           ilambda[j] = ilambda[j-1];
04661         }
04662         xpos[0] = peak[minpos];
04663         lambda[0] = line[ilambda[0] - 1];
04664         ilambda[0] = ilambda[0] - 1;
04665         ++n;
04666         found = 1;
04667       }
04668     }
04669   }
04670 
04671 
04672   /*
04673    * At this point all peaks are processed. Free memory, and return
04674    * the result.
04675    */
04676 
04677 /************************************************+
04678   for (i = 0; i < npeaks; i++) {
04679     printf("Peak %d:\n   ", i);
04680     for (j = 0; j < nident[i]; j++)
04681       printf("%.2f, ", line[ident[i][j]]);
04682     printf("\n");
04683   }
04684 
04685   printf("\n");
04686 
04687   for (i = 0; i < n; i++)
04688     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04689 +************************************************/
04690   for (i = 0; i < npeaks; i++)
04691     cpl_free(ident[i]);
04692   cpl_free(ident);
04693   cpl_free(nident);
04694   cpl_free(lident);
04695   cpl_free(ilambda);
04696   cpl_free(tmp_xpos);
04697   cpl_free(tmp_lambda);
04698   cpl_free(tmp_ilambda);
04699   cpl_free(peak_lo);
04700   cpl_free(flag);
04701   cpl_free(seq_length);
04702   cpl_free(peak_hi);
04703 
04704   if (n == 0) {
04705     cpl_free(xpos);
04706     cpl_free(lambda);
04707     return NULL;
04708   }
04709 
04710   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04711                                    cpl_vector_wrap(n, lambda));
04712 }
04713 
04714 
04732 /*
04733 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04734                     double refwave, double pixel)
04735 {
04736     double yellow;
04737     double cpixel;
04738     double tolerance = 0.02;
04739     int    max_iter = 20;
04740     int    iter = 0;
04741 
04742     
04743     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04744         return 0.0;
04745     
04746     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04747         return 0.0;
04748 
04749     yellow = (blue + red) / 2;
04750 
04751     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04752 
04753     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04754 
04755         if (cpixel > pixel)
04756             red = yellow;
04757         else
04758             blue = yellow;
04759 
04760         yellow = (blue + red) / 2;
04761         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04762 
04763         iter++;
04764 
04765     }
04766 
04767     return yellow;
04768 
04769 }
04770 */
04771 
04772 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04773                     double refwave, double pixel)
04774 {
04775     double     yellow;
04776     double     coeff;
04777     cpl_size   zero = 0;
04778 
04779     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04780         return 0.0;
04781 
04782     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04783         return 0.0;
04784 
04785     yellow = (blue + red) / 2 - refwave;
04786 
04787     coeff = cpl_polynomial_get_coeff(ids, &zero);
04788     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04789 
04790     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04791     if (cpl_error_get_code() != CPL_ERROR_NONE) {
04792         cpl_error_reset();
04793         return 0.0;
04794     }
04795 
04796     cpl_polynomial_set_coeff(ids, &zero, coeff);
04797 
04798     return yellow + refwave;
04799 
04800 }
04801 
04827 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04828                                  double reject, int minlines, 
04829                                  int *nlines, double *err)
04830 {
04831     const char   *func = "mos_poly_wav2pix";
04832 
04833     cpl_bivector *pixwav2;
04834     cpl_vector   *wavel;
04835     cpl_vector   *pixel;
04836     double       *d_wavel;
04837     double       *d_pixel;
04838     double        pixpos;
04839     int           fitlines;
04840     int           rejection = 0;
04841     int           i, j;
04842 
04843     cpl_polynomial *ids;
04844 
04845 
04846     *nlines = 0;
04847     *err = 0;
04848 
04849     if (pixwav == NULL) {
04850         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04851         return NULL;
04852     }
04853 
04854     fitlines = cpl_bivector_get_size(pixwav);
04855 
04856     if (fitlines < minlines) {
04857         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04858         return NULL;
04859     }
04860 
04861 
04862     /*
04863      * If outliers rejection was requested, allocate a working
04864      * vector (that can be modified as soon as outliers are removed)
04865      */
04866 
04867     if (reject > 0.0)
04868         rejection = 1;
04869 
04870     if (rejection)
04871         pixwav2 = cpl_bivector_duplicate(pixwav);
04872     else
04873         pixwav2 = pixwav;
04874 
04875 
04876     /*
04877      * The single vectors are extracted just because the fitting routine
04878      * requires it
04879      */
04880 
04881     pixel = cpl_bivector_get_x(pixwav2);
04882     wavel = cpl_bivector_get_y(pixwav2);
04883 
04884 
04885     /*
04886      * Get rid of the wrapper, in case of duplication
04887      */
04888 
04889     if (rejection)
04890         cpl_bivector_unwrap_vectors(pixwav2);
04891 
04892 
04893     /*
04894      * Here begins the iterative fit of identified lines
04895      */
04896 
04897     while (fitlines >= minlines) {
04898 
04899         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04900         *err = sqrt(*err);
04901     
04902         if (ids == NULL) {
04903             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04904             cpl_msg_debug(func, "Fitting IDS");
04905             cpl_error_set_where(func);
04906             if (rejection) {
04907                 cpl_vector_delete(wavel);
04908                 cpl_vector_delete(pixel);
04909             }
04910             return NULL;
04911         }
04912 
04913         if (rejection) {
04914 
04915 
04916             /*
04917              * Now work directly with the vector data buffers...
04918              */
04919 
04920             d_pixel = cpl_vector_unwrap(pixel);
04921             d_wavel = cpl_vector_unwrap(wavel);
04922 
04923             for (i = 0, j = 0; i < fitlines; i++) {
04924                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04925                 if (fabs(pixpos - d_pixel[i]) < reject) {
04926                     d_pixel[j] = d_pixel[i];
04927                     d_wavel[j] = d_wavel[i];
04928                     j++;
04929                 }
04930             }
04931     
04932             if (j == fitlines) {       /* No rejection in last iteration */
04933                 cpl_free(d_wavel);
04934                 cpl_free(d_pixel);
04935                 *nlines = fitlines;
04936                 return ids;
04937             }
04938             else {                     /* Some lines were rejected       */
04939                 fitlines = j;
04940                 cpl_polynomial_delete(ids);
04941                 if (fitlines >= minlines) {
04942                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04943                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04944                 }
04945                 else {                 /* Too few lines: failure         */
04946                     cpl_free(d_wavel);
04947                     cpl_free(d_pixel);
04948                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04949                     return NULL;
04950                 }
04951             }
04952         }
04953         else {
04954             *nlines = fitlines;
04955             return ids;       /* Exit at first iteration if no rejection */
04956         }
04957     }
04958 
04959     return ids;               /* To avoid compiler warnings */
04960 }
04961 
04962 
04987 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04988                                  double reject, int minlines, 
04989                                  int *nlines, double *err)
04990 {
04991 
04992     cpl_bivector *wavpix;
04993     cpl_vector   *wavel;
04994     cpl_vector   *pixel;
04995 
04996     cpl_polynomial *dds;
04997 
04998 
04999     /*
05000      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
05001      */
05002 
05003     pixel = cpl_bivector_get_x(pixwav);
05004     wavel = cpl_bivector_get_y(pixwav);
05005 
05006     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
05007 
05008     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
05009 
05010     cpl_bivector_unwrap_vectors(wavpix);
05011 
05012     return dds;
05013 
05014 }
05015 
05016 
05039 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 
05040                              cpl_vector *lines, cpl_polynomial *ids, 
05041                              double refwave, int sradius)
05042 {
05043     const char   *func = "mos_find_peaks";
05044 
05045     double       *data;
05046     double       *d_pixel;
05047     double       *d_wavel;
05048     float         pos;
05049     int           nlines;
05050     int           pixel;
05051     int           i, j;
05052 
05053 
05054     if (spectrum == NULL || lines == NULL || ids == NULL) {
05055         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05056         return NULL;
05057     }
05058 
05059     nlines = cpl_vector_get_size(lines);
05060 
05061     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
05062         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05063         return NULL;
05064     }
05065 
05066     d_wavel = cpl_malloc(nlines * sizeof(double));
05067     d_pixel = cpl_malloc(nlines * sizeof(double));
05068 
05069     data = cpl_vector_get_data(lines);
05070 
05071     for (i = 0, j = 0; i < nlines; i++) {
05072         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
05073         if (pixel - sradius < 0 || pixel + sradius >= length)
05074             continue;
05075         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
05076             pos += pixel - sradius;
05077             d_pixel[j] = pos;
05078             d_wavel[j] = data[i];
05079             j++;
05080         }
05081     }
05082 
05083     if (j > 0) {
05084         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
05085                                          cpl_vector_wrap(j, d_wavel));
05086     }
05087     else {
05088         cpl_free(d_wavel);
05089         cpl_free(d_pixel);
05090         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
05091         return NULL;
05092     }
05093 }
05094 
05095 
05213 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
05214                                           cpl_vector *lines,
05215                                           double dispersion, float level,
05216                                           int sradius, int order,
05217                                           double reject, double refwave, 
05218                                           double *wavestart, double *waveend,
05219                                           int *nlines, double *error, 
05220                                           cpl_table *idscoeff,
05221                                           cpl_image *calibration,
05222                                           cpl_image *residuals, 
05223                                           cpl_table *restable,
05224                                           cpl_mask *refmask)
05225 {
05226 
05227     const char *func = "mos_wavelength_calibration_raw";
05228 
05229     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
05230                                                  /* Max order is 5 */
05231 
05232     double  tolerance = 20.0;     /* Probably forever...                */
05233     int     step      = 10;       /* Compute restable every "step" rows */
05234 
05235     char            name[MAX_COLNAME];
05236     cpl_image      *resampled;
05237     cpl_bivector   *output;
05238     cpl_bivector   *new_output;
05239     cpl_vector     *peaks;
05240     cpl_vector     *wavel;
05241     cpl_polynomial *ids;
05242     cpl_polynomial *lin;
05243     cpl_matrix     *kernel;
05244     double          ids_err;
05245     double          max_disp, min_disp;
05246     double         *line;
05247     double          firstLambda, lastLambda, lambda;
05248     double          value, wave, pixe;
05249     cpl_binary     *mdata;
05250     const float    *sdata;
05251     float          *rdata;
05252     float          *idata;
05253     float          *ddata;
05254     float           v1, v2, vi;
05255     float           fpixel;
05256     int            *have_it;
05257     int             pixstart, pixend;
05258     int             extrapolation;
05259     int             nref;
05260     int             nl, nx, ny, pixel;
05261     int             countLines, usedLines;
05262     int             uorder;
05263     int             in, first, last;
05264     int             width, uradius;
05265     int             i, j;
05266     cpl_size        k;
05267 
05268 
05269     if (dispersion == 0.0) {
05270         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05271         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05272         return NULL;
05273     }
05274 
05275     if (dispersion < 0.0) {
05276         cpl_msg_error(func, "The expected dispersion must be positive");
05277         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05278         return NULL;
05279     }
05280 
05281     max_disp = dispersion + dispersion * tolerance / 100;
05282     min_disp = dispersion - dispersion * tolerance / 100;
05283 
05284     if (order < 1) {
05285         cpl_msg_error(func, "The order of the fitting polynomial "
05286                       "must be at least 1");
05287         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05288         return NULL;
05289     }
05290 
05291     if (image == NULL || lines == NULL) {
05292         cpl_msg_error(func, "Both spectral exposure and reference line "
05293                       "catalog are required in input");
05294         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05295         return NULL;
05296     }
05297 
05298     nx = cpl_image_get_size_x(image);
05299     ny = cpl_image_get_size_y(image);
05300     sdata = cpl_image_get_data_float_const(image);
05301 
05302     nref = cpl_vector_get_size(lines);
05303     line = cpl_vector_get_data(lines);
05304 
05305     if (*wavestart < 1.0 && *waveend < 1.0) {
05306         firstLambda = line[0];
05307         lastLambda = line[nref-1];
05308         extrapolation = (lastLambda - firstLambda) / 10;
05309         firstLambda -= extrapolation;
05310         lastLambda += extrapolation;
05311         *wavestart = firstLambda;
05312         *waveend = lastLambda;
05313     }
05314     else {
05315         firstLambda = *wavestart;
05316         lastLambda = *waveend;
05317     }
05318 
05319     nl = (lastLambda - firstLambda) / dispersion;
05320     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05321     rdata = cpl_image_get_data_float(resampled);
05322 
05323     if (calibration)
05324         idata = cpl_image_get_data_float(calibration);
05325 
05326     if (residuals)
05327         ddata = cpl_image_get_data_float(residuals);
05328 
05329     if (idscoeff)
05330         for (j = 0; j <= order; j++)
05331             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05332 
05333     if (restable) {
05334         cpl_table_set_size(restable, nref);
05335         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05336         cpl_table_copy_data_double(restable, "wavelength", line);
05337         for (i = 0; i < ny; i += step) {
05338              snprintf(name, MAX_COLNAME, "r%d", i);
05339              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05340              snprintf(name, MAX_COLNAME, "d%d", i);
05341              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05342              snprintf(name, MAX_COLNAME, "p%d", i);
05343              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05344         }
05345     }
05346 
05347     /*
05348      * Here is the real thing: detecting and identifying peaks,
05349      * and then fit the transformation from wavelength to pixel
05350      * and from pixel to wavelength.
05351      */
05352 
05353     for (i = 0; i < ny; i++) {
05354         width = mos_lines_width(sdata + i*nx, nx);
05355         if (sradius > 0) {
05356             if (width > sradius) {
05357                 uradius = width;
05358             }
05359             else {
05360                 uradius = sradius;
05361             }
05362         }
05363         if (width < 5)
05364             width = 5;
05365         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05366         if (peaks) {
05367             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05368         }
05369         if (peaks) {
05370             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05371             if (output) {
05372                 countLines = cpl_bivector_get_size(output);
05373                 if (countLines < 4) {
05374                     cpl_bivector_delete(output);
05375                     cpl_vector_delete(peaks);
05376                     if (nlines)
05377                         nlines[i] = 0;
05378                     if (error)
05379                         error[i] = 0.0;
05380                     continue;
05381                 }
05382 
05383                 /*
05384                  * Set reference wavelength as zero point
05385                  */
05386 
05387                 wavel = cpl_bivector_get_y(output);
05388                 cpl_vector_subtract_scalar(wavel, refwave);
05389 
05390                 uorder = countLines / 2 - 1;
05391                 if (uorder > order)
05392                     uorder = order;
05393 
05394 /* This part is now commented out. In case the first-guess iteration
05395  * was requested, the first fit was made with a lower polynomial degree:
05396  * more robust, and still accurate enough to be used as a first-guess.
05397 
05398                 if (sradius > 0 && uorder > 2)
05399                     --uorder;
05400 
05401  * End of commented part */
05402 
05403                 ids = mos_poly_wav2pix(output, uorder, reject,
05404                                        2 * (uorder + 1), &usedLines,
05405                                        &ids_err);
05406 
05407                 if (ids == NULL) {
05408                     cpl_bivector_delete(output);
05409                     cpl_vector_delete(peaks);
05410                     if (nlines)
05411                         nlines[i] = 0;
05412                     if (error)
05413                         error[i] = 0.0;
05414                     cpl_error_reset();
05415                     continue;
05416                 }
05417 
05418                 if (idscoeff) {
05419 
05420                     /*
05421                      * Write it anyway, even in case a first-guess based
05422                      * solution will be searched afterwards: in case of
05423                      * failure, the "blind" solution is kept.
05424                      */
05425 
05426                     for (k = 0; k <= order; k++) {
05427                         if (k > uorder) {
05428                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05429                         }
05430                         else {
05431                             cpl_table_set_double(idscoeff, clab[k], i,
05432                                       cpl_polynomial_get_coeff(ids, &k));
05433                         }
05434                     }
05435                 }
05436 
05437                 if (sradius > 0) {
05438 
05439                     /*
05440                      * Use ids as a first-guess
05441                      */
05442 
05443                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05444                                                 ids, refwave, uradius);
05445 
05446                     if (new_output) {
05447                         cpl_bivector_delete(output);
05448                         output = new_output;
05449                     }
05450                     else
05451                         cpl_error_reset();
05452 
05453 
05454                     cpl_polynomial_delete(ids);
05455 
05456                     countLines = cpl_bivector_get_size(output);
05457 
05458                     if (countLines < 4) {
05459                         cpl_bivector_delete(output);
05460                         cpl_vector_delete(peaks);
05461 
05462                         /* 
05463                          * With the following code a decision is taken:
05464                          * if using the first-guess gives no results,
05465                          * then also the "blind" solution is rejected.
05466                          */
05467 
05468                         if (nlines)
05469                             nlines[i] = 0;
05470                         if (error)
05471                             error[i] = 0.0;
05472                         if (idscoeff)
05473                             for (k = 0; k <= order; k++)
05474                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05475                         continue;
05476                     }
05477 
05478                     wavel = cpl_bivector_get_y(output);
05479                     cpl_vector_subtract_scalar(wavel, refwave);
05480 
05481                     uorder = countLines / 2 - 1;
05482                     if (uorder > order)
05483                         uorder = order;
05484 
05485                     ids = mos_poly_wav2pix(output, uorder, reject,
05486                                            2 * (uorder + 1), &usedLines,
05487                                            &ids_err);
05488 
05489                     if (ids == NULL) {
05490                         cpl_bivector_delete(output);
05491                         cpl_vector_delete(peaks);
05492 
05493                         /* 
05494                          * With the following code a decision is taken:
05495                          * if using the first-guess gives no results,
05496                          * then also the "blind" solution is rejected.
05497                          */
05498 
05499                         if (nlines)
05500                             nlines[i] = 0;
05501                         if (error)
05502                             error[i] = 0.0;
05503                         if (idscoeff)
05504                             for (k = 0; k <= order; k++)
05505                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05506                         cpl_error_reset();
05507                         continue;
05508                     }
05509 
05510                     if (idscoeff) {
05511                         for (k = 0; k <= order; k++) {
05512                             if (k > uorder) {
05513                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05514                             }
05515                             else {
05516                                 cpl_table_set_double(idscoeff, clab[k], i,
05517                                             cpl_polynomial_get_coeff(ids, &k));
05518                             }
05519                         }
05520                     }
05521 
05522                 } /* End of "use ids as a first-guess" */
05523 
05524                 if (nlines)
05525                     nlines[i] = usedLines;
05526                 if (error)
05527                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05528 
05529                 pixstart = cpl_polynomial_eval_1d(ids, 
05530                     cpl_bivector_get_y_data(output)[0], NULL);
05531                 pixend = cpl_polynomial_eval_1d(ids,
05532                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05533                 extrapolation = (pixend - pixstart) / 5;
05534                 pixstart -= extrapolation;
05535                 pixend += extrapolation;
05536                 if (pixstart < 0)
05537                     pixstart = 0;
05538                 if (pixend > nx)
05539                     pixend = nx;
05540 
05541                 /*
05542                  * Wavelength calibrated image (if requested):
05543                  */
05544 
05545                 if (calibration) {
05546                     for (j = pixstart; j < pixend; j++) {
05547                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05548                                                          lastLambda, refwave, 
05549                                                          j);
05550                     }
05551                 }
05552 
05553                 /*
05554                  * Resampled image:
05555                  */
05556 
05557                 for (j = 0; j < nl; j++) {
05558                     lambda = firstLambda + j * dispersion;
05559                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05560                                                     NULL);
05561                     pixel = fpixel;
05562                     if (pixel >= 0 && pixel < nx-1) {
05563                         v1 = (sdata + i*nx)[pixel];
05564                         v2 = (sdata + i*nx)[pixel+1];
05565                         vi = v1 + (v2-v1)*(fpixel-pixel);
05566                         (rdata + i*nl)[j] = vi;
05567                     }
05568                 }
05569 
05570                 /*
05571                  * Residuals image
05572                  */
05573 
05574                 if (residuals || (restable && !(i%step))) {
05575                     if (restable && !(i%step)) {
05576                         lin = cpl_polynomial_new(1);
05577                         for (k = 0; k < 2; k++)
05578                             cpl_polynomial_set_coeff(lin, &k, 
05579                                           cpl_polynomial_get_coeff(ids, &k));
05580                     }
05581                     for (j = 0; j < countLines; j++) {
05582                         pixe = cpl_bivector_get_x_data(output)[j];
05583                         wave = cpl_bivector_get_y_data(output)[j];
05584                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05585                         if (residuals) {
05586                             pixel = pixe + 0.5;
05587                             (ddata + i*nx)[pixel] = value;
05588                         }
05589                         if (restable && !(i%step)) {
05590                             for (k = 0; k < nref; k++) {
05591                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05592                                     snprintf(name, MAX_COLNAME, "r%d", i);
05593                                     cpl_table_set_double(restable, name, 
05594                                                          k, value);
05595                                     value = pixe
05596                                           - cpl_polynomial_eval_1d(lin, wave,
05597                                                                    NULL);
05598                                     snprintf(name, MAX_COLNAME, "d%d", i);
05599                                     cpl_table_set_double(restable, name, 
05600                                                          k, value);
05601                                     snprintf(name, MAX_COLNAME, "p%d", i);
05602                                     cpl_table_set_double(restable, name,
05603                                                          k, pixe);
05604                                     break;
05605                                 }
05606                             }
05607                         }
05608                     }
05609                     if (restable && !(i%step)) {
05610                         cpl_polynomial_delete(lin);
05611                     }
05612                 }
05613 
05614                 /*
05615                  * Mask at reference wavelength
05616                  */
05617 
05618                 if (refmask) {
05619                     mdata = cpl_mask_get_data(refmask);
05620                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05621                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05622                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05623                         mdata[pixel + i*nx] = CPL_BINARY_1;
05624                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05625                     }
05626                 }
05627 
05628                 cpl_polynomial_delete(ids);
05629                 cpl_bivector_delete(output);
05630             }
05631             cpl_vector_delete(peaks);
05632         }
05633     }
05634 
05635     if (refmask) {
05636         kernel = cpl_matrix_new(3, 3);
05637         cpl_matrix_set(kernel, 0, 1, 1.0);
05638         cpl_matrix_set(kernel, 1, 1, 1.0);
05639         cpl_matrix_set(kernel, 2, 1, 1.0);
05640 
05641         cpl_mask_dilation(refmask, kernel);
05642         cpl_mask_erosion(refmask, kernel);
05643         cpl_mask_erosion(refmask, kernel);
05644         cpl_mask_dilation(refmask, kernel);
05645 
05646         cpl_matrix_delete(kernel);
05647 
05648         /*
05649          *  Fill possible gaps
05650          */
05651 
05652         mdata = cpl_mask_get_data(refmask);
05653         have_it = cpl_calloc(ny, sizeof(int));
05654 
05655         for (i = 0; i < ny; i++, mdata += nx) {
05656             for (j = 0; j < nx; j++) {
05657                 if (mdata[j] == CPL_BINARY_1) {
05658                     have_it[i] = j;
05659                     break;
05660                 }
05661             }
05662         }
05663 
05664         mdata = cpl_mask_get_data(refmask);
05665         in = 0;
05666         first = last = 0;
05667 
05668         for (i = 0; i < ny; i++) {
05669             if (have_it[i]) {
05670                 if (!in) {
05671                     in = 1;
05672                     if (first) {
05673                         last = i;
05674                         if (abs(have_it[first] - have_it[last]) < 3) {
05675                             for (j = first; j < last; j++) {
05676                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05677                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05678                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05679                             }
05680                         }
05681                     }
05682                 }
05683             }
05684             else {
05685                 if (in) {
05686                     in = 0;
05687                     first = i - 1;
05688                 }
05689             }
05690         }
05691 
05692         cpl_free(have_it);
05693 
05694     }
05695 
05696 /*
05697     for (i = 0; i < ny; i++) {
05698         if (nlines[i] == 0) {
05699             for (k = 0; k <= order; k++) {
05700                 cpl_table_set_invalid(idscoeff, clab[k], i);
05701             }
05702         }
05703     }
05704 */
05705 
05706     return resampled;
05707 }
05708 
05709 
05731 /*
05732 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05733 {
05734     const char *func = "mos_locate_spectra_bis";
05735 
05736     cpl_apertures    *slits;
05737     cpl_image        *labimage;
05738     cpl_image        *refimage;
05739     cpl_binary       *mdata;
05740     cpl_table        *slitpos;
05741     cpl_propertylist *sort_col;
05742     int               nslits;
05743     int              *have_it;
05744     int               in, first, last;
05745     int               i, j;
05746 
05747 
05748     if (mask == NULL) {
05749         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05750         return NULL;
05751     }
05752 
05753     nx = cpl_mask_get_size_x(mask);
05754     ny = cpl_mask_get_size_y(mask);
05755 
05756     mdata = cpl_mask_get_data(refmask);
05757     have_it = cpl_calloc(ny, sizeof(int));
05758 
05759     for (i = 0; i < ny; i++, mdata += nx) {
05760         for (j = 0; j < nx; j++) {
05761             if (mdata[j] == CPL_BINARY_1) {
05762                 have_it[i] = j + 1;
05763                 break;
05764             }
05765         }
05766     }
05767 
05768     mdata = cpl_mask_get_data(refmask);
05769     in = 0;
05770     first = last = 0;
05771     nslits = 0;
05772 
05773     for (i = 0; i < ny; i++) {
05774         if (have_it[i]) {
05775             if (in) {
05776                 if (i) {
05777                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05778                         nslits++;
05779                     }
05780                 }
05781             }
05782             else {
05783                 in = 1;
05784                 nslits++;
05785             }
05786         }
05787         else {
05788             if (in) {
05789                 in = 0;
05790             }
05791         }
05792     }
05793 }
05794 */
05795 
05796 
05818 cpl_table *mos_locate_spectra(cpl_mask *mask)
05819 {
05820     const char *func = "mos_locate_spectra";
05821 
05822     cpl_apertures    *slits;
05823     cpl_image        *labimage;
05824     cpl_image        *refimage;
05825     cpl_table        *slitpos;
05826     cpl_propertylist *sort_col;
05827     cpl_size          nslits;
05828     int               i;
05829 
05830 
05831     if (mask == NULL) {
05832         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05833         return NULL;
05834     }
05835 
05836     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05837 
05838     if (nslits < 1) {
05839         cpl_image_delete(labimage);
05840         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05841         return NULL;
05842     }
05843 
05844     refimage = cpl_image_new_from_mask(mask);
05845 
05846     slits = cpl_apertures_new_from_image(refimage, labimage);
05847 
05848     cpl_image_delete(labimage);
05849     cpl_image_delete(refimage);
05850 
05851     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05852     if (nslits < 1) {
05853         cpl_apertures_delete(slits);
05854         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05855         return NULL;
05856     }
05857 
05858     slitpos = cpl_table_new(nslits);
05859     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05860     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05861     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05862     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05863     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05864     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05865     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05866     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05867 
05868     for (i = 0; i < nslits; i++) {
05869         cpl_table_set_double(slitpos, "xtop", i, 
05870                              cpl_apertures_get_top_x(slits, i+1) - 1);
05871         cpl_table_set_double(slitpos, "ytop", i, 
05872                              cpl_apertures_get_top(slits, i+1));
05873         cpl_table_set_double(slitpos, "xbottom", i, 
05874                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05875         cpl_table_set_double(slitpos, "ybottom", i, 
05876                              cpl_apertures_get_bottom(slits, i+1));
05877     }
05878 
05879     cpl_apertures_delete(slits);
05880 
05881     sort_col = cpl_propertylist_new();
05882     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05883     cpl_table_sort(slitpos, sort_col);
05884     cpl_propertylist_delete(sort_col);
05885 
05886     return slitpos;
05887 
05888 }
05889 
05890 
05906 cpl_error_code mos_validate_slits(cpl_table *slits) 
05907 {
05908     const char *func = "mos_validate_slits";
05909 
05910 
05911     if (slits == NULL)
05912         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05913 
05914     if (1 != cpl_table_has_column(slits, "xtop"))
05915         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05916 
05917     if (1 != cpl_table_has_column(slits, "ytop"))
05918         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05919 
05920     if (1 != cpl_table_has_column(slits, "xbottom"))
05921         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05922 
05923     if (1 != cpl_table_has_column(slits, "ybottom"))
05924         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05925 
05926     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05927         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05928 
05929     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05930         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05931 
05932     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05933         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05934 
05935     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05936         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05937 
05938     return CPL_ERROR_NONE;
05939 }
05940 
05941 
05970 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05971 {
05972     const char *func = "mos_rotate_slits";
05973 
05974     cpl_error_code error;
05975     char aux_name[] = "_0";
05976     int i;
05977 
05978 
05979     rotation %= 4;
05980     if (rotation < 0)
05981         rotation += 4;
05982 
05983     if (rotation == 0)
05984         return CPL_ERROR_NONE;
05985 
05986     error = mos_validate_slits(slits);
05987     if (error)
05988         return cpl_error_set(func, error);
05989 
05990     if (rotation == 1 || rotation == 3) {
05991 
05992         /*
05993          * Swap x and y column names
05994          */
05995 
05996         for (i = 0; i < 77; i++)
05997             if (1 == cpl_table_has_column(slits, aux_name))
05998                 aux_name[1]++;
05999         if (1 == cpl_table_has_column(slits, aux_name))
06000             return cpl_error_set(func, CPL_ERROR_CONTINUE);
06001         cpl_table_name_column(slits, "xtop", aux_name);
06002         cpl_table_name_column(slits, "ytop", "xtop");
06003         cpl_table_name_column(slits, aux_name, "ytop");
06004         cpl_table_name_column(slits, "xbottom", aux_name);
06005         cpl_table_name_column(slits, "ybottom", "xbottom");
06006         cpl_table_name_column(slits, aux_name, "ybottom");
06007     }
06008 
06009     if (rotation == 1 || rotation == 2) {
06010         cpl_table_multiply_scalar(slits, "xtop", -1.0);
06011         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
06012         cpl_table_add_scalar(slits, "xtop", nx);
06013         cpl_table_add_scalar(slits, "xbottom", nx);
06014     }
06015 
06016     if (rotation == 3 || rotation == 2) {
06017         cpl_table_multiply_scalar(slits, "ytop", -1.0);
06018         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
06019         cpl_table_add_scalar(slits, "ytop", ny);
06020         cpl_table_add_scalar(slits, "ybottom", ny);
06021     }
06022 
06023     return CPL_ERROR_NONE;
06024 }
06025 
06026 
06084 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
06085                               cpl_table *global)
06086 {
06087     cpl_array        *top_ident = NULL;;
06088     cpl_array        *bot_ident = NULL;;
06089     cpl_matrix       *mdata;
06090     cpl_matrix       *mpattern;
06091     cpl_matrix       *top_data;
06092     cpl_matrix       *top_pattern;
06093     cpl_matrix       *top_mdata;
06094     cpl_matrix       *top_mpattern;
06095     cpl_matrix       *bot_data;
06096     cpl_matrix       *bot_pattern;
06097     cpl_matrix       *bot_mdata;
06098     cpl_matrix       *bot_mpattern;
06099     cpl_propertylist *sort_col;
06100     double           *xtop;
06101     double           *ytop;
06102     double           *xmtop;
06103     double           *ymtop;
06104     double           *xbot;
06105     double           *ybot;
06106     double           *xmbot;
06107     double           *ymbot;
06108     double            top_scale, bot_scale;
06109     double            angle, top_angle, bot_angle;
06110     double            xmse, ymse;
06111     double            xrms, top_xrms, bot_xrms;
06112     double            yrms, top_yrms, bot_yrms;
06113     int               nslits;
06114     int               nmaskslits, use_pattern;
06115     int               found_slits, found_slits_top, found_slits_bot;
06116     int               degree;
06117     int               i;
06118     cpl_table        *positions;
06119     cpl_error_code    error;
06120 
06121     cpl_vector       *point;
06122     double           *dpoint;
06123     cpl_vector       *xpos;
06124     cpl_vector       *ypos;
06125     cpl_vector       *xmpos;
06126     cpl_vector       *ympos;
06127     cpl_bivector     *mpos;
06128     cpl_polynomial   *xpoly     = NULL;
06129     cpl_polynomial   *ypoly     = NULL;
06130     cpl_polynomial   *top_xpoly = NULL;
06131     cpl_polynomial   *top_ypoly = NULL;
06132     cpl_polynomial   *bot_xpoly = NULL;
06133     cpl_polynomial   *bot_ypoly = NULL;
06134 
06135     char *msg_multiplex = " ";
06136 
06137 
06138     error = mos_validate_slits(slits);
06139     if (error) {
06140         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
06141                       cpl_error_get_message());
06142         cpl_error_set(cpl_func, error);
06143         return NULL;
06144     }
06145 
06146     error = mos_validate_slits(maskslits);
06147     if (error) {
06148         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
06149                       cpl_error_get_message());
06150         cpl_error_set(cpl_func, error);
06151         return NULL;
06152     }
06153 
06154     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06155         cpl_msg_error(cpl_func, "Missing slits identifiers");
06156         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
06157         return NULL;
06158     }
06159 
06160     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06161         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
06162         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
06163         return NULL;
06164     }
06165 
06166     nslits = cpl_table_get_nrow(slits);
06167     nmaskslits = cpl_table_get_nrow(maskslits);
06168 
06169     if (nslits == 0 || nmaskslits == 0) {
06170         cpl_msg_error(cpl_func, "Empty slits table");
06171         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
06172         return NULL;
06173     }
06174 
06175     if (nslits > 25 && mos_multiplex < 0) {
06176         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
06177         positions = mos_identify_slits_fast(slits, maskslits, global);
06178         if (positions == NULL)
06179             cpl_error_set_where(cpl_func);
06180         return positions;
06181     }
06182 
06183     /*
06184      * Guarantee that both input tables are sorted in the same way
06185      */
06186 
06187     sort_col = cpl_propertylist_new();
06188     cpl_propertylist_append_bool(sort_col, "ytop", 1);
06189     cpl_table_sort(slits, sort_col);
06190     cpl_table_sort(maskslits, sort_col);
06191     cpl_propertylist_delete(sort_col);
06192 
06193     /*
06194      * First we handle all the special cases (too few slits...)
06195      */
06196 
06197     if (nslits < 3 && nmaskslits > nslits) {
06198 
06199         /*
06200          * If there are just 1 or 2 slits on the CCD, and more on the
06201          * mask, the ambiguity cannot be solved, and an error is returned.
06202          * This is a case that must be solved with a first-guess relation
06203          * between mask and CCD.
06204          */
06205 
06206         if (nslits > 1)
06207             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
06208                             "with the %d mask slits: process will continue "
06209                             "using the detected CCD slits positions", nslits,
06210                             nmaskslits);
06211         else
06212             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
06213                             "the %d mask slits: process will continue using "
06214                             "the detected CCD slit position", nmaskslits);
06215         return NULL;
06216     }
06217 
06218     if (nmaskslits < 3 && nslits > nmaskslits) {
06219 
06220         /*
06221          * If there are less than 3 slits on the mask the ambiguity cannot
06222          * be solved, and an error is returned. This is a case that must
06223          * be solved with a first-guess relation between mask and CCD.
06224          */
06225 
06226         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
06227                         "the %d mask slits: process will continue using "
06228                         "the detected CCD slits positions", nslits,
06229                         nmaskslits);
06230         return NULL;
06231     }
06232 
06233     /*
06234      * Pattern matching related operations begin here. Two pattern
06235      * matching will be run, one based on the "top" and another one
06236      * based on the "bottom" slit coordinates. The one with the
06237      * smallest rms in the Y coordinate will be chosen.
06238      */
06239 
06240     xtop  = cpl_table_get_data_double(slits, "xtop");
06241     ytop  = cpl_table_get_data_double(slits, "ytop");
06242     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06243     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06244 
06245     xbot  = cpl_table_get_data_double(slits, "xbottom");
06246     ybot  = cpl_table_get_data_double(slits, "ybottom");
06247     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06248     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06249 
06250     top_data    = cpl_matrix_new(2, nslits);
06251     top_pattern = cpl_matrix_new(2, nmaskslits);
06252     bot_data    = cpl_matrix_new(2, nslits);
06253     bot_pattern = cpl_matrix_new(2, nmaskslits);
06254 
06255     for (i = 0; i < nslits; i++)
06256         cpl_matrix_set(top_data, 0, i, xtop[i]);
06257 
06258     for (i = 0; i < nslits; i++)
06259         cpl_matrix_set(top_data, 1, i, ytop[i]);
06260 
06261     for (i = 0; i < nmaskslits; i++)
06262         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06263 
06264     for (i = 0; i < nmaskslits; i++)
06265         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06266 
06267     for (i = 0; i < nslits; i++)
06268         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06269 
06270     for (i = 0; i < nslits; i++)
06271         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06272 
06273     for (i = 0; i < nmaskslits; i++)
06274         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06275 
06276     for (i = 0; i < nmaskslits; i++)
06277         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06278 
06279     if (nmaskslits > nslits)
06280         use_pattern = nslits;
06281     else
06282         use_pattern = nmaskslits;
06283 
06284     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06285                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06286                                      &top_mpattern, &top_scale, &top_angle);
06287 
06288     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06289                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06290                                      &bot_mpattern, &bot_scale, &bot_angle);
06291 
06292     if (top_ident == NULL && bot_ident == NULL) {
06293         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06294                         "the %d found CCD slits with the %d mask slits: "
06295                         "process will continue using the detected CCD "
06296                         "slits positions", nslits, nmaskslits);
06297         return NULL;
06298     }
06299 
06300     found_slits_top = 0;
06301     found_slits_bot = 0;
06302     if (top_ident && bot_ident) {
06303         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06304                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06305         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06306                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06307         if (fabs(top_angle) < fabs(bot_angle))
06308             angle = fabs(top_angle);
06309         else
06310             angle = fabs(bot_angle);
06311         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06312         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06313     }
06314     else if (top_ident) {
06315         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06316         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06317         angle = fabs(top_angle);
06318         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06319     }
06320     else {
06321         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06322         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06323         angle = fabs(bot_angle);
06324         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06325     }
06326 
06327     cpl_array_delete(top_ident);
06328     cpl_array_delete(bot_ident);
06329 
06330     if (angle > 4.0) {
06331         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06332                         "angle is expected to be around zero. This match is "
06333                         "rejected: the process will continue using the %d "
06334                         "detected CCD slits positions", nslits);
06335         return NULL;
06336     }
06337 
06338     found_slits = found_slits_top;
06339     if (found_slits < found_slits_bot)
06340         found_slits = found_slits_bot;     /* Max value */
06341 
06342     if (found_slits < 4) {
06343         cpl_msg_warning(cpl_func,
06344                         "Too few safely identified slits: %d out of %d "
06345                         "candidates (%d expected). Process will continue "
06346                         "using the detected CCD slits positions", found_slits,
06347                         nslits, nmaskslits);
06348         return NULL;
06349     }
06350 
06351     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06352                  "candidates\n(%d expected)", found_slits, nslits,
06353                  nmaskslits);
06354 
06355     if (found_slits_top < 4)
06356         found_slits_top = 0;
06357 
06358     if (found_slits_bot < 4)
06359         found_slits_bot = 0;
06360 
06361     /*
06362      * Now for each set select the points of the identified slits, and fit
06363      * two bivariate polynomials to determine a first approximate relation
06364      * between positions on the mask and positions on the CCD.
06365      */
06366 
06367     for (i = 0; i < 2; i++) {
06368         if (i) {
06369             found_slits = found_slits_top;
06370             mdata = top_mdata;
06371             mpattern = top_mpattern;
06372         }
06373         else {
06374             found_slits = found_slits_bot;
06375             mdata = bot_mdata;
06376             mpattern = bot_mpattern;
06377         }
06378 
06379         if (found_slits == 0)
06380             continue;
06381         else if (found_slits < 10)
06382             degree = 1;
06383         else
06384             degree = 2;
06385 
06386         xpos  = cpl_vector_wrap(found_slits,
06387                                 cpl_matrix_get_data(mdata)              );
06388         ypos  = cpl_vector_wrap(found_slits,
06389                                 cpl_matrix_get_data(mdata) + found_slits);
06390         xmpos = cpl_vector_wrap(found_slits,
06391                                 cpl_matrix_get_data(mpattern)              );
06392         ympos = cpl_vector_wrap(found_slits,
06393                                 cpl_matrix_get_data(mpattern) + found_slits);
06394         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06395         xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06396         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06397 
06398         cpl_bivector_unwrap_vectors(mpos);
06399         cpl_vector_unwrap(xpos);
06400         cpl_vector_unwrap(ypos);
06401         cpl_vector_unwrap(xmpos);
06402         cpl_vector_unwrap(ympos);
06403         cpl_matrix_delete(mdata);
06404         cpl_matrix_delete(mpattern);
06405 
06406         if (i) {
06407             top_xpoly = xpoly;
06408             top_ypoly = ypoly;
06409             top_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06410             top_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06411         }
06412         else {
06413             bot_xpoly = xpoly;
06414             bot_ypoly = ypoly;
06415             bot_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06416             bot_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06417         }
06418     }
06419 
06420     if (top_xpoly && bot_xpoly) {
06421         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06422             xrms = top_xrms;
06423             xpoly = top_xpoly;
06424             cpl_polynomial_delete(bot_xpoly);
06425         }
06426         else {                      /* bottom X solution wins... */
06427             xrms = bot_xrms;
06428             xpoly = bot_xpoly;
06429             cpl_polynomial_delete(top_xpoly);
06430         }
06431     }
06432     else if (top_xpoly) {
06433         xrms = top_xrms;
06434         xpoly = top_xpoly;
06435     }
06436     else {
06437         xrms = bot_xrms;
06438         xpoly = bot_xpoly;
06439     }
06440 
06441     if (top_ypoly && bot_ypoly) {
06442         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06443             yrms = top_yrms;
06444             ypoly = top_ypoly;
06445             cpl_polynomial_delete(bot_ypoly);
06446         }
06447         else {                      /* bottom Y solution wins... */
06448             yrms = bot_yrms;
06449             ypoly = bot_ypoly;
06450             cpl_polynomial_delete(top_ypoly);
06451         }
06452     }
06453     else if (top_ypoly) {
06454         yrms = top_yrms;
06455         ypoly = top_ypoly;
06456     }
06457     else {
06458         yrms = bot_yrms;
06459         ypoly = bot_ypoly;
06460     }
06461 
06462     if (xpoly == NULL || ypoly == NULL) {
06463         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06464                         "identified slits positions cannot be improved.");
06465         cpl_polynomial_delete(xpoly);
06466         cpl_polynomial_delete(ypoly);
06467         cpl_error_reset();
06468         return NULL;
06469     }
06470 
06471     cpl_msg_info(cpl_func,
06472                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06473                  xrms, yrms);
06474 
06475     if (global) {
06476         write_global_distortion(global, 0, xpoly);
06477         write_global_distortion(global, 7, ypoly);
06478     }
06479 
06480     /*
06481      * The fit was successful: use the polynomials to obtain a new
06482      * position table with the improved positions of the slits
06483      */
06484 
06485     positions = cpl_table_duplicate(maskslits);
06486     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06487     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06488     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06489     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06490 
06491     point = cpl_vector_new(2);
06492     dpoint = cpl_vector_get_data(point);
06493 
06494     for (i = 0; i < nmaskslits; i++) {
06495         double position;
06496 
06497         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06498         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06499         position  = cpl_polynomial_eval(xpoly, point);
06500 //        if (mos_multiplex >= 0) {
06501 //            if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
06502 //                cpl_table_unselect_row(positions, i);
06503 //                continue;
06504 //            }
06505 //        }
06506         cpl_table_set_double(positions, "xtop", i, position);
06507         position  = cpl_polynomial_eval(ypoly, point);
06508         cpl_table_set_double(positions, "ytop", i, position);
06509         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06510         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06511         position  = cpl_polynomial_eval(xpoly, point);
06512         cpl_table_set_double(positions, "xbottom", i, position);
06513         position  = cpl_polynomial_eval(ypoly, point);
06514         cpl_table_set_double(positions, "ybottom", i, position);
06515     }
06516 
06517 //    if (mos_multiplex >= 0) {
06518 //        cpl_table_not_selected(positions);
06519 //        cpl_table_erase_selected(positions);
06520 //        nmaskslits = cpl_table_get_nrow(positions);
06521 //    }
06522 
06523     cpl_vector_delete(point);
06524     cpl_polynomial_delete(xpoly);
06525     cpl_polynomial_delete(ypoly);
06526 
06527     cpl_table_erase_column(positions, "xmtop");
06528     cpl_table_erase_column(positions, "ymtop");
06529     cpl_table_erase_column(positions, "xmbottom");
06530     cpl_table_erase_column(positions, "ymbottom");
06531 
06532     if (mos_multiplex >= 0) {
06533         msg_multiplex = 
06534         cpl_sprintf("in the CCD section between %d and %d pixel", 
06535                     mos_multiplex * mos_region_size, 
06536                     (mos_multiplex + 1) * mos_region_size);
06537     }
06538 
06539     if (nmaskslits > nslits)
06540         cpl_msg_info(cpl_func,
06541                      "Finally identified slits: %d out of %d expected %s\n"
06542                      "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
06543                      nmaskslits - nslits);
06544     else if (nmaskslits < nslits)
06545         cpl_msg_info(cpl_func,
06546                      "Finally identified slits: %d out of %d expected %s\n"
06547                      "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
06548                      nslits - nmaskslits);
06549     else
06550         cpl_msg_info(cpl_func,
06551                      "Finally identified slits: %d out of %d expected %s",
06552                      nmaskslits, nmaskslits, msg_multiplex);
06553 
06554     if (mos_multiplex >= 0) {
06555         cpl_free(msg_multiplex);
06556     }
06557 
06558     return positions;
06559 
06560 }
06561 
06562 
06563 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06564                                    cpl_table *global)
06565 {
06566     const char *func = "mos_identify_slits_fast";
06567 
06568     cpl_propertylist *sort_col;
06569     cpl_table        *positions;
06570     cpl_vector       *scales;
06571     cpl_vector       *angles;
06572     cpl_vector       *point;
06573     cpl_vector       *xpos;
06574     cpl_vector       *ypos;
06575     cpl_vector       *xmpos;
06576     cpl_vector       *ympos;
06577     cpl_bivector     *mpos;
06578     cpl_polynomial   *xpoly = NULL;
06579     cpl_polynomial   *ypoly = NULL;
06580     cpl_error_code    error;
06581     int nslits;
06582     int nmaskslits;
06583     int found_slits;
06584     int i, j, k;
06585 
06586     double  dist1, dist2, dist3, dist, mindist;
06587     double  scale, minscale, maxscale;
06588     double  angle, minangle, maxangle;
06589     double *dscale;
06590     double *dangle;
06591     double *dpoint;
06592     double *xtop;
06593     double *ytop;
06594     double *xbottom;
06595     double *ybottom;
06596     double *xcenter;
06597     double *ycenter;
06598     double *xpseudo;
06599     double *ypseudo;
06600     int    *slit_id;
06601     double *xmtop;
06602     double *ymtop;
06603     double *xmbottom;
06604     double *ymbottom;
06605     double *xmcenter;
06606     double *ymcenter;
06607     double *xmpseudo;
06608     double *ympseudo;
06609     double  xmse, ymse;
06610     int    *mslit_id;
06611     int    *good;
06612     int     minpos;
06613     int     degree;
06614 
06615     double  sradius = 0.01;   /* Candidate input argument... */
06616     int     in_sradius;
06617 
06618     double pi = 3.14159265358979323846;
06619 
06620 
06621     error = mos_validate_slits(slits);
06622     if (error) {
06623         cpl_msg_error(func, "CCD slits table validation: %s", 
06624                       cpl_error_get_message());
06625         cpl_error_set(func, error);
06626         return NULL;
06627     }
06628 
06629     error = mos_validate_slits(maskslits);
06630     if (error) {
06631         cpl_msg_error(func, "Mask slits table validation: %s", 
06632                       cpl_error_get_message());
06633         cpl_error_set(func, error);
06634         return NULL;
06635     }
06636 
06637     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06638         cpl_msg_error(func, "Missing slits identifiers");
06639         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06640         return NULL;
06641     }
06642 
06643     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06644         cpl_msg_error(func, "Wrong type used for slits identifiers");
06645         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06646         return NULL;
06647     }
06648 
06649     nslits = cpl_table_get_nrow(slits);
06650     nmaskslits = cpl_table_get_nrow(maskslits);
06651 
06652     if (nslits == 0 || nmaskslits == 0) {
06653         cpl_msg_error(func, "Empty slits table");
06654         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06655         return NULL;
06656     }
06657 
06658 
06659     /*
06660      * Compute middle point coordinates for each slit listed in both
06661      * input tables.
06662      */
06663 
06664     if (cpl_table_has_column(slits, "xcenter"))
06665         cpl_table_erase_column(slits, "xcenter");
06666 
06667     if (cpl_table_has_column(slits, "ycenter"))
06668         cpl_table_erase_column(slits, "ycenter");
06669 
06670     if (cpl_table_has_column(maskslits, "xcenter"))
06671         cpl_table_erase_column(maskslits, "xcenter");
06672 
06673     if (cpl_table_has_column(maskslits, "ycenter"))
06674         cpl_table_erase_column(maskslits, "ycenter");
06675 
06676     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06677     cpl_table_add_columns(slits, "xcenter", "xbottom");
06678     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06679     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06680     cpl_table_add_columns(slits, "ycenter", "ybottom");
06681     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06682 
06683     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06684     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06685     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06686     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06687     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06688     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06689 
06690 
06691     /*
06692      * Guarantee that both input tables are sorted in the same way
06693      */
06694 
06695     sort_col = cpl_propertylist_new();
06696     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06697     cpl_table_sort(slits, sort_col);
06698     cpl_table_sort(maskslits, sort_col);
06699     cpl_propertylist_delete(sort_col);
06700 
06701 
06702     /*
06703      * First we handle all the special cases (too few slits...)
06704      */
06705 
06706     if (nslits < 3 && nmaskslits > nslits) {
06707 
06708         /*
06709          * If there are just 1 or 2 slits on the CCD, and more on the
06710          * mask, the ambiguity cannot be solved, and an error is returned.
06711          * This is a case that must be solved with a first-guess relation
06712          * between mask and CCD.
06713          */
06714 
06715         if (nslits > 1)
06716             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06717                             "%d mask slits: process will continue using the "
06718                             "detected CCD slit position", nmaskslits);
06719         else
06720             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06721                             "the %d mask slits: process will continue using "
06722                             "the detected CCD slits positions", nslits, 
06723                             nmaskslits);
06724         return NULL;
06725     }
06726 
06727     if (nslits <= 3 && nslits == nmaskslits) {
06728 
06729         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06730         cpl_msg_warning(func, "Their detected positions are left unchanged");
06731 
06732         /*
06733          * If there are just up to 3 slits, both on the mask and on the CCD,
06734          * we can reasonably hope that those slits were found, and accept 
06735          * that their positions on the CCD cannot be improved. We prepare
06736          * therefore an output position table containing the slits with
06737          * their original positions. We can however give an estimate of
06738          * the platescale if there is more than one slit.
06739          */
06740 
06741         positions = cpl_table_duplicate(slits);
06742         cpl_table_erase_column(slits, "xcenter");
06743         cpl_table_erase_column(slits, "ycenter");
06744         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06745         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06746         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06747         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06748         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06749         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06750         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06751         cpl_table_erase_column(maskslits, "xcenter");
06752         cpl_table_erase_column(maskslits, "ycenter");
06753 
06754         if (nslits > 1) {
06755             xcenter = cpl_table_get_data_double(positions, "xcenter");
06756             ycenter = cpl_table_get_data_double(positions, "ycenter");
06757             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06758             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06759 
06760             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06761                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06762             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06763                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06764             scale = sqrt(dist1/dist2);
06765 
06766             if (nslits == 3) {
06767                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06768                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06769                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06770                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06771                 scale += sqrt(dist1/dist2);
06772                 scale /= 2;
06773             }
06774 
06775             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06776         }
06777 
06778         return positions;
06779     }
06780 
06781     if (nmaskslits < 3 && nslits > nmaskslits) {
06782 
06783         /*
06784          * If there are less than 3 slits on the mask the ambiguity cannot 
06785          * be solved, and an error is returned. This is a case that must 
06786          * be solved with a first-guess relation between mask and CCD.
06787          */
06788 
06789         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06790                         "the %d mask slits: process will continue using "
06791                         "the detected CCD slits positions", nslits, 
06792                         nmaskslits);
06793         return NULL;
06794     }
06795 
06796 
06797     /*
06798      * At this point of the program all the region of the plane
06799      * (nslits, nmaskslits) where either or both mask and CCD display
06800      * less than 3 slits are handled in some way. It would be better
06801      * to add in this place a special handling for identifying slits
06802      * in case of a very reduced number of slits (say, below 6).
06803      * It is also clear that if there are many more slits on the
06804      * mask than on the CCD, or many more on the CCD than on the
06805      * mask, something went deeply wrong with the preliminary 
06806      * wavelength calibration. Such cases should be handled with
06807      * a _complete_ pattern-recognition algorithm based on the
06808      * construction of all possible triangles. For the moment, 
06809      * we go directly to the limited pattern-recognition applied
06810      * below, based on triangles build only for consecutive slits.
06811      * This is reasonably safe, since the preliminary wavelength
06812      * calibration performed by mos_identify_peaks() is generally
06813      * robust.
06814      */
06815 
06816 
06817     /*
06818      * Compute (X, Y) coordinates on pseudo-plane describing the
06819      * different position ratios of successive slits, in both
06820      * input tables.
06821      */
06822 
06823     if (cpl_table_has_column(slits, "xpseudo"))
06824         cpl_table_erase_column(slits, "xpseudo");
06825 
06826     if (cpl_table_has_column(slits, "ypseudo"))
06827         cpl_table_erase_column(slits, "ypseudo");
06828 
06829     if (cpl_table_has_column(maskslits, "xpseudo"))
06830         cpl_table_erase_column(maskslits, "xpseudo");
06831 
06832     if (cpl_table_has_column(maskslits, "ypseudo"))
06833         cpl_table_erase_column(maskslits, "ypseudo");
06834 
06835     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06836     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06837 
06838     xcenter = cpl_table_get_data_double(slits, "xcenter");
06839     ycenter = cpl_table_get_data_double(slits, "ycenter");
06840     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06841     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06842 
06843     for (i = 1; i < nslits - 1; i++) {
06844         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06845               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06846         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06847               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06848         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06849               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06850         xpseudo[i] = sqrt(dist1/dist2);
06851         ypseudo[i] = sqrt(dist3/dist2);
06852     }
06853 
06854     cpl_table_set_invalid(slits, "xpseudo", 0);
06855     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06856     cpl_table_set_invalid(slits, "ypseudo", 0);
06857     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06858 
06859     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06860     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06861 
06862     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06863     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06864     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06865     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06866 
06867     for (i = 1; i < nmaskslits - 1; i++) {
06868         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06869               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06870         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06871               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06872         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06873               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06874         xmpseudo[i] = sqrt(dist1/dist2);
06875         ympseudo[i] = sqrt(dist3/dist2);
06876     }
06877     
06878     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06879     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06880     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06881     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06882 
06883 
06884     /*
06885      * For each (X, Y) on the pseudo-plane related to the mask positions,
06886      * find the closest (X, Y) on the pseudo-plane related to the CCD
06887      * positions. If the closest point is closer than a given search
06888      * radius, a triangle has been found, and 3 slits are identified.
06889      * However, if more than one point is found within the search
06890      * radius, we have an ambiguity and this is rejected.
06891      */
06892 
06893     if (cpl_table_has_column(slits, "slit_id"))
06894         cpl_table_erase_column(slits, "slit_id");
06895     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06896     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06897 
06898     for (i = 1; i < nmaskslits - 1; i++) {
06899         in_sradius = 0;
06900         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06901                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06902         minpos = 1;
06903         if (mindist < sradius*sradius)
06904             in_sradius++;
06905         for (j = 2; j < nslits - 1; j++) {
06906             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06907                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06908             if (dist < sradius*sradius)
06909                 in_sradius++;
06910             if (in_sradius > 1)    /* More than one triangle within radius */
06911                 break;
06912             if (mindist > dist) {
06913                 mindist = dist;
06914                 minpos = j;
06915             }
06916         }
06917 
06918         mindist = sqrt(mindist);
06919 
06920         if (mindist < sradius && in_sradius == 1) {
06921             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06922             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06923             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06924         }
06925     }
06926 
06927 
06928     /*
06929      * At this point, the slit_id column contains invalid elements 
06930      * corresponding to unidentified slits.
06931      */
06932 
06933     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06934 
06935     if (found_slits < 3) {
06936         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06937                         "%d out of %d", found_slits, nslits);
06938         if (nslits == nmaskslits) {
06939             cpl_msg_warning(func, "(this is not an error, it could be caused "
06940                             "by a mask with regularly located slits)");
06941             cpl_msg_warning(func, "The detected slits positions are left "
06942                             "unchanged");
06943 
06944             /*
06945              * If there are less than 3 identified slits, this is probably 
06946              * a mask with regularly spaced slits (leading to an ambiguous
06947              * pattern). Only in the case all expected slits appear to have 
06948              * been found on the CCD we can proceed...
06949              */
06950 
06951             cpl_table_erase_column(slits, "slit_id");
06952             cpl_table_erase_column(slits, "xpseudo");
06953             cpl_table_erase_column(slits, "ypseudo");
06954             positions = cpl_table_duplicate(slits);
06955             cpl_table_erase_column(slits, "xcenter");
06956             cpl_table_erase_column(slits, "ycenter");
06957 
06958             cpl_table_erase_column(maskslits, "xpseudo");
06959             cpl_table_erase_column(maskslits, "ypseudo");
06960             cpl_table_duplicate_column(positions, "xmtop", 
06961                                        maskslits, "xtop");
06962             cpl_table_duplicate_column(positions, "ymtop", 
06963                                        maskslits, "ytop");
06964             cpl_table_duplicate_column(positions, "xmbottom", 
06965                                        maskslits, "xbottom");
06966             cpl_table_duplicate_column(positions, "ymbottom", 
06967                                        maskslits, "ybottom");
06968             cpl_table_duplicate_column(positions, "xmcenter", 
06969                                        maskslits, "xcenter");
06970             cpl_table_duplicate_column(positions, "ymcenter", 
06971                                        maskslits, "ycenter");
06972             cpl_table_duplicate_column(positions, "slit_id", 
06973                                        maskslits, "slit_id");
06974             cpl_table_erase_column(maskslits, "xcenter");
06975             cpl_table_erase_column(maskslits, "ycenter");
06976             return positions;
06977         }
06978         else {
06979             cpl_table_erase_column(slits, "slit_id");
06980             cpl_table_erase_column(slits, "xpseudo");
06981             cpl_table_erase_column(slits, "ypseudo");
06982             positions = cpl_table_duplicate(slits);
06983             cpl_table_erase_column(slits, "xcenter");
06984             cpl_table_erase_column(slits, "ycenter");
06985             cpl_msg_warning(func, "(the failure could be caused "
06986                             "by a mask with regularly located slits)");
06987             return NULL;
06988         }
06989     }
06990     else {
06991         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06992                      "candidates (%d expected)", found_slits, nslits, 
06993                      nmaskslits);
06994     }
06995 
06996 
06997     /*
06998      * Create a table with the coordinates of the preliminarily identified 
06999      * slits, both on CCD and mask. The original order of the slits positions 
07000      * is preserved.
07001      */
07002 
07003     positions = cpl_table_new(found_slits);
07004     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
07005     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
07006     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
07007     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
07008     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
07009     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
07010     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
07011     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
07012     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
07013     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
07014     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
07015     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
07016     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
07017     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
07018     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
07019 
07020     slit_id = cpl_table_get_data_int   (slits, "slit_id");
07021     xtop    = cpl_table_get_data_double(slits, "xtop");
07022     ytop    = cpl_table_get_data_double(slits, "ytop");
07023     xbottom = cpl_table_get_data_double(slits, "xbottom");
07024     ybottom = cpl_table_get_data_double(slits, "ybottom");
07025     xcenter = cpl_table_get_data_double(slits, "xcenter");
07026     ycenter = cpl_table_get_data_double(slits, "ycenter");
07027 
07028     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
07029     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
07030     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
07031     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
07032     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
07033     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
07034     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
07035 
07036 
07037     /*
07038      * Transferring the valid slits information to the new table.
07039      * Note that invalid elements are coded as 0 in the internal
07040      * buffer, and this is the way they are recognised and excluded.
07041      */
07042 
07043     k = 0;
07044     cpl_table_fill_invalid_int(slits, "slit_id", 0);
07045     for (i = 0; i < nmaskslits; i++) {
07046         for (j = 0; j < nslits; j++) {
07047             if (slit_id[j] == 0)
07048                 continue; /* Skip invalid slit */
07049             if (mslit_id[i] == slit_id[j]) {
07050                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
07051 
07052                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
07053                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
07054                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
07055                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
07056                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
07057                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
07058 
07059                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
07060                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
07061                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
07062                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
07063                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
07064                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
07065 
07066                 k++;
07067 
07068                 break;
07069             }
07070         }
07071     }
07072 
07073     found_slits = k;
07074 
07075     cpl_table_erase_column(slits, "slit_id");
07076     cpl_table_erase_column(slits, "xpseudo");
07077     cpl_table_erase_column(slits, "ypseudo");
07078     cpl_table_erase_column(slits, "xcenter");
07079     cpl_table_erase_column(slits, "ycenter");
07080     cpl_table_erase_column(maskslits, "xpseudo");
07081     cpl_table_erase_column(maskslits, "ypseudo");
07082     cpl_table_erase_column(maskslits, "xcenter");
07083     cpl_table_erase_column(maskslits, "ycenter");
07084 
07085 
07086     /*
07087      * Find the median platescale and rotation angle from the identified 
07088      * slits, and then exclude slits outlaying more than 10% from the 
07089      * median platescale, and more than 2 degrees from the median
07090      * rotation angle.
07091      */
07092 
07093     ytop    = cpl_table_get_data_double(positions, "ytop");
07094     ybottom = cpl_table_get_data_double(positions, "ybottom");
07095     xcenter = cpl_table_get_data_double(positions, "xcenter");
07096     ycenter = cpl_table_get_data_double(positions, "ycenter");
07097     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
07098     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
07099 
07100     scales = cpl_vector_new(found_slits - 1);
07101     dscale = cpl_vector_get_data(scales);
07102     angles = cpl_vector_new(found_slits - 1);
07103     dangle = cpl_vector_get_data(angles);
07104 
07105     for (i = 1; i < found_slits; i++) {
07106         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
07107               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
07108         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
07109               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
07110         dscale[i-1] = sqrt(dist1/dist2);
07111         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
07112                             xcenter[i-1] - xcenter[i])
07113                     - atan2(ymcenter[i-1] - ymcenter[i], 
07114                             xmcenter[i-1] - xmcenter[i]);
07115         dangle[i-1] *= 180;
07116         dangle[i-1] /= pi;
07117     }
07118 
07119     minscale = cpl_vector_get_min(scales);
07120     scale = cpl_vector_get_median_const(scales);
07121     maxscale = cpl_vector_get_max(scales);
07122 
07123     minangle = cpl_vector_get_min(angles);
07124     angle = cpl_vector_get_median_const(angles);
07125     maxangle = cpl_vector_get_max(angles);
07126 
07127     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
07128     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
07129                  minscale, maxscale);
07130 
07131     cpl_msg_info(func, "Median rotation: %f degrees", angle);
07132     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
07133                  minangle, maxangle);
07134 
07135     good = cpl_table_get_data_int(positions, "good");
07136 
07137     good[0] = good[found_slits - 1] = 1;
07138     for (i = 1; i < found_slits; i++) {
07139         if (fabs((dscale[i-1] - scale)/scale) < 0.10
07140          && fabs(dangle[i-1] - angle) < 2) {
07141             good[i-1]++;
07142             good[i]++;
07143         }
07144     }
07145 
07146     for (i = 0; i < found_slits; i++) {
07147         if (good[i] < 2)
07148             good[i] = 0;
07149         else
07150             good[i] = 1;
07151     }
07152 
07153 /*
07154     for (i = 1; i < found_slits; i++)
07155         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
07156             good[i-1] = good[i] = 1;
07157 */
07158 
07159 /* DEBUG ************+
07160     for (i = 0; i < found_slits; i++) {
07161         if (good[i]) {
07162             if (i == found_slits - 1)
07163                 printf("include slit %d, prev = %f, %f\n", 
07164                        i, dscale[i-1], dangle[i-1]);
07165             else if (i == 0)
07166                 printf("include slit %d, next %f, %f\n", 
07167                        i, dscale[i], dangle[i]);
07168             else
07169                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
07170                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07171         }
07172         else {
07173             if (i == found_slits - 1)
07174                 printf("EXclude slit %d, prev = %f, %f\n", 
07175                        i, dscale[i-1], dangle[i-1]);
07176             else if (i == 0)
07177                 printf("EXclude slit %d, next %f, %f\n", 
07178                        i, dscale[i], dangle[i]);
07179             else
07180                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
07181                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07182         }
07183     }
07184 +*********** DEBUG */
07185 
07186     cpl_vector_delete(scales);
07187     cpl_vector_delete(angles);
07188 
07189     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
07190     cpl_table_erase_selected(positions);
07191     cpl_table_erase_column(positions, "good");
07192     found_slits = cpl_table_get_nrow(positions);
07193 
07194     if (found_slits < 4) {
07195 
07196         /*
07197          * If the self-consistency check gives such a poor result,
07198          * something must have gone really wrong in the preliminary
07199          * wavelength calibration... Nothing can be done.
07200          */
07201 
07202         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
07203                         "candidates (%d expected). Process will continue "
07204                         "using the detected CCD slits positions", found_slits, 
07205                         nslits, nmaskslits);
07206         cpl_table_delete(positions);
07207         return NULL;
07208     }
07209     else {
07210         cpl_msg_info(func, "Safely identified slits: %d out of %d "
07211                      "candidates\n(%d expected)", found_slits, nslits,
07212                      nmaskslits);
07213     }
07214 
07215 
07216     /*
07217      * Now select the central points of the identified slits, and
07218      * fit two bivariate polynomials to determine a first approximate
07219      * relation between positions on the mask and positions on the CCD.
07220      */
07221 
07222     xpos = cpl_vector_wrap(found_slits, 
07223                            cpl_table_get_data_double(positions, "xcenter"));
07224     ypos = cpl_vector_wrap(found_slits, 
07225                            cpl_table_get_data_double(positions, "ycenter"));
07226     xmpos = cpl_vector_wrap(found_slits, 
07227                             cpl_table_get_data_double(positions, "xmcenter"));
07228     ympos = cpl_vector_wrap(found_slits, 
07229                             cpl_table_get_data_double(positions, "ymcenter"));
07230     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
07231 
07232     if (found_slits < 10)
07233         degree = 1;
07234     else
07235         degree = 2;
07236 
07237     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
07238     if (xpoly != NULL)
07239         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
07240     cpl_bivector_unwrap_vectors(mpos);
07241     cpl_vector_unwrap(xpos);
07242     cpl_vector_unwrap(ypos);
07243     cpl_vector_unwrap(xmpos);
07244     cpl_vector_unwrap(ympos);
07245     if (ypoly == NULL) {
07246         if (found_slits == nmaskslits) {
07247             cpl_msg_warning(func, "Fit failure: the accuracy of the "
07248                             "identified slits positions is not improved.");
07249 
07250             /*
07251              * The determination of the transformation from mask to CCD
07252              * failed, but since all slits have been already identified
07253              * this is not a problem: data can be reduced also without
07254              * such transformation. Simply the accuracy of the slits 
07255              * positions cannot be improved.
07256              */ 
07257 
07258         } else {
07259             cpl_msg_info(func, "Fit failure: not all slits have been "
07260                          "identified. Process will continue using "
07261                          "the detected CCD slits positions");
07262         }
07263 
07264         cpl_polynomial_delete(xpoly);
07265         return positions;
07266     }
07267 
07268     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07269                  sqrt(xmse), sqrt(ymse));
07270 
07271     if (global) {
07272         write_global_distortion(global, 0, xpoly);
07273         write_global_distortion(global, 7, ypoly);
07274     }
07275 
07276     /*
07277      * The fit was successful: use the polynomials to obtain a new 
07278      * position table with the improved positions of the slits
07279      */
07280 
07281     cpl_table_delete(positions);
07282 
07283     positions = cpl_table_duplicate(maskslits);
07284     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07285     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07286     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07287     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07288 
07289     point = cpl_vector_new(2);
07290     dpoint = cpl_vector_get_data(point);
07291 
07292     for (i = 0; i < nmaskslits; i++) {
07293         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07294         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07295         cpl_table_set_double(positions, "xtop", i, 
07296                              cpl_polynomial_eval(xpoly, point));
07297         cpl_table_set_double(positions, "ytop", i, 
07298                              cpl_polynomial_eval(ypoly, point));
07299         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07300         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07301         cpl_table_set_double(positions, "xbottom", i, 
07302                              cpl_polynomial_eval(xpoly, point));
07303         cpl_table_set_double(positions, "ybottom", i, 
07304                              cpl_polynomial_eval(ypoly, point));
07305     }
07306 
07307     cpl_vector_delete(point);
07308     cpl_polynomial_delete(xpoly);
07309     cpl_polynomial_delete(ypoly);
07310 
07311     cpl_table_erase_column(positions, "xmtop");
07312     cpl_table_erase_column(positions, "ymtop");
07313     cpl_table_erase_column(positions, "xmbottom");
07314     cpl_table_erase_column(positions, "ymbottom");
07315 
07316     if (nmaskslits > nslits)
07317         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07318                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07319     else if (nmaskslits < nslits)
07320         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07321                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07322     else
07323         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07324                  nmaskslits, nmaskslits);
07325 
07326     return positions;
07327 }
07328 
07329 
07371 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07372                           double blue, double red, double dispersion)
07373 {
07374 
07375     const char  *func = "mos_trace_flat";
07376 
07377     cpl_image   *gradient;
07378     cpl_image   *sgradient;
07379     float       *dgradient;
07380     float        level = 500;   /* It was 250... */
07381     cpl_vector  *row;
07382     cpl_vector  *srow;
07383     cpl_vector **peaks;
07384     double      *peak;
07385     int         *slit_id;
07386     float       *g;
07387     double      *r;
07388     double      *xtop;
07389     double      *ytop;
07390     double      *xbottom;
07391     double      *ybottom;
07392     double       min, dist;
07393     double       sradius;
07394     double       tolerance;
07395     double       start_y, prev_y;
07396     int          minpos;
07397     int          nslits;
07398     int          nrows;
07399     int          step = 10;
07400     int          filtbox = 15;
07401     int          nx, ny, npix;
07402     int          pos, ypos;
07403     int          npeaks;
07404     int          pixel_above, pixel_below;
07405     int          i, j, k, l;
07406     char         trace_id[MAX_COLNAME];
07407 
07408     cpl_table   *traces;
07409 
07410 
07411     if (flat == NULL || slits == NULL) {
07412         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07413         return NULL;
07414     }
07415 
07416     if (dispersion <= 0.0) {
07417         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07418         return NULL;
07419     }
07420 
07421     if (red - blue < dispersion) {
07422         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07423         return NULL;
07424     }
07425 
07426     /*
07427      * Create a dummy slit_id column if it is missing in the
07428      * input slits table
07429      */
07430 
07431     nslits  = cpl_table_get_nrow(slits);
07432     if (1 != cpl_table_has_column(slits, "slit_id")) {
07433         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07434         for (i = 0; i < nslits; i++)
07435             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07436     }
07437 
07438     slit_id = cpl_table_get_data_int(slits, "slit_id");
07439 
07440     nx = cpl_image_get_size_x(flat);
07441     ny = cpl_image_get_size_y(flat);
07442     npix = nx * ny;
07443 
07444     gradient = cpl_image_duplicate(flat);
07445     dgradient = cpl_image_get_data_float(gradient);
07446 
07447     for (i = 0; i < ny - 1; i++) {
07448         k = i * nx;
07449         for (j = 0; j < nx; j++) {
07450             l = k + j;
07451             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07452         }
07453     }
07454 
07455     npix--;
07456     for (j = 0; j < nx; j++)
07457         dgradient[npix - j] = 0.0;
07458 
07459     cpl_image_turn(gradient, -1);
07460     nx = cpl_image_get_size_x(gradient);
07461     ny = cpl_image_get_size_y(gradient);
07462     sgradient = mos_image_vertical_median_filter(gradient, 
07463                                                  filtbox, 0, ny, 0, step);
07464     cpl_image_delete(gradient);
07465 
07466 
07467     /*
07468      * Remove background from processed image rows
07469      */
07470 
07471     dgradient = cpl_image_get_data_float(sgradient);
07472 
07473     for (i = 1; i <= ny; i += step) {
07474         row = cpl_vector_new_from_image_row(sgradient, i);
07475         srow = cpl_vector_filter_median_create(row, filtbox);
07476         cpl_vector_subtract(row, srow);
07477         cpl_vector_delete(srow);
07478         g = dgradient + (i-1)*nx;
07479         r = cpl_vector_get_data(row);
07480         for (j = 0; j < nx; j++)
07481             g[j] = r[j];
07482         cpl_vector_delete(row);
07483     }
07484 
07485 
07486     /*
07487      * Rotate (temporarily) the input slits table, to get coordinates
07488      * compatible with the rotated gradient image.
07489      */
07490 
07491     mos_rotate_slits(slits, 1, nx, ny);
07492     xtop    = cpl_table_get_data_double(slits, "xtop");
07493     ytop    = cpl_table_get_data_double(slits, "ytop");
07494     xbottom = cpl_table_get_data_double(slits, "xbottom");
07495     ybottom = cpl_table_get_data_double(slits, "ybottom");
07496 
07497 
07498     /*
07499      * Get positions of peaks candidates for each processed gradient
07500      * image row
07501      */
07502 
07503     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07504 
07505     for (i = 0; i < ny; i += step) {
07506         g = dgradient + i*nx;
07507         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07508 
07509         /* I thought this would be required, but apparently I was wrong.
07510          * Check twice... */
07511         if (peaks[i])
07512             cpl_vector_subtract_scalar(peaks[i], 0.5);
07513          
07514     }
07515 
07516     cpl_image_delete(sgradient);
07517 
07518 
07519     /*
07520      * Tracing the flat field spectra edges, starting from the
07521      * slits positions obtained at reference wavelength. The 
07522      * gradient maximum closest to each slits ends is found and
07523      * accepted only within a given search radius:
07524      */
07525 
07526     sradius = 5.0;  /* Pixel */
07527 
07528     /*
07529      * The tracing proceeds along the processed gradient image rows,
07530      * above and below the start position, finding the closest peak
07531      * to the previous obtained position, accepting the new position
07532      * only if it is closer than a given tolerance:
07533      */
07534 
07535     tolerance = 0.9;  /* Pixel */
07536 
07537     /*
07538      * The trace is attempted for a certain number of pixels above
07539      * and below the position of the reference wavelength:
07540      */
07541 
07542     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
07543     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
07544 
07545 
07546     /*
07547      * Prepare the structure of the output table:
07548      */
07549 
07550     nrows = (ny-1)/step + 1;
07551     traces = cpl_table_new(nrows);
07552     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07553     cpl_table_set_column_unit(traces, "x", "pixel");
07554     for (i = 0, j = 0; i < ny; i += step, j++)
07555         cpl_table_set(traces, "x", j, i);
07556 
07557     for (i = 0; i < nslits; i++) {
07558 
07559         /*
07560          * Find the closest processed gradient image row
07561          */
07562 
07563         ypos = ytop[i];
07564 
07565         if (ypos < 0)
07566             ypos = 0;
07567         if (ypos >= ny)
07568             ypos = ny - 1;
07569 
07570         pos = ypos / step;
07571         pos *= step;
07572 
07573         /*
07574          * Find the peak in that row that is closest to xtop[i]
07575          */
07576 
07577         if (peaks[pos]) {
07578             peak = cpl_vector_get_data(peaks[pos]);
07579             npeaks = cpl_vector_get_size(peaks[pos]);
07580 
07581             min = fabs(peak[0] - xtop[i]);
07582             minpos = 0;
07583             for (j = 1; j < npeaks; j++) {
07584                 dist = fabs(peak[j] - xtop[i]);
07585                 if (min > dist) {
07586                     min = dist;
07587                     minpos = j;
07588                 }
07589             }
07590         }
07591         else {
07592             npeaks = 0;
07593         }
07594 
07595         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07596         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07597 
07598         if (min > sradius || npeaks == 0) {
07599             cpl_msg_warning(func, "Cannot find spectrum edge for "
07600                             "top (or left) end of slit %d", slit_id[i]);
07601         }
07602         else {
07603 
07604             /*
07605              * Add to output table the start y position. Note that
07606              * y positions are written in coordinates of the unrotated
07607              * image, i.e., with horizontal dispersion. Currently nx
07608              * is the x size of the temporarily rotated image (for
07609              * faster memory access), but it has the sense of a y size.
07610              */
07611 
07612             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07613             start_y = peak[minpos];
07614 
07615             /*
07616              * Perform the tracing of current edge. Above:
07617              */
07618 
07619             prev_y = start_y;
07620 
07621             for (j = pos + step; j < ny; j += step) {
07622                 if (j - pos > pixel_above)
07623                     break;
07624                 if (peaks[j]) {
07625                     peak = cpl_vector_get_data(peaks[j]);
07626                     npeaks = cpl_vector_get_size(peaks[j]);
07627                     min = fabs(peak[0] - prev_y);
07628                     minpos = 0;
07629                     for (k = 1; k < npeaks; k++) {
07630                         dist = fabs(peak[k] - prev_y);
07631                         if (min > dist) {
07632                             min = dist;
07633                             minpos = k;
07634                         }
07635                     }
07636                     if (min < tolerance) {
07637                         cpl_table_set(traces, trace_id, j/step, 
07638                                       nx - peak[minpos]);
07639                         prev_y = peak[minpos];
07640                     }
07641                 }
07642             }
07643 
07644             /*
07645              * Perform the tracing of current edge. Below:
07646              */
07647 
07648             prev_y = start_y;
07649 
07650             for (j = pos - step; j >= 0; j -= step) {
07651                 if (pos - j > pixel_below)
07652                     break;
07653                 if (peaks[j]) {
07654                     peak = cpl_vector_get_data(peaks[j]);
07655                     npeaks = cpl_vector_get_size(peaks[j]);
07656                     min = fabs(peak[0] - prev_y);
07657                     minpos = 0;
07658                     for (k = 1; k < npeaks; k++) {
07659                         dist = fabs(peak[k] - prev_y);
07660                         if (min > dist) {
07661                             min = dist;
07662                             minpos = k;
07663                         }
07664                     }
07665                     if (min < tolerance) {
07666                         cpl_table_set(traces, trace_id, j/step, 
07667                                       nx - peak[minpos]);
07668                         prev_y = peak[minpos];
07669                     }
07670                 }
07671             }
07672         }
07673 
07674 
07675         /*
07676          * Find the peak in that row that is closest to xbottom[i]
07677          */
07678 
07679         if (peaks[pos]) {
07680             peak = cpl_vector_get_data(peaks[pos]);
07681             npeaks = cpl_vector_get_size(peaks[pos]);
07682     
07683             min = fabs(peak[0] - xbottom[i]);
07684             minpos = 0;
07685             for (j = 1; j < npeaks; j++) {
07686                 dist = fabs(peak[j] - xbottom[i]);
07687                 if (min > dist) {
07688                     min = dist;
07689                     minpos = j;
07690                 }
07691             }
07692         }
07693         else {
07694             npeaks = 0;
07695         }
07696 
07697         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07698         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07699 
07700         if (min > sradius || npeaks == 0) {
07701             cpl_msg_warning(func, "Cannot find spectrum edge for "
07702                             "bottom (or right) end of slit %d", slit_id[i]);
07703         }
07704         else {
07705 
07706             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07707             start_y = peak[minpos]; 
07708 
07709             /*
07710              * Perform the tracing of current edge. Above:
07711              */
07712 
07713             prev_y = start_y;
07714 
07715             for (j = pos + step; j < ny; j += step) {
07716                 if (j - pos > pixel_above)
07717                     break;
07718                 if (peaks[j]) {
07719                     peak = cpl_vector_get_data(peaks[j]);
07720                     npeaks = cpl_vector_get_size(peaks[j]);
07721                     min = fabs(peak[0] - prev_y);
07722                     minpos = 0;
07723                     for (k = 1; k < npeaks; k++) {
07724                         dist = fabs(peak[k] - prev_y);
07725                         if (min > dist) {
07726                             min = dist;
07727                             minpos = k;
07728                         }
07729                     }
07730                     if (min < tolerance) {
07731                         cpl_table_set(traces, trace_id, j/step, 
07732                                       nx - peak[minpos]);
07733                         prev_y = peak[minpos];
07734                     }
07735                 }
07736             }
07737 
07738             /*
07739              * Perform the tracing of current edge. Below:
07740              */
07741 
07742             prev_y = start_y;
07743 
07744             for (j = pos - step; j >= 0; j -= step) {
07745                 if (pos - j > pixel_below)
07746                     break;
07747                 if (peaks[j]) {
07748                     peak = cpl_vector_get_data(peaks[j]);
07749                     npeaks = cpl_vector_get_size(peaks[j]);
07750                     min = fabs(peak[0] - prev_y);
07751                     minpos = 0;
07752                     for (k = 1; k < npeaks; k++) {
07753                         dist = fabs(peak[k] - prev_y);
07754                         if (min > dist) {
07755                             min = dist;
07756                             minpos = k;
07757                         }
07758                     }
07759                     if (min < tolerance) {
07760                         cpl_table_set(traces, trace_id, j/step, 
07761                                       nx - peak[minpos]);
07762                         prev_y = peak[minpos];
07763                     }
07764                 }
07765             }
07766         }
07767 
07768     }   /* End of loop on slits */
07769 
07770     for (i = 0; i < ny; i += step)
07771         cpl_vector_delete(peaks[i]);
07772     cpl_free(peaks);
07773 
07774     /*
07775      * Restore original orientation of slits positions table
07776      */
07777 
07778     mos_rotate_slits(slits, -1, ny, nx);
07779 
07780     return traces;
07781 
07782 }
07783 
07784 
07805 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07806 {
07807     const char *func = "mos_poly_trace";
07808 
07809     cpl_table      *polytraces;
07810     cpl_table      *dummy;
07811     cpl_vector     *x;
07812     cpl_vector     *trace;
07813     cpl_polynomial *polytrace;
07814     char            trace_id[MAX_COLNAME];
07815     char            trace_res[MAX_COLNAME];
07816     char            trace_mod[MAX_COLNAME];
07817     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07818                                                  /* Max order is 5 */
07819     double         *xdata;
07820     int            *slit_id;
07821     int             nslits;
07822     int             nrows;
07823     int             npoints;
07824     int             i, j;
07825     cpl_size        k;
07826 
07827 
07828     if (traces == NULL || slits == NULL) {
07829         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07830         return NULL;
07831     }
07832 
07833     if (order > 5) {
07834         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07835         return NULL;
07836     }
07837 
07838     nrows   = cpl_table_get_nrow(traces);
07839     xdata   = cpl_table_get_data_double(traces, "x");
07840     nslits  = cpl_table_get_nrow(slits);
07841     slit_id = cpl_table_get_data_int(slits, "slit_id");
07842 
07843     polytraces = cpl_table_new(2*nslits);
07844     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07845     for (i = 0; i <= order; i++)
07846         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07847 
07848     for (i = 0; i < nslits; i++) {
07849         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07850 
07851             if (j) {
07852                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07853                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07854                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07855             }
07856             else {
07857                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07858                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07859                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07860             }
07861 
07862             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07863 
07864             /*
07865              * The "dummy" table is just a tool for eliminating invalid
07866              * points from the vectors to be fitted.
07867              */
07868 
07869             dummy = cpl_table_new(nrows);
07870             cpl_table_duplicate_column(dummy, "x", traces, "x");
07871             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07872             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07873             if (npoints < 2 * order) {
07874                 cpl_table_delete(dummy);
07875                 continue;
07876             }
07877             cpl_table_erase_invalid(dummy);
07878             x     = cpl_vector_wrap(npoints, 
07879                                     cpl_table_get_data_double(dummy, "x"));
07880             trace = cpl_vector_wrap(npoints, 
07881                                     cpl_table_get_data_double(dummy, trace_id));
07882             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07883             cpl_vector_unwrap(x);
07884             cpl_vector_unwrap(trace);
07885             cpl_table_delete(dummy);
07886 
07887             /*
07888              * Screen bad solutions. At the moment, a primitive screening
07889              * consists in excluding solutions displaying excessive
07890              * curvature (larger than 1E-5 / pixel)
07891              */
07892 
07893             k = 2;
07894             if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) >  1.E-4) {
07895                 cpl_polynomial_delete(polytrace);
07896                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07897                 cpl_table_duplicate_column(traces, trace_res, traces, 
07898                                            trace_mod);
07899                 if (j) 
07900                     cpl_msg_warning(func, "Exclude bad curvature solution "
07901                            "for bottom (right) edge of slit %d", slit_id[i]);
07902                 else
07903                     cpl_msg_warning(func, "Exclude bad curvature solution "
07904                                 "for top (left) edge of slit %d", slit_id[i]);
07905                 continue;
07906             }
07907 
07908             /*
07909              * Write polynomial coefficients to the output table,
07910              * tagged with the appropriate slit_id
07911              */
07912 
07913             for (k = 0; k <= order; k++)
07914                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07915                                      cpl_polynomial_get_coeff(polytrace, &k));
07916 
07917             /*
07918              * Add column of residuals to input traces table
07919              */
07920 
07921             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07922             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07923 
07924             for (k = 0; k < nrows; k++) {
07925                 cpl_table_set_double(traces, trace_mod, k,
07926                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07927             }
07928 
07929             cpl_polynomial_delete(polytrace);
07930 
07931             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07932             cpl_table_subtract_columns(traces, trace_res, trace_id);
07933             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07934 
07935         }
07936     }
07937 
07938     return polytraces;
07939 
07940 }
07941 
07942 
07966 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07967                                 int mode)
07968 {
07969     const char *func = "mos_global_trace";
07970 
07971     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07972                                                  /* Max order is 5 */
07973     cpl_table      *table;
07974     cpl_vector     *c0;
07975     cpl_vector     *cn;
07976     cpl_bivector   *list;
07977 /* alternative (not robust)
07978     cpl_polynomial *poly;
07979 */
07980 
07981     double *offset;
07982     double  rms, q, m;
07983 
07984     int order, nrows, nslits;
07985     int i, j;
07986 
07987 
07988     if (polytraces == NULL) {
07989         cpl_msg_error(func, "Missing spectral curvature table");
07990         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07991     }
07992 
07993     if (slits == NULL) {
07994         cpl_msg_error(func, "Missing slits positions table");
07995         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07996     }
07997 
07998     nslits = cpl_table_get_nrow(slits);
07999 
08000     table = cpl_table_duplicate(polytraces);
08001     cpl_table_erase_invalid(table);
08002 
08003     nrows = cpl_table_get_nrow(table);
08004 
08005     if (nrows < 4) {
08006         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
08007                       "(%d): the determination of a global curvature model "
08008                       "failed", nrows);
08009         return CPL_ERROR_NONE;
08010     }
08011     
08012     order = cpl_table_get_ncol(polytraces) - 2;
08013 
08014     for (i = 0; i <= order; i++) {
08015         if (!cpl_table_has_column(table, clab[i])) {
08016             cpl_msg_error(func, "Wrong spectral curvature table");
08017             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08018         }
08019     }
08020 
08021 
08022     /*
08023      * Fill in advance the missing offset terms
08024      */
08025 
08026     for (i = 0; i < nslits; i++) {
08027         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
08028             cpl_table_set_double(polytraces, clab[0], 2*i, 
08029                                 cpl_table_get_double(slits, "ytop", i, NULL));
08030         }
08031         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
08032             cpl_table_set_double(polytraces, clab[0], 2*i+1,
08033                              cpl_table_get_double(slits, "ybottom", i, NULL));
08034         }
08035     }
08036 
08037     offset = cpl_table_get_data_double(polytraces, clab[0]);
08038 
08039 
08040     /*
08041      * Fit the global model and modify polytraces table accordingly
08042      */
08043 
08044     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
08045 
08046     for (i = 1; i <= order; i++) {
08047         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
08048         list = cpl_bivector_wrap_vectors(c0, cn);
08049         robustLinearFit(list, &q, &m, &rms);
08050 /* alternative (not robust)
08051         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
08052 */
08053         for (j = 0; j < 2*nslits; j++) {
08054             if (mode == 1)
08055                 if (cpl_table_is_valid(polytraces, clab[i], j))
08056                     continue;
08057             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
08058 /* alternative (not robust)
08059             cpl_table_set_double(polytraces, clab[i], j, 
08060                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
08061 */
08062         }
08063         cpl_bivector_unwrap_vectors(list);
08064 /* alternative (not robust)
08065         cpl_polynomial_delete(poly);
08066 */
08067         cpl_vector_unwrap(cn);
08068     }
08069 
08070     cpl_vector_unwrap(c0);
08071     cpl_table_delete(table);
08072 
08073     return CPL_ERROR_NONE;
08074 
08075 }
08076 
08077 
08147 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
08148                                    cpl_table *polytraces, double reference,
08149                                    double blue, double red, double dispersion,
08150                                    int flux, cpl_image *calibration)
08151 {
08152     const char *func = "mos_spatial_calibration";
08153 
08154     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08155                                                  /* Max order is 5 */
08156     cpl_polynomial *polytop;
08157     cpl_polynomial *polybot;
08158     cpl_image     **exslit;
08159     cpl_image      *resampled;
08160     float          *data;
08161     float          *sdata;
08162     float          *xdata;
08163     double          vtop, vbot, value;
08164     double          top, bot;
08165     double          coeff;
08166     double          ytop, ybot;
08167     double          ypos, yfra;
08168     double          factor;
08169     int             yint, ysize, yprev;
08170     int             nslits;
08171     int             npseudo;
08172     int            *slit_id;
08173     int            *length;
08174     int             nx, ny;
08175     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
08176     int             missing_top, missing_bot;
08177     int             null;
08178     int             order;
08179     int             i, j; 
08180     cpl_size        k;
08181 
08182     int             create_position = 1;
08183 
08184 
08185     if (spectra == NULL || slits == NULL || polytraces == NULL) {
08186         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08187         return NULL;
08188     }
08189 
08190     if (dispersion <= 0.0) {
08191         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08192         return NULL;
08193     }
08194 
08195     if (red - blue < dispersion) {
08196         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08197         return NULL;
08198     }
08199 
08200     nx = cpl_image_get_size_x(spectra);
08201     ny = cpl_image_get_size_y(spectra);
08202     sdata = cpl_image_get_data(spectra);
08203     if (calibration)
08204         data = cpl_image_get_data(calibration);
08205 
08206     if (cpl_table_has_column(slits, "position"))
08207         create_position = 0;
08208 
08209     if (create_position) {
08210         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
08211         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
08212         cpl_table_set_column_unit(slits, "position", "pixel");
08213         cpl_table_set_column_unit(slits, "length", "pixel");
08214     }
08215     else
08216         length = cpl_table_get_data_int(slits, "length");
08217 
08218     nslits   = cpl_table_get_nrow(slits);
08219     slit_id  = cpl_table_get_data_int(slits, "slit_id");
08220     order    = cpl_table_get_ncol(polytraces) - 2;
08221 
08222     /*
08223      * The spatial resampling is performed for a certain number of 
08224      * pixels above and below the position of the reference wavelength:
08225      */
08226 
08227     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
08228     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
08229 
08230     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
08231 
08232     for (i = 0; i < nslits; i++) {
08233         
08234         if (create_position == 0)
08235             if (length[i] == 0)
08236                 continue;
08237 
08238         /*
08239          * Note that the x coordinate of the reference pixels on the CCD
08240          * is taken arbitrarily at the top end of each slit. This wouldn't
08241          * be entirely correct in case of curved slits, or in presence of
08242          * heavy distortions: in such cases the spatial resampling is
08243          * really performed across a wide range of wavelengths. But
08244          * the lag between top and bottom spectral curvature models 
08245          * would introduce even in such cases negligible effects on
08246          * the spectral spatial resampling.
08247          */
08248 
08249         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
08250 
08251         start_pixel = refpixel - pixel_below;
08252         if (start_pixel < 0)
08253             start_pixel = 0;
08254 
08255         end_pixel = refpixel + pixel_above;
08256         if (end_pixel > nx)
08257             end_pixel = nx;
08258 
08259         /*
08260          * Recover from the table of spectral curvature coefficients
08261          * the curvature polynomials.
08262          */
08263 
08264         missing_top = 0;
08265         polytop = cpl_polynomial_new(1);
08266         for (k = 0; k <= order; k++) {
08267             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08268             if (null) {
08269                 cpl_polynomial_delete(polytop);
08270                 missing_top = 1;
08271                 break;
08272             }
08273             cpl_polynomial_set_coeff(polytop, &k, coeff);
08274         }
08275 
08276         missing_bot = 0;
08277         polybot = cpl_polynomial_new(1);
08278         for (k = 0; k <= order; k++) {
08279             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08280             if (null) {
08281                 cpl_polynomial_delete(polybot);
08282                 missing_bot = 1;
08283                 break;
08284             }
08285             cpl_polynomial_set_coeff(polybot, &k, coeff);
08286         }
08287 
08288         if (missing_top && missing_bot) {
08289             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08290                             "traced: no extraction!", 
08291                             slit_id[i]);
08292             continue;
08293         }
08294 
08295         /*
08296          * In case just one of the two edges was not traced, the other
08297          * edge curvature model is duplicated and shifted to the other
08298          * end of the slit: better than nothing!
08299          */
08300 
08301         if (missing_top) {
08302             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08303                             "the spectral curvature of the lower edge "
08304                             "is used instead.", slit_id[i]);
08305             polytop = cpl_polynomial_duplicate(polybot);
08306             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08307             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08308             k = 0;
08309             coeff = cpl_polynomial_get_coeff(polybot, &k);
08310             coeff += ytop - ybot;
08311             cpl_polynomial_set_coeff(polytop, &k, coeff);
08312         }
08313 
08314         if (missing_bot) {
08315             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08316                             "the spectral curvature of the upper edge "
08317                             "is used instead.", slit_id[i]);
08318             polybot = cpl_polynomial_duplicate(polytop);
08319             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08320             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08321             k = 0;
08322             coeff = cpl_polynomial_get_coeff(polytop, &k);
08323             coeff -= ytop - ybot;
08324             cpl_polynomial_set_coeff(polybot, &k, coeff);
08325         }
08326 
08327         /*
08328          * Allocate image for current extracted slit
08329          */
08330 
08331         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08332         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08333         npseudo = ceil(top-bot) + 1;
08334 
08335         if (npseudo < 1) {
08336             cpl_polynomial_delete(polytop);
08337             cpl_polynomial_delete(polybot);
08338             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08339                             slit_id[i]);
08340             continue;
08341         }
08342 
08343         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08344         xdata = cpl_image_get_data(exslit[i]);
08345 
08346         /*
08347          * Write interpolated values to slit image.
08348          */
08349 
08350         for (j = start_pixel; j < end_pixel; j++) {
08351             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08352             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08353             factor = (top-bot)/npseudo;
08354             for (k = 0; k <= npseudo; k++) {
08355                 ypos = top - k*factor;
08356                 yint = ypos;
08357                 yfra = ypos - yint;
08358                 if (yint >= 0 && yint < ny-1) {
08359                     vtop = sdata[j + nx*yint];
08360                     vbot = sdata[j + nx*(yint+1)];
08361                     value = vtop*(1-yfra) + vbot*yfra;
08362                     if (flux)
08363                         value *= factor;
08364                     xdata[j + nx*(npseudo-k)] = value;
08365                     if (calibration) {
08366                         data[j + nx*yint] = (top-yint)/factor;
08367                         if (k) {
08368 
08369                             /*
08370                              * This is added to recover lost pixels on
08371                              * the CCD image (pixels are lost because
08372                              * the CCD pixels are less than npseudo+1).
08373                              */
08374 
08375                             if (yprev - yint > 1) {
08376                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08377                             }
08378                         }
08379                     }
08380                 }
08381                 yprev = yint;
08382             }
08383         }
08384         cpl_polynomial_delete(polytop);
08385         cpl_polynomial_delete(polybot);
08386     }
08387 
08388     /*
08389      * Now all the slits images are copied to a single image
08390      */
08391 
08392     ysize = 0;
08393     for (i = 0; i < nslits; i++)
08394         if (exslit[i])
08395             ysize += cpl_image_get_size_y(exslit[i]);
08396 
08397     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08398 
08399     yint = -1;
08400     for (i = 0; i < nslits; i++) {
08401         if (exslit[i]) {
08402             yint += cpl_image_get_size_y(exslit[i]);
08403             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08404             if (create_position) {
08405                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08406                 cpl_table_set_int(slits, "length", i, 
08407                                   cpl_image_get_size_y(exslit[i]));
08408             }
08409             cpl_image_delete(exslit[i]);
08410         }
08411         else if (create_position) {
08412             cpl_table_set_int(slits, "position", i, -1);
08413             cpl_table_set_int(slits, "length", i, 0);
08414         }
08415     }
08416 
08417 
08418     /*
08419      * Elimination of non-traced slits from slit position table: we cannot do
08420      * it because we would lose sync with polytraces and other slit-oriented
08421      * tables. COMMENTED OUT.
08422      * 
08423 
08424     if (create_position) {
08425 
08426         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08427             cpl_table_erase_selected(slits);
08428 
08429     }
08430 
08431     */
08432 
08433     cpl_free(exslit);
08434 
08435     return resampled;
08436 
08437 }
08438 
08439 
08546 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08547                                             cpl_vector *lines,
08548                                             double dispersion, float level,
08549                                             int sradius, int order,
08550                                             double reject, double refwave,
08551                                             double *wavestart, double *waveend,
08552                                             int *nlines, double *error, 
08553                                             cpl_table *idscoeff,
08554                                             cpl_image *calibration,
08555                                             cpl_image *residuals,
08556                                             cpl_table *restable)
08557 {
08558 
08559     const char *func = "mos_wavelength_calibration_final";
08560 
08561     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08562                                                  /* Max order is 5 */
08563 
08564     double  tolerance = 20.0;    /* Probably forever...                */
08565     int     step = 10;           /* Compute restable every "step" rows */
08566 
08567     char            name[MAX_COLNAME];
08568 
08569     cpl_image      *resampled;
08570     cpl_bivector   *output;
08571     cpl_vector     *wavel;
08572     cpl_vector     *peaks;
08573     cpl_polynomial *ids;
08574     cpl_polynomial *lin;
08575     cpl_polynomial *fguess;
08576     cpl_table      *coeff;
08577     double          ids_err;
08578     double          max_disp, min_disp;
08579     double         *line;
08580     double          firstLambda, lastLambda, lambda;
08581     double          wave, pixe, value;
08582     double          c;
08583     float          *sdata;
08584     float          *rdata;
08585     float          *idata;
08586     float          *ddata;
08587     float           v1, v2, vi;
08588     float           fpixel;
08589     int            *length;
08590     int             pixstart, pixend;
08591     int             row_top, row_bot;
08592     int             extrapolation;
08593     int             nref;
08594     int             nslits;
08595     int             nfits;
08596     int             nl, nx, ny, pixel;
08597     int             countLines, usedLines;
08598     int             uorder;
08599     int             missing;
08600     int             null;
08601     int             width, uradius;
08602     int             i, j, s;
08603     cpl_size        k;
08604 
08605 
08606     if (dispersion == 0.0) {
08607         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
08608         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08609         return NULL;
08610     }
08611 
08612     if (dispersion < 0.0) {
08613         cpl_msg_error(func, "The expected dispersion must be positive");
08614         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08615         return NULL;
08616     }
08617 
08618     if (idscoeff == NULL) {
08619         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
08620         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08621         return NULL;
08622     }
08623 
08624     max_disp = dispersion + dispersion * tolerance / 100;
08625     min_disp = dispersion - dispersion * tolerance / 100;
08626 
08627     if (order < 1) {
08628         cpl_msg_error(func, "The order of the fitting polynomial "
08629                       "must be at least 1");
08630         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08631         return NULL;
08632     }
08633 
08634     if (image == NULL || lines == NULL) {
08635         cpl_msg_error(func, "Both spectral exposure and reference line "
08636                       "catalog are required in input");
08637         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08638         return NULL;
08639     }
08640 
08641     nx = cpl_image_get_size_x(image);
08642     ny = cpl_image_get_size_y(image);
08643     sdata = cpl_image_get_data_float(image);
08644 
08645     nref = cpl_vector_get_size(lines);
08646     line = cpl_vector_get_data(lines);
08647 
08648     if (*wavestart < 1.0 && *waveend < 1.0) {
08649         firstLambda = line[0];
08650         lastLambda = line[nref-1];
08651         extrapolation = (lastLambda - firstLambda) / 10;
08652         firstLambda -= extrapolation;
08653         lastLambda += extrapolation;
08654         *wavestart = firstLambda;
08655         *waveend = lastLambda;
08656     }
08657     else {
08658         firstLambda = *wavestart;
08659         lastLambda = *waveend;
08660     }
08661 
08662     nl = (lastLambda - firstLambda) / dispersion;
08663     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08664     rdata = cpl_image_get_data_float(resampled);
08665 
08666     /*
08667      * Allocate total output table of IDS coefficients
08668      */
08669 
08670     for (j = 0; j <= order; j++)
08671         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08672 
08673     if (calibration)
08674         idata = cpl_image_get_data_float(calibration);
08675 
08676     if (residuals)
08677         ddata = cpl_image_get_data_float(residuals);
08678 
08679     if (restable) {
08680         cpl_table_set_size(restable, nref);
08681         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08682         cpl_table_copy_data_double(restable, "wavelength", line);
08683         for (i = 0; i < ny; i += step) {
08684              snprintf(name, MAX_COLNAME, "r%d", i);
08685              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08686              snprintf(name, MAX_COLNAME, "d%d", i);
08687              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08688              snprintf(name, MAX_COLNAME, "p%d", i);
08689              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08690         }
08691     }
08692 
08693 
08694     /*
08695      * Process all slits separately.
08696      */
08697 
08698     nslits   = cpl_table_get_nrow(slits);
08699     length   = cpl_table_get_data_int(slits, "length");
08700 
08701     row_top = ny;
08702     for (s = 0; s < nslits; s++) {
08703 
08704         if (length[s] == 0)
08705             continue;
08706 
08707         /*
08708          * row_top and row_bot define the boundaries of the current slit.
08709          * Here we begin (arbitrarily...) from the top slit.
08710          */
08711 
08712         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08713 
08714         if (sradius > 0) {
08715 
08716             /*
08717              * If a search radius was defined, allocate the table of
08718              * the fitting polynomials coefficients. This table is
08719              * just used to generate the first-guess polynomial made
08720              * of the median coefficients of all polynomials found
08721              * for this slit.
08722              */
08723 
08724             coeff = cpl_table_new(row_top - row_bot);
08725             for (j = 0; j <= order; j++)
08726                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08727         }
08728 
08729         /*
08730          * Here is the loop on all rows of the current slit. They are
08731          * wavelength calibrated one by one.
08732          */
08733 
08734         for (i = row_bot; i < row_top; i++) {
08735             width = mos_lines_width(sdata + i*nx, nx);
08736             if (width < 5)
08737                 width = 5;
08738             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08739             if (peaks) {
08740                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08741             }
08742             if (peaks) {
08743                 int keep_multiplex = mos_multiplex;
08744                 mos_multiplex = -1;
08745                 output = mos_identify_peaks(peaks, lines, 
08746                                             min_disp, max_disp, 0.05);
08747                 mos_multiplex = keep_multiplex;
08748                 if (output) {
08749                     countLines = cpl_bivector_get_size(output);
08750                     if (countLines < 4) {
08751                         cpl_bivector_delete(output);
08752                         cpl_vector_delete(peaks);
08753                         if (nlines)
08754                             nlines[i] = 0;
08755                         if (error)
08756                             error[i] = 0.0;
08757                         continue;
08758                     }
08759 
08760                     /*
08761                      * Set reference wavelength as zero point
08762                      */
08763 
08764                     wavel = cpl_bivector_get_y(output);
08765                     cpl_vector_subtract_scalar(wavel, refwave);
08766 
08767                     uorder = countLines / 2 - 1;
08768                     if (uorder > order)
08769                         uorder = order;
08770 
08771                     ids = mos_poly_wav2pix(output, uorder, reject,
08772                                            2 * (uorder + 1), &usedLines,
08773                                            &ids_err);
08774 
08775                     if (ids == NULL) {
08776                         cpl_bivector_delete(output);
08777                         cpl_vector_delete(peaks);
08778                         if (nlines)
08779                             nlines[i] = 0;
08780                         if (error)
08781                             error[i] = 0.0;
08782                         cpl_error_reset();
08783                         continue;
08784                     }
08785 
08786                     if (sradius > 0) {
08787                         for (k = 0; k <= order; k++) {
08788                             if (k > uorder) {
08789                                 cpl_table_set_double(coeff, clab[k], 
08790                                 i - row_bot, 0.0);
08791                             }
08792                             else {
08793                                 cpl_table_set_double(coeff, clab[k], 
08794                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08795                             }
08796                         }
08797                     }
08798                /*   else {   */
08799                         if (calibration) {
08800                             pixstart = cpl_polynomial_eval_1d(ids,
08801                               cpl_bivector_get_y_data(output)[0], 
08802                               NULL);
08803                             pixend = cpl_polynomial_eval_1d(ids,
08804                               cpl_bivector_get_y_data(output)[countLines-1],
08805                               NULL);
08806                             extrapolation = (pixend - pixstart) / 5;
08807                             pixstart -= extrapolation;
08808                             pixend += extrapolation;
08809                             if (pixstart < 0)
08810                                 pixstart = 0;
08811                             if (pixend > nx)
08812                                 pixend = nx;
08813    
08814                             for (j = pixstart; j < pixend; j++) {
08815                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08816                                      firstLambda, lastLambda, refwave, j);
08817                             }
08818                         }
08819 
08820                         /*
08821                          * Residuals image
08822                          */
08823         
08824                         if (residuals || (restable && !(i%step))) {
08825                             if (restable && !(i%step)) {
08826                                 lin = cpl_polynomial_new(1);
08827                                 for (k = 0; k < 2; k++)
08828                                     cpl_polynomial_set_coeff(lin, &k,
08829                                           cpl_polynomial_get_coeff(ids, &k));
08830                             }
08831                             for (j = 0; j < countLines; j++) {
08832                                 pixe = cpl_bivector_get_x_data(output)[j];
08833                                 wave = cpl_bivector_get_y_data(output)[j];
08834                                 value = pixe 
08835                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08836                                 if (residuals) {
08837                                     pixel = pixe + 0.5;
08838                                     (ddata + i*nx)[pixel] = value;
08839                                 }
08840                                 if (restable && !(i%step)) {
08841                                     for (k = 0; k < nref; k++) {
08842                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08843                                             snprintf(name, MAX_COLNAME, 
08844                                                      "r%d", i);
08845                                             cpl_table_set_double(restable, name,
08846                                                                  k, value);
08847                                             value = pixe
08848                                                   - cpl_polynomial_eval_1d(lin,
08849                                                               wave, NULL);
08850                                             snprintf(name, MAX_COLNAME, 
08851                                                      "d%d", i);
08852                                             cpl_table_set_double(restable, name,
08853                                                                  k, value);
08854                                             snprintf(name, MAX_COLNAME,
08855                                                      "p%d", i);
08856                                             cpl_table_set_double(restable, name,
08857                                                                  k, pixe);
08858                                             break;
08859                                         }
08860                                     }
08861                                 }
08862                             }
08863                             if (restable && !(i%step)) {
08864                                 cpl_polynomial_delete(lin);
08865                             }
08866 /***
08867                             for (j = 0; j < countLines; j++) {
08868                                 pixel = cpl_bivector_get_x_data(output)[j] 
08869                                       + 0.5;
08870                                 (ddata + i*nx)[pixel] =
08871                                 cpl_bivector_get_x_data(output)[j]
08872                               - cpl_polynomial_eval_1d(ids,
08873                                 cpl_bivector_get_y_data(output)[j], 
08874                                 NULL);
08875                             }
08876 ***/
08877                         }
08878                 /*  }   */
08879 
08880                     /*
08881                      * Write it anyway, even in case a first-guess based
08882                      * solution will be searched afterwards: in case of
08883                      * failure, the "blind" solution is kept.
08884                      */
08885 
08886                     if (nlines)
08887                         nlines[i] = usedLines;
08888                     if (error)
08889                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08890 
08891                     for (k = 0; k <= order; k++) {
08892                         if (k > uorder) {
08893                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08894                         }
08895                         else {
08896                             cpl_table_set_double(idscoeff, clab[k], i,
08897                                       cpl_polynomial_get_coeff(ids, &k));
08898                         }
08899                     }
08900 
08901                     cpl_polynomial_delete(ids);
08902                     cpl_bivector_delete(output);
08903                 }
08904                 cpl_vector_delete(peaks);
08905             }
08906         }       /* End of loop on current slit's rows */
08907 
08908 
08909         if (sradius > 0) {
08910 
08911             /*
08912              * See whether there are valid fits at all...
08913              */
08914     
08915             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08916     
08917             if (nfits) {
08918                 int slope = 0;
08919 
08920                 fguess = cpl_polynomial_new(1);
08921 
08922                 if (mos_interpolate_wavecalib(coeff, NULL, 2, 1)) {
08923 
08924                     slope = 0;
08925 
08926                     /*
08927                      * Compute a median IDS polynomial for the current slit
08928                      */
08929 
08930                     for (k = 0; k <= order; k++) {
08931                         c = cpl_table_get_column_median(coeff, clab[k]);
08932                         cpl_polynomial_set_coeff(fguess, &k, c);
08933                     }
08934                 }
08935                 else {
08936                     slope = 1;
08937                 }
08938 
08939                 for (i = row_bot; i < row_top; i++) {
08940 
08941                     /*
08942                      * Use first-guess to find the reference lines again
08943                      */
08944 
08945                     width = mos_lines_width(sdata + i*nx, nx);
08946                     if (width > sradius) {
08947                         uradius = width; 
08948                     }
08949                     else {
08950                         uradius = sradius;
08951                     }
08952 
08953                     if (slope) {
08954                         for (k = 0; k <= order; k++) {
08955                             c = cpl_table_get_double(coeff, clab[k], 
08956                                                      i - row_bot, NULL);
08957                             cpl_polynomial_set_coeff(fguess, &k, c);
08958                         }
08959                     }
08960 
08961                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08962                                             fguess, refwave, uradius);
08963 
08964                     if (output == NULL) {
08965                         cpl_error_reset();
08966                         continue;
08967                     }
08968 
08969                     countLines = cpl_bivector_get_size(output);
08970 
08971                     if (countLines < 4) {
08972                         cpl_bivector_delete(output);
08973                         continue;
08974                     }
08975 
08976                     /*
08977                      * Set reference wavelength as zero point
08978                      */
08979 
08980                     wavel = cpl_bivector_get_y(output);
08981                     cpl_vector_subtract_scalar(wavel, refwave);
08982 
08983                     uorder = countLines / 2 - 1;
08984                     if (uorder > order)
08985                         uorder = order;
08986 
08987                     ids = mos_poly_wav2pix(output, uorder, reject,
08988                                            2 * (uorder + 1), &usedLines,
08989                                            &ids_err);
08990 
08991                     if (ids == NULL) {
08992                         cpl_error_reset();
08993                         cpl_bivector_delete(output);
08994                         continue;
08995                     }
08996 
08997                     if (nlines)
08998                         nlines[i] = usedLines;
08999                     if (error)
09000                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
09001 
09002                     if (calibration) {
09003                         pixstart = cpl_polynomial_eval_1d(ids,
09004                            cpl_bivector_get_y_data(output)[0], 
09005                            NULL);
09006                         pixend = cpl_polynomial_eval_1d(ids,
09007                            cpl_bivector_get_y_data(output)[countLines-1], 
09008                            NULL);
09009                         extrapolation = (pixend - pixstart) / 5;
09010                         pixstart -= extrapolation;
09011                         pixend += extrapolation;
09012                         if (pixstart < 0)
09013                             pixstart = 0;
09014                         if (pixend > nx)
09015                             pixend = nx;
09016 
09017                         for (j = pixstart; j < pixend; j++) {
09018                             (idata + i*nx)[j] = mos_eval_dds(ids,
09019                                      firstLambda, lastLambda, refwave, j);
09020                         }
09021                     }
09022 
09023                     /*
09024                      * Residuals image
09025                      */
09026 
09027                     if (residuals || (restable && !(i%step))) {
09028                         if (restable && !(i%step)) {
09029                             lin = cpl_polynomial_new(1);
09030                             for (k = 0; k < 2; k++)
09031                                 cpl_polynomial_set_coeff(lin, &k,
09032                                       cpl_polynomial_get_coeff(ids, &k));
09033                         }
09034                         for (j = 0; j < countLines; j++) {
09035                             pixe = cpl_bivector_get_x_data(output)[j];
09036                             wave = cpl_bivector_get_y_data(output)[j];
09037                             value = pixe
09038                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
09039                             if (residuals) {
09040                                 pixel = pixe + 0.5;
09041                                 (ddata + i*nx)[pixel] = value;
09042                             }
09043                             if (restable && !(i%step)) {
09044                                 for (k = 0; k < nref; k++) {
09045                                     if (fabs(line[k]-refwave-wave) < 0.1) {
09046                                         snprintf(name, MAX_COLNAME,
09047                                                  "r%d", i);
09048                                         cpl_table_set_double(restable, name,
09049                                                              k, value);
09050                                         value = pixe
09051                                               - cpl_polynomial_eval_1d(lin,
09052                                                           wave, NULL);
09053                                         snprintf(name, MAX_COLNAME,
09054                                                  "d%d", i);
09055                                         cpl_table_set_double(restable, name,
09056                                                              k, value);
09057                                         snprintf(name, MAX_COLNAME,
09058                                                  "p%d", i);
09059                                         cpl_table_set_double(restable, name,
09060                                                              k, pixe);
09061                                         break;
09062                                     }
09063                                 }
09064                             }
09065                         }
09066                         if (restable && !(i%step)) {
09067                             cpl_polynomial_delete(lin);
09068                         }
09069 /***
09070                         for (j = 0; j < countLines; j++) {
09071                             pixel = cpl_bivector_get_x_data(output)[j]
09072                                   + 0.5; 
09073                             (ddata + i*nx)[pixel] =
09074                             cpl_bivector_get_x_data(output)[j]
09075                           - cpl_polynomial_eval_1d(ids,
09076                             cpl_bivector_get_y_data(output)[j], 
09077                             NULL);
09078                         }
09079 ***/
09080                     }
09081 
09082                     for (k = 0; k <= order; k++) {
09083                         if (k > uorder) {
09084                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
09085                         }
09086                         else {
09087                             cpl_table_set_double(idscoeff, clab[k], i,
09088                                         cpl_polynomial_get_coeff(ids, &k));
09089                         }
09090                     }
09091 
09092                     cpl_bivector_delete(output);
09093                     cpl_polynomial_delete(ids);
09094 
09095                 } /* End of loop "use ids as a first-guess" */
09096 
09097                 cpl_polynomial_delete(fguess);
09098             }
09099 
09100             cpl_table_delete(coeff);
09101 
09102         }
09103 
09104         row_top = row_bot;
09105 
09106     } /* End of loop on slits */
09107 
09108 
09109     /*
09110      * At this point the idscoeff table has been filled with all the 
09111      * fits coefficients obtained for all the rows of the input image.
09112      * Now we apply these coefficients to resample the input image
09113      * at constant wavelength step.
09114      */
09115 
09116     for (i = 0; i < ny; i++) {
09117 
09118         missing = 0;
09119         ids = cpl_polynomial_new(1);
09120         for (k = 0; k <= order; k++) {
09121             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09122             if (null) {
09123                 cpl_polynomial_delete(ids);
09124                 missing = 1;
09125                 break;
09126             }
09127             cpl_polynomial_set_coeff(ids, &k, c);
09128         }
09129         if (missing)
09130             continue;
09131 
09132         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09133         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09134         if (pixstart < 0)
09135             pixstart = 0;
09136         if (pixend > nx)
09137             pixend = nx;
09138 
09139         /*
09140          * Resampled image:
09141          */
09142 
09143         for (j = 0; j < nl; j++) {
09144             lambda = firstLambda + j * dispersion;
09145             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
09146             pixel = fpixel;
09147             if (pixel >= 0 && pixel < nx-1) {
09148                 v1 = (sdata + i*nx)[pixel];
09149                 v2 = (sdata + i*nx)[pixel+1];
09150                 vi = v1 + (v2-v1)*(fpixel-pixel);
09151                 (rdata + i*nl)[j] = vi;
09152             }
09153         }
09154 
09155         cpl_polynomial_delete(ids);
09156     }
09157 
09158     return resampled;
09159 }
09160 
09161 
09188 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
09189                                       double firstLambda, double lastLambda, 
09190                                       double dispersion, cpl_table *idscoeff, 
09191                                       int flux)
09192 {
09193 
09194     const char *func = "mos_wavelength_calibration";
09195 
09196     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09197                                                  /* Max order is 5 */
09198 
09199     cpl_image      *resampled;
09200     cpl_polynomial *ids;
09201     double          pixel_per_lambda;
09202     double          lambda;
09203     double          c;
09204     float          *sdata;
09205     float          *rdata;
09206     float           v0, v1, v2, v3, vi;
09207     float           fpixel;
09208     int             order;
09209     int             pixstart, pixend;
09210     int             nl, nx, ny, pixel;
09211     int             missing;
09212     int             null;
09213     int             i, j;
09214     cpl_size        k;
09215 
09216 
09217     if (dispersion <= 0.0) {
09218         cpl_msg_error(func, "The resampling step must be positive");
09219         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09220         return NULL;
09221     }
09222 
09223     if (lastLambda - firstLambda < dispersion) {
09224         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
09225                       firstLambda, lastLambda);
09226         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09227         return NULL;
09228     }
09229 
09230     if (idscoeff == NULL) {
09231         cpl_msg_error(func, "An IDS coeff table must be given");
09232         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09233         return NULL;
09234     }
09235 
09236     if (image == NULL) {
09237         cpl_msg_error(func, "A scientific spectral image must be given");
09238         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09239         return NULL;
09240     }
09241 
09242     nx = cpl_image_get_size_x(image);
09243     ny = cpl_image_get_size_y(image);
09244     sdata = cpl_image_get_data_float(image);
09245 
09246     nl = (lastLambda - firstLambda) / dispersion;
09247     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
09248     rdata = cpl_image_get_data_float(resampled);
09249 
09250     order = 0;
09251     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
09252         ++order;
09253     --order;
09254 
09255     for (i = 0; i < ny; i++) {
09256 
09257         missing = 0;
09258         ids = cpl_polynomial_new(1);
09259         for (k = 0; k <= order; k++) {
09260             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09261             if (null) {
09262                 cpl_polynomial_delete(ids);
09263                 missing = 1;
09264                 break;
09265             }
09266             cpl_polynomial_set_coeff(ids, &k, c);
09267         }
09268         if (missing)
09269             continue;
09270 
09271         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09272         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09273         if (pixstart < 0)
09274             pixstart = 0;
09275         if (pixend > nx)
09276             pixend = nx;
09277 
09278         /*
09279          * Resampled image:
09280          */
09281 
09282         for (j = 0; j < nl; j++) {
09283             lambda = firstLambda + j * dispersion;
09284             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09285                                             &pixel_per_lambda);
09286 
09287             /*
09288              * The local dispersion is 1 / pixel_per_lambda
09289              * and this factor is used for applying the flux
09290              * conservation correction (if requested).
09291              */
09292 
09293             pixel = fpixel;
09294 
09295          // if (dispersion * pixel_per_lambda < 2.0) {
09296             if (1) {  /* Old behaviour: this is safe. */
09297 
09298                 /*
09299                  * In this case we just sample interpolating the
09300                  * signal of nearby pixels.
09301                  */
09302 
09303                if (pixel >= 1 && pixel < nx-2) {
09304                    v0 = (sdata + i*nx)[pixel-1];
09305                    v1 = (sdata + i*nx)[pixel];
09306                    v2 = (sdata + i*nx)[pixel+1];
09307                    v3 = (sdata + i*nx)[pixel+2];
09308                    vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09309                       + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09310                       + 2*v1;
09311                    vi /= 2;
09312                    if (v1 > v2) {
09313                        if (vi > v1) { 
09314                            vi = v1;
09315                        }
09316                        else if (vi < v2) {
09317                            vi = v2;
09318                        }
09319                    }
09320                    else {
09321                        if (vi > v2) { 
09322                            vi = v2;
09323                        }
09324                        else if (vi < v1) {
09325                            vi = v1;
09326                        }
09327                    }
09328                    if (flux)
09329                        vi *= dispersion * pixel_per_lambda;
09330                    (rdata + i*nl)[j] = vi;
09331                }
09332                else if (pixel >= 0 && pixel < nx-1) {
09333                    v1 = (sdata + i*nx)[pixel];
09334                    v2 = (sdata + i*nx)[pixel+1];
09335                    vi = v1 + (v2-v1)*(fpixel-pixel);
09336                    if (flux)
09337                        vi *= dispersion * pixel_per_lambda;
09338                    (rdata + i*nl)[j] = vi;
09339                }
09340            }
09341            else {
09342 
09343                /*
09344                 * Here instead we integrate the pixel values in
09345                 * the interval centered at the interpolation point.
09346                 * This interval is long dispersion * pixel_per_lambda
09347                 * of the original pixels, and is centered at fpixel.
09348                 * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
09349                 * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
09350                 */
09351 
09352                double spos = fpixel - dispersion * pixel_per_lambda / 2;
09353                double epos = fpixel + dispersion * pixel_per_lambda / 2;
09354 
09355                /*
09356                 * Brutal sum over all involved pixels
09357                 */
09358 
09359                int spix = spos;
09360                int epix = epos + 1;
09361 
09362                if (spix < 0)
09363                    spix = 0;
09364 
09365                if (epix > nx)
09366                    epix = nx;
09367 
09368                vi = 0.0;
09369                for (k = spix; k < epix; k++) {
09370                    if (pixel >= 0 && pixel < nx) {
09371                        vi += (sdata + i*nx)[k];
09372                    }
09373                }
09374 
09375                /*
09376                 * Correct integrated flux by true length
09377                 * of interval. This is clearly an approximation,
09378                 * but it's good enough if the PSF is much larger
09379                 * than the original pix.
09380                 */
09381 
09382                vi *= dispersion * pixel_per_lambda / (epix - spix);
09383 
09384                 /*
09385                  * Flux conservation is a geometric factor that is applied
09386                  * always in the same way...
09387                  */
09388 
09389                if (flux)
09390                    vi *= dispersion * pixel_per_lambda;
09391 
09392                (rdata + i*nl)[j] = vi;
09393             }
09394         }
09395 
09396         cpl_polynomial_delete(ids);
09397     }
09398 
09399     return resampled;
09400 }
09401 
09402 
09469 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
09470                                 double refwave, double firstLambda, 
09471                                 double lastLambda, cpl_table *idscoeff,
09472                                 cpl_vector *skylines, int highres, int order,
09473                                 cpl_image *calibration, int sradius)
09474 {
09475     const char *func = "mos_wavelength_align";
09476 
09477     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09478                                                  /* Max order is 5 */
09479     double         *line;
09480     double         *data;
09481     double          expPos, offset;
09482     double          c;
09483     double          lambda1, lambda2;
09484     double          rms;
09485     float           pos;
09486     float          *sdata;
09487     float          *cdata;
09488     int            *idata;
09489     int             startPos, endPos;
09490     int             window = 2*sradius + 1;
09491     int             nlines;
09492     int             nslits;
09493     int             npoints;
09494     int             nrows;
09495     int             nx, ny;
09496     int             xlow, ylow, xhig, yhig;
09497     int             idsorder, uorder;
09498     int            *slit_id;
09499     int            *position;
09500     int            *length;
09501     int             missing;
09502     int             null;
09503     int             i;
09504     cpl_size        j, k;
09505 
09506     char            offname[MAX_COLNAME];
09507     char            name[MAX_COLNAME];
09508 
09509     cpl_polynomial *ids;
09510     cpl_polynomial *polycorr;
09511     cpl_image      *exslit;
09512     cpl_image      *sky;
09513     cpl_table      *offsets;
09514     cpl_table      *dummy;
09515     cpl_vector     *wave;
09516     cpl_vector     *offs;
09517     
09518 
09519     if (idscoeff == NULL) {
09520         cpl_msg_error(func, "An IDS coeff table must be given");
09521         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09522         return NULL;
09523     }
09524 
09525     if (image == NULL) {
09526         cpl_msg_error(func, "A scientific spectral image must be given");
09527         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09528         return NULL;
09529     }
09530 
09531     if (slits == NULL) {
09532         cpl_msg_error(func, "A slit position table must be given");
09533         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09534         return NULL;
09535     }
09536 
09537     if (skylines) {
09538         line = cpl_vector_get_data(skylines);
09539         nlines = cpl_vector_get_size(skylines);
09540     }
09541     else {
09542         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09543                         "given: using internal list of reference sky lines");
09544         if (highres) {
09545            line = default_lines_hi;
09546            nlines = sizeof(default_lines_hi) / sizeof(double);
09547         }
09548         else {
09549            line = default_lines_lo;
09550            nlines = sizeof(default_lines_lo) / sizeof(double);
09551         }
09552     }
09553 
09554     if (calibration)
09555         cdata = cpl_image_get_data(calibration);
09556 
09557     nx = cpl_image_get_size_x(image);
09558     ny = cpl_image_get_size_y(image);
09559 
09560     nslits   = cpl_table_get_nrow(slits);
09561     slit_id  = cpl_table_get_data_int(slits, "slit_id");
09562     position = cpl_table_get_data_int(slits, "position");
09563     length   = cpl_table_get_data_int(slits, "length");
09564 
09565 
09566     /*
09567      * Define the output table of offsets
09568      */
09569 
09570     nrows = 0;
09571     for (i = 0; i < nlines; i++)
09572         if (line[i] > firstLambda && line[i] < lastLambda)
09573             nrows++;
09574 
09575     offsets = cpl_table_new(nrows);
09576     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09577     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09578 
09579     nrows = 0;
09580     for (i = 0; i < nlines; i++) {
09581         if (line[i] > firstLambda && line[i] < lastLambda) {
09582             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09583             nrows++;
09584         }
09585     }
09586 
09587     /*
09588      * Here "line" is made to point to the new list of selected wavelengths
09589      */
09590 
09591     line = cpl_table_get_data_double(offsets, "wave");
09592     nlines = nrows;
09593 
09594     idsorder = 0;
09595     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09596         ++idsorder;
09597     --idsorder;
09598 
09599     xlow = 1;
09600     xhig = nx;
09601     for (i = 0; i < nslits; i++) {
09602 
09603         if (length[i] == 0)
09604             continue;
09605 
09606         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09607         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
09608 
09609         /* 
09610          * Define the extraction boundaries. We DON'T write:
09611          *
09612          * ylow = position[i];
09613          * yhig = ylow + length[i];
09614          *
09615          * because the cpl_image pixels are counted from 1, and because in
09616          * cpl_image_extract() the coordinates of the last pixel are inclusive.
09617          */
09618 
09619         ylow = position[i] + 1;
09620         yhig = ylow + length[i] - 1;
09621 
09622         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
09623         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
09624         sdata  = cpl_image_get_data(sky);
09625 
09626         cpl_image_delete(exslit);
09627 
09628         /* 
09629          * Return here to a decent way of counting pixels (i.e., starting
09630          * from 0)
09631          */
09632          
09633         ylow--;
09634 
09635         /*
09636          * Allocate a dummy table for collecting all the offsets
09637          * for all the lines: this is only needed for the computation
09638          * of the median offset for each sky line
09639          */
09640 
09641         dummy = cpl_table_new(yhig - ylow);
09642         for (j = 0; j < nlines; j++) {
09643             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
09644             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09645         }
09646 
09647         for (j = ylow; j < yhig; j++) {
09648 
09649             /*
09650              * Get the IDS polynomial for the current slit row
09651              */
09652 
09653             missing = 0;
09654             ids = cpl_polynomial_new(1);
09655             for (k = 0; k <= idsorder; k++) {
09656                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
09657                 if (null) {
09658                     cpl_polynomial_delete(ids);
09659                     missing = 1;
09660                     break;
09661                 }
09662                 cpl_polynomial_set_coeff(ids, &k, c);
09663             }
09664             if (missing)
09665                 continue;
09666 
09667             for (k = 0; k < nlines; k++) {
09668                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09669                 startPos = expPos - sradius;
09670                 endPos   = startPos + window;
09671                 if (startPos < 0 || endPos >= nx)
09672                     continue;
09673            
09674                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09675                     pos += startPos;
09676                     offset = pos - expPos;
09677                     snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k);
09678                     cpl_table_set_double(dummy, name, j - ylow, offset);
09679                 }
09680             }
09681 
09682             cpl_polynomial_delete(ids);
09683         }
09684 
09685         cpl_image_delete(sky);
09686 
09687         for (j = 0; j < nlines; j++) {
09688             snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j);
09689             if (cpl_table_has_valid(dummy, name)) {
09690                 offset = cpl_table_get_column_median(dummy, name);
09691                 cpl_table_set_double(offsets, offname, j, offset);
09692             }
09693         }
09694 
09695         cpl_table_delete(dummy);
09696 
09697     }
09698 
09699 
09700     /*
09701      * In the following the input idscoeff table is modified by simply
09702      * adding the coefficients of the polynomial used to fit the sky
09703      * line residuals to the coefficients of the IDS polynomials.
09704      */
09705 
09706     for (i = 0; i < nslits; i++) {
09707 
09708         if (length[i] == 0)
09709             continue;
09710 
09711         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09712 
09713         /*
09714          * In the following, the "dummy" table is just a tool for
09715          * eliminating invalid points from the vectors to be fitted.
09716          */
09717 
09718         dummy = cpl_table_new(nlines);
09719         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
09720         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
09721 
09722         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
09723         if (npoints == 0) {
09724             cpl_msg_warning(func, "No sky lines alignment was possible "
09725                             "for slit ID=%d: no sky line found", slit_id[i]);
09726             cpl_table_delete(dummy);
09727             continue;
09728         }
09729 
09730         uorder = order;
09731         if (npoints <= uorder) {
09732             uorder = npoints - 1;
09733             if (uorder) {
09734                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09735                                 "ID=%d, while a polynomial order %d was "
09736                                 "requested. Using polynomial order %d for "
09737                                 "this slit!", npoints, slit_id[i], order, 
09738                                 uorder);
09739             }
09740             else {
09741                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09742                                 "ID=%d, while a polynomial order %d was "
09743                                 "requested. Computing a median offset for "
09744                                 "this slit!", npoints, slit_id[i], order);
09745             }
09746         }
09747 
09748         cpl_table_erase_invalid(dummy);
09749 
09750         if (uorder > 1) {
09751 
09752             /*
09753              * Model offsets with polynomial fitting
09754              */
09755 
09756             wave = cpl_vector_wrap(npoints,
09757                                    cpl_table_get_data_double(dummy, "wave"));
09758             offs = cpl_vector_wrap(npoints,
09759                                    cpl_table_get_data_double(dummy, "offset"));
09760 
09761             /*
09762              * Set reference wavelength as zero point
09763              */
09764 
09765             cpl_vector_subtract_scalar(wave, refwave);
09766 
09767             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09768 
09769             rms = sqrt(rms * (uorder + 1) / npoints);
09770 
09771             cpl_vector_unwrap(wave);
09772             cpl_vector_unwrap(offs);
09773             cpl_table_delete(dummy);
09774 
09775             /*
09776              * Now correct the coefficients of the corresponding IDS
09777              * polynomials related to this slit:
09778              */
09779 
09780             ylow = position[i];
09781             yhig = ylow + length[i];
09782 
09783             for (j = 0; j <= uorder; j++) {
09784                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09785                 c = cpl_polynomial_get_coeff(polycorr, &j);
09786                 for (k = ylow; k < yhig; k++)
09787                     data[k] += c;
09788             }
09789 
09790             data = cpl_table_get_data_double(idscoeff, "error");
09791             for (k = ylow; k < yhig; k++)
09792                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09793 
09794             idata = cpl_table_get_data_int(idscoeff, "nlines");
09795             for (k = ylow; k < yhig; k++)
09796                  idata[k] = npoints;
09797 
09798             /*
09799              * If a wavelengths map was provided, correct it to keep
09800              * into account the alignment to skylines:
09801              */
09802 
09803             if (calibration) {
09804                 for (j = ylow; j < yhig; j++) {
09805                     for (k = 1; k < nx; k++) {
09806                         lambda1 = cdata[k - 1 + j*nx];
09807                         lambda2 = cdata[k + j*nx];
09808                         if (lambda1 < 1.0 || lambda2 < 1.0)
09809                             continue;
09810                         offset = cpl_polynomial_eval_1d(polycorr, 
09811                                                         lambda1-refwave, NULL);
09812                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09813                     }
09814                 }
09815             }
09816     
09817             cpl_polynomial_delete(polycorr);
09818         }
09819         else if (uorder == 1) {
09820 
09821             /*
09822              * Model offsets with robust linear fitting
09823              */
09824 
09825             double        q, m;
09826             cpl_bivector *list;
09827 
09828 
09829             wave = cpl_vector_wrap(npoints,
09830                                    cpl_table_get_data_double(dummy, "wave"));
09831             offs = cpl_vector_wrap(npoints,
09832                                    cpl_table_get_data_double(dummy, "offset"));
09833 
09834             list = cpl_bivector_wrap_vectors(wave, offs);
09835 
09836             /*
09837              * Set reference wavelength as zero point
09838              */
09839 
09840             cpl_vector_subtract_scalar(wave, refwave);
09841 
09842             robustLinearFit(list, &q, &m, &rms);
09843 
09844             rms = sqrt(rms * (uorder + 1) / npoints);
09845 
09846             cpl_bivector_unwrap_vectors(list);
09847             cpl_vector_unwrap(wave);
09848             cpl_vector_unwrap(offs);
09849             cpl_table_delete(dummy);
09850 
09851             /*
09852              * Now correct the coefficients of the corresponding IDS
09853              * polynomials related to this slit:
09854              */
09855 
09856             ylow = position[i];
09857             yhig = ylow + length[i];
09858 
09859             for (j = 0; j <= uorder; j++) {
09860                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09861                 if (j)
09862                     c = m;
09863                 else
09864                     c = q;
09865                 for (k = ylow; k < yhig; k++)
09866                     data[k] += c;
09867             }
09868 
09869             data = cpl_table_get_data_double(idscoeff, "error");
09870             for (k = ylow; k < yhig; k++)
09871                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09872 
09873             idata = cpl_table_get_data_int(idscoeff, "nlines");
09874             for (k = ylow; k < yhig; k++)
09875                  idata[k] = npoints;
09876 
09877             /*
09878              * If a wavelengths map was provided, correct it to keep
09879              * into account the alignment to skylines:
09880              */
09881 
09882             if (calibration) {
09883                 for (j = ylow; j < yhig; j++) {
09884                     for (k = 1; k < nx; k++) {
09885                         lambda1 = cdata[k - 1 + j*nx];
09886                         lambda2 = cdata[k + j*nx];
09887                         if (lambda1 < 1.0 || lambda2 < 1.0)
09888                             continue;
09889                         offset = q + m*(lambda1-refwave);
09890                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09891                     }
09892                 }
09893             }
09894         }
09895         else {
09896 
09897             /*
09898              * Just compute median offset
09899              */
09900 
09901             offs = cpl_vector_wrap(npoints,
09902                                    cpl_table_get_data_double(dummy, "offset"));
09903 
09904             offset = cpl_vector_get_median_const(offs);
09905 
09906             if (npoints > 1)
09907                 rms = cpl_table_get_column_stdev(dummy, "offset");
09908             else
09909                 rms = 0.0;
09910 
09911             rms /= sqrt(npoints);
09912 
09913             cpl_vector_unwrap(offs);
09914             cpl_table_delete(dummy);
09915 
09916             /*
09917              * Now correct the constant term of the corresponding IDS
09918              * polynomials related to this slit:
09919              */
09920 
09921             ylow = position[i];
09922             yhig = ylow + length[i];
09923 
09924             data = cpl_table_get_data_double(idscoeff, clab[0]);
09925             for (k = ylow; k < yhig; k++)
09926                 data[k] += offset;
09927 
09928             data = cpl_table_get_data_double(idscoeff, "error");
09929             for (k = ylow; k < yhig; k++)
09930                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09931 
09932             idata = cpl_table_get_data_int(idscoeff, "nlines");
09933             for (k = ylow; k < yhig; k++)
09934                  idata[k] = npoints;
09935 
09936             /*
09937              * If a wavelengths map was provided, correct it to keep
09938              * into account the alignment to skylines. Note that 
09939              * the offset must be converted from pixels to wavelengths.
09940              */
09941 
09942             if (calibration) {
09943                 for (j = ylow; j < yhig; j++) {
09944                     for (k = 1; k < nx; k++) {
09945                         lambda1 = cdata[k - 1 + j*nx];
09946                         lambda2 = cdata[k + j*nx];
09947                         if (lambda1 < 1.0 || lambda2 < 1.0)
09948                             continue; 
09949                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09950                     }
09951                 }
09952             }
09953         }
09954     }
09955 
09956     return offsets;
09957 
09958 }
09959 
09960 
10022 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
10023                                     double firstLambda, double lastLambda, 
10024                                     cpl_table *idscoeff, cpl_vector *skylines, 
10025                                     int highres, int order, 
10026                                     cpl_image *calibration, int sradius)
10027 {
10028     const char *func = "mos_wavelength_align_lss";
10029 
10030     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10031                                                  /* Max order is 5 */
10032     double         *line;
10033     double         *data;
10034     double         *wdata;
10035     double         *odata;
10036     double          expPos, offset;
10037     double          c;
10038     double          lambda1, lambda2;
10039     double          rms;
10040     float           pos;
10041     float          *sdata;
10042     float          *cdata;
10043     int            *idata;
10044     int             startPos, endPos;
10045     int             window = 2*sradius + 1;
10046     int             nlines;
10047     int             npoints;
10048     int             nrows;
10049     int             nx, ny;
10050     int             idsorder, uorder;
10051     int             missing;
10052     int             i;
10053     cpl_size        j, k;
10054 
10055     char            name[MAX_COLNAME];
10056     char            fname[MAX_COLNAME];
10057 
10058     cpl_polynomial *ids;
10059     cpl_polynomial *polycorr;
10060     cpl_table      *offsets;
10061     cpl_table      *fittable;
10062     cpl_table      *dummy;
10063     cpl_vector     *wave;
10064     cpl_vector     *offs;
10065     cpl_vector     *row;
10066     
10067 
10068     if (idscoeff == NULL) {
10069         cpl_msg_error(func, "An IDS coeff table must be given");
10070         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10071         return NULL;
10072     }
10073 
10074     if (image == NULL) {
10075         cpl_msg_error(func, "A scientific spectral image must be given");
10076         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10077         return NULL;
10078     }
10079 
10080     if (skylines) {
10081         line = cpl_vector_get_data(skylines);
10082         nlines = cpl_vector_get_size(skylines);
10083     }
10084     else {
10085         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10086                         "given: using internal list of reference sky lines");
10087         if (highres) {
10088            line = default_lines_hi;
10089            nlines = sizeof(default_lines_hi) / sizeof(double);
10090         }
10091         else {
10092            line = default_lines_lo;
10093            nlines = sizeof(default_lines_lo) / sizeof(double);
10094         }
10095     }
10096 
10097     if (calibration)
10098         cdata = cpl_image_get_data(calibration);
10099 
10100     nx = cpl_image_get_size_x(image);
10101     ny = cpl_image_get_size_y(image);
10102 
10103     sdata = cpl_image_get_data(image);
10104 
10105     if (ny != cpl_table_get_nrow(idscoeff)) {
10106         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10107         return NULL;
10108     }
10109     
10110 
10111     /*FIXME: This is a remnant of the adaptation of the function
10112      * mos_wavelength_align(), where an offset table was created.
10113      * I leave it here because I am in a hurry, it is just used to
10114      * hold the list of selected sky lines.
10115      *
10116      * Define table of wavelengths
10117      */
10118 
10119     nrows = 0;
10120     for (i = 0; i < nlines; i++)
10121         if (line[i] > firstLambda && line[i] < lastLambda)
10122             nrows++;
10123 
10124     offsets = cpl_table_new(nrows);
10125     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10126     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10127 
10128     nrows = 0;
10129     for (i = 0; i < nlines; i++) {
10130         if (line[i] > firstLambda && line[i] < lastLambda) {
10131             cpl_table_set_double(offsets, "wave", nrows, line[i]);
10132             nrows++;
10133         }
10134     }
10135 
10136     /*
10137      * Here "line" is made to point to the new list of selected wavelengths
10138      */
10139 
10140     line = cpl_table_get_data_double(offsets, "wave");
10141     nlines = nrows;
10142 
10143     idsorder = 0;
10144     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10145         ++idsorder;
10146     --idsorder;
10147 
10148 
10149     /*
10150      * Allocate a dummy table for collecting all the offsets
10151      * for all the lines
10152      */
10153 
10154     dummy = cpl_table_new(ny);
10155     for (j = 0; j < nlines; j++) {
10156         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10157         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10158         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10159         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10160     }
10161 
10162     for (j = 0; j < ny; j++, sdata += nx) {
10163 
10164         /*
10165          * Get the IDS polynomial for the current slit row
10166          */
10167 
10168         missing = 0;
10169         ids = cpl_polynomial_new(1);
10170         for (k = 0; k <= idsorder; k++) {
10171             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10172             if (missing) {
10173                 cpl_polynomial_delete(ids);
10174                 break;
10175             }
10176             cpl_polynomial_set_coeff(ids, &k, c);
10177         }
10178         if (missing)
10179             continue;
10180 
10181         for (k = 0; k < nlines; k++) {
10182             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10183             startPos = expPos - sradius;
10184             endPos   = startPos + window;
10185             if (startPos < 0 || endPos >= nx)
10186                 continue;
10187            
10188             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10189                 pos += startPos;
10190                 offset = pos - expPos;
10191                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10192                 cpl_table_set_double(dummy, name, j, offset);
10193             }
10194         }
10195 
10196         cpl_polynomial_delete(ids);
10197     }
10198 
10199 
10200     /*
10201      * At this point for each sky line we model its offset along
10202      * the image rows using a robust linear fitting
10203      */
10204 
10205     for (j = 0; j < nlines; j++) {
10206         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10207         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10208         if (cpl_table_has_valid(dummy, name)) {
10209 
10210             /*
10211              * In the following, the "fittable" is just a tool for
10212              * eliminating invalid points from the vectors to be fitted.
10213              */
10214 
10215             double        q, m;
10216             cpl_bivector *list;
10217 
10218             fittable = cpl_table_new(ny);
10219             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10220             cpl_table_set_column_unit(fittable, "row", "pixel");
10221             for (k = 0; k < ny; k++)
10222                  cpl_table_set_double(fittable, "row", k, k);
10223             cpl_table_duplicate_column(fittable, "offset", dummy, name);
10224             npoints = ny - cpl_table_count_invalid(fittable, "offset");
10225             cpl_table_erase_invalid(fittable);
10226             row = cpl_vector_wrap(npoints,
10227                                cpl_table_get_data_double(fittable, "row"));
10228             offs = cpl_vector_wrap(npoints,
10229                                cpl_table_get_data_double(fittable, "offset"));
10230             list = cpl_bivector_wrap_vectors(row, offs);
10231             robustLinearFit(list, &q, &m, &rms);
10232             cpl_bivector_unwrap_vectors(list);
10233             cpl_vector_unwrap(row);
10234             cpl_vector_unwrap(offs);
10235             cpl_table_delete(fittable);
10236             for (k = 0; k < ny; k++)
10237                  cpl_table_set_double(dummy, fname, k, q + m*k);
10238         }
10239     }
10240 
10241 
10242     /*
10243      * Now each dummy table row consists of a sequence of offsets,
10244      * one for each wavelength. A table row corresponds to an image row.
10245      * We must fit a polynomial to each one of these rows, in order to
10246      * express the offsets as a function of wavelength. The obtained 
10247      * polynomial coefficients are used to correct the IDS coefficients.
10248      */
10249 
10250     for (i = 0; i < ny; i++) {
10251 
10252         if (!cpl_table_is_valid(idscoeff, clab[0], i))
10253             continue;
10254 
10255         npoints = 0;
10256         for (j = 0; j < nlines; j++) {
10257             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10258             if (cpl_table_is_valid(dummy, name, i))
10259                 npoints++;
10260         }
10261 
10262         if (npoints == 0)
10263             continue;
10264 
10265         uorder = order;
10266         if (npoints <= uorder)
10267             uorder = npoints - 1;
10268 
10269         if (uorder > 1) {
10270 
10271             /*
10272              * Model offsets with polynomial fitting
10273              */
10274 
10275             wave = cpl_vector_new(npoints);
10276             wdata = cpl_vector_get_data(wave);
10277             offs = cpl_vector_new(npoints);
10278             odata = cpl_vector_get_data(offs);
10279 
10280             npoints = 0;
10281             for (j = 0; j < nlines; j++) {
10282                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10283                 if (cpl_table_is_valid(dummy, name, i)) {
10284                     wdata[npoints] = line[j] - refwave;
10285                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10286                     npoints++;
10287                 }
10288             }
10289 
10290             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10291 
10292             rms = sqrt(rms * (uorder + 1) / npoints);
10293 
10294             cpl_vector_delete(wave);
10295             cpl_vector_delete(offs);
10296 
10297             /*
10298              * Now correct the coefficients of the corresponding IDS
10299              * polynomials related to this slit:
10300              */
10301 
10302             for (j = 0; j <= uorder; j++) {
10303                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10304                 c = cpl_polynomial_get_coeff(polycorr, &j);
10305                 data[i] += c;
10306             }
10307 
10308             data = cpl_table_get_data_double(idscoeff, "error");
10309             data[i] = sqrt(data[i]*data[i] + rms*rms);
10310 
10311             idata = cpl_table_get_data_int(idscoeff, "nlines");
10312             idata[i] = npoints;
10313 
10314             /*
10315              * If a wavelengths map was provided, correct it to keep
10316              * into account the alignment to skylines:
10317              */
10318 
10319             if (calibration) {
10320                 for (k = 1; k < nx; k++) {
10321                     lambda1 = cdata[k - 1 + i*nx];
10322                     lambda2 = cdata[k + i*nx];
10323                     if (lambda1 < 1.0 || lambda2 < 1.0)
10324                         continue;
10325                     offset = cpl_polynomial_eval_1d(polycorr,
10326                                                     lambda1-refwave, NULL);
10327                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10328                 }
10329             }
10330 
10331             cpl_polynomial_delete(polycorr);
10332 
10333         }
10334         else if (uorder == 1) {
10335 
10336             /*
10337              * Model offsets with robust linear fitting
10338              */
10339 
10340             cpl_bivector *list;
10341             double        q, m;
10342 
10343             wave = cpl_vector_new(npoints);
10344             wdata = cpl_vector_get_data(wave);
10345             offs = cpl_vector_new(npoints);
10346             odata = cpl_vector_get_data(offs);
10347 
10348             npoints = 0;
10349             for (j = 0; j < nlines; j++) {
10350                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10351                 if (cpl_table_is_valid(dummy, name, i)) {
10352                     wdata[npoints] = line[j] - refwave;
10353                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10354                     npoints++;
10355                 }
10356             }
10357 
10358             list = cpl_bivector_wrap_vectors(wave, offs);
10359             robustLinearFit(list, &q, &m, &rms);
10360 
10361             rms = sqrt(rms * (uorder + 1) / npoints);
10362 
10363             cpl_bivector_unwrap_vectors(list);
10364             cpl_vector_delete(wave);
10365             cpl_vector_delete(offs);
10366 
10367             /*
10368              * Now correct the coefficients of the corresponding IDS
10369              * polynomials related to this row:
10370              */
10371 
10372             for (j = 0; j <= uorder; j++) {
10373                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10374                 if (j)
10375                     c = m;
10376                 else
10377                     c = q;
10378                 data[i] += c;
10379             }
10380 
10381             data = cpl_table_get_data_double(idscoeff, "error");
10382             data[i] = sqrt(data[i]*data[i] + rms*rms);
10383 
10384             idata = cpl_table_get_data_int(idscoeff, "nlines");
10385             idata[i] = npoints;
10386 
10387             /*
10388              * If a wavelengths map was provided, correct it to keep
10389              * into account the alignment to skylines:
10390              */
10391 
10392             if (calibration) {
10393                 for (k = 1; k < nx; k++) {
10394                     lambda1 = cdata[k - 1 + i*nx];
10395                     lambda2 = cdata[k + i*nx];
10396                     if (lambda1 < 1.0 || lambda2 < 1.0)
10397                         continue;
10398                     offset = q + m*(lambda1-refwave);
10399                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10400                 }
10401             }
10402         }
10403         else {
10404 
10405             /*
10406              * Just compute median offset
10407              */
10408 
10409             offs = cpl_vector_new(npoints);
10410             odata = cpl_vector_get_data(offs);
10411 
10412             npoints = 0;
10413             for (j = 0; j < nlines; j++) {
10414                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10415                 if (cpl_table_is_valid(dummy, name, i)) {
10416                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10417                     npoints++;
10418                 }
10419             }
10420 
10421             offset = cpl_vector_get_median_const(offs);
10422 
10423             if (npoints > 1) {
10424                 rms = cpl_vector_get_stdev(offs);
10425             }
10426             else if (npoints == 1) {
10427                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10428                 if (cpl_table_has_valid(dummy, name)) {
10429                     rms = cpl_table_get_column_stdev(dummy, name);
10430                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10431                 }
10432                 else {
10433                     rms = 0.0;
10434                 }
10435             }
10436             else {
10437                 rms = 0.0;
10438             }
10439 
10440             rms /= sqrt(npoints);
10441 
10442             cpl_vector_delete(offs);
10443 
10444             /*
10445              * Now correct the constant term of the corresponding IDS
10446              * polynomials related to this slit:
10447              */
10448 
10449             data = cpl_table_get_data_double(idscoeff, clab[0]);
10450             data[i] += offset;
10451 
10452             data = cpl_table_get_data_double(idscoeff, "error");
10453             data[i] = sqrt(data[i]*data[i] + rms*rms);
10454 
10455             idata = cpl_table_get_data_int(idscoeff, "nlines");
10456             idata[i] = npoints;
10457 
10458             /*
10459              * If a wavelengths map was provided, correct it to keep
10460              * into account the alignment to skylines. Note that
10461              * the offset must be converted from pixels to wavelengths.
10462              */
10463 
10464             if (calibration) {
10465                 for (k = 1; k < nx; k++) {
10466                     lambda1 = cdata[k - 1 + i*nx];
10467                     lambda2 = cdata[k + i*nx];
10468                     if (lambda1 < 1.0 || lambda2 < 1.0)
10469                         continue;
10470                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10471                 }
10472             }
10473         }
10474     }
10475 
10476     missing = 1;
10477     for (j = 0; j < nlines; j++) {
10478         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10479         if (cpl_table_has_valid(dummy, name)) {
10480             missing = 0;
10481             offset = cpl_table_get_column_median(dummy, name);
10482             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
10483                          line[j], offset);
10484         }
10485         else {
10486             cpl_msg_info(func, 
10487                          "Median offset for %.2f: not available", line[j]);
10488         }
10489     }
10490 
10491     cpl_table_delete(offsets);
10492 
10493     if (missing) {
10494         cpl_table_delete(dummy);
10495         dummy = NULL;
10496     }
10497 
10498     return dummy;
10499 
10500 }
10501 
10502 
10530 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
10531                            double wavestart, double dispersion, int radius,
10532                            int highres)
10533 {
10534 
10535     const char *func = "mos_distortions_rms";
10536 
10537     int xlen;
10538     int ylen;
10539     int numLines;
10540     int cpix, npix, nzero;
10541     int sp, ep;
10542     int i, j, k;
10543     int npeaks, allPeaks;
10544 
10545     float *profile;
10546     float  peak, expectPeak, offset;
10547     double lambda;
10548 
10549     double  average;
10550     double  rms, oneRms;
10551 
10552     float  *sdata;
10553     double *wdata;
10554 
10555   
10556     xlen = cpl_image_get_size_x(rectified);
10557     ylen = cpl_image_get_size_y(rectified);
10558     sdata = cpl_image_get_data(rectified);
10559 
10560     if (lines) {
10561         wdata = cpl_vector_get_data(lines);
10562         numLines = cpl_vector_get_size(lines);
10563     }
10564     else {
10565         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10566                         "given: using internal list of reference sky lines");
10567         if (highres) {
10568            wdata = default_lines_hi;
10569            numLines = sizeof(default_lines_hi) / sizeof(double);
10570         }
10571         else {
10572            wdata = default_lines_lo;
10573            numLines = sizeof(default_lines_lo) / sizeof(double);
10574         }
10575     }
10576 
10577     npix = 2 * radius + 1;
10578     profile = cpl_calloc(npix, sizeof(float));
10579 
10580     rms = 0.0;
10581     allPeaks = 0;
10582 
10583     for (i = 0; i < numLines; i++) {
10584 
10585         /*
10586          *  Expected peak and closest pixel to specified wavelength.
10587          */
10588 
10589         lambda = wdata[i];
10590         expectPeak = (lambda - wavestart) / dispersion;
10591         cpix = floor(expectPeak + 0.5);
10592 
10593         /*
10594          *  Search interval for peak. Abort if too close to image border.
10595          */
10596 
10597         sp = cpix - radius;
10598         ep = cpix + radius;
10599 
10600         if (sp < 0 || ep > xlen)
10601             continue;
10602 
10603         average = 0.0;
10604         npeaks = 0;
10605         oneRms = 0.0;
10606 
10607         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
10608             nzero = 0;
10609             for (k = 0; k < npix; k++) {
10610                 profile[k] = sdata[sp + k + j * xlen];
10611                 if (fabs(profile[k]) < 0.0001)
10612                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
10613             }
10614             if (nzero > 0)
10615                 continue;
10616 
10617             if (peakPosition(profile, npix, &peak, 1) == 0) {
10618                 offset = (sp + peak) - expectPeak;
10619                 average += offset;
10620                 rms += fabs(offset);
10621                 oneRms += fabs(offset);
10622                 npeaks++;
10623                 allPeaks++;
10624             }
10625         }
10626 
10627         if (npeaks)
10628             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
10629                          lambda, oneRms / npeaks * 1.25, npeaks);
10630         else
10631             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
10632     }
10633 
10634     cpl_free(profile);
10635 
10636     if (allPeaks < 10)
10637         return 0.0;
10638 
10639     rms /= allPeaks;
10640     rms *= 1.25;       /* Factor to convert average deviation to sigma */
10641 
10642     return rms;
10643 
10644 }
10645 
10646 
10667 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
10668                          double blue, double red, double dispersion, int trend)
10669 {
10670     const char *func = "mos_map_pixel";
10671 
10672     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10673                                                  /* Max order is 5 */
10674 
10675     cpl_polynomial *ids;
10676     cpl_image      *map;
10677     float          *mdata;
10678     double          lambda;
10679     double          c;
10680     int             order;
10681     int             xsize, ysize;
10682     int             missing;
10683     int             i, j;
10684     cpl_size        k;
10685 
10686 
10687     if (idscoeff == NULL) {
10688         cpl_msg_error(func, "An IDS coeff table must be given");
10689         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10690         return NULL;
10691     }
10692 
10693     xsize = (red - blue) / dispersion;
10694     ysize = cpl_table_get_nrow(idscoeff);
10695     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10696     mdata = cpl_image_get_data(map);
10697 
10698     order = 0;
10699     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10700         ++order;
10701     --order;
10702 
10703     for (i = 0; i < ysize; i++, mdata += xsize) {
10704 
10705         missing = 0;
10706         ids = cpl_polynomial_new(1);
10707         for (k = trend; k <= order; k++) {
10708             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10709             if (missing) {
10710                 cpl_polynomial_delete(ids);
10711                 break;
10712             }
10713             cpl_polynomial_set_coeff(ids, &k, c);
10714         }
10715         if (missing)
10716             continue;
10717 
10718         for (j = 0; j < xsize; j++) {
10719             lambda = blue + j*dispersion;
10720             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
10721         }
10722 
10723         cpl_polynomial_delete(ids);
10724     }
10725 
10726     return map;
10727 
10728 }
10729 
10730 
10752 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
10753                             double blue, double red)
10754 {
10755     const char *func = "mos_map_idscoeff";
10756 
10757     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10758                                                  /* Max order is 5 */
10759 
10760     cpl_polynomial *ids;
10761     cpl_image      *map;
10762     float          *mdata;
10763     double          lambda;
10764     double          c;
10765     int             order;
10766     int             ysize;
10767     int             missing;
10768     int             i, j;
10769     cpl_size        k;
10770 
10771 
10772     if (idscoeff == NULL) {
10773         cpl_msg_error(func, "An IDS coeff table must be given");
10774         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10775         return NULL;
10776     }
10777 
10778     if (xsize < 1) {
10779         cpl_msg_error(func, "Invalid image size");
10780         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10781         return NULL;
10782     }
10783 
10784     if (xsize < 20 || xsize > 5000) {
10785         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10786                         xsize);
10787     }
10788 
10789     ysize = cpl_table_get_nrow(idscoeff);
10790     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10791     mdata = cpl_image_get_data(map);
10792 
10793     order = 0;
10794     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10795         ++order;
10796     --order;
10797 
10798     for (i = 0; i < ysize; i++, mdata += xsize) {
10799 
10800         missing = 0;
10801         ids = cpl_polynomial_new(1);
10802         for (k = 0; k <= order; k++) {
10803             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10804             if (missing) {
10805                 cpl_polynomial_delete(ids);
10806                 break;
10807             }
10808             cpl_polynomial_set_coeff(ids, &k, c);
10809         }
10810         if (missing)
10811             continue;
10812 
10813         for (j = 0; j < xsize; j++) {
10814             lambda = mos_eval_dds(ids, blue, red, reference, j);
10815 
10816             if (lambda >= blue && lambda <= red) {
10817                 mdata[j] = lambda;
10818             }
10819         }
10820 
10821         cpl_polynomial_delete(ids);
10822     }
10823 
10824     return map;
10825 
10826 }
10827 
10828 
10863 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10864                                cpl_table *slits, cpl_table *polytraces, 
10865                                double reference, double blue, double red, 
10866                                double dispersion)
10867 {
10868     const char *func = "mos_map_wavelengths";
10869 
10870     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10871                                                  /* Max order is 5 */
10872     cpl_polynomial *polytop;
10873     cpl_polynomial *polybot;
10874     cpl_image      *remapped;
10875     float          *data;
10876     float          *wdata;
10877     float          *sdata;
10878     float          *xdata;
10879     double          vtop, vbot, value;
10880     double          top, bot;
10881     double          coeff;
10882     double          ytop, ybot;
10883     double          ypos;
10884     double          fvalue;
10885     int             ivalue;
10886     int             yint, ysize, yprev;
10887     int             nslits;
10888     int             npseudo;
10889     int            *slit_id;
10890     int            *position;
10891     int            *length;
10892     int             nx, ny;
10893     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10894     int             missing_top, missing_bot;
10895     int             null;
10896     int             order;
10897     int             i, j;
10898     cpl_size        k;
10899 
10900 
10901     if (spatial == NULL || calibration == NULL || 
10902         slits == NULL || polytraces == NULL) {
10903         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10904         return NULL;
10905     }
10906 
10907     if (dispersion <= 0.0) {
10908         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10909         return NULL;
10910     }
10911 
10912     if (red - blue < dispersion) {
10913         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10914         return NULL;
10915     }
10916 
10917     nx = cpl_image_get_size_x(spatial);
10918     ny = cpl_image_get_size_y(spatial);
10919     ysize = cpl_image_get_size_y(calibration);
10920     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10921     data  = cpl_image_get_data(remapped);
10922     sdata = cpl_image_get_data(spatial);
10923     wdata = cpl_image_get_data(calibration);
10924 
10925     nslits   = cpl_table_get_nrow(slits);
10926     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10927     order    = cpl_table_get_ncol(polytraces) - 2;
10928     position = cpl_table_get_data_int(slits, "position");
10929     length   = cpl_table_get_data_int(slits, "length");
10930 
10931     /*
10932      * The spatial resampling is performed for a certain number of 
10933      * pixels above and below the position of the reference wavelength:
10934      */
10935 
10936     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
10937     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
10938 
10939     for (i = 0; i < nslits; i++) {
10940 
10941         if (length[i] == 0)
10942             continue;
10943 
10944         /*
10945          * Note that the x coordinate of the reference pixels on the CCD
10946          * is taken arbitrarily at the top end of each slit. This wouldn't
10947          * be entirely correct in case of curved slits, or in presence of
10948          * heavy distortions: in such cases the spatial resampling is
10949          * really performed across a wide range of wavelengths. But
10950          * the lag between top and bottom spectral curvature models 
10951          * would introduce even in such cases negligible effects on
10952          * the spectral spatial resampling.
10953          */
10954 
10955         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10956 
10957         start_pixel = refpixel - pixel_below;
10958         if (start_pixel < 0)
10959             start_pixel = 0;
10960 
10961         end_pixel = refpixel + pixel_above;
10962         if (end_pixel > nx)
10963             end_pixel = nx;
10964 
10965         /*
10966          * Recover from the table of spectral curvature coefficients
10967          * the curvature polynomials.
10968          */
10969 
10970         missing_top = 0;
10971         polytop = cpl_polynomial_new(1);
10972         for (k = 0; k <= order; k++) {
10973             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10974             if (null) {
10975                 cpl_polynomial_delete(polytop);
10976                 missing_top = 1;
10977                 break;
10978             }
10979             cpl_polynomial_set_coeff(polytop, &k, coeff);
10980         }
10981 
10982         missing_bot = 0;
10983         polybot = cpl_polynomial_new(1);
10984         for (k = 0; k <= order; k++) {
10985             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10986             if (null) {
10987                 cpl_polynomial_delete(polybot);
10988                 missing_bot = 1;
10989                 break;
10990             }
10991             cpl_polynomial_set_coeff(polybot, &k, coeff);
10992         }
10993 
10994         if (missing_top && missing_bot) {
10995             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10996                           slit_id[i]);
10997             continue;
10998         }
10999 
11000         /*
11001          * In case just one of the two edges was not traced, the other
11002          * edge curvature model is duplicated and shifted to the other
11003          * end of the slit: better than nothing!
11004          */
11005 
11006         if (missing_top) {
11007             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11008                           "the spectral curvature of the lower edge "
11009                           "is used instead.", slit_id[i]);
11010             polytop = cpl_polynomial_duplicate(polybot);
11011             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11012             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11013             k = 0;
11014             coeff = cpl_polynomial_get_coeff(polybot, &k);
11015             coeff += ytop - ybot;
11016             cpl_polynomial_set_coeff(polytop, &k, coeff);
11017         }
11018 
11019         if (missing_bot) {
11020             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11021                           "the spectral curvature of the upper edge "
11022                           "is used instead.", slit_id[i]);
11023             polybot = cpl_polynomial_duplicate(polytop);
11024             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11025             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11026             k = 0;
11027             coeff = cpl_polynomial_get_coeff(polytop, &k);
11028             coeff -= ytop - ybot;
11029             cpl_polynomial_set_coeff(polybot, &k, coeff);
11030         }
11031 
11032         /*
11033          * Point to current slit on wavelength calibration image.
11034          * Note that the npseudo value related to this slit is equal 
11035          * to the number of spatial pseudo-pixels decreased by 1 
11036          * (compare with function mos_spatial_calibration()).
11037          */
11038 
11039         xdata = wdata + nx*position[i];
11040         npseudo = length[i] - 1;
11041 
11042         /*
11043          * Write interpolated wavelengths to CCD image
11044          */
11045 
11046         for (j = start_pixel; j < end_pixel; j++) {
11047             top = cpl_polynomial_eval_1d(polytop, j, NULL);
11048             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11049             for (k = 0; k <= npseudo; k++) {
11050                 ypos = top - k*(top-bot)/npseudo;
11051                 yint = ypos;
11052 
11053                 /* 
11054                  * The line:
11055                  *     value = sdata[j + nx*yint];
11056                  * should be equivalent to:
11057                  *     value = npseudo*(top-yint)/(top-bot);
11058                  */
11059 
11060                 if (yint < 0 || yint >= ny-1) {
11061                     yprev = yint;
11062                     continue;
11063                 }
11064 
11065                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
11066                 ivalue = value;               /* Nearest spatial pixels:   */
11067                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
11068                 if (ivalue < npseudo && ivalue >= 0) {
11069                     vtop = xdata[j + nx*(npseudo-ivalue)];
11070                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
11071                     if (vtop < 1.0) {  /* Impossible wavelength */
11072                         if (vbot < 1.0) {
11073                             value = 0.0;
11074                         }
11075                         else {
11076                             value = vbot;
11077                         }
11078                     }
11079                     else if (vbot < 1.0) {
11080                         if (k)
11081                             value = vtop;
11082                         else
11083                             value = 0.0;
11084                     }
11085                     else if (fabs(vbot-vtop) > 10*dispersion) {
11086                         value = 0.0;
11087                     }
11088                     else {
11089                         value = vtop*(1-fvalue) + vbot*fvalue;
11090                     }
11091                     data[j + nx*yint] = value;
11092 
11093                     if (k) {
11094 
11095                         /*
11096                          * This is added to recover lost pixels on
11097                          * the CCD image (pixels are lost because
11098                          * the CCD pixels are less than npseudo+1).
11099                          */
11100 
11101                         if (yprev - yint > 1) {
11102                             value = sdata[j + nx*(yint+1)];
11103                             ivalue = value;
11104                             fvalue = value - ivalue;
11105                             if (ivalue < npseudo && ivalue >= 0) {
11106                                 vtop = xdata[j + nx*(npseudo-ivalue)];
11107                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
11108                                 if (vtop < 1.0) {
11109                                     if (vbot < 1.0) {
11110                                         value = data[j + nx*(yint+1)];
11111                                     }
11112                                     else {
11113                                         value = vbot;
11114                                     }
11115                                 }
11116                                 else if (vbot < 1.0) {
11117                                     value = vtop;
11118                                 }
11119                                 else if (fabs(vbot-vtop) > 2*dispersion) {
11120                                     value = vtop;
11121                                 }
11122                                 else {
11123                                     value = vtop*(1-fvalue) + vbot*fvalue;
11124                                 }
11125                                 data[j + nx*(yint+1)] = value;
11126                             }
11127                         }
11128                     }
11129                 }
11130                 yprev = yint;
11131             }
11132         }
11133         cpl_polynomial_delete(polytop);
11134         cpl_polynomial_delete(polybot);
11135     }
11136 
11137     return remapped;
11138 }
11139 
11213 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
11214                             cpl_image *spatial, cpl_table *slits,
11215                             cpl_table *polytraces, double reference,
11216                             double blue, double red, double dispersion,
11217                             int flux)
11218 {
11219     const char *func = "mos_map_spectrum";
11220     
11221     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11222                                                  /* Max order is 5 */
11223     cpl_polynomial *polytop;
11224     cpl_polynomial *polybot;
11225     cpl_image      *remapped;
11226     cpl_image     **exslit;
11227     float          *data;
11228     float          *wdata;
11229     float          *sdata;
11230     float          *xdata;
11231     double          lambda00, lambda01, lambda10, lambda11, lambda;
11232     double          space00, space01, space10, space11, space;
11233     double          value00, value01, value10, value11, value0, value1, value;
11234     double          dL, dS;
11235     double          top, bot;
11236     double          coeff;
11237     double          ytop, ybot;
11238     double          xfrac, yfrac;
11239     int             yint, ysize;
11240     int             itop, ibot;
11241     int             shift;
11242     int             L, S;
11243     int             nslits;
11244     int             npseudo;
11245     int            *slit_id;
11246     int            *position;
11247     int            *length;
11248     int             nx, ny;
11249     int             x, y;
11250     int             nlambda;
11251     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11252     int             missing_top, missing_bot; 
11253     int             null;
11254     int             order;
11255     int             i; 
11256     cpl_size        k;
11257     
11258 
11259     flux += flux;
11260 
11261     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11262         slits == NULL || polytraces == NULL) { 
11263         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11264         return NULL;
11265     }
11266 
11267     if (dispersion <= 0.0) {
11268         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11269         return NULL;
11270     }
11271 
11272     if (red - blue < dispersion) {
11273         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11274         return NULL;
11275     }
11276     
11277     nx = cpl_image_get_size_x(spectra);
11278     ny = cpl_image_get_size_y(spectra);
11279 
11280     if (nx != cpl_image_get_size_x(spatial) ||
11281         ny != cpl_image_get_size_y(spatial) ||
11282         nx != cpl_image_get_size_x(wavecalib) ||
11283         ny != cpl_image_get_size_y(wavecalib)) {
11284         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11285         return NULL;
11286     }
11287 
11288     nlambda     = STRETCH_FACTOR * (red - blue) / dispersion;
11289     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11290     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11291 
11292     data  = cpl_image_get_data(spectra);
11293     sdata = cpl_image_get_data(spatial);
11294     wdata = cpl_image_get_data(wavecalib);
11295     
11296     nslits   = cpl_table_get_nrow(slits);
11297     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11298     order    = cpl_table_get_ncol(polytraces) - 2;
11299     position = cpl_table_get_data_int(slits, "position");
11300     length   = cpl_table_get_data_int(slits, "length");
11301     
11302     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11303 
11304     for (i = 0; i < nslits; i++) {
11305 
11306          if (length == 0)
11307              continue;
11308 
11309         /*
11310          * Note that the x coordinate of the reference pixels on the CCD
11311          * is taken arbitrarily at the top end of each slit. This wouldn't
11312          * be entirely correct in case of curved slits, or in presence of
11313          * heavy distortions: in such cases the spatial resampling is
11314          * really performed across a wide range of wavelengths. But
11315          * the lag between top and bottom spectral curvature models
11316          * would introduce even in such cases negligible effects on
11317          * the spectral spatial resampling.
11318          */
11319 
11320         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11321 
11322         start_pixel = refpixel - pixel_below;
11323         if (start_pixel < 1)
11324             start_pixel = 1;
11325 
11326         end_pixel = refpixel + pixel_above;
11327         if (end_pixel > nx)
11328             end_pixel = nx;
11329 
11330         /*
11331          * Recover from the table of spectral curvature coefficients
11332          * the curvature polynomials.
11333          */
11334 
11335         missing_top = 0;
11336         polytop = cpl_polynomial_new(1);
11337         for (k = 0; k <= order; k++) {
11338             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11339             if (null) {
11340                 cpl_polynomial_delete(polytop);
11341                 missing_top = 1;
11342                 break;
11343             }
11344             cpl_polynomial_set_coeff(polytop, &k, coeff);
11345         }
11346 
11347         missing_bot = 0;
11348         polybot = cpl_polynomial_new(1);
11349         for (k = 0; k <= order; k++) {
11350             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11351             if (null) {
11352                 cpl_polynomial_delete(polybot);
11353                 missing_bot = 1;
11354                 break;
11355             }
11356             cpl_polynomial_set_coeff(polybot, &k, coeff);
11357         }
11358 
11359         if (missing_top && missing_bot) {
11360             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11361                           slit_id[i]);
11362             continue;
11363         }
11364 
11365         /*
11366          * In case just one of the two edges was not traced, the other
11367          * edge curvature model is duplicated and shifted to the other
11368          * end of the slit: better than nothing!
11369          */
11370 
11371         if (missing_top) {
11372             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11373                           "the spectral curvature of the lower edge "
11374                           "is used instead.", slit_id[i]);
11375             polytop = cpl_polynomial_duplicate(polybot);
11376             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11377             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11378             k = 0;
11379             coeff = cpl_polynomial_get_coeff(polybot, &k);
11380             coeff += ytop - ybot;
11381             cpl_polynomial_set_coeff(polytop, &k, coeff);
11382         }
11383 
11384         if (missing_bot) {
11385             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11386                           "the spectral curvature of the upper edge "
11387                           "is used instead.", slit_id[i]);
11388             polybot = cpl_polynomial_duplicate(polytop);
11389             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11390             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11391             k = 0;
11392             coeff = cpl_polynomial_get_coeff(polytop, &k);
11393             coeff -= ytop - ybot;
11394             cpl_polynomial_set_coeff(polybot, &k, coeff);
11395         }
11396 
11397         /*
11398          * Allocate image for current extracted slit
11399          */
11400 
11401         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11402         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11403         npseudo = ceil(top-bot) + 1;
11404 
11405         if (npseudo < 1) {
11406             cpl_polynomial_delete(polytop);
11407             cpl_polynomial_delete(polybot);
11408             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11409                           slit_id[i]);
11410             continue;
11411         }
11412 
11413         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11414         xdata = cpl_image_get_data(exslit[i]);
11415 
11416         /*
11417          * Write interpolated spectral values to remapped slit spectrum.
11418          */
11419 
11420         for (x = start_pixel; x < end_pixel; x++) {
11421             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11422             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11423             itop = top + 1;
11424             ibot = bot;
11425             if (itop < 0)
11426                 itop = 0;
11427             if (itop > ny - 1)
11428                 itop = ny - 1;
11429             if (ibot < 0)
11430                 ibot = 0;
11431             if (ibot > ny - 1)
11432                 ibot = ny - 1;
11433             for (y = ibot; y < itop; y++) {
11434                  lambda11 = wdata[x + y*nx];
11435                  if (lambda11 < 1.0)        /* Impossible wavelength */
11436                      continue;
11437                  space11 = sdata[x + y*nx];
11438                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11439                      continue;
11440                  lambda01 = wdata[x - 1 + y*nx];
11441                  if (lambda01 < 1.0)        /* Impossible wavelength */
11442                      continue;
11443                  space01 = sdata[x - 1 + y*nx];
11444                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11445                      continue;
11446 
11447                  shift = 0;
11448 
11449 /****+
11450                  if (wdata[x + (y+1)*nx] > 1.0) {
11451                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11452                          shift = -1;
11453                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11454                              shift--;
11455                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11456                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11457                              shift++;
11458                          }
11459                      }
11460                      else {
11461                          shift = 1;
11462                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11463                              shift++;
11464                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
11465                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11466                              shift--;
11467                          }
11468                      }
11469                  }
11470 ****/
11471 
11472 /****
11473 printf("y = %d, shift = %d\n", y, shift);
11474 ****/
11475 
11476                  lambda10 = wdata[x + shift + (y+1)*nx];
11477                  if (lambda10 < 1.0)        /* Impossible wavelength */
11478                      continue;
11479                  space10 = sdata[x + shift + (y+1)*nx];
11480                  if (space10 < 0.0)         /* Impossible spatial coordinate */
11481                      continue;
11482                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
11483                  if (lambda00 < 1.0)        /* Impossible wavelength */
11484                      continue;
11485                  space00 = sdata[x - 1 + shift + (y+1)*nx];
11486                  if (space00 < 0.0)         /* Impossible spatial coordinate */
11487                      continue;
11488                  
11489                  /*
11490                   * Find the variation in lambda and space in this
11491                   * position for each CCD pixel (both quantities are 
11492                   * expected to be positive).
11493                   */
11494 
11495                  dL = lambda11 - lambda01;
11496                  dS = space11 - space10;
11497 
11498                  /*
11499                   * Find the position (L,S) of the output pixel 
11500                   * (by integer truncation).
11501                   */
11502 
11503                  L = (lambda11 - blue)/dispersion + 0.5;
11504                  S = space11 + 0.5;                   /* Counted from top! */
11505 
11506                  if (L < 0 || L >= nlambda)
11507                      continue;
11508                  if (S < 0 || S > npseudo)
11509                      continue;
11510 
11511                  /*
11512                   * Find the coordinate of pixel (L,S)
11513                   */
11514 
11515                  lambda = blue + L*dispersion;
11516                  space  = S;
11517 
11518                  /*
11519                   * Find the interpolation point on the CCD: it is
11520                   * defined as the (positive) distance from current
11521                   * CCD pixel (x,y) of the interpolation point (x',y'),
11522                   * measured in CCD pixels. The interpolation point
11523                   * is located between the four CCD pixels selected
11524                   * above.
11525                   */
11526 
11527                  xfrac = (lambda11-lambda)/dL;
11528                  yfrac = (space11-space)/dS;
11529 
11530 /*
11531 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
11532 printf("xyfrac = %f, %f\n", xfrac, yfrac);
11533 */
11534 
11535                  /*
11536                   * Get the four values to interpolate
11537                   */
11538 
11539                  value11 = data[x + y*nx];
11540                  value01 = data[x - 1 + y*nx];
11541                  value10 = data[x + shift + (y+1)*nx];
11542                  value00 = data[x + shift - 1 + (y+1)*nx];
11543 
11544                  /*
11545                   * Interpolation
11546                   */
11547 
11548                  value1 = (1-xfrac)*value11 + xfrac*value01;
11549                  value0 = (1-xfrac)*value10 + xfrac*value00;
11550                  value  = (1-yfrac)*value1  + yfrac*value0;
11551 
11552                  /*
11553                   * Write this value to the appropriate (L,S) coordinate
11554                   * on output slit
11555                   */
11556 
11557                  xdata[L + nlambda*(npseudo-S)] = value;
11558                  
11559             }
11560         }
11561         cpl_polynomial_delete(polytop);
11562         cpl_polynomial_delete(polybot);
11563     }
11564 
11565     /*
11566      * Now all the slits images are copied to a single image
11567      */
11568 
11569     ysize = 0;
11570     for (i = 0; i < nslits; i++)
11571         if (exslit[i])
11572             ysize += cpl_image_get_size_y(exslit[i]);
11573 
11574     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
11575 
11576     yint = -1;
11577     for (i = 0; i < nslits; i++) {
11578         if (exslit[i]) {
11579             yint += cpl_image_get_size_y(exslit[i]);
11580             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
11581             cpl_image_delete(exslit[i]);
11582             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
11583         }
11584     }
11585 
11586     cpl_free(exslit);
11587 
11588     return remapped;
11589 
11590 }
11591 
11592 
11625 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
11626                              double dispersion, double factor, int minpoints,
11627                              cpl_image *skymap)
11628 {
11629     const char *func = "mos_sky_map_super";
11630 
11631     cpl_vector **vector;
11632     cpl_vector **wvector;
11633     double       firstLambda, lastLambda;
11634     double       lambda, lambda1, lambda2;
11635     double       value, value1, value2;
11636     double       frac;
11637     float        min, max;
11638     int         *count;
11639     int          nbin, bin;
11640     int          nx, ny, npix;
11641     int          first_valid, valid_bins;
11642     int          i, j;
11643 
11644     cpl_table   *sky;
11645     double      *sky_spectrum;
11646     double      *sky_wave;
11647     float       *data;
11648     float       *sdata;
11649     float       *kdata;
11650 
11651 
11652     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11653         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11654         return NULL;
11655     }
11656     
11657     if (dispersion <= 0.0) {
11658         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11659         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
11660         return NULL;
11661     }
11662 
11663     nx = cpl_image_get_size_x(spectra);
11664     ny = cpl_image_get_size_y(spectra);
11665     npix = nx * ny;
11666 
11667     if (nx != cpl_image_get_size_x(wavemap) ||
11668         ny != cpl_image_get_size_y(wavemap) ||
11669         nx != cpl_image_get_size_x(skymap) ||
11670         ny != cpl_image_get_size_y(skymap)) {
11671         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11672         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
11673         return NULL;
11674     }
11675 
11676     if (factor < 1.0) {
11677         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11678         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
11679                       cpl_error_get_message());
11680         return NULL;
11681     }
11682 
11683     if (minpoints < 0) {
11684         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11685         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
11686         return NULL;
11687     }
11688 
11689     dispersion /= factor;
11690 
11691 
11692     /*
11693      * Find bluest and reddest wavelengths in the whole image
11694      */
11695 
11696     data = cpl_image_get_data(wavemap);
11697 
11698     for (i = 0; i < npix; i++) {
11699         if (data[i] > 1.0) {
11700             min = max = data[i];
11701             j = i+1;
11702             break;
11703         }
11704     }
11705 
11706     for (i = j; i < npix; i++) {
11707         if (data[i] < 1.0)      /* Impossible wavelength */
11708             continue;
11709         if (min > data[i])
11710             min = data[i];
11711         if (max < data[i])
11712             max = data[i];
11713     }
11714 
11715     firstLambda = min;
11716     lastLambda = max;
11717 
11718 
11719     /*
11720      * Determine length of median spectrum
11721      */
11722 
11723     nbin = (lastLambda - firstLambda) / dispersion;
11724 
11725     /*
11726      * Count how many values will be found for each spectral bin.
11727      * The ith bin has a wavelength range from firstLambda + i*dispersion
11728      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11729      * it is assigned to its central wavelength.
11730      */
11731 
11732     count = cpl_calloc(nbin, sizeof(int));
11733 
11734     data = cpl_image_get_data(wavemap);
11735 
11736     for (i = 0; i < npix; i++) {
11737         if (data[i] < 1.0)
11738             continue;
11739         bin = (data[i] - firstLambda) / dispersion;
11740         if (bin < nbin)                               /* Safer */
11741             count[bin]++;
11742     }
11743 
11744     valid_bins = 0;
11745     for (i = 0; i < nbin; i++)
11746         if (count[i] >= minpoints)
11747             valid_bins++;
11748 
11749     if (valid_bins < nbin/3) {
11750         cpl_msg_warning(func, "Cannot determine a good global sky "
11751                         "spectrum from input data");
11752         return NULL;
11753     }
11754 
11755 
11756     /*
11757      * Allocate an array of vectors with the appropriate size, to
11758      * contain a list of all the spectral pixels values. At the same
11759      * time, reset the array of counters (because we will have to
11760      * count again...).
11761      */
11762 
11763     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11764     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11765     for (i = 0; i < nbin; i++) {
11766         if (count[i] >= minpoints) {
11767             vector[i] = cpl_vector_new(count[i]);
11768             wvector[i] = cpl_vector_new(count[i]);
11769         }
11770         count[i] = 0;
11771     }
11772 
11773 
11774     /*
11775      * Read the wavemap and the spectral images, and add the data values
11776      * to the appropriate wavelength bins
11777      */
11778 
11779     data  = cpl_image_get_data(wavemap);
11780     sdata = cpl_image_get_data(spectra);
11781 
11782     for (i = 0; i < npix; i++) {
11783         if (data[i] < 1.0)
11784             continue;
11785         bin = (data[i] - firstLambda) / dispersion;
11786         if (bin < nbin) {                             /* Safer */
11787             if (vector[bin]) {
11788                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11789                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11790             }
11791             count[bin]++;
11792         }
11793     }
11794 
11795 
11796     /*
11797      * Compute the median flux for each wavelength bin, and destroy
11798      * at the same time the used vectors
11799      */
11800 
11801     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11802     sky_wave = cpl_calloc(nbin, sizeof(double));
11803     for (i = 0; i < nbin; i++) {
11804         if (vector[i]) {
11805             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11806             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11807             cpl_vector_delete(vector[i]);
11808             cpl_vector_delete(wvector[i]);
11809         }
11810     }
11811 
11812     cpl_free(vector);
11813     cpl_free(wvector);
11814 
11815 
11816     /*
11817      * Here possible gaps in the final spectrum are filled by interpolation
11818      */
11819 
11820     for (i = 0; i < nbin; i++) {
11821         if (count[i] >= minpoints) {
11822             first_valid = i;
11823             break;
11824         }
11825     }
11826     
11827     for (i = first_valid; i < nbin; i++) {
11828         if (count[i] < minpoints) {
11829             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11830             for (j = i+1; j < nbin; j++) {
11831                 if (count[j] >= minpoints) {
11832                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11833                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11834                                         / 2;
11835                     }
11836                     else {
11837                         frac = (sky_wave[i] - sky_wave[i-1]) 
11838                              / (sky_wave[j] - sky_wave[i-1]);
11839                         sky_spectrum[i] = frac * sky_spectrum[j]
11840                                         + (1 - frac) * sky_spectrum[i-1];
11841                     }
11842                 }
11843             }
11844         }
11845     }
11846 
11847 
11848     /*
11849      * Create the output table
11850      */
11851 
11852     sky = cpl_table_new(nbin);
11853     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11854     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11855     cpl_table_wrap_int(sky, count, "npoints");
11856 
11857 
11858     /*
11859      * Fill the sky map
11860      */
11861 
11862     data  = cpl_image_get_data(wavemap);
11863     sdata = cpl_image_get_data(spectra);
11864     kdata = cpl_image_get_data(skymap);
11865 
11866     for (i = 0; i < npix; i++) {
11867 
11868         /*
11869          * Currently based on linear interpolation
11870          */
11871 
11872         lambda = data[i];
11873         if (lambda < 1.0)
11874             continue;
11875         bin = (lambda - firstLambda) / dispersion;
11876         lambda1 = sky_wave[bin];
11877         value1 = sky_spectrum[bin];
11878         if (lambda1 < lambda) {
11879             bin++;
11880             if (bin < nbin) {
11881                 lambda2 = sky_wave[bin];
11882                 value2  = sky_spectrum[bin];
11883                 if (lambda2 - lambda1 < 0.1) {
11884                     value = (value1 + value2) / 2;
11885                 }
11886                 else {
11887                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11888                     value = frac * value2 + (1 - frac) * value1;
11889                 }
11890             }
11891             else {
11892                 value = value1;
11893             }
11894         }
11895         else {
11896             if (bin > 0) {
11897                 bin--;
11898                 lambda2 = lambda1;
11899                 value2  = value1;
11900                 lambda1 = sky_wave[bin];
11901                 value1  = sky_spectrum[bin];
11902                 if (lambda2 - lambda1 < 0.1) {
11903                     value = (value1 + value2) / 2;
11904                 }
11905                 else {
11906                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11907                     value = frac * value2 + (1 - frac) * value1;
11908                 }
11909             }
11910             else {
11911                 value = value1;
11912             }
11913         }
11914         kdata[i] = value;
11915     }
11916 
11917     if (first_valid)
11918         cpl_table_erase_window(sky, 0, first_valid);
11919 
11920     return sky;
11921 
11922 }
11923 
11924 
11958 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11959                        double dispersion, cpl_image *skymap)
11960 {
11961     const char *func = "mos_sky_map";
11962 
11963     cpl_vector **vector;
11964     double       firstLambda, lastLambda;
11965     double       lambda, lambda1, lambda2;
11966     double       value, value1, value2;
11967     float        min, max;
11968     int         *count;
11969     int          nbin, bin;
11970     int          nx, ny, npix;
11971     int          i, j;
11972 
11973     cpl_table   *sky;
11974     double      *sky_spectrum;
11975     float       *data;
11976     float       *sdata;
11977     float       *kdata;
11978     double      *wdata;
11979 
11980 
11981     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11982         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11983         return NULL;
11984     }
11985     
11986     if (dispersion <= 0.0) {
11987         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11988         return NULL;
11989     }
11990 
11991     nx = cpl_image_get_size_x(spectra);
11992     ny = cpl_image_get_size_y(spectra);
11993     npix = nx * ny;
11994 
11995     if (nx != cpl_image_get_size_x(wavemap) ||
11996         ny != cpl_image_get_size_y(wavemap) ||
11997         nx != cpl_image_get_size_x(skymap) ||
11998         ny != cpl_image_get_size_y(skymap)) {
11999         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
12000         return NULL;
12001     }
12002 
12003 
12004     /*
12005      * Find bluest and reddest wavelengths in the whole image
12006      */
12007 
12008     data = cpl_image_get_data(wavemap);
12009 
12010     for (i = 0; i < npix; i++) {
12011         if (data[i] > 1.0) {
12012             min = max = data[i];
12013             j = i+1;
12014             break;
12015         }
12016     }
12017 
12018     for (i = j; i < npix; i++) {
12019         if (data[i] < 1.0)      /* Impossible wavelength */
12020             continue;
12021         if (min > data[i])
12022             min = data[i];
12023         if (max < data[i])
12024             max = data[i];
12025     }
12026 
12027     firstLambda = min;
12028     lastLambda = max;
12029 
12030 
12031     /*
12032      * Determine length of median spectrum
12033      */
12034 
12035     nbin = (lastLambda - firstLambda) / dispersion;
12036 
12037     /*
12038      * Count how many values will be found for each spectral bin.
12039      * The ith bin has a wavelength range from firstLambda + i*dispersion
12040      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12041      * it is assigned to its central wavelength.
12042      */
12043 
12044     count = cpl_calloc(nbin, sizeof(int));
12045 
12046     data = cpl_image_get_data(wavemap);
12047 
12048     for (i = 0; i < npix; i++) {
12049         if (data[i] < 1.0)
12050             continue;
12051         bin = (data[i] - firstLambda) / dispersion;
12052         if (bin < nbin)                               /* Safer */
12053             count[bin]++;
12054     }
12055 
12056 
12057     /*
12058      * Allocate an array of vectors with the appropriate size, to
12059      * contain a list of all the spectral pixels values. At the same
12060      * time, reset the array of counters (because we will have to
12061      * count again...).
12062      */
12063 
12064     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12065     for (i = 0; i < nbin; i++) {
12066         if (count[i])
12067             vector[i] = cpl_vector_new(count[i]);
12068         else
12069             vector[i] = NULL;
12070         count[i] = 0;
12071     }
12072 
12073 
12074     /*
12075      * Read the wavemap and the spectral images, and add the data values
12076      * to the appropriate wavelength bins
12077      */
12078 
12079     data  = cpl_image_get_data(wavemap);
12080     sdata = cpl_image_get_data(spectra);
12081 
12082     for (i = 0; i < npix; i++) {
12083         if (data[i] < 1.0)
12084             continue;
12085         bin = (data[i] - firstLambda) / dispersion;
12086         if (bin < nbin) {                             /* Safer */
12087             cpl_vector_set(vector[bin], count[bin], sdata[i]);
12088             count[bin]++;
12089         }
12090     }
12091 
12092 
12093     /*
12094      * Compute the median flux for each wavelength bin, and destroy
12095      * at the same time the used vectors
12096      */
12097 
12098     sky_spectrum = cpl_calloc(nbin, sizeof(double));
12099     for (i = 0; i < nbin; i++) {
12100         if (vector[i]) {
12101             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12102             cpl_vector_delete(vector[i]);
12103         }
12104     }
12105 
12106     cpl_free(vector);
12107 
12108 
12109     /*
12110      * Here possible gaps in the final spectrum should be filled
12111      * by interpolation
12112      */
12113 
12114     /* ... */
12115 
12116     /*
12117      * Create the output table
12118      */
12119 
12120     sky = cpl_table_new(nbin);
12121     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12122     cpl_table_set_column_unit(sky, "wavelength", "pixel");
12123     cpl_table_wrap_double(sky, sky_spectrum, "sky");
12124     cpl_table_wrap_int(sky, count, "npoints");
12125     for (i = 0; i < nbin; i++)
12126         cpl_table_set_double(sky, "wavelength", i, 
12127                              firstLambda + (i+0.5)*dispersion);
12128 
12129 
12130     /*
12131      * Fill the sky map
12132      */
12133 
12134     data  = cpl_image_get_data(wavemap);
12135     sdata = cpl_image_get_data(spectra);
12136     kdata = cpl_image_get_data(skymap);
12137     wdata = cpl_table_get_data_double(sky, "wavelength");
12138 
12139     for (i = 0; i < npix; i++) {
12140 
12141         /*
12142          * Currently based on linear interpolation
12143          */
12144 
12145         lambda = data[i];
12146         if (lambda < 1.0)
12147             continue;
12148         bin = (lambda - firstLambda) / dispersion;
12149         lambda1 = wdata[bin];
12150         value1 = sky_spectrum[bin];
12151         if (lambda1 < lambda) {
12152             bin++;
12153             if (bin < nbin) {
12154                 lambda2 = wdata[bin];
12155                 value2  = sky_spectrum[bin];
12156                 value   = ((lambda2 - lambda)*value1 
12157                         +  (lambda - lambda1)*value2) / dispersion;
12158             }
12159             else {
12160                 value = value1;
12161             }
12162         }
12163         else {
12164             if (bin > 0) {
12165                 bin--;
12166                 lambda2 = lambda1;
12167                 value2  = value1;
12168                 lambda1 = wdata[bin];
12169                 value1  = sky_spectrum[bin];
12170                 value   = ((lambda2 - lambda)*value1 
12171                         +  (lambda - lambda1)*value2)/dispersion;
12172             }
12173             else {
12174                 value = value1;
12175             }
12176         }
12177         kdata[i] = value;
12178     }
12179 
12180     return sky;
12181 
12182 }
12183 
12184 
12200 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12201 {
12202     const char *func = "mos_sky_local_old";
12203 
12204     cpl_image *exslit;
12205     cpl_image *sky;
12206     cpl_image *skymap;
12207     float     *data;
12208     float     *sdata;
12209     int        nx, ny;
12210     int        xlow, ylow, xhig, yhig;
12211     int        nslits;
12212     int       *slit_id;
12213     int       *position;
12214     int       *length;
12215     int        i, j, k;
12216 
12217 
12218     if (spectra == NULL) {
12219         cpl_msg_error(func, 
12220                       "A scientific rectified spectral image must be given");
12221         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12222         return NULL;
12223     }
12224 
12225     if (slits == NULL) {
12226         cpl_msg_error(func, "A slits position table must be given");
12227         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12228         return NULL;
12229     }
12230 
12231     nslits   = cpl_table_get_nrow(slits);
12232     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12233     position = cpl_table_get_data_int(slits, "position");
12234     length   = cpl_table_get_data_int(slits, "length");
12235 
12236     nx = cpl_image_get_size_x(spectra);
12237     ny = cpl_image_get_size_y(spectra);
12238 
12239     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12240 
12241     xlow = 1;
12242     xhig = nx;
12243     for (i = 0; i < nslits; i++) {
12244 
12245         if (length[i] == 0)
12246             continue;
12247 
12248         /*
12249          * Define the extraction boundaries. We DON'T write:
12250          *
12251          * ylow = position[i];
12252          * yhig = ylow + length[i];
12253          *
12254          * because the cpl_image pixels are counted from 1, and because in
12255          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12256          */
12257 
12258         ylow = position[i] + 1;
12259         yhig = ylow + length[i] - 1;
12260 
12261         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12262         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12263         cpl_image_delete(exslit);
12264 
12265         data   = cpl_image_get_data(skymap);
12266         data  += nx * position[i];
12267 
12268         for (j = 0; j < length[i]; j++) {
12269             sdata  = cpl_image_get_data(sky);
12270             for (k = 0; k < nx; k++) {
12271                 *data++ = *sdata++;
12272             }
12273         }
12274 
12275         cpl_image_delete(sky);
12276     }
12277 
12278     return skymap;
12279 
12280 }
12281 
12282 
12302 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12303 {
12304     const char *func = "mos_sky_local";
12305 
12306     char        name[MAX_COLNAME];
12307 
12308     cpl_polynomial *fit;
12309     cpl_vector     *points;
12310     cpl_vector     *values;
12311     cpl_vector     *keep_points;
12312     cpl_vector     *keep_values;
12313     cpl_image      *exslit;
12314     cpl_image      *sky;
12315     cpl_image      *subtracted;
12316     cpl_image      *profile;
12317     cpl_image      *skymap;
12318     cpl_table      *objects;
12319     float          *data;
12320     float          *sdata;
12321     float          *xdata;
12322     double         *vdata;
12323     double         *pdata;
12324     double          median;
12325     int             nx, ny;
12326     int             xlow, ylow, xhig, yhig;
12327     int             nslits;
12328     int            *slit_id;
12329     int            *position;
12330     int            *length;
12331     int            *is_sky;
12332     int             nsky, nbad;
12333     int             maxobjects;
12334     int             margin = 3;
12335     int             radius = 6;
12336     int             i, j, k;
12337 
12338 
12339     if (spectra == NULL) {
12340         cpl_msg_error(func, 
12341                       "A scientific rectified spectral image must be given");
12342         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12343         return NULL;
12344     }
12345 
12346     if (slits == NULL) {
12347         cpl_msg_error(func, "A slits position table must be given");
12348         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12349         return NULL;
12350     }
12351 
12352     if (order < 0) {
12353         cpl_msg_error(func, "Invalid fit order");
12354         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12355         return NULL;
12356     }
12357 
12358     nslits   = cpl_table_get_nrow(slits);
12359     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12360     position = cpl_table_get_data_int(slits, "position");
12361     length   = cpl_table_get_data_int(slits, "length");
12362 
12363     nx = cpl_image_get_size_x(spectra);
12364     ny = cpl_image_get_size_y(spectra);
12365 
12366     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12367 
12368     xlow = 1;
12369     xhig = nx;
12370     for (i = 0; i < nslits; i++) {
12371 
12372         if (length[i] == 0)
12373             continue;
12374 
12375         /*
12376          * Define the extraction boundaries. We DON'T write:
12377          *
12378          * ylow = position[i];
12379          * yhig = ylow + length[i];
12380          *
12381          * because the cpl_image pixels are counted from 1, and because in
12382          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12383          */
12384 
12385         ylow = position[i] + 1;
12386         yhig = ylow + length[i] - 1;
12387 
12388         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12389         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12390         cpl_image_delete(exslit);
12391 
12392         data   = cpl_image_get_data(skymap);
12393         data  += nx * position[i];
12394 
12395         for (j = 0; j < length[i]; j++) {
12396             sdata  = cpl_image_get_data(sky);
12397             for (k = 0; k < nx; k++) {
12398                 *data++ = *sdata++;
12399             }
12400         }
12401 
12402         cpl_image_delete(sky);
12403     }
12404 
12405 
12406     /*
12407      * Preliminary sky-subtracted image
12408      */
12409 
12410     subtracted = cpl_image_duplicate(spectra);
12411     cpl_image_subtract(subtracted, skymap);
12412     cpl_image_delete(skymap);
12413 
12414 
12415     /*
12416      * Detect objects positions in all slits
12417      */
12418 
12419     objects = cpl_table_duplicate(slits);
12420     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12421     cpl_image_delete(profile);
12422     cpl_image_delete(subtracted);
12423 
12424 
12425     /*
12426      * Flag the sky pixels. Note that maxobjects is intentionally 
12427      * the max number of objects increased by one.
12428      */
12429 
12430     maxobjects = 1;
12431     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12432     while (cpl_table_has_column(objects, name)) {
12433         maxobjects++;
12434         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12435     }
12436 
12437     is_sky = cpl_calloc(ny, sizeof(int));
12438 
12439     for (i = 0; i < nslits; i++) {
12440 
12441         if (length[i] == 0)
12442             continue;
12443 
12444         ylow = position[i] + margin;
12445         yhig = position[i] + length[i] - margin;
12446 
12447         for (j = ylow; j < yhig; j++)
12448             is_sky[j] = 1;
12449 
12450         for (j = 1; j < maxobjects; j++) {
12451             snprintf(name, MAX_COLNAME, "object_%d", j);
12452             if (cpl_table_is_valid(objects, name, i)) {
12453                 snprintf(name, MAX_COLNAME, "start_%d", j);
12454                 ylow = cpl_table_get_int(objects, name, i, NULL);
12455                 snprintf(name, MAX_COLNAME, "end_%d", j);
12456                 yhig = cpl_table_get_int(objects, name, i, NULL);
12457                 for (k = ylow; k <= yhig; k++)
12458                     is_sky[k] = 0;
12459             }
12460         }
12461 
12462 
12463         /*
12464          * Eliminate isolated sky points
12465          */
12466 
12467         ylow = position[i] + margin + 1;
12468         yhig = position[i] + length[i] - margin - 1;
12469 
12470         for (j = ylow; j < yhig; j++)
12471             if (is_sky[j])
12472                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12473                     is_sky[j] = 0;
12474 
12475     }
12476 
12477 
12478     /*
12479      * Determination of the sky map
12480      */
12481 
12482     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12483 
12484     for (i = 0; i < nslits; i++) {
12485 
12486         if (length[i] == 0)
12487             continue;
12488 
12489         ylow = position[i];
12490         yhig = ylow + length[i];
12491 
12492         nsky = 0;
12493         for (j = ylow; j < yhig; j++)
12494             if (is_sky[j])
12495                 nsky++;
12496 
12497         if (nsky > order + 1) {
12498             if (order) {
12499                 points = cpl_vector_new(nsky);
12500                 nsky = 0;
12501                 for (j = ylow; j < yhig; j++) {
12502                     if (is_sky[j]) {
12503                         cpl_vector_set(points, nsky, j);
12504                         nsky++;
12505                     }
12506                 }
12507 
12508                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12509                 xdata = cpl_image_get_data(exslit);
12510                 values = cpl_vector_new(nsky);
12511 
12512                 for (j = 0; j < nx; j++) {
12513                     nsky = 0;
12514                     for (k = ylow; k < yhig; k++) {
12515                         if (is_sky[k]) {
12516                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12517                             nsky++;
12518                         }
12519                     }
12520 
12521                     /*
12522                      * Eliminate obvious outliers
12523                      */
12524 
12525                     median = cpl_vector_get_median_const(values);
12526                     vdata = cpl_vector_get_data(values);
12527                     pdata = cpl_vector_get_data(points);
12528                     nbad = 0;
12529                     for (k = 0; k < nsky; k++) {
12530                         if (fabs(vdata[k] - median) < 100) {
12531                             if (nbad) {
12532                                 vdata[k-nbad] = vdata[k];
12533                                 pdata[k-nbad] = pdata[k];
12534                             }
12535                         }
12536                         else
12537                             nbad++;
12538                     }
12539 
12540                     if (nsky == nbad)
12541                         continue;
12542 
12543                     if (nbad && nsky - nbad > order + 1) {
12544                         keep_values = values;
12545                         keep_points = points;
12546                         values = cpl_vector_wrap(nsky-nbad, vdata);
12547                         points = cpl_vector_wrap(nsky-nbad, pdata);
12548                     }
12549 
12550                     if (nsky - nbad > order + 1) {
12551 
12552                         fit = cpl_polynomial_fit_1d_create(points, values, 
12553                                                            order, NULL);
12554 
12555                         if (fit) {
12556                             for (k = ylow; k < yhig; k++) {
12557                                 xdata[j+(k-ylow)*nx] = 
12558                                          cpl_polynomial_eval_1d(fit, k, NULL);
12559                             }
12560 
12561                             cpl_polynomial_delete(fit);
12562                         }
12563                         else
12564                             cpl_error_reset();
12565                     }
12566                     else {
12567                         for (k = 0; k < nsky; k++) {
12568                             xdata[j+k*nx] = median;
12569                         }
12570                     }
12571 
12572                     if (nbad && nsky - nbad > order + 1) {
12573                         cpl_vector_unwrap(values);
12574                         cpl_vector_unwrap(points);
12575                         values = keep_values;
12576                         points = keep_points;
12577                     }
12578 
12579                     if (nbad) {
12580                         nsky = 0;
12581                         for (k = ylow; k < yhig; k++) {
12582                             if (is_sky[k]) {
12583                                 cpl_vector_set(points, nsky, k);
12584                                 nsky++;
12585                             }
12586                         }
12587                     }
12588 
12589                 }
12590 
12591                 cpl_vector_delete(values);
12592                 cpl_vector_delete(points);
12593 
12594                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12595                 cpl_image_delete(exslit);
12596 
12597             }
12598             else {
12599                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12600                 xdata = cpl_image_get_data(exslit);
12601                 values = cpl_vector_new(nsky);
12602 
12603                 for (j = 0; j < nx; j++) {
12604                     nsky = 0;
12605                     for (k = ylow; k < yhig; k++) {
12606                         if (is_sky[k]) {
12607                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12608                             nsky++;
12609                         }
12610                     }
12611 
12612                     median = cpl_vector_get_median_const(values);
12613 
12614                     for (k = ylow; k < yhig; k++)
12615                         xdata[j+(k-ylow)*nx] = median;
12616 
12617                 }
12618 
12619                 cpl_vector_delete(values);
12620 
12621                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12622                 cpl_image_delete(exslit);
12623             }
12624         }
12625         else
12626             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
12627     }
12628 
12629     cpl_free(is_sky);
12630 
12631     return skymap;
12632 
12633 }
12634 
12635 
12657 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
12658                                  float threshold, float ratio)
12659 {
12660     const char *func = "mos_clean_cosmics";
12661 
12662     cpl_image  *smoothImage;
12663     cpl_table  *table;
12664     cpl_matrix *kernel;
12665     int        *xdata;
12666     int        *ydata;
12667     float      *idata;
12668     float      *sdata;
12669     float       sigma, sum, value, smoothValue;
12670     double      noise;
12671     int         count;
12672     float       fMax;
12673     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
12674     int         xLen;
12675     int         yLen;
12676     int         nPix;
12677     int         first = 1;  /* position of first cosmic ray candidate
12678                                encountered while scanning the image */
12679     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
12680     int         numCosmic = 0;
12681     int         found, foundContiguousCandidate;
12682     int        *cosmic;
12683   
12684 
12685     if (image == NULL)
12686         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12687 
12688 
12689     /*
12690      *  "cosmic" is a flags holder (initialized to zero):
12691      *
12692      *           -1 = candidate for cosmic ray
12693      *            0 = not a cosmic
12694      *            1 = a cosmic ray
12695      *            2 = member of current group of contiguous candidates
12696      *            3 = examined member of current group
12697      */
12698 
12699     xLen = cpl_image_get_size_x(image);
12700     yLen = cpl_image_get_size_y(image);
12701 
12702     if (xLen < 4 || yLen < 4)
12703         return CPL_ERROR_NONE;
12704 
12705     nPix = xLen * yLen;
12706 
12707     /*
12708      * Noise estimation from negative offsets in image. Note that this
12709      * assumes that the background level (skyLevel) has already been 
12710      * subtracted from the data. In this way we estimate the noise due 
12711      * to detector readout and to the background signal level (before 
12712      * it were removed). Theoretically this is given by 
12713      *
12714      *        noise = sqrt(ron^2 + skyLevel/gain)
12715      *
12716      * where ron is the read-out-noise. To this we will sum the noise 
12717      * contribution due to any increase of the signal above the background
12718      * by an amount scienceLevel. Theoretically the total noise is given by
12719      *
12720      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
12721      *
12722      * that is
12723      *
12724      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
12725      *
12726      */
12727 
12728     idata = cpl_image_get_data(image);
12729     noise = 0.0;
12730     count = 0;
12731 
12732     for (i = 0; i < nPix; i++) {
12733         if (idata[i] < -0.00001) {
12734             noise -= idata[i];
12735             count++;
12736         }
12737     }
12738 
12739     noise /= count;
12740     noise *= 1.25;       /* Factor to convert average deviation to sigma */
12741 
12742     cosmic = cpl_calloc(nPix, sizeof(int));
12743 
12744     if (threshold < 0.)
12745         threshold = 4.0;
12746     if (ratio < 0.)
12747         ratio = 2.0;
12748 
12749     kernel = cpl_matrix_new(3, 3);
12750     cpl_matrix_fill(kernel, 1.0);
12751     cpl_matrix_set(kernel, 1, 1, 0.0);
12752     smoothImage = cpl_image_filter_median(image, kernel);
12753     cpl_matrix_delete(kernel);
12754     
12755     /*
12756      *  Loop on images pixels, searching for cosmic rays candidates.
12757      *  Border pixels are currently excluded (they cannot contain
12758      *  candidates), to avoid that the search for groups of contiguous
12759      *  pixels would ever go out of image boundaries. In future we may
12760      *  overcome this limit, adding an appropriate check when contiguous
12761      *  pixels are searched.
12762      */
12763 
12764     sdata = cpl_image_get_data(smoothImage);
12765 
12766     for (j = 1; j < yLen - 1; j++) {
12767         for (i = 1; i < xLen - 1; i++) {
12768             value = idata[i + j * xLen];
12769             smoothValue = sdata[i + j * xLen];
12770             if (smoothValue < 1.0)
12771                 smoothValue = 1.0;
12772             sigma = sqrt(noise * noise + smoothValue / gain);
12773             if (value - smoothValue >= threshold * sigma) 
12774                 cosmic[i + j * xLen] = -1;
12775         }
12776     }
12777 
12778     cpl_image_delete(smoothImage);
12779 
12780 
12781     /*
12782      *  Search for groups of contiguous cosmic rays candidates.
12783      */
12784 
12785     do {
12786         found = 0;
12787         for (pos = first; pos < nPix; pos++) {
12788             if (cosmic[pos] == -1) {
12789                 cosmic[pos] = 2;         /*  Candidate found.  */
12790                 i = pos % xLen;          /*  Its coordinates.  */
12791                 j = pos / xLen;
12792                 first = pos;
12793                 first++;      /* ???  really necessary? */
12794                 found = 1;
12795                 break;
12796             }
12797         }
12798 
12799         if (found) {
12800 
12801             /*
12802              *  Determine new group of contiguous cosmic rays candidates.
12803              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12804              *  and the value of the max pixel and its position, fMax, iPosMax,
12805              *  jPosMax.
12806              */
12807 
12808             iMin = iMax = iPosMax = i;
12809             jMin = jMax = jPosMax = j;
12810             fMax = idata[i + j * xLen];
12811 
12812             do {
12813                 foundContiguousCandidate = 0;
12814                 for (l = 0; l <= 1; l++) {
12815                     for (k = 0; k <= 1; k++) {
12816 
12817                         /*
12818                          *  Looping on 4 pixels to North, East, South and West
12819                          */
12820 
12821                         ii = i + k - l;
12822                         jj = j + k + l - 1;
12823                         if (cosmic[ii + jj * xLen] == -1) {
12824                             foundContiguousCandidate = 1;
12825                             cosmic[ii + jj * xLen] = 2;
12826                                         /* Candidate belongs to current group */
12827                             iii = ii;   /* Keep its position */
12828                             jjj = jj;
12829 
12830                             /*
12831                              * Upgrade search box
12832                              */
12833 
12834                             if (ii < iMin)
12835                                 iMin = ii;
12836                             if (ii > iMax)
12837                                 iMax = ii;
12838                             if (jj < jMin)
12839                                 jMin = jj;
12840                             if (jj > jMax)
12841                                 jMax = jj;
12842 
12843                             if (idata[ii + jj * xLen] > fMax) {
12844                                 fMax = idata[ii + jj * xLen];
12845                                 iPosMax = ii;
12846                                 jPosMax = jj;
12847                             }
12848                         }
12849                     }
12850                 }
12851 
12852                 /*
12853                  *  We are done exploring the "cross". Now mark as "examined"
12854                  *  the current candidate (at the center of the cross):
12855                  */
12856 
12857                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12858 
12859                 if (foundContiguousCandidate) {
12860 
12861                     /*
12862                      * Pass (arbitrarily) the coordinates of the LAST found 
12863                      * candidate
12864                      */
12865 
12866                     i = iii;
12867                     j = jjj;
12868 
12869                     /* 
12870                      * Skip the rest, continue loop on new candidate 
12871                      */
12872 
12873                     continue; 
12874                 }
12875 
12876 
12877                 /*
12878                  *  Look for leftovers in the (growing!) search box
12879                  */
12880 
12881                 for (l = jMin; l <= jMax; l++) {
12882                     for (k = iMin; k <= iMax; k++) {
12883                         if (cosmic[k + l * xLen] == 2) {
12884                             i = k;
12885                             j = l;
12886                             foundContiguousCandidate = 1;
12887                             break;
12888                         }
12889                     }
12890                     if (foundContiguousCandidate) 
12891                         break;
12892                 }
12893             } while (foundContiguousCandidate);
12894 
12895 
12896             /*
12897              *  No more contiguous candidates are found. Decide now
12898              *  whether the current group is a cosmic ray or not.
12899              */
12900 
12901             sum = 0.;                /* Sum of 8 pixels around max position */
12902             for (l = -1; l <= 1; l++) {
12903                 for (k = -1; k <= 1; k++) {
12904                     if (l != 0 || k != 0) {
12905                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12906                     }
12907                 }
12908             }
12909 
12910             sum /= 8.;
12911             if (fMax > ratio * sum) {
12912                 for (l = jMin - 1; l <= jMax + 1; l++) {
12913                     for (k = iMin - 1; k <= iMax + 1; k++) {
12914                         if (cosmic[k + l * xLen] == 3) {
12915                             cosmic[k + l * xLen] = 1;
12916                             numCosmic++;
12917                         }
12918                     }
12919                 }
12920             }
12921             else {
12922                 for (l = jMin - 1; l <= jMax + 1; l++) {
12923                     for (k = iMin - 1; k <= iMax + 1; k++) {
12924                         if (cosmic[k + l * xLen] != -1) {
12925                             if (cosmic[k + l * xLen] == 1) 
12926                                 numCosmic--;
12927                             cosmic[k + l * xLen] = 0;
12928                         }
12929                     }
12930                 }
12931             }
12932         }
12933     } while (found);
12934 
12935 
12936     /*
12937      *  Prepare table containing cosmic rays coordinates. 
12938      */
12939 
12940     table = cpl_table_new(numCosmic);
12941     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12942     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12943     cpl_table_set_column_unit(table, "x", "pixel");
12944     cpl_table_set_column_unit(table, "y", "pixel");
12945     xdata = cpl_table_get_data_int(table, "x");
12946     ydata = cpl_table_get_data_int(table, "y");
12947 
12948     for (pos = 0, i = 0; pos < nPix; pos++) {
12949         if (cosmic[pos] == 1) {
12950             xdata[i] = (pos % xLen);
12951             ydata[i] = (pos / xLen);
12952             i++;
12953         }
12954     }
12955 
12956     mos_clean_bad_pixels(image, table, 1);
12957 
12958     cpl_free(cosmic);
12959     cpl_table_delete(table);
12960 
12961     return CPL_ERROR_NONE;
12962 
12963 }
12964 
12965 
12966 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12967                                     int spectral)
12968 {
12969     const char *func = "mos_clean_cosmics";
12970  
12971     float       *idata;
12972     int         *isBadPix;
12973     int          i, j, k, d;
12974     int          xlen, ylen, totPix;
12975     int          nBadPixels = 0;
12976     int          sign, foundFirst;
12977     int         *xValue = NULL;
12978     int         *yValue = NULL;
12979     float        save = 0.;
12980     double       sumd;
12981     int          cx, cy;
12982     int          nPairs;
12983     float        estimate[4];
12984     int          sx[] = {0, 1, 1, 1};
12985     int          sy[] = {1,-1, 0, 1};
12986     int          searchHorizon = 100;
12987     int          percent = 15;
12988 
12989 
12990     if (image == NULL || table == NULL)
12991         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12992 
12993     if (1 != cpl_table_has_column(table, "x"))
12994         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12995 
12996     if (1 != cpl_table_has_column(table, "y"))
12997         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12998 
12999     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
13000         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13001 
13002     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
13003         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
13004 
13005     nBadPixels = cpl_table_get_nrow(table);
13006 
13007     if (nBadPixels) {
13008         xlen = cpl_image_get_size_x(image);
13009         ylen = cpl_image_get_size_y(image);
13010         idata = cpl_image_get_data(image);
13011         totPix = xlen * ylen;
13012         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
13013             isBadPix = cpl_calloc(totPix, sizeof(int));
13014         }
13015         else {
13016             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
13017                             "skip bad pixel correction", percent);
13018             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13019         }
13020     }
13021     else {
13022         cpl_msg_debug(func, "No pixel values to interpolate");
13023         return CPL_ERROR_NONE;
13024     }
13025 
13026     xValue = cpl_table_get_data_int(table, "x");
13027     yValue = cpl_table_get_data_int(table, "y");
13028 
13029     for (i = 0; i < nBadPixels; i++)
13030         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13031 
13032     for (i = 0; i < nBadPixels; i++) {
13033 
13034         /*
13035          *  Search for the closest good pixel along the 4 fundamental 
13036          *  directions (in both senses):
13037          *                            \ | /
13038          *                             \|/
13039          *                           --- ---
13040          *                             /|\
13041          *                            / | \
13042          *
13043          *  Then collect pairs of values to interpolate linearly.
13044          */
13045 
13046         nPairs = 0;
13047         for (j = 0; j < 4; j++) {
13048 
13049             if (spectral) /* Just horizontal interpolation for spectral data */
13050                 if (j != 2)
13051                     continue;
13052 
13053             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
13054             sumd = 0.;
13055             foundFirst = 0;
13056             for (k = 0; k < 2; k++) {
13057                 sign = 2 * k - 1;
13058                 d = 0;
13059                 cx = xValue[i];
13060                 cy = yValue[i];
13061                 do {
13062                     cx += sign * sx[j];
13063                     cy += sign * sy[j];
13064                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
13065                         break;
13066                     d++;
13067                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13068 
13069                 if (cx >= 0 && cx < xlen && 
13070                     cy >= 0 && cy < ylen && d < searchHorizon) {
13071 
13072                     /*
13073                      *  In this block is cripted the linear interpolation...
13074                      */
13075 
13076                     save = idata[cx + cy * xlen];
13077                     estimate[nPairs] += save / d;
13078                     sumd += 1. / (double) d;
13079                     if (k) {
13080                         estimate[nPairs] /= sumd;
13081                         nPairs++;
13082                     }
13083                     else {
13084                         foundFirst = 1;
13085                     }
13086                 }
13087                 else {
13088 
13089                     /*
13090                      * Image borders were crossed, incomplete pair of values
13091                      */
13092 
13093                     if (k) {
13094                         if (foundFirst) {
13095                             estimate[nPairs] = save;
13096                             nPairs++;
13097                         }
13098                     }
13099                 }
13100             }
13101         }
13102 
13103         /*
13104          * Replace pixel value of the input image, corresponding to
13105          * the current bad pixel, with the median of the estimates
13106          * resulted from the 4 linear interpolations.
13107          */
13108 
13109         if (nPairs > 2) {
13110             idata[xValue[i] + yValue[i] * xlen] = 
13111                                cpl_tools_get_median_float(estimate, nPairs);
13112         }
13113         else if (nPairs == 2) {
13114             idata[xValue[i] + yValue[i] * xlen] =
13115                                             (estimate[0] + estimate[1]) / 2.;
13116         }
13117         else if (nPairs == 1) {
13118             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13119         }
13120         else {
13121             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13122                           xValue[i], yValue[i]);
13123         }
13124     }
13125 
13126     cpl_free(isBadPix);
13127 
13128     return CPL_ERROR_NONE;
13129 }
13130 
13131 
13161 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13162                            cpl_table *polytraces, double reference,
13163                            double blue, double red, double dispersion)
13164 {
13165     const char *func = "mos_spatial_map";
13166 
13167     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13168                                                  /* Max order is 5 */
13169     cpl_polynomial *polytop;
13170     cpl_polynomial *polybot;
13171     cpl_image      *calibration;
13172     float          *data;
13173     double          top, bot;
13174     double          coeff;
13175     double          ytop, ybot;
13176     double          ypos, yfra;
13177     double          factor;
13178     int             yint, yprev;
13179     int             nslits;
13180     int             npseudo;
13181     int            *slit_id;
13182     int            *length;
13183     int             nx, ny;
13184     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13185     int             missing_top, missing_bot;
13186     int             null;
13187     int             order;
13188     int             i, j;
13189     cpl_size        k;
13190 
13191 
13192     if (spectra == NULL || slits == NULL || polytraces == NULL) {
13193         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13194         return NULL;
13195     }
13196 
13197     if (dispersion <= 0.0) {
13198         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13199         return NULL;
13200     }
13201 
13202     if (red - blue < dispersion) {
13203         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13204         return NULL;
13205     }
13206 
13207     nx = cpl_image_get_size_x(spectra);
13208     ny = cpl_image_get_size_y(spectra);
13209 
13210     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13211     data = cpl_image_get_data(calibration);
13212 
13213     length  = cpl_table_get_data_int(slits, "length");
13214     nslits  = cpl_table_get_nrow(slits);
13215     slit_id = cpl_table_get_data_int(slits, "slit_id");
13216     order   = cpl_table_get_ncol(polytraces) - 2;
13217 
13218     /*
13219      * The spatial resampling is performed for a certain number of 
13220      * pixels above and below the position of the reference wavelength:
13221      */
13222 
13223     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13224     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13225 
13226     for (i = 0; i < nslits; i++) {
13227         
13228         if (length[i] == 0)
13229             continue;
13230 
13231         /*
13232          * Note that the x coordinate of the reference pixels on the CCD
13233          * is taken arbitrarily at the top end of each slit. This wouldn't
13234          * be entirely correct in case of curved slits, or in presence of
13235          * heavy distortions: in such cases the spatial resampling is
13236          * really performed across a wide range of wavelengths. But
13237          * the lag between top and bottom spectral curvature models 
13238          * would introduce even in such cases negligible effects on
13239          * the spectral spatial resampling.
13240          */
13241 
13242         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13243 
13244         start_pixel = refpixel - pixel_below;
13245         if (start_pixel < 0)
13246             start_pixel = 0;
13247 
13248         end_pixel = refpixel + pixel_above;
13249         if (end_pixel > nx)
13250             end_pixel = nx;
13251 
13252         /*
13253          * Recover from the table of spectral curvature coefficients
13254          * the curvature polynomials.
13255          */
13256 
13257         missing_top = 0;
13258         polytop = cpl_polynomial_new(1);
13259         for (k = 0; k <= order; k++) {
13260             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13261             if (null) {
13262                 cpl_polynomial_delete(polytop);
13263                 missing_top = 1;
13264                 break;
13265             }
13266             cpl_polynomial_set_coeff(polytop, &k, coeff);
13267         }
13268 
13269         missing_bot = 0;
13270         polybot = cpl_polynomial_new(1);
13271         for (k = 0; k <= order; k++) {
13272             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13273             if (null) {
13274                 cpl_polynomial_delete(polybot);
13275                 missing_bot = 1;
13276                 break;
13277             }
13278             cpl_polynomial_set_coeff(polybot, &k, coeff);
13279         }
13280 
13281         if (missing_top && missing_bot) {
13282             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
13283                             slit_id[i]);
13284             continue;
13285         }
13286 
13287         /*
13288          * In case just one of the two edges was not traced, the other
13289          * edge curvature model is duplicated and shifted to the other
13290          * end of the slit: better than nothing!
13291          */
13292 
13293         if (missing_top) {
13294             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13295                             "the spectral curvature of the lower edge "
13296                             "is used instead.", slit_id[i]);
13297             polytop = cpl_polynomial_duplicate(polybot);
13298             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13299             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13300             k = 0;
13301             coeff = cpl_polynomial_get_coeff(polybot, &k);
13302             coeff += ytop - ybot;
13303             cpl_polynomial_set_coeff(polytop, &k, coeff);
13304         }
13305 
13306         if (missing_bot) {
13307             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13308                             "the spectral curvature of the upper edge "
13309                             "is used instead.", slit_id[i]);
13310             polybot = cpl_polynomial_duplicate(polytop);
13311             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13312             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13313             k = 0;
13314             coeff = cpl_polynomial_get_coeff(polytop, &k);
13315             coeff -= ytop - ybot;
13316             cpl_polynomial_set_coeff(polybot, &k, coeff);
13317         }
13318 
13319         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13320         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13321         npseudo = ceil(top-bot) + 1;
13322 
13323         if (npseudo < 1) {
13324             cpl_polynomial_delete(polytop);
13325             cpl_polynomial_delete(polybot);
13326             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13327                             slit_id[i]);
13328             continue;
13329         }
13330 
13331         for (j = start_pixel; j < end_pixel; j++) {
13332             top = cpl_polynomial_eval_1d(polytop, j, NULL);
13333             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13334             factor = (top-bot)/npseudo;
13335             for (k = 0; k <= npseudo; k++) {
13336                 ypos = top - k*factor;
13337                 yint = ypos;
13338                 yfra = ypos - yint;
13339                 if (yint >= 0 && yint < ny-1) {
13340                     data[j + nx*yint] = (top-yint)/factor;
13341                     if (k) {
13342 
13343                         /*
13344                          * This is added to recover lost pixels on
13345                          * the CCD image (pixels are lost because
13346                          * the CCD pixels are less than npseudo+1).
13347                          */
13348 
13349                         if (yprev - yint > 1) {
13350                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
13351                         }
13352                     }
13353                 }
13354                 yprev = yint;
13355             }
13356         }
13357         cpl_polynomial_delete(polytop);
13358         cpl_polynomial_delete(polybot);
13359     }
13360 
13361     return calibration;
13362 }
13363 
13364 
13427 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13428                               int maxradius, int conradius)
13429 {
13430     const char *func = "mos_detect_objects";
13431 
13432     cpl_image  *profile;
13433     float      *pdata;
13434     float      *p;
13435 
13436     char        name[MAX_COLNAME];
13437 
13438     int         nslits;
13439     int         npeaks;
13440     int         nobjects, objpos, totobj;
13441     int         maxobjects;
13442     int        *position;
13443     int        *length;
13444     int        *reject;
13445     double     *place;
13446     double     *bright;
13447     double      mindistance;
13448     int         pos, count;
13449     int         up;
13450     int         low, hig;
13451     int         row;
13452     int         i, j, k;
13453 
13454     const int   min_pixels = 10;
13455 
13456     
13457     if (cpl_error_get_code() != CPL_ERROR_NONE)
13458         return NULL;
13459  
13460     if (image == NULL || slits == NULL) { 
13461         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13462         return NULL;
13463     }
13464 
13465     if (margin < 0)
13466         margin = 0;
13467 
13468     if (maxradius < 0) {
13469         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13470         return NULL;
13471     }
13472 
13473     if (conradius < 0) {
13474         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13475         return NULL;
13476     }
13477 
13478     nslits   = cpl_table_get_nrow(slits);
13479     position = cpl_table_get_data_int(slits, "position");
13480     length   = cpl_table_get_data_int(slits, "length");
13481 
13482     profile = cpl_image_collapse_create(image, 1);
13483     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
13484     pdata = cpl_image_get_data(profile);
13485 
13486     row = 1;
13487     maxobjects = 0;
13488     totobj = 0;
13489     for (i = 0; i < nslits; i++) {
13490 
13491         if (length[i] == 0)
13492             continue;
13493 
13494         pos = position[i] + margin;
13495         count = length[i] - 2*margin;
13496 
13497         if (count < min_pixels)
13498             continue;
13499 
13500         p = pdata + pos;
13501 
13502 
13503         /*
13504          * Count peaks candidates
13505          */
13506 
13507         npeaks = 0;
13508         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13509             npeaks++;
13510         }
13511 
13512         up = 0;
13513         for (j = 0; j < count - 3; j++) {
13514             if (p[j] > 0) {
13515                 if (p[j+1] > p[j]) {
13516                     up++;
13517                 }
13518                 else {
13519                     if (up > 2) {
13520                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13521                             if (p[j] > 5)
13522                                 npeaks++;
13523                         }
13524                     }
13525                     else if (up > 1) {
13526                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13527                             if (p[j] > 5)
13528                                 npeaks++;
13529                         }
13530                     }
13531                     up = 0;
13532                 }
13533             }
13534             else {
13535                 up = 0;
13536             }
13537         }
13538 
13539         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
13540             && p[count-3] > p[count-4] && p[count-4] > 0) {
13541             npeaks++;
13542         }
13543 
13544         if (npeaks == 0)
13545             continue;
13546 
13547 
13548         /*
13549          * Get candidates parameters
13550          */
13551 
13552         reject = cpl_calloc(npeaks, sizeof(int));
13553         bright = cpl_calloc(npeaks, sizeof(double));
13554         place  = cpl_calloc(npeaks, sizeof(double));
13555 
13556         npeaks = 0;
13557         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13558             bright[0] = p[0];
13559             place[0] = position[i] + margin;
13560             npeaks++;
13561         }
13562 
13563         up = 0;
13564         for (j = 0; j < count - 3; j++) {
13565             if (p[j] > 0) {
13566                 if (p[j+1] > p[j]) {
13567                     up++;
13568                 }
13569                 else {
13570                     if (up > 2) {
13571                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13572                             if (p[j] > 5) {
13573                                 bright[npeaks] = p[j];
13574                                 place[npeaks] = position[i] + margin + j + 1
13575                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13576                                 npeaks++;
13577                             }
13578                         }
13579                     }
13580                     else if (up > 1) {
13581                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13582                             if (p[j] > 5) {
13583                                 bright[npeaks] = p[j];
13584                                 place[npeaks] = position[i] + margin + j + 1
13585                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13586                                 npeaks++;
13587                             }
13588                         }
13589                     }
13590                     up = 0;
13591                 }
13592             }
13593             else {
13594                 up = 0;
13595             }
13596         }
13597 
13598         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
13599             && p[count-3] > p[count-4] && p[count-4] > 0) {
13600             bright[npeaks] = p[count-1];
13601             place[npeaks] = position[i] + count;
13602             npeaks++;
13603         }
13604 
13605 
13606         /*
13607          * Now select the uncontaminated peaks
13608          */
13609 
13610         if (fabs(place[0] - pos) < 1.0)
13611             reject[0] = 1;
13612         if (fabs(place[npeaks-1] - pos - count) < 1.0)
13613             reject[npeaks-1] = 1;
13614         for (j = 0; j < npeaks; j++) {
13615             for (k = 0; k < npeaks; k++) {
13616                 if (k == j)
13617                     continue;
13618                 mindistance = conradius * bright[k] / bright[j] 
13619                                         * bright[k] / bright[j];
13620                 if (fabs(place[j] - place[k]) < mindistance)
13621                     reject[j] = 1;
13622             }
13623         }
13624 
13625 /* new part */
13626         for (j = 0; j < npeaks; j++) {
13627             if (reject[j])
13628                 continue;
13629             if (j) {
13630                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13631                     / (bright[j-1] + bright[j]) + 1;
13632             }
13633             else {
13634                 low = pos;
13635             }
13636             if (j < npeaks - 1) {
13637                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13638                     / (bright[j+1] + bright[j]) + 1;
13639             }
13640             else {
13641                 hig = pos + count;
13642             }
13643 
13644             if (low < pos)
13645                 low = pos;
13646             if (hig > pos + count)
13647                 hig = pos + count;
13648             if (place[j] - low > maxradius)
13649                 low = place[j] - maxradius;
13650             if (hig - place[j] > maxradius)
13651                 hig = place[j] + maxradius;
13652             if (hig == low)
13653                 reject[j] = 1;
13654         }
13655 /* end new part */
13656 
13657         nobjects = npeaks;
13658         for (j = 0; j < npeaks; j++)
13659             if (reject[j])
13660                 nobjects--;
13661 
13662         for (j = 0; j < nobjects; j++) {
13663             snprintf(name, MAX_COLNAME, "object_%d", j+1);
13664             if (cpl_table_has_column(slits, name))
13665                 continue;
13666             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
13667             snprintf(name, MAX_COLNAME, "start_%d", j+1);
13668             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13669             cpl_table_set_column_unit(slits, name, "pixel");
13670             snprintf(name, MAX_COLNAME, "end_%d", j+1);
13671             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13672             cpl_table_set_column_unit(slits, name, "pixel");
13673             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13674             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13675             cpl_table_set_column_unit(slits, name, "pixel");
13676         }
13677 
13678         objpos = nobjects;
13679         for (j = 0; j < npeaks; j++) {
13680             if (reject[j])
13681                 continue;
13682             if (j) {
13683                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13684                     / (bright[j-1] + bright[j]) + 1;
13685             }
13686             else {
13687                low = pos;
13688             }
13689             if (j < npeaks - 1) {
13690                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13691                     / (bright[j+1] + bright[j]) + 1;
13692             }
13693             else {
13694                 hig = pos + count;
13695             }
13696 
13697             if (low < pos)
13698                 low = pos;
13699             if (hig > pos + count)
13700                 hig = pos + count;
13701             if (place[j] - low > maxradius)
13702                 low = place[j] - maxradius;
13703             if (hig - place[j] > maxradius)
13704                 hig = place[j] + maxradius;
13705 
13706             snprintf(name, MAX_COLNAME, "object_%d", objpos);
13707             cpl_table_set_double(slits, name, i, place[j]);
13708             snprintf(name, MAX_COLNAME, "start_%d", objpos);
13709             cpl_table_set_int(slits, name, i, low);
13710             snprintf(name, MAX_COLNAME, "end_%d", objpos);
13711             cpl_table_set_int(slits, name, i, hig);
13712             snprintf(name, MAX_COLNAME, "row_%d", objpos);
13713             cpl_table_set_int(slits, name, i, row + objpos - 1);
13714             totobj++;
13715             objpos--;
13716         }
13717 
13718         row += nobjects;
13719 
13720         if (maxobjects < nobjects)
13721             maxobjects = nobjects;
13722 
13723         cpl_free(reject);
13724         cpl_free(bright);
13725         cpl_free(place);
13726 
13727     }
13728 
13729 /*    nobjects = row - nobjects;     A bug, I think... */
13730     row = cpl_table_get_nrow(slits);
13731 
13732     for (i = 0; i < row; i++) {
13733         for (j = 0; j < maxobjects; j++) {
13734             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13735             if (cpl_table_is_valid(slits, name, i))
13736                 cpl_table_set_int(slits, name, i, totobj -
13737                                   cpl_table_get_int(slits, name, i, NULL));
13738         }
13739     }
13740 
13741     for (i = 0; i < maxobjects; i++) {
13742         snprintf(name, MAX_COLNAME, "start_%d", i+1);
13743         cpl_table_fill_invalid_int(slits, name, -1);
13744         snprintf(name, MAX_COLNAME, "end_%d", i+1);
13745         cpl_table_fill_invalid_int(slits, name, -1);
13746         snprintf(name, MAX_COLNAME, "row_%d", i+1);
13747         cpl_table_fill_invalid_int(slits, name, -1);
13748     }
13749 
13750     return profile;
13751 }
13752 
13753 
13778 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13779                                 cpl_table *objects, int extraction, double ron,
13780                                 double gain, int ncombined)
13781 {
13782     const char *func = "mos_extract_objects";
13783 
13784     char        name[MAX_COLNAME];
13785 
13786     cpl_image **output;
13787     cpl_image  *extracted;
13788     cpl_image  *extr_sky;
13789     cpl_image  *error;
13790     cpl_image  *sciwin;
13791     cpl_image  *skywin;
13792     int         nslits;
13793     int         nobjects;
13794     int         maxobjects;
13795     int         nx, ny;
13796     int         ylow, yhig;
13797     int         i, j;
13798 
13799 
13800     if (science == NULL || sky == NULL) {
13801         cpl_msg_error(func, "Both scientific exposures are required in input");
13802         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13803         return NULL;
13804     }
13805 
13806     if (objects == NULL) {
13807         cpl_msg_error(func, "An object table is required in input");
13808         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13809         return NULL;
13810     }
13811 
13812     if (extraction < 0 || extraction > 1) {
13813         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13814                       "either 0 or 1", extraction); 
13815         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13816         return NULL;
13817     }
13818 
13819     if (ron < 0.0) {
13820         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13821         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13822         return NULL;
13823     }
13824 
13825     if (gain < 0.1) {
13826         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13827         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13828         return NULL;
13829     }
13830 
13831     if (ncombined < 1) {
13832         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13833                       "it should be at least 1", ncombined);
13834         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13835         return NULL;
13836     }
13837 
13838 
13839     /*
13840      * Count the max number of objects per slit. Note that maxobjects 
13841      * is intentionally the max number of objects increased by one.
13842      */
13843 
13844     maxobjects = 1;
13845     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13846     while (cpl_table_has_column(objects, name)) {
13847         maxobjects++;
13848         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13849     }
13850 
13851 
13852     /*
13853      * Count objects to extract
13854      */
13855 
13856     nobjects = 0;
13857     nslits = cpl_table_get_nrow(objects);
13858 
13859     for (i = 0; i < nslits; i++) {
13860         for (j = 1; j < maxobjects; j++) {
13861             snprintf(name, MAX_COLNAME, "object_%d", j);
13862             if (cpl_table_is_valid(objects, name, i))
13863                 nobjects++;
13864         }
13865     }
13866 
13867     if (nobjects == 0)
13868         return NULL;
13869 
13870     nx = cpl_image_get_size_x(science);
13871     ny = cpl_image_get_size_x(science);
13872 
13873     output = cpl_calloc(3, sizeof(cpl_image *));
13874     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13875     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13876     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13877 
13878 
13879     /*
13880      * Extract objects
13881      */
13882 
13883     nobjects = 0;
13884     for (i = 0; i < nslits; i++) {
13885         for (j = 1; j < maxobjects; j++) {
13886             snprintf(name, MAX_COLNAME, "object_%d", j);
13887             if (cpl_table_is_valid(objects, name, i)) {
13888                 snprintf(name, MAX_COLNAME, "start_%d", j);
13889                 ylow = cpl_table_get_int(objects, name, i, NULL);
13890                 snprintf(name, MAX_COLNAME, "end_%d", j);
13891                 yhig = cpl_table_get_int(objects, name, i, NULL);
13892                 snprintf(name, MAX_COLNAME, "row_%d", j);
13893                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13894                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13895                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13896 /*
13897  * Cleaning the cosmics locally was really NOT a good idea...
13898  * I leave it here, commented out, to never forget this mistake!
13899 
13900                 if (extraction) {
13901                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13902                 }
13903  */
13904                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13905                                nobjects, extraction, ron, gain, ncombined);
13906 
13907                 /*
13908                  * Hidden check whether the spectrum was saturated or not
13909                  */
13910 
13911                 {
13912                     cpl_image *total = cpl_image_add_create(sciwin, skywin);
13913                     float     *data  = cpl_image_get_data_float(total);
13914                     int        size  = cpl_image_get_size_x(total)
13915                                      * cpl_image_get_size_y(total);
13916                     int        k;
13917                     char      *saturation_level = getenv("SATURATION_LEVEL");
13918                     float      saturation = 62000.0;
13919                     char      *max_saturated = getenv("MAX_SATURATED");
13920                     int        max_satur = 10;
13921                     int        saturated;
13922 
13923                     if (saturation_level)
13924                         saturation = atof(saturation_level);
13925 
13926                     if (max_saturated)
13927                         max_satur = atoi(max_saturated);
13928 
13929                     saturated = 0;
13930                     for (k = 0; k < size; k++) {
13931                         if (data[k] > saturation) {
13932                             saturated++;
13933                             if (saturated > max_satur) {
13934                                 break;
13935                             }
13936                         }
13937                     }
13938 
13939                     if (saturated > max_satur)
13940                         saturated = 1;
13941                     else
13942                         saturated = 0;
13943 
13944                     data = cpl_image_get_data(extracted);
13945                     data[nobjects * nx] = saturated;
13946                 }
13947 
13948                 cpl_image_delete(sciwin);
13949                 cpl_image_delete(skywin);
13950                 nobjects++;
13951             }
13952         }
13953     }
13954 
13955     return output;
13956 
13957 }
13958 
13959 
13982 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13983                             double dispersion, int saturation, 
13984                             double *mfwhm, double *rmsfwhm,
13985                             double *resolution, double *rmsres, int *nlines)
13986 {
13987     cpl_vector *vector;
13988 
13989     int     i, j, n, m;
13990     int     position, maxpos;
13991     int     xlen, ylen;
13992     int     sp, ep;
13993     int     radius;
13994     int     sradius = 40;
13995     int     threshold = 250;    /* Peak must be so many ADUs above min */
13996 
13997     int     ifwhm;
13998     double  fwhm;
13999     double *buffer;
14000     double  min, max, halfmax;
14001     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
14002     double  value, rms;
14003 
14004     float  *data;
14005 
14006 
14007     *resolution = 0.0;
14008     *rmsres = 0.0;
14009     *nlines = 0;
14010 
14011     xlen = cpl_image_get_size_x(image);
14012     ylen = cpl_image_get_size_y(image);
14013     data = cpl_image_get_data(image);
14014 
14015     buffer = cpl_malloc(ylen * sizeof(double));
14016 
14017     /*
14018      *  Closest pixel to specified wavelength.
14019      */
14020 
14021     position = floor((lambda - startwave) / dispersion + 0.5);
14022 
14023     sp = position - sradius;
14024     ep = position + sradius;
14025 
14026     if (sp < 0 || ep > xlen) {
14027         cpl_free(buffer);
14028         return 0;
14029     }
14030 
14031     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
14032 
14033         /*
14034          *  Search interval for peak. Abort if too close to image border.
14035          */
14036 
14037         radius = mos_lines_width(data + i*xlen + position - sradius, 
14038                                  2*sradius + 1);
14039         if (radius < 5)
14040             radius = 5;
14041 
14042         sp = position - radius;
14043         ep = position + radius;
14044 
14045         if (sp < 0 || ep > xlen) {
14046             cpl_free(buffer);
14047             return 0;
14048         }
14049 
14050 
14051         /*
14052          *  Determine min-max value and position.
14053          */
14054 
14055         maxpos = sp;
14056         min = max = data[sp + i * xlen];
14057         for (j = sp; j < ep; j++) {
14058             if (data[j + i * xlen] > max) {
14059                 max = data[j + i * xlen];
14060                 maxpos = j;
14061             }
14062             if (data[j + i * xlen] < min) {
14063                 min = data[j + i * xlen];
14064             }
14065         }
14066 
14067         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
14068             continue;
14069 
14070         if (max - min < threshold)        /* Low signal... */
14071             continue;
14072 
14073         if (max > saturation)             /* Saturation */
14074             continue;
14075 
14076         /*
14077          *  Determine FWHM counting pixels with value greater than
14078          *  half of the max value, to the right and to the left of
14079          *  the max. Linear interpolation between the pixels where
14080          *  the transition happens.
14081          */
14082 
14083         halfmax = (max + min)/ 2.0;
14084 
14085         fwhm = 0.0;
14086         ifwhm = 0;
14087         for (j = maxpos; j < maxpos + radius; j++) {
14088             if (j < xlen) {
14089                 if (data[j + i * xlen] < halfmax) {
14090                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14091                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14092                     break;
14093                 }
14094                 ifwhm++;
14095             }
14096         }
14097 
14098         ifwhm = 0;
14099         for (j = maxpos; j > maxpos - radius; j--) {
14100             if (j >= 0) {
14101                 if (data[j + i * xlen] < halfmax) {
14102                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14103                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14104                     break;
14105                 }
14106                 ifwhm++;
14107             }
14108         }
14109 
14110         if (fwhm > 3.0) {
14111             buffer[n] = fwhm - 2.0;
14112             n++;
14113         }
14114 
14115     }
14116 
14117     if (n == 0) {
14118         cpl_free(buffer);
14119         return 0;
14120     }
14121 
14122     vector = cpl_vector_wrap(n, buffer);
14123     value = cpl_vector_get_median_const(vector);
14124     cpl_vector_unwrap(vector);
14125 
14126     rms = 0.0;
14127     for (i = 0, m = 0; i < n; i++) {
14128         if (fabs(buffer[i] - value) < cut) {
14129             rms += fabs(buffer[i] - value);
14130             m++;
14131         }
14132     }
14133 
14134     cpl_free(buffer);
14135 
14136     if (m < 3)
14137         return 0;
14138 
14139     rms /= m;
14140     rms *= 1.25;       /* Factor to convert average deviation to sigma */
14141 
14142     value *= dispersion;
14143     rms *= dispersion;
14144 
14145     *mfwhm = value;
14146     *rmsfwhm = rms;
14147 
14148     *resolution = lambda / value;
14149     *rmsres = *resolution * rms / value;
14150 
14151     *nlines = m;
14152 
14153     return 1;
14154 }
14155 
14156 
14178 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
14179                                 double dispersion, int saturation, 
14180                                 cpl_vector *lines)
14181 {
14182 
14183     cpl_table *table;
14184     double    *line;
14185     double     fwhm;
14186     double     rmsfwhm;
14187     double     resolution;
14188     double     rmsres;
14189     int        nref;
14190     int        nlines;
14191     int        i;
14192 
14193 
14194     nref = cpl_vector_get_size(lines);
14195     line = cpl_vector_get_data(lines);
14196 
14197     table = cpl_table_new(nref);
14198     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14199     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14200     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14201     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14202     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14203     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14204     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14205     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14206     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14207 
14208     for (i = 0; i < nref; i++) {
14209         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
14210                                     saturation, &fwhm, &rmsfwhm, 
14211                                     &resolution, &rmsres, &nlines)) {
14212             cpl_table_set_double(table, "wavelength", i, line[i]);
14213             cpl_table_set_double(table, "fwhm", i, fwhm);
14214             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14215             cpl_table_set_double(table, "resolution", i, resolution);
14216             cpl_table_set_double(table, "resolution_rms", i, rmsres);
14217             cpl_table_set_int(table, "nlines", i, nlines);
14218         }
14219         else
14220             cpl_table_set_int(table, "nlines", i, 0);
14221     }
14222 
14223     if (cpl_table_has_valid(table, "wavelength"))
14224         return table;
14225 
14226     cpl_table_delete(table);
14227 
14228     return NULL;
14229     
14230 }
14231 
14232 
14250 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14251                             int ystart, int yend, double wstart, double wend)
14252 {
14253     const char *func = "mos_integrate_signal";
14254 
14255     double sum;
14256     float *sdata;
14257     float *wdata;
14258     int    nx, ny;
14259     int    x, y;
14260     
14261 
14262     if (image == NULL || wavemap == NULL) { 
14263         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14264         return 0.0;
14265     }
14266 
14267     if (ystart > yend || wstart >= wend) {
14268         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14269         return 0.0;
14270     }
14271 
14272     nx = cpl_image_get_size_x(image);
14273     ny = cpl_image_get_size_y(image);
14274 
14275     if (!(nx == cpl_image_get_size_x(wavemap) 
14276         && ny == cpl_image_get_size_y(wavemap))) {
14277         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14278         return 0.0;
14279     }
14280 
14281     if (ystart < 0 || yend > ny) {
14282         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14283         return 0.0;
14284     }
14285 
14286     sdata = cpl_image_get_data(image);
14287     wdata = cpl_image_get_data(wavemap);
14288 
14289     sdata += ystart*nx;
14290     wdata += ystart*nx;
14291 
14292     sum = 0.0;
14293     for (y = ystart; y < yend; y++) {
14294         for (x = 0; x < nx; x++) {
14295             if (wdata[x] < wstart || wdata[x] > wend)
14296                 continue;
14297             sum += sdata[x];
14298         }
14299         sdata += nx;
14300         wdata += nx;
14301     }
14302 
14303     return sum;
14304 
14305 }
14306 
14307 /****************************************************************************
14308  * From this point on, the instrument dependent functions are added:
14309  * they are functions that retrieve information that is stored in
14310  * the data headers in some instrument specific way, such as the
14311  * location of overscans, the slits positions on the telescope
14312  * focal plane, the gain factor, etc.
14313  */
14314 
14315 
14338 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14339 {
14340     const char *func = "mos_load_slits_fors_mxu";
14341 
14342     cpl_table  *slits;
14343     char        keyname[MAX_COLNAME];
14344     const char *instrume;
14345     const char *target_name;
14346     float       slit_x;
14347     float       slit_y;
14348     float       length;
14349 /*    double      arc2mm = 0.53316;         */
14350     double      arc2mm = 0.528;
14351     int         nslits;
14352     int         slit_id;
14353     int         fors;
14354     int         chip;
14355     int         found;
14356 
14357     /*
14358      * The limits below are used to exclude from the loaded slit list
14359      * any slit that surely doesn't belong to the used chip. This is
14360      * a way to reduce the chance of ambiguous slit identification.
14361      */
14362 
14363     float      low_limit1 = 10.0;
14364     float      hig_limit2 = 30.0;
14365 
14366 
14367     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14368         return NULL;
14369     }
14370 
14371     if (header == NULL) {
14372         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14373         return NULL;
14374     }
14375 
14376 
14377     /*
14378      * See if this is FORS1 or FORS2;
14379      */
14380 
14381     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14382 
14383     fors = 0;
14384     if (instrume[4] == '1')
14385         fors = 1;
14386     if (instrume[4] == '2')
14387         fors = 2;
14388 
14389     if (fors != 2) {
14390         cpl_msg_error(func, "Wrong instrument: %s\n"
14391                       "FORS2 is expected for MXU data", instrume);
14392         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14393         return NULL;
14394     }
14395 
14396 
14397     /*
14398      * The master and slave chips can be identified by their positions
14399      * in the chip array in the case of FORS2 data (with fors1 the chip
14400      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14401      */
14402 
14403     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14404 
14405     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14406         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14407                       "in FITS header");
14408         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14409         return NULL;
14410     }
14411 
14412     if (chip != 1 && chip != 2) {
14413         cpl_msg_error(func, "Unexpected chip position in keyword "
14414                       "ESO DET CHIP1 Y: %d", chip);
14415         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14416         return NULL;
14417     }
14418 
14419 
14420     /*
14421      * Count slits in header (excluding reference slits, and the slits
14422      * that _surely_ belong to the other chip)
14423      */
14424 
14425     nslits = 0;
14426     slit_id = 0;
14427     found = 1;
14428 
14429     while (found) {
14430         slit_id++;
14431         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14432         if (cpl_propertylist_has(header, keyname)) {
14433             slit_y = cpl_propertylist_get_double(header, keyname);
14434 
14435             if (chip == 1)
14436                 if (slit_y < low_limit1)
14437                     continue;
14438             if (chip == 2)
14439                 if (slit_y > hig_limit2)
14440                     continue;
14441                 
14442             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14443                      slit_id + 100);
14444             if (cpl_propertylist_has(header, keyname)) {
14445                 target_name = cpl_propertylist_get_string(header, keyname);
14446                 if (strncmp(target_name, "refslit", 7))
14447                     nslits++;
14448             }
14449             else
14450                 nslits++;
14451         }
14452         else
14453             found = 0;
14454     }
14455 
14456     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14457         cpl_msg_error(func, "%s while loading slits coordinates from "
14458                       "FITS header", cpl_error_get_message());
14459         cpl_error_set_where(func);
14460         return NULL;
14461     }
14462 
14463     if (nslits == 0)  {
14464         cpl_msg_error(func, "No slits coordinates found in header");
14465         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14466         return NULL;
14467     }
14468 
14469     slits = cpl_table_new(nslits);
14470     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14471     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14472     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14473     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14474     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14475     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14476     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14477     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14478     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14479 
14480     nslits = 0;
14481     slit_id = 0; 
14482     found = 1;
14483     while (found) {
14484         slit_id++;
14485         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14486         if (cpl_propertylist_has(header, keyname)) {
14487             slit_y = cpl_propertylist_get_double(header, keyname);
14488 
14489             if (chip == 1) 
14490                 if (slit_y < low_limit1)
14491                     continue;
14492             if (chip == 2)
14493                 if (slit_y > hig_limit2)
14494                     continue;
14495 
14496             /*
14497              * Y-flip the slit position, to match CCD pixel coordinate
14498              * convention
14499              */
14500 
14501             slit_y = -slit_y;
14502 
14503             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
14504             slit_x = cpl_propertylist_get_double(header, keyname);
14505             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14506                 cpl_table_delete(slits);
14507                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14508                               keyname);
14509                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14510                 return NULL;
14511             }
14512 
14513             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
14514             length = cpl_propertylist_get_double(header, keyname);
14515             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14516                 cpl_table_delete(slits);
14517                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14518                               keyname);
14519                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14520                 return NULL;
14521             }
14522 
14523             length *= arc2mm;
14524 
14525             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14526                      slit_id + 100);
14527             if (cpl_propertylist_has(header, keyname)) {
14528                 target_name = cpl_propertylist_get_string(header, keyname);
14529                 if (strncmp(target_name, "refslit", 7)) {
14530                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14531                     cpl_table_set(slits, "xtop", nslits, slit_x);
14532                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14533                     cpl_table_set(slits, "xbottom", nslits, slit_x);
14534                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14535                     nslits++;
14536                 }
14537             }
14538             else {
14539                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14540                 cpl_table_set(slits, "xtop", nslits, slit_x);
14541                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14542                 cpl_table_set(slits, "xbottom", nslits, slit_x);
14543                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14544                 nslits++;
14545             }
14546         }
14547         else
14548             found = 0;
14549     }
14550 
14551     return slits;
14552 }
14553 
14554 
14578 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header, 
14579                                    int * nslits_out_det)
14580 {
14581     const char *func = "mos_load_slits_fors_mos";
14582 
14583     cpl_table  *slits;
14584     char        keyname[MAX_COLNAME];
14585     const char *instrume;
14586     const char *chipname;
14587     float       slit_x;
14588     int         first_slit, last_slit;
14589     cpl_size    nslits;
14590     int         slit_id;
14591     int         fors;
14592     int         chip;
14593     int         fors_is_old;
14594 
14595     /*
14596      * The Y coordinates of the slits are fixed
14597      */
14598 
14599     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
14600                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
14601                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
14602                                -102.1 };
14603     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
14604                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
14605                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
14606                                -113.9 };
14607 
14608 
14609     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14610         return NULL;
14611     }
14612 
14613     if (header == NULL) {
14614         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14615         return NULL;
14616     }
14617 
14618 
14619     /*
14620      * See if this is FORS1 or FORS2;
14621      */
14622 
14623     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14624 
14625     fors = 0;
14626     if (instrume[4] == '1')
14627         fors = 1;
14628     if (instrume[4] == '2')
14629         fors = 2;
14630 
14631     if (fors == 0) {
14632         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14633                       instrume);
14634         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14635         return NULL;
14636     }
14637 
14638     /* FIXME:
14639      * This is the way FORS1 data belong to the upgraded chips,
14640      * named "Marlene" and "Norma III". It's a quick solution,
14641      * there are hardcoded values here!!!
14642      */
14643 
14644     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
14645 
14646     if (chipname[0] == 'M' || chipname[0] == 'N')
14647         fors_is_old = 0;
14648     else
14649         fors_is_old = 1;
14650 
14651     if (fors == 1 && fors_is_old) {
14652         first_slit = 1;
14653         last_slit = 19;
14654     }
14655     else {
14656 
14657         /*
14658          * The master and slave chips can be identified by their positions
14659          * in the chip array in the case of FORS2 data: chip = 2 is the 
14660          * master, chip = 1 is the slave.
14661          */
14662 
14663         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14664 
14665         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14666             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14667                           "in FITS header");
14668             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14669             return NULL;
14670         }
14671 
14672         if (chip != 1 && chip != 2) {
14673             cpl_msg_error(func, "Unexpected chip position in keyword "
14674                           "ESO DET CHIP1 Y: %d", chip);
14675             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14676             return NULL;
14677         }
14678 
14679         if (chip == 1) {
14680             first_slit = 12;
14681             last_slit = 19;
14682         }
14683         else {
14684             first_slit = 1;
14685             last_slit = 11;
14686         }
14687     }
14688 
14689 
14690     /*
14691      * Count slits in header (excluding closed slits - i.e. those with
14692      * offsets greater than 115 mm - and the slits that do not belong 
14693      * to this chip)
14694      */
14695 
14696     nslits = 0;
14697     slit_id = 0;
14698     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14699         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14700         if (cpl_propertylist_has(header, keyname)) {
14701             slit_x = cpl_propertylist_get_double(header, keyname);
14702             if (fabs(slit_x) < 115.0)
14703                 nslits++;
14704             else
14705                 (*nslits_out_det)++;        
14706         }
14707         else {
14708             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
14709             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14710             return NULL;
14711         }
14712     }
14713 
14714     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14715         cpl_msg_error(func, "%s while loading slits coordinates from "
14716                       "FITS header", cpl_error_get_message());
14717         cpl_error_set_where(func);
14718         return NULL;
14719     }
14720 
14721     if (nslits == 0)  {
14722         cpl_msg_error(func, "No slits coordinates found in header");
14723         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14724         return NULL;
14725     }
14726 
14727     slits = cpl_table_new(nslits);
14728     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14729     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14730     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14731     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14732     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14733     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14734     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14735     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14736     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14737 
14738     nslits = 0;
14739     slit_id = 0;
14740     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14741         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14742         slit_x = cpl_propertylist_get_double(header, keyname);
14743         if (fabs(slit_x) < 115.0) {
14744             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14745             cpl_table_set(slits, "xtop", nslits, slit_x);
14746             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
14747             cpl_table_set(slits, "xbottom", nslits, slit_x);
14748             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
14749             nslits++;
14750         }
14751     }
14752 
14753     return slits;
14754 }
14755 
14756 
14780 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
14781 {
14782     const char *func = "mos_load_slits_fors_lss";
14783 
14784     cpl_table  *slits;
14785     char       *slit_name;
14786     const char *instrume;
14787     int         fors;
14788     int         chip;
14789     float       ytop;
14790     float       ybottom;
14791 
14792     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14793         return NULL;
14794     }
14795 
14796     if (header == NULL) {
14797         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14798         return NULL;
14799     }
14800 
14801 
14802     /*
14803      * See if this is FORS1 or FORS2;
14804      */
14805 
14806     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14807 
14808     fors = 0;
14809     if (instrume[4] == '1')
14810         fors = 1;
14811     if (instrume[4] == '2')
14812         fors = 2;
14813 
14814     if (fors == 0) {
14815         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14816                       instrume);
14817         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14818         return NULL;
14819     }
14820 
14821     if (fors == 1) {
14822         ytop = 109.94;
14823         ybottom = -109.94;
14824     }
14825     else {
14826 
14827         /*
14828          * The master and slave chips can be identified by their positions
14829          * in the chip array in the case of FORS2 data: chip = 2 is the 
14830          * master, chip = 1 is the slave.
14831          */
14832 
14833         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14834 
14835         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14836             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14837                           "in FITS header");
14838             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14839             return NULL;
14840         }
14841 
14842         if (chip != 1 && chip != 2) {
14843             cpl_msg_error(func, "Unexpected chip position in keyword "
14844                           "ESO DET CHIP1 Y: %d", chip);
14845             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14846             return NULL;
14847         }
14848 
14849         if (chip == 1) {
14850             ytop = 30.0;
14851             ybottom = -109.94;
14852         }
14853         else {
14854             ytop = 109.94;
14855             ybottom = -20.0;
14856         }
14857     }
14858 
14859 
14860     slits = cpl_table_new(1);
14861     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14862     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14863     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14864     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14865     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14866     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14867     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14868     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14869     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14870 
14871     slit_name = (char *)cpl_propertylist_get_string(header, 
14872                                                     "ESO INS SLIT NAME");
14873 
14874     cpl_table_set(slits, "ytop", 0, ytop);
14875     cpl_table_set(slits, "ybottom", 0, ybottom);
14876 
14877     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14878         cpl_table_set_int(slits, "slit_id", 0, 1);
14879         cpl_table_set(slits, "xbottom", 0, -0.075);
14880         cpl_table_set(slits, "xtop", 0, 0.075);
14881     }
14882     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14883         cpl_table_set_int(slits, "slit_id", 0, 2);
14884         cpl_table_set(slits, "xbottom", 0, 5.895);
14885         cpl_table_set(slits, "xtop", 0, 6.105);
14886     }
14887     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14888         cpl_table_set_int(slits, "slit_id", 0, 3);
14889         cpl_table_set(slits, "xbottom", 0, -6.135);
14890         cpl_table_set(slits, "xtop", 0, -5.865);
14891     }
14892     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14893         cpl_table_set_int(slits, "slit_id", 0, 4);
14894         cpl_table_set(slits, "xbottom", 0, 11.815);
14895         cpl_table_set(slits, "xtop", 0, 12.185);
14896     }
14897     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14898         cpl_table_set_int(slits, "slit_id", 0, 5);
14899         cpl_table_set(slits, "xbottom", 0, -12.265);
14900         cpl_table_set(slits, "xtop", 0, -11.735);
14901     }
14902     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14903         cpl_table_set_int(slits, "slit_id", 0, 6);
14904         cpl_table_set(slits, "xbottom", 0, 17.655);
14905         cpl_table_set(slits, "xtop", 0, 18.345);
14906     }
14907     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14908         cpl_table_set_int(slits, "slit_id", 0, 7);
14909         cpl_table_set(slits, "xbottom", 0, -18.425);
14910         cpl_table_set(slits, "xtop", 0, -17.575);
14911     }
14912     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14913         cpl_table_set_int(slits, "slit_id", 0, 8);
14914         cpl_table_set(slits, "xbottom", 0, 23.475);
14915         cpl_table_set(slits, "xtop", 0, 24.525);
14916     }
14917     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14918         cpl_table_set_int(slits, "slit_id", 0, 9);
14919         cpl_table_set(slits, "xbottom", 0, -24.66);
14920         cpl_table_set(slits, "xtop", 0, -23.34);
14921     }
14922     else {
14923         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14924                       slit_name);
14925         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14926         cpl_table_delete(slits);
14927         return NULL;
14928     }
14929 
14930     return slits;
14931 }
14932 
14933 
14948 double mos_get_gain_vimos(cpl_propertylist *header)
14949 {
14950     const char *func = "mos_get_gain_vimos";
14951 
14952     double gain = -1.0;
14953 
14954 
14955     if (cpl_error_get_code() != CPL_ERROR_NONE)
14956         return gain;
14957 
14958     if (header == NULL) {
14959         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14960         return gain;
14961     }
14962 
14963     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14964     if (cpl_error_get_code()) {
14965         cpl_error_set_where(func);
14966         gain = -1.0;
14967     }
14968 
14969     return gain;
14970 
14971 }
14972 
14973 
14993 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14994 {
14995     const char *func = "mos_load_slits_vimos";
14996 
14997     cpl_table *slits;
14998     char       keyname[MAX_COLNAME];
14999     float      slit_x;
15000     float      slit_y;
15001     float      dim_x;
15002     float      dim_y;
15003     int        nslits;
15004     int        slit_id;
15005     int        curved;
15006     int        i;
15007 
15008 
15009     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15010         return NULL;
15011     }
15012 
15013     if (header == NULL) {
15014         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15015         return NULL;
15016     }
15017 
15018     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
15019 
15020     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15021         cpl_error_set_where(func);
15022         return NULL;
15023     }
15024 
15025     slits = cpl_table_new(nslits);
15026     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15027     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15028     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15029     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15030     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15031     cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15032     cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15033     cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15034     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15035     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15036     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15037     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15038     cpl_table_set_column_unit(slits, "xwidth", "mm");
15039     cpl_table_set_column_unit(slits, "ywidth", "mm");
15040 
15041     for (i = 0; i < nslits; i++) {
15042         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15043         slit_id = cpl_propertylist_get_int(header, keyname);
15044         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15045             cpl_error_set_where(func);
15046             return NULL;
15047         }
15048         sprintf(keyname, "ESO INS SLIT%d X", i+1);
15049         slit_x = cpl_propertylist_get_double(header, keyname);
15050         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15051             cpl_error_set_where(func);
15052             return NULL;
15053         }
15054         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15055         slit_y = cpl_propertylist_get_double(header, keyname);
15056         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15057             cpl_error_set_where(func);
15058             return NULL;
15059         }
15060         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15061         dim_x = cpl_propertylist_get_double(header, keyname);
15062         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15063             cpl_error_set_where(func);
15064             return NULL;
15065         }
15066 
15067         sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15068         if (cpl_propertylist_has(header, keyname)) {
15069             curved = 1;
15070         }
15071         else {
15072             sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15073             curved = 0;
15074         }
15075         dim_y = cpl_propertylist_get_double(header, keyname);
15076         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15077             cpl_error_set_where(func);
15078             return NULL;
15079         }
15080 
15081         cpl_table_set_int(slits, "slit_id", i, slit_id);
15082         cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15083         cpl_table_set(slits, "ytop", i, slit_y);
15084         cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15085         cpl_table_set(slits, "ybottom", i, slit_y);
15086         cpl_table_set(slits, "xwidth", i, dim_x);
15087         cpl_table_set(slits, "ywidth", i, dim_y);
15088         cpl_table_set_int(slits, "curved", i, curved);
15089     }
15090 
15091     return slits;
15092 }
15093 
15094 
15104 int mos_check_multiplex(cpl_table *slits)
15105 {
15106     cpl_propertylist *sort;
15107     int               nrow;
15108     int               i, multiplex, xprev, xcur;
15109     double            prev, cur;
15110     double            tolerance = 1.0; // About spatially aligned slits (mm)
15111 
15112 
15113     /*
15114      * Create an auxiliary column containing a sort of integer
15115      * x coordinate of the slit, to guarantee that slits at the
15116      * same spatial offset are recognised immediately as in spectral 
15117      * multiplexing.
15118      */
15119 
15120     sort = cpl_propertylist_new();
15121     cpl_propertylist_append_bool(sort, "xtop", 0);
15122     cpl_table_sort(slits, sort);
15123     cpl_propertylist_delete(sort);
15124 
15125     prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15126     cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15127     cpl_table_set_int(slits, "xind", 0, prev);   // cast to int is intentional
15128     nrow = cpl_table_get_nrow(slits);
15129     for (i = 1; i < nrow; i++) {
15130         cur = cpl_table_get_double(slits, "xtop", i, NULL);
15131         if (fabs(prev - cur) > tolerance)
15132             prev = cur;
15133         cpl_table_set_int(slits, "xind", i, prev);
15134     }
15135 
15136     /*
15137      * Now sort according to increasing (integer) x positions, and when
15138      * those are equal (multiplexed) according to the increasing y position.
15139      */
15140 
15141     sort = cpl_propertylist_new();
15142     cpl_propertylist_append_bool(sort, "xind", 0);
15143     cpl_propertylist_append_bool(sort, "ytop", 0);
15144     cpl_table_sort(slits, sort);
15145     cpl_propertylist_delete(sort);
15146 
15147     /*
15148      * Now assign to each slit its multiplex order.
15149      */
15150 
15151     multiplex = 0;
15152     cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15153     xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15154     cpl_table_set_int(slits, "multiplex", 0, multiplex);
15155     nrow = cpl_table_get_nrow(slits);
15156     for (i = 1; i < nrow; i++) {
15157         xcur = cpl_table_get_int(slits, "xind", i, NULL);
15158         if (xcur == xprev) {
15159             multiplex++;
15160         }
15161         else {
15162             xprev = xcur;
15163             multiplex = 0;
15164         }
15165         cpl_table_set_int(slits, "multiplex", i, multiplex);
15166     }
15167 
15168     cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15169 
15170     cpl_table_erase_column(slits, "xind");
15171 
15172     return 1 + cpl_table_get_column_max(slits, "multiplex");
15173 
15174 }
15175 
15176 
15203 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
15204                                     int check_consistency)
15205 {
15206     const char *func = "mos_load_overscans_vimos";
15207 
15208     int        nx = 0;
15209     int        ny = 0;
15210     int        px = 0;
15211     int        py = 0;
15212     int        ox = 0;
15213     int        oy = 0;
15214     int        vx = 0;
15215     int        vy = 0;
15216     int        nrows;
15217     cpl_table *overscans;
15218 
15219 
15220     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15221         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15222         return NULL;
15223     }
15224 
15225     if (header == NULL) {
15226         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15227         return NULL;
15228     }
15229 
15230     if (cpl_propertylist_has(header, "NAXIS1"))
15231         nx = cpl_propertylist_get_int(header, "NAXIS1");
15232     if (cpl_propertylist_has(header, "NAXIS2"))
15233         ny = cpl_propertylist_get_int(header, "NAXIS2");
15234     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15235         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15236     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15237         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15238     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15239         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15240     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15241         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15242     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15243         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15244     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15245         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15246 
15247     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15248         cpl_msg_error(func, "Missing overscan keywords in header");
15249         cpl_error_set_where(func);
15250         return NULL;
15251     }
15252 
15253     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15254         cpl_msg_error(func, "Missing overscan keywords in header");
15255         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15256         return NULL;
15257     }
15258 
15259     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15260         if (check_consistency) {
15261             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15262             return NULL;
15263         }
15264         else {
15265             cpl_msg_debug(func, "Overscans description conflicts with "
15266                           "reported image sizes, "
15267                           "%d + %d + %d != %d or "
15268                           "%d + %d + %d != %d",
15269                           px, vx, ox, nx,
15270                           py, vy, oy, ny);
15271         }
15272     }
15273 
15274     nrows = 0;
15275     if (px > 0)
15276         nrows++;
15277     if (ox > 0)
15278         nrows++;
15279     if (py > 0)
15280         nrows++;
15281     if (oy > 0)
15282         nrows++;
15283 
15284     if (nrows > 2) {
15285         cpl_msg_error(func, "Unexpected overscan regions "
15286                       "(both in X and Y direction)");
15287         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15288         return NULL;
15289     }
15290 
15291 
15292     /*
15293      * A row is added for the description of the valid region of the
15294      * exposure the input header belongs to.
15295      */
15296 
15297     nrows++;
15298 
15299     overscans = cpl_table_new(nrows);
15300     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15301     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15302     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15303     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15304 
15305     nrows = 0;
15306 
15307     cpl_table_set_int(overscans, "xlow", nrows, px);
15308     cpl_table_set_int(overscans, "ylow", nrows, py);
15309     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15310     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15311     nrows++;
15312 
15313     if (px > 0) {
15314         cpl_table_set_int(overscans, "xlow", nrows, 0);
15315         cpl_table_set_int(overscans, "ylow", nrows, 0);
15316         cpl_table_set_int(overscans, "xhig", nrows, px);
15317         cpl_table_set_int(overscans, "yhig", nrows, ny);
15318         nrows++;
15319     }
15320 
15321     if (ox > 0) {
15322         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15323         cpl_table_set_int(overscans, "ylow", nrows, 0);
15324         cpl_table_set_int(overscans, "xhig", nrows, nx);
15325         cpl_table_set_int(overscans, "yhig", nrows, ny);
15326         nrows++;
15327     }
15328 
15329     if (py > 0) {
15330         cpl_table_set_int(overscans, "xlow", nrows, 0);
15331         cpl_table_set_int(overscans, "ylow", nrows, 0);
15332         cpl_table_set_int(overscans, "xhig", nrows, nx);
15333         cpl_table_set_int(overscans, "yhig", nrows, py);
15334         nrows++;
15335     }
15336 
15337     if (oy > 0) {
15338         cpl_table_set_int(overscans, "xlow", nrows, 0);
15339         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15340         cpl_table_set_int(overscans, "xhig", nrows, nx);
15341         cpl_table_set_int(overscans, "yhig", nrows, ny);
15342         nrows++;
15343     }
15344 
15345     return overscans;
15346 
15347 }
15348 
15349 
15350 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15351 {
15352     const char *func = "mos_load_overscans_fors";
15353 
15354     int        nports;
15355     int        nx = 0;
15356     int        ny = 0;
15357     int        px = 0;
15358     int        py = 0;
15359     int        ox = 0;
15360     int        oy = 0;
15361     int        rebin;
15362     int        nrows;
15363     cpl_table *overscans;
15364 
15365 
15366     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15367         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15368         return NULL;
15369     }
15370 
15371     if (header == NULL) {
15372         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15373         return NULL;
15374     }
15375 
15376     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15377         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15378 
15379     if (nports == 4                                        && 
15380         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15381         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15382 
15383         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15384 
15385         overscans = cpl_table_new(3);
15386         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15387         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15388         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15389         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15390 
15391         px = 16 / rebin;
15392         ox = 16 / rebin;
15393         nx = 2080 / rebin;
15394         ny = 2048 / rebin;
15395         nrows = 0;
15396 
15397         cpl_table_set_int(overscans, "xlow", nrows, px);
15398         cpl_table_set_int(overscans, "ylow", nrows, py);
15399         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15400         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15401         nrows++;
15402 
15403         cpl_table_set_int(overscans, "xlow", nrows, 0);
15404         cpl_table_set_int(overscans, "ylow", nrows, 0);
15405         cpl_table_set_int(overscans, "xhig", nrows, px);
15406         cpl_table_set_int(overscans, "yhig", nrows, ny);
15407         nrows++;
15408 
15409         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15410         cpl_table_set_int(overscans, "ylow", nrows, 0);
15411         cpl_table_set_int(overscans, "xhig", nrows, nx);
15412         cpl_table_set_int(overscans, "yhig", nrows, ny);
15413         nrows++;
15414     }
15415     else {
15416         overscans = mos_load_overscans_vimos(header, 0);
15417     }
15418 
15419     return overscans;
15420 
15421 }
15422 
15454 #define READY 1
15455 #ifdef READY
15456 
15457 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
15458                                        int samples, int order)
15459 {
15460 
15461     const char *func = "mos_montecarlo_polyfit";
15462 
15463     cpl_polynomial *p;
15464     cpl_polynomial *q;
15465     cpl_vector     *listx;
15466     cpl_vector     *listy;
15467     double          err;
15468     double         *x;
15469     double         *px;
15470     double         *x_eval;
15471     double         *px_eval;
15472     double         *sigma;
15473     double         *vy;
15474     double         *dy;
15475     int             npoints, nevaluate;
15476     int             i, j;
15477 
15478 
15479     if (points == NULL || evaluate == NULL) {
15480         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15481         return NULL;
15482     }
15483 
15484     if (!cpl_table_has_column(points, "x")) {
15485         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15486         return NULL;
15487     }
15488 
15489     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
15490         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15491         return NULL;
15492     }
15493 
15494     if (cpl_table_has_invalid(points, "x")) {
15495         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15496         return NULL;
15497     }
15498 
15499     if (!cpl_table_has_column(points, "y")) {
15500         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15501         return NULL;
15502     }
15503 
15504     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
15505         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15506         return NULL;
15507     }
15508 
15509     if (cpl_table_has_invalid(points, "y")) {
15510         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15511         return NULL;
15512     }
15513 
15514     if (cpl_table_has_column(points, "y_err")) {
15515 
15516         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
15517             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15518             return NULL;
15519         }
15520     
15521         if (cpl_table_has_invalid(points, "y_err")) {
15522             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15523             return NULL;
15524         }
15525     }
15526 
15527     if (!cpl_table_has_column(evaluate, "x")) {
15528         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15529         return NULL;
15530     }
15531 
15532     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
15533         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15534         return NULL;
15535     }
15536 
15537     if (cpl_table_has_invalid(evaluate, "x")) {
15538         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15539         return NULL;
15540     }
15541 
15542     if (samples < 2 || order < 0) {
15543         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15544         return NULL;
15545     }
15546 
15547     npoints = cpl_table_get_nrow(points);
15548     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
15549     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
15550 
15551     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
15552 
15553     if (!cpl_table_has_column(points, "y_err")) {
15554         err = sqrt(err);
15555         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
15556         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
15557         cpl_msg_info(func, "Error column not found - set to %f\n", err);
15558     }
15559 
15560     /*
15561      * Create columns containing modeled values at each x
15562      */
15563 
15564     if (cpl_table_has_column(points, "px"))
15565         cpl_table_erase_column(points, "px");
15566     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
15567     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
15568     x = cpl_table_get_data_double(points, "x");
15569     px = cpl_table_get_data_double(points, "px");
15570     for (i = 0; i < npoints; i++)
15571         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
15572 
15573     nevaluate = cpl_table_get_nrow(evaluate);
15574 
15575     if (cpl_table_has_column(evaluate, "px"))
15576         cpl_table_erase_column(evaluate, "px");
15577     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
15578     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
15579     x_eval = cpl_table_get_data_double(evaluate, "x");
15580     px_eval = cpl_table_get_data_double(evaluate, "px");
15581     for (i = 0; i < nevaluate; i++)
15582         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
15583 
15584     /*
15585      * Initialise column with sigma
15586      */
15587 
15588     if (cpl_table_has_column(evaluate, "sigma"))
15589         cpl_table_erase_column(evaluate, "sigma");
15590     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
15591     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
15592     sigma = cpl_table_get_data_double(evaluate, "sigma");
15593 
15594     /*
15595      * Compute varied y cordinates to fit
15596      */
15597 
15598     if (cpl_table_has_column(points, "vy"))
15599         cpl_table_erase_column(points, "vy");
15600     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
15601     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
15602     vy = cpl_table_get_data_double(points, "vy");
15603     dy = cpl_table_get_data_double(points, "y_err");
15604     cpl_vector_unwrap(listy);
15605     listy = cpl_vector_wrap(npoints, vy);
15606 
15607     for (i = 0; i < samples; i++) {
15608         for (j = 0; j < npoints; j++)
15609             vy[j] = px[j] + dy[j] * mos_randg(1);
15610         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
15611         for (j = 0; j < nevaluate; j++)
15612             sigma[j] += fabs(px_eval[j] 
15613                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
15614         cpl_polynomial_delete(q);
15615     }
15616 
15617     /* 
15618      * Factor 1.25 to convert average deviation to sigma 
15619      */
15620 
15621     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
15622     cpl_table_divide_scalar(evaluate, "sigma", samples);
15623 
15624     cpl_vector_unwrap(listx);
15625     cpl_vector_unwrap(listy);
15626 
15627     return p;
15628 }
15629 
15630 #endif
15631 
15654 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 
15655                                    double gain, double bias)
15656 {
15657     float *data;
15658     int    npix, i;
15659 
15660 
15661     if (image == NULL)
15662         return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
15663 
15664     if (ron < 0.0 || gain <= FLT_EPSILON)
15665         return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
15666 
15667     data = cpl_image_get_data_float(image);
15668     npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
15669     ron *= ron;
15670 
15671     for (i = 0; i < npix; i++) {
15672         if (data[i] < bias) {
15673             data[i] += sqrt(ron) * mos_randg(1);
15674         }
15675         else {
15676             data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
15677         }
15678     }
15679 
15680     return CPL_ERROR_NONE;
15681 }
15682 
15683 
15698 cpl_error_code mos_refmask_find_gaps(cpl_mask  *refmask,
15699                                      cpl_image *master_flat,
15700                                      double     level)
15701 {
15702     int          nx     = cpl_mask_get_size_x(refmask);
15703     int          ny     = cpl_mask_get_size_y(refmask);
15704 
15705     int        * xpos   = cpl_calloc(sizeof(int), ny);
15706 
15707     cpl_image  * filtered = cpl_image_duplicate(master_flat);
15708     cpl_mask   * kernel = cpl_mask_new(9, 9);
15709     cpl_vector * v      = cpl_vector_new(ny);
15710     cpl_vector * truev;
15711     int          nvalid = 0;
15712     double     * flats  = cpl_vector_get_data(v);
15713 
15714     double       median, stdev, delta;
15715 
15716     int          i, kill;
15717 
15718 
15719     cpl_mask_not(kernel);
15720     cpl_image_filter_mask(filtered, master_flat, kernel, 
15721                           CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
15722     cpl_mask_delete(kernel);
15723 
15724     for (i = 1; i <= ny; i++) {
15725         int j = 0;
15726 
15727         do j++;
15728         while (!cpl_mask_get(refmask, j, i) && j < nx);
15729 
15730         if (j < nx) {
15731             int rejected;
15732 
15733             xpos[i - 1] = j;
15734             flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
15735             nvalid++;
15736         }
15737         else {
15738             xpos[i - 1] = -1;
15739         }
15740     }
15741 
15742     if (nvalid == 0)
15743         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15744 
15745     truev = cpl_vector_wrap(nvalid, flats);
15746 
15747     median = cpl_vector_get_median(truev);
15748 
15749     if (level < 0.0)
15750        stdev = cpl_vector_get_stdev(truev);
15751 
15752     cpl_vector_unwrap(truev);
15753     cpl_vector_delete(v);
15754 
15755     for (i = 1; i <= ny; i++) {
15756         if (xpos[i - 1] > 0) {
15757             int    rejected;
15758             double kappa = 1.5;
15759 
15760             delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
15761 
15762             if (level < 0.0)
15763                 kill = fabs(delta) > stdev * kappa;
15764             else
15765                 kill = delta < level;
15766 
15767             if (kill) {
15768                 int j = 0;
15769             
15770                 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
15771                     cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
15772                     j++;
15773                 }
15774             }
15775         }
15776     }
15777 
15778     cpl_image_delete(filtered);
15779     cpl_free(xpos);
15780 
15781     return cpl_error_get_code();
15782 }
15783 
15791 cpl_error_code mos_saturation_process(cpl_image * image)
15792 {
15793     int     nx    = cpl_image_get_size_x(image);
15794     int     ny    = cpl_image_get_size_y(image);
15795     int     npix  = nx * ny;
15796     float * sdata = cpl_image_get_data_float(image);
15797 
15798     int count, i, j, k;
15799 
15800     /*
15801      * This is used to avoid saturation level coded with pixel value zero
15802      * To make it more robust against random 0.0 values, check that also
15803      * next pixel along the spatial direction is 0.0.
15804      */
15805 
15806     for (i = 0; i < npix - nx; i++)
15807         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
15808             sdata[i] = 65535.0;
15809 
15810     for (i = npix - nx; i < npix; i++)
15811         if (sdata[i] == 0.0) 
15812             sdata[i] = 65535.0;
15813 
15814     /*
15815      * This is a dirty trick to overcome saturations (making up a false
15816      * tip on their flat tops). This should be useless with a better
15817      * peak detection algorithm.
15818      */
15819 
15820     for (i = 0; i < npix; i++) {
15821         if (sdata[i] >= 65535.0) {
15822             count = 0;
15823             for (j = i; j < npix; j++) {
15824                 if (sdata[j] < 65535.0) {
15825                     break;
15826                 }
15827                 else {
15828                     count++;
15829                 }
15830             }
15831             if (count < 30 && count > 2) {
15832                 for (j = i; j < i + count/2; j++)
15833                     sdata[j] = sdata[i] + 1000.0 * (j - i);
15834                 if (count % 2 != 0) {
15835                     sdata[j] = sdata[j-1] + 1000.0;
15836                     j++;
15837                 }
15838                 for (k = j; k <= i + count; k++)
15839                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
15840                 i = k;
15841             }
15842         }
15843     }
15844 
15845     return cpl_error_get_code();
15846 }
15847 
15848 
15857 cpl_error_code mos_subtract_background(cpl_image * image)
15858 {
15859     /*
15860      * Create and subtract background
15861      */
15862 
15863     cpl_image * bimage = mos_arc_background(image, 15, 15);
15864     cpl_image_subtract(image, bimage);
15865     cpl_image_delete(bimage);
15866 
15867     return cpl_error_get_code();
15868 }
15869 
15870 
15887 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 
15888                                     int nscience, float tolerance)
15889 {
15890     int i, j;
15891 
15892     cpl_table *summary;
15893     int summary_nobjs = 0;
15894  
15895     int nobjs;
15896 
15897     int nmatches;
15898     int nslits = cpl_table_get_nrow(slitss[0]);
15899 
15900     int maxobjs;
15901     int k, m;
15902     int nstokes, sstokes;
15903 
15904     cpl_table **work;
15905 
15906     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
15907 
15908 
15909     /* 
15910      * First we build a table listing the offset of each detected
15911      * object at each angle and each beam, from the bottom of each 
15912      * slit spectrum, and the pair that slit spectrum belongs to.
15913      * This summary table will have as many rows as objects found 
15914      * in total at all angles.
15915      */
15916 
15917     for (j = 0; j < nscience; j++) {
15918         int c_nobjs = mos_get_nobjects(slitss[j]);
15919         if (!c_nobjs) 
15920             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15921         summary_nobjs += c_nobjs;
15922     }
15923 
15924     summary = cpl_table_new(summary_nobjs);
15925 
15926     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
15927     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
15928     cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
15929     cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
15930 
15931     /*
15932      * Fill the summary table with data from all objects:
15933      */
15934 
15935     nobjs = 0;
15936 
15937     /* Loop on all object tables (one for each angle) */
15938     for (j = 0; j < nscience; j++) {
15939         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
15940 
15941         /* Loop on all slits found on first - i.e., ALL - object table */
15942         for (k = 0; k < nslits; k++) {
15943 
15944             /* Loop on all objects found on each object table */
15945             for (m = 0; m < c_maxobjs; m++) {
15946                 int null;
15947                 char *name = cpl_sprintf("object_%d", m + 1);
15948                 double obj = cpl_table_get_double(slitss[j], name, k, &null);
15949                 int pos;
15950                 int pair;
15951 
15952                 cpl_free(name);
15953 
15954                 if (null) 
15955                     break;  /* No object #m+1 in this slit - go to next slit */
15956 
15957                 /*
15958                  * Copy necessary object data to summary table. Note 
15959                  * that the absolute object position (row) in the
15960                  * rectified image is made relative to the bottom
15961                  * position (row) of the current slit.
15962                  */ 
15963         
15964                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
15965                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
15966                 cpl_table_set(summary, "absolute", nobjs, obj);
15967                 cpl_table_set(summary, "pos", nobjs, pos);
15968                 cpl_table_set(summary, "offset", nobjs, obj - pos);
15969                 cpl_table_set(summary, "pair", nobjs, pair);
15970 
15971                 nobjs++;
15972             }
15973         }
15974     }
15975 
15976 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
15977 
15978     /* 
15979      * Perform the intersection: what are the objects belonging
15980      * to the same slit (same pair ordinary + extraordinary) which 
15981      * are observed at the same offset at all angles? Those are
15982      * the polarimetric objects.
15983      */
15984 
15985     nmatches = 0;
15986     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
15987 
15988     /*
15989      * We loop on the objects of the first-angle object table as 
15990      * reference, and check whether those objects are present also
15991      * at *all* other angles. Note that the loop advances by pairs.
15992      * If the top (k = 0) slit spectrum is not an ordinary beam,
15993      * it is ignored. The loop advances by pairs, starting at the
15994      * first complete pair. It is implicitely assumed that the 
15995      * slit spectrum on top is always from the ordinary beam, and 
15996      * the spectrum below (k+1) its extraordinary match.
15997      */
15998 
15999     for (k = 0; k < nslits; k+=2) {
16000         int slitmatches = 0;
16001 
16002         if (k == 0) {
16003             if (cpl_table_get_int(slitss[0], "pair_id",  0, NULL) !=
16004                 cpl_table_get_int(slitss[0], "pair_id",  1, NULL)) {
16005 
16006                 /*
16007                  * This is not an ordinary beam - advance to next slit.
16008                  */
16009 
16010                 k++;
16011                 continue;
16012             }
16013         }
16014 
16015         for (m = 0; m < maxobjs; m++) {
16016             int null;
16017             char *name = cpl_sprintf("object_%d", m + 1);
16018             double obj = cpl_table_get_double(slitss[0], name, k, &null);
16019             double pos;
16020             int pair;
16021 
16022             char *name_obj = NULL;
16023             char *name_start = NULL;
16024             char *name_end = NULL;
16025             char *name_row = NULL;
16026             char *name_row_s = NULL;
16027 
16028             char *name_start_o = NULL;
16029             char *name_end_o = NULL;
16030             char *name_row_o = NULL;
16031             char *name_start_v = NULL;
16032             char *name_end_v = NULL;
16033             char *name_obj_v = NULL;
16034 
16035             int start, end;
16036             int length;
16037  
16038             int selected;
16039             int v, start_v, end_v;
16040             double min_v, obj_v;
16041 
16042 
16043             cpl_free(name);
16044 
16045             if (null) 
16046                 break;
16047 
16048             /*
16049              * Each object of the first object table belongs to a
16050              * slit spectrum (k). This slit spectrum has a position
16051              * in the rectified image, and it belongs to a given 
16052              * ordinary + extraordinary pair.
16053              */
16054      
16055             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
16056             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
16057 
16058             /*
16059              * Now from the summary table we can select all objects
16060              * which have the same offset (obj - pos) within all slit
16061              * spectra belonging to the same ordinary + extraordinary 
16062              * pair (at all angles).
16063              */
16064 
16065             cpl_table_select_all(summary);  /* Reset selection */
16066 
16067             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16068             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16069                                           obj - pos + tolerance);
16070             selected = 
16071             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16072                                           obj - pos - tolerance);
16073 
16074 
16075             /*
16076              * If this object were observed at all angles (nscience) and 
16077              * at all beams (2), we should have selected exactly 2*nscience
16078              * objects. If not, this is not a polarimetric object, and it
16079              * is discarded from the intersection.
16080              */
16081             
16082             if (selected != nscience * 2) 
16083                 continue;
16084 
16085             /*
16086              * If we reach this point we have found one valid polarimetric
16087              * object, that must be inserted in the intersection object
16088              * table.
16089              */
16090  
16091             slitmatches++;
16092 
16093             /*
16094              * Names of the columns of the output table where the
16095              * object information needs to be copied. Note that a
16096              * new column is created, the "row_stokes_#", where the
16097              * row number of the extracted polarimetric signal is
16098              * also computed. For the moment this column will be 
16099              * left empty - it will be filled only when all matches 
16100              * are collected.
16101              */
16102 
16103             name_obj   = cpl_sprintf("object_%d",     slitmatches);
16104             name_start = cpl_sprintf("start_%d",      slitmatches);
16105             name_end   = cpl_sprintf("end_%d",        slitmatches);
16106             name_row   = cpl_sprintf("row_%d",        slitmatches);
16107             name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16108 
16109             /*
16110              * Names of the columns of the input table where the
16111              * object information is available.
16112              */
16113 
16114             name_start_o = cpl_sprintf("start_%d",  m + 1);
16115             name_end_o   = cpl_sprintf("end_%d",    m + 1);
16116             name_row_o   = cpl_sprintf("row_%d",    m + 1);
16117 
16118             /*
16119              * If the output columns do not exist yet, create them.
16120              */
16121  
16122             if (!cpl_table_has_column(origslits, name_obj)) {
16123                 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16124                 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16125                 cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
16126                 cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
16127                 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16128             }
16129 
16130             /*
16131              * The current slit spectrum is k. The slit spectrum immediately
16132              * below (in the rectified image) is k+1. We need the length of
16133              * the spectrum below for computing the _absolute_ coordinates
16134              * of the objects in the rectified image in both beams.
16135              */
16136  
16137             length = cpl_table_get_int(origslits, "length", k + 1, &null);
16138 
16139             /* NEW:
16140              * Names of the columns of the input table where
16141              * the information of the corresponding object of 
16142              * the next beam is available.
16143              */
16144 
16145             for (v = 0; v < maxobjs; v++) {
16146                 char *name_v = cpl_sprintf("object_%d", v + 1);
16147                 double obj_v = cpl_table_get_double(slitss[0], name_v, 
16148                                                     k + 1, &null);
16149 
16150                 cpl_free(name_v);
16151 
16152                 if (null) 
16153                     break;
16154 
16155                 if (v) {
16156                     if (fabs(obj - length - obj_v) < min_v) {
16157                         min_v = fabs(obj - length - obj_v);
16158                         cpl_free(name_start_v);
16159                         cpl_free(name_end_v);
16160                         cpl_free(name_obj_v);
16161                         name_start_v = cpl_sprintf("start_%d", v + 1);
16162                         name_end_v   = cpl_sprintf("end_%d",   v + 1);
16163                         name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16164                     }
16165                 }
16166                 else {
16167                     min_v = fabs(obj - length - obj_v);
16168                     name_start_v = cpl_sprintf("start_%d", v + 1);
16169                     name_end_v   = cpl_sprintf("end_%d",   v + 1);
16170                     name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16171                 }
16172             }
16173 
16174             /*
16175              * Read from the first input object table (first angle)
16176              * the spatial window enclosing the object.
16177              */
16178 
16179             start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16180             end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
16181 
16182             /* NEW:
16183              * Spatial window of the matching object in the next beam.
16184              */
16185 
16186             start_v = cpl_table_get_int(slitss[0], name_start_v,  k + 1, &null);
16187             end_v   = cpl_table_get_int(slitss[0], name_end_v,    k + 1, &null);
16188             obj_v   = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16189 
16190             /*
16191              * Write the object coordinates in the same slit, and in the
16192              * slit below. Note that here we assume that all slits were
16193              * traced perfectly, and we compute the theoretical coords
16194              * (obj - length) within the next slit spectrum (k + 1). In
16195              * principle we should read them as well from the input
16196              * table!
16197              */
16198 
16199             cpl_table_set_double(origslits, name_obj,   k,     obj);
16200             cpl_table_set_double(origslits, name_obj,   k + 1, obj_v);
16201          // cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
16202 
16203             cpl_table_set_int(origslits,    name_start, k,     start);
16204             cpl_table_set_int(origslits,    name_start, k + 1, start_v);
16205          // cpl_table_set_int(origslits,    name_start, k + 1, start - length);
16206 
16207             cpl_table_set_int(origslits,    name_end,   k,     end);
16208             cpl_table_set_int(origslits,    name_end,   k + 1, end_v);
16209          // cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
16210 
16211             /*
16212              * "nmatches" is counting at what "reduced" image row the
16213              * extracted spectra are. Note that this is s preliminary
16214              * numbering - which is wrong: other objects may be found
16215              * in the same slit, and then the indeces would not be in
16216              * sequence. What is important is that at the end of this
16217              * loop "nmatches" would be the total number of matching 
16218              * objects. The two cpl_table_set_int() calls made here
16219              * cannot be removed - they "validate" those table elements
16220              * (see ahead). 
16221              */
16222 
16223             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
16224             nmatches++;
16225             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
16226             nmatches++;
16227 
16228             cpl_free(name_obj);
16229             cpl_free(name_start);
16230             cpl_free(name_end);
16231             cpl_free(name_row);
16232             cpl_free(name_row_s);
16233 
16234             cpl_free(name_start_o);
16235             cpl_free(name_end_o);
16236             cpl_free(name_row_o);
16237 
16238             cpl_free(name_start_v); name_start_v = NULL;
16239             cpl_free(name_end_v); name_end_v = NULL;
16240             cpl_free(name_obj_v); name_obj_v = NULL;
16241         }
16242     }
16243 
16244     /*
16245      * The summary table has fulfilled its function. If no matching 
16246      * objects are found, the function returns with an error.
16247      */
16248 
16249     cpl_table_delete(summary);
16250 
16251     if (!nmatches)
16252         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
16253 
16254     /*
16255      * Now we consider the resulting intersection object table,
16256      * listing all matches. As seen, the image row number reported
16257      * in the columns "row_#" was not really performed sequentially.
16258      * We need to renumber sequentially...
16259      * We need also to fill the "row_stokes_#" column the way the
16260      * extracted polarimetric signal will be stored in the 
16261      * reduced_pol_images...
16262      */
16263  
16264     maxobjs = mos_get_maxobjs_per_slit(origslits);
16265     nstokes = nmatches / 2;         /* nmatches is always an even number     */
16266 
16267     for (k = 0; k < nslits; k++) {
16268         if (k % 2) { /* Extraordinary beam */
16269             nstokes = sstokes;      /* Use same start value as for ordinary  */
16270         }
16271         else {       /* Ordinary beam      */
16272             sstokes = nstokes;      /* Memorise start value at ordinary beam */
16273         }
16274 
16275         for (m = 0; m < maxobjs; m++) {
16276             char *name       = cpl_sprintf("row_%d",        m + 1);
16277             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16278 
16279             if (!cpl_table_is_valid(origslits, name, k)) {
16280                 cpl_free(name);
16281                 cpl_free(namestokes);
16282                 break;
16283             }
16284             else { 
16285                 nmatches--;
16286                 nstokes--;
16287                 cpl_table_set_int(origslits, name, k, nmatches);
16288                 cpl_table_set_int(origslits, namestokes, k, nstokes);
16289             }
16290 
16291             cpl_free(name);
16292             cpl_free(namestokes);
16293         }
16294     }
16295 
16296 
16297     /*
16298      * This is done to avoid the NULL value is zero (it would invalidate
16299      * also the row_# = 0 or start_# = 0 for an object), and to enable 
16300      * working directly with the column data buffers, when using this 
16301      * table afterwards.
16302      */
16303 
16304     for (j = 0; j < maxobjs; j++) {
16305         char *name = cpl_sprintf("object_%d", j + 1);
16306         cpl_table_fill_invalid_double(origslits, name, -1);
16307         cpl_free(name);
16308 
16309         name       = cpl_sprintf("start_%d", j + 1);
16310         cpl_table_fill_invalid_int(origslits, name, -1);
16311         cpl_free(name);
16312 
16313         name       = cpl_sprintf("end_%d", j + 1);
16314         cpl_table_fill_invalid_int(origslits, name, -1);
16315         cpl_free(name);
16316 
16317         name       = cpl_sprintf("row_%d", j + 1);
16318         cpl_table_fill_invalid_int(origslits, name, -1);
16319         cpl_free(name);
16320 
16321         name       = cpl_sprintf("row_stokes_%d", j + 1);
16322         cpl_table_fill_invalid_int(origslits, name, -1);
16323         cpl_free(name);
16324     }
16325 
16326     /*********************************************************************
16327      * This tail has been added to propagate the selection of valid
16328      * objects also to the input slitss[] tables. Just eliminate all
16329      * this final part to suppress this behaviour.
16330      */
16331 
16332     /*
16333      * First of all, make a working copy and remove all columns related 
16334      * to objects from the input object tables. 
16335      */
16336 
16337     for (i = 0; i < nscience; i++) {
16338         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16339 
16340         work[i] = cpl_table_duplicate(slitss[i]);
16341 
16342         for (m = 0; m < c_maxobjs; m++) {
16343             char *object_o = cpl_sprintf("object_%d", m + 1);
16344             char *start_o  = cpl_sprintf("start_%d",  m + 1);
16345             char *end_o    = cpl_sprintf("end_%d",    m + 1);
16346             char *row_o    = cpl_sprintf("row_%d",    m + 1);
16347 
16348             cpl_table_erase_column(slitss[i], object_o);
16349             cpl_table_erase_column(slitss[i], start_o);
16350             cpl_table_erase_column(slitss[i], end_o);
16351             cpl_table_erase_column(slitss[i], row_o);
16352         }
16353     }
16354 
16355     /* 
16356      * Now just consider all the objects in the intersection table.
16357      */
16358 
16359     for (k = 0; k < nslits; k++) {
16360         for (j = 0; j < maxobjs; j++) {
16361             double object_w, object_r;
16362             int    start_w, start_r;
16363             int    end_w, end_r;
16364             int    row_w, row_r;
16365 
16366             char  *object_i = cpl_sprintf("object_%d", j + 1);
16367             char  *start_i  = cpl_sprintf("start_%d",  j + 1);
16368             char  *end_i    = cpl_sprintf("end_%d",    j + 1);
16369             char  *row_i    = cpl_sprintf("row_%d",    j + 1);
16370 
16371 
16372             if (!cpl_table_is_valid(origslits, object_i, k))
16373                 break;
16374 
16375             /* 
16376              * We have found a valid object (valid because it belongs
16377              * to the intersection). Now we look for this object in each
16378              * one of the original tables, we get its parameters, and
16379              * copy them at the right position (i.e., same position as
16380              * in intersection table). The object will be the one closest
16381              * to the object position (column object_i) in the intersection
16382              * table. Note that we examine the same row, k, in all tables.
16383              */
16384 
16385             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16386             start_w  = cpl_table_get_int   (origslits, start_i,  k, NULL);
16387             end_w    = cpl_table_get_int   (origslits, end_i,    k, NULL);
16388             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
16389 
16390             for (i = 0; i < nscience; i++) {
16391                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16392                 int        minpos;
16393                 double     mindiff, diff;
16394                 char      *object_o;
16395                 char      *start_o;
16396                 char      *end_o;
16397                 char      *row_o;
16398 
16399                 for (m = 0; m < c_maxobjs; m++) {
16400                     object_o = cpl_sprintf("object_%d", m + 1);
16401                     start_o  = cpl_sprintf("start_%d",  m + 1);
16402                     end_o    = cpl_sprintf("end_%d",    m + 1);
16403                     row_o    = cpl_sprintf("row_%d",    m + 1);
16404 
16405                     if (!cpl_table_is_valid(work[i], object_o, k))
16406                         break;
16407 
16408                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16409                     start_r  = cpl_table_get_int   (work[i], start_o,  k, NULL);
16410                     end_r    = cpl_table_get_int   (work[i], end_o,    k, NULL);
16411                     row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
16412 
16413                     diff = fabs(object_w - object_r);
16414                     if (m) {
16415                         if (mindiff > diff) {
16416                             mindiff = diff;
16417                             minpos = m;
16418                         }
16419                     }
16420                     else {
16421                         mindiff = diff;
16422                         minpos = 0;
16423                     }
16424 
16425                     cpl_free(object_o);
16426                     cpl_free(start_o);
16427                     cpl_free(end_o);
16428                     cpl_free(row_o);
16429                 }
16430 
16431                 object_o = cpl_sprintf("object_%d", minpos + 1);
16432                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
16433                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
16434                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
16435 
16436                 if (!cpl_table_has_column(slitss[i], object_i)) {
16437                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16438                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
16439                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
16440                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
16441                     cpl_table_fill_invalid_double(slitss[i], object_i, -1);
16442                     cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
16443                     cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
16444                     cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
16445                 }
16446 
16447                 cpl_table_set_double(slitss[i], object_i, k,
16448                                      cpl_table_get_double(work[i], object_o, 
16449                                                           k, NULL));
16450                 cpl_table_set_int(slitss[i], start_i , k,
16451                                   cpl_table_get_int(work[i], start_o, k, NULL));
16452                 cpl_table_set_int(slitss[i], end_i , k,
16453                                   cpl_table_get_int(work[i], end_o, k, NULL));
16454                 cpl_table_set_int(slitss[i], row_i , k, row_w);
16455 
16456                 cpl_free(object_o);
16457                 cpl_free(start_o);
16458                 cpl_free(end_o);
16459                 cpl_free(row_o);
16460             }
16461 
16462             cpl_free(object_i);
16463             cpl_free(start_i);
16464             cpl_free(end_i);
16465             cpl_free(row_i);
16466         }
16467     }
16468 
16469     for (i = 0; i < nscience; i++)
16470         cpl_table_delete(work[i]);
16471 
16472     cpl_free(work);
16473 
16474 
16475     return cpl_error_get_code();
16476 }
16477 
16478 
16486 int mos_get_maxobjs_per_slit(cpl_table * slits)
16487 {
16488     int maxobjs = 1;
16489 
16490     char * colname = cpl_sprintf("object_%d", maxobjs);
16491     
16492     while (cpl_table_has_column(slits, colname)) {
16493         maxobjs++;
16494         cpl_free(colname);
16495         colname = cpl_sprintf("object_%d", maxobjs);
16496     }
16497     
16498     cpl_free(colname);
16499 
16500     maxobjs--;
16501 
16502     return maxobjs;
16503 }
16504 
16512 int mos_get_nobjects(cpl_table * slits)
16513 {
16514     int nobjs = 0;
16515 
16516     int nslits  = cpl_table_get_nrow(slits);
16517     int maxobjs = mos_get_maxobjs_per_slit(slits);
16518 
16519     int k, m;
16520 
16521     for (k = 0; k < nslits; k++) {
16522         for (m = 0; m < maxobjs; m++) {
16523             char * name = cpl_sprintf("object_%d", m + 1);
16524             int    null = !cpl_table_is_valid(slits, name, k);
16525 
16526             cpl_free(name);
16527 
16528             if (null)  break;
16529             else nobjs++;
16530         }
16531     }
16532 
16533     return nobjs;
16534 }
16535 
16543 int mos_check_slits(cpl_table *slits, float rescale)
16544 {
16545 
16546     cpl_propertylist *sort;
16547 
16548     int nslits  = cpl_table_get_nrow(slits);
16549 
16550     int k, null;
16551 
16552     const float interval = 90.0 * rescale;
16553     const float offset   = (90.0 - 5) * rescale;
16554 
16555 
16556     for (k = 0; k < nslits; k++) {
16557         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16558         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16559 
16560         double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
16561         double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
16562 
16563         int nmiss = (int)((ytop - ybottom) / interval + 0.5);
16564 
16565         if (nmiss > 1) {
16566             cpl_msg_warning(cpl_func, 
16567                             "Some slits could not be properly detected. "
16568                             "There might be accountable inaccuracies.");
16569             while (nmiss > 1) {
16570                 cpl_table_set_size(slits, nslits + 1);
16571 
16572                 /* Fill in new slit 'cut' */
16573 
16574                 /* x coordinates be the same (acceptable approximation) */
16575                 cpl_table_set_double(slits, "xtop",    nslits, xtop);
16576                 cpl_table_set_double(slits, "xbottom", nslits, xbottom);
16577 
16578                 /* Cut */
16579                 if (k == 0) {
16580                     cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
16581                     cpl_table_set_double(slits, "ytop",    nslits, ybottom
16582                                                                    + offset);
16583                     ybottom += interval;
16584                     cpl_table_set_double(slits, "ybottom", k,      ybottom);
16585                 } else {
16586                     cpl_table_set_double(slits, "ytop",    nslits, ytop);
16587                     cpl_table_set_double(slits, "ybottom", nslits, ytop 
16588                                                                    - offset);
16589                     ytop -= interval;
16590                     cpl_table_set_double(slits, "ytop",     k,     ytop);
16591                 }
16592 
16593                 nslits++; nmiss--;
16594             }
16595         }
16596     }
16597 
16598     sort = cpl_propertylist_new();
16599     cpl_propertylist_append_bool(sort, "ytop", 1);
16600     cpl_table_sort(slits, sort);
16601     cpl_propertylist_delete(sort);
16602 
16603     /*
16604      * Add here an ad hoc check on the last slit: is it too long 
16605      * (by more than 10%)? Then shorten it...
16606      */
16607 
16608     k = cpl_table_get_nrow(slits) - 1;
16609 
16610     {
16611         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16612         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16613         double length  = (ytop - ybottom) / interval;
16614 
16615         if (length > 1.1) {
16616             cpl_table_set_double(slits, "ybottom", k, ytop - offset);
16617         }
16618   
16619     }
16620 
16621     return 0;
16622 }
16623 
16646 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header, 
16647                                     int * nslits_out_det)
16648 {
16649     int m, null;
16650     int halfsize;
16651 
16652     cpl_propertylist * sort;
16653     cpl_table        * slits; 
16654 
16655     slits    = mos_load_slits_fors_mos(header, nslits_out_det);
16656     halfsize = cpl_table_get_nrow(slits);
16657 
16658     cpl_table_set_size(slits, 2 * halfsize);
16659 
16660     for (m = 0; m < halfsize; m++) {
16661 
16662         double gap = 1.4;
16663 
16664         double length = 
16665             cpl_table_get(slits, "ytop",    m, &null) -
16666             cpl_table_get(slits, "ybottom", m, &null);
16667 
16668         if (m) {
16669             double interval = 
16670                 cpl_table_get(slits, "ybottom", m - 1, &null) -
16671                 cpl_table_get(slits, "ytop",    m,     &null);
16672 
16673             gap = (interval - length) / 2;
16674         }
16675 
16676         cpl_table_set(slits, "slit_id", m + halfsize,
16677                       cpl_table_get(slits, "slit_id", m, &null) - 1);
16678 
16679         cpl_table_set(slits, "xtop",    m + halfsize,
16680                       cpl_table_get(slits, "xtop",    m, &null));
16681 
16682         cpl_table_set(slits, "xbottom", m + halfsize,
16683                       cpl_table_get(slits, "xbottom", m, &null));
16684 
16685         cpl_table_set(slits, "ytop",    m + halfsize, 
16686                       cpl_table_get(slits, "ytop", m, &null) + gap + length);
16687 
16688         cpl_table_set(slits, "ybottom", m + halfsize,
16689                       cpl_table_get(slits, "ytop", m, &null) + gap);
16690     }
16691 
16692     for (m = 0; m < 2 * halfsize; m++) {
16693         cpl_table_set(slits, "ytop",    m, 
16694                       cpl_table_get(slits, "ytop",    m, &null) - 5.3);
16695 
16696         cpl_table_set(slits, "ybottom", m,
16697                       cpl_table_get(slits, "ybottom", m, &null) - 5.3);
16698 
16699     }
16700 
16701     sort = cpl_propertylist_new();
16702     cpl_propertylist_append_bool(sort, "ytop", 1);
16703     cpl_table_sort(slits, sort);
16704 
16705     cpl_propertylist_delete(sort);
16706 
16707     return slits;
16708 }
16709 
16710 int * fors_get_nobjs_perslit(cpl_table * slits)
16711 {
16712     int nslits  = cpl_table_get_nrow(slits);
16713     int maxobjs = mos_get_maxobjs_per_slit(slits);
16714 
16715     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
16716 
16717     int k, m;
16718 
16719     for (k = 0; k < nslits; k++) {
16720         int nobjs = 0;
16721         for (m = 0; m < maxobjs; m++) {
16722             char * name = cpl_sprintf("object_%d", m + 1);
16723             int    null = !cpl_table_is_valid(slits, name, k);
16724 
16725             cpl_free(name);
16726 
16727             if (null)  break;
16728             else nobjs++;
16729         }
16730         
16731         nobjs_per_slit[k] = nobjs;
16732     }
16733 
16734     return nobjs_per_slit;
16735 }
16736 
16737 double fors_get_object_position(cpl_table *slits, int slit, int object)
16738 {
16739     char   *name = cpl_sprintf("object_%d", object);
16740     double  position;
16741 
16742     position = cpl_table_get_double(slits, name, slit, NULL)
16743              - cpl_table_get_int(slits, "position", slit, NULL);
16744 
16745     cpl_free(name);
16746 
16747     return position;
16748 }
16749 
16750 int mos_rebin_signal(cpl_image **image, int rebin)
16751 {
16752     cpl_image *rebinned;
16753 
16754 
16755     if (*image == NULL)
16756         return 1;
16757 
16758     if (rebin == 1)
16759         return 0;
16760 
16761     rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
16762 
16763     cpl_image_delete(*image);
16764 
16765     *image = rebinned;
16766 
16767     return 0;
16768 }
16769 
16770 int mos_rebin_error(cpl_image **image, int rebin)
16771 {
16772     if (*image == NULL)
16773         return 1;
16774 
16775     if (rebin == 1)
16776         return 0;
16777 
16778     cpl_image_power(*image, 2);
16779     mos_rebin_signal(image, rebin);
16780     cpl_image_power(*image, 0.5);
16781 
16782     return 0;
16783 }
16784 
16785 /*
16786  * @brief
16787  *   Map table values into a 1D image
16788  *  
16789  * @param image       Target image
16790  * @param start       Coordinate of first pixel in image
16791  * @param step        Coordinate step for one pixel in image
16792  * @param table       Source table
16793  * @param xname       Name of coordinate column
16794  * @param yname       Name of values column
16795  *
16796  * @return 0 on success
16797  *
16798  * The values in @em yname are linearly interpolated at the @em image 
16799  * pixel coordinates. The @em image must have Nx1 size.
16800  */
16801 
16802 int map_table(cpl_image *image, double start, double step,
16803               cpl_table *table, char *xname, char *yname)
16804 {
16805     int      length = cpl_image_get_size_x(image);
16806     int      nrows  = cpl_table_get_nrow(table);
16807     float   *data   = cpl_image_get_data_float(image);
16808     float   *fdata  = NULL;
16809     double  *xdata  = NULL;
16810     double  *ydata  = NULL;
16811     cpl_type xtype  = cpl_table_get_column_type(table, xname);
16812     cpl_type ytype  = cpl_table_get_column_type(table, yname);
16813     double   xzero, pos;
16814     int      i, j, n;
16815 
16816 
16817     /*
16818      * Initialization of output image at 0.0 - this value is left 
16819      * on non-overlapping portions.
16820      */
16821 
16822     for (i = 0; i < length; i++)
16823         data[i] = 0.0;
16824 
16825 
16826     /*
16827      * Do everything in double precision
16828      */
16829 
16830     if (xtype == CPL_TYPE_FLOAT) {
16831         fdata = cpl_table_get_data_float(table, xname);
16832         xdata = cpl_malloc(nrows * sizeof(double));
16833         for (i = 0; i < nrows; i++) {
16834            xdata[i] = fdata[i];
16835         }
16836     }
16837     else {
16838         xdata = cpl_table_get_data_double(table, xname);
16839     }
16840 
16841     if (ytype == CPL_TYPE_FLOAT) {
16842         fdata = cpl_table_get_data_float(table, yname);
16843         ydata = cpl_malloc(nrows * sizeof(double));
16844         for (i = 0; i < nrows; i++) {
16845            ydata[i] = fdata[i];
16846         }
16847     }
16848     else {
16849         ydata = cpl_table_get_data_double(table, yname);
16850     }
16851 
16852     /*
16853      * Mapping
16854      */
16855 
16856     n = 0;
16857     xzero = xdata[n];
16858 
16859     for (i = 0; i < length; i++) {
16860         pos = start + step * i;
16861         if (pos < xzero)
16862             continue;
16863         for (j = n; j < nrows; j++) {
16864             if (xdata[j] > pos) {
16865                 n = j;
16866                 data[i] = ydata[j-1]
16867                         + (ydata[j] - ydata[j-1])
16868                         * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
16869                 break;
16870             }
16871         }
16872     }
16873 
16874     if (xtype == CPL_TYPE_FLOAT)
16875         cpl_free(xdata);
16876 
16877     if (ytype == CPL_TYPE_FLOAT)
16878         cpl_free(ydata);
16879 
16880     return 0;
16881 }
16882 
16883 
16884 /*
16885  * @brief
16886  *   Fit overall trend of a Nx1 image
16887  *  
16888  * @param image       Values to smooth
16889  * @param order       Order of fitting polynomial
16890  * @param hw          Half width of smoothing window
16891  *
16892  * @return Smoothed image, or NULL on failure.
16893  *
16894  * Heavily smooth and fit data in the input Nx1 size @em image.
16895  */
16896 
16897 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
16898 {
16899     int             npoints;
16900     cpl_vector     *x;
16901     cpl_vector     *y;
16902     double         *xdata;
16903     double         *ydata;
16904     cpl_polynomial *poly;
16905     cpl_vector     *ysmooth;
16906     cpl_image      *smoothed;
16907     float          *sdata;
16908     int             i;
16909 
16910 
16911     npoints = cpl_image_get_size_x(image);
16912 
16913     if (2 * hw + 1 > npoints)
16914         return NULL;
16915 
16916     x       = cpl_vector_new(npoints);
16917     y       = cpl_vector_new(npoints);
16918     xdata   = cpl_vector_get_data(x);
16919     ydata   = cpl_vector_get_data(y);
16920 
16921     smoothed = cpl_image_duplicate(image);
16922     sdata = cpl_image_get_data_float(smoothed);
16923 
16924     for (i = 0; i < npoints; i++) {
16925         xdata[i] = i;
16926         ydata[i] = sdata[i];
16927     }
16928 
16929     ysmooth = cpl_vector_filter_median_create(y, hw);
16930     cpl_vector_delete(y);
16931 
16932     poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
16933     cpl_vector_delete(x);
16934     cpl_vector_delete(ysmooth);
16935 
16936     if (poly) {
16937         for (i = 0; i < npoints; i++)
16938             sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
16939     
16940         cpl_polynomial_delete(poly);
16941     }
16942     else {
16943         cpl_image_delete(smoothed);
16944         return NULL;
16945     }
16946 
16947     return smoothed;
16948 }
16949 
16950 #undef cleanup
16951 #define cleanup                       \
16952 do {                                  \
16953     cpl_image_delete(spectrum);       \
16954     cpl_image_delete(flux);           \
16955     cpl_image_delete(efficiency);     \
16956     cpl_image_delete(smo_efficiency); \
16957     cpl_image_delete(extinction);     \
16958     cpl_image_delete(response);       \
16959     cpl_image_delete(smo_response);   \
16960     cpl_image_delete(physical);       \
16961 } while (0)
16962 
16986 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 
16987                                  double dispersion, double gain,
16988                                  double exptime, cpl_table *ext_table,
16989                                  double airmass, cpl_table *flux_table,
16990                                  int order)
16991 {
16992 
16993     cpl_image *spectrum       = NULL; // Extracted standard star spectrum
16994     float     *data;
16995     cpl_image *extinction     = NULL; // Extinction binned as "spectrum"
16996     float     *ext_data;
16997     cpl_image *flux           = NULL; // Standard star flux binned as "spectrum"
16998     float     *flux_data;
16999     cpl_image *physical       = NULL; // Physical units of above
17000     float     *phys_data;
17001     cpl_image *efficiency     = NULL; // Raw efficiency curve
17002     float     *eff_data;
17003     cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
17004     float     *smo_eff_data;
17005     cpl_image *response       = NULL; // Raw response curve
17006     float     *res_data;
17007     cpl_image *smo_response   = NULL; // Smoothed response curve
17008     float     *smo_res_data;
17009     cpl_image *image;
17010     cpl_image *smo_image;
17011     cpl_table *table;
17012     float      lambda;
17013     int        nx, ny;
17014     int        ext_count, ext_pos;
17015     int        eff_count, eff_pos;
17016     int        flux_count, flux_pos;
17017     int        start, end;
17018     int        i;
17019 
17020 
17021     if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
17022         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17023         return NULL;
17024     }
17025 
17026     if (!cpl_table_has_column(ext_table, "WAVE")) {
17027         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17028                               "Column WAVE in atmospheric extinction table");
17029         return NULL;
17030     }
17031 
17032     if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17033         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17034                         "Column EXTINCTION in atmospheric extinction table");
17035         return NULL;
17036     }
17037 
17038     if (!cpl_table_has_column(flux_table, "WAVE")) {
17039         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17040                               "Column WAVE in standard star flux table");
17041         return NULL;
17042     }
17043 
17044     if (!cpl_table_has_column(flux_table, "FLUX")) {
17045         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17046                               "Column FLUX in standard star flux table");
17047         return NULL;
17048     }
17049 
17050     if (gain < 0.1) {
17051         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17052                               "Invalid gain factor (%.2f)", gain);
17053         return NULL;
17054     }
17055 
17056     if (exptime < 0.001) {
17057         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17058                               "Invalid exposure time (%.2f)", exptime);
17059         return NULL;
17060     }
17061 
17062     if (dispersion < 0.001) {
17063         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17064                               "Invalid dispersion (%.2f)", dispersion);
17065         return NULL;
17066     }
17067 
17068     if (order < 2) {
17069         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17070                               "Order of the polynomial fitting the "
17071                               "instrument response must be at least 2");
17072         return NULL;
17073     }
17074 
17075     nx = cpl_image_get_size_x(spectra);
17076     ny = cpl_image_get_size_y(spectra);
17077 
17078 
17079     /*
17080      * Find brightest spectrum and duplicate it.
17081      */
17082 
17083     if (ny == 1) {
17084         spectrum = cpl_image_duplicate(spectra);
17085     }
17086     else {
17087         cpl_size        x, y;
17088         cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17089 
17090         cpl_image_get_maxpos(brights, &x, &y);
17091         cpl_image_delete(brights);
17092         spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17093     }
17094 
17095 
17096     /*
17097      * Convert standard star spectrum in electrons per second per Angstrom.
17098      */
17099 
17100     cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17101 
17102 
17103     /*
17104      * Map the atmospheric extinction factors to the same lambda sampling
17105      * of the extracted spectrum.
17106      */
17107 
17108     extinction = cpl_image_duplicate(spectrum);
17109     map_table(extinction, startwave + dispersion/2, dispersion, 
17110               ext_table, "WAVE", "EXTINCTION");
17111 
17112 
17113     /*
17114      * Convert from magnitudes to actual flux loss.
17115      */
17116 
17117     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17118     cpl_image_exponential(extinction, 10.);
17119 
17120 
17121     /*
17122      * Correct the scientific spectrum to airmass 0
17123      */
17124 
17125     cpl_image_multiply(spectrum, extinction);
17126 
17127 
17128     /*
17129      * Find in what pixel interval (start at "ext_pos", for "ext_count" 
17130      * pixels) the atmospheric extinction is available.
17131      */
17132     
17133     ext_data = cpl_image_get_data_float(extinction);
17134 
17135     ext_count = 0;
17136     ext_pos = 0;
17137     for (i = 0; i < nx; i++) {
17138         if (ext_data[i] > 0.0) {
17139             if (ext_count == 0) {
17140                 ext_pos = i;
17141             }
17142             ext_count++;
17143         }
17144         else {
17145             if (ext_count) {
17146                 break;
17147             }
17148         }
17149     }
17150 
17151     cpl_image_delete(extinction); extinction = NULL;
17152 
17153 
17154     /*
17155      * Map the standard star catalog flux to the same lambda sampling
17156      * of the extracted spectrum.
17157      */
17158 
17159     flux = cpl_image_duplicate(spectrum);
17160     map_table(flux, startwave + dispersion/2, dispersion, 
17161               flux_table, "WAVE", "FLUX");
17162 
17163 
17164     /*
17165      * Find in what pixel interval (start at "flux_pos", for "flux_count" 
17166      * pixels) the standard star flux is available.
17167      */
17168     
17169     flux_data = cpl_image_get_data_float(flux);
17170 
17171     flux_count = 0;
17172     flux_pos = 0;
17173     for (i = 0; i < nx; i++) {
17174         if (flux_data[i] > 0.0) {
17175             if (flux_count == 0) {
17176                 flux_pos = i;
17177             }
17178             flux_count++;
17179         }
17180         else {
17181             if (flux_count) {
17182                 break;
17183             }
17184         }
17185     }
17186 
17187 
17188     /*
17189      * Intersection with previous selection
17190      */
17191 
17192     start      = ext_pos > flux_pos ? ext_pos : flux_pos;
17193     end        = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17194                  (ext_pos + ext_count) : (flux_pos + flux_count);
17195     flux_pos   = start;
17196     flux_count = end - start;
17197 
17198 
17199     /*
17200      * Convert the flux to photons (per second per Angstrom).
17201      * std_flux is in units of erg / cm^2 / s / Angstrom. This
17202      * must be multiplied by the efficient area of the telescope,
17203      * 5.18E+5 cm^2, and divided by hv (v = frequency). With 
17204      * hc = 1.98E-8 erg*Angstrom one obtains the following:
17205      */
17206 
17207     physical = cpl_image_duplicate(spectrum);
17208     phys_data = cpl_image_get_data_float(physical);
17209 
17210     for (i = 0; i < nx; i++) {
17211         lambda = startwave + dispersion * (i + 0.5);
17212         phys_data[i] = 0.0026 * lambda * flux_data[i];
17213     }
17214 
17215     efficiency = cpl_image_duplicate(spectrum);
17216     eff_data = cpl_image_get_data_float(efficiency);
17217     data = cpl_image_get_data_float(spectrum);
17218 
17219     for (i = 0; i < nx; i++) {
17220         if (phys_data[i] > 0.0)
17221             eff_data[i] = data[i] / phys_data[i];
17222         else
17223             eff_data[i] = 0.0;
17224     }
17225 
17226     cpl_image_delete(physical); physical = NULL;
17227 
17228 
17229     /*
17230      * Find interval (longer than 300 pixels) where efficiency is 
17231      * greater than 1%
17232      */
17233 
17234     eff_count = 0;
17235     eff_pos = 0;
17236     for (i = 0; i < nx; i++) {
17237         if (eff_data[i] > 0.01) {
17238             if (eff_count == 0) {
17239                 eff_pos = i; 
17240             }
17241             eff_count++;
17242         }
17243         else {
17244             if (eff_count > 300) {
17245                 break;
17246             }
17247         }
17248     }
17249 
17250 
17251     /*
17252      * Intersection with previous selection
17253      */
17254 
17255     start      = eff_pos > flux_pos ? eff_pos : flux_pos;
17256     end        = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17257                  (eff_pos + eff_count) : (flux_pos + flux_count);
17258     eff_pos    = start;
17259     eff_count  = end - start;
17260 
17261     if (eff_count < 1) {
17262         cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17263                               "No overlap between catalog and spectrum");
17264         cleanup;
17265         return NULL;
17266     }
17267 
17268 
17269     /*
17270      * Extract only data to fit, i.e., where the efficiency is available.
17271      */
17272 
17273     image = cpl_image_extract(efficiency, eff_pos + 1, 1, 
17274                               eff_pos + eff_count, 1);
17275 
17276     smo_image = polysmooth(image, order, 50);
17277     cpl_image_delete(image);
17278 
17279     smo_efficiency = cpl_image_duplicate(efficiency);
17280     smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17281     cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17282 
17283     cpl_image_delete(smo_image);
17284 
17285 
17286     /*
17287      * Compute instrument response as the ratio between the catalog
17288      * spectrum and the observed spectrum (converted in physical units).
17289      * The polynomial smoothing, however, is performed on the inverse
17290      * of this ration, for obvious reasons (i.e., no divergence at zero
17291      * efficiency).
17292      */
17293 
17294     response = cpl_image_duplicate(spectrum);
17295     res_data = cpl_image_get_data_float(response);
17296 
17297     for (i = 0; i < nx; i++) {
17298         if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17299             res_data[i] = data[i] / flux_data[i];
17300         else
17301             res_data[i] = 0.0;
17302     }
17303 
17304 
17305     /*
17306      * Extract only data to fit, i.e., where the response is available.
17307      */
17308 
17309     image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17310 
17311     smo_image = polysmooth(image, order, 50);
17312     cpl_image_delete(image);
17313 
17314     smo_response = cpl_image_duplicate(response);
17315     smo_res_data = cpl_image_get_data_float(smo_response);
17316     cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17317 
17318     cpl_image_delete(smo_image);
17319 
17320     for (i = 0; i < nx; i++) {
17321         if (eff_data[i] > 0.01) {
17322             res_data[i] = 1 / res_data[i];
17323             smo_res_data[i] = 1 / smo_res_data[i];
17324         }
17325         else {
17326             res_data[i] = 0.0;
17327             smo_res_data[i] = 0.0;
17328         }
17329     }
17330 
17331 
17332     /*
17333      * Assemble the product spectrophotometric table.
17334      */
17335 
17336     table = cpl_table_new(nx);
17337 
17338     cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17339     cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17340 
17341     for (i = 0; i < nx; i++)
17342         cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17343 
17344     cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17345     cpl_table_set_column_unit(table, "STD_FLUX", 
17346                               "10^(-16) erg/(cm^2 s Angstrom)");
17347     cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17348     cpl_image_delete(flux); flux = NULL;
17349 
17350     cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17351     cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17352     cpl_table_copy_data_float(table, "OBS_FLUX", data);
17353     cpl_image_delete(spectrum); spectrum = NULL;
17354 
17355     cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17356     cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17357     cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17358     cpl_image_delete(efficiency); efficiency = NULL;
17359 
17360     cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17361     cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17362     cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17363     cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17364 
17365     cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17366     cpl_table_set_column_unit(table, "RAW_RESPONSE", 
17367                               "10^(-16) erg/(cm^2 electron)");
17368     cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17369     cpl_image_delete(response); response = NULL;
17370 
17371     cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17372     cpl_table_set_column_unit(table, 
17373                               "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17374     cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17375     cpl_image_delete(smo_response); smo_response = NULL;
17376 
17377     cleanup;
17378 
17379     return table;
17380 }
17381 
17382 static double ksigma_vector(cpl_vector *values, 
17383                             double klow, double khigh, int kiter, int *good)
17384 {
17385     cpl_vector *accepted;
17386     double  mean  = 0.0;
17387     double  sigma = 0.0;
17388     double *data  = cpl_vector_get_data(values);
17389     int     n     = cpl_vector_get_size(values);
17390     int     ngood = n;
17391     int     count = 0;
17392     int     i;
17393 
17394 
17395     /*
17396      * At first iteration the mean is taken as the median, and the
17397      * standard deviation relative to this value is computed.
17398      */
17399 
17400     mean = cpl_vector_get_median(values);
17401 
17402     for (i = 0; i < n; i++) 
17403         sigma += (mean - data[i]) * (mean - data[i]);
17404 
17405     sigma = sqrt(sigma / (n - 1));
17406 
17407     while (kiter) {
17408         count = 0;
17409         for (i = 0; i < ngood; i++) {
17410             if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17411                 data[count] = data[i];
17412                 ++count;
17413             }
17414         }
17415 
17416         if (count == 0) // This cannot happen at first iteration.
17417             break;      // So we can break: we have already computed a mean.
17418 
17419         /*
17420          * The mean must be computed even if no element was rejected
17421          * (count == ngood), because at first iteration median instead 
17422          * of mean was computed.
17423          */
17424 
17425         accepted = cpl_vector_wrap(count, data);
17426         mean = cpl_vector_get_mean(accepted);
17427         if (count > 1)
17428             sigma = cpl_vector_get_stdev(accepted);
17429         cpl_vector_unwrap(accepted);
17430 
17431         if (count == ngood || count == 1)
17432             break;
17433 
17434         ngood = count;
17435         --kiter;
17436     }
17437 
17438     if (good)
17439         *good = ngood;
17440 
17441     return mean;
17442 }
17443 
17444 
17463 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 
17464                             double klow, double khigh, int kiter,
17465                             cpl_image **good)
17466 {
17467     int         ni, nx, ny, npix;
17468     cpl_image  *out_ima;
17469     float      *pout_ima;
17470     float      *good_ima;
17471     cpl_image  *image;
17472     float     **data;
17473     cpl_vector *time_line;
17474     double     *ptime_line;
17475     int         ngood;
17476     int         i, j;
17477 
17478 
17479     ni         = cpl_imagelist_get_size(imlist);
17480 
17481     image      = cpl_imagelist_get(imlist, 0);
17482     nx         = cpl_image_get_size_x(image);
17483     ny         = cpl_image_get_size_y(image);
17484     npix       = nx * ny;
17485     
17486     out_ima    = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17487     pout_ima   = cpl_image_get_data_float(out_ima);
17488 
17489     if (good) {
17490         *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17491         good_ima = cpl_image_get_data_float(*good);
17492     }
17493 
17494     time_line  = cpl_vector_new(ni);
17495     ptime_line = cpl_vector_get_data(time_line);
17496 
17497     data = cpl_calloc(sizeof(float *), ni);
17498     
17499     for (i = 0; i < ni; i++) {
17500         image = cpl_imagelist_get(imlist, i);
17501         data[i] = cpl_image_get_data_float(image);
17502     }
17503 
17504     for (i = 0; i < npix; i++) {
17505         for (j = 0; j < ni; j++) {
17506             ptime_line[j] = data[j][i];
17507         }
17508         pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
17509         if (good) {
17510             good_ima[i] = ngood;
17511         }
17512     }
17513 
17514     cpl_free(data);
17515     cpl_vector_delete(time_line);
17516 
17517     return out_ima;
17518 
17519 }
17520 
17521 
17538 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
17539                                 cpl_table *ext_table, double startwave,
17540                                 double dispersion, double gain,
17541                                 double exptime, double airmass)
17542 {
17543     cpl_image *extinction;
17544     cpl_image *outspectra;
17545     cpl_image *mapresponse;
17546     float     *res_data;
17547     float     *out_data;
17548     float     *ext_data;
17549     int        tlength, xlength, ylength;
17550     int        i, j, k;
17551 
17552 
17553     if (spectra == NULL || ext_table == NULL || response == NULL) {
17554         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17555         return NULL;
17556     }
17557 
17558     res_data = cpl_table_get_data_float(response, "RESPONSE");
17559 
17560     if (res_data == NULL) {
17561         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17562         return NULL;
17563     }
17564 
17565     tlength = cpl_table_get_nrow(response);
17566     xlength = cpl_image_get_size_x(spectra);
17567     ylength = cpl_image_get_size_y(spectra);
17568 
17569     if (xlength != tlength) {
17570         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17571         map_table(mapresponse, startwave + dispersion/2, dispersion,
17572                   response, "WAVE", "RESPONSE");
17573         res_data = cpl_image_get_data_float(mapresponse);
17574     }
17575 
17576     /*
17577      * Map the atmospheric extinction factors to the same lambda sampling
17578      * of the extracted spectrum.
17579      */
17580 
17581     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17582     map_table(extinction, startwave + dispersion/2, dispersion,
17583               ext_table, "WAVE", "EXTINCTION");
17584 
17585 
17586     /*
17587      * Convert from magnitudes to actual flux loss.
17588      */
17589 
17590     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17591     cpl_image_exponential(extinction, 10.);
17592 
17593     outspectra = cpl_image_duplicate(spectra);
17594 
17595     ext_data = cpl_image_get_data_float(extinction);
17596     out_data = cpl_image_get_data_float(outspectra);
17597 
17598     for (k = 0, i = 0; i < ylength; i++) {
17599         for (j = 0; j < xlength; j++, k++) {
17600             out_data[k] *= ext_data[j] * res_data[j];
17601         }
17602     }
17603 
17604     cpl_image_delete(extinction);
17605     if (xlength != tlength) {
17606         cpl_image_delete(mapresponse);
17607     }
17608 
17609     cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
17610 
17611     return outspectra;
17612 }
17613 
17614 
17631 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 
17632                                           cpl_image *errors, 
17633                                           cpl_table *response,
17634                                           cpl_table *ext_table, 
17635                                           double startwave,
17636                                           double dispersion, double gain,
17637                                           double exptime, double airmass)
17638 {
17639     cpl_image *extinction;
17640     cpl_image *outerrors;
17641     cpl_image *mapresponse;
17642     cpl_image *maperror;
17643     float     *err_data;
17644     float     *out_data;
17645     float     *ext_data;
17646     float     *res_data;
17647     float     *spe_data;
17648     int        tlength, xlength, ylength;
17649     int        i, j, k;
17650 
17651 
17652     if (errors == NULL || ext_table == NULL || response == NULL) {
17653         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17654         return NULL;
17655     }
17656 
17657     if (!cpl_table_has_column(response, "ERROR")) {
17658         return mos_apply_photometry(errors, response, ext_table, startwave,
17659                                     dispersion, gain, exptime, airmass);
17660     }
17661 
17662     res_data = cpl_table_get_data_float(response, "RESPONSE");
17663 
17664     if (res_data == NULL) {
17665         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17666         return NULL;
17667     }
17668 
17669     err_data = cpl_table_get_data_float(response, "ERROR");
17670 
17671     if (err_data == NULL) {
17672         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17673         return NULL;
17674     }
17675 
17676     tlength = cpl_table_get_nrow(response);
17677     xlength = cpl_image_get_size_x(errors);
17678     ylength = cpl_image_get_size_y(errors);
17679 
17680     if (xlength != tlength) {
17681         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17682         map_table(mapresponse, startwave + dispersion/2, dispersion,
17683                   response, "WAVE", "RESPONSE");
17684         res_data = cpl_image_get_data_float(mapresponse);
17685 
17686         maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17687         map_table(maperror, startwave + dispersion/2, dispersion,
17688                   response, "WAVE", "ERROR");
17689         err_data = cpl_image_get_data_float(maperror);
17690     }
17691 
17692     /*
17693      * Map the atmospheric extinction factors to the same lambda sampling
17694      * of the extracted spectrum.
17695      */
17696 
17697     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17698     map_table(extinction, startwave + dispersion/2, dispersion,
17699               ext_table, "WAVE", "EXTINCTION");
17700 
17701 
17702     /*
17703      * Convert from magnitudes to actual flux loss.
17704      */
17705 
17706     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17707     cpl_image_exponential(extinction, 10.);
17708 
17709     outerrors = cpl_image_duplicate(errors);
17710 
17711     ext_data = cpl_image_get_data_float(extinction);
17712     out_data = cpl_image_get_data_float(outerrors);
17713     spe_data = cpl_image_get_data_float(spectra);
17714 
17715     for (k = 0, i = 0; i < ylength; i++) {
17716         for (j = 0; j < xlength; j++, k++) {
17717             out_data[k] = ext_data[j] * 
17718               sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
17719                    res_data[j] * res_data[j] * out_data[k] * out_data[k]);
17720         }
17721     }
17722 
17723     cpl_image_delete(extinction);
17724     if (xlength != tlength) {
17725         cpl_image_delete(maperror);
17726     }
17727 
17728     cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
17729 
17730     return outerrors;
17731 }
17732 
17733 
17809 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
17810                            cpl_image *u_image, cpl_image *u_error,
17811                            double startwave, double dispersion,
17812                            double band, cpl_table *pol_sta,
17813                            double ra, double dec, char *filter, 
17814                            int *polarisation,
17815                            double *p_offset, double *p_error,
17816                            double *a_offset, double *a_error)
17817 {
17818     cpl_table *standard;
17819     cpl_image *q_noise;
17820     cpl_image *q_signal;
17821     cpl_image *u_noise;
17822     cpl_image *u_signal;
17823     cpl_image *noise;
17824     double    *q_ndata;
17825     double    *q_sdata;
17826     double    *u_ndata;
17827     double    *u_sdata;
17828     double     arctol = 0.5;  /* Arc tolerance in degrees */
17829     double     mindist;
17830     double     cwave;
17831     double     bwave[] = {3650., 4450., 5510., 6580., 8060};
17832     char      *bands = "UBVRI";
17833     char       p_label[] = {' ', 'p', '\0'};
17834     char       dp_label[] = {' ', 'd', 'p', '\0'};
17835     char       a_label[] = {' ', 'a', '\0'};
17836     char       da_label[] = {' ', 'd', 'a', '\0'};
17837     int        nbands = strlen(bands);
17838     int        selected;
17839     int        first, last, count, center;
17840     int        nx;
17841     cpl_size   col, row;
17842     int        i, found, closest;
17843     int        pband;
17844     int        polarised;
17845     double     q_obs;
17846     double     q_err;
17847     double     u_obs;
17848     double     u_err;
17849     double     p_obs;
17850     double     p_err;
17851     double     p_ref;
17852     double     dp_ref;
17853     double     a_obs;
17854     double     a_err;
17855     double     a_ref;
17856     double     da_ref;
17857 
17858 
17859     *filter       = '\0';
17860     *polarisation = 0;
17861     *p_offset     = 0.0;
17862     *p_error      = 0.0;
17863     *a_offset     = 0.0;
17864     *a_error      = 0.0;
17865 
17866     /*
17867      * Select reference standard star
17868      */
17869 
17870     cpl_table_select_all(pol_sta);
17871     cpl_table_and_selected_double(pol_sta, "RA",  CPL_GREATER_THAN, ra-arctol);
17872     cpl_table_and_selected_double(pol_sta, "RA",  CPL_LESS_THAN,    ra+arctol);
17873     cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol);
17874     selected =
17875     cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN,    dec+arctol);
17876 
17877     if (selected == 0) {
17878         cpl_msg_warning(cpl_func, "No standard star found in FOV");
17879         return 1;
17880     }
17881 
17882     if (selected > 1) {
17883         cpl_msg_warning(cpl_func, 
17884                         "Ambiguity: %d standard stars found in FOV", selected);
17885         return 1;
17886     }
17887 
17888     standard = cpl_table_extract_selected(pol_sta);
17889 
17890     cpl_msg_info(cpl_func, "Standard star: %s", 
17891                  cpl_table_get_string(standard, "name", 0));
17892 
17893     /*
17894      * Check whether the star is polarised or not
17895      */
17896 
17897     polarised = cpl_table_get_int(standard,  "polarised", 0, NULL);
17898 
17899     cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
17900                  polarised ? " " : " not ");
17901 
17902 
17903     /*
17904      * Determine the image row with the smallest median noise: this 
17905      * row is assumed to refer to the standard star.
17906      * (note: the higher the S/N ratio of the original spectra, the 
17907      * smaller the noise of the Stokes parameters Q and U).
17908      */
17909 
17910     nx = cpl_image_get_size_x(q_error);
17911 
17912     noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
17913     cpl_image_get_minpos(noise, &col, &row);
17914 
17915     cpl_image_delete(noise);
17916 
17917     if (col != 1) {
17918         cpl_table_delete(standard);
17919         cpl_msg_error(cpl_func, 
17920                       "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col);
17921         return 1;
17922     }
17923 
17924     q_signal = cpl_image_extract(q_image, 1, row, nx, row);
17925     q_noise  = cpl_image_extract(q_error, 1, row, nx, row);
17926     u_signal = cpl_image_extract(u_image, 1, row, nx, row);
17927     u_noise  = cpl_image_extract(u_error, 1, row, nx, row);
17928 
17929     q_sdata = cpl_image_get_data_double(q_signal);
17930     q_ndata = cpl_image_get_data_double(q_noise);
17931     u_sdata = cpl_image_get_data_double(u_signal);
17932     u_ndata = cpl_image_get_data_double(u_noise);
17933 
17934 
17935     /*
17936      * Determine valid interval in input images (where error is positive).
17937      */
17938 
17939     first = -1;
17940     last = nx = cpl_image_get_size_x(q_signal);
17941     for (i = 0; i < nx; i++) {
17942         if (first < 0) {
17943             if (q_ndata[i] > 0.0) {
17944                 first = i;
17945             }
17946         }
17947         else {
17948             if (q_ndata[i] <= 0.0) {
17949                 last = i - 1;
17950                 break;
17951             }
17952         }
17953     }
17954 
17955     count = last - first + 1;
17956 
17957     if (first < 0 || count < band) {
17958         cpl_table_delete(standard);
17959         cpl_image_delete(q_signal);
17960         cpl_image_delete(q_noise);
17961         cpl_image_delete(u_signal);
17962         cpl_image_delete(u_noise);
17963         cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
17964         return 1;
17965     }
17966 
17967     center = (first + last) / 2;              // Center of valid spectrum
17968     cwave = startwave + dispersion * center;  // Corresponding wavelength
17969 
17970 
17971     /*
17972      * Find the band UBVRI closest to the central wavelength.
17973      */
17974 
17975     found = 0;
17976     for (i = 0; i < nbands; i++) {
17977         p_label[0] = bands[i];
17978         if (cpl_table_is_valid(standard, p_label, 0)) {
17979             if (found == 0) {
17980                 found = 1;
17981                 mindist = fabs(bwave[i] - cwave);
17982                 closest = i;
17983             }
17984             else if (mindist > fabs(bwave[i] - cwave)) {
17985                 mindist = fabs(bwave[i] - cwave);
17986                 closest = i;
17987             }
17988         }
17989     }
17990 
17991     if (!found) {
17992         cpl_table_delete(standard);
17993         cpl_image_delete(q_signal);
17994         cpl_image_delete(q_noise);
17995         cpl_image_delete(u_signal);
17996         cpl_image_delete(u_noise);
17997         cpl_msg_warning(cpl_func, "No reference value available");
17998         return 1;
17999     }
18000 
18001     center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
18002     cwave  =  bwave[closest];                           // Wavelength of band
18003 
18004 
18005     /*
18006      * Check that the integration interval is entirely contained
18007      * in the valid interval, or give it up.
18008      */
18009 
18010     pband = floor(band / dispersion);  // Band width in pixels
18011 
18012     if (center - pband/2 < first || center + pband/2 > last) {
18013         cpl_table_delete(standard);
18014         cpl_image_delete(q_signal);
18015         cpl_image_delete(q_noise);
18016         cpl_image_delete(u_signal);
18017         cpl_image_delete(u_noise);
18018         cpl_msg_warning(cpl_func, "No reference value available");
18019         return 1;
18020     }
18021 
18022     first = center - pband/2;
18023     last  = center + pband/2;
18024 
18025     /*
18026      * Collect reference values. Note that if angle info is not available,
18027      * angle stuff is set automaticaly to zero.
18028      */
18029 
18030      p_label[0] = bands[closest];
18031     dp_label[0] = bands[closest];
18032      a_label[0] = bands[closest];
18033     da_label[0] = bands[closest];
18034 
18035      p_ref = cpl_table_get(standard,  p_label, 0, NULL);
18036     dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18037      a_ref = cpl_table_get(standard,  a_label, 0, NULL);
18038     da_ref = cpl_table_get(standard, da_label, 0, NULL);
18039 
18040     cpl_msg_info(cpl_func, 
18041                  "The expected polarisation is %.2f +- %.2f %%", 
18042                  p_ref, dp_ref);
18043 
18044     if (polarised) {
18045         cpl_msg_info(cpl_func, 
18046                      "The expected polarisation angle is %.2f +- %.2f degrees", 
18047                      a_ref, da_ref);
18048     }
18049 
18050     /*
18051      * Find median signal and median error.
18052      */
18053 
18054     q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18055     q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18056     u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18057     u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18058 
18059     /*
18060      * Measured linear polarisation and its error
18061      */
18062 
18063     p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18064     p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18065 
18066     /*
18067      * Measured polarisation angle
18068      */
18069 
18070     a_obs = 0.0;
18071     if (polarised) {
18072         if (fabs(q_obs) < 0.00001) {
18073             if (u_obs > 0.0) {
18074                 a_obs = 45.0;
18075             }
18076             else {
18077                 a_obs = 135.0;
18078             }
18079         }
18080         else {
18081             a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18082             if (q_obs > 0.0) {
18083                 if (u_obs < 0.0) {
18084                     a_obs += 180.;
18085                 }
18086             }
18087             else {
18088                 a_obs += 90.;
18089             }
18090         }
18091     }
18092 
18093     /*
18094      * Error on polarisation angle
18095      */
18096 
18097     a_err = 0.0;
18098     if (polarised) {
18099         a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18100               / (p_obs * p_obs) 
18101               * 90 / CPL_MATH_PI;
18102     }
18103 
18104     p_obs *= 100;
18105     p_err *= 100;
18106     cpl_msg_info(cpl_func, 
18107                  "The measured polarisation is %.2f +- %.2f %%", 
18108                  p_obs, p_err);
18109 
18110     if (polarised) {
18111         cpl_msg_info(cpl_func, 
18112                      "The measured polarisation angle is %.2f +- %.2f degrees", 
18113                      a_obs, a_err);
18114     }
18115 
18116     *filter       = bands[closest];
18117     *polarisation = polarised;
18118 
18119     if (polarised) {
18120         *p_offset = (p_obs - p_ref) / p_ref;
18121         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18122     }
18123     else {
18124         *p_offset = p_obs - p_ref;
18125         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref);
18126     }
18127 
18128     *a_offset     = a_obs - a_ref;
18129     *a_error      = sqrt(a_err*a_err + da_ref*da_ref);
18130 
18131     return 0;
18132 
18133 }
18134 
18135 
18167 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18168 {
18169     cpl_array *offsets;
18170     int        noffset;
18171     int        nslits = cpl_table_get_nrow(reference);
18172     int       *nref;
18173     int       *nobj;
18174     int        corr, maxcorr;
18175     int        shift, best_shift;
18176     int        i, j, k;
18177 
18178     cpl_error_code status = CPL_ERROR_NONE;
18179 
18180 
18181     *offset = 0.0;
18182 
18183     if (nslits != cpl_table_get_nrow(objects))
18184         return CPL_ERROR_INCOMPATIBLE_INPUT;
18185 
18186     nref = fors_get_nobjs_perslit(reference);
18187     nobj = fors_get_nobjs_perslit(objects);
18188 
18189     noffset = 0;
18190     for (i = 0; i < nslits; i++)
18191         noffset += nobj[i];
18192 
18193     if (noffset == 0) {
18194         cpl_free(nref);
18195         cpl_free(nobj);
18196         return CPL_ERROR_DATA_NOT_FOUND;
18197     }
18198 
18199     noffset = 0;
18200     for (i = 0; i < nslits; i++)
18201         noffset += nref[i];
18202 
18203     if (noffset == 0) {
18204         cpl_free(nref);
18205         cpl_free(nobj);
18206         return CPL_ERROR_DATA_NOT_FOUND;
18207     }
18208 
18209     offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18210 
18211     noffset = 0;    // The real number of offsets found will be counted.
18212 
18213     for (i = 0; i < nslits; i++) {
18214         if (nref[i] > 0 && nobj[i] > 0) {
18215             double shift;
18216             int    length  = cpl_table_get_int(objects, "length", i, NULL);
18217             double ytop    = cpl_table_get_double(objects, "xtop", i, NULL);
18218             double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18219             int   *aref    = cpl_calloc(length, sizeof(int));
18220             int   *aobj    = cpl_calloc(length, sizeof(int));
18221             float *pref    = cpl_calloc(nref[i], sizeof(float));
18222             float *pobj    = cpl_calloc(nobj[i], sizeof(float));
18223             
18224             for (j = 0; j < nref[i]; j++) {
18225                 pref[j] = fors_get_object_position(reference, i, j + 1);
18226                 aref[(int)pref[j]] = 1;
18227             }
18228 
18229             for (j = 0; j < nobj[i]; j++) {
18230                 pobj[j] = fors_get_object_position(objects, i, j + 1);
18231                 aobj[(int)pobj[j]] = 1;
18232             }
18233 
18234             /*
18235              * Do not consider objects at border
18236              */
18237 
18238             aref[0] = 0;
18239             aref[length - 1] = 0;
18240             aobj[0] = 0;
18241             aobj[length - 1] = 0;
18242 
18243 //for (j = 0; j < nref[i]; j++)
18244 //printf("references: %f, ", pref[j]);
18245 //printf("\n");
18246 //for (j = 0; j < nref[i]; j++)
18247 //printf("objects   : %f, ", pobj[j]);
18248 //printf("\n");
18249 //for (j = 0; j < length; j++)
18250 //printf("%d", aref[j]);
18251 //printf("\n");
18252 //for (j = 0; j < length; j++)
18253 //printf("%d", aobj[j]);
18254 //printf("\n");
18255 
18256             /*
18257              * Object matching by correlation
18258              */
18259 
18260             maxcorr = 0;
18261             best_shift = length;
18262 
18263             for (shift = length/2, j = 0; j <= length; shift--, j++) {
18264                 int rstart, ostart, count;
18265 
18266                 if (shift > 0) {
18267                    rstart = shift;
18268                    ostart = 0;
18269                    count  = length - shift;
18270                 }
18271                 else {
18272                    rstart = 0;
18273                    ostart = -shift;
18274                    count  = length + shift;
18275                 }
18276 
18277                 corr = 0;
18278                 for (k = 0; k < count; k++) {
18279                     corr += aref[rstart + k] * aobj[ostart + k];
18280                 }
18281 
18282                 if (maxcorr < corr) {
18283                     maxcorr = corr;
18284                     best_shift = shift;
18285                 }
18286             }
18287 
18288             if (best_shift == length) { // No shift found
18289 //printf("%d: No shift found\n", i);
18290                 cpl_free(aref);
18291                 cpl_free(aobj);
18292                 cpl_free(pref);
18293                 cpl_free(pobj);
18294                 continue;
18295             }
18296 //printf("%d: Integer shift found = %d\n", i, best_shift);
18297 
18298             for (j = 0; j < nref[i]; j++) {
18299                 for (k = 0; k < nobj[i]; k++) {
18300                     if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18301                        double ccd_offset = (pref[j] - pobj[k]) 
18302                                          * (ytop - ybottom)
18303                                          / length;
18304 
18305 //printf("%d: Match found: %f\n", i, ccd_offset);
18306                        /* 
18307                         * The matching object is found, store the
18308                         * corresponding offset
18309                         */
18310 
18311                        cpl_array_set(offsets, noffset, ccd_offset);
18312                        noffset++;
18313                        break;
18314                     }
18315                 }
18316             }
18317 
18318             cpl_free(aref);
18319             cpl_free(aobj);
18320             cpl_free(pref);
18321             cpl_free(pobj);
18322         }
18323 //else
18324 //printf("%d: No object found\n", i);
18325     }
18326 
18327     cpl_free(nref);
18328     cpl_free(nobj);
18329 
18330 //printf("%d offsets found in total\n", noffset);
18331     if (noffset > 0) {
18332         if (noffset % 2) {
18333             *offset = cpl_array_get_median(offsets);
18334         }
18335         else {
18336             double *a = cpl_malloc(sizeof(double) * noffset);
18337             for (i = 0; i < noffset; i++) {
18338                 a[i] = cpl_array_get_double(offsets, i, NULL);
18339             }
18340             *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18341                        fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18342             cpl_free(a);
18343         }
18344     }
18345     else
18346         status = CPL_ERROR_DATA_NOT_FOUND;
18347 //printf("Median offset: %f\n", *offset);
18348 
18349     cpl_array_delete(offsets);
18350 
18351     return status;
18352 
18353 }
18354 
18355 
18367 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18368 {
18369     cpl_image *source;
18370     int        nx = cpl_image_get_size_x(image);
18371     int        ny = cpl_image_get_size_y(image);
18372     float     *idata;
18373     float     *sdata;
18374     int        i, j, pos;
18375     double     xpos, ypos, xfrac, yfrac;
18376     int        xint, yint;
18377 
18378 
18379     if (fabs(dx) >= nx || fabs(dy) >= ny)
18380         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18381 
18382     source = cpl_image_duplicate(image);
18383     idata = cpl_image_get_data_float(image);
18384     sdata = cpl_image_get_data_float(source);
18385 
18386     /*
18387      * Shift in y
18388      */
18389 
18390     yfrac = - dy - floor(- dy);
18391     xfrac = - dx - floor(- dx);
18392 
18393     for (pos = 0, j = 0; j < ny; j++) {
18394         ypos = j - dy;
18395         yint = floor(ypos);
18396         for (i = 0; i < nx; i++) {
18397             xpos = i - dx;
18398             xint = floor(xpos);
18399             if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18400                 idata[pos] = 0.0;
18401             }
18402             else {
18403                 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18404                            + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18405                            + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
18406                            + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
18407             }
18408             pos++;
18409         }
18410     }
18411 
18412     cpl_image_delete(source);
18413 
18414     return CPL_ERROR_NONE;
18415 }
18416 
18428 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
18429 {
18430 #ifdef CPL_SIZE_FORMAT
18431     cpl_size row;
18432 #else
18433     int row;
18434 #endif
18435 
18436     cpl_table_duplicate_column(slits, "x", slits, "xtop");
18437     cpl_table_add_columns(slits, "x", "xbottom");
18438     cpl_table_divide_scalar(slits, "x", 2);         // Mean x position
18439     cpl_table_subtract_scalar(slits, "x", nx/2);    // Relative to CCD center
18440     cpl_table_multiply_columns(slits, "x", "x");    // Squared
18441 
18442     cpl_table_duplicate_column(slits, "y", slits, "ytop");
18443     cpl_table_add_columns(slits, "y", "ybottom");
18444     cpl_table_divide_scalar(slits, "y", 2);         // Mean y position
18445     cpl_table_subtract_scalar(slits, "y", ny/2);    // Relative to CCD center
18446     cpl_table_multiply_columns(slits, "y", "y");    // Squared
18447 
18448     cpl_table_add_columns(slits, "x", "y");         // Distance from center
18449     cpl_table_get_column_minpos(slits, "x", &row);  // Min distance from center
18450 
18451     cpl_table_erase_column(slits, "x");
18452     cpl_table_erase_column(slits, "y");
18453 
18454     return row;
18455 }
18456 
18476 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 
18477                      double xwidth, double ywidth,
18478                      int dx, double gain, double *o_flux, double *o_err)
18479 {
18480     int    nx      = cpl_image_get_size_x(image);
18481     int    ny      = cpl_image_get_size_y(image);
18482     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18483     int    ytop    = (int)cpl_table_get(slits, "ytop", slit, NULL);
18484     int    ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
18485     int    dy      = ytop - ybottom;
18486     int    xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
18487                             cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
18488     int    xleft   = xcenter - dx;
18489     int    xright  = xcenter + dx + 1;
18490     double area    = xwidth * ywidth; // squared mm
18491     int    npix    = (2*dx + 1) * dy;
18492     int    count   = 0;
18493     float *data    = cpl_image_get_data_float(image);
18494     double flux    = 0.0;
18495     double error   = 0.0;
18496     int    satur   = 60000;
18497     int    x, y;
18498 
18499 
18500     if (cpl_table_has_column(slits, "ywidth")) {
18501         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18502                 * cpl_table_get(slits, "ywidth", slit, NULL);
18503     }
18504 
18505     *o_flux = 0.0;
18506     *o_err = 0.0;
18507 
18508     if (xleft < 0)
18509         xleft = 0;
18510 
18511     if (xleft > nx)
18512         xleft = nx;
18513 
18514     if (xright < 0)
18515         xright = 0;
18516 
18517     if (xright > nx)
18518         xright = nx;
18519 
18520     if (ytop < 0)
18521         ytop = 0;
18522 
18523     if (ytop > ny)
18524         ytop = ny;
18525 
18526     if (ybottom < 0)
18527         ybottom = 0;
18528 
18529     if (ybottom > ny)
18530         ybottom = ny;
18531 
18532     count = (xright - xleft) * (ytop - ybottom);
18533 
18534     if (count == 0)
18535         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18536 
18537     count = 0;
18538 
18539     for (y = ybottom; y < ytop; y++) {
18540         for (x = xleft; x < xright; x++) {
18541             double value = data[x + y * nx];
18542             if (value < satur) {
18543                 flux += value;
18544                 count++;
18545             }
18546         }
18547     }
18548 
18549     if (count == 0)
18550         return CPL_ERROR_DIVISION_BY_ZERO;
18551 
18552     error = sqrt(flux/gain);
18553 
18554     /*
18555      * Flux correction for lost pixels
18556      */
18557 
18558     flux *= (float)npix / count;
18559     error *= (float)npix / count;
18560 
18561     flux /= area;
18562     error /= area;
18563 
18564     *o_flux = flux;
18565     *o_err = error;
18566 
18567     return CPL_ERROR_NONE;
18568 }
18569 
18570 
18593 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
18594                                        double xwidth, double ywidth,
18595                                        double lambda, double startwave, 
18596                                        double dispersion, int dx, double gain, 
18597                                        double *o_flux, double *o_err)
18598 {
18599     int    nx      = cpl_image_get_size_x(image);
18600     int    ny      = cpl_image_get_size_y(image);
18601     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18602     int    dy      = (int)cpl_table_get(slits, "length", slit, NULL);
18603     int    ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
18604     int    ytop    = ybottom + dy;
18605     int    xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
18606     int    xleft   = xcenter - dx;
18607     int    xright  = xcenter + dx + 1;
18608     double area    = xwidth * ywidth;
18609     int    npix    = (2*dx + 1) * dy;
18610     int    count   = 0;
18611     float *data    = cpl_image_get_data_float(image);
18612     double flux    = 0.0;
18613     double error   = 0.0;
18614     int    satur   = 60000;
18615     int    x, y;
18616 
18617 
18618     if (cpl_table_has_column(slits, "ywidth")) {
18619         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18620                 * cpl_table_get(slits, "ywidth", slit, NULL);
18621     }
18622 
18623     *o_flux = 0.0;
18624     *o_err = 0.0;
18625 
18626     if (xleft < 0)
18627         xleft = 0;
18628 
18629     if (xleft > nx)
18630         xleft = nx;
18631 
18632     if (xright < 0)
18633         xright = 0;
18634 
18635     if (xright > nx)
18636         xright = nx;
18637 
18638     if (ytop < 0)
18639         ytop = 0;
18640 
18641     if (ytop > ny)
18642         ytop = ny;
18643 
18644     if (ybottom < 0)
18645         ybottom = 0;
18646 
18647     if (ybottom > ny)
18648         ybottom = ny;
18649 
18650     count = (xright - xleft) * (ytop - ybottom);
18651 
18652     if (count == 0)
18653         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18654 
18655     count = 0;
18656 
18657     for (y = ybottom; y < ytop; y++) {
18658         for (x = xleft; x < xright; x++) {
18659             double value = data[x + y * nx];
18660             if (value < satur) {
18661                 flux += value;
18662                 count++;
18663             }
18664         }
18665     }
18666 
18667     if (count == 0)
18668         return CPL_ERROR_DIVISION_BY_ZERO;
18669 
18670     error = sqrt(flux/gain);
18671 
18672     /*
18673      * Flux correction for lost pixels
18674      */
18675 
18676     flux *= (float)npix / count;
18677     error *= (float)npix / count;
18678     
18679     flux /= area;  
18680     error /= area; 
18681     
18682     *o_flux = flux;
18683     *o_err = error;
18684 
18685     return CPL_ERROR_NONE;
18686 
18687 }
18688 
18689 
18703 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 
18704                        char *label, double *mvalue)
18705 {
18706     int        position   = cpl_table_get_int(slits, "position", slit, NULL);
18707     int        length     = cpl_table_get_int(slits, "length", slit, NULL);
18708     cpl_table *tmp        = cpl_table_extract(table, position, length);
18709 
18710     *mvalue = cpl_table_get_column_median(tmp, label);
18711     cpl_table_delete(tmp);
18712 
18713     if (cpl_error_get_code() != CPL_ERROR_NONE)
18714         return 1;
18715 
18716     return 0;
18717 }
18718 
18719 
18731 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
18732 {
18733       cpl_mask  *kernel   = cpl_mask_new(nx, ny);
18734       cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
18735                                           cpl_image_get_size_y(image),
18736                                           cpl_image_get_type(image));
18737 
18738       cpl_mask_not(kernel);
18739       cpl_image_filter_mask(filtered, image, kernel,
18740                             CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
18741       cpl_mask_delete(kernel);
18742 
18743       return filtered;
18744 }
18745 
18746 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det)
18747 {
18748     int treat_as_lss = 1;
18749     double mxpos = cpl_table_get_column_median(maskslits, "xtop");
18750     double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop");
18751     cpl_size nslits = cpl_table_get_nrow(maskslits);
18752 
18753     //If not all the slits are illuminated, then we cannot say that
18754     //it is lss-like (PIPE-4380)
18755     if(nslits_out_det != 0)
18756         return 0;
18757 
18758     cpl_msg_warning(cpl_func, "Number of slits %"CPL_SIZE_FORMAT, nslits);
18759     for (cpl_size i = 0; i < nslits; i++) {
18760         if (fabs(mxpos-slit_xpos[i]) > 0.01) {
18761             treat_as_lss = 0;
18762             break;
18763         }
18764     }
18765     return treat_as_lss;
18766 }