moses_multiplex.c

00001 /* $Id: moses.c,v 1.35 2009/05/14 11:39:42 cizzo Exp $
00002  *
00003  * This file is part of the MOSES library
00004  * Copyright (C) 2002-2006 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: cizzo $
00023  * $Date: 2009/05/14 11:39:42 $
00024  * $Revision: 1.35 $
00025  * $Name:  $
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 <moses.h>
00040 
00041 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
00042  * even if cpl_tool.h is not public. It should be removed as soon as 
00043  * an image median filtering with generic kernel will be implemented
00044  * in the CPL, or as soon as this module will be moved into the CPL. */
00045 
00046 float cpl_tools_get_median_float(float *, int);
00047 
00048 #define MAX_COLNAME      (80)
00049 
00050 static double default_lines_hi[] = {   /* Default sky line catalog */
00051                     5577.338,          /* for high res data        */
00052                     5889.953,
00053                     5895.923,
00054                     5915.301,
00055                     5932.862,
00056                     5953.420,
00057                     6257.961,
00058                     6287.434,
00059                     6300.304,
00060                     6306.869,
00061                     6363.780,
00062                     6498.729,
00063                     6533.044,
00064                     6553.617,
00065                     6841.945,
00066                     6863.955,
00067                     6870.994,
00068                     6889.288,
00069                     6900.833,
00070                     6912.623,
00071                     6923.220,
00072                     6939.521,
00073                     6969.930,
00074                     7003.858,
00075                     7244.907,
00076                     7276.405,
00077                     7284.439,
00078                     7316.282,
00079                     7329.148,
00080                     7340.885,
00081                     7358.659,
00082                     7571.746,
00083                     7750.640,
00084                     7759.996,
00085                     7794.112,
00086                     7808.467,
00087                     7821.503,
00088                     7841.266,
00089                     7913.708,
00090                     7949.204,
00091                     7964.650,
00092                     7993.332,
00093                     8014.059,
00094                     8310.719,
00095                     8344.602,
00096                     8382.392,
00097                     8399.170,
00098                     8415.231,
00099                     8430.174,
00100                     8452.250,
00101                     8493.389,
00102                     8791.186,
00103                     8827.096,
00104                     8885.850,
00105                     8903.114,
00106                     8943.395,
00107                     8988.366
00108                     };
00109 
00110 static double default_lines_lo[] = {   /* Default sky line catalog */
00111                     5577.338,          /* for low res data         */
00112                     6300.304,
00113                     6863.955,
00114                     7571.746,
00115                     7964.650,
00116                     7993.332
00117                     };
00118 
00119 
00129 /*
00130  * The following macros and function for finding the k-th smallest
00131  * value on a float array will be accessible from cpl_tools once
00132  * this module will be moved into the CPL.
00133  */
00134 
00135 /****
00136 
00137 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00138 
00139 static float kthSmallest(float a[], int n, int k)
00140 {
00141   register int i,j,l,m;
00142   register float x;
00143 
00144   l = 0;
00145   m = n-1;
00146   while (l < m) {
00147     x = a[k];
00148     i = l;
00149     j = m;
00150     do {
00151       while (a[i] < x) {
00152         i++;
00153       }
00154       while (x < a[j]) {
00155         j--;
00156       }
00157       if (i <= j) {
00158         PIX_SWAP(a[i],a[j]);
00159         i++;
00160         j--;
00161       }
00162     } while (i <= j);
00163 
00164     if (j < k) {
00165       l = i;
00166     }
00167     if (k < i) {
00168       m = j;
00169     }
00170 
00171   }
00172   return(a[k]);
00173 }
00174 
00175 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00176 
00177 ****/
00178 
00179 /* 
00180  * Return random number with gaussian distribution (mean = 0, variance = 1)
00181  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00182  * indicating whether to "seed" or not the sequence of generated random 
00183  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00184  * and at further calls the input argument is ignored. This function
00185  * generates two random numbers at each call, returning the first one
00186  * at odd calls, and the second one at even calls.
00187  */
00188 
00189 static void mos_seed(void)
00190 {
00191     srand((unsigned int)time((time_t *)0));
00192 }
00193 
00194 static double mos_randg(int seme)
00195 {
00196     static int doit = 1;
00197     static int gotit = 1;
00198     double x1, x2, w, y1;
00199     static double y2;
00200 
00201     if (gotit && seme) {
00202         mos_seed();
00203         gotit = 0;
00204     }
00205 
00206     if (doit) {
00207         doit = 0;
00208         do {
00209             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00210             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00211             w = x1 * x1 + x2 * x2;
00212         } while (w >= 1.0 || w == 0.0);
00213     
00214         w = sqrt( (-2.0 * log(w)) / w);
00215     
00216         y1 = x1 * w;
00217         y2 = x2 * w;
00218         return y1;
00219     }
00220 
00221     doit = 1;
00222     return y2;
00223 }
00224 
00225 /* 
00226  * This function contained a dependency on the VIMOS library
00227  * (function medianPixelvalue()): it should be removed as soon as an 
00228  * image median filtering with generic kernel will be implemented
00229  * in the CPL. Currently it has been solved by a direct call to
00230  * a cpl_tool function.
00231  */
00232 
00233 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00234                                             int filtsizey, int refrow,
00235                                             int above, int below, int step)
00236 {
00237 
00238   const char *func = "mos_image_general_median_filter";
00239 
00240   cpl_image  *filt_img = NULL;
00241   int         col, row;
00242   float      *buf = NULL;
00243   float      *data;
00244   float      *fdata;
00245   int         upright_y, loleft_y;
00246   int         j;
00247   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00248   int         f2y;
00249   int         nx = cpl_image_get_size_x(ima_in);
00250   int         ny = cpl_image_get_size_y(ima_in);
00251   int         firstRow;
00252 
00253 
00254   if (yIsEven) filtsizey++;
00255 
00256   if (ny <= filtsizey) {
00257     cpl_msg_error(func, 
00258                   "Median filter size: %d, image size: %d", filtsizey, ny);
00259     return NULL;
00260   }
00261 
00262   f2y = filtsizey / 2;
00263 
00264   filt_img = cpl_image_duplicate(ima_in);
00265   buf = cpl_malloc(filtsizey * sizeof(float));
00266   data = cpl_image_get_data(ima_in);
00267   fdata = cpl_image_get_data(filt_img);
00268 
00269   firstRow = refrow - step * (below / step);
00270   if (firstRow < f2y)
00271     firstRow += step;
00272 
00273   for (col = 0; col < nx; col++) {
00274     for (row = firstRow; row < refrow + above; row += step) {
00275       if (row >= ny - f2y)
00276         break;
00277       loleft_y = row - f2y;
00278       upright_y = row + f2y + 1;
00279       for (j = loleft_y; j < upright_y; j++)
00280         buf[j - loleft_y] = data[col + j * nx];
00281 
00282       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00283     }
00284   }
00285 
00286   cpl_free(buf);
00287 
00288   return filt_img;
00289 
00290 }
00291 
00292 
00293 /*
00294  * The following static function is used to find an accurate position
00295  * of a peak within a short interval (however at least 5 pixels long). 
00296  * The basic idea is to find the baricenter of all the pixel values 
00297  * that pass a threshold level between the median value and the maximum
00298  * value within the examined interval (in case such levels are equal,
00299  * the input is considered flat and no position is returned). At least
00300  * minPoints must pass this threshold, or no position is computed. To
00301  * evaluate the significance of the computed baricenter, the variance 
00302  * of the contributing positions (relative to the found baricenter) is 
00303  * also evaluated, and compared with the expected variance for a uniform 
00304  * distribution of positions. If the observed variance is greater than 
00305  * 80% of the variance of the uniform distribution, the found position 
00306  * is rejected.
00307  */
00308 
00309 static int peakPosition(const float *data, int size, float *position,
00310             int minPoints)
00311 {
00312   int    i;
00313   int    count = 0;
00314   float *copy;
00315   float  max, median, level, pos, variance, uniformVariance;
00316   double sum, weights;
00317 
00318 
00319   if (data == NULL)
00320       return 1;
00321 
00322   if (size < 5)         /* Hardcoded, I know... */
00323       return 1;
00324 
00325 
00326   /*
00327    *  Find median level
00328    */
00329 
00330   copy = (float *) cpl_malloc(size*sizeof(float));
00331   for (i = 0; i < size; i++)
00332       copy[i] = data[i];
00333   median = cpl_tools_get_median_float(copy, size);
00334   cpl_free(copy);
00335 
00336 
00337   /*
00338    *  Find max
00339    */
00340 
00341   max = data[0];
00342   for (i = 1; i < size; i++)
00343       if (data[i] > max)
00344           max = data[i];
00345 
00346 
00347   /*
00348    *  If the max equals the median we have a flat input, therefore
00349    *  no peak is found.
00350    */
00351 
00352   if (max-median < 0.00001)
00353       return 1;
00354 
00355 
00356   /*
00357    *  Discrimination level: only pixels with values above this
00358    *  level are considered in baricenter calculation.
00359    */
00360 
00361   level = (max + median) / 2;
00362 
00363 
00364   /*
00365    *  Of the values above this level compute the baricenter and
00366    *  then the variance of the positions used. Note that the weights
00367    *  are taken as the difference between the pixels values and
00368    *  the median level (supposedly the background).
00369    */
00370 
00371   count = 0;
00372   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00373       if (data[i] > level) {
00374           count++;
00375           weights += (data[i] - median);
00376           sum     += i * (data[i] - median);
00377       }
00378   }
00379 
00380 
00381   /*
00382    *  If too few values are above threshold, refuse the position
00383    *  as insignificant
00384    */
00385 
00386   if (count < minPoints)
00387       return 1;
00388 
00389   pos = sum / weights;
00390   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00391       if (data[i] > level) {
00392           weights++;
00393           sum += (i - pos) * (i - pos);
00394       }
00395   }
00396   variance = sqrt(sum / weights);
00397 
00398 
00399  /*
00400   *  The "uniform variance" is the variance that should be obtained
00401   *  in the case of uniform distribution of the points positions in
00402   *  the selected interval. If the real variance is comparable with
00403   *  this value, the peak is considered not found.
00404   */
00405 
00406   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00407 
00408   if (variance > 0.8 * uniformVariance)
00409       return 1;
00410 
00411   *position = pos + 0.5;
00412 
00413   return 0;
00414 }
00415 
00416 
00417 /*
00418  *  The following static function determines the quantity dx to be
00419  *  added to the position of the highest pixel of a fiber profile,
00420  *  to get the true position of the profile maximum. All is needed
00421  *  is the maximum observed value v2 in the profile, and the observed
00422  *  values v1 and v3 of the previous and the next pixels in the profile.
00423  *  
00424  *  The following ratio is defined:
00425  *  
00426  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00427  *      
00428  *  This is a conventional ratio that wouldn't diverge for any set of
00429  *  pixel values, and that would not depend on the presence of background
00430  *  (with the assumption that the background level is the same for the 
00431  *  three pixels). R has also been chosen in such a way that its value
00432  *  is already quite close to the real dx. It should be noted that the
00433  *  following condition should be fulfilled:
00434  *
00435  *           v1  <= v2   and   v3  <  v2
00436  *  or
00437  *           v1  <  v2   and   v3  <=  v2
00438  *
00439  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00440  *  boundary cases, one has:
00441  *
00442  *           v2 = v1   and   R = dx = -0.5
00443  *           v2 = v3   and   R = dx =  0.5
00444  *
00445  *  Another special case is when the observed pixel values are perfectly
00446  *  symmetrical:
00447  *
00448  *           v1 = v3   and   R = dx =  0.0
00449  *
00450  *  In all the intermediate cases the relation between R and dx depends
00451  *  on the shape of the fiber profile, that has been determined elsewhere.
00452  *  Using the accurate reconstruction of the fiber profile obtained by 
00453  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00454  *  that R differs from dx always less than 0.01 pixels. If the condition
00455  *
00456  *           v1  <= v2   and   v3  <  v2
00457  *  or
00458  *           v1  <  v2   and   v3  <=  v2
00459  *
00460  *  is not fulfilled, then this function returns the value 2.0.
00461  */
00462 
00463 static double values_to_dx(double v1, double v2, double v3)
00464 {
00465 
00466   static double epsilon = 0.00000001;
00467   double        r       = 2.0;
00468 
00469 
00470   if (v1 > v2 || v3 > v2)
00471     return r;
00472 
00473   if (2 * v2 - v1 - v3 < epsilon)
00474     return r;
00475 
00476   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00477 
00478   return r;
00479 
00480 }
00481 
00482 
00483 /*
00484  * The following static function passes a min filter of given box
00485  * size on the data buffer. The box size must be a positive odd integer.
00486  */
00487 
00488 static float *min_filter(float *buffer, int length, int size)
00489 {
00490     float *minf  = cpl_calloc(length, sizeof(float));
00491     float  min;
00492     int    start = size / 2;
00493     int    end   = length - size / 2;
00494     int    i, j;
00495 
00496 
00497     for (i = start; i < end; i++) {
00498         min = buffer[i-start];
00499         for (j = i - start + 1; j <= i + start; j++)
00500             if (min > buffer[j])
00501                 min = buffer[j];
00502         minf[i] = min;
00503     }
00504 
00505     for (i = 0; i < start; i++)
00506         minf[i] = minf[start];
00507 
00508     for (i = end; i < length; i++)
00509         minf[i] = minf[end-1];
00510 
00511     return minf;
00512 }
00513 
00514 
00515 /*
00516  * The following static function passes a max filter of given box
00517  * size on the data buffer. The box size must be a positive odd integer.
00518  */
00519  
00520 static float *max_filter(float *buffer, int length, int size)
00521 {
00522     float *maxf  = cpl_calloc(length, sizeof(float));
00523     float  max;
00524     int    start = size / 2;
00525     int    end   = length - size / 2;
00526     int    i, j;
00527 
00528 
00529     for (i = start; i < end; i++) {
00530         max = buffer[i-start];
00531         for (j = i - start + 1; j <= i + start; j++)
00532             if (max < buffer[j])
00533                 max = buffer[j];
00534         maxf[i] = max;
00535     }
00536 
00537     for (i = 0; i < start; i++)
00538         maxf[i] = maxf[start];
00539 
00540     for (i = end; i < length; i++)
00541         maxf[i] = maxf[end-1];
00542 
00543     return maxf;
00544 }
00545 
00546 
00547 /*
00548  * The following static function passes a running average of given box
00549  * size on the data buffer. The box size must be a positive odd integer.
00550  */
00551  
00552 static float *smo_filter(float *buffer, int length, int size)
00553 {
00554     float *smof  = cpl_calloc(length, sizeof(float));
00555     double sum;
00556     int    start = size / 2;
00557     int    end   = length - size / 2;
00558     int    i, j;
00559 
00560 
00561     for (i = start; i < end; i++) {
00562         sum = 0.0;
00563         for (j = i - start; j <= i + start; j++)
00564             sum += buffer[j];
00565         smof[i] = sum / size;
00566     }
00567 
00568     for (i = 0; i < start; i++)
00569         smof[i] = smof[start];
00570 
00571     for (i = end; i < length; i++)
00572         smof[i] = smof[end-1];
00573 
00574     return smof;
00575 }
00576 
00577 /*
00578  * The following two static functions are used to read and write from the 
00579  * global distortion table the different model components. Conventionally
00580  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00581  * storage for model coefficients, and these functions guarantee that the
00582  * coefficients are read in and written out correctly, independent on their
00583  * physical meaning. The first 6 table rows are a description of the IDS
00584  * coefficients, followed by a row containing only the used reference 
00585  * wavelength. The remaining 3 are a description of the spectral curvature.
00586  * The first row is a description of coefficient c0, the second of coefficient
00587  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00588  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00589  * bivariate polynomialx on x,y mask coordinates. If the input table
00590  * to the write routine is NULL, it is allocated and initialised. Also
00591  * the input polynomial could be NULL, and nothing would be written to 
00592  * the table. If both pointers are NULL the function is basically a
00593  * constructor of the global distortion table.
00594  */
00595 
00596 static cpl_polynomial *read_global_distortion(cpl_table *global, int row)
00597 {
00598     cpl_polynomial *poly = NULL;
00599     int             p[2];
00600     int             degree = 2;
00601     int             null;
00602     double          coeff;
00603 
00604     char   name[MAX_COLNAME];
00605 
00606 
00607     for (p[0] = 0; p[0] <= degree; p[0]++) {
00608         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00609             snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00610             coeff = cpl_table_get_double(global, name, row, &null);
00611             if (null)
00612                 continue;
00613             if (poly == NULL)
00614                 poly = cpl_polynomial_new(2);
00615             cpl_polynomial_set_coeff(poly, p, coeff);
00616         }
00617     }
00618 
00619     return poly;
00620 }
00621 
00622 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00623                                           cpl_polynomial *poly)
00624 {
00625     cpl_table *table;
00626     int        p[2];
00627     int        degree = 2;
00628     int        nrow = 10;
00629 
00630     char       name[MAX_COLNAME];
00631 
00632 
00633     if (global) {
00634         table = global;
00635     }
00636     else {
00637         table = cpl_table_new(nrow);
00638         for (p[0] = 0; p[0] <= degree; p[0]++) {
00639             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00640                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00641                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00642             }
00643         }
00644     }
00645 
00646     if (poly) {
00647         for (p[0] = 0; p[0] <= degree; p[0]++) {
00648             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00649                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00650                 cpl_table_set_double(table, name, row, 
00651                                      cpl_polynomial_get_coeff(poly, p));
00652             }
00653         }
00654     }
00655 
00656     return table;
00657 }
00658 
00659 
00660 /*
00661  * The following static function is performing a robust linear fit
00662  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00663  *
00664  *  ----> y = a + b * x
00665  *
00666  * This function return 0 on success.
00667  */
00668 
00669 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00670 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00671                            double *abdev)
00672 {
00673     cpl_vector *vx;
00674     cpl_vector *vy;
00675     cpl_vector *va;
00676 
00677     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00678     double  sx, sy, sxy, sxx, chisq;
00679     double *arr;
00680     double  aa_ls, bb_ls;
00681     double *x;
00682     double *y;
00683     int     np;
00684     int     iter;
00685     int     max_iterate = 30;
00686     int     i;
00687 
00688 
00689     np = cpl_bivector_get_size(list);
00690     vx = cpl_bivector_get_x(list);
00691     vy = cpl_bivector_get_y(list);
00692     x = cpl_vector_get_data(vx);
00693     y = cpl_vector_get_data(vy);
00694 
00695     sx = sy = sxx = sxy = 0.00;
00696     for (i = 0; i < np; i++) {
00697         sx  += x[i];
00698         sy  += y[i];
00699         sxy += x[i] * y[i];
00700         sxx += x[i] * x[i];
00701     }
00702 
00703     del = np * sxx - sx * sx;
00704     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00705     bb_ls = bb = (np * sxy - sx * sy) / del;
00706 
00707     chisq = 0.00;
00708     for (i = 0; i < np; i++) {
00709         temp = y[i] - (aa+bb*x[i]);
00710         temp *= temp;
00711         chisq += temp;
00712     }
00713 
00714     va = cpl_vector_new(np);
00715     arr = cpl_vector_get_data(va);
00716     sigb = sqrt(chisq/del);
00717     b1 = bb;
00718 
00719     bcomp = b1;
00720     sum = 0.00;
00721     for (i = 0; i < np; i++) {
00722         arr[i] = y[i] - bcomp * x[i];
00723     }
00724     aa = cpl_vector_get_median_const(va);
00725     abdevt = 0.0;
00726     for (i = 0; i < np; i++) {
00727         d = y[i] - (bcomp * x[i] + aa);
00728         abdevt += fabs(d);
00729         if (y[i] != 0.0) 
00730             d /= fabs(y[i]);
00731         if (fabs(d) > 1e-7) 
00732             sum += (d >= 0.0 ? x[i] : -x[i]);
00733     }
00734     f1 = sum;
00735 
00736     b2 = bb + SEGNO(3.0 * sigb, f1);
00737 
00738     bcomp = b2;
00739     sum = 0.00;
00740     for (i = 0; i < np; i++) {
00741         arr[i] = y[i] - bcomp * x[i];
00742     }
00743     aa = cpl_vector_get_median_const(va);
00744     abdevt = 0.0;
00745     for (i = 0; i < np; i++) {
00746         d = y[i] - (bcomp * x[i] + aa);
00747         abdevt += fabs(d);
00748         if (y[i] != 0.0) 
00749             d /= fabs(y[i]);
00750         if (fabs(d) > 1e-7) 
00751             sum += (d >= 0.0 ? x[i] : -x[i]);
00752     }
00753     f2 = sum;
00754 
00755     if (fabs(b2-b1)<1e-7) {
00756         *a = aa;
00757         *b = bb;
00758         *abdev = abdevt / (double)np;
00759         cpl_vector_delete(va);
00760         return 0;
00761     }
00762 
00763     iter = 0;
00764     while (f1*f2 > 0.0) {
00765         bb = 2.0*b2-b1;
00766         b1 = b2;
00767         f1 = f2;
00768         b2 = bb;
00769 
00770         bcomp = b2;
00771         sum = 0.00;
00772         for (i = 0; i < np; i++) {
00773             arr[i] = y[i] - bcomp * x[i];
00774         }
00775         aa = cpl_vector_get_median_const(va);
00776         abdevt = 0.0;
00777         for (i = 0; i < np; i++) {
00778             d = y[i] - (bcomp * x[i] + aa);
00779             abdevt += fabs(d);
00780             if (y[i] != 0.0) 
00781                 d /= fabs(y[i]);
00782             if (fabs(d) > 1e-7) 
00783                 sum += (d >= 0.0 ? x[i] : -x[i]);
00784         }
00785         f2 = sum;
00786         iter++;
00787         if (iter >= max_iterate) 
00788             break;
00789     }
00790     if (iter >= max_iterate) {
00791         *a = aa_ls;
00792         *b = bb_ls;
00793         *abdev = -1.0;
00794         cpl_vector_delete(va);
00795         return 1;
00796     }
00797 
00798     sigb = 0.01 * sigb;
00799     while (fabs(b2-b1) > sigb) {
00800         bb = 0.5 * (b1 + b2);
00801         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00802             break;
00803         bcomp = bb;
00804         sum = 0.0;
00805         for (i = 0; i < np; i++) {
00806             arr[i] = y[i] - bcomp * x[i];
00807         }
00808         aa = cpl_vector_get_median_const(va);
00809         abdevt = 0.0;
00810         for (i = 0; i < np; i++) {
00811             d = y[i] - (bcomp * x[i] + aa);
00812             abdevt += fabs(d);
00813             if (y[i] != 0.0) 
00814                 d /= fabs(y[i]);
00815             if (fabs(d) > 1e-7) 
00816                 sum += (d >= 0.0 ? x[i] : -x[i]);
00817         }
00818         f = sum;
00819 
00820         if (f*f1 >= 0.0) {
00821             f1=f;
00822             b1=bb;
00823         } 
00824         else {
00825             f2=f;
00826             b2=bb;
00827         }
00828     }
00829     cpl_vector_delete(va);
00830     *a = aa;
00831     *b = bb;
00832     *abdev = abdevt / np;
00833     return 0;
00834 }
00835 #undef SEGNO
00836 
00837 /*      
00838  * The following static function applies the Hough transform from a table
00839  * of points to another table of points. Given the points p_i = (x_i,y_i)
00840  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00841  * and Y = y_i - X*x_i is computed and added to the output table for each
00842  * p_i, p_j pair. This means that if the input table has N points, the
00843  * output table has N*(N-1)/2 points.
00844  */
00845     
00846 /* static */
00847 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00848 {
00849     cpl_table *output;
00850     double    *xdata;
00851     double    *ydata;
00852     double    *xodata;
00853     double    *yodata;
00854     int        npoints;
00855     int        opoints;
00856     int        i, j, k;
00857 
00858 
00859     npoints = cpl_table_get_nrow(table);
00860     opoints = npoints*(npoints-1)/2;
00861 
00862     output = cpl_table_new(opoints);
00863     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00864     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00865     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00866     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00867 
00868     xodata = cpl_table_get_data_double(output, "m");
00869     yodata = cpl_table_get_data_double(output, "q");
00870     
00871     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00872     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00873 
00874     xdata = cpl_table_get_data_double(table, "x");
00875     ydata = cpl_table_get_data_double(table, "y");
00876 
00877     k = 0;
00878     for (i = 0; i < npoints; i++) {
00879         for (j = i+1; j < npoints; j++) {
00880             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00881             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00882             k++;
00883         }
00884     }
00885 
00886     if (k != opoints)
00887         printf("Assert k = %d, expected %d\n", k, opoints);
00888 
00889     cpl_table_erase_column(table, "x");
00890     cpl_table_erase_column(table, "y");
00891 
00892     return output;
00893 }
00894 
00895 
00896 /*
00897  * The following static function is performing the spectral
00898  * extraction for the function mos_extract_objects()
00899  */
00900 
00901 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00902                            cpl_image *extracted, cpl_image *sky, 
00903                            cpl_image *error, int nobjects, int extraction, 
00904                            double ron, double conad, int ncomb)
00905 {
00906 
00907   cpl_vector *vprofile;
00908   cpl_matrix *kernel;
00909   cpl_image  *smowin;
00910 
00911   int i, j;
00912   int specLen;
00913   int numRows;
00914   int index;
00915   int iter;
00916   int maxIter   = 2;         /* Not less than 2 !!! */
00917   int smoothBox = 31;        /* Not less than 5 !!! */
00918   double nsigma = 5.0;
00919 
00920   double sumWeight, sum, sumSky, sumProf, variance, weight;
00921   double *profile;
00922   double *buffer;
00923   float  *edata;
00924   float  *ekdata;
00925   float  *endata;
00926   float  *sdata;
00927   float  *kdata;
00928   float  *fdata;
00929 
00930   double value;
00931 
00932 
00933   specLen = cpl_image_get_size_x(sciwin);
00934   numRows = cpl_image_get_size_y(sciwin);
00935 
00936   edata = cpl_image_get_data(extracted);
00937   edata += nobjects * specLen;
00938 
00939   ekdata = cpl_image_get_data(sky);
00940   ekdata += nobjects * specLen;
00941 
00942   endata = cpl_image_get_data(error);
00943   endata += nobjects * specLen;
00944 
00945   sdata = cpl_image_get_data(sciwin);
00946   kdata = cpl_image_get_data(skywin);
00947 
00948 
00949   /*
00950    * Initial spectrum estimate
00951       if (sdata[i + j * specLen] > 0.0)
00952    */
00953 
00954   if (extraction && numRows > 5) {
00955       kernel = cpl_matrix_new(3, 3);
00956       cpl_matrix_fill(kernel, 1.0);
00957       smowin = cpl_image_filter_median(sciwin, kernel);
00958       cpl_matrix_delete(kernel);
00959       fdata = cpl_image_get_data(smowin);
00960       for (i = 0; i < specLen; i++)
00961         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00962             edata[i] += fdata[i + j * specLen];
00963       cpl_image_delete(smowin);
00964   }
00965   else {
00966       for (i = 0; i < specLen; i++)
00967         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00968             edata[i] += sdata[i + j * specLen];
00969   }
00970 
00971   if (extraction) {
00972 
00973     profile = cpl_calloc(specLen * numRows, sizeof(double));
00974     buffer  = cpl_calloc(specLen, sizeof(double));
00975 
00976     for (iter = 0; iter < maxIter; iter++) {
00977 
00978       /*
00979        * Normalised spatial profile
00980        */
00981 
00982       for (i = 0; i < specLen; i++) {
00983         for (j = 0; j < numRows; j++) {
00984           index = i + j * specLen;
00985 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00986           if (fabs(edata[i]) > 0.00001)
00987             profile[index] = sdata[index] / edata[i];
00988           else
00989             profile[index] = 0.0;
00990         }
00991       }
00992 
00993       for (j = 0; j < numRows; j++) {
00994 
00995         /*
00996          * Smooth each row in the dispersion direction, and enforce positivity
00997          */
00998 
00999         for (i = 0; i < specLen - smoothBox; i++) {
01000           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01001           value = cpl_vector_get_median_const(vprofile);
01002           cpl_vector_unwrap(vprofile);
01003           if (value < 0)
01004             value = 0.0;
01005           buffer[i + smoothBox / 2] = value;
01006         }
01007 
01008         /*
01009          * Replace the end portions (i.e., not median filtered) with a mean
01010          */
01011 
01012         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01013         value = cpl_vector_get_mean(vprofile);
01014         cpl_vector_unwrap(vprofile);
01015 
01016         if (value < 0)
01017             value = 0.0;
01018 
01019         for (i = 0; i < smoothBox / 2; i++)
01020           buffer[i] = value;
01021 
01022         vprofile = cpl_vector_wrap(smoothBox / 2, 
01023                                    profile + specLen - smoothBox/2 + j*specLen);
01024         value = cpl_vector_get_mean(vprofile);
01025         cpl_vector_unwrap(vprofile);
01026 
01027         if (value < 0)
01028             value = 0.0;
01029 
01030         for (i = 0; i < smoothBox / 2; i++)
01031           buffer[i + specLen - smoothBox / 2] = value;
01032 
01033         for (i = 0; i < specLen; i++)
01034           profile[i + j * specLen] = buffer[i];
01035 
01036       }
01037 
01038       /*
01039        * Enforce normalization of spatial profile after smoothing
01040        */
01041 
01042       for (i = 0; i < specLen; i++) {
01043         for (j = 0, value = 0.0; j < numRows; j++)
01044           value += profile[i + j * specLen];
01045         if (value > 0.00001)
01046           for (j = 0; j < numRows; j++)
01047             profile[i + j * specLen] /= value;
01048         else
01049           for (j = 0; j < numRows; j++)
01050             profile[i + j * specLen] = 0.0;
01051       }
01052 
01053 
01054       /*
01055        * Optimal extraction
01056        */
01057 
01058       for (i = 0; i < specLen; i++) {
01059         sum = 0.0;
01060         sumSky = 0.0;
01061         sumWeight = 0.0;
01062         sumProf = 0.0;
01063         for (j = 0; j < numRows; j++) {
01064           index = i + j * specLen;
01065 /*        
01066 if (sdata[index] > 0.0) {
01067 */
01068             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01069                      / conad;
01070             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01071             value = sdata[index] - edata[i] * profile[index];
01072             if (fabs(value) / sqrt(variance) < nsigma) {
01073               weight = 1000000 * profile[index] / variance;
01074               sum += weight * sdata[index];
01075               sumSky += weight * kdata[index];
01076               sumWeight += weight * profile[index];
01077               sumProf += profile[index];
01078             }
01079 /*
01080 }      
01081 */
01082         }
01083 
01084         if (sumWeight > 0.00001) {
01085           edata[i] = sum / sumWeight;
01086           ekdata[i] = sumSky / sumWeight;
01087           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01088         }
01089         else {
01090 /*
01091           edata[i] = 0.0;
01092           ekdata[i] = 0.0;
01093           endata[i] = 0.0;
01094 */
01095           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01096         }
01097       }
01098     }
01099     cpl_free(profile);
01100     cpl_free(buffer);
01101   }
01102   else {
01103 
01104     /*
01105      * Add sky estimation for the simple aperture extraction.
01106         if (kdata[i + j * specLen] > 0.0)
01107      */
01108 
01109     for (i = 0; i < specLen; i++)
01110       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01111           ekdata[i] += kdata[i + j * specLen];
01112 
01113     /*
01114      * Add error estimation for the simple aperture extraction.
01115      */
01116 
01117     for (i = 0; i < specLen; i++)
01118       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01119 
01120   }
01121 
01122 }
01123 
01124 
01171 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01172                                  cpl_table *ids, cpl_table *crv, 
01173                                  double reference)
01174 {
01175     const char *func = "mos_global_distortion";
01176 
01177     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01178 
01179     cpl_table      *global = NULL;
01180     cpl_table      *coeff;
01181     cpl_table      *dummy;
01182     cpl_vector     *ci;
01183     cpl_vector     *xmask;
01184     cpl_vector     *ymask;
01185     cpl_bivector   *mask;
01186     cpl_vector     *xccd;
01187     cpl_vector     *yccd;
01188     cpl_bivector   *ccd;
01189     cpl_polynomial *poly;
01190     double         *xtop;
01191     double         *ytop;
01192     double         *xbottom;
01193     double         *ybottom;
01194     double         *mxtop;
01195     double         *mytop;
01196     double         *mxbottom;
01197     double         *mybottom;
01198     int            *position;
01199     int            *length;
01200     int            *slit_id;
01201     int            *mslit_id;
01202     int             nslits, nmaskslits, npoints;
01203     int             order;
01204     int             i, j;
01205     int             minslit = 6;    // 12;
01206 
01207 
01208 /* *+
01209 printf("error1: %s\n", cpl_error_get_message());
01210 +* */
01211     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01212         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01213         return NULL;
01214     }
01215 /* *+
01216 printf("error1a: %s\n", cpl_error_get_message());
01217 +* */
01218 
01219     nslits = cpl_table_get_nrow(slits);
01220 /* *+
01221 printf("error1b: %s\n", cpl_error_get_message());
01222 +* */
01223 
01224     if (nslits < minslit) {
01225         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01226                         "distortion model determination", nslits, minslit);
01227         return NULL;
01228     }
01229 /* *+
01230 printf("error1c: %s\n", cpl_error_get_message());
01231 +* */
01232 
01233     nmaskslits = cpl_table_get_nrow(maskslits);
01234 
01235     length   = cpl_table_get_data_int(slits, "length");
01236     position = cpl_table_get_data_int(slits, "position");
01237     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01238     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01239     xtop     = cpl_table_get_data_double(slits, "xtop");
01240     ytop     = cpl_table_get_data_double(slits, "ytop");
01241     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01242     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01243     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01244     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01245     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01246     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01247 
01248 
01249     /*
01250      * Global IDS
01251      */
01252 
01253     coeff = cpl_table_new(nslits);
01254     cpl_table_copy_structure(coeff, ids);
01255     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01256     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01257     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01258     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01259 
01260 /* *+
01261 printf("error2: %s\n", cpl_error_get_message());
01262 +* */
01263     for (i = 0; i < nslits; i++) {
01264         for (j = 0; j < nmaskslits; j++) {
01265             if (slit_id[i] == mslit_id[j]) {
01266                 cpl_table_set_double(coeff, "xmask", i,
01267                                      (mxtop[j] + mxbottom[j]) / 2);
01268                 cpl_table_set_double(coeff, "ymask", i,
01269                                      (mytop[j] + mybottom[j]) / 2);
01270             }
01271         }
01272     }
01273 
01274     if (cpl_table_has_invalid(coeff, "xmask")) {
01275         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01276         cpl_table_delete(coeff);
01277         return NULL;
01278     }
01279 
01280     for (i = 0; i < nslits; i++) {
01281         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01282         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01283     }
01284 
01285 /* *+
01286 printf("error3: %s\n", cpl_error_get_message());
01287 +* */
01288     for (i = 0; i < nslits; i++) {
01289 
01290         if (length[i] == 0)
01291             continue;
01292 
01293         cpl_table_and_selected_window(ids, position[i], length[i]);
01294         dummy = cpl_table_extract_selected(ids);
01295         for (j = 0; j < 6; j++) {
01296             if (cpl_table_has_column(dummy, clab[j])) {
01297                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01298                     cpl_table_set_double(coeff, clab[j], i, 
01299                          cpl_table_get_column_median(dummy, clab[j]));
01300                 }
01301             }
01302         }
01303 
01304         cpl_table_delete(dummy);
01305         cpl_table_select_all(ids);
01306             
01307     }
01308 
01309 /* *+
01310 printf("error4: %s\n", cpl_error_get_message());
01311 +* */
01312     for (j = 0; j < 6; j++) {
01313         if (cpl_table_has_column(coeff, clab[j])) {
01314             cpl_table_and_selected_invalid(coeff, clab[j]);
01315 
01316             if (cpl_table_not_selected(coeff))
01317                 dummy = cpl_table_extract_selected(coeff);
01318             else
01319                 break;
01320 
01321             npoints = cpl_table_get_nrow(dummy);
01322 
01323             if (npoints >= 6) {
01324 
01325                 if (npoints >= 12)
01326                     order = 2;
01327                 else
01328                     order = 1;
01329                    
01330                 ci = cpl_vector_wrap(npoints,
01331                                      cpl_table_get_data_double(dummy, clab[j]));
01332                 if (j) {
01333                     xccd = cpl_vector_wrap(npoints,
01334                                      cpl_table_get_data_double(dummy, "xccd"));
01335                     yccd = cpl_vector_wrap(npoints,
01336                                      cpl_table_get_data_double(dummy, "yccd"));
01337                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01338 
01339 /* %%% */
01340                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01341 
01342                     cpl_bivector_unwrap_vectors(ccd);
01343                     cpl_vector_unwrap(xccd);
01344                     cpl_vector_unwrap(yccd);
01345                     cpl_vector_unwrap(ci);
01346                 }
01347                 else {
01348                     xmask = cpl_vector_wrap(npoints,
01349                                      cpl_table_get_data_double(dummy, "xmask"));
01350                     ymask = cpl_vector_wrap(npoints,
01351                                      cpl_table_get_data_double(dummy, "ymask"));
01352                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01353 
01354 /* %%% */
01355                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01356 
01357                     cpl_bivector_unwrap_vectors(mask);
01358                     cpl_vector_unwrap(xmask);
01359                     cpl_vector_unwrap(ymask);
01360                     cpl_vector_unwrap(ci);
01361                 }
01362             }
01363             else {
01364                 int p[2] = {0, 0};
01365                 poly = cpl_polynomial_new(2);
01366                 cpl_polynomial_set_coeff(poly, p, 
01367                                cpl_table_get_column_median(dummy, clab[j]));
01368             }
01369 
01370             cpl_table_delete(dummy);
01371 
01372             global = write_global_distortion(global, j, poly);
01373 
01374             cpl_polynomial_delete(poly);
01375 
01376             cpl_table_select_all(coeff);
01377         }
01378     }
01379 
01380 /* *+
01381 printf("error5: %s\n", cpl_error_get_message());
01382 +* */
01383     cpl_table_delete(coeff);
01384 /* *+
01385 printf("error6: %s\n", cpl_error_get_message());
01386 +* */
01387 
01388 
01389     /*
01390      * Add model's reference wavelength
01391      */
01392 
01393     cpl_table_set_double(global, "a00", 6, reference);
01394 
01395 
01396     /*
01397      * Global curvature model
01398      */
01399 
01400     coeff = cpl_table_duplicate(crv);
01401     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01402     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01403     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01404     npoints = cpl_table_get_nrow(coeff);
01405 
01406 /* *+
01407 printf("error7: %s\n", cpl_error_get_message());
01408 +* */
01409     for (i = 0; i < npoints; i++) {
01410         for (j = 0; j < nmaskslits; j++) {
01411             if (slit_id[i] == mslit_id[j]) {
01412                 if (i%2) {
01413                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01414                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01415                 }
01416                 else {
01417                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01418                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01419                 }
01420             }
01421         }
01422     }
01423 
01424 /* *+
01425 printf("error8: %s\n", cpl_error_get_message());
01426 +* */
01427     if (cpl_table_has_invalid(coeff, "xmask")) {
01428         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01429         cpl_table_delete(coeff);
01430         return NULL;
01431     }
01432 
01433 /* *+
01434 printf("error9: %s\n", cpl_error_get_message());
01435 +* */
01436     for (j = 0; j < 3; j++) {
01437         if (cpl_table_has_column(coeff, clab[j])) {
01438             cpl_table_and_selected_invalid(coeff, clab[j]);
01439 
01440             if (cpl_table_not_selected(coeff))
01441                 dummy = cpl_table_extract_selected(coeff);
01442             else
01443                 break;
01444 
01445             npoints = cpl_table_get_nrow(dummy);
01446 
01447             if (npoints >= 6) {
01448 
01449                 if (npoints >= 12)
01450                     order = 2;
01451                 else
01452                     order = 1;
01453 
01454                 ci = cpl_vector_wrap(npoints,
01455                                      cpl_table_get_data_double(dummy, clab[j]));
01456                 xmask = cpl_vector_wrap(npoints,
01457                                      cpl_table_get_data_double(dummy, "xmask"));
01458                 ymask = cpl_vector_wrap(npoints,
01459                                      cpl_table_get_data_double(dummy, "ymask"));
01460                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01461 
01462                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01463 
01464                 cpl_bivector_unwrap_vectors(mask);
01465                 cpl_vector_unwrap(ci);
01466                 cpl_vector_unwrap(xmask);
01467                 cpl_vector_unwrap(ymask);
01468             }
01469             else {
01470                 int p[2] = {0, 0};
01471                 poly = cpl_polynomial_new(2);
01472                 cpl_polynomial_set_coeff(poly, p,
01473                                cpl_table_get_column_median(dummy, clab[j]));
01474             }
01475 
01476             cpl_table_delete(dummy);
01477 
01478             global = write_global_distortion(global, j + 7, poly);
01479 
01480             cpl_polynomial_delete(poly);
01481             cpl_table_select_all(coeff);
01482         }
01483     }
01484 
01485 /* *+
01486 printf("error10: %s\n", cpl_error_get_message());
01487 +* */
01488     cpl_table_delete(coeff);
01489 /* *+
01490 printf("error11: %s\n", cpl_error_get_message());
01491 +* */
01492 
01493     return global;
01494 
01495 }
01496 
01497 
01535 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01536                                    int ysize)
01537 {
01538     const char *func = "mos_build_slit_location";
01539 
01540     cpl_propertylist *sort_col;
01541     cpl_polynomial   *ids0;
01542     cpl_polynomial   *crv[3];
01543     cpl_polynomial   *loc_crv;
01544     cpl_vector       *point;
01545     cpl_table        *slits;
01546     int               nslits;
01547     int              *slit_id;
01548     double           *dpoint;
01549     double           *xtop;
01550     double           *ytop;
01551     double           *xbottom;
01552     double           *ybottom;
01553     double           *mxtop;
01554     double           *mytop;
01555     double           *mxbottom;
01556     double           *mybottom;
01557     int               i, j;
01558 
01559 
01560     if (global == NULL || maskslits == NULL) {
01561         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01562         return NULL;
01563     }
01564 
01565     nslits   = cpl_table_get_nrow(maskslits);
01566     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01567     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01568     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01569     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01570     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01571 
01572     slits = cpl_table_duplicate(maskslits);
01573 
01574     xtop    = cpl_table_get_data_double(slits, "xtop");
01575     ytop    = cpl_table_get_data_double(slits, "ytop");
01576     xbottom = cpl_table_get_data_double(slits, "xbottom");
01577     ybottom = cpl_table_get_data_double(slits, "ybottom");
01578 
01579     ids0 = read_global_distortion(global, 0);
01580     crv[0] = read_global_distortion(global, 7);
01581     crv[1] = read_global_distortion(global, 8);
01582     crv[2] = read_global_distortion(global, 9);
01583 
01584     loc_crv = cpl_polynomial_new(1);
01585 
01586     point = cpl_vector_new(2);
01587     dpoint = cpl_vector_get_data(point);
01588 
01589     for (i = 0; i < nslits; i++) {
01590         dpoint[0] = mxtop[i];
01591         dpoint[1] = mytop[i];
01592 
01593         xtop[i] = cpl_polynomial_eval(ids0, point);
01594 
01595         for (j = 0; j < 3; j++)
01596             if (crv[j])
01597                 cpl_polynomial_set_coeff(loc_crv, &j, 
01598                                          cpl_polynomial_eval(crv[j], point));
01599 
01600         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01601 
01602         dpoint[0] = mxbottom[i];
01603         dpoint[1] = mybottom[i];
01604         xbottom[i] = cpl_polynomial_eval(ids0, point);
01605 
01606         for (j = 0; j < 3; j++)
01607             if (crv[j])
01608                 cpl_polynomial_set_coeff(loc_crv, &j,
01609                                          cpl_polynomial_eval(crv[j], point));
01610 
01611         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01612     }
01613 
01614     cpl_vector_delete(point);
01615     cpl_polynomial_delete(ids0);
01616     cpl_polynomial_delete(loc_crv);
01617     for (j = 0; j < 3; j++)
01618         cpl_polynomial_delete(crv[j]);
01619 
01620     sort_col = cpl_propertylist_new();
01621     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01622     cpl_table_sort(slits, sort_col);
01623     cpl_table_sort(maskslits, sort_col);
01624     cpl_propertylist_delete(sort_col);
01625 
01626     /*
01627      * Eliminate slits which are _entirely_ outside the CCD
01628      */
01629 
01630     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01631     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01632     cpl_table_erase_selected(slits);
01633 
01634     nslits = cpl_table_get_nrow(slits);
01635 
01636     if (nslits == 0) {
01637         cpl_msg_warning(func, "No slits found on the CCD");
01638         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01639         cpl_table_delete(slits);
01640         return NULL;
01641     }
01642 
01643     if (nslits > 1)
01644         cpl_msg_info(func, "Slit location: %d slits are entirely or partially "
01645                      "contained in CCD", nslits);
01646     else
01647         cpl_msg_info(func, "Slit location: %d slit is entirely or partially "
01648                      "contained in CCD", nslits);
01649 
01650     return slits;
01651 
01652 }
01653 
01654 
01681 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01682                                 cpl_table *slits)
01683 {
01684     const char *func = "mos_build_curv_coeff";
01685 
01686     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01687                                                  /* Max order is 5 */
01688 
01689     cpl_polynomial *crv[3];
01690     cpl_vector     *point;
01691     cpl_table      *polytraces;
01692     double         *dpoint;
01693     double         *xtop;
01694     double         *ytop;
01695     double         *xbottom;
01696     double         *ybottom;
01697     int            *slit_id;
01698     int            *valid_id;
01699     int             nslits, nvalid;
01700     int             found;
01701     int             i, j, k;
01702 
01703 
01704     if (global == NULL || slits == NULL || maskslits == NULL) {
01705         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01706         return NULL;
01707     }
01708 
01709     nslits  = cpl_table_get_nrow(maskslits);
01710     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01711     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01712     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01713     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01714     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01715 
01716     polytraces = cpl_table_new(2*nslits);
01717     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01718     for (i = 0; i < 3; i++)
01719         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01720 
01721     crv[0] = read_global_distortion(global, 7);
01722     crv[1] = read_global_distortion(global, 8);
01723     crv[2] = read_global_distortion(global, 9);
01724 
01725     point = cpl_vector_new(2);
01726     dpoint = cpl_vector_get_data(point);
01727 
01728     for (i = 0; i < nslits; i++) {
01729         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01730 
01731             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01732 
01733             if (j) {
01734                 dpoint[0] = xbottom[i];
01735                 dpoint[1] = ybottom[i];                
01736             }
01737             else {
01738                 dpoint[0] = xtop[i];
01739                 dpoint[1] = ytop[i];                
01740             }
01741 
01742             for (k = 0; k < 3; k++)
01743                 if (crv[j])
01744                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01745                                          cpl_polynomial_eval(crv[k], point));
01746         }
01747     }
01748 
01749     cpl_vector_delete(point);
01750     for (j = 0; j < 3; j++)
01751         cpl_polynomial_delete(crv[j]);
01752 
01753     /*
01754      * Eliminate slits which are _entirely_ outside the CCD
01755      */
01756  
01757     nvalid  = cpl_table_get_nrow(slits);
01758     valid_id = cpl_table_get_data_int(slits, "slit_id");
01759     cpl_table_unselect_all(polytraces);
01760     for (i = 0; i < nslits; i++) {
01761         found = 0;
01762         for (j = 0; j < nvalid; j++) {
01763             if (slit_id[i] == valid_id[j]) {
01764                 found = 1;
01765                 break;
01766             }
01767         }
01768         if (!found) {
01769             cpl_table_select_row(polytraces, 2*i);
01770             cpl_table_select_row(polytraces, 2*i + 1);
01771         }
01772     }
01773     cpl_table_erase_selected(polytraces);
01774  
01775     nslits = cpl_table_get_nrow(polytraces);
01776 
01777     if (nslits == 0) {
01778         cpl_msg_warning(func, "No slits found on the CCD");
01779         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01780         cpl_table_delete(polytraces);
01781         return NULL;
01782     }
01783 
01784     if (nslits > 2) 
01785         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01786                      "partially contained in CCD", nslits / 2);
01787     else
01788         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01789                      "partially contained in CCD", nslits / 2);
01790 
01791     return polytraces;
01792 }
01793 
01794 
01836 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01837 {
01838     const char *func = "mos_build_disp_coeff";
01839 
01840     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01841 
01842     cpl_polynomial *ids[6];
01843     cpl_vector     *point;
01844     cpl_table      *idscoeff;
01845     double         *dpoint;
01846     double         *xtop;
01847     double         *ytop;
01848     double         *xbottom;
01849     double         *ybottom;
01850     int            *position;
01851     int            *length;
01852     int             nslits;
01853     int             nrows;
01854     int             order;
01855     int             ylow, yhig;
01856     int             i, j, k;
01857 
01858 
01859     if (global == NULL || slits == NULL) {
01860         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01861         return NULL;
01862     }
01863     
01864     nslits   = cpl_table_get_nrow(slits);
01865     position = cpl_table_get_data_int(slits, "position");
01866     length   = cpl_table_get_data_int(slits, "length");
01867     xtop     = cpl_table_get_data_double(slits, "xtop");
01868     ytop     = cpl_table_get_data_double(slits, "ytop");
01869     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01870     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01871 
01872     for (i = 0; i < 6; i++)
01873         ids[i] = read_global_distortion(global, i);
01874 
01875     for (i = 0; i < 6; i++)
01876         if (ids[i] == NULL)
01877             break;
01878 
01879     order = i - 1;
01880 
01881     if (order < 1) {
01882         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01883         return NULL;
01884     }
01885 
01886     nrows = 0;
01887     for (i = 0; i < nslits; i++)
01888         nrows += length[i]; 
01889 
01890     idscoeff = cpl_table_new(nrows);
01891 
01892     for (j = 0; j <= order; j++)
01893         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01894 
01895     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01896     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01897     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01898     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01899 
01900     point = cpl_vector_new(2);
01901     dpoint = cpl_vector_get_data(point);
01902 
01903     for (i = 0; i < nslits; i++) {
01904 
01905         if (length[i] == 0)
01906             continue;
01907 
01908         ylow = position[i];
01909         yhig = ylow + length[i];
01910 
01911         for (j = 0; j <= order; j++) {
01912             if (j) {
01913                 for (k = 0; k < length[i]; k++) {
01914                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01915                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01916                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01917                                          cpl_polynomial_eval(ids[j], point));
01918                 }
01919             }
01920             else {
01921                 for (k = 0; k < length[i]; k++) {
01922                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01923                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01924                 }
01925             }
01926         }
01927     }
01928 
01929     cpl_vector_delete(point);
01930     for (j = 0; j < 6; j++)
01931         cpl_polynomial_delete(ids[j]);
01932 
01933     return idscoeff;
01934 
01935 }
01936 
01937 
01960 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01961                             cpl_table *polytraces, double reference, 
01962                             double blue, double red, double dispersion)
01963 {
01964     const char     *func = "mos_subtract_sky";
01965 
01966     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01967                                                  /* Max order is 5 */
01968 
01969     cpl_image      *sky;
01970     cpl_bivector   *list;
01971     cpl_vector     *listx;
01972     cpl_vector     *listy;
01973     cpl_polynomial *polytop;
01974     cpl_polynomial *polybot;
01975     cpl_polynomial *trend;
01976 
01977     int            *slit_id;
01978     double         *dlistx;
01979     double         *dlisty;
01980     float          *sdata;
01981     float          *kdata;
01982     double          top, bot;
01983     int             itop, ibot;
01984     double          coeff;
01985     double          ytop, ybot;
01986     double          m, q, err;
01987     int             npix;
01988 
01989     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
01990     int             nx, ny;
01991     int             nslits;
01992     int            *length;
01993     int             missing_top, missing_bot;
01994     int             order;
01995     int             null;
01996     int             window = 50;  /* Longer slits have polynomial sky model */
01997     int             count;
01998     int             i, j, k;
01999 
02000 
02001     if (science == NULL || slits == NULL || polytraces == NULL) {
02002         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02003         return NULL;
02004     }
02005  
02006     if (dispersion <= 0.0) {
02007         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02008         return NULL;
02009     }
02010 
02011     if (red - blue < dispersion) {
02012         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02013         return NULL;
02014     }
02015 
02016     nx = cpl_image_get_size_x(science);
02017     ny = cpl_image_get_size_y(science);
02018 
02019     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02020 
02021     sdata = cpl_image_get_data(science);
02022     kdata = cpl_image_get_data(sky);
02023 
02024     nslits   = cpl_table_get_nrow(slits);
02025     order    = cpl_table_get_ncol(polytraces) - 2;
02026     length   = cpl_table_get_data_int(slits, "length");
02027     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02028 
02029     /*
02030      * The spatial resampling is performed for a certain number of
02031      * pixels above and below the position of the reference wavelength:
02032      */
02033     
02034     pixel_above = (red - reference) / dispersion;
02035     pixel_below = (reference - blue) / dispersion;
02036 
02037     for (i = 0; i < nslits; i++) {
02038 
02039         if (length[i] == 0)
02040             continue;
02041 
02042         
02043         /*
02044          * Recover from the table of spectral curvature coefficients
02045          * the curvature polynomials.
02046          */
02047 
02048         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02049 
02050         start_pixel = refpixel - pixel_below;
02051         if (start_pixel < 0)
02052             start_pixel = 0;
02053 
02054         end_pixel = refpixel + pixel_above;
02055         if (end_pixel > nx)
02056             end_pixel = nx;
02057 
02058         missing_top = 0;
02059         polytop = cpl_polynomial_new(1);
02060         for (k = 0; k <= order; k++) {
02061             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02062             if (null) {
02063                 cpl_polynomial_delete(polytop);
02064                 missing_top = 1;
02065                 break;
02066             }
02067             cpl_polynomial_set_coeff(polytop, &k, coeff);
02068         }
02069 
02070         missing_bot = 0;
02071         polybot = cpl_polynomial_new(1);
02072         for (k = 0; k <= order; k++) {
02073             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02074             if (null) {
02075                 cpl_polynomial_delete(polybot);
02076                 missing_bot = 1;
02077                 break;
02078             }
02079             cpl_polynomial_set_coeff(polybot, &k, coeff);
02080         }
02081 
02082         if (missing_top && missing_bot) {
02083             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02084                           slit_id[i]);
02085             continue;
02086         }
02087 
02088         /*
02089          * In case just one of the two edges was not traced, the other
02090          * edge curvature model is duplicated and shifted to the other
02091          * end of the slit: better than nothing!
02092          */
02093 
02094         if (missing_top) {
02095             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02096                           "the spectral curvature of the lower edge "
02097                           "is used instead.", slit_id[i]);
02098             polytop = cpl_polynomial_duplicate(polybot);
02099             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02100             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02101             k = 0;
02102             coeff = cpl_polynomial_get_coeff(polybot, &k);
02103             coeff += ytop - ybot;
02104             cpl_polynomial_set_coeff(polytop, &k, coeff);
02105         }
02106 
02107         if (missing_bot) {
02108             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02109                           "the spectral curvature of the upper edge "
02110                           "is used instead.", slit_id[i]);
02111             polybot = cpl_polynomial_duplicate(polytop);
02112             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02113             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02114             k = 0;
02115             coeff = cpl_polynomial_get_coeff(polytop, &k);
02116             coeff -= ytop - ybot;
02117             cpl_polynomial_set_coeff(polybot, &k, coeff);
02118         }
02119 
02120 
02121         /*
02122          * Now read pixel values along spatial direction, and fit them.
02123          */
02124 
02125         for (j = start_pixel; j < end_pixel; j++) {
02126             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02127             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02128             itop = floor(top + 0.5) + 1;
02129             ibot = floor(bot + 0.5);
02130             if (itop > ny)
02131                 itop = ny;
02132             if (ibot < 0)
02133                 ibot = 0;
02134             npix = itop - ibot;
02135             if (npix < 5)
02136                 break;
02137 
02138             list = cpl_bivector_new(npix);
02139             listx = cpl_bivector_get_x(list);
02140             listy = cpl_bivector_get_y(list);
02141             dlistx = cpl_vector_get_data(listx);
02142             dlisty = cpl_vector_get_data(listy);
02143 
02144             for (k = 0; k < npix; k++) {
02145                 dlistx[k] = k;
02146                 dlisty[k] = sdata[j + (ibot + k)*nx];
02147             }
02148 
02149             if (robustLinearFit(list, &q, &m, &err)) {
02150                 cpl_bivector_delete(list);
02151                 continue;
02152             }
02153 
02154             cpl_bivector_delete(list);
02155 
02156             for (k = 0; k < npix; k++) {
02157                 kdata[j + (ibot + k)*nx] = m*k + q;
02158             }
02159 
02160             if (npix > window) {
02161 
02162                 /*
02163                  * Polynomial iteration
02164                  */
02165 
02166                 err = 3*sqrt(err);
02167 
02168                 count = 0;
02169                 for (k = 0; k < npix; k++)
02170                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02171                         count++;
02172 
02173                 if (count < 10)
02174                     continue;
02175 
02176                 list = cpl_bivector_new(count);
02177                 listx = cpl_bivector_get_x(list);
02178                 listy = cpl_bivector_get_y(list);
02179                 dlistx = cpl_vector_get_data(listx);
02180                 dlisty = cpl_vector_get_data(listy);
02181 
02182                 count = 0;
02183                 for (k = 0; k < npix; k++) {
02184                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02185                         dlistx[count] = k;
02186                         dlisty[count] = sdata[j + (ibot + k)*nx];
02187                         count++;
02188                     }
02189                 }
02190 
02191                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02192  
02193                 cpl_bivector_delete(list);
02194 
02195                 err = 3*sqrt(err);
02196 
02197                 count = 0;
02198                 for (k = 0; k < npix; k++)
02199                     if (fabs(sdata[j + (ibot + k)*nx] 
02200                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02201                         count++;
02202 
02203                 if (count < 10) {
02204                     cpl_polynomial_delete(trend);
02205                     continue;
02206                 }
02207 
02208                 list = cpl_bivector_new(count);
02209                 listx = cpl_bivector_get_x(list);
02210                 listy = cpl_bivector_get_y(list);
02211                 dlistx = cpl_vector_get_data(listx);
02212                 dlisty = cpl_vector_get_data(listy);
02213 
02214                 count = 0;
02215                 for (k = 0; k < npix; k++) {
02216                     if (fabs(sdata[j + (ibot + k)*nx] 
02217                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02218                         dlistx[count] = k;
02219                         dlisty[count] = sdata[j + (ibot + k)*nx];
02220                         count++;
02221                     }
02222                 }
02223 
02224                 cpl_polynomial_delete(trend);
02225 
02226                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02227 
02228                 cpl_bivector_delete(list);
02229  
02230                 for (k = 0; k < npix; k++) {
02231                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02232                                                k, NULL);
02233                 }
02234 
02235                 cpl_polynomial_delete(trend);
02236             }
02237         }
02238         cpl_polynomial_delete(polytop);
02239         cpl_polynomial_delete(polybot);
02240     }
02241 
02242     cpl_image_subtract(science, sky);
02243 
02244     return sky;
02245 }
02246 
02247 
02280 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02281                               cpl_table *slits, cpl_table *polytraces, 
02282                               double reference, double blue, double red, 
02283                               double dispersion, int sradius, int polyorder)
02284 {
02285     const char     *func = "mos_normalise_flat";
02286 
02287     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02288                                                  /* Max order is 5 */
02289 
02290     cpl_image      *rectified;
02291     cpl_image      *smo_flat;
02292     cpl_image      *exslit;
02293     cpl_vector     *positions;
02294     cpl_vector     *flux;
02295     cpl_vector     *smo_flux;
02296     cpl_polynomial *trend;
02297     cpl_polynomial *polytop;
02298     cpl_polynomial *polybot;
02299 
02300     int            *slit_id;
02301     float          *p;
02302     float          *data;
02303     double         *fdata;
02304     double         *pdata;
02305     float          *sdata;
02306     float          *xdata;
02307     float          *wdata;
02308     double          vtop, vbot, value;
02309     double          top, bot;
02310     double          coeff;
02311     double          ytop, ybot;
02312     double          ypos;
02313     double          fvalue;
02314     int             ivalue;
02315     int             yint, yprev;
02316     int             npseudo;
02317 
02318     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02319     int             nx, ny, nsubx, nsuby;
02320     int             xlow, ylow, xhig, yhig;
02321     int             nslits;
02322     int            *position;
02323     int            *length;
02324     int             missing_top, missing_bot;
02325     int             order;
02326     int             npoints;
02327     int             uradius;
02328     int             null;
02329     int             i, j, k;
02330 
02331 /*    int             exclude = 5;     Number of excluded pixels at edges */
02332 
02333     /* For debug puposes only: cpl_image      *smo_rectified; */
02334 
02335 
02336     if (flat == NULL || slits == NULL || polytraces == NULL) {
02337         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02338         return NULL;
02339     }
02340  
02341     if (dispersion <= 0.0) {
02342         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02343         return NULL;
02344     }
02345 
02346     if (red - blue < dispersion) {
02347         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02348         return NULL;
02349     }
02350 
02351     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02352                                         blue, red, dispersion, 0, NULL);
02353 
02354     nx = cpl_image_get_size_x(rectified);
02355     ny = cpl_image_get_size_y(rectified);
02356 
02357     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02358                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02359     wdata = cpl_image_get_data(smo_flat);
02360 
02361     nslits   = cpl_table_get_nrow(slits);
02362     order    = cpl_table_get_ncol(polytraces) - 2;
02363     position = cpl_table_get_data_int(slits, "position");
02364     length   = cpl_table_get_data_int(slits, "length");
02365     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02366 
02367     /*
02368      * The spatial resampling is performed for a certain number of
02369      * pixels above and below the position of the reference wavelength:
02370      */
02371     
02372     pixel_above = (red - reference) / dispersion;
02373     pixel_below = (reference - blue) / dispersion;
02374 
02375     xlow = 1;
02376     xhig = nx;
02377     for (i = 0; i < nslits; i++) {
02378 
02379         if (length[i] == 0)
02380             continue;
02381 
02382         /*
02383          * We DON'T write:
02384          *
02385          * ylow = position[i];
02386          * yhig = ylow + length[i];
02387          *
02388          * because the cpl_image pixels are counted from 1, and because in 
02389          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02390          */
02391 
02392         ylow = position[i] + 1;
02393         yhig = ylow + length[i] - 1;
02394 
02395         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02396 
02397         if (polyorder < 0) {
02398 
02399             cpl_image_turn(exslit, -1);   /* For faster memory access */
02400     
02401             nsubx = cpl_image_get_size_x(exslit);
02402             nsuby = cpl_image_get_size_y(exslit);
02403             data = cpl_image_get_data(exslit);
02404             flux = cpl_vector_new(nsubx);
02405 
02406             uradius = nsubx / 2;
02407             if (uradius > sradius)
02408                 uradius = sradius;
02409 
02410             for (j = 0; j < nsuby; j++) {
02411                 fdata = cpl_vector_get_data(flux);
02412                 p = data;
02413                 for (k = 0; k < nsubx; k++)
02414                     *fdata++ = *p++;
02415                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02416                 fdata = cpl_vector_get_data(smo_flux);
02417                 p = data;
02418                 for (k = 0; k < nsubx; k++)
02419                     *p++ = *fdata++;
02420                 cpl_vector_delete(smo_flux);
02421                 data += nsubx;
02422             }
02423 
02424             cpl_vector_delete(flux);
02425 
02426 
02427             /*
02428              * First fit fluxes along the spatial direction with a low-degree
02429              * polynomial (excluding the first and the last pixels, close to
02430              * the edges)
02431              */
02432 /*
02433             if (nsubx-2*exclude > 10) {
02434                 flux = cpl_vector_new(nsubx-2*exclude);
02435                 fdata = cpl_vector_get_data(flux);
02436                 positions = cpl_vector_new(nsubx-2*exclude);
02437                 for (j = 0; j < nsubx-2*exclude; j++)
02438                     cpl_vector_set(positions, j, j+exclude);
02439         
02440                 for (k = 0; k < nsuby; k++) {
02441                     for (j = 0; j < nsubx-2*exclude; j++)
02442                         fdata[j] = data[j+exclude];
02443                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02444                                                          1, NULL);
02445                     for (j = 0; j < nsubx; j++)
02446                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02447                     cpl_polynomial_delete(trend);
02448                     data += nsubx;
02449                 }
02450 
02451                 cpl_vector_delete(flux);
02452                 cpl_vector_delete(positions);
02453             }
02454 */
02455 
02456             /*
02457              * Now smooth along the dispersion direction 
02458              */
02459 
02460             cpl_image_turn(exslit, 1);   /* For faster memory access */
02461             nsubx = cpl_image_get_size_x(exslit);
02462             nsuby = cpl_image_get_size_y(exslit);
02463             data = cpl_image_get_data(exslit);
02464 
02465             for (j = 0; j < nsuby; j++) {
02466                 flux = cpl_vector_new(nsubx);
02467                 fdata = cpl_vector_get_data(flux);
02468                 p = data;
02469                 for (k = 0; k < nsubx; k++)
02470                     *fdata++ = *p++;
02471                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02472                 cpl_vector_delete(flux);
02473                 fdata = cpl_vector_get_data(smo_flux);
02474                 p = data;
02475                 for (k = 0; k < nsubx; k++)
02476                     *p++ = *fdata++;
02477                 cpl_vector_delete(smo_flux);
02478                 data += nsubx;
02479             }
02480         }
02481         else {
02482 
02483             /*
02484              * Fit with a polynomial the flat field trend row by row.
02485              */
02486 
02487             nsubx = cpl_image_get_size_x(exslit);
02488             nsuby = cpl_image_get_size_y(exslit);
02489             data = cpl_image_get_data(exslit);
02490 
02491             for (j = 0; j < nsuby; j++) {
02492 
02493                 /*
02494                  * First get the size of the vectors to allocate:
02495                  */
02496 
02497                 npoints = 0;
02498                 p = data + j*nsubx;
02499                 for (k = 0; k < nsubx; k++)
02500                     if (p[k] > 1.0)
02501                         npoints++;
02502 
02503                 if (npoints > polyorder + 1) {
02504 
02505                     /*
02506                      * Fill the vectors for the fitting
02507                      */
02508 
02509                     flux = cpl_vector_new(npoints);
02510                     fdata = cpl_vector_get_data(flux);
02511                     positions = cpl_vector_new(npoints);
02512                     pdata = cpl_vector_get_data(positions);
02513 
02514                     npoints = 0;
02515                     p = data + j*nsubx;
02516                     for (k = 0; k < nsubx; k++) {
02517                         if (p[k] > 1.0) {
02518                             fdata[npoints] = p[k];
02519                             pdata[npoints] = k;
02520                             npoints++;
02521                         }
02522                     }
02523     
02524                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02525                                                          polyorder, NULL);
02526 
02527                     cpl_vector_delete(flux);
02528                     cpl_vector_delete(positions);
02529 
02530                     if (trend) {
02531                         p = data + j*nsubx;
02532                         for (k = 0; k < nsubx; k++)
02533                             if (p[k] > 1.0)
02534                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02535                         cpl_polynomial_delete(trend);
02536                     }
02537                     else {
02538                         cpl_msg_warning(func, "Invalid flat field flux fit "
02539                                         "(ignored)");
02540                     }
02541                 }
02542             }
02543         }
02544 
02545         
02546         /*
02547          * Recover from the table of spectral curvature coefficients
02548          * the curvature polynomials.
02549          */
02550 
02551         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02552 
02553         start_pixel = refpixel - pixel_below;
02554         if (start_pixel < 0)
02555             start_pixel = 0;
02556 
02557         end_pixel = refpixel + pixel_above;
02558         if (end_pixel > nx)
02559             end_pixel = nx;
02560 
02561         missing_top = 0;
02562         polytop = cpl_polynomial_new(1);
02563         for (k = 0; k <= order; k++) {
02564             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02565             if (null) {
02566                 cpl_polynomial_delete(polytop);
02567                 missing_top = 1;
02568                 break;
02569             }
02570             cpl_polynomial_set_coeff(polytop, &k, coeff);
02571         }
02572 
02573         missing_bot = 0;
02574         polybot = cpl_polynomial_new(1);
02575         for (k = 0; k <= order; k++) {
02576             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02577             if (null) {
02578                 cpl_polynomial_delete(polybot);
02579                 missing_bot = 1;
02580                 break;
02581             }
02582             cpl_polynomial_set_coeff(polybot, &k, coeff);
02583         }
02584 
02585         if (missing_top && missing_bot) {
02586             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02587                           slit_id[i]);
02588             continue;
02589         }
02590 
02591         /*
02592          * In case just one of the two edges was not traced, the other
02593          * edge curvature model is duplicated and shifted to the other
02594          * end of the slit: better than nothing!
02595          */
02596 
02597         if (missing_top) {
02598             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02599                           "the spectral curvature of the lower edge "
02600                           "is used instead.", slit_id[i]);
02601             polytop = cpl_polynomial_duplicate(polybot);
02602             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02603             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02604             k = 0;
02605             coeff = cpl_polynomial_get_coeff(polybot, &k);
02606             coeff += ytop - ybot;
02607             cpl_polynomial_set_coeff(polytop, &k, coeff);
02608         }
02609 
02610         if (missing_bot) {
02611             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02612                           "the spectral curvature of the upper edge "
02613                           "is used instead.", slit_id[i]);
02614             polybot = cpl_polynomial_duplicate(polytop);
02615             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02616             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02617             k = 0;
02618             coeff = cpl_polynomial_get_coeff(polytop, &k);
02619             coeff -= ytop - ybot;
02620             cpl_polynomial_set_coeff(polybot, &k, coeff);
02621         }
02622 
02623 
02624         /*
02625          * Now map smoothed image to CCD.
02626          * Note that the npseudo value related to this slit is equal
02627          * to the number of spatial pseudo-pixels decreased by 1
02628          * (compare with function mos_spatial_calibration()).
02629          */
02630 
02631         nx = cpl_image_get_size_x(flat);
02632         ny = cpl_image_get_size_y(flat);
02633 
02634         sdata = cpl_image_get_data(spatial);
02635         xdata = cpl_image_get_data(exslit);
02636         npseudo = cpl_image_get_size_y(exslit) - 1;
02637 
02638         /*
02639          * Write interpolated smoothed values to CCD image
02640          */
02641 
02642         for (j = start_pixel; j < end_pixel; j++) {
02643             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02644             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02645             for (k = 0; k <= npseudo; k++) {
02646                 ypos = top - k*(top-bot)/npseudo;
02647                 yint = ypos;
02648 
02649                 /*
02650                  * The line:
02651                  *     value = sdata[j + nx*yint];
02652                  * should be equivalent to:
02653                  *     value = npseudo*(top-yint)/(top-bot);
02654                  */
02655 
02656                 if (yint < 0 || yint >= ny-1) {
02657                     yprev = yint;
02658                     continue;
02659                 }
02660 
02661                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02662                 ivalue = value;               /* Nearest spatial pixels:   */
02663                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02664                 if (ivalue < npseudo && ivalue >= 0) {
02665                     vtop = xdata[j + nx*(npseudo-ivalue)];
02666                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02667                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02668 
02669                     if (k) {
02670 
02671                         /*
02672                          * This is added to recover lost pixels on
02673                          * the CCD image (pixels are lost because
02674                          * the CCD pixels are less than npseudo+1).
02675                          */
02676 
02677                         if (yprev - yint > 1) {
02678                             value = sdata[j + nx*(yint+1)];
02679                             ivalue = value;
02680                             fvalue = value - ivalue;
02681                             if (ivalue < npseudo && ivalue >= 0) {
02682                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02683                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02684                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02685                                                        + vbot*fvalue;
02686                             }
02687                         }
02688                     }
02689                 }
02690                 yprev = yint;
02691             }
02692         }
02693         cpl_polynomial_delete(polytop);
02694         cpl_polynomial_delete(polybot);
02695         cpl_image_delete(exslit);
02696     }
02697 
02698     cpl_image_delete(rectified);
02699 
02700     cpl_image_divide(flat, smo_flat);
02701 
02702     return smo_flat;
02703 }
02704 
02705 
02730 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02731                                   int polyorder)
02732 {
02733     const char     *func = "mos_normalise_longflat";
02734 
02735     cpl_image      *smo_flat;
02736     cpl_image      *profile;
02737     cpl_vector     *flux;
02738     cpl_vector     *smo_flux;
02739     cpl_vector     *positions;
02740     cpl_polynomial *trend;
02741 
02742     float          *level;
02743     float          *p;
02744     float          *data;
02745     double         *fdata;
02746     double         *pdata;
02747 
02748     int             nx, ny;
02749     int             npoints;
02750     int             i, j;
02751 
02752 
02753     if (flat == NULL) {
02754         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02755         return NULL;
02756     }
02757  
02758     if (sradius < 1 || dradius < 1) {
02759         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02760         return NULL;
02761     }
02762 
02763     smo_flat = cpl_image_duplicate(flat);
02764 
02765     if (polyorder < 0) {
02766 
02767         /*
02768          * First smooth along the spatial direction
02769          */
02770 
02771         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02772 
02773         nx = cpl_image_get_size_x(smo_flat);
02774         ny = cpl_image_get_size_y(smo_flat);
02775         data = cpl_image_get_data(smo_flat);
02776     
02777         for (i = 0; i < ny; i++) {
02778             flux = cpl_vector_new(nx);
02779             fdata = cpl_vector_get_data(flux);
02780             p = data;
02781             for (j = 0; j < nx; j++)
02782                 *fdata++ = *p++;
02783             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02784             cpl_vector_delete(flux);
02785             fdata = cpl_vector_get_data(smo_flux);
02786             p = data;
02787             for (j = 0; j < nx; j++)
02788                 *p++ = *fdata++;
02789             cpl_vector_delete(smo_flux);
02790             data += nx;
02791         }
02792 
02793         /*
02794          * Second smooth along the dispersion direction
02795          */
02796 
02797         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02798 
02799         nx = cpl_image_get_size_x(smo_flat);
02800         ny = cpl_image_get_size_y(smo_flat);
02801         data = cpl_image_get_data(smo_flat);
02802 
02803         for (i = 0; i < ny; i++) {
02804             flux = cpl_vector_new(nx);
02805             fdata = cpl_vector_get_data(flux);
02806             p = data;
02807             for (j = 0; j < nx; j++)
02808                 *fdata++ = *p++;
02809             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02810             cpl_vector_delete(flux);
02811             fdata = cpl_vector_get_data(smo_flux);
02812             p = data;
02813             for (j = 0; j < nx; j++)
02814                 *p++ = *fdata++;
02815             cpl_vector_delete(smo_flux);
02816             data += nx;
02817         }
02818     }
02819     else {
02820 
02821         /*
02822          * Fit with a polynomial the flat field trend column by column.
02823          */
02824 
02825         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02826 
02827         nx = cpl_image_get_size_x(smo_flat);
02828         ny = cpl_image_get_size_y(smo_flat);
02829         data = cpl_image_get_data(smo_flat);
02830 
02831         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02832         level = cpl_image_get_data(profile);
02833 
02834         for (i = 0; i < ny; i++) {
02835 
02836             /*
02837              * First get the size of the vectors to allocate:
02838              * eliminate from fit any value more than 20% away
02839              * from median level in current column.
02840              */
02841 
02842             npoints = 0;
02843             p = data + i*nx;
02844             for (j = 0; j < nx; j++)
02845                 if (fabs(p[j]/level[i] - 1) < 0.20)
02846                     npoints++;
02847 
02848             if (npoints > polyorder + 1) {
02849 
02850                 /*
02851                  * Fill the vectors for the fitting
02852                  */
02853 
02854                 flux = cpl_vector_new(npoints);
02855                 fdata = cpl_vector_get_data(flux);
02856                 positions = cpl_vector_new(npoints);
02857                 pdata = cpl_vector_get_data(positions);
02858 
02859                 npoints = 0;
02860                 p = data + i*nx;
02861                 for (j = 0; j < nx; j++) {
02862                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02863                         fdata[npoints] = p[j];
02864                         pdata[npoints] = j;
02865                         npoints++;
02866                     }
02867                 }
02868     
02869                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02870                                                      polyorder, NULL);
02871 
02872                 cpl_vector_delete(flux);
02873                 cpl_vector_delete(positions);
02874 
02875                 if (trend) {
02876                     p = data + i*nx;
02877                     for (j = 0; j < nx; j++)
02878                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02879                     cpl_polynomial_delete(trend);
02880                 }
02881                 else {
02882                     cpl_msg_warning(func, 
02883                                     "Invalid flat field flux fit (ignored)");
02884                 }
02885             }
02886         }
02887 
02888         cpl_image_delete(profile);
02889         cpl_image_turn(smo_flat, 1);
02890 
02891     }
02892 
02893     cpl_image_divide(flat, smo_flat);
02894 
02895     return smo_flat;
02896 }
02897 
02898 
02923 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
02924                                          cpl_image *wavemap, int mode)
02925 {
02926     const char *func = "mos_interpolate_wavecalib";
02927 
02928     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02929                                                  /* Max order is 5 */
02930 
02931     cpl_vector     *wave;
02932     cpl_vector     *positions;
02933     cpl_polynomial *trend;
02934 
02935     float          *p;
02936     float          *data;
02937     double         *wdata;
02938     double         *pdata;
02939 
02940     double          c;
02941     double          mse, ksigma;
02942 
02943     int             order;
02944     int             nrows, first_row, last_row;
02945     int             nx, ny;
02946     int             npoints, rpoints;
02947     int             null;
02948     int             i, j, k;
02949 
02950     int             polyorder = 4;  /* Candidate input argument */
02951 
02952 
02953     if (idscoeff == NULL)
02954         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02955 
02956     if (mode < 0 || mode > 2)
02957         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02958 
02959     if (mode == 0)
02960         return CPL_ERROR_NONE;
02961 
02962     if (wavemap) {
02963 
02964         /*
02965          * Fit with a polynomial the wavelength trend column by column.
02966          */
02967 
02968         cpl_image_turn(wavemap, -1);   /* For faster memory access */
02969 
02970         nx = cpl_image_get_size_x(wavemap);
02971         ny = cpl_image_get_size_y(wavemap);
02972         data = cpl_image_get_data(wavemap);
02973 
02974         for (i = 0; i < ny; i++) {
02975 
02976             /*
02977              * First get the size of the vectors to allocate:
02978              * eliminate from fit any value with "impossible" wavelength.
02979              */
02980 
02981             npoints = 0;
02982             p = data + i*nx;
02983             for (j = 0; j < nx; j++)
02984                 if (p[j] > 1.0)
02985                     npoints++;
02986 
02987             if (npoints > polyorder + 1) {
02988 
02989                 /*
02990                  * Fill the vectors for the fitting
02991                  */
02992 
02993                 wave = cpl_vector_new(npoints);
02994                 wdata = cpl_vector_get_data(wave);
02995                 positions = cpl_vector_new(npoints);
02996                 pdata = cpl_vector_get_data(positions);
02997 
02998                 npoints = 0;
02999                 p = data + i*nx;
03000                 for (j = 0; j < nx; j++) {
03001                     if (p[j] > 1.0) {
03002                         wdata[npoints] = p[j];
03003                         pdata[npoints] = j;
03004                         npoints++;
03005                     }
03006                 }
03007     
03008                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03009                                                      polyorder, &mse);
03010 
03011                 ksigma = 3*sqrt(mse);
03012 
03013                 cpl_vector_delete(wave);
03014                 cpl_vector_delete(positions);
03015 
03016                 if (trend) {
03017 
03018                     /*
03019                      * Apply 3-sigma rejection
03020                      */
03021 
03022                     rpoints = 0;
03023                     p = data + i*nx;
03024                     for (j = 0; j < nx; j++)
03025                         if (p[j] > 1.0)
03026                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03027                                                     - p[j]) < ksigma)
03028                                 rpoints++;
03029 
03030                     if (rpoints < npoints && rpoints > polyorder + 1) {
03031 
03032                         wave = cpl_vector_new(rpoints);
03033                         wdata = cpl_vector_get_data(wave);
03034                         positions = cpl_vector_new(rpoints);
03035                         pdata = cpl_vector_get_data(positions);
03036 
03037                         npoints = 0;
03038                         p = data + i*nx;
03039                         for (j = 0; j < nx; j++) {
03040                             if (p[j] > 1.0) {
03041                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03042                                                                 j, NULL) - p[j])
03043                                                                 < ksigma) {
03044                                     wdata[npoints] = p[j];
03045                                     pdata[npoints] = j;
03046                                     npoints++;
03047                                 }
03048                             }
03049                         }
03050         
03051                         cpl_polynomial_delete(trend);
03052                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03053                                                              polyorder, NULL);
03054 
03055                         cpl_vector_delete(wave);
03056                         cpl_vector_delete(positions);
03057                     }
03058                 }
03059 
03060                 if (trend) {
03061                     p = data + i*nx;
03062                     if (mode == 1) {
03063                         for (j = 0; j < nx; j++)
03064                             if (p[j] < 1.0)
03065                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03066                     }
03067                     else if (mode == 2) {
03068                         for (j = 0; j < nx; j++)
03069                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03070                     }
03071                     cpl_polynomial_delete(trend);
03072                 }
03073                 else {
03074                     cpl_msg_warning(func, 
03075                                     "Invalid wavelength field fit (ignored)");
03076                 }
03077             }
03078     
03079         }
03080 
03081         cpl_image_turn(wavemap, 1);
03082 
03083     }
03084 
03085 
03086     /*
03087      * Interpolating the IDS coefficients
03088      */
03089 
03090     nrows = cpl_table_get_nrow(idscoeff);
03091 
03092     order = 0;
03093     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03094         ++order;
03095     --order;
03096 
03097     first_row = 0;
03098     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03099         first_row++;
03100 
03101     last_row = nrows - 1;
03102     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03103         last_row--;
03104 
03105     for (k = 0; k <= order; k++) {
03106 
03107         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03108         wave = cpl_vector_new(npoints);
03109         wdata = cpl_vector_get_data(wave);
03110         positions = cpl_vector_new(npoints);
03111         pdata = cpl_vector_get_data(positions);
03112 
03113         npoints = 0;
03114         for (i = first_row; i <= last_row; i++) {
03115             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03116             if (null == 0) {
03117                 wdata[npoints] = c;
03118                 pdata[npoints] = i;
03119                 npoints++;
03120             }
03121         }
03122 
03123         trend = cpl_polynomial_fit_1d_create(positions, wave, 2, &mse);
03124 
03125         ksigma = 3*sqrt(mse);
03126 
03127         cpl_vector_delete(wave);
03128         cpl_vector_delete(positions);
03129 
03130         /*
03131          * Iteration
03132          */
03133 
03134         if (trend) {
03135             rpoints = 0;
03136             for (i = first_row; i <= last_row; i++) {
03137                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03138                 if (null == 0) {
03139                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03140                                                                  < ksigma) {
03141                         rpoints++;
03142                     }
03143                 }
03144             }
03145 
03146             if (rpoints > 0 && rpoints < npoints) {
03147                 cpl_msg_debug(func, "%d points rejected from "
03148                               "wavelength calibration fit", 
03149                               npoints - rpoints);
03150 
03151                 wave = cpl_vector_new(rpoints);
03152                 wdata = cpl_vector_get_data(wave);
03153                 positions = cpl_vector_new(rpoints);
03154                 pdata = cpl_vector_get_data(positions);
03155 
03156                 npoints = 0;
03157                 for (i = first_row; i <= last_row; i++) {
03158                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03159                     if (null == 0) {
03160                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03161                                                                  < ksigma) {
03162                             wdata[npoints] = c;
03163                             pdata[npoints] = i;
03164                             npoints++;
03165                         }
03166                     }
03167                 }
03168 
03169                 if (npoints) {
03170                     cpl_polynomial_delete(trend);
03171                     trend = cpl_polynomial_fit_1d_create(positions, 
03172                                                          wave, 2, NULL);
03173                 }
03174 
03175                 cpl_vector_delete(wave);
03176                 cpl_vector_delete(positions);
03177 
03178             }
03179         }
03180 
03181         if (trend) {
03182             for (i = first_row; i <= last_row; i++) {
03183                 if (mode == 1) {
03184                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03185                         cpl_table_set_double(idscoeff, clab[k], i, 
03186                                              cpl_polynomial_eval_1d(trend, i,
03187                                                                     NULL));
03188                     }
03189                 }
03190                 else if (mode == 2) {
03191                     cpl_table_set_double(idscoeff, clab[k], i, 
03192                                      cpl_polynomial_eval_1d(trend, i, NULL));
03193                 }
03194             }
03195             cpl_polynomial_delete(trend);
03196         }
03197         else {
03198             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03199         }
03200 
03201     }
03202 
03203     return CPL_ERROR_NONE;
03204 }
03205 
03206 
03207 
03233 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03234                            cpl_table *overscans)
03235 {
03236     const char *func = "mos_remove_bias";
03237 
03238     cpl_image *unbiased;
03239     cpl_image *overscan;
03240     double     mean_bias_level;
03241     double     mean_overscans_level;
03242     int        count;
03243     int        nrows;
03244     int        xlow, ylow, xhig, yhig;
03245     int        i;
03246 
03247 
03248     if (image == NULL || overscans == NULL) {
03249         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03250         return NULL;
03251     }
03252 
03253     nrows = cpl_table_get_nrow(overscans);
03254 
03255     if (nrows == 0) {
03256         cpl_msg_error(func, "Empty overscan table");
03257         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03258         return NULL;
03259     }
03260 
03261     if (bias) {
03262         if (nrows == 1) {
03263             unbiased = cpl_image_subtract_create(image, bias);
03264             if (unbiased == NULL) {
03265                 cpl_msg_error(func, "Incompatible master bias");
03266                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03267             }
03268             return unbiased;
03269         }
03270         mean_bias_level = cpl_image_get_mean(bias);
03271     }
03272     else {
03273         if (nrows == 1) {
03274             cpl_msg_error(func, "No master bias in input, and no overscan "
03275                           "regions in input image: bias subtraction "
03276                           "cannot be performed!");
03277             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03278             return NULL;
03279         }
03280         mean_bias_level = 0.0;
03281     }
03282 
03283     mean_overscans_level = 0.0;
03284     count = 0;
03285     for (i = 0; i < nrows; i++) {
03286         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03287         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03288         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03289         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03290 
03291         if (i == 0) {
03292             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03293             if (unbiased == NULL) {
03294                 cpl_msg_error(func, "Incompatible overscan table");
03295                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03296                 return NULL;
03297             }
03298             if (bias) {
03299                 if (cpl_image_subtract(unbiased, bias)) {
03300                     cpl_msg_error(func, "Incompatible master bias");
03301                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03302                     cpl_image_delete(unbiased);
03303                     return NULL;
03304                 }
03305             }
03306         }
03307         else {
03308             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03309             if (overscan == NULL) {
03310                 cpl_msg_error(func, "Incompatible overscan table");
03311                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03312                 cpl_image_delete(unbiased);
03313                 return NULL;
03314             }
03315 
03316             mean_overscans_level += cpl_image_get_median(overscan);
03317             count++;
03318 
03319 /***
03320  * Here the mean level was used: not very robust...
03321 
03322             mean_overscans_level += cpl_image_get_flux(overscan);
03323             count += cpl_image_get_size_x(overscan)
03324                    * cpl_image_get_size_y(overscan);
03325 ***/
03326             cpl_image_delete(overscan);
03327         }
03328     }
03329 
03330     /*
03331      * Overscan correction
03332      */
03333 
03334     mean_overscans_level /= count;
03335 
03336     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03337 
03338     return unbiased;
03339 
03340 }
03341 
03342 
03401 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03402                                      int length, int msize, int fsize) 
03403 {
03404     const char *func = "mos_arc_background_1D";
03405 
03406     float  *minf;
03407     float  *maxf;
03408     float  *smof;
03409     int     i;
03410 
03411 
03412     if (spectrum == NULL || back == NULL)
03413         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03414 
03415     if (msize % 2 == 0)
03416         msize++;
03417 
03418     if (fsize % 2 == 0)
03419         fsize++;
03420 
03421     if (msize < 3 || fsize < msize || length < 2*fsize)
03422         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03423 
03424 
03425     minf = min_filter(spectrum, length, msize);
03426     smof = smo_filter(minf, length, fsize);
03427     cpl_free(minf);
03428     maxf = max_filter(smof, length, 2*msize+1);
03429     cpl_free(smof);
03430     smof = smo_filter(maxf, length, 2*fsize+1);
03431     cpl_free(maxf);
03432     minf = min_filter(smof, length, 2*msize+1);
03433     cpl_free(smof);
03434     smof = smo_filter(minf, length, 2*fsize+1);
03435     cpl_free(minf);
03436 
03437     for (i = 0; i < length; i++)
03438         back[i] = smof[i];
03439 
03440     cpl_free(smof);
03441 
03442     return CPL_ERROR_NONE;
03443 
03444 }
03445 
03446 
03503 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03504 {
03505     const char *func = "mos_arc_background";
03506 
03507     cpl_image  *fimage;
03508     cpl_image  *bimage;
03509     cpl_matrix *kernel;
03510     float      *data;
03511     float      *bdata;
03512     float      *row;
03513     float      *brow;
03514     int         nx, ny;
03515     int         i;
03516 
03517 
03518     if (image == NULL) {
03519         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03520         return NULL;
03521     }
03522 
03523     if (msize % 2 == 0)
03524         msize++;
03525 
03526     if (fsize % 2 == 0)
03527         fsize++;
03528 
03529     nx = cpl_image_get_size_x(image);
03530     ny = cpl_image_get_size_y(image);
03531 
03532     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03533 
03534     kernel = cpl_matrix_new(3, 3);
03535     cpl_matrix_fill(kernel, 1.0);
03536     fimage = cpl_image_filter_median(image, kernel);
03537     cpl_matrix_delete(kernel);
03538 
03539     data = cpl_image_get_data_float(fimage);
03540     bdata = cpl_image_get_data_float(bimage);
03541 
03542     for (i = 0; i < ny; i++) {
03543         row = data + i * nx;
03544         brow = bdata + i * nx;
03545         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03546             cpl_error_set_where(func); 
03547             cpl_image_delete(fimage);
03548             cpl_image_delete(bimage);
03549             return NULL;
03550         }
03551     }
03552 
03553     cpl_image_delete(fimage);
03554 
03555     return bimage;
03556 }
03557 
03558 
03579 int mos_lines_width(const float *spectrum, int length)
03580 {
03581 
03582   const char *func = "mos_lines_width";
03583 
03584   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03585   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03586 
03587   double  norm, value, max;
03588   int     radius = 20;
03589   int     short_length = length - 2*radius - 1;
03590   int     width;
03591   int     i, j, k;
03592 
03593 
03594   /*
03595    * Derivative, and separation of positive and negative derivatives
03596    */
03597 
03598   for (j = 0, i = 1; i < length; j++, i++) {
03599       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03600       if (profile1[j] < 0)
03601           profile1[j] = 0;
03602       if (profile2[j] > 0)
03603           profile2[j] = 0;
03604       else
03605           profile2[j] = -profile2[j];
03606   }
03607 
03608 
03609   /*
03610    * Profiles normalisation
03611    */
03612 
03613   length--;
03614 
03615   norm = 0;
03616   for (i = 0; i < length; i++)
03617       if (norm < profile1[i])
03618           norm = profile1[i];
03619 
03620   for (i = 0; i < length; i++) {
03621       profile1[i] /= norm;
03622       profile2[i] /= norm;
03623   }
03624 
03625 
03626   /*
03627    * Cross-correlation
03628    */
03629 
03630   max = -1;
03631   for (i = 0; i <= radius; i++) {
03632       value = 0;
03633       for (j = 0; j < short_length; j++) {
03634           k = radius+j;
03635           value += profile1[k] * profile2[k+i];
03636       }
03637       if (max < value) {
03638           max = value;
03639           width = i;
03640       }
03641   }
03642 
03643   cpl_free(profile1);
03644   cpl_free(profile2);
03645 
03646   if (max < 0.0) {
03647       cpl_msg_debug(func, "Cannot estimate line width");
03648       width = 1;
03649   }
03650 
03651   return width;
03652 
03653 }
03654 
03655 
03682 cpl_vector *mos_peak_candidates(const float *spectrum, 
03683                 int length, float level, 
03684                                 float exp_width)
03685 { 
03686 
03687   const char *func = "mos_peak_candidates";
03688 
03689   int     i, j;
03690   int     nint   = length - 1;
03691   int     n      = 0;
03692   int     width  = 2 * ceil(exp_width / 2) + 1;
03693   int     start  = width / 2;
03694   int     end    = length - width / 2;
03695   int     step;
03696   float  *smo;
03697   double *data   = cpl_calloc(length/2, sizeof(double));
03698 
03699 
03700   if (spectrum == NULL) {
03701       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03702       return NULL;
03703   }
03704 
03705 
03706   /*
03707    * If lines have a flat top (as in the case of broad slit), smooth
03708    * before determining the max.
03709    */
03710 
03711   if (width > 7) {
03712     smo = cpl_calloc(length, sizeof(float));
03713     start = width / 2;
03714     end = length - width / 2;
03715     for (i = 0; i < start; i++)
03716       smo[i] = spectrum[i];
03717     for (i = start; i < end; i++) {
03718       for (j = i - start; j <= i + start; j++)
03719         smo[i] += spectrum[j];
03720       smo[i] /= width;
03721     }
03722     for (i = end; i < length; i++)
03723       smo[i] = spectrum[i];
03724   }
03725   else {
03726       smo = (float *)spectrum;
03727   }
03728 
03729   /*
03730    * Collect all relative maxima along spectrum, that are higher than the
03731    * specified level.
03732    */
03733 
03734   if (width > 20)
03735     step = width / 2;
03736   else
03737     step = 1;
03738 
03739   for (i = step; i < nint - step + 1; i += step) {
03740     if (smo[i] > level) {
03741       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03742         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03743           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03744           ++n;
03745         }
03746       }
03747     }
03748   }
03749 
03750   if (width > 7) {
03751     cpl_free(smo);
03752   }
03753 
03754   if (n == 0) {
03755     cpl_free(data);
03756     return NULL;
03757   }
03758 
03759   return cpl_vector_wrap(n, data);
03760 
03761 }
03762 
03763 
03785 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
03786                              cpl_vector *peaks, int sradius)
03787 {
03788 
03789     const char *func = "mos_refine_peaks";
03790 
03791     double *data;
03792     float   pos;
03793     int     npeaks;
03794     int     startPos, endPos;
03795     int     window = 2*sradius+1;
03796     int     i, j;
03797 
03798 
03799     if (peaks == NULL || spectrum == NULL) {
03800         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03801         return NULL;
03802     }
03803 
03804     npeaks = cpl_vector_get_size(peaks);
03805     data = cpl_vector_unwrap(peaks);
03806 
03807     for (i = 0; i < npeaks; i++) {
03808         startPos = data[i] - window/2;
03809         endPos   = startPos + window;
03810         if (startPos < 0 || endPos >= length)
03811             continue;
03812 
03813         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
03814             pos += startPos;
03815             data[i] = pos;
03816         }
03817     }
03818 
03819     for (i = 1; i < npeaks; i++)
03820         if (data[i] - data[i-1] < 0.5)
03821             data[i-1] = -1.0;
03822 
03823     for (i = 0, j = 0; i < npeaks; i++) {
03824         if (data[i] > 0.0) {
03825             if (i != j)
03826                 data[j] = data[i];
03827             j++;
03828         }
03829     }
03830 
03831     return cpl_vector_wrap(j, data);
03832 
03833 }
03834 
03835 
03889 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
03890                                  double min_disp, double max_disp,
03891                                  double tolerance, int multiplex)
03892 {
03893 
03894   int      i, j, k, l;
03895   int      nlint, npint;
03896   int      minpos;
03897   float    min;
03898   double   lratio, pratio;
03899   double   lo_start, lo_end, hi_start, hi_end, denom;
03900   double   disp, variation, prev_variation;
03901   int      max, maxpos, minl, mink;
03902   int      ambiguous;
03903   int      npeaks_lo, npeaks_hi;
03904   int     *peak_lo;
03905   int     *peak_hi;
03906   int    **ident;
03907   int     *nident;
03908   int     *lident;
03909 
03910   double  *peak;
03911   double  *line;
03912   int      npeaks, nlines;
03913 
03914   double  *xpos;
03915   double  *lambda;
03916   int     *ilambda;
03917   double  *tmp_xpos;
03918   double  *tmp_lambda;
03919   int     *tmp_ilambda;
03920   int     *flag;
03921   int      n = 0;
03922   int      nn;
03923   int      nseq = 0;
03924   int      gap;
03925   int     *seq_length;
03926   int      found, valid;
03927 
03928   peak        = cpl_vector_get_data(peaks);
03929   npeaks      = cpl_vector_get_size(peaks);
03930   line        = cpl_vector_get_data(lines);
03931   nlines      = cpl_vector_get_size(lines);
03932 
03933   if (npeaks < 4)
03934       return NULL;
03935 
03936   peak_lo     = cpl_malloc(npeaks * sizeof(int));
03937   peak_hi     = cpl_malloc(npeaks * sizeof(int));
03938   nident      = cpl_calloc(npeaks, sizeof(int));
03939   lident      = cpl_calloc(nlines, sizeof(int));
03940   xpos        = cpl_calloc(npeaks, sizeof(double));
03941   lambda      = cpl_calloc(npeaks, sizeof(double));
03942   ilambda     = cpl_calloc(npeaks, sizeof(int));
03943   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
03944   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
03945   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
03946   flag        = cpl_calloc(npeaks, sizeof(int));
03947   seq_length  = cpl_calloc(npeaks, sizeof(int));
03948   ident       = cpl_malloc(npeaks * sizeof(int *));
03949   for (i = 0; i < npeaks; i++)
03950     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
03951 
03952   /*
03953    * This is just the number of intervals - one less than the number
03954    * of points (catalog wavelengths, or detected peaks).
03955    */
03956 
03957   nlint = nlines - 1;
03958   npint = npeaks - 1;
03959 
03960 
03961   /*
03962    * Here the big loops on catalog lines begins.
03963    */
03964 
03965   for (i = 1; i < nlint; i++) {
03966 
03967 
03968     /*
03969      * For each catalog wavelength I take the previous and the next one, 
03970      * and compute the ratio of the corresponding wavelength intervals.
03971      * This ratio will be compared to all the ratios obtained doing the
03972      * same with all the detected peaks positions.
03973      */
03974 
03975     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
03976 
03977 
03978     /*
03979      * Here the loop on detected peaks positions begins.
03980      */
03981 
03982     for (j = 1; j < npint; j++) {
03983 
03984       /*
03985        * Not all peaks are used for computing ratios: just the ones
03986        * that are compatible with the expected spectral dispersion
03987        * are taken into consideration. Therefore, I define the pixel
03988        * intervals before and after any peak that are compatible with
03989        * the specified dispersion interval, and select just the peaks
03990        * within such intervals. If either of the two intervals doesn't
03991        * contain any peak, then I skip the current peak and continue
03992        * with the next.
03993        */
03994 
03995       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
03996       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
03997       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
03998       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
03999 
04000       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04001         if (peak[k] > lo_end)
04002           break;
04003         if (peak[k] > lo_start) {
04004           peak_lo[npeaks_lo] = k;
04005           ++npeaks_lo;
04006         }
04007       }
04008 
04009       if (npeaks_lo == 0)
04010         continue;
04011 
04012       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04013         if (peak[k] > hi_end)
04014           break;
04015         if (peak[k] > hi_start) {
04016           peak_hi[npeaks_hi] = k;
04017           ++npeaks_hi;
04018         }
04019       }
04020 
04021       if (npeaks_hi == 0)
04022         continue;
04023 
04024 
04025       /*
04026        * Now I have all peaks that may help for a local identification.
04027        * peak_lo[k] is the sequence number of the k-th peak of the lower
04028        * interval; peak_hi[l] is the sequence number of the l-th peak of
04029        * the higher interval. j is, of course, the sequence number of the
04030        * current peak (second big loop).
04031        */
04032 
04033       prev_variation = 1000.0;
04034       minl = mink = 0;
04035 
04036       for (k = 0; k < npeaks_lo; k++) {
04037         denom = peak[j] - peak[peak_lo[k]];
04038         for (l = 0; l < npeaks_hi; l++) {
04039 
04040           /*
04041            * For any pair of peaks - one from the lower and the other
04042            * from the higher interval - I compute the same ratio that
04043            * was computed with the current line catalog wavelength.
04044            */
04045 
04046           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04047 
04048           /*
04049            * If the two ratios are compatible within the specified
04050            * tolerance, we have a preliminary identification. This
04051            * will be marked in the matrix ident[][], where the first
04052            * index corresponds to a peak sequence number, and the second
04053            * index is the counter of the identifications made during
04054            * this whole process. The array of counters is nident[].
04055            * If more than one interval pair fulfills the specified
04056            * tolerance, the closest to the expected ratio is selected.
04057            */
04058 
04059           variation = fabs(lratio-pratio) / pratio;
04060 
04061           if (variation < tolerance) {
04062             if (variation < prev_variation) {
04063               prev_variation = variation;
04064               minl = l;
04065               mink = k;
04066             }
04067           }
04068         }
04069       }
04070       if (prev_variation < tolerance) {
04071         ident[j][nident[j]]                         = i;
04072         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04073         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04074         ++nident[j];
04075         ++nident[peak_hi[minl]];
04076         ++nident[peak_lo[mink]];
04077       }
04078     }   /* End loop on positions */
04079   }    /* End loop on lines     */
04080 
04081 
04082   /*
04083    * At this point I have filled the ident matrix with all my preliminary
04084    * identifications. Ambiguous identifications must be eliminated.
04085    */
04086 
04087 
04088   for (i = 0; i < npeaks; i++) {
04089 
04090 
04091     /*
04092      * I don't take into consideration peaks that were never identified.
04093      * They are likely contaminations, or emission lines that were not
04094      * listed in the input wavelength catalog.
04095      */
04096 
04097     if (nident[i] > 1) {
04098 
04099 
04100       /*
04101        * Initialise the histogram of wavelengths assigned to the i-th peak.
04102        */
04103 
04104       for (j = 0; j < nlines; j++)
04105         lident[j] = 0;
04106 
04107 
04108       /*
04109        * Count how many times each catalog wavelength was assigned
04110        * to the i-th peak.
04111        */
04112 
04113       for (j = 0; j < nident[i]; j++)
04114         ++lident[ident[i][j]];
04115 
04116 
04117       /*
04118        * What wavelength was most frequently assigned to the i-th peak?
04119        */
04120 
04121       max = 0;
04122       maxpos = 0;
04123       for (j = 0; j < nlines; j++) {
04124         if (max < lident[j]) {
04125           max = lident[j];
04126           maxpos = j;
04127         }
04128       }
04129 
04130 
04131       /*
04132        * Were there other wavelengths assigned with the same frequency?
04133        * This would be the case of an ambiguous identification. It is
04134        * safer to reject this peak...
04135        */
04136 
04137       ambiguous = 0;
04138 
04139       for (k = maxpos + 1; k < nlines; k++) {
04140         if (lident[k] == max) {
04141           ambiguous = 1;
04142           break;
04143         }
04144       }
04145 
04146       if (ambiguous)
04147         continue;
04148 
04149 
04150       /*
04151        * Otherwise, I assign to the i-th peak the wavelength that was
04152        * most often assigned to it.
04153        */
04154 
04155       tmp_xpos[n]   = peak[i];
04156       tmp_lambda[n] = line[maxpos];
04157       tmp_ilambda[n] = maxpos;
04158 
04159       ++n;
04160 
04161     }
04162 
04163   }
04164 
04165 
04166   /*
04167    * Check on identified peaks. Contaminations from other spectra might 
04168    * be present and should be excluded: this type of contamination 
04169    * consists of peaks that have been _correctly_ identified! The non-
04170    * spectral type of light contamination should have been almost all 
04171    * removed already in the previous steps, but it may still be present.
04172    * Here, the self-consistent sequences of identified peaks are
04173    * separated one from the other. At the moment, just the longest of
04174    * such sequences is selected (in other words, spectral multiplexing
04175    * is ignored).
04176    */
04177 
04178   if (n > 1) {
04179     nn = 0;                  /* Number of peaks in the list of sequences */
04180     nseq = 0;                /* Current sequence */
04181     for (k = 0; k < n; k++) {
04182       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04183         flag[k] = 1;
04184         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04185         lambda[nn] = tmp_lambda[k];
04186         ilambda[nn] = tmp_ilambda[k];
04187         ++seq_length[nseq];
04188         ++nn;
04189 
04190         /*
04191          * Now look for all the following peaks that are compatible
04192          * with the expected spectral dispersion, and add them in 
04193          * sequence to xpos. Note that missing peaks are not a problem...
04194          */
04195          
04196         i = k;
04197         while (i < n - 1) {
04198           found = 0;
04199           for (j = i + 1; j < n; j++) {
04200             if (flag[j] == 0) {
04201               disp = (tmp_lambda[j] - tmp_lambda[i])
04202                    / (tmp_xpos[j] - tmp_xpos[i]);
04203               if (disp >= min_disp && disp <= max_disp) {
04204                 flag[j] = 1;
04205                 xpos[nn] = tmp_xpos[j];
04206                 lambda[nn] = tmp_lambda[j];
04207                 ilambda[nn] = tmp_ilambda[j];
04208                 ++seq_length[nseq];
04209                 ++nn;
04210                 i = j;
04211                 found = 1;
04212                 break;
04213               }
04214             }
04215           }
04216           if (!found)
04217             break;
04218         }
04219 
04220         /*
04221          * Current sequence is completed: begin new sequence on the
04222          * excluded peaks, starting the loop on peaks again.
04223          */
04224 
04225         ++nseq;
04226         k = 0;
04227       }
04228     }
04229 
04230 
04231     /*
04232      * Find the longest sequence of self-consistent peaks.
04233      */
04234 
04235     max = 0;
04236     maxpos = 0;
04237     for (i = 0; i < nseq; i++) {
04238       if (seq_length[i] > max) {
04239         max = seq_length[i];
04240         maxpos = i;
04241       }
04242     }
04243 
04244     if (multiplex >= 0) {
04245 
04246         /*
04247          * At least 6 identified lines per sequence, to consider valid
04248          * that sequence.
04249          */
04250 
04251         if (max > 5) {
04252             valid = -1;
04253             for (i = 0; i < nseq; i++) {
04254                 if (seq_length[i] > 5) {
04255                     valid++;
04256                 }
04257                 if (multiplex == valid) {
04258                     max = seq_length[i];
04259                     maxpos = i;
04260                     break;
04261                 }
04262             }
04263         }
04264     }
04265 
04266     /*
04267      * Find where this sequence starts in the whole peak position
04268      * storage.
04269      */
04270 
04271     nn = 0;
04272     for (i = 0; i < maxpos; i++)
04273       nn += seq_length[i];
04274 
04275     /*
04276      * Move the longest sequence at the beginning of the returned lists
04277      */
04278 
04279     n = max;
04280     for (i = 0; i < n; i++, nn++) {
04281       xpos[i] = xpos[nn];
04282       lambda[i] = lambda[nn];
04283       ilambda[i] = ilambda[nn];
04284     }
04285 
04286 
04287     /*
04288      * Are some wavelengths missing? Recover them.
04289      */
04290 
04291     for (i = 1; i < n; i++) {
04292       gap = ilambda[i] - ilambda[i-1];
04293       for (j = 1; j < gap; j++) {
04294 
04295         if (j == 1) {
04296 
04297           /*
04298            * Determine the local dispersion from the current pair of peaks
04299            */
04300   
04301           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04302         }
04303 
04304         /*
04305          * With this, find the expected position of the missing
04306          * peak by linear interpolation.
04307          */
04308 
04309         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04310 
04311         /*
04312          * Is there a peak at that position? Here a peak from the
04313          * original list is searched, that is closer than 2 pixels
04314          * to the expected position. If it is found, insert it at
04315          * the current position on the list of identified peaks,
04316          * and leave immediately the loop (taking the new position
04317          * for the following linear interpolation, in case more
04318          * than one peak is missing in the current interval).
04319          * If it is not found, stay in the loop, looking for 
04320          * the following missing peaks in this interval.
04321          */
04322 
04323         found = 0;
04324         for (k = 0; k < npeaks; k++) {
04325           if (fabs(peak[k] - hi_start) < 2) {
04326             for (l = n; l > i; l--) {
04327               xpos[l] = xpos[l-1];
04328               lambda[l] = lambda[l-1];
04329               ilambda[l] = ilambda[l-1];
04330             }
04331             xpos[i] = peak[k];
04332             lambda[i] = line[ilambda[i-1] + j];
04333             ilambda[i] = ilambda[i-1] + j;
04334             ++n;
04335             found = 1;
04336             break;
04337           }
04338         }
04339         if (found)
04340           break;
04341       }
04342     }
04343 
04344 
04345     /*
04346      * Try to extrapolate forward
04347      */
04348 
04349     found = 1;
04350     while (ilambda[n-1] < nlines - 1 && found) {
04351 
04352       /*
04353        * Determine the local dispersion from the last pair of 
04354        * identified peaks
04355        */
04356 
04357       if (n > 1)
04358           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04359       else
04360           disp = 0.0;
04361 
04362       if (disp > max_disp || disp < min_disp)
04363         break;
04364 
04365 
04366       /*
04367        * With this, find the expected position of the missing
04368        * peak by linear interpolation.
04369        */
04370 
04371       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04372 
04373       /*
04374        * Is there a peak at that position? Here a peak from the
04375        * original list is searched, that is closer than 6 pixels
04376        * to the expected position. If it is found, insert it at
04377        * the end of the list of identified peaks. If it is not
04378        * found, leave the loop.
04379        */
04380 
04381       found = 0;
04382       min = fabs(peak[0] - hi_start);
04383       minpos = 0;
04384       for (k = 1; k < npeaks; k++) {
04385         if (min > fabs(peak[k] - hi_start)) {
04386             min = fabs(peak[k] - hi_start);
04387             minpos = k;
04388         }
04389       }
04390       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04391         xpos[n] = peak[minpos];
04392         lambda[n] = line[ilambda[n-1] + 1];
04393         ilambda[n] = ilambda[n-1] + 1;
04394         ++n;
04395         found = 1;
04396       }
04397     }
04398 
04399 
04400     /*
04401      * Try to extrapolate backward
04402      */
04403 
04404     found = 1;
04405     while (ilambda[0] > 0 && found) {
04406 
04407       /*
04408        * Determine the local dispersion from the first pair of
04409        * identified peaks
04410        */
04411 
04412       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04413 
04414       if (disp > max_disp || disp < min_disp)
04415         break;
04416 
04417 
04418       /*
04419        * With this, find the expected position of the missing
04420        * peak by linear interpolation.
04421        */
04422 
04423       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04424 
04425 
04426       /*
04427        * Is there a peak at that position? Here a peak from the
04428        * original list is searched, that is closer than 6 pixels
04429        * to the expected position. If it is found, insert it at
04430        * the beginning of the list of identified peaks. If it is not
04431        * found, leave the loop.
04432        */
04433 
04434       found = 0;
04435       min = fabs(peak[0] - hi_start);
04436       minpos = 0;
04437       for (k = 1; k < npeaks; k++) {
04438         if (min > fabs(peak[k] - hi_start)) {
04439             min = fabs(peak[k] - hi_start);
04440             minpos = k;
04441         }
04442       }
04443       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04444         for (j = n; j > 0; j--) {
04445           xpos[j] = xpos[j-1];
04446           lambda[j] = lambda[j-1];
04447           ilambda[j] = ilambda[j-1];
04448         }
04449         xpos[0] = peak[minpos];
04450         lambda[0] = line[ilambda[0] - 1];
04451         ilambda[0] = ilambda[0] - 1;
04452         ++n;
04453         found = 1;
04454       }
04455     }
04456   }
04457 
04458 
04459   /*
04460    * At this point all peaks are processed. Free memory, and return
04461    * the result.
04462    */
04463 
04464 /************************************************+
04465   for (i = 0; i < npeaks; i++) {
04466     printf("Peak %d:\n   ", i);
04467     for (j = 0; j < nident[i]; j++)
04468       printf("%.2f, ", line[ident[i][j]]);
04469     printf("\n");
04470   }
04471 
04472   printf("\n");
04473 
04474   for (i = 0; i < n; i++)
04475     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04476 +************************************************/
04477   for (i = 0; i < npeaks; i++)
04478     cpl_free(ident[i]);
04479   cpl_free(ident);
04480   cpl_free(nident);
04481   cpl_free(lident);
04482   cpl_free(ilambda);
04483   cpl_free(tmp_xpos);
04484   cpl_free(tmp_lambda);
04485   cpl_free(tmp_ilambda);
04486   cpl_free(peak_lo);
04487   cpl_free(flag);
04488   cpl_free(seq_length);
04489   cpl_free(peak_hi);
04490 
04491   if (n == 0) {
04492     cpl_free(xpos);
04493     cpl_free(lambda);
04494     return NULL;
04495   }
04496 
04497   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04498                                    cpl_vector_wrap(n, lambda));
04499 }
04500 
04501 
04519 /*
04520 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04521                     double refwave, double pixel)
04522 {
04523     double yellow;
04524     double cpixel;
04525     double tolerance = 0.02;
04526     int    max_iter = 20;
04527     int    iter = 0;
04528 
04529     
04530     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04531         return 0.0;
04532     
04533     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04534         return 0.0;
04535 
04536     yellow = (blue + red) / 2;
04537 
04538     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04539 
04540     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04541 
04542         if (cpixel > pixel)
04543             red = yellow;
04544         else
04545             blue = yellow;
04546 
04547         yellow = (blue + red) / 2;
04548         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04549 
04550         iter++;
04551 
04552     }
04553 
04554     return yellow;
04555 
04556 }
04557 */
04558 
04559 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04560                     double refwave, double pixel)
04561 {
04562     double yellow;
04563     double coeff;
04564     int    zero = 0;
04565 
04566     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04567         return 0.0;
04568 
04569     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04570         return 0.0;
04571 
04572     yellow = (blue + red) / 2 - refwave;
04573 
04574     coeff = cpl_polynomial_get_coeff(ids, &zero);
04575     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04576 
04577     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04578 
04579     cpl_polynomial_set_coeff(ids, &zero, coeff);
04580 
04581     return yellow + refwave;
04582 
04583 }
04584 
04610 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04611                                  double reject, int minlines, 
04612                                  int *nlines, double *err)
04613 {
04614     const char   *func = "mos_poly_wav2pix";
04615 
04616     cpl_bivector *pixwav2;
04617     cpl_vector   *wavel;
04618     cpl_vector   *pixel;
04619     double       *d_wavel;
04620     double       *d_pixel;
04621     double        pixpos;
04622     int           fitlines;
04623     int           rejection = 0;
04624     int           i, j;
04625 
04626     cpl_polynomial *ids;
04627 
04628 
04629     *nlines = 0;
04630     *err = 0;
04631 
04632     if (pixwav == NULL) {
04633         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04634         return NULL;
04635     }
04636 
04637     fitlines = cpl_bivector_get_size(pixwav);
04638 
04639     if (fitlines < minlines) {
04640         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04641         return NULL;
04642     }
04643 
04644 
04645     /*
04646      * If outliers rejection was requested, allocate a working
04647      * vector (that can be modified as soon as outliers are removed)
04648      */
04649 
04650     if (reject > 0.0)
04651         rejection = 1;
04652 
04653     if (rejection)
04654         pixwav2 = cpl_bivector_duplicate(pixwav);
04655     else
04656         pixwav2 = pixwav;
04657 
04658 
04659     /*
04660      * The single vectors are extracted just because the fitting routine
04661      * requires it
04662      */
04663 
04664     pixel = cpl_bivector_get_x(pixwav2);
04665     wavel = cpl_bivector_get_y(pixwav2);
04666 
04667 
04668     /*
04669      * Get rid of the wrapper, in case of duplication
04670      */
04671 
04672     if (rejection)
04673         cpl_bivector_unwrap_vectors(pixwav2);
04674 
04675 
04676     /*
04677      * Here begins the iterative fit of identified lines
04678      */
04679 
04680     while (fitlines >= minlines) {
04681 
04682         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04683         *err = sqrt(*err);
04684     
04685         if (ids == NULL) {
04686             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04687             cpl_msg_debug(func, "Fitting IDS");
04688             cpl_error_set_where(func);
04689             if (rejection) {
04690                 cpl_vector_delete(wavel);
04691                 cpl_vector_delete(pixel);
04692             }
04693             return NULL;
04694         }
04695 
04696         if (rejection) {
04697 
04698 
04699             /*
04700              * Now work directly with the vector data buffers...
04701              */
04702 
04703             d_pixel = cpl_vector_unwrap(pixel);
04704             d_wavel = cpl_vector_unwrap(wavel);
04705 
04706             for (i = 0, j = 0; i < fitlines; i++) {
04707                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04708                 if (fabs(pixpos - d_pixel[i]) < reject) {
04709                     d_pixel[j] = d_pixel[i];
04710                     d_wavel[j] = d_wavel[i];
04711                     j++;
04712                 }
04713             }
04714     
04715             if (j == fitlines) {       /* No rejection in last iteration */
04716                 cpl_free(d_wavel);
04717                 cpl_free(d_pixel);
04718                 *nlines = fitlines;
04719                 return ids;
04720             }
04721             else {                     /* Some lines were rejected       */
04722                 fitlines = j;
04723                 cpl_polynomial_delete(ids);
04724                 if (fitlines >= minlines) {
04725                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04726                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04727                 }
04728                 else {                 /* Too few lines: failure         */
04729                     cpl_free(d_wavel);
04730                     cpl_free(d_pixel);
04731                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04732                     return NULL;
04733                 }
04734             }
04735         }
04736         else {
04737             *nlines = fitlines;
04738             return ids;       /* Exit at first iteration if no rejection */
04739         }
04740     }
04741 
04742     return ids;               /* To avoid compiler warnings */
04743 }
04744 
04745 
04770 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04771                                  double reject, int minlines, 
04772                                  int *nlines, double *err)
04773 {
04774 
04775     cpl_bivector *wavpix;
04776     cpl_vector   *wavel;
04777     cpl_vector   *pixel;
04778 
04779     cpl_polynomial *dds;
04780 
04781 
04782     /*
04783      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
04784      */
04785 
04786     pixel = cpl_bivector_get_x(pixwav);
04787     wavel = cpl_bivector_get_y(pixwav);
04788 
04789     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
04790 
04791     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
04792 
04793     cpl_bivector_unwrap_vectors(wavpix);
04794 
04795     return dds;
04796 
04797 }
04798 
04799 
04822 cpl_bivector *mos_find_peaks(const float *spectrum, int length, cpl_vector *lines,
04823                              cpl_polynomial *ids, double refwave, int sradius)
04824 {
04825     const char   *func = "mos_find_peaks";
04826 
04827     double       *data;
04828     double       *d_pixel;
04829     double       *d_wavel;
04830     float         pos;
04831     int           nlines;
04832     int           pixel;
04833     int           i, j;
04834 
04835 
04836     if (spectrum == NULL || lines == NULL || ids == NULL) {
04837         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04838         return NULL;
04839     }
04840 
04841     nlines = cpl_vector_get_size(lines);
04842 
04843     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
04844         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04845         return NULL;
04846     }
04847 
04848     d_wavel = cpl_malloc(nlines * sizeof(double));
04849     d_pixel = cpl_malloc(nlines * sizeof(double));
04850 
04851     data = cpl_vector_get_data(lines);
04852 
04853     for (i = 0, j = 0; i < nlines; i++) {
04854         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
04855         if (pixel - sradius < 0 || pixel + sradius >= length)
04856             continue;
04857         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
04858             pos += pixel - sradius;
04859             d_pixel[j] = pos;
04860             d_wavel[j] = data[i];
04861             j++;
04862         }
04863     }
04864 
04865     if (j > 0) {
04866         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
04867                                          cpl_vector_wrap(j, d_wavel));
04868     }
04869     else {
04870         cpl_free(d_wavel);
04871         cpl_free(d_pixel);
04872         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
04873         return NULL;
04874     }
04875 }
04876 
04877 
04995 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
04996                       cpl_vector *lines,
04997                                           double dispersion, float level,
04998                                           int sradius, int order,
04999                                           double reject, double refwave, 
05000                                           double *wavestart, double *waveend,
05001                                           int *nlines, double *error, 
05002                                           cpl_table *idscoeff,
05003                                           cpl_image *calibration,
05004                                           cpl_image *residuals, 
05005                                           cpl_table *restable,
05006                                           cpl_mask *refmask)
05007 {
05008 
05009     const char *func = "mos_wavelength_calibration_raw";
05010 
05011     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
05012                                                  /* Max order is 5 */
05013 
05014     double  tolerance = 20.0;     /* Probably forever...                */
05015     int     step      = 10;       /* Compute restable every "step" rows */
05016 
05017     char            name[MAX_COLNAME];
05018     cpl_image      *resampled;
05019     cpl_bivector   *output;
05020     cpl_bivector   *new_output;
05021     cpl_vector     *peaks;
05022     cpl_vector     *wavel;
05023     cpl_polynomial *ids;
05024     cpl_polynomial *lin;
05025     cpl_matrix     *kernel;
05026     double          ids_err;
05027     double          max_disp, min_disp;
05028     double         *line;
05029     double          firstLambda, lastLambda, lambda;
05030     double          value, wave, pixe;
05031     cpl_binary     *mdata;
05032     const float    *sdata;
05033     float          *rdata;
05034     float          *idata;
05035     float          *ddata;
05036     float           v1, v2, vi;
05037     float           fpixel;
05038     int            *have_it;
05039     int             pixstart, pixend;
05040     int             extrapolation;
05041     int             nref;
05042     int             nl, nx, ny, pixel;
05043     int             countLines, usedLines;
05044     int             uorder;
05045     int             in, first, last;
05046     int             width, uradius;
05047     int             i, j, k;
05048 
05049 
05050     if (dispersion == 0.0) {
05051         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05052         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05053         return NULL;
05054     }
05055 
05056     if (dispersion < 0.0) {
05057         cpl_msg_error(func, "The expected dispersion must be positive");
05058         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05059         return NULL;
05060     }
05061 
05062     max_disp = dispersion + dispersion * tolerance / 100;
05063     min_disp = dispersion - dispersion * tolerance / 100;
05064 
05065     if (order < 1) {
05066         cpl_msg_error(func, "The order of the fitting polynomial "
05067                       "must be at least 1");
05068         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05069         return NULL;
05070     }
05071 
05072     if (image == NULL || lines == NULL) {
05073         cpl_msg_error(func, "Both spectral exposure and reference line "
05074                       "catalog are required in input");
05075         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05076         return NULL;
05077     }
05078 
05079     nx = cpl_image_get_size_x(image);
05080     ny = cpl_image_get_size_y(image);
05081     sdata = cpl_image_get_data_float_const(image);
05082 
05083     nref = cpl_vector_get_size(lines);
05084     line = cpl_vector_get_data(lines);
05085 
05086     if (*wavestart < 1.0 && *waveend < 1.0) {
05087         firstLambda = line[0];
05088         lastLambda = line[nref-1];
05089         extrapolation = (lastLambda - firstLambda) / 10;
05090         firstLambda -= extrapolation;
05091         lastLambda += extrapolation;
05092         *wavestart = firstLambda;
05093         *waveend = lastLambda;
05094     }
05095     else {
05096         firstLambda = *wavestart;
05097         lastLambda = *waveend;
05098     }
05099 
05100     nl = (lastLambda - firstLambda) / dispersion;
05101     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05102     rdata = cpl_image_get_data_float(resampled);
05103 
05104     if (calibration)
05105         idata = cpl_image_get_data_float(calibration);
05106 
05107     if (residuals)
05108         ddata = cpl_image_get_data_float(residuals);
05109 
05110     if (idscoeff)
05111         for (j = 0; j <= order; j++)
05112             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05113 
05114     if (restable) {
05115         cpl_table_set_size(restable, nref);
05116         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05117         cpl_table_copy_data_double(restable, "wavelength", line);
05118         for (i = 0; i < ny; i += step) {
05119              snprintf(name, MAX_COLNAME, "r%d", i);
05120              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05121              snprintf(name, MAX_COLNAME, "d%d", i);
05122              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05123              snprintf(name, MAX_COLNAME, "p%d", i);
05124              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05125         }
05126     }
05127 
05128     /*
05129      * Here is the real thing: detecting and identifying peaks,
05130      * and then fit the transformation from wavelength to pixel
05131      * and from pixel to wavelength.
05132      */
05133 
05134     for (i = 0; i < ny; i++) {
05135         width = mos_lines_width(sdata + i*nx, nx);
05136         if (sradius > 0) {
05137             if (width > sradius) {
05138                 uradius = width;
05139             }
05140             else {
05141                 uradius = sradius;
05142             }
05143         }
05144         if (width < 5)
05145             width = 5;
05146         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05147         if (peaks) {
05148             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05149         }
05150 /* TESTICLE */
05151         if (peaks) {
05152             output = mos_identify_peaks(peaks, lines, min_disp, 
05153                                         max_disp, 0.05, 2);
05154             if (output) {
05155                 countLines = cpl_bivector_get_size(output);
05156                 if (countLines < 4) {
05157                     cpl_bivector_delete(output);
05158                     cpl_vector_delete(peaks);
05159                     if (nlines)
05160                         nlines[i] = 0;
05161                     if (error)
05162                         error[i] = 0.0;
05163                     continue;
05164                 }
05165 
05166                 /*
05167                  * Set reference wavelength as zero point
05168                  */
05169 
05170                 wavel = cpl_bivector_get_y(output);
05171                 cpl_vector_subtract_scalar(wavel, refwave);
05172 
05173                 uorder = countLines / 2 - 1;
05174                 if (uorder > order)
05175                     uorder = order;
05176 
05177 /* This part is now commented out. In case the first-guess iteration
05178  * was requested, the first fit was made with a lower polynomial degree:
05179  * more robust, and still accurate enough to be used as a first-guess.
05180 
05181                 if (sradius > 0 && uorder > 2)
05182                     --uorder;
05183 
05184  * End of commented part */
05185 
05186                 ids = mos_poly_wav2pix(output, uorder, reject,
05187                                        2 * (uorder + 1), &usedLines,
05188                                        &ids_err);
05189 
05190                 if (ids == NULL) {
05191                     cpl_bivector_delete(output);
05192                     cpl_vector_delete(peaks);
05193                     if (nlines)
05194                         nlines[i] = 0;
05195                     if (error)
05196                         error[i] = 0.0;
05197                     cpl_error_reset();
05198                     continue;
05199                 }
05200 
05201                 if (idscoeff) {
05202 
05203                     /*
05204                      * Write it anyway, even in case a first-guess based
05205                      * solution will be searched afterwards: in case of
05206                      * failure, the "blind" solution is kept.
05207                      */
05208 
05209                     for (k = 0; k <= order; k++) {
05210                         if (k > uorder) {
05211                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05212                         }
05213                         else {
05214                             cpl_table_set_double(idscoeff, clab[k], i,
05215                                       cpl_polynomial_get_coeff(ids, &k));
05216                         }
05217                     }
05218                 }
05219 
05220                 if (sradius > 0) {
05221 
05222                     /*
05223                      * Use ids as a first-guess
05224                      */
05225 
05226                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05227                                                 ids, refwave, uradius);
05228 
05229                     if (new_output) {
05230                         cpl_bivector_delete(output);
05231                         output = new_output;
05232                     }
05233                     else
05234                         cpl_error_reset();
05235 
05236 
05237                     cpl_polynomial_delete(ids);
05238 
05239                     countLines = cpl_bivector_get_size(output);
05240 
05241                     if (countLines < 4) {
05242                         cpl_bivector_delete(output);
05243                         cpl_vector_delete(peaks);
05244 
05245                         /* 
05246                          * With the following code a decision is taken:
05247                          * if using the first-guess gives no results,
05248                          * then also the "blind" solution is rejected.
05249                          */
05250 
05251                         if (nlines)
05252                             nlines[i] = 0;
05253                         if (error)
05254                             error[i] = 0.0;
05255                         if (idscoeff)
05256                             for (k = 0; k <= order; k++)
05257                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05258                         continue;
05259                     }
05260 
05261                     wavel = cpl_bivector_get_y(output);
05262                     cpl_vector_subtract_scalar(wavel, refwave);
05263 
05264                     uorder = countLines / 2 - 1;
05265                     if (uorder > order)
05266                         uorder = order;
05267 
05268                     ids = mos_poly_wav2pix(output, uorder, reject,
05269                                            2 * (uorder + 1), &usedLines,
05270                                            &ids_err);
05271 
05272                     if (ids == NULL) {
05273                         cpl_bivector_delete(output);
05274                         cpl_vector_delete(peaks);
05275 
05276                         /* 
05277                          * With the following code a decision is taken:
05278                          * if using the first-guess gives no results,
05279                          * then also the "blind" solution is rejected.
05280                          */
05281 
05282                         if (nlines)
05283                             nlines[i] = 0;
05284                         if (error)
05285                             error[i] = 0.0;
05286                         if (idscoeff)
05287                             for (k = 0; k <= order; k++)
05288                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05289                         cpl_error_reset();
05290                         continue;
05291                     }
05292 
05293                     if (idscoeff) {
05294                         for (k = 0; k <= order; k++) {
05295                             if (k > uorder) {
05296                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05297                             }
05298                             else {
05299                                 cpl_table_set_double(idscoeff, clab[k], i,
05300                                             cpl_polynomial_get_coeff(ids, &k));
05301                             }
05302                         }
05303                     }
05304 
05305                 } /* End of "use ids as a first-guess" */
05306 
05307                 if (nlines)
05308                     nlines[i] = usedLines;
05309                 if (error)
05310                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05311 
05312                 pixstart = cpl_polynomial_eval_1d(ids, 
05313                     cpl_bivector_get_y_data(output)[0], NULL);
05314                 pixend = cpl_polynomial_eval_1d(ids,
05315                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05316                 extrapolation = (pixend - pixstart) / 5;
05317                 pixstart -= extrapolation;
05318                 pixend += extrapolation;
05319                 if (pixstart < 0)
05320                     pixstart = 0;
05321                 if (pixend > nx)
05322                     pixend = nx;
05323 
05324                 /*
05325                  * Wavelength calibrated image (if requested):
05326                  */
05327 
05328                 if (calibration) {
05329                     for (j = pixstart; j < pixend; j++) {
05330                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05331                                                          lastLambda, refwave, 
05332                                                          j);
05333                     }
05334                 }
05335 
05336                 /*
05337                  * Resampled image:
05338                  */
05339 
05340                 for (j = 0; j < nl; j++) {
05341                     lambda = firstLambda + j * dispersion;
05342                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05343                                                     NULL);
05344                     pixel = fpixel;
05345                     if (pixel >= 0 && pixel < nx-1) {
05346                         v1 = (sdata + i*nx)[pixel];
05347                         v2 = (sdata + i*nx)[pixel+1];
05348                         vi = v1 + (v2-v1)*(fpixel-pixel);
05349                         (rdata + i*nl)[j] = vi;
05350                     }
05351                 }
05352 
05353                 /*
05354                  * Residuals image
05355                  */
05356 
05357                 if (residuals || (restable && !(i%step))) {
05358                     if (restable && !(i%step)) {
05359                         lin = cpl_polynomial_new(1);
05360                         for (k = 0; k < 2; k++)
05361                             cpl_polynomial_set_coeff(lin, &k, 
05362                                           cpl_polynomial_get_coeff(ids, &k));
05363                     }
05364                     for (j = 0; j < countLines; j++) {
05365                         pixe = cpl_bivector_get_x_data(output)[j];
05366                         wave = cpl_bivector_get_y_data(output)[j];
05367                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05368                         if (residuals) {
05369                             pixel = pixe + 0.5;
05370                             (ddata + i*nx)[pixel] = value;
05371                         }
05372                         if (restable && !(i%step)) {
05373                             for (k = 0; k < nref; k++) {
05374                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05375                                     snprintf(name, MAX_COLNAME, "r%d", i);
05376                                     cpl_table_set_double(restable, name, 
05377                                                          k, value);
05378                                     value = pixe
05379                                           - cpl_polynomial_eval_1d(lin, wave,
05380                                                                    NULL);
05381                                     snprintf(name, MAX_COLNAME, "d%d", i);
05382                                     cpl_table_set_double(restable, name, 
05383                                                          k, value);
05384                                     snprintf(name, MAX_COLNAME, "p%d", i);
05385                                     cpl_table_set_double(restable, name,
05386                                                          k, pixe);
05387                                     break;
05388                                 }
05389                             }
05390                         }
05391                     }
05392                     if (restable && !(i%step)) {
05393                         cpl_polynomial_delete(lin);
05394                     }
05395                 }
05396 
05397                 /*
05398                  * Mask at reference wavelength
05399                  */
05400 
05401                 if (refmask) {
05402                     mdata = cpl_mask_get_data(refmask);
05403                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05404                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05405                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05406                         mdata[pixel + i*nx] = CPL_BINARY_1;
05407                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05408                     }
05409                 }
05410 
05411                 cpl_polynomial_delete(ids);
05412                 cpl_bivector_delete(output);
05413             }
05414             cpl_vector_delete(peaks);
05415         }
05416     }
05417 
05418     if (refmask) {
05419         kernel = cpl_matrix_new(3, 3);
05420         cpl_matrix_set(kernel, 0, 1, 1.0);
05421         cpl_matrix_set(kernel, 1, 1, 1.0);
05422         cpl_matrix_set(kernel, 2, 1, 1.0);
05423 
05424         cpl_mask_dilation(refmask, kernel);
05425         cpl_mask_erosion(refmask, kernel);
05426         cpl_mask_erosion(refmask, kernel);
05427         cpl_mask_dilation(refmask, kernel);
05428 
05429         cpl_matrix_delete(kernel);
05430 
05431         /*
05432          *  Fill possible gaps
05433          */
05434 
05435         mdata = cpl_mask_get_data(refmask);
05436         have_it = cpl_calloc(ny, sizeof(int));
05437 
05438         for (i = 0; i < ny; i++, mdata += nx) {
05439             for (j = 0; j < nx; j++) {
05440                 if (mdata[j] == CPL_BINARY_1) {
05441                     have_it[i] = j;
05442                     break;
05443                 }
05444             }
05445         }
05446 
05447         mdata = cpl_mask_get_data(refmask);
05448         in = 0;
05449         first = last = 0;
05450 
05451         for (i = 0; i < ny; i++) {
05452             if (have_it[i]) {
05453                 if (!in) {
05454                     in = 1;
05455                     if (first) {
05456                         last = i;
05457                         if (abs(have_it[first] - have_it[last]) < 3) {
05458                             for (j = first; j < last; j++) {
05459                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05460                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05461                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05462                             }
05463                         }
05464                     }
05465                 }
05466             }
05467             else {
05468                 if (in) {
05469                     in = 0;
05470                     first = i - 1;
05471                 }
05472             }
05473         }
05474 
05475         cpl_free(have_it);
05476 
05477     }
05478 
05479 /*
05480     for (i = 0; i < ny; i++) {
05481         if (nlines[i] == 0) {
05482             for (k = 0; k <= order; k++) {
05483                 cpl_table_set_invalid(idscoeff, clab[k], i);
05484             }
05485         }
05486     }
05487 */
05488 
05489     return resampled;
05490 }
05491 
05492 
05514 /*
05515 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05516 {
05517     const char *func = "mos_locate_spectra_bis";
05518 
05519     cpl_apertures    *slits;
05520     cpl_image        *labimage;
05521     cpl_image        *refimage;
05522     cpl_binary       *mdata;
05523     cpl_table        *slitpos;
05524     cpl_propertylist *sort_col;
05525     int               nslits;
05526     int              *have_it;
05527     int               in, first, last;
05528     int               i, j;
05529 
05530 
05531     if (mask == NULL) {
05532         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05533         return NULL;
05534     }
05535 
05536     nx = cpl_mask_get_size_x(mask);
05537     ny = cpl_mask_get_size_y(mask);
05538 
05539     mdata = cpl_mask_get_data(refmask);
05540     have_it = cpl_calloc(ny, sizeof(int));
05541 
05542     for (i = 0; i < ny; i++, mdata += nx) {
05543         for (j = 0; j < nx; j++) {
05544             if (mdata[j] == CPL_BINARY_1) {
05545                 have_it[i] = j + 1;
05546                 break;
05547             }
05548         }
05549     }
05550 
05551     mdata = cpl_mask_get_data(refmask);
05552     in = 0;
05553     first = last = 0;
05554     nslits = 0;
05555 
05556     for (i = 0; i < ny; i++) {
05557         if (have_it[i]) {
05558             if (in) {
05559                 if (i) {
05560                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05561                         nslits++;
05562                     }
05563                 }
05564             }
05565             else {
05566                 in = 1;
05567                 nslits++;
05568             }
05569         }
05570         else {
05571             if (in) {
05572                 in = 0;
05573             }
05574         }
05575     }
05576 }
05577 */
05578 
05579 
05601 cpl_table *mos_locate_spectra(cpl_mask *mask)
05602 {
05603     const char *func = "mos_locate_spectra";
05604 
05605     cpl_apertures    *slits;
05606     cpl_image        *labimage;
05607     cpl_image        *refimage;
05608     cpl_table        *slitpos;
05609     cpl_propertylist *sort_col;
05610     int               nslits;
05611     int               i;
05612 
05613 
05614     if (mask == NULL) {
05615         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05616         return NULL;
05617     }
05618 
05619     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05620 
05621     if (nslits < 1) {
05622         cpl_image_delete(labimage);
05623         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05624         return NULL;
05625     }
05626 
05627     refimage = cpl_image_new_from_mask(mask);
05628 
05629     slits = cpl_apertures_new_from_image(refimage, labimage);
05630 
05631     cpl_image_delete(labimage);
05632     cpl_image_delete(refimage);
05633 
05634     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05635     if (nslits < 1) {
05636         cpl_apertures_delete(slits);
05637         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05638         return NULL;
05639     }
05640 
05641     slitpos = cpl_table_new(nslits);
05642     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05643     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05644     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05645     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05646     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05647     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05648     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05649     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05650 
05651     for (i = 0; i < nslits; i++) {
05652         cpl_table_set_double(slitpos, "xtop", i, 
05653                              cpl_apertures_get_top_x(slits, i+1) - 1);
05654         cpl_table_set_double(slitpos, "ytop", i, 
05655                              cpl_apertures_get_top(slits, i+1));
05656         cpl_table_set_double(slitpos, "xbottom", i, 
05657                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05658         cpl_table_set_double(slitpos, "ybottom", i, 
05659                              cpl_apertures_get_bottom(slits, i+1));
05660     }
05661 
05662     cpl_apertures_delete(slits);
05663 
05664     sort_col = cpl_propertylist_new();
05665     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05666     cpl_table_sort(slitpos, sort_col);
05667     cpl_propertylist_delete(sort_col);
05668 
05669     return slitpos;
05670 
05671 }
05672 
05673 
05689 cpl_error_code mos_validate_slits(cpl_table *slits) 
05690 {
05691     const char *func = "mos_validate_slits";
05692 
05693 
05694     if (slits == NULL)
05695         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05696 
05697     if (1 != cpl_table_has_column(slits, "xtop"))
05698         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05699 
05700     if (1 != cpl_table_has_column(slits, "ytop"))
05701         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05702 
05703     if (1 != cpl_table_has_column(slits, "xbottom"))
05704         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05705 
05706     if (1 != cpl_table_has_column(slits, "ybottom"))
05707         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05708 
05709     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05710         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05711 
05712     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05713         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05714 
05715     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05716         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05717 
05718     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05719         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05720 
05721     return CPL_ERROR_NONE;
05722 }
05723 
05724 
05753 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05754 {
05755     const char *func = "mos_rotate_slits";
05756 
05757     cpl_error_code error;
05758     char aux_name[] = "_0";
05759     int i;
05760 
05761 
05762     rotation %= 4;
05763     if (rotation < 0)
05764         rotation += 4;
05765 
05766     if (rotation == 0)
05767         return CPL_ERROR_NONE;
05768 
05769     error = mos_validate_slits(slits);
05770     if (error)
05771         return cpl_error_set(func, error);
05772 
05773     if (rotation == 1 || rotation == 3) {
05774 
05775         /*
05776          * Swap x and y column names
05777          */
05778 
05779         for (i = 0; i < 77; i++)
05780             if (1 == cpl_table_has_column(slits, aux_name))
05781                 aux_name[1]++;
05782         if (1 == cpl_table_has_column(slits, aux_name))
05783             return cpl_error_set(func, CPL_ERROR_CONTINUE);
05784         cpl_table_name_column(slits, "xtop", aux_name);
05785         cpl_table_name_column(slits, "ytop", "xtop");
05786         cpl_table_name_column(slits, aux_name, "ytop");
05787         cpl_table_name_column(slits, "xbottom", aux_name);
05788         cpl_table_name_column(slits, "ybottom", "xbottom");
05789         cpl_table_name_column(slits, aux_name, "ybottom");
05790     }
05791 
05792     if (rotation == 1 || rotation == 2) {
05793         cpl_table_multiply_scalar(slits, "xtop", -1.0);
05794         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
05795         cpl_table_add_scalar(slits, "xtop", nx);
05796         cpl_table_add_scalar(slits, "xbottom", nx);
05797     }
05798 
05799     if (rotation == 3 || rotation == 2) {
05800         cpl_table_multiply_scalar(slits, "ytop", -1.0);
05801         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
05802         cpl_table_add_scalar(slits, "ytop", ny);
05803         cpl_table_add_scalar(slits, "ybottom", ny);
05804     }
05805 
05806     return CPL_ERROR_NONE;
05807 }
05808 
05809 
05867 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
05868                               cpl_table *global)
05869 {
05870     cpl_array        *top_ident;
05871     cpl_array        *bot_ident;
05872     cpl_matrix       *mdata;
05873     cpl_matrix       *mpattern;
05874     cpl_matrix       *top_data;
05875     cpl_matrix       *top_pattern;
05876     cpl_matrix       *top_mdata;
05877     cpl_matrix       *top_mpattern;
05878     cpl_matrix       *bot_data;
05879     cpl_matrix       *bot_pattern;
05880     cpl_matrix       *bot_mdata;
05881     cpl_matrix       *bot_mpattern;
05882     cpl_propertylist *sort_col;
05883     double           *xtop;
05884     double           *ytop;
05885     double           *xmtop;
05886     double           *ymtop;
05887     double           *xbot;
05888     double           *ybot;
05889     double           *xmbot;
05890     double           *ymbot;
05891     double            top_scale, bot_scale;
05892     double            angle, top_angle, bot_angle;
05893     double            xmse, ymse;
05894     double            xrms, top_xrms, bot_xrms;
05895     double            yrms, top_yrms, bot_yrms;
05896     int               nslits;
05897     int               nmaskslits, use_pattern;
05898     int               found_slits, found_slits_top, found_slits_bot;
05899     int               degree;
05900     int               i;
05901     cpl_table        *positions;
05902     cpl_error_code    error;
05903 
05904     cpl_vector       *point;
05905     double           *dpoint;
05906     cpl_vector       *xpos;
05907     cpl_vector       *ypos;
05908     cpl_vector       *xmpos;
05909     cpl_vector       *ympos;
05910     cpl_bivector     *mpos;
05911     cpl_polynomial   *xpoly     = NULL;
05912     cpl_polynomial   *ypoly     = NULL;
05913     cpl_polynomial   *top_xpoly = NULL;
05914     cpl_polynomial   *top_ypoly = NULL;
05915     cpl_polynomial   *bot_xpoly = NULL;
05916     cpl_polynomial   *bot_ypoly = NULL;
05917 
05918 
05919 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 3, 0)
05920     positions = mos_identify_slits_fast(slits, maskslits, global);
05921     if (positions == NULL)
05922         cpl_error_set_where(cpl_func);
05923     return positions;
05924 #endif
05925 
05926     error = mos_validate_slits(slits);
05927     if (error) {
05928         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
05929                       cpl_error_get_message());
05930         cpl_error_set(cpl_func, error);
05931         return NULL;
05932     }
05933 
05934     error = mos_validate_slits(maskslits);
05935     if (error) {
05936         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
05937                       cpl_error_get_message());
05938         cpl_error_set(cpl_func, error);
05939         return NULL;
05940     }
05941 
05942     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
05943         cpl_msg_error(cpl_func, "Missing slits identifiers");
05944         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
05945         return NULL;
05946     }
05947 
05948     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
05949         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
05950         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
05951         return NULL;
05952     }
05953 
05954     nslits = cpl_table_get_nrow(slits);
05955     nmaskslits = cpl_table_get_nrow(maskslits);
05956 
05957     if (nslits == 0 || nmaskslits == 0) {
05958         cpl_msg_error(cpl_func, "Empty slits table");
05959         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
05960         return NULL;
05961     }
05962 
05963     if (nslits > 25) {
05964         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
05965         positions = mos_identify_slits_fast(slits, maskslits, global);
05966         if (positions == NULL)
05967             cpl_error_set_where(cpl_func);
05968         return positions;
05969     }
05970 
05971     /*
05972      * Guarantee that both input tables are sorted in the same way
05973      */
05974 
05975     sort_col = cpl_propertylist_new();
05976     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05977     cpl_table_sort(slits, sort_col);
05978     cpl_table_sort(maskslits, sort_col);
05979     cpl_propertylist_delete(sort_col);
05980 
05981     /*
05982      * First we handle all the special cases (too few slits...)
05983      */
05984 
05985     if (nslits < 3 && nmaskslits > nslits) {
05986 
05987         /*
05988          * If there are just 1 or 2 slits on the CCD, and more on the
05989          * mask, the ambiguity cannot be solved, and an error is returned.
05990          * This is a case that must be solved with a first-guess relation
05991          * between mask and CCD.
05992          */
05993 
05994         if (nslits > 1)
05995             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
05996                             "with the %d mask slits: process will continue "
05997                             "using the detected CCD slits positions", nslits,
05998                             nmaskslits);
05999         else
06000             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
06001                             "the %d mask slits: process will continue using "
06002                             "the detected CCD slit position", nmaskslits);
06003         return NULL;
06004     }
06005 
06006     if (nmaskslits < 3 && nslits > nmaskslits) {
06007 
06008         /*
06009          * If there are less than 3 slits on the mask the ambiguity cannot
06010          * be solved, and an error is returned. This is a case that must
06011          * be solved with a first-guess relation between mask and CCD.
06012          */
06013 
06014         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
06015                         "the %d mask slits: process will continue using "
06016                         "the detected CCD slits positions", nslits,
06017                         nmaskslits);
06018         return NULL;
06019     }
06020 
06021     /*
06022      * Pattern matching related operations begin here. Two pattern
06023      * matching will be run, one based on the "top" and another one
06024      * based on the "bottom" slit coordinates. The one with the
06025      * smallest rms in the Y coordinate will be chosen.
06026      */
06027 
06028     xtop  = cpl_table_get_data_double(slits, "xtop");
06029     ytop  = cpl_table_get_data_double(slits, "ytop");
06030     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06031     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06032 
06033     xbot  = cpl_table_get_data_double(slits, "xbottom");
06034     ybot  = cpl_table_get_data_double(slits, "ybottom");
06035     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06036     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06037 
06038     top_data    = cpl_matrix_new(2, nslits);
06039     top_pattern = cpl_matrix_new(2, nmaskslits);
06040     bot_data    = cpl_matrix_new(2, nslits);
06041     bot_pattern = cpl_matrix_new(2, nmaskslits);
06042 
06043     for (i = 0; i < nslits; i++)
06044         cpl_matrix_set(top_data, 0, i, xtop[i]);
06045 
06046     for (i = 0; i < nslits; i++)
06047         cpl_matrix_set(top_data, 1, i, ytop[i]);
06048 
06049     for (i = 0; i < nmaskslits; i++)
06050         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06051 
06052     for (i = 0; i < nmaskslits; i++)
06053         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06054 
06055     for (i = 0; i < nslits; i++)
06056         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06057 
06058     for (i = 0; i < nslits; i++)
06059         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06060 
06061     for (i = 0; i < nmaskslits; i++)
06062         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06063 
06064     for (i = 0; i < nmaskslits; i++)
06065         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06066 
06067     if (nmaskslits > nslits)
06068         use_pattern = nslits;
06069     else
06070         use_pattern = nmaskslits;
06071 
06072 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 3, 0)
06073     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06074                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06075                                      &top_mpattern, &top_scale, &top_angle);
06076 
06077     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06078                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06079                                      &bot_mpattern, &bot_scale, &bot_angle);
06080 #else
06081     top_ident = NULL;
06082     bot_ident = NULL;
06083 #endif
06084 
06085     if (top_ident == NULL && bot_ident == NULL) {
06086         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06087                         "the %d found CCD slits with the %d mask slits: "
06088                         "process will continue using the detected CCD "
06089                         "slits positions", nslits, nmaskslits);
06090         return NULL;
06091     }
06092 
06093     found_slits_top = 0;
06094     found_slits_bot = 0;
06095     if (top_ident && bot_ident) {
06096         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06097                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06098         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06099                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06100         if (fabs(top_angle) < fabs(bot_angle))
06101             angle = fabs(top_angle);
06102         else
06103             angle = fabs(bot_angle);
06104         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06105         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06106     }
06107     else if (top_ident) {
06108         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06109         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06110         angle = fabs(top_angle);
06111         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06112     }
06113     else {
06114         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06115         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06116         angle = fabs(bot_angle);
06117         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06118     }
06119 
06120     cpl_array_delete(top_ident);
06121     cpl_array_delete(bot_ident);
06122 
06123     if (angle > 4.0) {
06124         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06125                         "angle is expected to be around zero. This match is "
06126                         "rejected: the process will continue using the %d "
06127                         "detected CCD slits positions", nslits);
06128         return NULL;
06129     }
06130 
06131     found_slits = found_slits_top;
06132     if (found_slits < found_slits_bot)
06133         found_slits = found_slits_bot;     /* Max value */
06134 
06135     if (found_slits < 4) {
06136         cpl_msg_warning(cpl_func,
06137                         "Too few safely identified slits: %d out of %d "
06138                         "candidates (%d expected). Process will continue "
06139                         "using the detected CCD slits positions", found_slits,
06140                         nslits, nmaskslits);
06141         return NULL;
06142     }
06143 
06144     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06145                  "candidates\n(%d expected)", found_slits, nslits,
06146                  nmaskslits);
06147 
06148     if (found_slits_top < 4)
06149         found_slits_top = 0;
06150 
06151     if (found_slits_bot < 4)
06152         found_slits_bot = 0;
06153 
06154     /*
06155      * Now for each set select the points of the identified slits, and fit
06156      * two bivariate polynomials to determine a first approximate relation
06157      * between positions on the mask and positions on the CCD.
06158      */
06159 
06160     for (i = 0; i < 2; i++) {
06161         if (i) {
06162             found_slits = found_slits_top;
06163             mdata = top_mdata;
06164             mpattern = top_mpattern;
06165         }
06166         else {
06167             found_slits = found_slits_bot;
06168             mdata = bot_mdata;
06169             mpattern = bot_mpattern;
06170         }
06171 
06172         if (found_slits == 0)
06173             continue;
06174         else if (found_slits < 10)
06175             degree = 1;
06176         else
06177             degree = 2;
06178 
06179         xpos  = cpl_vector_wrap(found_slits,
06180                                 cpl_matrix_get_data(mdata)              );
06181         ypos  = cpl_vector_wrap(found_slits,
06182                                 cpl_matrix_get_data(mdata) + found_slits);
06183         xmpos = cpl_vector_wrap(found_slits,
06184                                 cpl_matrix_get_data(mpattern)              );
06185         ympos = cpl_vector_wrap(found_slits,
06186                                 cpl_matrix_get_data(mpattern) + found_slits);
06187         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06188         xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06189         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06190 
06191         cpl_bivector_unwrap_vectors(mpos);
06192         cpl_vector_unwrap(xpos);
06193         cpl_vector_unwrap(ypos);
06194         cpl_vector_unwrap(xmpos);
06195         cpl_vector_unwrap(ympos);
06196         cpl_matrix_delete(mdata);
06197         cpl_matrix_delete(mpattern);
06198 
06199         if (i) {
06200             top_xpoly = xpoly;
06201             top_ypoly = ypoly;
06202             top_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06203             top_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06204         }
06205         else {
06206             bot_xpoly = xpoly;
06207             bot_ypoly = ypoly;
06208             bot_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06209             bot_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06210         }
06211     }
06212 
06213     if (top_xpoly && bot_xpoly) {
06214         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06215             xrms = top_xrms;
06216             xpoly = top_xpoly;
06217             cpl_polynomial_delete(bot_xpoly);
06218         }
06219         else {                      /* bottom X solution wins... */
06220             xrms = bot_xrms;
06221             xpoly = bot_xpoly;
06222             cpl_polynomial_delete(top_xpoly);
06223         }
06224     }
06225     else if (top_xpoly) {
06226         xrms = top_xrms;
06227         xpoly = top_xpoly;
06228     }
06229     else {
06230         xrms = bot_xrms;
06231         xpoly = bot_xpoly;
06232     }
06233 
06234     if (top_ypoly && bot_ypoly) {
06235         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06236             yrms = top_yrms;
06237             ypoly = top_ypoly;
06238             cpl_polynomial_delete(bot_ypoly);
06239         }
06240         else {                      /* bottom Y solution wins... */
06241             yrms = bot_yrms;
06242             ypoly = bot_ypoly;
06243             cpl_polynomial_delete(top_ypoly);
06244         }
06245     }
06246     else if (top_ypoly) {
06247         yrms = top_yrms;
06248         ypoly = top_ypoly;
06249     }
06250     else {
06251         yrms = bot_yrms;
06252         ypoly = bot_ypoly;
06253     }
06254 
06255     if (xpoly == NULL || ypoly == NULL) {
06256         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06257                         "identified slits positions cannot be improved.");
06258         cpl_polynomial_delete(xpoly);
06259         cpl_polynomial_delete(ypoly);
06260         cpl_error_reset();
06261         return NULL;
06262     }
06263 
06264     cpl_msg_info(cpl_func,
06265                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06266                  xrms, yrms);
06267 
06268     if (global) {
06269         write_global_distortion(global, 0, xpoly);
06270         write_global_distortion(global, 7, ypoly);
06271     }
06272 
06273     /*
06274      * The fit was successful: use the polynomials to obtain a new
06275      * position table with the improved positions of the slits
06276      */
06277 
06278     positions = cpl_table_duplicate(maskslits);
06279     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06280     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06281     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06282     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06283 
06284     point = cpl_vector_new(2);
06285     dpoint = cpl_vector_get_data(point);
06286 
06287     for (i = 0; i < nmaskslits; i++) {
06288         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06289         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06290         cpl_table_set_double(positions, "xtop", i,
06291                              cpl_polynomial_eval(xpoly, point));
06292         cpl_table_set_double(positions, "ytop", i,
06293                              cpl_polynomial_eval(ypoly, point));
06294         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06295         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06296         cpl_table_set_double(positions, "xbottom", i,
06297                              cpl_polynomial_eval(xpoly, point));
06298         cpl_table_set_double(positions, "ybottom", i,
06299                              cpl_polynomial_eval(ypoly, point));
06300     }
06301 
06302     cpl_vector_delete(point);
06303     cpl_polynomial_delete(xpoly);
06304     cpl_polynomial_delete(ypoly);
06305 
06306     cpl_table_erase_column(positions, "xmtop");
06307     cpl_table_erase_column(positions, "ymtop");
06308     cpl_table_erase_column(positions, "xmbottom");
06309     cpl_table_erase_column(positions, "ymbottom");
06310 
06311     if (nmaskslits > nslits)
06312         cpl_msg_info(cpl_func,
06313                      "Finally identified slits: %d out of %d expected\n"
06314                      "(%d recovered)", nmaskslits, nmaskslits,
06315                      nmaskslits - nslits);
06316     else if (nmaskslits < nslits)
06317         cpl_msg_info(cpl_func,
06318                      "Finally identified slits: %d out of %d expected\n"
06319                      "(%d rejected)", nmaskslits, nmaskslits,
06320                      nslits - nmaskslits);
06321     else
06322         cpl_msg_info(cpl_func,
06323                      "Finally identified slits: %d out of %d expected",
06324                      nmaskslits, nmaskslits);
06325 
06326     return positions;
06327 
06328 }
06329 
06330 
06331 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06332                                    cpl_table *global)
06333 {
06334     const char *func = "mos_identify_slits_fast";
06335 
06336     cpl_propertylist *sort_col;
06337     cpl_table        *positions;
06338     cpl_vector       *scales;
06339     cpl_vector       *angles;
06340     cpl_vector       *point;
06341     cpl_vector       *xpos;
06342     cpl_vector       *ypos;
06343     cpl_vector       *xmpos;
06344     cpl_vector       *ympos;
06345     cpl_bivector     *mpos;
06346     cpl_polynomial   *xpoly = NULL;
06347     cpl_polynomial   *ypoly = NULL;
06348     cpl_error_code    error;
06349     int nslits;
06350     int nmaskslits;
06351     int found_slits;
06352     int i, j, k;
06353 
06354     double  dist1, dist2, dist3, dist, mindist;
06355     double  scale, minscale, maxscale;
06356     double  angle, minangle, maxangle;
06357     double *dscale;
06358     double *dangle;
06359     double *dpoint;
06360     double *xtop;
06361     double *ytop;
06362     double *xbottom;
06363     double *ybottom;
06364     double *xcenter;
06365     double *ycenter;
06366     double *xpseudo;
06367     double *ypseudo;
06368     int    *slit_id;
06369     double *xmtop;
06370     double *ymtop;
06371     double *xmbottom;
06372     double *ymbottom;
06373     double *xmcenter;
06374     double *ymcenter;
06375     double *xmpseudo;
06376     double *ympseudo;
06377     double  xmse, ymse;
06378     int    *mslit_id;
06379     int    *good;
06380     int     minpos;
06381     int     degree;
06382 
06383     double  sradius = 0.01;   /* Candidate input argument... */
06384     int     in_sradius;
06385 
06386     double pi = 3.14159265358979323846;
06387 
06388 
06389     error = mos_validate_slits(slits);
06390     if (error) {
06391         cpl_msg_error(func, "CCD slits table validation: %s", 
06392                       cpl_error_get_message());
06393         cpl_error_set(func, error);
06394         return NULL;
06395     }
06396 
06397     error = mos_validate_slits(maskslits);
06398     if (error) {
06399         cpl_msg_error(func, "Mask slits table validation: %s", 
06400                       cpl_error_get_message());
06401         cpl_error_set(func, error);
06402         return NULL;
06403     }
06404 
06405     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06406         cpl_msg_error(func, "Missing slits identifiers");
06407         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06408         return NULL;
06409     }
06410 
06411     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06412         cpl_msg_error(func, "Wrong type used for slits identifiers");
06413         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06414         return NULL;
06415     }
06416 
06417     nslits = cpl_table_get_nrow(slits);
06418     nmaskslits = cpl_table_get_nrow(maskslits);
06419 
06420     if (nslits == 0 || nmaskslits == 0) {
06421         cpl_msg_error(func, "Empty slits table");
06422         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06423         return NULL;
06424     }
06425 
06426 
06427     /*
06428      * Compute middle point coordinates for each slit listed in both
06429      * input tables.
06430      */
06431 
06432     if (cpl_table_has_column(slits, "xcenter"))
06433         cpl_table_erase_column(slits, "xcenter");
06434 
06435     if (cpl_table_has_column(slits, "ycenter"))
06436         cpl_table_erase_column(slits, "ycenter");
06437 
06438     if (cpl_table_has_column(maskslits, "xcenter"))
06439         cpl_table_erase_column(maskslits, "xcenter");
06440 
06441     if (cpl_table_has_column(maskslits, "ycenter"))
06442         cpl_table_erase_column(maskslits, "ycenter");
06443 
06444     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06445     cpl_table_add_columns(slits, "xcenter", "xbottom");
06446     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06447     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06448     cpl_table_add_columns(slits, "ycenter", "ybottom");
06449     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06450 
06451     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06452     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06453     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06454     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06455     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06456     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06457 
06458 
06459     /*
06460      * Guarantee that both input tables are sorted in the same way
06461      */
06462 
06463     sort_col = cpl_propertylist_new();
06464     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06465     cpl_table_sort(slits, sort_col);
06466     cpl_table_sort(maskslits, sort_col);
06467     cpl_propertylist_delete(sort_col);
06468 
06469 
06470     /*
06471      * First we handle all the special cases (too few slits...)
06472      */
06473 
06474     if (nslits < 3 && nmaskslits > nslits) {
06475 
06476         /*
06477          * If there are just 1 or 2 slits on the CCD, and more on the
06478          * mask, the ambiguity cannot be solved, and an error is returned.
06479          * This is a case that must be solved with a first-guess relation
06480          * between mask and CCD.
06481          */
06482 
06483         if (nslits > 1)
06484             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06485                             "%d mask slits: process will continue using the "
06486                             "detected CCD slit position", nmaskslits);
06487         else
06488             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06489                             "the %d mask slits: process will continue using "
06490                             "the detected CCD slits positions", nslits, 
06491                             nmaskslits);
06492         return NULL;
06493     }
06494 
06495     if (nslits <= 3 && nslits == nmaskslits) {
06496 
06497         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06498         cpl_msg_warning(func, "Their detected positions are left unchanged");
06499 
06500         /*
06501          * If there are just up to 3 slits, both on the mask and on the CCD,
06502          * we can reasonably hope that those slits were found, and accept 
06503          * that their positions on the CCD cannot be improved. We prepare
06504          * therefore an output position table containing the slits with
06505          * their original positions. We can however give an estimate of
06506          * the platescale if there is more than one slit.
06507          */
06508 
06509         positions = cpl_table_duplicate(slits);
06510         cpl_table_erase_column(slits, "xcenter");
06511         cpl_table_erase_column(slits, "ycenter");
06512         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06513         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06514         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06515         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06516         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06517         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06518         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06519         cpl_table_erase_column(maskslits, "xcenter");
06520         cpl_table_erase_column(maskslits, "ycenter");
06521 
06522         if (nslits > 1) {
06523             xcenter = cpl_table_get_data_double(positions, "xcenter");
06524             ycenter = cpl_table_get_data_double(positions, "ycenter");
06525             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06526             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06527 
06528             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06529                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06530             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06531                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06532             scale = sqrt(dist1/dist2);
06533 
06534             if (nslits == 3) {
06535                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06536                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06537                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06538                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06539                 scale += sqrt(dist1/dist2);
06540                 scale /= 2;
06541             }
06542 
06543             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06544         }
06545 
06546         return positions;
06547     }
06548 
06549     if (nmaskslits < 3 && nslits > nmaskslits) {
06550 
06551         /*
06552          * If there are less than 3 slits on the mask the ambiguity cannot 
06553          * be solved, and an error is returned. This is a case that must 
06554          * be solved with a first-guess relation between mask and CCD.
06555          */
06556 
06557         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06558                         "the %d mask slits: process will continue using "
06559                         "the detected CCD slits positions", nslits, 
06560                         nmaskslits);
06561         return NULL;
06562     }
06563 
06564 
06565     /*
06566      * At this point of the program all the region of the plane
06567      * (nslits, nmaskslits) where either or both mask and CCD display
06568      * less than 3 slits are handled in some way. It would be better
06569      * to add in this place a special handling for identifying slits
06570      * in case of a very reduced number of slits (say, below 6).
06571      * It is also clear that if there are many more slits on the
06572      * mask than on the CCD, or many more on the CCD than on the
06573      * mask, something went deeply wrong with the preliminary 
06574      * wavelength calibration. Such cases should be handled with
06575      * a _complete_ pattern-recognition algorithm based on the
06576      * construction of all possible triangles. For the moment, 
06577      * we go directly to the limited pattern-recognition applied
06578      * below, based on triangles build only for consecutive slits.
06579      * This is reasonably safe, since the preliminary wavelength
06580      * calibration performed by mos_identify_peaks() is generally
06581      * robust.
06582      */
06583 
06584 
06585     /*
06586      * Compute (X, Y) coordinates on pseudo-plane describing the
06587      * different position ratios of successive slits, in both
06588      * input tables.
06589      */
06590 
06591     if (cpl_table_has_column(slits, "xpseudo"))
06592         cpl_table_erase_column(slits, "xpseudo");
06593 
06594     if (cpl_table_has_column(slits, "ypseudo"))
06595         cpl_table_erase_column(slits, "ypseudo");
06596 
06597     if (cpl_table_has_column(maskslits, "xpseudo"))
06598         cpl_table_erase_column(maskslits, "xpseudo");
06599 
06600     if (cpl_table_has_column(maskslits, "ypseudo"))
06601         cpl_table_erase_column(maskslits, "ypseudo");
06602 
06603     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06604     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06605 
06606     xcenter = cpl_table_get_data_double(slits, "xcenter");
06607     ycenter = cpl_table_get_data_double(slits, "ycenter");
06608     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06609     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06610 
06611     for (i = 1; i < nslits - 1; i++) {
06612         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06613               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06614         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06615               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06616         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06617               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06618         xpseudo[i] = sqrt(dist1/dist2);
06619         ypseudo[i] = sqrt(dist3/dist2);
06620     }
06621 
06622     cpl_table_set_invalid(slits, "xpseudo", 0);
06623     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06624     cpl_table_set_invalid(slits, "ypseudo", 0);
06625     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06626 
06627     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06628     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06629 
06630     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06631     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06632     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06633     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06634 
06635     for (i = 1; i < nmaskslits - 1; i++) {
06636         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06637               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06638         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06639               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06640         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06641               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06642         xmpseudo[i] = sqrt(dist1/dist2);
06643         ympseudo[i] = sqrt(dist3/dist2);
06644     }
06645     
06646     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06647     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06648     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06649     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06650 
06651 
06652     /*
06653      * For each (X, Y) on the pseudo-plane related to the mask positions,
06654      * find the closest (X, Y) on the pseudo-plane related to the CCD
06655      * positions. If the closest point is closer than a given search
06656      * radius, a triangle has been found, and 3 slits are identified.
06657      * However, if more than one point is found within the search
06658      * radius, we have an ambiguity and this is rejected.
06659      */
06660 
06661     if (cpl_table_has_column(slits, "slit_id"))
06662         cpl_table_erase_column(slits, "slit_id");
06663     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06664     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06665 
06666     for (i = 1; i < nmaskslits - 1; i++) {
06667         in_sradius = 0;
06668         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06669                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06670         minpos = 1;
06671         if (mindist < sradius*sradius)
06672             in_sradius++;
06673         for (j = 2; j < nslits - 1; j++) {
06674             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06675                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06676             if (dist < sradius*sradius)
06677                 in_sradius++;
06678             if (in_sradius > 1)    /* More than one triangle within radius */
06679                 break;
06680             if (mindist > dist) {
06681                 mindist = dist;
06682                 minpos = j;
06683             }
06684         }
06685 
06686         mindist = sqrt(mindist);
06687 
06688         if (mindist < sradius && in_sradius == 1) {
06689             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06690             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06691             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06692         }
06693     }
06694 
06695 
06696     /*
06697      * At this point, the slit_id column contains invalid elements 
06698      * corresponding to unidentified slits.
06699      */
06700 
06701     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06702 
06703     if (found_slits < 3) {
06704         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06705                         "%d out of %d", found_slits, nslits);
06706         if (nslits == nmaskslits) {
06707             cpl_msg_warning(func, "(this is not an error, it could be caused "
06708                             "by a mask with regularly located slits)");
06709             cpl_msg_warning(func, "The detected slits positions are left "
06710                             "unchanged");
06711 
06712             /*
06713              * If there are less than 3 identified slits, this is probably 
06714              * a mask with regularly spaced slits (leading to an ambiguous
06715              * pattern). Only in the case all expected slits appear to have 
06716              * been found on the CCD we can proceed...
06717              */
06718 
06719             cpl_table_erase_column(slits, "slit_id");
06720             cpl_table_erase_column(slits, "xpseudo");
06721             cpl_table_erase_column(slits, "ypseudo");
06722             positions = cpl_table_duplicate(slits);
06723             cpl_table_erase_column(slits, "xcenter");
06724             cpl_table_erase_column(slits, "ycenter");
06725 
06726             cpl_table_erase_column(maskslits, "xpseudo");
06727             cpl_table_erase_column(maskslits, "ypseudo");
06728             cpl_table_duplicate_column(positions, "xmtop", 
06729                                        maskslits, "xtop");
06730             cpl_table_duplicate_column(positions, "ymtop", 
06731                                        maskslits, "ytop");
06732             cpl_table_duplicate_column(positions, "xmbottom", 
06733                                        maskslits, "xbottom");
06734             cpl_table_duplicate_column(positions, "ymbottom", 
06735                                        maskslits, "ybottom");
06736             cpl_table_duplicate_column(positions, "xmcenter", 
06737                                        maskslits, "xcenter");
06738             cpl_table_duplicate_column(positions, "ymcenter", 
06739                                        maskslits, "ycenter");
06740             cpl_table_duplicate_column(positions, "slit_id", 
06741                                        maskslits, "slit_id");
06742             cpl_table_erase_column(maskslits, "xcenter");
06743             cpl_table_erase_column(maskslits, "ycenter");
06744             return positions;
06745         }
06746         else {
06747             cpl_table_erase_column(slits, "slit_id");
06748             cpl_table_erase_column(slits, "xpseudo");
06749             cpl_table_erase_column(slits, "ypseudo");
06750             positions = cpl_table_duplicate(slits);
06751             cpl_table_erase_column(slits, "xcenter");
06752             cpl_table_erase_column(slits, "ycenter");
06753             cpl_msg_warning(func, "(the failure could be caused "
06754                             "by a mask with regularly located slits)");
06755             return NULL;
06756         }
06757     }
06758     else {
06759         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06760                      "candidates (%d expected)", found_slits, nslits, 
06761                      nmaskslits);
06762     }
06763 
06764 
06765     /*
06766      * Create a table with the coordinates of the preliminarily identified 
06767      * slits, both on CCD and mask. The original order of the slits positions 
06768      * is preserved.
06769      */
06770 
06771     positions = cpl_table_new(found_slits);
06772     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
06773     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
06774     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
06775     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
06776     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
06777     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
06778     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
06779     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
06780     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
06781     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
06782     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
06783     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
06784     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
06785     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
06786     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
06787 
06788     slit_id = cpl_table_get_data_int   (slits, "slit_id");
06789     xtop    = cpl_table_get_data_double(slits, "xtop");
06790     ytop    = cpl_table_get_data_double(slits, "ytop");
06791     xbottom = cpl_table_get_data_double(slits, "xbottom");
06792     ybottom = cpl_table_get_data_double(slits, "ybottom");
06793     xcenter = cpl_table_get_data_double(slits, "xcenter");
06794     ycenter = cpl_table_get_data_double(slits, "ycenter");
06795 
06796     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
06797     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
06798     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
06799     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
06800     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
06801     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
06802     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
06803 
06804 
06805     /*
06806      * Transferring the valid slits information to the new table.
06807      * Note that invalid elements are coded as 0 in the internal
06808      * buffer, and this is the way they are recognised and excluded.
06809      */
06810 
06811     k = 0;
06812     cpl_table_fill_invalid_int(slits, "slit_id", 0);
06813     for (i = 0; i < nmaskslits; i++) {
06814         for (j = 0; j < nslits; j++) {
06815             if (slit_id[j] == 0)
06816                 continue; /* Skip invalid slit */
06817             if (mslit_id[i] == slit_id[j]) {
06818                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
06819 
06820                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
06821                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
06822                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
06823                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
06824                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
06825                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
06826 
06827                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
06828                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
06829                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
06830                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
06831                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
06832                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
06833 
06834                 k++;
06835 
06836                 break;
06837             }
06838         }
06839     }
06840 
06841     found_slits = k;
06842 
06843     cpl_table_erase_column(slits, "slit_id");
06844     cpl_table_erase_column(slits, "xpseudo");
06845     cpl_table_erase_column(slits, "ypseudo");
06846     cpl_table_erase_column(slits, "xcenter");
06847     cpl_table_erase_column(slits, "ycenter");
06848     cpl_table_erase_column(maskslits, "xpseudo");
06849     cpl_table_erase_column(maskslits, "ypseudo");
06850     cpl_table_erase_column(maskslits, "xcenter");
06851     cpl_table_erase_column(maskslits, "ycenter");
06852 
06853 
06854     /*
06855      * Find the median platescale and rotation angle from the identified 
06856      * slits, and then exclude slits outlaying more than 10% from the 
06857      * median platescale, and more than 2 degrees from the median
06858      * rotation angle.
06859      */
06860 
06861     ytop    = cpl_table_get_data_double(positions, "ytop");
06862     ybottom = cpl_table_get_data_double(positions, "ybottom");
06863     xcenter = cpl_table_get_data_double(positions, "xcenter");
06864     ycenter = cpl_table_get_data_double(positions, "ycenter");
06865     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06866     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06867 
06868     scales = cpl_vector_new(found_slits - 1);
06869     dscale = cpl_vector_get_data(scales);
06870     angles = cpl_vector_new(found_slits - 1);
06871     dangle = cpl_vector_get_data(angles);
06872 
06873     for (i = 1; i < found_slits; i++) {
06874         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06875               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06876         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
06877               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
06878         dscale[i-1] = sqrt(dist1/dist2);
06879         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
06880                             xcenter[i-1] - xcenter[i])
06881                     - atan2(ymcenter[i-1] - ymcenter[i], 
06882                             xmcenter[i-1] - xmcenter[i]);
06883         dangle[i-1] *= 180;
06884         dangle[i-1] /= pi;
06885     }
06886 
06887     minscale = cpl_vector_get_min(scales);
06888     scale = cpl_vector_get_median_const(scales);
06889     maxscale = cpl_vector_get_max(scales);
06890 
06891     minangle = cpl_vector_get_min(angles);
06892     angle = cpl_vector_get_median_const(angles);
06893     maxangle = cpl_vector_get_max(angles);
06894 
06895     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
06896     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
06897                  minscale, maxscale);
06898 
06899     cpl_msg_info(func, "Median rotation: %f degrees", angle);
06900     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
06901                  minangle, maxangle);
06902 
06903     good = cpl_table_get_data_int(positions, "good");
06904 
06905     good[0] = good[found_slits - 1] = 1;
06906     for (i = 1; i < found_slits; i++) {
06907         if (fabs((dscale[i-1] - scale)/scale) < 0.10
06908          && fabs(dangle[i-1] - angle) < 2) {
06909             good[i-1]++;
06910             good[i]++;
06911         }
06912     }
06913 
06914     for (i = 0; i < found_slits; i++) {
06915         if (good[i] < 2)
06916             good[i] = 0;
06917         else
06918             good[i] = 1;
06919     }
06920 
06921 /*
06922     for (i = 1; i < found_slits; i++)
06923         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
06924             good[i-1] = good[i] = 1;
06925 */
06926 
06927 /* DEBUG ************+
06928     for (i = 0; i < found_slits; i++) {
06929         if (good[i]) {
06930             if (i == found_slits - 1)
06931                 printf("include slit %d, prev = %f, %f\n", 
06932                        i, dscale[i-1], dangle[i-1]);
06933             else if (i == 0)
06934                 printf("include slit %d, next %f, %f\n", 
06935                        i, dscale[i], dangle[i]);
06936             else
06937                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
06938                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06939         }
06940         else {
06941             if (i == found_slits - 1)
06942                 printf("EXclude slit %d, prev = %f, %f\n", 
06943                        i, dscale[i-1], dangle[i-1]);
06944             else if (i == 0)
06945                 printf("EXclude slit %d, next %f, %f\n", 
06946                        i, dscale[i], dangle[i]);
06947             else
06948                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
06949                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06950         }
06951     }
06952 +*********** DEBUG */
06953 
06954     cpl_vector_delete(scales);
06955     cpl_vector_delete(angles);
06956 
06957     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
06958     cpl_table_erase_selected(positions);
06959     cpl_table_erase_column(positions, "good");
06960     found_slits = cpl_table_get_nrow(positions);
06961 
06962     if (found_slits < 4) {
06963 
06964         /*
06965          * If the self-consistency check gives such a poor result,
06966          * something must have gone really wrong in the preliminary
06967          * wavelength calibration... Nothing can be done.
06968          */
06969 
06970         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
06971                         "candidates (%d expected). Process will continue "
06972                         "using the detected CCD slits positions", found_slits, 
06973                         nslits, nmaskslits);
06974         cpl_table_delete(positions);
06975         return NULL;
06976     }
06977     else {
06978         cpl_msg_info(func, "Safely identified slits: %d out of %d "
06979                      "candidates\n(%d expected)", found_slits, nslits,
06980                      nmaskslits);
06981     }
06982 
06983 
06984     /*
06985      * Now select the central points of the identified slits, and
06986      * fit two bivariate polynomials to determine a first approximate
06987      * relation between positions on the mask and positions on the CCD.
06988      */
06989 
06990     xpos = cpl_vector_wrap(found_slits, 
06991                            cpl_table_get_data_double(positions, "xcenter"));
06992     ypos = cpl_vector_wrap(found_slits, 
06993                            cpl_table_get_data_double(positions, "ycenter"));
06994     xmpos = cpl_vector_wrap(found_slits, 
06995                             cpl_table_get_data_double(positions, "xmcenter"));
06996     ympos = cpl_vector_wrap(found_slits, 
06997                             cpl_table_get_data_double(positions, "ymcenter"));
06998     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
06999 
07000     if (found_slits < 10)
07001         degree = 1;
07002     else
07003         degree = 2;
07004 
07005     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
07006     if (xpoly != NULL)
07007         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
07008     cpl_bivector_unwrap_vectors(mpos);
07009     cpl_vector_unwrap(xpos);
07010     cpl_vector_unwrap(ypos);
07011     cpl_vector_unwrap(xmpos);
07012     cpl_vector_unwrap(ympos);
07013     if (ypoly == NULL) {
07014         if (found_slits == nmaskslits) {
07015             cpl_msg_warning(func, "Fit failure: the accuracy of the "
07016                             "identified slits positions is not improved.");
07017 
07018             /*
07019              * The determination of the transformation from mask to CCD
07020              * failed, but since all slits have been already identified
07021              * this is not a problem: data can be reduced also without
07022              * such transformation. Simply the accuracy of the slits 
07023              * positions cannot be improved.
07024              */ 
07025 
07026         } else {
07027             cpl_msg_info(func, "Fit failure: not all slits have been "
07028                          "identified. Process will continue using "
07029                          "the detected CCD slits positions");
07030         }
07031 
07032         cpl_polynomial_delete(xpoly);
07033         return positions;
07034     }
07035 
07036     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07037                  sqrt(xmse), sqrt(ymse));
07038 
07039     if (global) {
07040         write_global_distortion(global, 0, xpoly);
07041         write_global_distortion(global, 7, ypoly);
07042     }
07043 
07044     /*
07045      * The fit was successful: use the polynomials to obtain a new 
07046      * position table with the improved positions of the slits
07047      */
07048 
07049     cpl_table_delete(positions);
07050 
07051     positions = cpl_table_duplicate(maskslits);
07052     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07053     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07054     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07055     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07056 
07057     point = cpl_vector_new(2);
07058     dpoint = cpl_vector_get_data(point);
07059 
07060     for (i = 0; i < nmaskslits; i++) {
07061         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07062         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07063         cpl_table_set_double(positions, "xtop", i, 
07064                              cpl_polynomial_eval(xpoly, point));
07065         cpl_table_set_double(positions, "ytop", i, 
07066                              cpl_polynomial_eval(ypoly, point));
07067         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07068         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07069         cpl_table_set_double(positions, "xbottom", i, 
07070                              cpl_polynomial_eval(xpoly, point));
07071         cpl_table_set_double(positions, "ybottom", i, 
07072                              cpl_polynomial_eval(ypoly, point));
07073     }
07074 
07075     cpl_vector_delete(point);
07076     cpl_polynomial_delete(xpoly);
07077     cpl_polynomial_delete(ypoly);
07078 
07079     cpl_table_erase_column(positions, "xmtop");
07080     cpl_table_erase_column(positions, "ymtop");
07081     cpl_table_erase_column(positions, "xmbottom");
07082     cpl_table_erase_column(positions, "ymbottom");
07083 
07084     if (nmaskslits > nslits)
07085         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07086                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07087     else if (nmaskslits < nslits)
07088         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07089                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07090     else
07091         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07092                  nmaskslits, nmaskslits);
07093 
07094     return positions;
07095 }
07096 
07097 
07139 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07140                           double blue, double red, double dispersion)
07141 {
07142 
07143     const char  *func = "mos_trace_flat";
07144 
07145     cpl_image   *gradient;
07146     cpl_image   *sgradient;
07147     float       *dgradient;
07148     float        level = 500;   /* It was 250... */
07149     cpl_vector  *row;
07150     cpl_vector  *srow;
07151     cpl_vector **peaks;
07152     double      *peak;
07153     int         *slit_id;
07154     float       *g;
07155     double      *r;
07156     double      *xtop;
07157     double      *ytop;
07158     double      *xbottom;
07159     double      *ybottom;
07160     double       min, dist;
07161     double       sradius;
07162     double       tolerance;
07163     double       start_y, prev_y;
07164     int          minpos;
07165     int          nslits;
07166     int          nrows;
07167     int          step = 10;
07168     int          filtbox = 15;
07169     int          nx, ny, npix;
07170     int          pos, ypos;
07171     int          npeaks;
07172     int          pixel_above, pixel_below;
07173     int          i, j, k, l;
07174     char         trace_id[MAX_COLNAME];
07175 
07176     cpl_table   *traces;
07177 
07178 
07179     if (flat == NULL || slits == NULL) {
07180         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07181         return NULL;
07182     }
07183 
07184     if (dispersion <= 0.0) {
07185         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07186         return NULL;
07187     }
07188 
07189     if (red - blue < dispersion) {
07190         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07191         return NULL;
07192     }
07193 
07194     /*
07195      * Create a dummy slit_id column if it is missing in the
07196      * input slits table
07197      */
07198 
07199     nslits  = cpl_table_get_nrow(slits);
07200     if (1 != cpl_table_has_column(slits, "slit_id")) {
07201         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07202         for (i = 0; i < nslits; i++)
07203             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07204     }
07205 
07206     slit_id = cpl_table_get_data_int(slits, "slit_id");
07207 
07208     nx = cpl_image_get_size_x(flat);
07209     ny = cpl_image_get_size_y(flat);
07210     npix = nx * ny;
07211 
07212     gradient = cpl_image_duplicate(flat);
07213     dgradient = cpl_image_get_data_float(gradient);
07214 
07215     for (i = 0; i < ny - 1; i++) {
07216         k = i * nx;
07217         for (j = 0; j < nx; j++) {
07218             l = k + j;
07219             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07220         }
07221     }
07222 
07223     npix--;
07224     for (j = 0; j < nx; j++)
07225         dgradient[npix - j] = 0.0;
07226 
07227     cpl_image_turn(gradient, -1);
07228     nx = cpl_image_get_size_x(gradient);
07229     ny = cpl_image_get_size_y(gradient);
07230     sgradient = mos_image_vertical_median_filter(gradient, 
07231                                                  filtbox, 0, ny, 0, step);
07232     cpl_image_delete(gradient);
07233 
07234 
07235     /*
07236      * Remove background from processed image rows
07237      */
07238 
07239     dgradient = cpl_image_get_data_float(sgradient);
07240 
07241     for (i = 1; i <= ny; i += step) {
07242         row = cpl_vector_new_from_image_row(sgradient, i);
07243         srow = cpl_vector_filter_median_create(row, filtbox);
07244         cpl_vector_subtract(row, srow);
07245         cpl_vector_delete(srow);
07246         g = dgradient + (i-1)*nx;
07247         r = cpl_vector_get_data(row);
07248         for (j = 0; j < nx; j++)
07249             g[j] = r[j];
07250         cpl_vector_delete(row);
07251     }
07252 
07253 
07254     /*
07255      * Rotate (temporarily) the input slits table, to get coordinates
07256      * compatible with the rotated gradient image.
07257      */
07258 
07259     mos_rotate_slits(slits, 1, nx, ny);
07260     xtop    = cpl_table_get_data_double(slits, "xtop");
07261     ytop    = cpl_table_get_data_double(slits, "ytop");
07262     xbottom = cpl_table_get_data_double(slits, "xbottom");
07263     ybottom = cpl_table_get_data_double(slits, "ybottom");
07264 
07265 
07266     /*
07267      * Get positions of peaks candidates for each processed gradient
07268      * image row
07269      */
07270 
07271     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07272 
07273     for (i = 0; i < ny; i += step) {
07274         g = dgradient + i*nx;
07275         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07276 
07277         /* I thought this would be required, but apparently I was wrong.
07278          * Check twice... */
07279         if (peaks[i])
07280             cpl_vector_subtract_scalar(peaks[i], 0.5);
07281          
07282     }
07283 
07284     cpl_image_delete(sgradient);
07285 
07286 
07287     /*
07288      * Tracing the flat field spectra edges, starting from the
07289      * slits positions obtained at reference wavelength. The 
07290      * gradient maximum closest to each slits ends is found and
07291      * accepted only within a given search radius:
07292      */
07293 
07294     sradius = 5.0;  /* Pixel */
07295 
07296     /*
07297      * The tracing proceeds along the processed gradient image rows,
07298      * above and below the start position, finding the closest peak
07299      * to the previous obtained position, accepting the new position
07300      * only if it is closer than a given tolerance:
07301      */
07302 
07303     tolerance = 0.9;  /* Pixel */
07304 
07305     /*
07306      * The trace is attempted for a certain number of pixels above
07307      * and below the position of the reference wavelength:
07308      */
07309 
07310     pixel_above = (red - reference) / dispersion;
07311     pixel_below = (reference - blue) / dispersion;
07312 
07313 
07314     /*
07315      * Prepare the structure of the output table:
07316      */
07317 
07318     nrows = (ny-1)/step + 1;
07319     traces = cpl_table_new(nrows);
07320     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07321     cpl_table_set_column_unit(traces, "x", "pixel");
07322     for (i = 0, j = 0; i < ny; i += step, j++)
07323         cpl_table_set(traces, "x", j, i);
07324 
07325     for (i = 0; i < nslits; i++) {
07326 
07327         /*
07328          * Find the closest processed gradient image row
07329          */
07330 
07331         ypos = ytop[i];
07332 
07333         if (ypos < 0)
07334             ypos = 0;
07335         if (ypos >= ny)
07336             ypos = ny - 1;
07337 
07338         pos = ypos / step;
07339         pos *= step;
07340 
07341         /*
07342          * Find the peak in that row that is closest to xtop[i]
07343          */
07344 
07345         if (peaks[pos]) {
07346             peak = cpl_vector_get_data(peaks[pos]);
07347             npeaks = cpl_vector_get_size(peaks[pos]);
07348 
07349             min = fabs(peak[0] - xtop[i]);
07350             minpos = 0;
07351             for (j = 1; j < npeaks; j++) {
07352                 dist = fabs(peak[j] - xtop[i]);
07353                 if (min > dist) {
07354                     min = dist;
07355                     minpos = j;
07356                 }
07357             }
07358         }
07359         else {
07360             npeaks = 0;
07361         }
07362 
07363         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07364         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07365 
07366         if (min > sradius || npeaks == 0) {
07367             cpl_msg_warning(func, "Cannot find spectrum edge for "
07368                             "top (or left) end of slit %d", slit_id[i]);
07369         }
07370         else {
07371 
07372             /*
07373              * Add to output table the start y position. Note that
07374              * y positions are written in coordinates of the unrotated
07375              * image, i.e., with horizontal dispersion. Currently nx
07376              * is the x size of the temporarily rotated image (for
07377              * faster memory access), but it has the sense of a y size.
07378              */
07379 
07380             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07381             start_y = peak[minpos];
07382 
07383             /*
07384              * Perform the tracing of current edge. Above:
07385              */
07386 
07387             prev_y = start_y;
07388 
07389             for (j = pos + step; j < ny; j += step) {
07390                 if (j - pos > pixel_above)
07391                     break;
07392                 if (peaks[j]) {
07393                     peak = cpl_vector_get_data(peaks[j]);
07394                     npeaks = cpl_vector_get_size(peaks[j]);
07395                     min = fabs(peak[0] - prev_y);
07396                     minpos = 0;
07397                     for (k = 1; k < npeaks; k++) {
07398                         dist = fabs(peak[k] - prev_y);
07399                         if (min > dist) {
07400                             min = dist;
07401                             minpos = k;
07402                         }
07403                     }
07404                     if (min < tolerance) {
07405                         cpl_table_set(traces, trace_id, j/step, 
07406                                       nx - peak[minpos]);
07407                         prev_y = peak[minpos];
07408                     }
07409                 }
07410             }
07411 
07412             /*
07413              * Perform the tracing of current edge. Below:
07414              */
07415 
07416             prev_y = start_y;
07417 
07418             for (j = pos - step; j >= 0; j -= step) {
07419                 if (pos - j > pixel_below)
07420                     break;
07421                 if (peaks[j]) {
07422                     peak = cpl_vector_get_data(peaks[j]);
07423                     npeaks = cpl_vector_get_size(peaks[j]);
07424                     min = fabs(peak[0] - prev_y);
07425                     minpos = 0;
07426                     for (k = 1; k < npeaks; k++) {
07427                         dist = fabs(peak[k] - prev_y);
07428                         if (min > dist) {
07429                             min = dist;
07430                             minpos = k;
07431                         }
07432                     }
07433                     if (min < tolerance) {
07434                         cpl_table_set(traces, trace_id, j/step, 
07435                                       nx - peak[minpos]);
07436                         prev_y = peak[minpos];
07437                     }
07438                 }
07439             }
07440         }
07441 
07442 
07443         /*
07444          * Find the peak in that row that is closest to xbottom[i]
07445          */
07446 
07447         if (peaks[pos]) {
07448             peak = cpl_vector_get_data(peaks[pos]);
07449             npeaks = cpl_vector_get_size(peaks[pos]);
07450     
07451             min = fabs(peak[0] - xbottom[i]);
07452             minpos = 0;
07453             for (j = 1; j < npeaks; j++) {
07454                 dist = fabs(peak[j] - xbottom[i]);
07455                 if (min > dist) {
07456                     min = dist;
07457                     minpos = j;
07458                 }
07459             }
07460         }
07461         else {
07462             npeaks = 0;
07463         }
07464 
07465         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07466         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07467 
07468         if (min > sradius || npeaks == 0) {
07469             cpl_msg_warning(func, "Cannot find spectrum edge for "
07470                             "bottom (or right) end of slit %d", slit_id[i]);
07471         }
07472         else {
07473 
07474             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07475             start_y = peak[minpos]; 
07476 
07477             /*
07478              * Perform the tracing of current edge. Above:
07479              */
07480 
07481             prev_y = start_y;
07482 
07483             for (j = pos + step; j < ny; j += step) {
07484                 if (j - pos > pixel_above)
07485                     break;
07486                 if (peaks[j]) {
07487                     peak = cpl_vector_get_data(peaks[j]);
07488                     npeaks = cpl_vector_get_size(peaks[j]);
07489                     min = fabs(peak[0] - prev_y);
07490                     minpos = 0;
07491                     for (k = 1; k < npeaks; k++) {
07492                         dist = fabs(peak[k] - prev_y);
07493                         if (min > dist) {
07494                             min = dist;
07495                             minpos = k;
07496                         }
07497                     }
07498                     if (min < tolerance) {
07499                         cpl_table_set(traces, trace_id, j/step, 
07500                                       nx - peak[minpos]);
07501                         prev_y = peak[minpos];
07502                     }
07503                 }
07504             }
07505 
07506             /*
07507              * Perform the tracing of current edge. Below:
07508              */
07509 
07510             prev_y = start_y;
07511 
07512             for (j = pos - step; j >= 0; j -= step) {
07513                 if (pos - j > pixel_below)
07514                     break;
07515                 if (peaks[j]) {
07516                     peak = cpl_vector_get_data(peaks[j]);
07517                     npeaks = cpl_vector_get_size(peaks[j]);
07518                     min = fabs(peak[0] - prev_y);
07519                     minpos = 0;
07520                     for (k = 1; k < npeaks; k++) {
07521                         dist = fabs(peak[k] - prev_y);
07522                         if (min > dist) {
07523                             min = dist;
07524                             minpos = k;
07525                         }
07526                     }
07527                     if (min < tolerance) {
07528                         cpl_table_set(traces, trace_id, j/step, 
07529                                       nx - peak[minpos]);
07530                         prev_y = peak[minpos];
07531                     }
07532                 }
07533             }
07534         }
07535 
07536     }   /* End of loop on slits */
07537 
07538     for (i = 0; i < ny; i += step)
07539         cpl_vector_delete(peaks[i]);
07540     cpl_free(peaks);
07541 
07542     /*
07543      * Restore original orientation of slits positions table
07544      */
07545 
07546     mos_rotate_slits(slits, -1, ny, nx);
07547 
07548     return traces;
07549 
07550 }
07551 
07552 
07573 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07574 {
07575     const char *func = "mos_poly_trace";
07576 
07577     cpl_table      *polytraces;
07578     cpl_table      *dummy;
07579     cpl_vector     *x;
07580     cpl_vector     *trace;
07581     cpl_polynomial *polytrace;
07582     char            trace_id[MAX_COLNAME];
07583     char            trace_res[MAX_COLNAME];
07584     char            trace_mod[MAX_COLNAME];
07585     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07586                                                  /* Max order is 5 */
07587     double         *xdata;
07588     int            *slit_id;
07589     int             nslits;
07590     int             nrows;
07591     int             npoints;
07592     int             i, j, k;
07593 
07594 
07595     if (traces == NULL || slits == NULL) {
07596         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07597         return NULL;
07598     }
07599 
07600     if (order > 5) {
07601         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07602         return NULL;
07603     }
07604 
07605     nrows   = cpl_table_get_nrow(traces);
07606     xdata   = cpl_table_get_data_double(traces, "x");
07607     nslits  = cpl_table_get_nrow(slits);
07608     slit_id = cpl_table_get_data_int(slits, "slit_id");
07609 
07610     polytraces = cpl_table_new(2*nslits);
07611     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07612     for (i = 0; i <= order; i++)
07613         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07614 
07615     for (i = 0; i < nslits; i++) {
07616         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07617 
07618             if (j) {
07619                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07620                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07621                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07622             }
07623             else {
07624                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07625                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07626                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07627             }
07628 
07629             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07630 
07631             /*
07632              * The "dummy" table is just a tool for eliminating invalid
07633              * points from the vectors to be fitted.
07634              */
07635 
07636             dummy = cpl_table_new(nrows);
07637             cpl_table_duplicate_column(dummy, "x", traces, "x");
07638             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07639             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07640             if (npoints < 2 * order) {
07641                 cpl_table_delete(dummy);
07642                 continue;
07643             }
07644             cpl_table_erase_invalid(dummy);
07645             x     = cpl_vector_wrap(npoints, 
07646                                     cpl_table_get_data_double(dummy, "x"));
07647             trace = cpl_vector_wrap(npoints, 
07648                                     cpl_table_get_data_double(dummy, trace_id));
07649             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07650             cpl_vector_unwrap(x);
07651             cpl_vector_unwrap(trace);
07652             cpl_table_delete(dummy);
07653 
07654             /*
07655              * Screen bad solutions. At the moment, a primitive screening
07656              * consists in excluding solutions displaying excessive
07657              * curvature (larger than 1E-5 / pixel)
07658              */
07659 
07660             k = 2;
07661             if (cpl_polynomial_get_coeff(polytrace, &k) >  1.E-5) {
07662                 cpl_polynomial_delete(polytrace);
07663                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07664                 cpl_table_duplicate_column(traces, trace_res, traces, 
07665                                            trace_mod);
07666                 if (j) 
07667                     cpl_msg_warning(func, "Exclude bad curvature solution "
07668                            "for bottom (right) edge of slit %d", slit_id[i]);
07669                 else
07670                     cpl_msg_warning(func, "Exclude bad curvature solution "
07671                                 "for top (left) edge of slit %d", slit_id[i]);
07672                 continue;
07673             }
07674 
07675             /*
07676              * Write polynomial coefficients to the output table,
07677              * tagged with the appropriate slit_id
07678              */
07679 
07680             for (k = 0; k <= order; k++)
07681                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07682                                      cpl_polynomial_get_coeff(polytrace, &k));
07683 
07684             /*
07685              * Add column of residuals to input traces table
07686              */
07687 
07688             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07689             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07690 
07691             for (k = 0; k < nrows; k++) {
07692                 cpl_table_set_double(traces, trace_mod, k,
07693                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07694             }
07695 
07696             cpl_polynomial_delete(polytrace);
07697 
07698             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07699             cpl_table_subtract_columns(traces, trace_res, trace_id);
07700             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07701 
07702         }
07703     }
07704 
07705     return polytraces;
07706 
07707 }
07708 
07709 
07733 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07734                                 int mode)
07735 {
07736     const char *func = "mos_global_trace";
07737 
07738     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07739                                                  /* Max order is 5 */
07740     cpl_table      *table;
07741     cpl_vector     *c0;
07742     cpl_vector     *cn;
07743     cpl_bivector   *list;
07744 /* alternative (not robust)
07745     cpl_polynomial *poly;
07746 */
07747 
07748     double *offset;
07749     double  rms, q, m;
07750 
07751     int order, nrows, nslits;
07752     int i, j;
07753 
07754 
07755     if (polytraces == NULL) {
07756         cpl_msg_error(func, "Missing spectral curvature table");
07757         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07758     }
07759 
07760     if (slits == NULL) {
07761         cpl_msg_error(func, "Missing slits positions table");
07762         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07763     }
07764 
07765     nslits = cpl_table_get_nrow(slits);
07766 
07767     table = cpl_table_duplicate(polytraces);
07768     cpl_table_erase_invalid(table);
07769 
07770     nrows = cpl_table_get_nrow(table);
07771 
07772     if (nrows < 4) {
07773         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
07774                       "(%d): the determination of a global curvature model "
07775                       "failed", nrows);
07776         return CPL_ERROR_NONE;
07777     }
07778     
07779     order = cpl_table_get_ncol(polytraces) - 2;
07780 
07781     for (i = 0; i <= order; i++) {
07782         if (!cpl_table_has_column(table, clab[i])) {
07783             cpl_msg_error(func, "Wrong spectral curvature table");
07784             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07785         }
07786     }
07787 
07788 
07789     /*
07790      * Fill in advance the missing offset terms
07791      */
07792 
07793     for (i = 0; i < nslits; i++) {
07794         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
07795             cpl_table_set_double(polytraces, clab[0], 2*i, 
07796                                 cpl_table_get_double(slits, "ytop", i, NULL));
07797         }
07798         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
07799             cpl_table_set_double(polytraces, clab[0], 2*i+1,
07800                              cpl_table_get_double(slits, "ybottom", i, NULL));
07801         }
07802     }
07803 
07804     offset = cpl_table_get_data_double(polytraces, clab[0]);
07805 
07806 
07807     /*
07808      * Fit the global model and modify polytraces table accordingly
07809      */
07810 
07811     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
07812 
07813     for (i = 1; i <= order; i++) {
07814         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
07815         list = cpl_bivector_wrap_vectors(c0, cn);
07816         robustLinearFit(list, &q, &m, &rms);
07817 /* alternative (not robust)
07818         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
07819 */
07820         for (j = 0; j < 2*nslits; j++) {
07821             if (mode == 1)
07822                 if (cpl_table_is_valid(polytraces, clab[i], j))
07823                     continue;
07824             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
07825 /* alternative (not robust)
07826             cpl_table_set_double(polytraces, clab[i], j, 
07827                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
07828 */
07829         }
07830         cpl_bivector_unwrap_vectors(list);
07831 /* alternative (not robust)
07832         cpl_polynomial_delete(poly);
07833 */
07834         cpl_vector_unwrap(cn);
07835     }
07836 
07837     cpl_vector_unwrap(c0);
07838     cpl_table_delete(table);
07839 
07840     return CPL_ERROR_NONE;
07841 
07842 }
07843 
07844 
07914 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
07915                                    cpl_table *polytraces, double reference,
07916                                    double blue, double red, double dispersion,
07917                                    int flux, cpl_image *calibration)
07918 {
07919     const char *func = "mos_spatial_calibration";
07920 
07921     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07922                                                  /* Max order is 5 */
07923     cpl_polynomial *polytop;
07924     cpl_polynomial *polybot;
07925     cpl_image     **exslit;
07926     cpl_image      *resampled;
07927     float          *data;
07928     float          *sdata;
07929     float          *xdata;
07930     double          vtop, vbot, value;
07931     double          top, bot;
07932     double          coeff;
07933     double          ytop, ybot;
07934     double          ypos, yfra;
07935     double          factor;
07936     int             yint, ysize, yprev;
07937     int             nslits;
07938     int             npseudo;
07939     int            *slit_id;
07940     int            *length;
07941     int             nx, ny;
07942     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
07943     int             missing_top, missing_bot;
07944     int             null;
07945     int             order;
07946     int             i, j, k;
07947 
07948     int             create_position = 1;
07949 
07950 
07951     if (spectra == NULL || slits == NULL || polytraces == NULL) {
07952         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07953         return NULL;
07954     }
07955 
07956     if (dispersion <= 0.0) {
07957         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07958         return NULL;
07959     }
07960 
07961     if (red - blue < dispersion) {
07962         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07963         return NULL;
07964     }
07965 
07966     nx = cpl_image_get_size_x(spectra);
07967     ny = cpl_image_get_size_y(spectra);
07968     sdata = cpl_image_get_data(spectra);
07969     if (calibration)
07970         data = cpl_image_get_data(calibration);
07971 
07972     if (cpl_table_has_column(slits, "position"))
07973         create_position = 0;
07974 
07975     if (create_position) {
07976         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
07977         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
07978         cpl_table_set_column_unit(slits, "position", "pixel");
07979         cpl_table_set_column_unit(slits, "length", "pixel");
07980     }
07981     else
07982         length = cpl_table_get_data_int(slits, "length");
07983 
07984     nslits   = cpl_table_get_nrow(slits);
07985     slit_id  = cpl_table_get_data_int(slits, "slit_id");
07986     order    = cpl_table_get_ncol(polytraces) - 2;
07987 
07988     /*
07989      * The spatial resampling is performed for a certain number of 
07990      * pixels above and below the position of the reference wavelength:
07991      */
07992 
07993     pixel_above = (red - reference) / dispersion;
07994     pixel_below = (reference - blue) / dispersion;
07995 
07996     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
07997 
07998     for (i = 0; i < nslits; i++) {
07999         
08000         if (create_position == 0)
08001             if (length[i] == 0)
08002                 continue;
08003 
08004         /*
08005          * Note that the x coordinate of the reference pixels on the CCD
08006          * is taken arbitrarily at the top end of each slit. This wouldn't
08007          * be entirely correct in case of curved slits, or in presence of
08008          * heavy distortions: in such cases the spatial resampling is
08009          * really performed across a wide range of wavelengths. But
08010          * the lag between top and bottom spectral curvature models 
08011          * would introduce even in such cases negligible effects on
08012          * the spectral spatial resampling.
08013          */
08014 
08015         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
08016 
08017         start_pixel = refpixel - pixel_below;
08018         if (start_pixel < 0)
08019             start_pixel = 0;
08020 
08021         end_pixel = refpixel + pixel_above;
08022         if (end_pixel > nx)
08023             end_pixel = nx;
08024 
08025         /*
08026          * Recover from the table of spectral curvature coefficients
08027          * the curvature polynomials.
08028          */
08029 
08030         missing_top = 0;
08031         polytop = cpl_polynomial_new(1);
08032         for (k = 0; k <= order; k++) {
08033             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08034             if (null) {
08035                 cpl_polynomial_delete(polytop);
08036                 missing_top = 1;
08037                 break;
08038             }
08039             cpl_polynomial_set_coeff(polytop, &k, coeff);
08040         }
08041 
08042         missing_bot = 0;
08043         polybot = cpl_polynomial_new(1);
08044         for (k = 0; k <= order; k++) {
08045             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08046             if (null) {
08047                 cpl_polynomial_delete(polybot);
08048                 missing_bot = 1;
08049                 break;
08050             }
08051             cpl_polynomial_set_coeff(polybot, &k, coeff);
08052         }
08053 
08054         if (missing_top && missing_bot) {
08055             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08056                             "traced: no extraction!", 
08057                             slit_id[i]);
08058             continue;
08059         }
08060 
08061         /*
08062          * In case just one of the two edges was not traced, the other
08063          * edge curvature model is duplicated and shifted to the other
08064          * end of the slit: better than nothing!
08065          */
08066 
08067         if (missing_top) {
08068             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08069                             "the spectral curvature of the lower edge "
08070                             "is used instead.", slit_id[i]);
08071             polytop = cpl_polynomial_duplicate(polybot);
08072             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08073             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08074             k = 0;
08075             coeff = cpl_polynomial_get_coeff(polybot, &k);
08076             coeff += ytop - ybot;
08077             cpl_polynomial_set_coeff(polytop, &k, coeff);
08078         }
08079 
08080         if (missing_bot) {
08081             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08082                             "the spectral curvature of the upper edge "
08083                             "is used instead.", slit_id[i]);
08084             polybot = cpl_polynomial_duplicate(polytop);
08085             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08086             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08087             k = 0;
08088             coeff = cpl_polynomial_get_coeff(polytop, &k);
08089             coeff -= ytop - ybot;
08090             cpl_polynomial_set_coeff(polybot, &k, coeff);
08091         }
08092 
08093         /*
08094          * Allocate image for current extracted slit
08095          */
08096 
08097         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08098         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08099         npseudo = ceil(top-bot) + 1;
08100 
08101         if (npseudo < 1) {
08102             cpl_polynomial_delete(polytop);
08103             cpl_polynomial_delete(polybot);
08104             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08105                             slit_id[i]);
08106             continue;
08107         }
08108 
08109         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08110         xdata = cpl_image_get_data(exslit[i]);
08111 
08112         /*
08113          * Write interpolated values to slit image.
08114          */
08115 
08116         for (j = start_pixel; j < end_pixel; j++) {
08117             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08118             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08119             factor = (top-bot)/npseudo;
08120             for (k = 0; k <= npseudo; k++) {
08121                 ypos = top - k*factor;
08122                 yint = ypos;
08123                 yfra = ypos - yint;
08124                 if (yint >= 0 && yint < ny-1) {
08125                     vtop = sdata[j + nx*yint];
08126                     vbot = sdata[j + nx*(yint+1)];
08127                     value = vtop*(1-yfra) + vbot*yfra;
08128                     if (flux)
08129                         value *= factor;
08130                     xdata[j + nx*(npseudo-k)] = value;
08131                     if (calibration) {
08132                         data[j + nx*yint] = (top-yint)/factor;
08133                         if (k) {
08134 
08135                             /*
08136                              * This is added to recover lost pixels on
08137                              * the CCD image (pixels are lost because
08138                              * the CCD pixels are less than npseudo+1).
08139                              */
08140 
08141                             if (yprev - yint > 1) {
08142                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08143                             }
08144                         }
08145                     }
08146                 }
08147                 yprev = yint;
08148             }
08149         }
08150         cpl_polynomial_delete(polytop);
08151         cpl_polynomial_delete(polybot);
08152     }
08153 
08154     /*
08155      * Now all the slits images are copied to a single image
08156      */
08157 
08158     ysize = 0;
08159     for (i = 0; i < nslits; i++)
08160         if (exslit[i])
08161             ysize += cpl_image_get_size_y(exslit[i]);
08162 
08163     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08164 
08165     yint = -1;
08166     for (i = 0; i < nslits; i++) {
08167         if (exslit[i]) {
08168             yint += cpl_image_get_size_y(exslit[i]);
08169             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08170             if (create_position) {
08171                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08172                 cpl_table_set_int(slits, "length", i, 
08173                                   cpl_image_get_size_y(exslit[i]));
08174             }
08175             cpl_image_delete(exslit[i]);
08176         }
08177         else if (create_position) {
08178             cpl_table_set_int(slits, "position", i, -1);
08179             cpl_table_set_int(slits, "length", i, 0);
08180         }
08181     }
08182 
08183 
08184     /*
08185      * Elimination of non-traced slits from slit position table: we cannot do
08186      * it because we would lose sync with polytraces and other slit-oriented
08187      * tables. COMMENTED OUT.
08188      * 
08189 
08190     if (create_position) {
08191 
08192         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08193             cpl_table_erase_selected(slits);
08194 
08195     }
08196 
08197     */
08198 
08199     cpl_free(exslit);
08200 
08201     return resampled;
08202 
08203 }
08204 
08205 
08312 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08313                                             cpl_vector *lines,
08314                                             double dispersion, float level,
08315                                             int sradius, int order,
08316                                             double reject, double refwave,
08317                                             double *wavestart, double *waveend,
08318                                             int *nlines, double *error, 
08319                                             cpl_table *idscoeff,
08320                                             cpl_image *calibration,
08321                                             cpl_image *residuals,
08322                                             cpl_table *restable)
08323 {
08324 
08325     const char *func = "mos_wavelength_calibration_final";
08326 
08327     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08328                                                  /* Max order is 5 */
08329 
08330     double  tolerance = 20.0;    /* Probably forever...                */
08331     int     step = 10;           /* Compute restable every "step" rows */
08332 
08333     char            name[MAX_COLNAME];
08334 
08335     cpl_image      *resampled;
08336     cpl_bivector   *output;
08337     cpl_vector     *wavel;
08338     cpl_vector     *peaks;
08339     cpl_polynomial *ids;
08340     cpl_polynomial *lin;
08341     cpl_polynomial *fguess;
08342     cpl_table      *coeff;
08343     double          ids_err;
08344     double          max_disp, min_disp;
08345     double         *line;
08346     double          firstLambda, lastLambda, lambda;
08347     double          wave, pixe, value;
08348     double          c;
08349     float          *sdata;
08350     float          *rdata;
08351     float          *idata;
08352     float          *ddata;
08353     float           v1, v2, vi;
08354     float           fpixel;
08355     int            *length;
08356     int             pixstart, pixend;
08357     int             row_top, row_bot;
08358     int             extrapolation;
08359     int             nref;
08360     int             nslits;
08361     int             nfits;
08362     int             nl, nx, ny, pixel;
08363     int             countLines, usedLines;
08364     int             uorder;
08365     int             missing;
08366     int             null;
08367     int             width, uradius;
08368     int             i, j, k, s;
08369 
08370 
08371     if (dispersion == 0.0) {
08372         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
08373         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08374         return NULL;
08375     }
08376 
08377     if (dispersion < 0.0) {
08378         cpl_msg_error(func, "The expected dispersion must be positive");
08379         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08380         return NULL;
08381     }
08382 
08383     if (idscoeff == NULL) {
08384         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
08385         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08386         return NULL;
08387     }
08388 
08389     max_disp = dispersion + dispersion * tolerance / 100;
08390     min_disp = dispersion - dispersion * tolerance / 100;
08391 
08392     if (order < 1) {
08393         cpl_msg_error(func, "The order of the fitting polynomial "
08394                       "must be at least 1");
08395         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08396         return NULL;
08397     }
08398 
08399     if (image == NULL || lines == NULL) {
08400         cpl_msg_error(func, "Both spectral exposure and reference line "
08401                       "catalog are required in input");
08402         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08403         return NULL;
08404     }
08405 
08406     nx = cpl_image_get_size_x(image);
08407     ny = cpl_image_get_size_y(image);
08408     sdata = cpl_image_get_data_float(image);
08409 
08410     nref = cpl_vector_get_size(lines);
08411     line = cpl_vector_get_data(lines);
08412 
08413     if (*wavestart < 1.0 && *waveend < 1.0) {
08414         firstLambda = line[0];
08415         lastLambda = line[nref-1];
08416         extrapolation = (lastLambda - firstLambda) / 10;
08417         firstLambda -= extrapolation;
08418         lastLambda += extrapolation;
08419         *wavestart = firstLambda;
08420         *waveend = lastLambda;
08421     }
08422     else {
08423         firstLambda = *wavestart;
08424         lastLambda = *waveend;
08425     }
08426 
08427     nl = (lastLambda - firstLambda) / dispersion;
08428     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08429     rdata = cpl_image_get_data_float(resampled);
08430 
08431     /*
08432      * Allocate total output table of IDS coefficients
08433      */
08434 
08435     for (j = 0; j <= order; j++)
08436         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08437 
08438     if (calibration)
08439         idata = cpl_image_get_data_float(calibration);
08440 
08441     if (residuals)
08442         ddata = cpl_image_get_data_float(residuals);
08443 
08444     if (restable) {
08445         cpl_table_set_size(restable, nref);
08446         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08447         cpl_table_copy_data_double(restable, "wavelength", line);
08448         for (i = 0; i < ny; i += step) {
08449              snprintf(name, MAX_COLNAME, "r%d", i);
08450              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08451              snprintf(name, MAX_COLNAME, "d%d", i);
08452              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08453              snprintf(name, MAX_COLNAME, "p%d", i);
08454              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08455         }
08456     }
08457 
08458 
08459     /*
08460      * Process all slits separately.
08461      */
08462 
08463     nslits   = cpl_table_get_nrow(slits);
08464     length   = cpl_table_get_data_int(slits, "length");
08465 
08466     row_top = ny;
08467     for (s = 0; s < nslits; s++) {
08468 
08469         if (length[s] == 0)
08470             continue;
08471 
08472         /*
08473          * row_top and row_bot define the boundaries of the current slit.
08474          * Here we begin (arbitrarily...) from the top slit.
08475          */
08476 
08477         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08478 
08479         if (sradius > 0) {
08480 
08481             /*
08482              * If a search radius was defined, allocate the table of
08483              * the fitting polynomials coefficients. This table is
08484              * just used to generate the first-guess polynomial made
08485              * of the median coefficients of all polynomials found
08486              * for this slit.
08487              */
08488 
08489             coeff = cpl_table_new(row_top - row_bot);
08490             for (j = 0; j <= order; j++)
08491                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08492         }
08493 
08494         /*
08495          * Here is the loop on all rows of the current slit. They are
08496          * wavelength calibrated one by one.
08497          */
08498 
08499         for (i = row_bot; i < row_top; i++) {
08500             width = mos_lines_width(sdata + i*nx, nx);
08501             if (width < 5)
08502                 width = 5;
08503             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08504             if (peaks) {
08505                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08506             }
08507             if (peaks) {
08508                 output = mos_identify_peaks(peaks, lines, 
08509                                             min_disp, max_disp, 0.05, -1);
08510                 if (output) {
08511                     countLines = cpl_bivector_get_size(output);
08512                     if (countLines < 4) {
08513                         cpl_bivector_delete(output);
08514                         cpl_vector_delete(peaks);
08515                         if (nlines)
08516                             nlines[i] = 0;
08517                         if (error)
08518                             error[i] = 0.0;
08519                         continue;
08520                     }
08521 
08522                     /*
08523                      * Set reference wavelength as zero point
08524                      */
08525 
08526                     wavel = cpl_bivector_get_y(output);
08527                     cpl_vector_subtract_scalar(wavel, refwave);
08528 
08529                     uorder = countLines / 2 - 1;
08530                     if (uorder > order)
08531                         uorder = order;
08532 
08533                     ids = mos_poly_wav2pix(output, uorder, reject,
08534                                            2 * (uorder + 1), &usedLines,
08535                                            &ids_err);
08536 
08537                     if (ids == NULL) {
08538                         cpl_bivector_delete(output);
08539                         cpl_vector_delete(peaks);
08540                         if (nlines)
08541                             nlines[i] = 0;
08542                         if (error)
08543                             error[i] = 0.0;
08544                         cpl_error_reset();
08545                         continue;
08546                     }
08547 
08548                     if (sradius > 0) {
08549                         for (k = 0; k <= order; k++) {
08550                             if (k > uorder) {
08551                                 cpl_table_set_double(coeff, clab[k], 
08552                                 i - row_bot, 0.0);
08553                             }
08554                             else {
08555                                 cpl_table_set_double(coeff, clab[k], 
08556                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08557                             }
08558                         }
08559                     }
08560                /*   else {   */
08561                         if (calibration) {
08562                             pixstart = cpl_polynomial_eval_1d(ids,
08563                               cpl_bivector_get_y_data(output)[0], 
08564                               NULL);
08565                             pixend = cpl_polynomial_eval_1d(ids,
08566                               cpl_bivector_get_y_data(output)[countLines-1],
08567                               NULL);
08568                             extrapolation = (pixend - pixstart) / 5;
08569                             pixstart -= extrapolation;
08570                             pixend += extrapolation;
08571                             if (pixstart < 0)
08572                                 pixstart = 0;
08573                             if (pixend > nx)
08574                                 pixend = nx;
08575    
08576                             for (j = pixstart; j < pixend; j++) {
08577                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08578                                      firstLambda, lastLambda, refwave, j);
08579                             }
08580                         }
08581 
08582                         /*
08583                          * Residuals image
08584                          */
08585         
08586                         if (residuals || (restable && !(i%step))) {
08587                             if (restable && !(i%step)) {
08588                                 lin = cpl_polynomial_new(1);
08589                                 for (k = 0; k < 2; k++)
08590                                     cpl_polynomial_set_coeff(lin, &k,
08591                                           cpl_polynomial_get_coeff(ids, &k));
08592                             }
08593                             for (j = 0; j < countLines; j++) {
08594                                 pixe = cpl_bivector_get_x_data(output)[j];
08595                                 wave = cpl_bivector_get_y_data(output)[j];
08596                                 value = pixe 
08597                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08598                                 if (residuals) {
08599                                     pixel = pixe + 0.5;
08600                                     (ddata + i*nx)[pixel] = value;
08601                                 }
08602                                 if (restable && !(i%step)) {
08603                                     for (k = 0; k < nref; k++) {
08604                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08605                                             snprintf(name, MAX_COLNAME, 
08606                                                      "r%d", i);
08607                                             cpl_table_set_double(restable, name,
08608                                                                  k, value);
08609                                             value = pixe
08610                                                   - cpl_polynomial_eval_1d(lin,
08611                                                               wave, NULL);
08612                                             snprintf(name, MAX_COLNAME, 
08613                                                      "d%d", i);
08614                                             cpl_table_set_double(restable, name,
08615                                                                  k, value);
08616                                             snprintf(name, MAX_COLNAME,
08617                                                      "p%d", i);
08618                                             cpl_table_set_double(restable, name,
08619                                                                  k, pixe);
08620                                             break;
08621                                         }
08622                                     }
08623                                 }
08624                             }
08625                             if (restable && !(i%step)) {
08626                                 cpl_polynomial_delete(lin);
08627                             }
08628 /***
08629                             for (j = 0; j < countLines; j++) {
08630                                 pixel = cpl_bivector_get_x_data(output)[j] 
08631                                       + 0.5;
08632                                 (ddata + i*nx)[pixel] =
08633                                 cpl_bivector_get_x_data(output)[j]
08634                               - cpl_polynomial_eval_1d(ids,
08635                                 cpl_bivector_get_y_data(output)[j], 
08636                                 NULL);
08637                             }
08638 ***/
08639                         }
08640                 /*  }   */
08641 
08642                     /*
08643                      * Write it anyway, even in case a first-guess based
08644                      * solution will be searched afterwards: in case of
08645                      * failure, the "blind" solution is kept.
08646                      */
08647 
08648                     if (nlines)
08649                         nlines[i] = usedLines;
08650                     if (error)
08651                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08652 
08653                     for (k = 0; k <= order; k++) {
08654                         if (k > uorder) {
08655                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08656                         }
08657                         else {
08658                             cpl_table_set_double(idscoeff, clab[k], i,
08659                                       cpl_polynomial_get_coeff(ids, &k));
08660                         }
08661                     }
08662 
08663                     cpl_polynomial_delete(ids);
08664                     cpl_bivector_delete(output);
08665                 }
08666                 cpl_vector_delete(peaks);
08667             }
08668         }       /* End of loop on current slit's rows */
08669 
08670 
08671         if (sradius > 0) {
08672 
08673             /*
08674              * See whether there are valid fits at all...
08675              */
08676     
08677             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08678     
08679             if (nfits) {
08680 
08681                 /*
08682                  * Compute a median IDS polynomial for the current slit
08683                  */
08684 
08685                 fguess = cpl_polynomial_new(1);
08686 
08687                 for (k = 0; k <= order; k++) {
08688                     c = cpl_table_get_column_median(coeff, clab[k]);
08689                     cpl_polynomial_set_coeff(fguess, &k, c);
08690                 }
08691 
08692                 for (i = row_bot; i < row_top; i++) {
08693 
08694                     /*
08695                      * Use first-guess to find the reference lines again
08696                      */
08697 
08698                     width = mos_lines_width(sdata + i*nx, nx);
08699                     if (width > sradius) {
08700                         uradius = width; 
08701                     }
08702                     else {
08703                         uradius = sradius;
08704                     }
08705 
08706                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08707                                             fguess, refwave, uradius);
08708 
08709                     if (output == NULL) {
08710                         cpl_error_reset();
08711                         continue;
08712                     }
08713 
08714                     countLines = cpl_bivector_get_size(output);
08715 
08716                     if (countLines < 4) {
08717                         cpl_bivector_delete(output);
08718                         continue;
08719                     }
08720 
08721                     /*
08722                      * Set reference wavelength as zero point
08723                      */
08724 
08725                     wavel = cpl_bivector_get_y(output);
08726                     cpl_vector_subtract_scalar(wavel, refwave);
08727 
08728                     uorder = countLines / 2 - 1;
08729                     if (uorder > order)
08730                         uorder = order;
08731 
08732                     ids = mos_poly_wav2pix(output, uorder, reject,
08733                                            2 * (uorder + 1), &usedLines,
08734                                            &ids_err);
08735 
08736                     if (ids == NULL) {
08737                         cpl_error_reset();
08738                         cpl_bivector_delete(output);
08739                         continue;
08740                     }
08741 
08742                     if (nlines)
08743                         nlines[i] = usedLines;
08744                     if (error)
08745                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08746 
08747                     if (calibration) {
08748                         pixstart = cpl_polynomial_eval_1d(ids,
08749                            cpl_bivector_get_y_data(output)[0], 
08750                            NULL);
08751                         pixend = cpl_polynomial_eval_1d(ids,
08752                            cpl_bivector_get_y_data(output)[countLines-1], 
08753                            NULL);
08754                         extrapolation = (pixend - pixstart) / 5;
08755                         pixstart -= extrapolation;
08756                         pixend += extrapolation;
08757                         if (pixstart < 0)
08758                             pixstart = 0;
08759                         if (pixend > nx)
08760                             pixend = nx;
08761 
08762                         for (j = pixstart; j < pixend; j++) {
08763                             (idata + i*nx)[j] = mos_eval_dds(ids,
08764                                      firstLambda, lastLambda, refwave, j);
08765                         }
08766                     }
08767 
08768                     /*
08769                      * Residuals image
08770                      */
08771 
08772                     if (residuals || (restable && !(i%step))) {
08773                         if (restable && !(i%step)) {
08774                             lin = cpl_polynomial_new(1);
08775                             for (k = 0; k < 2; k++)
08776                                 cpl_polynomial_set_coeff(lin, &k,
08777                                       cpl_polynomial_get_coeff(ids, &k));
08778                         }
08779                         for (j = 0; j < countLines; j++) {
08780                             pixe = cpl_bivector_get_x_data(output)[j];
08781                             wave = cpl_bivector_get_y_data(output)[j];
08782                             value = pixe
08783                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
08784                             if (residuals) {
08785                                 pixel = pixe + 0.5;
08786                                 (ddata + i*nx)[pixel] = value;
08787                             }
08788                             if (restable && !(i%step)) {
08789                                 for (k = 0; k < nref; k++) {
08790                                     if (fabs(line[k]-refwave-wave) < 0.1) {
08791                                         snprintf(name, MAX_COLNAME,
08792                                                  "r%d", i);
08793                                         cpl_table_set_double(restable, name,
08794                                                              k, value);
08795                                         value = pixe
08796                                               - cpl_polynomial_eval_1d(lin,
08797                                                           wave, NULL);
08798                                         snprintf(name, MAX_COLNAME,
08799                                                  "d%d", i);
08800                                         cpl_table_set_double(restable, name,
08801                                                              k, value);
08802                                         snprintf(name, MAX_COLNAME,
08803                                                  "p%d", i);
08804                                         cpl_table_set_double(restable, name,
08805                                                              k, pixe);
08806                                         break;
08807                                     }
08808                                 }
08809                             }
08810                         }
08811                         if (restable && !(i%step)) {
08812                             cpl_polynomial_delete(lin);
08813                         }
08814 /***
08815                         for (j = 0; j < countLines; j++) {
08816                             pixel = cpl_bivector_get_x_data(output)[j]
08817                                   + 0.5; 
08818                             (ddata + i*nx)[pixel] =
08819                             cpl_bivector_get_x_data(output)[j]
08820                           - cpl_polynomial_eval_1d(ids,
08821                             cpl_bivector_get_y_data(output)[j], 
08822                             NULL);
08823                         }
08824 ***/
08825                     }
08826 
08827                     for (k = 0; k <= order; k++) {
08828                         if (k > uorder) {
08829                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08830                         }
08831                         else {
08832                             cpl_table_set_double(idscoeff, clab[k], i,
08833                                         cpl_polynomial_get_coeff(ids, &k));
08834                         }
08835                     }
08836 
08837                     cpl_bivector_delete(output);
08838                     cpl_polynomial_delete(ids);
08839 
08840                 } /* End of loop "use ids as a first-guess" */
08841 
08842                 cpl_polynomial_delete(fguess);
08843             }
08844 
08845             cpl_table_delete(coeff);
08846 
08847         }
08848 
08849         row_top = row_bot;
08850 
08851     } /* End of loop on slits */
08852 
08853 
08854     /*
08855      * At this point the idscoeff table has been filled with all the 
08856      * fits coefficients obtained for all the rows of the input image.
08857      * Now we apply these coefficients to resample the input image
08858      * at constant wavelength step.
08859      */
08860 
08861     for (i = 0; i < ny; i++) {
08862 
08863         missing = 0;
08864         ids = cpl_polynomial_new(1);
08865         for (k = 0; k <= order; k++) {
08866             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
08867             if (null) {
08868                 cpl_polynomial_delete(ids);
08869                 missing = 1;
08870                 break;
08871             }
08872             cpl_polynomial_set_coeff(ids, &k, c);
08873         }
08874         if (missing)
08875             continue;
08876 
08877         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
08878         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
08879         if (pixstart < 0)
08880             pixstart = 0;
08881         if (pixend > nx)
08882             pixend = nx;
08883 
08884         /*
08885          * Resampled image:
08886          */
08887 
08888         for (j = 0; j < nl; j++) {
08889             lambda = firstLambda + j * dispersion;
08890             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
08891             pixel = fpixel;
08892             if (pixel >= 0 && pixel < nx-1) {
08893                 v1 = (sdata + i*nx)[pixel];
08894                 v2 = (sdata + i*nx)[pixel+1];
08895                 vi = v1 + (v2-v1)*(fpixel-pixel);
08896                 (rdata + i*nl)[j] = vi;
08897             }
08898         }
08899 
08900         cpl_polynomial_delete(ids);
08901     }
08902 
08903     return resampled;
08904 }
08905 
08906 
08933 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
08934                                       double firstLambda, double lastLambda, 
08935                                       double dispersion, cpl_table *idscoeff, 
08936                                       int flux)
08937 {
08938 
08939     const char *func = "mos_wavelength_calibration";
08940 
08941     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08942                                                  /* Max order is 5 */
08943 
08944     cpl_image      *resampled;
08945     cpl_polynomial *ids;
08946     double          pixel_per_lambda;
08947     double          lambda;
08948     double          c;
08949     float          *sdata;
08950     float          *rdata;
08951     float           v0, v1, v2, v3, vi;
08952     float           fpixel;
08953     int             order;
08954     int             pixstart, pixend;
08955     int             nl, nx, ny, pixel;
08956     int             missing;
08957     int             null;
08958     int             i, j, k;
08959 
08960 
08961     if (dispersion <= 0.0) {
08962         cpl_msg_error(func, "The resampling step must be positive");
08963         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08964         return NULL;
08965     }
08966 
08967     if (lastLambda - firstLambda < dispersion) {
08968         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
08969                       firstLambda, lastLambda);
08970         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08971         return NULL;
08972     }
08973 
08974     if (idscoeff == NULL) {
08975         cpl_msg_error(func, "An IDS coeff table must be given");
08976         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08977         return NULL;
08978     }
08979 
08980     if (image == NULL) {
08981         cpl_msg_error(func, "A scientific spectral image must be given");
08982         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08983         return NULL;
08984     }
08985 
08986     nx = cpl_image_get_size_x(image);
08987     ny = cpl_image_get_size_y(image);
08988     sdata = cpl_image_get_data_float(image);
08989 
08990     nl = (lastLambda - firstLambda) / dispersion;
08991     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08992     rdata = cpl_image_get_data_float(resampled);
08993 
08994     order = 0;
08995     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
08996         ++order;
08997     --order;
08998 
08999     for (i = 0; i < ny; i++) {
09000 
09001         missing = 0;
09002         ids = cpl_polynomial_new(1);
09003         for (k = 0; k <= order; k++) {
09004             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09005             if (null) {
09006                 cpl_polynomial_delete(ids);
09007                 missing = 1;
09008                 break;
09009             }
09010             cpl_polynomial_set_coeff(ids, &k, c);
09011         }
09012         if (missing)
09013             continue;
09014 
09015         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09016         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09017         if (pixstart < 0)
09018             pixstart = 0;
09019         if (pixend > nx)
09020             pixend = nx;
09021 
09022         /*
09023          * Resampled image:
09024          */
09025 
09026         for (j = 0; j < nl; j++) {
09027             lambda = firstLambda + j * dispersion;
09028             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09029                                             &pixel_per_lambda);
09030 
09031             /*
09032              * The local dispersion is 1 / pixel_per_lambda
09033              * and this factor is used for applying the flux
09034              * conservation correction (if requested).
09035              */
09036 
09037             pixel = fpixel;
09038             if (pixel >= 1 && pixel < nx-2) {
09039                 v0 = (sdata + i*nx)[pixel-1];
09040                 v1 = (sdata + i*nx)[pixel];
09041                 v2 = (sdata + i*nx)[pixel+1];
09042                 v3 = (sdata + i*nx)[pixel+2];
09043                 vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09044                    + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09045                    + 2*v1;
09046                 vi /= 2;
09047                 if (v1 > v2) {
09048                     if (vi > v1) { 
09049                         vi = v1;
09050                     }
09051                     else if (vi < v2) {
09052                         vi = v2;
09053                     }
09054                 }
09055                 else {
09056                     if (vi > v2) { 
09057                         vi = v2;
09058                     }
09059                     else if (vi < v1) {
09060                         vi = v1;
09061                     }
09062                 }
09063                 if (flux)
09064                     vi *= dispersion * pixel_per_lambda;
09065                 (rdata + i*nl)[j] = vi;
09066             }
09067             else if (pixel >= 0 && pixel < nx-1) {
09068                 v1 = (sdata + i*nx)[pixel];
09069                 v2 = (sdata + i*nx)[pixel+1];
09070                 vi = v1 + (v2-v1)*(fpixel-pixel);
09071                 if (flux)
09072                     vi *= dispersion * pixel_per_lambda;
09073                 (rdata + i*nl)[j] = vi;
09074             }
09075         }
09076 
09077         cpl_polynomial_delete(ids);
09078     }
09079 
09080     return resampled;
09081 }
09082 
09083 
09150 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
09151                                 double refwave, double firstLambda, 
09152                                 double lastLambda, cpl_table *idscoeff,
09153                                 cpl_vector *skylines, int highres, int order,
09154                                 cpl_image *calibration, int sradius)
09155 {
09156     const char *func = "mos_wavelength_align";
09157 
09158     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09159                                                  /* Max order is 5 */
09160     double         *line;
09161     double         *data;
09162     double          expPos, offset;
09163     double          c;
09164     double          lambda1, lambda2;
09165     double          rms;
09166     float           pos;
09167     float          *sdata;
09168     float          *cdata;
09169     int            *idata;
09170     int             startPos, endPos;
09171     int             window = 2*sradius + 1;
09172     int             nlines;
09173     int             nslits;
09174     int             npoints;
09175     int             nrows;
09176     int             nx, ny;
09177     int             xlow, ylow, xhig, yhig;
09178     int             idsorder, uorder;
09179     int            *slit_id;
09180     int            *position;
09181     int            *length;
09182     int             missing;
09183     int             null;
09184     int             i, j, k;
09185 
09186     char            offname[MAX_COLNAME];
09187     char            name[MAX_COLNAME];
09188 
09189     cpl_polynomial *ids;
09190     cpl_polynomial *polycorr;
09191     cpl_image      *exslit;
09192     cpl_image      *sky;
09193     cpl_table      *offsets;
09194     cpl_table      *dummy;
09195     cpl_vector     *wave;
09196     cpl_vector     *offs;
09197     
09198 
09199     if (idscoeff == NULL) {
09200         cpl_msg_error(func, "An IDS coeff table must be given");
09201         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09202         return NULL;
09203     }
09204 
09205     if (image == NULL) {
09206         cpl_msg_error(func, "A scientific spectral image must be given");
09207         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09208         return NULL;
09209     }
09210 
09211     if (slits == NULL) {
09212         cpl_msg_error(func, "A slit position table must be given");
09213         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09214         return NULL;
09215     }
09216 
09217     if (skylines) {
09218         line = cpl_vector_get_data(skylines);
09219         nlines = cpl_vector_get_size(skylines);
09220     }
09221     else {
09222         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09223                         "given: using internal list of reference sky lines");
09224         if (highres) {
09225            line = default_lines_hi;
09226            nlines = sizeof(default_lines_hi) / sizeof(double);
09227         }
09228         else {
09229            line = default_lines_lo;
09230            nlines = sizeof(default_lines_lo) / sizeof(double);
09231         }
09232     }
09233 
09234     if (calibration)
09235         cdata = cpl_image_get_data(calibration);
09236 
09237     nx = cpl_image_get_size_x(image);
09238     ny = cpl_image_get_size_y(image);
09239 
09240     nslits   = cpl_table_get_nrow(slits);
09241     slit_id  = cpl_table_get_data_int(slits, "slit_id");
09242     position = cpl_table_get_data_int(slits, "position");
09243     length   = cpl_table_get_data_int(slits, "length");
09244 
09245 
09246     /*
09247      * Define the output table of offsets
09248      */
09249 
09250     nrows = 0;
09251     for (i = 0; i < nlines; i++)
09252         if (line[i] > firstLambda && line[i] < lastLambda)
09253             nrows++;
09254 
09255     offsets = cpl_table_new(nrows);
09256     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09257     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09258 
09259     nrows = 0;
09260     for (i = 0; i < nlines; i++) {
09261         if (line[i] > firstLambda && line[i] < lastLambda) {
09262             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09263             nrows++;
09264         }
09265     }
09266 
09267     /*
09268      * Here "line" is made to point to the new list of selected wavelengths
09269      */
09270 
09271     line = cpl_table_get_data_double(offsets, "wave");
09272     nlines = nrows;
09273 
09274     idsorder = 0;
09275     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09276         ++idsorder;
09277     --idsorder;
09278 
09279     xlow = 1;
09280     xhig = nx;
09281     for (i = 0; i < nslits; i++) {
09282 
09283         if (length[i] == 0)
09284             continue;
09285 
09286         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09287         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
09288 
09289         /* 
09290          * Define the extraction boundaries. We DON'T write:
09291          *
09292          * ylow = position[i];
09293          * yhig = ylow + length[i];
09294          *
09295          * because the cpl_image pixels are counted from 1, and because in
09296          * cpl_image_extract() the coordinates of the last pixel are inclusive.
09297          */
09298 
09299         ylow = position[i] + 1;
09300         yhig = ylow + length[i] - 1;
09301 
09302         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
09303         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
09304         sdata  = cpl_image_get_data(sky);
09305 
09306         cpl_image_delete(exslit);
09307 
09308         /* 
09309          * Return here to a decent way of counting pixels (i.e., starting
09310          * from 0)
09311          */
09312          
09313         ylow--;
09314 
09315         /*
09316          * Allocate a dummy table for collecting all the offsets
09317          * for all the lines: this is only needed for the computation
09318          * of the median offset for each sky line
09319          */
09320 
09321         dummy = cpl_table_new(yhig - ylow);
09322         for (j = 0; j < nlines; j++) {
09323             snprintf(name, MAX_COLNAME, "%d", j);
09324             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09325         }
09326 
09327         for (j = ylow; j < yhig; j++) {
09328 
09329             /*
09330              * Get the IDS polynomial for the current slit row
09331              */
09332 
09333             missing = 0;
09334             ids = cpl_polynomial_new(1);
09335             for (k = 0; k <= idsorder; k++) {
09336                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
09337                 if (null) {
09338                     cpl_polynomial_delete(ids);
09339                     missing = 1;
09340                     break;
09341                 }
09342                 cpl_polynomial_set_coeff(ids, &k, c);
09343             }
09344             if (missing)
09345                 continue;
09346 
09347             for (k = 0; k < nlines; k++) {
09348                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09349                 startPos = expPos - sradius;
09350                 endPos   = startPos + window;
09351                 if (startPos < 0 || endPos >= nx)
09352                     continue;
09353            
09354                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09355                     pos += startPos;
09356                     offset = pos - expPos;
09357                     snprintf(name, MAX_COLNAME, "%d", k);
09358                     cpl_table_set_double(dummy, name, j - ylow, offset);
09359                 }
09360             }
09361 
09362             cpl_polynomial_delete(ids);
09363         }
09364 
09365         cpl_image_delete(sky);
09366 
09367         for (j = 0; j < nlines; j++) {
09368             snprintf(name, MAX_COLNAME, "%d", j);
09369             if (cpl_table_has_valid(dummy, name)) {
09370                 offset = cpl_table_get_column_median(dummy, name);
09371                 cpl_table_set_double(offsets, offname, j, offset);
09372             }
09373         }
09374 
09375         cpl_table_delete(dummy);
09376 
09377     }
09378 
09379 
09380     /*
09381      * In the following the input idscoeff table is modified by simply
09382      * adding the coefficients of the polynomial used to fit the sky
09383      * line residuals to the coefficients of the IDS polynomials.
09384      */
09385 
09386     for (i = 0; i < nslits; i++) {
09387 
09388         if (length[i] == 0)
09389             continue;
09390 
09391         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09392 
09393         /*
09394          * In the following, the "dummy" table is just a tool for
09395          * eliminating invalid points from the vectors to be fitted.
09396          */
09397 
09398         dummy = cpl_table_new(nlines);
09399         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
09400         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
09401 
09402         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
09403         if (npoints == 0) {
09404             cpl_msg_warning(func, "No sky lines alignment was possible "
09405                             "for slit ID=%d: no sky line found", slit_id[i]);
09406             cpl_table_delete(dummy);
09407             continue;
09408         }
09409 
09410         uorder = order;
09411         if (npoints <= uorder) {
09412             uorder = npoints - 1;
09413             if (uorder) {
09414                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09415                                 "ID=%d, while a polynomial order %d was "
09416                                 "requested. Using polynomial order %d for "
09417                                 "this slit!", npoints, slit_id[i], order, 
09418                                 uorder);
09419             }
09420             else {
09421                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09422                                 "ID=%d, while a polynomial order %d was "
09423                                 "requested. Computing a median offset for "
09424                                 "this slit!", npoints, slit_id[i], order);
09425             }
09426         }
09427 
09428         cpl_table_erase_invalid(dummy);
09429 
09430         if (uorder > 1) {
09431 
09432             /*
09433              * Model offsets with polynomial fitting
09434              */
09435 
09436             wave = cpl_vector_wrap(npoints,
09437                                    cpl_table_get_data_double(dummy, "wave"));
09438             offs = cpl_vector_wrap(npoints,
09439                                    cpl_table_get_data_double(dummy, "offset"));
09440 
09441             /*
09442              * Set reference wavelength as zero point
09443              */
09444 
09445             cpl_vector_subtract_scalar(wave, refwave);
09446 
09447             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09448 
09449             rms = sqrt(rms * (uorder + 1) / npoints);
09450 
09451             cpl_vector_unwrap(wave);
09452             cpl_vector_unwrap(offs);
09453             cpl_table_delete(dummy);
09454 
09455             /*
09456              * Now correct the coefficients of the corresponding IDS
09457              * polynomials related to this slit:
09458              */
09459 
09460             ylow = position[i];
09461             yhig = ylow + length[i];
09462 
09463             for (j = 0; j <= uorder; j++) {
09464                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09465                 c = cpl_polynomial_get_coeff(polycorr, &j);
09466                 for (k = ylow; k < yhig; k++)
09467                     data[k] += c;
09468             }
09469 
09470             data = cpl_table_get_data_double(idscoeff, "error");
09471             for (k = ylow; k < yhig; k++)
09472                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09473 
09474             idata = cpl_table_get_data_int(idscoeff, "nlines");
09475             for (k = ylow; k < yhig; k++)
09476                  idata[k] = npoints;
09477 
09478             /*
09479              * If a wavelengths map was provided, correct it to keep
09480              * into account the alignment to skylines:
09481              */
09482 
09483             if (calibration) {
09484                 for (j = ylow; j < yhig; j++) {
09485                     for (k = 1; k < nx; k++) {
09486                         lambda1 = cdata[k - 1 + j*nx];
09487                         lambda2 = cdata[k + j*nx];
09488                         if (lambda1 < 1.0 || lambda2 < 1.0)
09489                             continue;
09490                         offset = cpl_polynomial_eval_1d(polycorr, 
09491                                                         lambda1-refwave, NULL);
09492                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09493                     }
09494                 }
09495             }
09496     
09497             cpl_polynomial_delete(polycorr);
09498         }
09499         else if (uorder == 1) {
09500 
09501             /*
09502              * Model offsets with robust linear fitting
09503              */
09504 
09505             double        q, m;
09506             cpl_bivector *list;
09507 
09508 
09509             wave = cpl_vector_wrap(npoints,
09510                                    cpl_table_get_data_double(dummy, "wave"));
09511             offs = cpl_vector_wrap(npoints,
09512                                    cpl_table_get_data_double(dummy, "offset"));
09513 
09514             list = cpl_bivector_wrap_vectors(wave, offs);
09515 
09516             /*
09517              * Set reference wavelength as zero point
09518              */
09519 
09520             cpl_vector_subtract_scalar(wave, refwave);
09521 
09522             robustLinearFit(list, &q, &m, &rms);
09523 
09524             rms = sqrt(rms * (uorder + 1) / npoints);
09525 
09526             cpl_bivector_unwrap_vectors(list);
09527             cpl_vector_unwrap(wave);
09528             cpl_vector_unwrap(offs);
09529             cpl_table_delete(dummy);
09530 
09531             /*
09532              * Now correct the coefficients of the corresponding IDS
09533              * polynomials related to this slit:
09534              */
09535 
09536             ylow = position[i];
09537             yhig = ylow + length[i];
09538 
09539             for (j = 0; j <= uorder; j++) {
09540                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09541                 if (j)
09542                     c = m;
09543                 else
09544                     c = q;
09545                 for (k = ylow; k < yhig; k++)
09546                     data[k] += c;
09547             }
09548 
09549             data = cpl_table_get_data_double(idscoeff, "error");
09550             for (k = ylow; k < yhig; k++)
09551                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09552 
09553             idata = cpl_table_get_data_int(idscoeff, "nlines");
09554             for (k = ylow; k < yhig; k++)
09555                  idata[k] = npoints;
09556 
09557             /*
09558              * If a wavelengths map was provided, correct it to keep
09559              * into account the alignment to skylines:
09560              */
09561 
09562             if (calibration) {
09563                 for (j = ylow; j < yhig; j++) {
09564                     for (k = 1; k < nx; k++) {
09565                         lambda1 = cdata[k - 1 + j*nx];
09566                         lambda2 = cdata[k + j*nx];
09567                         if (lambda1 < 1.0 || lambda2 < 1.0)
09568                             continue;
09569                         offset = q + m*(lambda1-refwave);
09570                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09571                     }
09572                 }
09573             }
09574         }
09575         else {
09576 
09577             /*
09578              * Just compute median offset
09579              */
09580 
09581             offs = cpl_vector_wrap(npoints,
09582                                    cpl_table_get_data_double(dummy, "offset"));
09583 
09584             offset = cpl_vector_get_median_const(offs);
09585 
09586             if (npoints > 1)
09587                 rms = cpl_table_get_column_stdev(dummy, "offset");
09588             else
09589                 rms = 0.0;
09590 
09591             rms /= sqrt(npoints);
09592 
09593             cpl_vector_unwrap(offs);
09594             cpl_table_delete(dummy);
09595 
09596             /*
09597              * Now correct the constant term of the corresponding IDS
09598              * polynomials related to this slit:
09599              */
09600 
09601             ylow = position[i];
09602             yhig = ylow + length[i];
09603 
09604             data = cpl_table_get_data_double(idscoeff, clab[0]);
09605             for (k = ylow; k < yhig; k++)
09606                 data[k] += offset;
09607 
09608             data = cpl_table_get_data_double(idscoeff, "error");
09609             for (k = ylow; k < yhig; k++)
09610                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09611 
09612             idata = cpl_table_get_data_int(idscoeff, "nlines");
09613             for (k = ylow; k < yhig; k++)
09614                  idata[k] = npoints;
09615 
09616             /*
09617              * If a wavelengths map was provided, correct it to keep
09618              * into account the alignment to skylines. Note that 
09619              * the offset must be converted from pixels to wavelengths.
09620              */
09621 
09622             if (calibration) {
09623                 for (j = ylow; j < yhig; j++) {
09624                     for (k = 1; k < nx; k++) {
09625                         lambda1 = cdata[k - 1 + j*nx];
09626                         lambda2 = cdata[k + j*nx];
09627                         if (lambda1 < 1.0 || lambda2 < 1.0)
09628                             continue; 
09629                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09630                     }
09631                 }
09632             }
09633         }
09634     }
09635 
09636     return offsets;
09637 
09638 }
09639 
09640 
09702 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
09703                                     double firstLambda, double lastLambda, 
09704                                     cpl_table *idscoeff, cpl_vector *skylines, 
09705                                     int highres, int order, 
09706                                     cpl_image *calibration, int sradius)
09707 {
09708     const char *func = "mos_wavelength_align_lss";
09709 
09710     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09711                                                  /* Max order is 5 */
09712     double         *line;
09713     double         *data;
09714     double         *wdata;
09715     double         *odata;
09716     double          expPos, offset;
09717     double          c;
09718     double          lambda1, lambda2;
09719     double          rms;
09720     float           pos;
09721     float          *sdata;
09722     float          *cdata;
09723     int            *idata;
09724     int             startPos, endPos;
09725     int             window = 2*sradius + 1;
09726     int             nlines;
09727     int             npoints;
09728     int             nrows;
09729     int             nx, ny;
09730     int             idsorder, uorder;
09731     int             missing;
09732     int             i, j, k;
09733 
09734     char            name[MAX_COLNAME];
09735     char            fname[MAX_COLNAME];
09736 
09737     cpl_polynomial *ids;
09738     cpl_polynomial *polycorr;
09739     cpl_table      *offsets;
09740     cpl_table      *fittable;
09741     cpl_table      *dummy;
09742     cpl_vector     *wave;
09743     cpl_vector     *offs;
09744     cpl_vector     *row;
09745     
09746 
09747     if (idscoeff == NULL) {
09748         cpl_msg_error(func, "An IDS coeff table must be given");
09749         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09750         return NULL;
09751     }
09752 
09753     if (image == NULL) {
09754         cpl_msg_error(func, "A scientific spectral image must be given");
09755         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09756         return NULL;
09757     }
09758 
09759     if (skylines) {
09760         line = cpl_vector_get_data(skylines);
09761         nlines = cpl_vector_get_size(skylines);
09762     }
09763     else {
09764         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09765                         "given: using internal list of reference sky lines");
09766         if (highres) {
09767            line = default_lines_hi;
09768            nlines = sizeof(default_lines_hi) / sizeof(double);
09769         }
09770         else {
09771            line = default_lines_lo;
09772            nlines = sizeof(default_lines_lo) / sizeof(double);
09773         }
09774     }
09775 
09776     if (calibration)
09777         cdata = cpl_image_get_data(calibration);
09778 
09779     nx = cpl_image_get_size_x(image);
09780     ny = cpl_image_get_size_y(image);
09781 
09782     sdata = cpl_image_get_data(image);
09783     
09784 
09785     /*FIXME: This is a remnant of the adaptation of the function
09786      * mos_wavelength_align(), where an offset table was created.
09787      * I leave it here because I am in a hurry, it is just used to
09788      * hold the list of selected sky lines.
09789      *
09790      * Define table of wavelengths
09791      */
09792 
09793     nrows = 0;
09794     for (i = 0; i < nlines; i++)
09795         if (line[i] > firstLambda && line[i] < lastLambda)
09796             nrows++;
09797 
09798     offsets = cpl_table_new(nrows);
09799     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09800     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09801 
09802     nrows = 0;
09803     for (i = 0; i < nlines; i++) {
09804         if (line[i] > firstLambda && line[i] < lastLambda) {
09805             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09806             nrows++;
09807         }
09808     }
09809 
09810     /*
09811      * Here "line" is made to point to the new list of selected wavelengths
09812      */
09813 
09814     line = cpl_table_get_data_double(offsets, "wave");
09815     nlines = nrows;
09816 
09817     idsorder = 0;
09818     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09819         ++idsorder;
09820     --idsorder;
09821 
09822 
09823     /*
09824      * Allocate a dummy table for collecting all the offsets
09825      * for all the lines
09826      */
09827 
09828     dummy = cpl_table_new(ny);
09829     for (j = 0; j < nlines; j++) {
09830         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09831         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09832         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09833         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
09834     }
09835 
09836     for (j = 0; j < ny; j++, sdata += nx) {
09837 
09838         /*
09839          * Get the IDS polynomial for the current slit row
09840          */
09841 
09842         missing = 0;
09843         ids = cpl_polynomial_new(1);
09844         for (k = 0; k <= idsorder; k++) {
09845             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
09846             if (missing) {
09847                 cpl_polynomial_delete(ids);
09848                 break;
09849             }
09850             cpl_polynomial_set_coeff(ids, &k, c);
09851         }
09852         if (missing)
09853             continue;
09854 
09855         for (k = 0; k < nlines; k++) {
09856             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09857             startPos = expPos - sradius;
09858             endPos   = startPos + window;
09859             if (startPos < 0 || endPos >= nx)
09860                 continue;
09861            
09862             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09863                 pos += startPos;
09864                 offset = pos - expPos;
09865                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
09866                 cpl_table_set_double(dummy, name, j, offset);
09867             }
09868         }
09869 
09870         cpl_polynomial_delete(ids);
09871     }
09872 
09873 
09874     /*
09875      * At this point for each sky line we model its offset along
09876      * the image rows using a robust linear fitting
09877      */
09878 
09879     for (j = 0; j < nlines; j++) {
09880         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09881         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09882         if (cpl_table_has_valid(dummy, name)) {
09883 
09884             /*
09885              * In the following, the "fittable" is just a tool for
09886              * eliminating invalid points from the vectors to be fitted.
09887              */
09888 
09889             double        q, m;
09890             cpl_bivector *list;
09891 
09892             fittable = cpl_table_new(ny);
09893             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
09894             cpl_table_set_column_unit(fittable, "row", "pixel");
09895             for (k = 0; k < ny; k++)
09896                  cpl_table_set_double(fittable, "row", k, k);
09897             cpl_table_duplicate_column(fittable, "offset", dummy, name);
09898             npoints = ny - cpl_table_count_invalid(fittable, "offset");
09899             cpl_table_erase_invalid(fittable);
09900             row = cpl_vector_wrap(npoints,
09901                                cpl_table_get_data_double(fittable, "row"));
09902             offs = cpl_vector_wrap(npoints,
09903                                cpl_table_get_data_double(fittable, "offset"));
09904             list = cpl_bivector_wrap_vectors(row, offs);
09905             robustLinearFit(list, &q, &m, &rms);
09906             cpl_bivector_unwrap_vectors(list);
09907             cpl_vector_unwrap(row);
09908             cpl_vector_unwrap(offs);
09909             cpl_table_delete(fittable);
09910             for (k = 0; k < ny; k++)
09911                  cpl_table_set_double(dummy, fname, k, q + m*k);
09912         }
09913     }
09914 
09915 
09916     /*
09917      * Now each dummy table row consists of a sequence of offsets,
09918      * one for each wavelength. A table row corresponds to an image row.
09919      * We must fit a polynomial to each one of these rows, in order to
09920      * express the offsets as a function of wavelength. The obtained 
09921      * polynomial coefficients are used to correct the IDS coefficients.
09922      */
09923 
09924     for (i = 0; i < ny; i++) {
09925 
09926         if (!cpl_table_is_valid(idscoeff, clab[0], i))
09927             continue;
09928 
09929         npoints = 0;
09930         for (j = 0; j < nlines; j++) {
09931             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09932             if (cpl_table_is_valid(dummy, name, i))
09933                 npoints++;
09934         }
09935 
09936         if (npoints == 0)
09937             continue;
09938 
09939         uorder = order;
09940         if (npoints <= uorder)
09941             uorder = npoints - 1;
09942 
09943         if (uorder > 1) {
09944 
09945             /*
09946              * Model offsets with polynomial fitting
09947              */
09948 
09949             wave = cpl_vector_new(npoints);
09950             wdata = cpl_vector_get_data(wave);
09951             offs = cpl_vector_new(npoints);
09952             odata = cpl_vector_get_data(offs);
09953 
09954             npoints = 0;
09955             for (j = 0; j < nlines; j++) {
09956                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09957                 if (cpl_table_is_valid(dummy, name, i)) {
09958                     wdata[npoints] = line[j] - refwave;
09959                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
09960                     npoints++;
09961                 }
09962             }
09963 
09964             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09965 
09966             rms = sqrt(rms * (uorder + 1) / npoints);
09967 
09968             cpl_vector_delete(wave);
09969             cpl_vector_delete(offs);
09970 
09971             /*
09972              * Now correct the coefficients of the corresponding IDS
09973              * polynomials related to this slit:
09974              */
09975 
09976             for (j = 0; j <= uorder; j++) {
09977                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09978                 c = cpl_polynomial_get_coeff(polycorr, &j);
09979                 data[i] += c;
09980             }
09981 
09982             data = cpl_table_get_data_double(idscoeff, "error");
09983             data[i] = sqrt(data[i]*data[i] + rms*rms);
09984 
09985             idata = cpl_table_get_data_int(idscoeff, "nlines");
09986             idata[i] = npoints;
09987 
09988             /*
09989              * If a wavelengths map was provided, correct it to keep
09990              * into account the alignment to skylines:
09991              */
09992 
09993             if (calibration) {
09994                 for (k = 1; k < nx; k++) {
09995                     lambda1 = cdata[k - 1 + i*nx];
09996                     lambda2 = cdata[k + i*nx];
09997                     if (lambda1 < 1.0 || lambda2 < 1.0)
09998                         continue;
09999                     offset = cpl_polynomial_eval_1d(polycorr,
10000                                                     lambda1-refwave, NULL);
10001                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10002                 }
10003             }
10004 
10005             cpl_polynomial_delete(polycorr);
10006 
10007         }
10008         else if (uorder == 1) {
10009 
10010             /*
10011              * Model offsets with robust linear fitting
10012              */
10013 
10014             cpl_bivector *list;
10015             double        q, m;
10016 
10017             wave = cpl_vector_new(npoints);
10018             wdata = cpl_vector_get_data(wave);
10019             offs = cpl_vector_new(npoints);
10020             odata = cpl_vector_get_data(offs);
10021 
10022             npoints = 0;
10023             for (j = 0; j < nlines; j++) {
10024                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10025                 if (cpl_table_is_valid(dummy, name, i)) {
10026                     wdata[npoints] = line[j] - refwave;
10027                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10028                     npoints++;
10029                 }
10030             }
10031 
10032             list = cpl_bivector_wrap_vectors(wave, offs);
10033             robustLinearFit(list, &q, &m, &rms);
10034 
10035             rms = sqrt(rms * (uorder + 1) / npoints);
10036 
10037             cpl_bivector_unwrap_vectors(list);
10038             cpl_vector_delete(wave);
10039             cpl_vector_delete(offs);
10040 
10041             /*
10042              * Now correct the coefficients of the corresponding IDS
10043              * polynomials related to this row:
10044              */
10045 
10046             for (j = 0; j <= uorder; j++) {
10047                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10048                 if (j)
10049                     c = m;
10050                 else
10051                     c = q;
10052                 data[i] += c;
10053             }
10054 
10055             data = cpl_table_get_data_double(idscoeff, "error");
10056             data[i] = sqrt(data[i]*data[i] + rms*rms);
10057 
10058             idata = cpl_table_get_data_int(idscoeff, "nlines");
10059             idata[i] = npoints;
10060 
10061             /*
10062              * If a wavelengths map was provided, correct it to keep
10063              * into account the alignment to skylines:
10064              */
10065 
10066             if (calibration) {
10067                 for (k = 1; k < nx; k++) {
10068                     lambda1 = cdata[k - 1 + i*nx];
10069                     lambda2 = cdata[k + i*nx];
10070                     if (lambda1 < 1.0 || lambda2 < 1.0)
10071                         continue;
10072                     offset = q + m*(lambda1-refwave);
10073                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10074                 }
10075             }
10076         }
10077         else {
10078 
10079             /*
10080              * Just compute median offset
10081              */
10082 
10083             offs = cpl_vector_new(npoints);
10084             odata = cpl_vector_get_data(offs);
10085 
10086             npoints = 0;
10087             for (j = 0; j < nlines; j++) {
10088                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10089                 if (cpl_table_is_valid(dummy, name, i)) {
10090                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10091                     npoints++;
10092                 }
10093             }
10094 
10095             offset = cpl_vector_get_median_const(offs);
10096 
10097             if (npoints > 1) {
10098                 rms = cpl_vector_get_stdev(offs);
10099             }
10100             else if (npoints == 1) {
10101                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10102                 if (cpl_table_has_valid(dummy, name)) {
10103                     rms = cpl_table_get_column_stdev(dummy, name);
10104                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10105                 }
10106                 else {
10107                     rms = 0.0;
10108                 }
10109             }
10110             else {
10111                 rms = 0.0;
10112             }
10113 
10114             rms /= sqrt(npoints);
10115 
10116             cpl_vector_delete(offs);
10117 
10118             /*
10119              * Now correct the constant term of the corresponding IDS
10120              * polynomials related to this slit:
10121              */
10122 
10123             data = cpl_table_get_data_double(idscoeff, clab[0]);
10124             data[i] += offset;
10125 
10126             data = cpl_table_get_data_double(idscoeff, "error");
10127             data[i] = sqrt(data[i]*data[i] + rms*rms);
10128 
10129             idata = cpl_table_get_data_int(idscoeff, "nlines");
10130             idata[i] = npoints;
10131 
10132             /*
10133              * If a wavelengths map was provided, correct it to keep
10134              * into account the alignment to skylines. Note that
10135              * the offset must be converted from pixels to wavelengths.
10136              */
10137 
10138             if (calibration) {
10139                 for (k = 1; k < nx; k++) {
10140                     lambda1 = cdata[k - 1 + i*nx];
10141                     lambda2 = cdata[k + i*nx];
10142                     if (lambda1 < 1.0 || lambda2 < 1.0)
10143                         continue;
10144                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10145                 }
10146             }
10147         }
10148     }
10149 
10150     missing = 1;
10151     for (j = 0; j < nlines; j++) {
10152         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10153         if (cpl_table_has_valid(dummy, name)) {
10154             missing = 0;
10155             offset = cpl_table_get_column_median(dummy, name);
10156             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
10157                          line[j], offset);
10158         }
10159         else {
10160             cpl_msg_info(func, 
10161                          "Median offset for %.2f: not available", line[j]);
10162         }
10163     }
10164 
10165     cpl_table_delete(offsets);
10166 
10167     if (missing) {
10168         cpl_table_delete(dummy);
10169         dummy = NULL;
10170     }
10171 
10172     return dummy;
10173 
10174 }
10175 
10176 
10204 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
10205                            double wavestart, double dispersion, int radius,
10206                            int highres)
10207 {
10208 
10209     const char *func = "mos_distortions_rms";
10210 
10211     int xlen;
10212     int ylen;
10213     int numLines;
10214     int cpix, npix, nzero;
10215     int sp, ep;
10216     int i, j, k;
10217     int npeaks, allPeaks;
10218 
10219     float *profile;
10220     float  peak, expectPeak, offset;
10221     double lambda;
10222 
10223     double  average;
10224     double  rms, oneRms;
10225 
10226     float  *sdata;
10227     double *wdata;
10228 
10229   
10230     xlen = cpl_image_get_size_x(rectified);
10231     ylen = cpl_image_get_size_y(rectified);
10232     sdata = cpl_image_get_data(rectified);
10233 
10234     if (lines) {
10235         wdata = cpl_vector_get_data(lines);
10236         numLines = cpl_vector_get_size(lines);
10237     }
10238     else {
10239         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10240                         "given: using internal list of reference sky lines");
10241         if (highres) {
10242            wdata = default_lines_hi;
10243            numLines = sizeof(default_lines_hi) / sizeof(double);
10244         }
10245         else {
10246            wdata = default_lines_lo;
10247            numLines = sizeof(default_lines_lo) / sizeof(double);
10248         }
10249     }
10250 
10251     npix = 2 * radius + 1;
10252     profile = cpl_calloc(npix, sizeof(float));
10253 
10254     rms = 0.0;
10255     allPeaks = 0;
10256 
10257     for (i = 0; i < numLines; i++) {
10258 
10259         /*
10260          *  Expected peak and closest pixel to specified wavelength.
10261          */
10262 
10263         lambda = wdata[i];
10264         expectPeak = (lambda - wavestart) / dispersion;
10265         cpix = floor(expectPeak + 0.5);
10266 
10267         /*
10268          *  Search interval for peak. Abort if too close to image border.
10269          */
10270 
10271         sp = cpix - radius;
10272         ep = cpix + radius;
10273 
10274         if (sp < 0 || ep > xlen)
10275             continue;
10276 
10277         average = 0.0;
10278         npeaks = 0;
10279         oneRms = 0.0;
10280 
10281         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
10282             nzero = 0;
10283             for (k = 0; k < npix; k++) {
10284                 profile[k] = sdata[sp + k + j * xlen];
10285                 if (fabs(profile[k]) < 0.0001)
10286                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
10287             }
10288             if (nzero > 0)
10289                 continue;
10290 
10291             if (peakPosition(profile, npix, &peak, 1) == 0) {
10292                 offset = (sp + peak) - expectPeak;
10293                 average += offset;
10294                 rms += fabs(offset);
10295                 oneRms += fabs(offset);
10296                 npeaks++;
10297                 allPeaks++;
10298             }
10299         }
10300 
10301         if (npeaks)
10302             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
10303                          lambda, oneRms / npeaks * 1.25, npeaks);
10304         else
10305             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
10306     }
10307 
10308     cpl_free(profile);
10309 
10310     if (allPeaks < 10)
10311         return 0.0;
10312 
10313     rms /= allPeaks;
10314     rms *= 1.25;       /* Factor to convert average deviation to sigma */
10315 
10316     return rms;
10317 
10318 }
10319 
10320 
10341 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
10342                          double blue, double red, double dispersion, int trend)
10343 {
10344     const char *func = "mos_map_pixel";
10345 
10346     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10347                                                  /* Max order is 5 */
10348 
10349     cpl_polynomial *ids;
10350     cpl_image      *map;
10351     float          *mdata;
10352     double          lambda;
10353     double          c;
10354     int             order;
10355     int             xsize, ysize;
10356     int             missing;
10357     int             i, j, k;
10358 
10359 
10360     if (idscoeff == NULL) {
10361         cpl_msg_error(func, "An IDS coeff table must be given");
10362         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10363         return NULL;
10364     }
10365 
10366     xsize = (red - blue) / dispersion;
10367     ysize = cpl_table_get_nrow(idscoeff);
10368     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10369     mdata = cpl_image_get_data(map);
10370 
10371     order = 0;
10372     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10373         ++order;
10374     --order;
10375 
10376     for (i = 0; i < ysize; i++, mdata += xsize) {
10377 
10378         missing = 0;
10379         ids = cpl_polynomial_new(1);
10380         for (k = trend; k <= order; k++) {
10381             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10382             if (missing) {
10383                 cpl_polynomial_delete(ids);
10384                 break;
10385             }
10386             cpl_polynomial_set_coeff(ids, &k, c);
10387         }
10388         if (missing)
10389             continue;
10390 
10391         for (j = 0; j < xsize; j++) {
10392             lambda = blue + j*dispersion;
10393             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
10394         }
10395 
10396         cpl_polynomial_delete(ids);
10397     }
10398 
10399     return map;
10400 
10401 }
10402 
10403 
10425 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
10426                             double blue, double red)
10427 {
10428     const char *func = "mos_map_idscoeff";
10429 
10430     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10431                                                  /* Max order is 5 */
10432 
10433     cpl_polynomial *ids;
10434     cpl_image      *map;
10435     float          *mdata;
10436     double          lambda;
10437     double          c;
10438     int             order;
10439     int             ysize;
10440     int             missing;
10441     int             i, j, k;
10442 
10443 
10444     if (idscoeff == NULL) {
10445         cpl_msg_error(func, "An IDS coeff table must be given");
10446         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10447         return NULL;
10448     }
10449 
10450     if (xsize < 1) {
10451         cpl_msg_error(func, "Invalid image size");
10452         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10453         return NULL;
10454     }
10455 
10456     if (xsize < 20 || xsize > 5000) {
10457         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10458                         xsize);
10459     }
10460 
10461     ysize = cpl_table_get_nrow(idscoeff);
10462     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10463     mdata = cpl_image_get_data(map);
10464 
10465     order = 0;
10466     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10467         ++order;
10468     --order;
10469 
10470     for (i = 0; i < ysize; i++, mdata += xsize) {
10471 
10472         missing = 0;
10473         ids = cpl_polynomial_new(1);
10474         for (k = 0; k <= order; k++) {
10475             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10476             if (missing) {
10477                 cpl_polynomial_delete(ids);
10478                 break;
10479             }
10480             cpl_polynomial_set_coeff(ids, &k, c);
10481         }
10482         if (missing)
10483             continue;
10484 
10485         for (j = 0; j < xsize; j++) {
10486             lambda = mos_eval_dds(ids, blue, red, reference, j);
10487             if (lambda >= blue && lambda <= red) {
10488                 mdata[j] = lambda;
10489             }
10490         }
10491 
10492         cpl_polynomial_delete(ids);
10493     }
10494 
10495     return map;
10496 
10497 }
10498 
10499 
10534 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10535                                cpl_table *slits, cpl_table *polytraces, 
10536                                double reference, double blue, double red, 
10537                                double dispersion)
10538 {
10539     const char *func = "mos_map_wavelengths";
10540 
10541     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10542                                                  /* Max order is 5 */
10543     cpl_polynomial *polytop;
10544     cpl_polynomial *polybot;
10545     cpl_image      *remapped;
10546     float          *data;
10547     float          *wdata;
10548     float          *sdata;
10549     float          *xdata;
10550     double          vtop, vbot, value;
10551     double          top, bot;
10552     double          coeff;
10553     double          ytop, ybot;
10554     double          ypos;
10555     double          fvalue;
10556     int             ivalue;
10557     int             yint, ysize, yprev;
10558     int             nslits;
10559     int             npseudo;
10560     int            *slit_id;
10561     int            *position;
10562     int            *length;
10563     int             nx, ny;
10564     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10565     int             missing_top, missing_bot;
10566     int             null;
10567     int             order;
10568     int             i, j, k;
10569 
10570 
10571     if (spatial == NULL || calibration == NULL || 
10572         slits == NULL || polytraces == NULL) {
10573         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10574         return NULL;
10575     }
10576 
10577     if (dispersion <= 0.0) {
10578         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10579         return NULL;
10580     }
10581 
10582     if (red - blue < dispersion) {
10583         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10584         return NULL;
10585     }
10586 
10587     nx = cpl_image_get_size_x(spatial);
10588     ny = cpl_image_get_size_y(spatial);
10589     ysize = cpl_image_get_size_y(calibration);
10590     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10591     data  = cpl_image_get_data(remapped);
10592     sdata = cpl_image_get_data(spatial);
10593     wdata = cpl_image_get_data(calibration);
10594 
10595     nslits   = cpl_table_get_nrow(slits);
10596     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10597     order    = cpl_table_get_ncol(polytraces) - 2;
10598     position = cpl_table_get_data_int(slits, "position");
10599     length   = cpl_table_get_data_int(slits, "length");
10600 
10601     /*
10602      * The spatial resampling is performed for a certain number of 
10603      * pixels above and below the position of the reference wavelength:
10604      */
10605 
10606     pixel_above = (red - reference) / dispersion;
10607     pixel_below = (reference - blue) / dispersion;
10608 
10609     for (i = 0; i < nslits; i++) {
10610 
10611         if (length[i] == 0)
10612             continue;
10613 
10614         /*
10615          * Note that the x coordinate of the reference pixels on the CCD
10616          * is taken arbitrarily at the top end of each slit. This wouldn't
10617          * be entirely correct in case of curved slits, or in presence of
10618          * heavy distortions: in such cases the spatial resampling is
10619          * really performed across a wide range of wavelengths. But
10620          * the lag between top and bottom spectral curvature models 
10621          * would introduce even in such cases negligible effects on
10622          * the spectral spatial resampling.
10623          */
10624 
10625         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10626 
10627         start_pixel = refpixel - pixel_below;
10628         if (start_pixel < 0)
10629             start_pixel = 0;
10630 
10631         end_pixel = refpixel + pixel_above;
10632         if (end_pixel > nx)
10633             end_pixel = nx;
10634 
10635         /*
10636          * Recover from the table of spectral curvature coefficients
10637          * the curvature polynomials.
10638          */
10639 
10640         missing_top = 0;
10641         polytop = cpl_polynomial_new(1);
10642         for (k = 0; k <= order; k++) {
10643             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10644             if (null) {
10645                 cpl_polynomial_delete(polytop);
10646                 missing_top = 1;
10647                 break;
10648             }
10649             cpl_polynomial_set_coeff(polytop, &k, coeff);
10650         }
10651 
10652         missing_bot = 0;
10653         polybot = cpl_polynomial_new(1);
10654         for (k = 0; k <= order; k++) {
10655             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10656             if (null) {
10657                 cpl_polynomial_delete(polybot);
10658                 missing_bot = 1;
10659                 break;
10660             }
10661             cpl_polynomial_set_coeff(polybot, &k, coeff);
10662         }
10663 
10664         if (missing_top && missing_bot) {
10665             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10666                           slit_id[i]);
10667             continue;
10668         }
10669 
10670         /*
10671          * In case just one of the two edges was not traced, the other
10672          * edge curvature model is duplicated and shifted to the other
10673          * end of the slit: better than nothing!
10674          */
10675 
10676         if (missing_top) {
10677             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
10678                           "the spectral curvature of the lower edge "
10679                           "is used instead.", slit_id[i]);
10680             polytop = cpl_polynomial_duplicate(polybot);
10681             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10682             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10683             k = 0;
10684             coeff = cpl_polynomial_get_coeff(polybot, &k);
10685             coeff += ytop - ybot;
10686             cpl_polynomial_set_coeff(polytop, &k, coeff);
10687         }
10688 
10689         if (missing_bot) {
10690             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
10691                           "the spectral curvature of the upper edge "
10692                           "is used instead.", slit_id[i]);
10693             polybot = cpl_polynomial_duplicate(polytop);
10694             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10695             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10696             k = 0;
10697             coeff = cpl_polynomial_get_coeff(polytop, &k);
10698             coeff -= ytop - ybot;
10699             cpl_polynomial_set_coeff(polybot, &k, coeff);
10700         }
10701 
10702         /*
10703          * Point to current slit on wavelength calibration image.
10704          * Note that the npseudo value related to this slit is equal 
10705          * to the number of spatial pseudo-pixels decreased by 1 
10706          * (compare with function mos_spatial_calibration()).
10707          */
10708 
10709         xdata = wdata + nx*position[i];
10710         npseudo = length[i] - 1;
10711 
10712         /*
10713          * Write interpolated wavelengths to CCD image
10714          */
10715 
10716         for (j = start_pixel; j < end_pixel; j++) {
10717             top = cpl_polynomial_eval_1d(polytop, j, NULL);
10718             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
10719             for (k = 0; k <= npseudo; k++) {
10720                 ypos = top - k*(top-bot)/npseudo;
10721                 yint = ypos;
10722 
10723                 /* 
10724                  * The line:
10725                  *     value = sdata[j + nx*yint];
10726                  * should be equivalent to:
10727                  *     value = npseudo*(top-yint)/(top-bot);
10728                  */
10729 
10730                 if (yint < 0 || yint >= ny-1) {
10731                     yprev = yint;
10732                     continue;
10733                 }
10734 
10735                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
10736                 ivalue = value;               /* Nearest spatial pixels:   */
10737                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
10738                 if (ivalue < npseudo && ivalue >= 0) {
10739                     vtop = xdata[j + nx*(npseudo-ivalue)];
10740                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
10741                     if (vtop < 1.0) {  /* Impossible wavelength */
10742                         if (vbot < 1.0) {
10743                             value = 0.0;
10744                         }
10745                         else {
10746                             value = vbot;
10747                         }
10748                     }
10749                     else if (vbot < 1.0) {
10750                         if (k)
10751                             value = vtop;
10752                         else
10753                             value = 0.0;
10754                     }
10755                     else if (fabs(vbot-vtop) > 10*dispersion) {
10756                         value = 0.0;
10757                     }
10758                     else {
10759                         value = vtop*(1-fvalue) + vbot*fvalue;
10760                     }
10761                     data[j + nx*yint] = value;
10762 
10763                     if (k) {
10764 
10765                         /*
10766                          * This is added to recover lost pixels on
10767                          * the CCD image (pixels are lost because
10768                          * the CCD pixels are less than npseudo+1).
10769                          */
10770 
10771                         if (yprev - yint > 1) {
10772                             value = sdata[j + nx*(yint+1)];
10773                             ivalue = value;
10774                             fvalue = value - ivalue;
10775                             if (ivalue < npseudo && ivalue >= 0) {
10776                                 vtop = xdata[j + nx*(npseudo-ivalue)];
10777                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
10778                                 if (vtop < 1.0) {
10779                                     if (vbot < 1.0) {
10780                                         value = data[j + nx*(yint+1)];
10781                                     }
10782                                     else {
10783                                         value = vbot;
10784                                     }
10785                                 }
10786                                 else if (vbot < 1.0) {
10787                                     value = vtop;
10788                                 }
10789                                 else if (fabs(vbot-vtop) > 2*dispersion) {
10790                                     value = vtop;
10791                                 }
10792                                 else {
10793                                     value = vtop*(1-fvalue) + vbot*fvalue;
10794                                 }
10795                                 data[j + nx*(yint+1)] = value;
10796                             }
10797                         }
10798                     }
10799                 }
10800                 yprev = yint;
10801             }
10802         }
10803         cpl_polynomial_delete(polytop);
10804         cpl_polynomial_delete(polybot);
10805     }
10806 
10807     return remapped;
10808 }
10809 
10883 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
10884                             cpl_image *spatial, cpl_table *slits,
10885                             cpl_table *polytraces, double reference,
10886                             double blue, double red, double dispersion,
10887                             int flux)
10888 {
10889     const char *func = "mos_map_spectrum";
10890     
10891     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10892                                                  /* Max order is 5 */
10893     cpl_polynomial *polytop;
10894     cpl_polynomial *polybot;
10895     cpl_image      *remapped;
10896     cpl_image     **exslit;
10897     float          *data;
10898     float          *wdata;
10899     float          *sdata;
10900     float          *xdata;
10901     double          lambda00, lambda01, lambda10, lambda11, lambda;
10902     double          space00, space01, space10, space11, space;
10903     double          value00, value01, value10, value11, value0, value1, value;
10904     double          dL, dS;
10905     double          top, bot;
10906     double          coeff;
10907     double          ytop, ybot;
10908     double          xfrac, yfrac;
10909     int             yint, ysize;
10910     int             itop, ibot;
10911     int             shift;
10912     int             L, S;
10913     int             nslits;
10914     int             npseudo;
10915     int            *slit_id;
10916     int            *position;
10917     int            *length;
10918     int             nx, ny;
10919     int             x, y;
10920     int             nlambda;
10921     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10922     int             missing_top, missing_bot; 
10923     int             null;
10924     int             order;
10925     int             i, k;
10926     
10927 
10928     flux += flux;
10929 
10930     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
10931         slits == NULL || polytraces == NULL) { 
10932         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10933         return NULL;
10934     }
10935 
10936     if (dispersion <= 0.0) {
10937         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10938         return NULL;
10939     }
10940 
10941     if (red - blue < dispersion) {
10942         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10943         return NULL;
10944     }
10945     
10946     nx = cpl_image_get_size_x(spectra);
10947     ny = cpl_image_get_size_y(spectra);
10948 
10949     if (nx != cpl_image_get_size_x(spatial) ||
10950         ny != cpl_image_get_size_y(spatial) ||
10951         nx != cpl_image_get_size_x(wavecalib) ||
10952         ny != cpl_image_get_size_y(wavecalib)) {
10953         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10954         return NULL;
10955     }
10956 
10957     nlambda     = (red - blue) / dispersion;
10958     pixel_above = (red - reference) / dispersion;
10959     pixel_below = (reference - blue) / dispersion;
10960 
10961     data  = cpl_image_get_data(spectra);
10962     sdata = cpl_image_get_data(spatial);
10963     wdata = cpl_image_get_data(wavecalib);
10964     
10965     nslits   = cpl_table_get_nrow(slits);
10966     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10967     order    = cpl_table_get_ncol(polytraces) - 2;
10968     position = cpl_table_get_data_int(slits, "position");
10969     length   = cpl_table_get_data_int(slits, "length");
10970     
10971     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
10972 
10973     for (i = 0; i < nslits; i++) {
10974 
10975          if (length == 0)
10976              continue;
10977 
10978         /*
10979          * Note that the x coordinate of the reference pixels on the CCD
10980          * is taken arbitrarily at the top end of each slit. This wouldn't
10981          * be entirely correct in case of curved slits, or in presence of
10982          * heavy distortions: in such cases the spatial resampling is
10983          * really performed across a wide range of wavelengths. But
10984          * the lag between top and bottom spectral curvature models
10985          * would introduce even in such cases negligible effects on
10986          * the spectral spatial resampling.
10987          */
10988 
10989         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10990 
10991         start_pixel = refpixel - pixel_below;
10992         if (start_pixel < 1)
10993             start_pixel = 1;
10994 
10995         end_pixel = refpixel + pixel_above;
10996         if (end_pixel > nx)
10997             end_pixel = nx;
10998 
10999         /*
11000          * Recover from the table of spectral curvature coefficients
11001          * the curvature polynomials.
11002          */
11003 
11004         missing_top = 0;
11005         polytop = cpl_polynomial_new(1);
11006         for (k = 0; k <= order; k++) {
11007             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11008             if (null) {
11009                 cpl_polynomial_delete(polytop);
11010                 missing_top = 1;
11011                 break;
11012             }
11013             cpl_polynomial_set_coeff(polytop, &k, coeff);
11014         }
11015 
11016         missing_bot = 0;
11017         polybot = cpl_polynomial_new(1);
11018         for (k = 0; k <= order; k++) {
11019             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11020             if (null) {
11021                 cpl_polynomial_delete(polybot);
11022                 missing_bot = 1;
11023                 break;
11024             }
11025             cpl_polynomial_set_coeff(polybot, &k, coeff);
11026         }
11027 
11028         if (missing_top && missing_bot) {
11029             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11030                           slit_id[i]);
11031             continue;
11032         }
11033 
11034         /*
11035          * In case just one of the two edges was not traced, the other
11036          * edge curvature model is duplicated and shifted to the other
11037          * end of the slit: better than nothing!
11038          */
11039 
11040         if (missing_top) {
11041             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11042                           "the spectral curvature of the lower edge "
11043                           "is used instead.", slit_id[i]);
11044             polytop = cpl_polynomial_duplicate(polybot);
11045             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11046             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11047             k = 0;
11048             coeff = cpl_polynomial_get_coeff(polybot, &k);
11049             coeff += ytop - ybot;
11050             cpl_polynomial_set_coeff(polytop, &k, coeff);
11051         }
11052 
11053         if (missing_bot) {
11054             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11055                           "the spectral curvature of the upper edge "
11056                           "is used instead.", slit_id[i]);
11057             polybot = cpl_polynomial_duplicate(polytop);
11058             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11059             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11060             k = 0;
11061             coeff = cpl_polynomial_get_coeff(polytop, &k);
11062             coeff -= ytop - ybot;
11063             cpl_polynomial_set_coeff(polybot, &k, coeff);
11064         }
11065 
11066         /*
11067          * Allocate image for current extracted slit
11068          */
11069 
11070         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11071         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11072         npseudo = ceil(top-bot) + 1;
11073 
11074         if (npseudo < 1) {
11075             cpl_polynomial_delete(polytop);
11076             cpl_polynomial_delete(polybot);
11077             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11078                           slit_id[i]);
11079             continue;
11080         }
11081 
11082         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11083         xdata = cpl_image_get_data(exslit[i]);
11084 
11085         /*
11086          * Write interpolated spectral values to remapped slit spectrum.
11087          */
11088 
11089         for (x = start_pixel; x < end_pixel; x++) {
11090             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11091             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11092             itop = top + 1;
11093             ibot = bot;
11094             if (itop < 0)
11095                 itop = 0;
11096             if (itop > ny - 1)
11097                 itop = ny - 1;
11098             if (ibot < 0)
11099                 ibot = 0;
11100             if (ibot > ny - 1)
11101                 ibot = ny - 1;
11102             for (y = ibot; y < itop; y++) {
11103                  lambda11 = wdata[x + y*nx];
11104                  if (lambda11 < 1.0)        /* Impossible wavelength */
11105                      continue;
11106                  space11 = sdata[x + y*nx];
11107                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11108                      continue;
11109                  lambda01 = wdata[x - 1 + y*nx];
11110                  if (lambda01 < 1.0)        /* Impossible wavelength */
11111                      continue;
11112                  space01 = sdata[x - 1 + y*nx];
11113                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11114                      continue;
11115 
11116                  shift = 0;
11117 
11118 /****+
11119                  if (wdata[x + (y+1)*nx] > 1.0) {
11120                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11121                          shift = -1;
11122                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11123                              shift--;
11124                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11125                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11126                              shift++;
11127                          }
11128                      }
11129                      else {
11130                          shift = 1;
11131                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11132                              shift++;
11133                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
11134                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11135                              shift--;
11136                          }
11137                      }
11138                  }
11139 ****/
11140 
11141 /****
11142 printf("y = %d, shift = %d\n", y, shift);
11143 ****/
11144 
11145                  lambda10 = wdata[x + shift + (y+1)*nx];
11146                  if (lambda10 < 1.0)        /* Impossible wavelength */
11147                      continue;
11148                  space10 = sdata[x + shift + (y+1)*nx];
11149                  if (space10 < 0.0)         /* Impossible spatial coordinate */
11150                      continue;
11151                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
11152                  if (lambda00 < 1.0)        /* Impossible wavelength */
11153                      continue;
11154                  space00 = sdata[x - 1 + shift + (y+1)*nx];
11155                  if (space00 < 0.0)         /* Impossible spatial coordinate */
11156                      continue;
11157                  
11158                  /*
11159                   * Find the variation in lambda and space in this
11160                   * position for each CCD pixel (both quantities are 
11161                   * expected to be positive).
11162                   */
11163 
11164                  dL = lambda11 - lambda01;
11165                  dS = space11 - space10;
11166 
11167                  /*
11168                   * Find the position (L,S) of the output pixel 
11169                   * (by integer truncation).
11170                   */
11171 
11172                  L = (lambda11 - blue)/dispersion + 0.5;
11173                  S = space11 + 0.5;                   /* Counted from top! */
11174 
11175                  if (L < 0 || L >= nlambda)
11176                      continue;
11177                  if (S < 0 || S > npseudo)
11178                      continue;
11179 
11180                  /*
11181                   * Find the coordinate of pixel (L,S)
11182                   */
11183 
11184                  lambda = blue + L*dispersion;
11185                  space  = S;
11186 
11187                  /*
11188                   * Find the interpolation point on the CCD: it is
11189                   * defined as the (positive) distance from current
11190                   * CCD pixel (x,y) of the interpolation point (x',y'),
11191                   * measured in CCD pixels. The interpolation point
11192                   * is located between the four CCD pixels selected
11193                   * above.
11194                   */
11195 
11196                  xfrac = (lambda11-lambda)/dL;
11197                  yfrac = (space11-space)/dS;
11198 
11199 /*
11200 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
11201 printf("xyfrac = %f, %f\n", xfrac, yfrac);
11202 */
11203 
11204                  /*
11205                   * Get the four values to interpolate
11206                   */
11207 
11208                  value11 = data[x + y*nx];
11209                  value01 = data[x - 1 + y*nx];
11210                  value10 = data[x + shift + (y+1)*nx];
11211                  value00 = data[x + shift - 1 + (y+1)*nx];
11212 
11213                  /*
11214                   * Interpolation
11215                   */
11216 
11217                  value1 = (1-xfrac)*value11 + xfrac*value01;
11218                  value0 = (1-xfrac)*value10 + xfrac*value00;
11219                  value  = (1-yfrac)*value1  + yfrac*value0;
11220 
11221                  /*
11222                   * Write this value to the appropriate (L,S) coordinate
11223                   * on output slit
11224                   */
11225 
11226                  xdata[L + nlambda*(npseudo-S)] = value;
11227                  
11228             }
11229         }
11230         cpl_polynomial_delete(polytop);
11231         cpl_polynomial_delete(polybot);
11232     }
11233 
11234     /*
11235      * Now all the slits images are copied to a single image
11236      */
11237 
11238     ysize = 0;
11239     for (i = 0; i < nslits; i++)
11240         if (exslit[i])
11241             ysize += cpl_image_get_size_y(exslit[i]);
11242 
11243     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
11244 
11245     yint = -1;
11246     for (i = 0; i < nslits; i++) {
11247         if (exslit[i]) {
11248             yint += cpl_image_get_size_y(exslit[i]);
11249             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
11250             cpl_image_delete(exslit[i]);
11251             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
11252         }
11253     }
11254 
11255     cpl_free(exslit);
11256 
11257     return remapped;
11258 
11259 }
11260 
11261 
11294 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
11295                              double dispersion, double factor, int minpoints,
11296                              cpl_image *skymap)
11297 {
11298     const char *func = "mos_sky_map_super";
11299 
11300     cpl_vector **vector;
11301     cpl_vector **wvector;
11302     double       firstLambda, lastLambda;
11303     double       lambda, lambda1, lambda2;
11304     double       value, value1, value2;
11305     double       frac;
11306     float        min, max;
11307     int         *count;
11308     int          nbin, bin;
11309     int          nx, ny, npix;
11310     int          first_valid, valid_bins;
11311     int          i, j;
11312 
11313     cpl_table   *sky;
11314     double      *sky_spectrum;
11315     double      *sky_wave;
11316     float       *data;
11317     float       *sdata;
11318     float       *kdata;
11319 
11320 
11321     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11322         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11323         return NULL;
11324     }
11325     
11326     if (dispersion <= 0.0) {
11327         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11328         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
11329         return NULL;
11330     }
11331 
11332     nx = cpl_image_get_size_x(spectra);
11333     ny = cpl_image_get_size_y(spectra);
11334     npix = nx * ny;
11335 
11336     if (nx != cpl_image_get_size_x(wavemap) ||
11337         ny != cpl_image_get_size_y(wavemap) ||
11338         nx != cpl_image_get_size_x(skymap) ||
11339         ny != cpl_image_get_size_y(skymap)) {
11340         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11341         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
11342         return NULL;
11343     }
11344 
11345     if (factor < 1.0) {
11346         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11347         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
11348                       cpl_error_get_message());
11349         return NULL;
11350     }
11351 
11352     if (minpoints < 0) {
11353         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11354         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
11355         return NULL;
11356     }
11357 
11358     dispersion /= factor;
11359 
11360 
11361     /*
11362      * Find bluest and reddest wavelengths in the whole image
11363      */
11364 
11365     data = cpl_image_get_data(wavemap);
11366 
11367     for (i = 0; i < npix; i++) {
11368         if (data[i] > 1.0) {
11369             min = max = data[i];
11370             j = i+1;
11371             break;
11372         }
11373     }
11374 
11375     for (i = j; i < npix; i++) {
11376         if (data[i] < 1.0)      /* Impossible wavelength */
11377             continue;
11378         if (min > data[i])
11379             min = data[i];
11380         if (max < data[i])
11381             max = data[i];
11382     }
11383 
11384     firstLambda = min;
11385     lastLambda = max;
11386 
11387 
11388     /*
11389      * Determine length of median spectrum
11390      */
11391 
11392     nbin = (lastLambda - firstLambda) / dispersion;
11393 
11394     /*
11395      * Count how many values will be found for each spectral bin.
11396      * The ith bin has a wavelength range from firstLambda + i*dispersion
11397      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11398      * it is assigned to its central wavelength.
11399      */
11400 
11401     count = cpl_calloc(nbin, sizeof(int));
11402 
11403     data = cpl_image_get_data(wavemap);
11404 
11405     for (i = 0; i < npix; i++) {
11406         if (data[i] < 1.0)
11407             continue;
11408         bin = (data[i] - firstLambda) / dispersion;
11409         if (bin < nbin)                               /* Safer */
11410             count[bin]++;
11411     }
11412 
11413     valid_bins = 0;
11414     for (i = 0; i < nbin; i++)
11415         if (count[i] >= minpoints)
11416             valid_bins++;
11417 
11418     if (valid_bins < nbin/3) {
11419         cpl_msg_warning(func, "Cannot determine a good global sky "
11420                         "spectrum from input data");
11421         return NULL;
11422     }
11423 
11424 
11425     /*
11426      * Allocate an array of vectors with the appropriate size, to
11427      * contain a list of all the spectral pixels values. At the same
11428      * time, reset the array of counters (because we will have to
11429      * count again...).
11430      */
11431 
11432     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11433     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11434     for (i = 0; i < nbin; i++) {
11435         if (count[i] >= minpoints) {
11436             vector[i] = cpl_vector_new(count[i]);
11437             wvector[i] = cpl_vector_new(count[i]);
11438         }
11439         count[i] = 0;
11440     }
11441 
11442 
11443     /*
11444      * Read the wavemap and the spectral images, and add the data values
11445      * to the appropriate wavelength bins
11446      */
11447 
11448     data  = cpl_image_get_data(wavemap);
11449     sdata = cpl_image_get_data(spectra);
11450 
11451     for (i = 0; i < npix; i++) {
11452         if (data[i] < 1.0)
11453             continue;
11454         bin = (data[i] - firstLambda) / dispersion;
11455         if (bin < nbin) {                             /* Safer */
11456             if (vector[bin]) {
11457                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11458                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11459             }
11460             count[bin]++;
11461         }
11462     }
11463 
11464 
11465     /*
11466      * Compute the median flux for each wavelength bin, and destroy
11467      * at the same time the used vectors
11468      */
11469 
11470     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11471     sky_wave = cpl_calloc(nbin, sizeof(double));
11472     for (i = 0; i < nbin; i++) {
11473         if (vector[i]) {
11474             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11475             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11476             cpl_vector_delete(vector[i]);
11477             cpl_vector_delete(wvector[i]);
11478         }
11479     }
11480 
11481     cpl_free(vector);
11482     cpl_free(wvector);
11483 
11484 
11485     /*
11486      * Here possible gaps in the final spectrum are filled by interpolation
11487      */
11488 
11489     for (i = 0; i < nbin; i++) {
11490         if (count[i] >= minpoints) {
11491             first_valid = i;
11492             break;
11493         }
11494     }
11495     
11496     for (i = first_valid; i < nbin; i++) {
11497         if (count[i] < minpoints) {
11498             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11499             for (j = i+1; j < nbin; j++) {
11500                 if (count[j] >= minpoints) {
11501                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11502                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11503                                         / 2;
11504                     }
11505                     else {
11506                         frac = (sky_wave[i] - sky_wave[i-1]) 
11507                              / (sky_wave[j] - sky_wave[i-1]);
11508                         sky_spectrum[i] = frac * sky_spectrum[j]
11509                                         + (1 - frac) * sky_spectrum[i-1];
11510                     }
11511                 }
11512             }
11513         }
11514     }
11515 
11516 
11517     /*
11518      * Create the output table
11519      */
11520 
11521     sky = cpl_table_new(nbin);
11522     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11523     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11524     cpl_table_wrap_int(sky, count, "npoints");
11525 
11526 
11527     /*
11528      * Fill the sky map
11529      */
11530 
11531     data  = cpl_image_get_data(wavemap);
11532     sdata = cpl_image_get_data(spectra);
11533     kdata = cpl_image_get_data(skymap);
11534 
11535     for (i = 0; i < npix; i++) {
11536 
11537         /*
11538          * Currently based on linear interpolation
11539          */
11540 
11541         lambda = data[i];
11542         if (lambda < 1.0)
11543             continue;
11544         bin = (lambda - firstLambda) / dispersion;
11545         lambda1 = sky_wave[bin];
11546         value1 = sky_spectrum[bin];
11547         if (lambda1 < lambda) {
11548             bin++;
11549             if (bin < nbin) {
11550                 lambda2 = sky_wave[bin];
11551                 value2  = sky_spectrum[bin];
11552                 if (lambda2 - lambda1 < 0.1) {
11553                     value = (value1 + value2) / 2;
11554                 }
11555                 else {
11556                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11557                     value = frac * value2 + (1 - frac) * value1;
11558                 }
11559             }
11560             else {
11561                 value = value1;
11562             }
11563         }
11564         else {
11565             if (bin > 0) {
11566                 bin--;
11567                 lambda2 = lambda1;
11568                 value2  = value1;
11569                 lambda1 = sky_wave[bin];
11570                 value1  = sky_spectrum[bin];
11571                 if (lambda2 - lambda1 < 0.1) {
11572                     value = (value1 + value2) / 2;
11573                 }
11574                 else {
11575                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11576                     value = frac * value2 + (1 - frac) * value1;
11577                 }
11578             }
11579             else {
11580                 value = value1;
11581             }
11582         }
11583         kdata[i] = value;
11584     }
11585 
11586     if (first_valid)
11587         cpl_table_erase_window(sky, 0, first_valid);
11588 
11589     return sky;
11590 
11591 }
11592 
11593 
11627 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11628                        double dispersion, cpl_image *skymap)
11629 {
11630     const char *func = "mos_sky_map";
11631 
11632     cpl_vector **vector;
11633     double       firstLambda, lastLambda;
11634     double       lambda, lambda1, lambda2;
11635     double       value, value1, value2;
11636     float        min, max;
11637     int         *count;
11638     int          nbin, bin;
11639     int          nx, ny, npix;
11640     int          i, j;
11641 
11642     cpl_table   *sky;
11643     double      *sky_spectrum;
11644     float       *data;
11645     float       *sdata;
11646     float       *kdata;
11647     double      *wdata;
11648 
11649 
11650     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11651         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11652         return NULL;
11653     }
11654     
11655     if (dispersion <= 0.0) {
11656         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11657         return NULL;
11658     }
11659 
11660     nx = cpl_image_get_size_x(spectra);
11661     ny = cpl_image_get_size_y(spectra);
11662     npix = nx * ny;
11663 
11664     if (nx != cpl_image_get_size_x(wavemap) ||
11665         ny != cpl_image_get_size_y(wavemap) ||
11666         nx != cpl_image_get_size_x(skymap) ||
11667         ny != cpl_image_get_size_y(skymap)) {
11668         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11669         return NULL;
11670     }
11671 
11672 
11673     /*
11674      * Find bluest and reddest wavelengths in the whole image
11675      */
11676 
11677     data = cpl_image_get_data(wavemap);
11678 
11679     for (i = 0; i < npix; i++) {
11680         if (data[i] > 1.0) {
11681             min = max = data[i];
11682             j = i+1;
11683             break;
11684         }
11685     }
11686 
11687     for (i = j; i < npix; i++) {
11688         if (data[i] < 1.0)      /* Impossible wavelength */
11689             continue;
11690         if (min > data[i])
11691             min = data[i];
11692         if (max < data[i])
11693             max = data[i];
11694     }
11695 
11696     firstLambda = min;
11697     lastLambda = max;
11698 
11699 
11700     /*
11701      * Determine length of median spectrum
11702      */
11703 
11704     nbin = (lastLambda - firstLambda) / dispersion;
11705 
11706     /*
11707      * Count how many values will be found for each spectral bin.
11708      * The ith bin has a wavelength range from firstLambda + i*dispersion
11709      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11710      * it is assigned to its central wavelength.
11711      */
11712 
11713     count = cpl_calloc(nbin, sizeof(int));
11714 
11715     data = cpl_image_get_data(wavemap);
11716 
11717     for (i = 0; i < npix; i++) {
11718         if (data[i] < 1.0)
11719             continue;
11720         bin = (data[i] - firstLambda) / dispersion;
11721         if (bin < nbin)                               /* Safer */
11722             count[bin]++;
11723     }
11724 
11725 
11726     /*
11727      * Allocate an array of vectors with the appropriate size, to
11728      * contain a list of all the spectral pixels values. At the same
11729      * time, reset the array of counters (because we will have to
11730      * count again...).
11731      */
11732 
11733     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11734     for (i = 0; i < nbin; i++) {
11735         if (count[i])
11736             vector[i] = cpl_vector_new(count[i]);
11737         else
11738             vector[i] = NULL;
11739         count[i] = 0;
11740     }
11741 
11742 
11743     /*
11744      * Read the wavemap and the spectral images, and add the data values
11745      * to the appropriate wavelength bins
11746      */
11747 
11748     data  = cpl_image_get_data(wavemap);
11749     sdata = cpl_image_get_data(spectra);
11750 
11751     for (i = 0; i < npix; i++) {
11752         if (data[i] < 1.0)
11753             continue;
11754         bin = (data[i] - firstLambda) / dispersion;
11755         if (bin < nbin) {                             /* Safer */
11756             cpl_vector_set(vector[bin], count[bin], sdata[i]);
11757             count[bin]++;
11758         }
11759     }
11760 
11761 
11762     /*
11763      * Compute the median flux for each wavelength bin, and destroy
11764      * at the same time the used vectors
11765      */
11766 
11767     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11768     for (i = 0; i < nbin; i++) {
11769         if (vector[i]) {
11770             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11771             cpl_vector_delete(vector[i]);
11772         }
11773     }
11774 
11775     cpl_free(vector);
11776 
11777 
11778     /*
11779      * Here possible gaps in the final spectrum should be filled
11780      * by interpolation
11781      */
11782 
11783     /* ... */
11784 
11785     /*
11786      * Create the output table
11787      */
11788 
11789     sky = cpl_table_new(nbin);
11790     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
11791     cpl_table_set_column_unit(sky, "wavelength", "pixel");
11792     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11793     cpl_table_wrap_int(sky, count, "npoints");
11794     for (i = 0; i < nbin; i++)
11795         cpl_table_set_double(sky, "wavelength", i, 
11796                              firstLambda + (i+0.5)*dispersion);
11797 
11798 
11799     /*
11800      * Fill the sky map
11801      */
11802 
11803     data  = cpl_image_get_data(wavemap);
11804     sdata = cpl_image_get_data(spectra);
11805     kdata = cpl_image_get_data(skymap);
11806     wdata = cpl_table_get_data_double(sky, "wavelength");
11807 
11808     for (i = 0; i < npix; i++) {
11809 
11810         /*
11811          * Currently based on linear interpolation
11812          */
11813 
11814         lambda = data[i];
11815         if (lambda < 1.0)
11816             continue;
11817         bin = (lambda - firstLambda) / dispersion;
11818         lambda1 = wdata[bin];
11819         value1 = sky_spectrum[bin];
11820         if (lambda1 < lambda) {
11821             bin++;
11822             if (bin < nbin) {
11823                 lambda2 = wdata[bin];
11824                 value2  = sky_spectrum[bin];
11825                 value   = ((lambda2 - lambda)*value1 
11826                         +  (lambda - lambda1)*value2) / dispersion;
11827             }
11828             else {
11829                 value = value1;
11830             }
11831         }
11832         else {
11833             if (bin > 0) {
11834                 bin--;
11835                 lambda2 = lambda1;
11836                 value2  = value1;
11837                 lambda1 = wdata[bin];
11838                 value1  = sky_spectrum[bin];
11839                 value   = ((lambda2 - lambda)*value1 
11840                         +  (lambda - lambda1)*value2)/dispersion;
11841             }
11842             else {
11843                 value = value1;
11844             }
11845         }
11846         kdata[i] = value;
11847     }
11848 
11849     return sky;
11850 
11851 }
11852 
11853 
11869 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
11870 {
11871     const char *func = "mos_sky_local_old";
11872 
11873     cpl_image *exslit;
11874     cpl_image *sky;
11875     cpl_image *skymap;
11876     float     *data;
11877     float     *sdata;
11878     int        nx, ny;
11879     int        xlow, ylow, xhig, yhig;
11880     int        nslits;
11881     int       *slit_id;
11882     int       *position;
11883     int       *length;
11884     int        i, j, k;
11885 
11886 
11887     if (spectra == NULL) {
11888         cpl_msg_error(func, 
11889                       "A scientific rectified spectral image must be given");
11890         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11891         return NULL;
11892     }
11893 
11894     if (slits == NULL) {
11895         cpl_msg_error(func, "A slits position table must be given");
11896         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11897         return NULL;
11898     }
11899 
11900     nslits   = cpl_table_get_nrow(slits);
11901     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11902     position = cpl_table_get_data_int(slits, "position");
11903     length   = cpl_table_get_data_int(slits, "length");
11904 
11905     nx = cpl_image_get_size_x(spectra);
11906     ny = cpl_image_get_size_y(spectra);
11907 
11908     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11909 
11910     xlow = 1;
11911     xhig = nx;
11912     for (i = 0; i < nslits; i++) {
11913 
11914         if (length[i] == 0)
11915             continue;
11916 
11917         /*
11918          * Define the extraction boundaries. We DON'T write:
11919          *
11920          * ylow = position[i];
11921          * yhig = ylow + length[i];
11922          *
11923          * because the cpl_image pixels are counted from 1, and because in
11924          * cpl_image_extract() the coordinates of the last pixel are inclusive.
11925          */
11926 
11927         ylow = position[i] + 1;
11928         yhig = ylow + length[i] - 1;
11929 
11930         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
11931         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
11932         cpl_image_delete(exslit);
11933 
11934         data   = cpl_image_get_data(skymap);
11935         data  += nx * position[i];
11936 
11937         for (j = 0; j < length[i]; j++) {
11938             sdata  = cpl_image_get_data(sky);
11939             for (k = 0; k < nx; k++) {
11940                 *data++ = *sdata++;
11941             }
11942         }
11943 
11944         cpl_image_delete(sky);
11945     }
11946 
11947     return skymap;
11948 
11949 }
11950 
11951 
11971 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
11972 {
11973     const char *func = "mos_sky_local";
11974 
11975     char        name[MAX_COLNAME];
11976 
11977     cpl_polynomial *fit;
11978     cpl_vector     *points;
11979     cpl_vector     *values;
11980     cpl_vector     *keep_points;
11981     cpl_vector     *keep_values;
11982     cpl_image      *exslit;
11983     cpl_image      *sky;
11984     cpl_image      *subtracted;
11985     cpl_image      *profile;
11986     cpl_image      *skymap;
11987     cpl_table      *objects;
11988     float          *data;
11989     float          *sdata;
11990     float          *xdata;
11991     double         *vdata;
11992     double         *pdata;
11993     double          median;
11994     int             nx, ny;
11995     int             xlow, ylow, xhig, yhig;
11996     int             nslits;
11997     int            *slit_id;
11998     int            *position;
11999     int            *length;
12000     int            *is_sky;
12001     int             nsky, nbad;
12002     int             maxobjects;
12003     int             margin = 3;
12004     int             radius = 6;
12005     int             i, j, k;
12006 
12007 
12008     if (spectra == NULL) {
12009         cpl_msg_error(func, 
12010                       "A scientific rectified spectral image must be given");
12011         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12012         return NULL;
12013     }
12014 
12015     if (slits == NULL) {
12016         cpl_msg_error(func, "A slits position table must be given");
12017         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12018         return NULL;
12019     }
12020 
12021     if (order < 0) {
12022         cpl_msg_error(func, "Invalid fit order");
12023         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12024         return NULL;
12025     }
12026 
12027     nslits   = cpl_table_get_nrow(slits);
12028     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12029     position = cpl_table_get_data_int(slits, "position");
12030     length   = cpl_table_get_data_int(slits, "length");
12031 
12032     nx = cpl_image_get_size_x(spectra);
12033     ny = cpl_image_get_size_y(spectra);
12034 
12035     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12036 
12037     xlow = 1;
12038     xhig = nx;
12039     for (i = 0; i < nslits; i++) {
12040 
12041         if (length[i] == 0)
12042             continue;
12043 
12044         /*
12045          * Define the extraction boundaries. We DON'T write:
12046          *
12047          * ylow = position[i];
12048          * yhig = ylow + length[i];
12049          *
12050          * because the cpl_image pixels are counted from 1, and because in
12051          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12052          */
12053 
12054         ylow = position[i] + 1;
12055         yhig = ylow + length[i] - 1;
12056 
12057         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12058         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12059         cpl_image_delete(exslit);
12060 
12061         data   = cpl_image_get_data(skymap);
12062         data  += nx * position[i];
12063 
12064         for (j = 0; j < length[i]; j++) {
12065             sdata  = cpl_image_get_data(sky);
12066             for (k = 0; k < nx; k++) {
12067                 *data++ = *sdata++;
12068             }
12069         }
12070 
12071         cpl_image_delete(sky);
12072     }
12073 
12074 
12075     /*
12076      * Preliminary sky-subtracted image
12077      */
12078 
12079     subtracted = cpl_image_duplicate(spectra);
12080     cpl_image_subtract(subtracted, skymap);
12081     cpl_image_delete(skymap);
12082 
12083 
12084     /*
12085      * Detect objects positions in all slits
12086      */
12087 
12088     objects = cpl_table_duplicate(slits);
12089     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12090     cpl_image_delete(profile);
12091     cpl_image_delete(subtracted);
12092 
12093 
12094     /*
12095      * Flag the sky pixels. Note that maxobjects is intentionally 
12096      * the max number of objects increased by one.
12097      */
12098 
12099     maxobjects = 1;
12100     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12101     while (cpl_table_has_column(objects, name)) {
12102         maxobjects++;
12103         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12104     }
12105 
12106     is_sky = cpl_calloc(ny, sizeof(int));
12107 
12108     for (i = 0; i < nslits; i++) {
12109 
12110         if (length[i] == 0)
12111             continue;
12112 
12113         ylow = position[i] + margin;
12114         yhig = position[i] + length[i] - margin;
12115 
12116         for (j = ylow; j < yhig; j++)
12117             is_sky[j] = 1;
12118 
12119         for (j = 1; j < maxobjects; j++) {
12120             snprintf(name, MAX_COLNAME, "object_%d", j);
12121             if (cpl_table_is_valid(objects, name, i)) {
12122                 snprintf(name, MAX_COLNAME, "start_%d", j);
12123                 ylow = cpl_table_get_int(objects, name, i, NULL);
12124                 snprintf(name, MAX_COLNAME, "end_%d", j);
12125                 yhig = cpl_table_get_int(objects, name, i, NULL);
12126                 for (k = ylow; k <= yhig; k++)
12127                     is_sky[k] = 0;
12128             }
12129         }
12130 
12131 
12132         /*
12133          * Eliminate isolated sky points
12134          */
12135 
12136         ylow = position[i] + margin + 1;
12137         yhig = position[i] + length[i] - margin - 1;
12138 
12139         for (j = ylow; j < yhig; j++)
12140             if (is_sky[j])
12141                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12142                     is_sky[j] = 0;
12143 
12144     }
12145 
12146 
12147     /*
12148      * Determination of the sky map
12149      */
12150 
12151     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12152 
12153     for (i = 0; i < nslits; i++) {
12154 
12155         if (length[i] == 0)
12156             continue;
12157 
12158         ylow = position[i];
12159         yhig = ylow + length[i];
12160 
12161         nsky = 0;
12162         for (j = ylow; j < yhig; j++)
12163             if (is_sky[j])
12164                 nsky++;
12165 
12166         if (nsky > order + 1) {
12167             if (order) {
12168                 points = cpl_vector_new(nsky);
12169                 nsky = 0;
12170                 for (j = ylow; j < yhig; j++) {
12171                     if (is_sky[j]) {
12172                         cpl_vector_set(points, nsky, j);
12173                         nsky++;
12174                     }
12175                 }
12176 
12177                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12178                 xdata = cpl_image_get_data(exslit);
12179                 values = cpl_vector_new(nsky);
12180 
12181                 for (j = 0; j < nx; j++) {
12182                     nsky = 0;
12183                     for (k = ylow; k < yhig; k++) {
12184                         if (is_sky[k]) {
12185                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12186                             nsky++;
12187                         }
12188                     }
12189 
12190                     /*
12191                      * Eliminate obvious outliers
12192                      */
12193 
12194                     median = cpl_vector_get_median_const(values);
12195                     vdata = cpl_vector_get_data(values);
12196                     pdata = cpl_vector_get_data(points);
12197                     nbad = 0;
12198                     for (k = 0; k < nsky; k++) {
12199                         if (fabs(vdata[k] - median) < 100) {
12200                             if (nbad) {
12201                                 vdata[k-nbad] = vdata[k];
12202                                 pdata[k-nbad] = pdata[k];
12203                             }
12204                         }
12205                         else
12206                             nbad++;
12207                     }
12208 
12209                     if (nsky == nbad)
12210                         continue;
12211 
12212                     if (nbad && nsky - nbad > order + 1) {
12213                         keep_values = values;
12214                         keep_points = points;
12215                         values = cpl_vector_wrap(nsky-nbad, vdata);
12216                         points = cpl_vector_wrap(nsky-nbad, pdata);
12217                     }
12218 
12219                     if (nsky - nbad > order + 1) {
12220 
12221                         fit = cpl_polynomial_fit_1d_create(points, values, 
12222                                                            order, NULL);
12223 
12224                         if (fit) {
12225                             for (k = ylow; k < yhig; k++) {
12226                                 xdata[j+(k-ylow)*nx] = 
12227                                          cpl_polynomial_eval_1d(fit, k, NULL);
12228                             }
12229 
12230                             cpl_polynomial_delete(fit);
12231                         }
12232                         else
12233                             cpl_error_reset();
12234                     }
12235                     else {
12236                         for (k = 0; k < nsky; k++) {
12237                             xdata[j+k*nx] = median;
12238                         }
12239                     }
12240 
12241                     if (nbad && nsky - nbad > order + 1) {
12242                         cpl_vector_unwrap(values);
12243                         cpl_vector_unwrap(points);
12244                         values = keep_values;
12245                         points = keep_points;
12246                     }
12247 
12248                     if (nbad) {
12249                         nsky = 0;
12250                         for (k = ylow; k < yhig; k++) {
12251                             if (is_sky[k]) {
12252                                 cpl_vector_set(points, nsky, k);
12253                                 nsky++;
12254                             }
12255                         }
12256                     }
12257 
12258                 }
12259 
12260                 cpl_vector_delete(values);
12261                 cpl_vector_delete(points);
12262 
12263                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12264                 cpl_image_delete(exslit);
12265 
12266             }
12267             else {
12268                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12269                 xdata = cpl_image_get_data(exslit);
12270                 values = cpl_vector_new(nsky);
12271 
12272                 for (j = 0; j < nx; j++) {
12273                     nsky = 0;
12274                     for (k = ylow; k < yhig; k++) {
12275                         if (is_sky[k]) {
12276                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12277                             nsky++;
12278                         }
12279                     }
12280 
12281                     median = cpl_vector_get_median_const(values);
12282 
12283                     for (k = ylow; k < yhig; k++)
12284                         xdata[j+(k-ylow)*nx] = median;
12285 
12286                 }
12287 
12288                 cpl_vector_delete(values);
12289 
12290                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12291                 cpl_image_delete(exslit);
12292             }
12293         }
12294         else
12295             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
12296     }
12297 
12298     cpl_free(is_sky);
12299 
12300     return skymap;
12301 
12302 }
12303 
12304 
12326 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
12327                                  float threshold, float ratio)
12328 {
12329     const char *func = "mos_clean_cosmics";
12330 
12331     cpl_image  *smoothImage;
12332     cpl_table  *table;
12333     cpl_matrix *kernel;
12334     int        *xdata;
12335     int        *ydata;
12336     float      *idata;
12337     float      *sdata;
12338     float       sigma, sum, value, smoothValue;
12339     double      noise;
12340     int         count;
12341     float       fMax;
12342     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
12343     int         xLen;
12344     int         yLen;
12345     int         nPix;
12346     int         first = 1;  /* position of first cosmic ray candidate
12347                                encountered while scanning the image */
12348     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
12349     int         numCosmic = 0;
12350     int         found, foundContiguousCandidate;
12351     int        *cosmic;
12352   
12353 
12354     if (image == NULL)
12355         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12356 
12357 
12358     /*
12359      *  "cosmic" is a flags holder (initialized to zero):
12360      *
12361      *           -1 = candidate for cosmic ray
12362      *            0 = not a cosmic
12363      *            1 = a cosmic ray
12364      *            2 = member of current group of contiguous candidates
12365      *            3 = examined member of current group
12366      */
12367 
12368     xLen = cpl_image_get_size_x(image);
12369     yLen = cpl_image_get_size_y(image);
12370 
12371     if (xLen < 4 || yLen < 4)
12372         return CPL_ERROR_NONE;
12373 
12374     nPix = xLen * yLen;
12375 
12376     /*
12377      * Noise estimation from negative offsets in image. Note that this
12378      * assumes that the background level (skyLevel) has already been 
12379      * subtracted from the data. In this way we estimate the noise due 
12380      * to detector readout and to the background signal level (before 
12381      * it were removed). Theoretically this is given by 
12382      *
12383      *        noise = sqrt(ron^2 + skyLevel/gain)
12384      *
12385      * where ron is the read-out-noise. To this we will sum the noise 
12386      * contribution due to any increase of the signal above the background
12387      * by an amount scienceLevel. Theoretically the total noise is given by
12388      *
12389      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
12390      *
12391      * that is
12392      *
12393      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
12394      *
12395      */
12396 
12397     idata = cpl_image_get_data(image);
12398     noise = 0.0;
12399     count = 0;
12400 
12401     for (i = 0; i < nPix; i++) {
12402         if (idata[i] < -0.00001) {
12403             noise -= idata[i];
12404             count++;
12405         }
12406     }
12407 
12408     noise /= count;
12409     noise *= 1.25;       /* Factor to convert average deviation to sigma */
12410 
12411     cosmic = cpl_calloc(nPix, sizeof(int));
12412 
12413     if (threshold < 0.)
12414         threshold = 4.0;
12415     if (ratio < 0.)
12416         ratio = 2.0;
12417 
12418     kernel = cpl_matrix_new(3, 3);
12419     cpl_matrix_fill(kernel, 1.0);
12420     cpl_matrix_set(kernel, 1, 1, 0.0);
12421     smoothImage = cpl_image_filter_median(image, kernel);
12422     cpl_matrix_delete(kernel);
12423     
12424     /*
12425      *  Loop on images pixels, searching for cosmic rays candidates.
12426      *  Border pixels are currently excluded (they cannot contain
12427      *  candidates), to avoid that the search for groups of contiguous
12428      *  pixels would ever go out of image boundaries. In future we may
12429      *  overcome this limit, adding an appropriate check when contiguous
12430      *  pixels are searched.
12431      */
12432 
12433     sdata = cpl_image_get_data(smoothImage);
12434 
12435     for (j = 1; j < yLen - 1; j++) {
12436         for (i = 1; i < xLen - 1; i++) {
12437             value = idata[i + j * xLen];
12438             smoothValue = sdata[i + j * xLen];
12439             if (smoothValue < 1.0)
12440                 smoothValue = 1.0;
12441             sigma = sqrt(noise * noise + smoothValue / gain);
12442             if (value - smoothValue >= threshold * sigma) 
12443                 cosmic[i + j * xLen] = -1;
12444         }
12445     }
12446 
12447     cpl_image_delete(smoothImage);
12448 
12449 
12450     /*
12451      *  Search for groups of contiguous cosmic rays candidates.
12452      */
12453 
12454     do {
12455         found = 0;
12456         for (pos = first; pos < nPix; pos++) {
12457             if (cosmic[pos] == -1) {
12458                 cosmic[pos] = 2;         /*  Candidate found.  */
12459                 i = pos % xLen;          /*  Its coordinates.  */
12460                 j = pos / xLen;
12461                 first = pos;
12462                 first++;      /* ???  really necessary? */
12463                 found = 1;
12464                 break;
12465             }
12466         }
12467 
12468         if (found) {
12469 
12470             /*
12471              *  Determine new group of contiguous cosmic rays candidates.
12472              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12473              *  and the value of the max pixel and its position, fMax, iPosMax,
12474              *  jPosMax.
12475              */
12476 
12477             iMin = iMax = iPosMax = i;
12478             jMin = jMax = jPosMax = j;
12479             fMax = idata[i + j * xLen];
12480 
12481             do {
12482                 foundContiguousCandidate = 0;
12483                 for (l = 0; l <= 1; l++) {
12484                     for (k = 0; k <= 1; k++) {
12485 
12486                         /*
12487                          *  Looping on 4 pixels to North, East, South and West
12488                          */
12489 
12490                         ii = i + k - l;
12491                         jj = j + k + l - 1;
12492                         if (cosmic[ii + jj * xLen] == -1) {
12493                             foundContiguousCandidate = 1;
12494                             cosmic[ii + jj * xLen] = 2;
12495                                         /* Candidate belongs to current group */
12496                             iii = ii;   /* Keep its position */
12497                             jjj = jj;
12498 
12499                             /*
12500                              * Upgrade search box
12501                              */
12502 
12503                             if (ii < iMin)
12504                                 iMin = ii;
12505                             if (ii > iMax)
12506                                 iMax = ii;
12507                             if (jj < jMin)
12508                                 jMin = jj;
12509                             if (jj > jMax)
12510                                 jMax = jj;
12511 
12512                             if (idata[ii + jj * xLen] > fMax) {
12513                                 fMax = idata[ii + jj * xLen];
12514                                 iPosMax = ii;
12515                                 jPosMax = jj;
12516                             }
12517                         }
12518                     }
12519                 }
12520 
12521                 /*
12522                  *  We are done exploring the "cross". Now mark as "examined"
12523                  *  the current candidate (at the center of the cross):
12524                  */
12525 
12526                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12527 
12528                 if (foundContiguousCandidate) {
12529 
12530                     /*
12531                      * Pass (arbitrarily) the coordinates of the LAST found 
12532                      * candidate
12533                      */
12534 
12535                     i = iii;
12536                     j = jjj;
12537 
12538                     /* 
12539                      * Skip the rest, continue loop on new candidate 
12540                      */
12541 
12542                     continue; 
12543                 }
12544 
12545 
12546                 /*
12547                  *  Look for leftovers in the (growing!) search box
12548                  */
12549 
12550                 for (l = jMin; l <= jMax; l++) {
12551                     for (k = iMin; k <= iMax; k++) {
12552                         if (cosmic[k + l * xLen] == 2) {
12553                             i = k;
12554                             j = l;
12555                             foundContiguousCandidate = 1;
12556                             break;
12557                         }
12558                     }
12559                     if (foundContiguousCandidate) 
12560                         break;
12561                 }
12562             } while (foundContiguousCandidate);
12563 
12564 
12565             /*
12566              *  No more contiguous candidates are found. Decide now
12567              *  whether the current group is a cosmic ray or not.
12568              */
12569 
12570             sum = 0.;                /* Sum of 8 pixels around max position */
12571             for (l = -1; l <= 1; l++) {
12572                 for (k = -1; k <= 1; k++) {
12573                     if (l != 0 || k != 0) {
12574                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12575                     }
12576                 }
12577             }
12578 
12579             sum /= 8.;
12580             if (fMax > ratio * sum) {
12581                 for (l = jMin - 1; l <= jMax + 1; l++) {
12582                     for (k = iMin - 1; k <= iMax + 1; k++) {
12583                         if (cosmic[k + l * xLen] == 3) {
12584                             cosmic[k + l * xLen] = 1;
12585                             numCosmic++;
12586                         }
12587                     }
12588                 }
12589             }
12590             else {
12591                 for (l = jMin - 1; l <= jMax + 1; l++) {
12592                     for (k = iMin - 1; k <= iMax + 1; k++) {
12593                         if (cosmic[k + l * xLen] != -1) {
12594                             if (cosmic[k + l * xLen] == 1) 
12595                                 numCosmic--;
12596                             cosmic[k + l * xLen] = 0;
12597                         }
12598                     }
12599                 }
12600             }
12601         }
12602     } while (found);
12603 
12604 
12605     /*
12606      *  Prepare table containing cosmic rays coordinates. 
12607      */
12608 
12609     table = cpl_table_new(numCosmic);
12610     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12611     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12612     cpl_table_set_column_unit(table, "x", "pixel");
12613     cpl_table_set_column_unit(table, "y", "pixel");
12614     xdata = cpl_table_get_data_int(table, "x");
12615     ydata = cpl_table_get_data_int(table, "y");
12616 
12617     for (pos = 0, i = 0; pos < nPix; pos++) {
12618         if (cosmic[pos] == 1) {
12619             xdata[i] = (pos % xLen);
12620             ydata[i] = (pos / xLen);
12621             i++;
12622         }
12623     }
12624 
12625     mos_clean_bad_pixels(image, table, 1);
12626 
12627     cpl_free(cosmic);
12628     cpl_table_delete(table);
12629 
12630     return CPL_ERROR_NONE;
12631 
12632 }
12633 
12634 
12635 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12636                                     int spectral)
12637 {
12638     const char *func = "mos_clean_cosmics";
12639  
12640     float       *idata;
12641     int         *isBadPix;
12642     int          i, j, k, d;
12643     int          xlen, ylen, totPix;
12644     int          nBadPixels = 0;
12645     int          sign, foundFirst;
12646     int         *xValue = NULL;
12647     int         *yValue = NULL;
12648     float        save = 0.;
12649     double       sumd;
12650     int          cx, cy;
12651     int          nPairs;
12652     float        estimate[4];
12653     int          sx[] = {0, 1, 1, 1};
12654     int          sy[] = {1,-1, 0, 1};
12655     int          searchHorizon = 100;
12656     int          percent = 15;
12657 
12658 
12659     if (image == NULL || table == NULL)
12660         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12661 
12662     if (1 != cpl_table_has_column(table, "x"))
12663         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12664 
12665     if (1 != cpl_table_has_column(table, "y"))
12666         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12667 
12668     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
12669         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12670 
12671     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
12672         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12673 
12674     nBadPixels = cpl_table_get_nrow(table);
12675 
12676     if (nBadPixels) {
12677         xlen = cpl_image_get_size_x(image);
12678         ylen = cpl_image_get_size_y(image);
12679         idata = cpl_image_get_data(image);
12680         totPix = xlen * ylen;
12681         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
12682             isBadPix = cpl_calloc(totPix, sizeof(int));
12683         }
12684         else {
12685             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
12686                             "skip bad pixel correction", percent);
12687             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12688         }
12689     }
12690     else {
12691         cpl_msg_debug(func, "No pixel values to interpolate");
12692         return CPL_ERROR_NONE;
12693     }
12694 
12695     xValue = cpl_table_get_data_int(table, "x");
12696     yValue = cpl_table_get_data_int(table, "y");
12697 
12698     for (i = 0; i < nBadPixels; i++)
12699         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
12700 
12701     for (i = 0; i < nBadPixels; i++) {
12702 
12703         /*
12704          *  Search for the closest good pixel along the 4 fundamental 
12705          *  directions (in both senses):
12706          *                            \ | /
12707          *                             \|/
12708          *                           --- ---
12709          *                             /|\
12710          *                            / | \
12711          *
12712          *  Then collect pairs of values to interpolate linearly.
12713          */
12714 
12715         nPairs = 0;
12716         for (j = 0; j < 4; j++) {
12717 
12718             if (spectral) /* Just horizontal interpolation for spectral data */
12719                 if (j != 2)
12720                     continue;
12721 
12722             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
12723             sumd = 0.;
12724             foundFirst = 0;
12725             for (k = 0; k < 2; k++) {
12726                 sign = 2 * k - 1;
12727                 d = 0;
12728                 cx = xValue[i];
12729                 cy = yValue[i];
12730                 do {
12731                     cx += sign * sx[j];
12732                     cy += sign * sy[j];
12733                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
12734                         break;
12735                     d++;
12736                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
12737 
12738                 if (cx >= 0 && cx < xlen && 
12739                     cy >= 0 && cy < ylen && d < searchHorizon) {
12740 
12741                     /*
12742                      *  In this block is cripted the linear interpolation...
12743                      */
12744 
12745                     save = idata[cx + cy * xlen];
12746                     estimate[nPairs] += save / d;
12747                     sumd += 1. / (double) d;
12748                     if (k) {
12749                         estimate[nPairs] /= sumd;
12750                         nPairs++;
12751                     }
12752                     else {
12753                         foundFirst = 1;
12754                     }
12755                 }
12756                 else {
12757 
12758                     /*
12759                      * Image borders were crossed, incomplete pair of values
12760                      */
12761 
12762                     if (k) {
12763                         if (foundFirst) {
12764                             estimate[nPairs] = save;
12765                             nPairs++;
12766                         }
12767                     }
12768                 }
12769             }
12770         }
12771 
12772         /*
12773          * Replace pixel value of the input image, corresponding to
12774          * the current bad pixel, with the median of the estimates
12775          * resulted from the 4 linear interpolations.
12776          */
12777 
12778         if (nPairs > 2) {
12779             idata[xValue[i] + yValue[i] * xlen] = 
12780                                cpl_tools_get_median_float(estimate, nPairs);
12781         }
12782         else if (nPairs == 2) {
12783             idata[xValue[i] + yValue[i] * xlen] =
12784                                             (estimate[0] + estimate[1]) / 2.;
12785         }
12786         else if (nPairs == 1) {
12787             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
12788         }
12789         else {
12790             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
12791                           xValue[i], yValue[i]);
12792         }
12793     }
12794 
12795     cpl_free(isBadPix);
12796 
12797     return CPL_ERROR_NONE;
12798 }
12799 
12800 
12830 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
12831                            cpl_table *polytraces, double reference,
12832                            double blue, double red, double dispersion)
12833 {
12834     const char *func = "mos_spatial_map";
12835 
12836     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
12837                                                  /* Max order is 5 */
12838     cpl_polynomial *polytop;
12839     cpl_polynomial *polybot;
12840     cpl_image      *calibration;
12841     float          *data;
12842     double          top, bot;
12843     double          coeff;
12844     double          ytop, ybot;
12845     double          ypos, yfra;
12846     double          factor;
12847     int             yint, yprev;
12848     int             nslits;
12849     int             npseudo;
12850     int            *slit_id;
12851     int            *length;
12852     int             nx, ny;
12853     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
12854     int             missing_top, missing_bot;
12855     int             null;
12856     int             order;
12857     int             i, j, k;
12858 
12859 
12860     if (spectra == NULL || slits == NULL || polytraces == NULL) {
12861         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12862         return NULL;
12863     }
12864 
12865     if (dispersion <= 0.0) {
12866         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12867         return NULL;
12868     }
12869 
12870     if (red - blue < dispersion) {
12871         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12872         return NULL;
12873     }
12874 
12875     nx = cpl_image_get_size_x(spectra);
12876     ny = cpl_image_get_size_y(spectra);
12877 
12878     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12879     data = cpl_image_get_data(calibration);
12880 
12881     length  = cpl_table_get_data_int(slits, "length");
12882     nslits  = cpl_table_get_nrow(slits);
12883     slit_id = cpl_table_get_data_int(slits, "slit_id");
12884     order   = cpl_table_get_ncol(polytraces) - 2;
12885 
12886     /*
12887      * The spatial resampling is performed for a certain number of 
12888      * pixels above and below the position of the reference wavelength:
12889      */
12890 
12891     pixel_above = (red - reference) / dispersion;
12892     pixel_below = (reference - blue) / dispersion;
12893 
12894     for (i = 0; i < nslits; i++) {
12895         
12896         if (length[i] == 0)
12897             continue;
12898 
12899         /*
12900          * Note that the x coordinate of the reference pixels on the CCD
12901          * is taken arbitrarily at the top end of each slit. This wouldn't
12902          * be entirely correct in case of curved slits, or in presence of
12903          * heavy distortions: in such cases the spatial resampling is
12904          * really performed across a wide range of wavelengths. But
12905          * the lag between top and bottom spectral curvature models 
12906          * would introduce even in such cases negligible effects on
12907          * the spectral spatial resampling.
12908          */
12909 
12910         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
12911 
12912         start_pixel = refpixel - pixel_below;
12913         if (start_pixel < 0)
12914             start_pixel = 0;
12915 
12916         end_pixel = refpixel + pixel_above;
12917         if (end_pixel > nx)
12918             end_pixel = nx;
12919 
12920         /*
12921          * Recover from the table of spectral curvature coefficients
12922          * the curvature polynomials.
12923          */
12924 
12925         missing_top = 0;
12926         polytop = cpl_polynomial_new(1);
12927         for (k = 0; k <= order; k++) {
12928             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
12929             if (null) {
12930                 cpl_polynomial_delete(polytop);
12931                 missing_top = 1;
12932                 break;
12933             }
12934             cpl_polynomial_set_coeff(polytop, &k, coeff);
12935         }
12936 
12937         missing_bot = 0;
12938         polybot = cpl_polynomial_new(1);
12939         for (k = 0; k <= order; k++) {
12940             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
12941             if (null) {
12942                 cpl_polynomial_delete(polybot);
12943                 missing_bot = 1;
12944                 break;
12945             }
12946             cpl_polynomial_set_coeff(polybot, &k, coeff);
12947         }
12948 
12949         if (missing_top && missing_bot) {
12950             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
12951                             slit_id[i]);
12952             continue;
12953         }
12954 
12955         /*
12956          * In case just one of the two edges was not traced, the other
12957          * edge curvature model is duplicated and shifted to the other
12958          * end of the slit: better than nothing!
12959          */
12960 
12961         if (missing_top) {
12962             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
12963                             "the spectral curvature of the lower edge "
12964                             "is used instead.", slit_id[i]);
12965             polytop = cpl_polynomial_duplicate(polybot);
12966             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12967             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12968             k = 0;
12969             coeff = cpl_polynomial_get_coeff(polybot, &k);
12970             coeff += ytop - ybot;
12971             cpl_polynomial_set_coeff(polytop, &k, coeff);
12972         }
12973 
12974         if (missing_bot) {
12975             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
12976                             "the spectral curvature of the upper edge "
12977                             "is used instead.", slit_id[i]);
12978             polybot = cpl_polynomial_duplicate(polytop);
12979             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12980             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12981             k = 0;
12982             coeff = cpl_polynomial_get_coeff(polytop, &k);
12983             coeff -= ytop - ybot;
12984             cpl_polynomial_set_coeff(polybot, &k, coeff);
12985         }
12986 
12987         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
12988         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
12989         npseudo = ceil(top-bot) + 1;
12990 
12991         if (npseudo < 1) {
12992             cpl_polynomial_delete(polytop);
12993             cpl_polynomial_delete(polybot);
12994             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
12995                             slit_id[i]);
12996             continue;
12997         }
12998 
12999         for (j = start_pixel; j < end_pixel; j++) {
13000             top = cpl_polynomial_eval_1d(polytop, j, NULL);
13001             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13002             factor = (top-bot)/npseudo;
13003             for (k = 0; k <= npseudo; k++) {
13004                 ypos = top - k*factor;
13005                 yint = ypos;
13006                 yfra = ypos - yint;
13007                 if (yint >= 0 && yint < ny-1) {
13008                     data[j + nx*yint] = (top-yint)/factor;
13009                     if (k) {
13010 
13011                         /*
13012                          * This is added to recover lost pixels on
13013                          * the CCD image (pixels are lost because
13014                          * the CCD pixels are less than npseudo+1).
13015                          */
13016 
13017                         if (yprev - yint > 1) {
13018                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
13019                         }
13020                     }
13021                 }
13022                 yprev = yint;
13023             }
13024         }
13025         cpl_polynomial_delete(polytop);
13026         cpl_polynomial_delete(polybot);
13027     }
13028 
13029     return calibration;
13030 }
13031 
13032 
13095 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13096                               int maxradius, int conradius)
13097 {
13098     const char *func = "mos_detect_objects";
13099 
13100     cpl_image  *profile;
13101     float      *pdata;
13102     float      *p;
13103 
13104     char        name[MAX_COLNAME];
13105 
13106     int         nslits;
13107     int         npeaks;
13108     int         nobjects, objpos, totobj;
13109     int         maxobjects;
13110     int        *position;
13111     int        *length;
13112     int        *reject;
13113     double     *place;
13114     double     *bright;
13115     double      mindistance;
13116     int         pos, count;
13117     int         up;
13118     int         low, hig;
13119     int         row;
13120     int         i, j, k;
13121 
13122     const int   min_pixels = 10;
13123 
13124     
13125     if (cpl_error_get_code() != CPL_ERROR_NONE)
13126         return NULL;
13127  
13128     if (image == NULL || slits == NULL) { 
13129         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13130         return NULL;
13131     }
13132 
13133     if (margin < 0)
13134         margin = 0;
13135 
13136     if (maxradius < 0) {
13137         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13138         return NULL;
13139     }
13140 
13141     if (conradius < 0) {
13142         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13143         return NULL;
13144     }
13145 
13146     nslits   = cpl_table_get_nrow(slits);
13147     position = cpl_table_get_data_int(slits, "position");
13148     length   = cpl_table_get_data_int(slits, "length");
13149 
13150     profile = cpl_image_collapse_create(image, 1);
13151     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
13152     pdata = cpl_image_get_data(profile);
13153 
13154     row = 1;
13155     maxobjects = 0;
13156     totobj = 0;
13157     for (i = 0; i < nslits; i++) {
13158 
13159         if (length[i] == 0)
13160             continue;
13161 
13162         pos = position[i] + margin;
13163         count = length[i] - 2*margin;
13164 
13165         if (count < min_pixels)
13166             continue;
13167 
13168         p = pdata + pos;
13169 
13170 
13171         /*
13172          * Count peaks candidates
13173          */
13174 
13175         npeaks = 0;
13176         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13177             npeaks++;
13178         }
13179 
13180         up = 0;
13181         for (j = 0; j < count - 3; j++) {
13182             if (p[j] > 0) {
13183                 if (p[j+1] > p[j]) {
13184                     up++;
13185                 }
13186                 else {
13187                     if (up > 2) {
13188                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13189                             if (p[j] > 5)
13190                                 npeaks++;
13191                         }
13192                     }
13193                     else if (up > 1) {
13194                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13195                             if (p[j] > 5)
13196                                 npeaks++;
13197                         }
13198                     }
13199                     up = 0;
13200                 }
13201             }
13202             else {
13203                 up = 0;
13204             }
13205         }
13206 
13207         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
13208             && p[count-3] > p[count-4] && p[count-4] > 0) {
13209             npeaks++;
13210         }
13211 
13212         if (npeaks == 0)
13213             continue;
13214 
13215 
13216         /*
13217          * Get candidates parameters
13218          */
13219 
13220         reject = cpl_calloc(npeaks, sizeof(int));
13221         bright = cpl_calloc(npeaks, sizeof(double));
13222         place  = cpl_calloc(npeaks, sizeof(double));
13223 
13224         npeaks = 0;
13225         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13226             bright[0] = p[0];
13227             place[0] = position[i] + margin;
13228             npeaks++;
13229         }
13230 
13231         up = 0;
13232         for (j = 0; j < count - 3; j++) {
13233             if (p[j] > 0) {
13234                 if (p[j+1] > p[j]) {
13235                     up++;
13236                 }
13237                 else {
13238                     if (up > 2) {
13239                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13240                             if (p[j] > 5) {
13241                                 bright[npeaks] = p[j];
13242                                 place[npeaks] = position[i] + margin + j + 1
13243                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13244                                 npeaks++;
13245                             }
13246                         }
13247                     }
13248                     else if (up > 1) {
13249                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13250                             if (p[j] > 5) {
13251                                 bright[npeaks] = p[j];
13252                                 place[npeaks] = position[i] + margin + j + 1
13253                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13254                                 npeaks++;
13255                             }
13256                         }
13257                     }
13258                     up = 0;
13259                 }
13260             }
13261             else {
13262                 up = 0;
13263             }
13264         }
13265 
13266         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
13267             && p[count-3] > p[count-4] && p[count-4] > 0) {
13268             bright[npeaks] = p[count-1];
13269             place[npeaks] = position[i] + count;
13270             npeaks++;
13271         }
13272 
13273 
13274         /*
13275          * Now select the uncontaminated peaks
13276          */
13277 
13278         if (fabs(place[0] - pos) < 1.0)
13279             reject[0] = 1;
13280         if (fabs(place[npeaks-1] - pos - count) < 1.0)
13281             reject[npeaks-1] = 1;
13282         for (j = 0; j < npeaks; j++) {
13283             for (k = 0; k < npeaks; k++) {
13284                 if (k == j)
13285                     continue;
13286                 mindistance = conradius * bright[k] / bright[j] 
13287                                         * bright[k] / bright[j];
13288                 if (fabs(place[j] - place[k]) < mindistance)
13289                     reject[j] = 1;
13290             }
13291         }
13292 
13293 /* new part */
13294         for (j = 0; j < npeaks; j++) {
13295             if (reject[j])
13296                 continue;
13297             if (j) {
13298                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13299                     / (bright[j-1] + bright[j]) + 1;
13300             }
13301             else {
13302                 low = pos;
13303             }
13304             if (j < npeaks - 1) {
13305                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13306                     / (bright[j+1] + bright[j]) + 1;
13307             }
13308             else {
13309                 hig = pos + count;
13310             }
13311 
13312             if (low < pos)
13313                 low = pos;
13314             if (hig > pos + count)
13315                 hig = pos + count;
13316             if (place[j] - low > maxradius)
13317                 low = place[j] - maxradius;
13318             if (hig - place[j] > maxradius)
13319                 hig = place[j] + maxradius;
13320             if (hig == low)
13321                 reject[j] = 1;
13322         }
13323 /* end new part */
13324 
13325         nobjects = npeaks;
13326         for (j = 0; j < npeaks; j++)
13327             if (reject[j])
13328                 nobjects--;
13329 
13330         for (j = 0; j < nobjects; j++) {
13331             snprintf(name, MAX_COLNAME, "object_%d", j+1);
13332             if (cpl_table_has_column(slits, name))
13333                 continue;
13334             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
13335             snprintf(name, MAX_COLNAME, "start_%d", j+1);
13336             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13337             cpl_table_set_column_unit(slits, name, "pixel");
13338             snprintf(name, MAX_COLNAME, "end_%d", j+1);
13339             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13340             cpl_table_set_column_unit(slits, name, "pixel");
13341             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13342             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13343             cpl_table_set_column_unit(slits, name, "pixel");
13344         }
13345 
13346         objpos = nobjects;
13347         for (j = 0; j < npeaks; j++) {
13348             if (reject[j])
13349                 continue;
13350             if (j) {
13351                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13352                     / (bright[j-1] + bright[j]) + 1;
13353             }
13354             else {
13355                low = pos;
13356             }
13357             if (j < npeaks - 1) {
13358                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13359                     / (bright[j+1] + bright[j]) + 1;
13360             }
13361             else {
13362                 hig = pos + count;
13363             }
13364 
13365             if (low < pos)
13366                 low = pos;
13367             if (hig > pos + count)
13368                 hig = pos + count;
13369             if (place[j] - low > maxradius)
13370                 low = place[j] - maxradius;
13371             if (hig - place[j] > maxradius)
13372                 hig = place[j] + maxradius;
13373 
13374             snprintf(name, MAX_COLNAME, "object_%d", objpos);
13375             cpl_table_set_double(slits, name, i, place[j]);
13376             snprintf(name, MAX_COLNAME, "start_%d", objpos);
13377             cpl_table_set_int(slits, name, i, low);
13378             snprintf(name, MAX_COLNAME, "end_%d", objpos);
13379             cpl_table_set_int(slits, name, i, hig);
13380             snprintf(name, MAX_COLNAME, "row_%d", objpos);
13381             cpl_table_set_int(slits, name, i, row + objpos - 1);
13382             totobj++;
13383             objpos--;
13384         }
13385 
13386         row += nobjects;
13387 
13388         if (maxobjects < nobjects)
13389             maxobjects = nobjects;
13390 
13391         cpl_free(reject);
13392         cpl_free(bright);
13393         cpl_free(place);
13394 
13395     }
13396 
13397 /*    nobjects = row - nobjects;     A bug, I think... */
13398     row = cpl_table_get_nrow(slits);
13399 
13400     for (i = 0; i < row; i++) {
13401         for (j = 0; j < maxobjects; j++) {
13402             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13403             if (cpl_table_is_valid(slits, name, i))
13404                 cpl_table_set_int(slits, name, i, totobj -
13405                                   cpl_table_get_int(slits, name, i, NULL));
13406         }
13407     }
13408 
13409     for (i = 0; i < maxobjects; i++) {
13410         snprintf(name, MAX_COLNAME, "start_%d", i+1);
13411         cpl_table_fill_invalid_int(slits, name, -1);
13412         snprintf(name, MAX_COLNAME, "end_%d", i+1);
13413         cpl_table_fill_invalid_int(slits, name, -1);
13414         snprintf(name, MAX_COLNAME, "row_%d", i+1);
13415         cpl_table_fill_invalid_int(slits, name, -1);
13416     }
13417 
13418     return profile;
13419 }
13420 
13421 
13446 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13447                                 cpl_table *objects, int extraction, double ron,
13448                                 double gain, int ncombined)
13449 {
13450     const char *func = "mos_extract_objects";
13451 
13452     char        name[MAX_COLNAME];
13453 
13454     cpl_image **output;
13455     cpl_image  *extracted;
13456     cpl_image  *extr_sky;
13457     cpl_image  *error;
13458     cpl_image  *sciwin;
13459     cpl_image  *skywin;
13460     int         nslits;
13461     int         nobjects;
13462     int         maxobjects;
13463     int         nx, ny;
13464     int         ylow, yhig;
13465     int         i, j;
13466 
13467 
13468     if (science == NULL || sky == NULL) {
13469         cpl_msg_error(func, "Both scientific exposures are required in input");
13470         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13471         return NULL;
13472     }
13473 
13474     if (objects == NULL) {
13475         cpl_msg_error(func, "An object table is required in input");
13476         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13477         return NULL;
13478     }
13479 
13480     if (extraction < 0 || extraction > 1) {
13481         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13482                       "either 0 or 1", extraction); 
13483         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13484         return NULL;
13485     }
13486 
13487     if (ron < 0.0) {
13488         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13489         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13490         return NULL;
13491     }
13492 
13493     if (gain < 0.1) {
13494         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13495         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13496         return NULL;
13497     }
13498 
13499     if (ncombined < 1) {
13500         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13501                       "it should be at least 1", ncombined);
13502         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13503         return NULL;
13504     }
13505 
13506 
13507     /*
13508      * Count the max number of objects per slit. Note that maxobjects 
13509      * is intentionally the max number of objects increased by one.
13510      */
13511 
13512     maxobjects = 1;
13513     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13514     while (cpl_table_has_column(objects, name)) {
13515         maxobjects++;
13516         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13517     }
13518 
13519 
13520     /*
13521      * Count objects to extract
13522      */
13523 
13524     nobjects = 0;
13525     nslits = cpl_table_get_nrow(objects);
13526 
13527     for (i = 0; i < nslits; i++) {
13528         for (j = 1; j < maxobjects; j++) {
13529             snprintf(name, MAX_COLNAME, "object_%d", j);
13530             if (cpl_table_is_valid(objects, name, i))
13531                 nobjects++;
13532         }
13533     }
13534 
13535     if (nobjects == 0)
13536         return NULL;
13537 
13538     nx = cpl_image_get_size_x(science);
13539     ny = cpl_image_get_size_x(science);
13540 
13541     output = cpl_calloc(3, sizeof(cpl_image *));
13542     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13543     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13544     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13545 
13546 
13547     /*
13548      * Extract objects
13549      */
13550 
13551     nobjects = 0;
13552     for (i = 0; i < nslits; i++) {
13553         for (j = 1; j < maxobjects; j++) {
13554             snprintf(name, MAX_COLNAME, "object_%d", j);
13555             if (cpl_table_is_valid(objects, name, i)) {
13556                 snprintf(name, MAX_COLNAME, "start_%d", j);
13557                 ylow = cpl_table_get_int(objects, name, i, NULL);
13558                 snprintf(name, MAX_COLNAME, "end_%d", j);
13559                 yhig = cpl_table_get_int(objects, name, i, NULL);
13560                 snprintf(name, MAX_COLNAME, "row_%d", j);
13561                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13562                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13563                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13564 /*
13565  * Cleaning the cosmics locally was really NOT a good idea...
13566  * I leave it here, commented out, to never forget this mistake!
13567 
13568                 if (extraction) {
13569                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13570                 }
13571  */
13572                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13573                                nobjects, extraction, ron, gain, ncombined);
13574                 cpl_image_delete(sciwin);
13575                 cpl_image_delete(skywin);
13576                 nobjects++;
13577             }
13578         }
13579     }
13580 
13581     return output;
13582 
13583 }
13584 
13585 
13608 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13609                             double dispersion, int saturation, 
13610                             double *mfwhm, double *rmsfwhm,
13611                             double *resolution, double *rmsres, int *nlines)
13612 {
13613     cpl_vector *vector;
13614 
13615     int     i, j, n, m;
13616     int     position, maxpos;
13617     int     xlen, ylen;
13618     int     sp, ep;
13619     int     radius;
13620     int     sradius = 40;
13621     int     threshold = 250;    /* Peak must be so many ADUs above min */
13622 
13623     int     ifwhm;
13624     double  fwhm;
13625     double *buffer;
13626     double  min, max, halfmax;
13627     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
13628     double  value, rms;
13629 
13630     float  *data;
13631 
13632 
13633     *resolution = 0.0;
13634     *rmsres = 0.0;
13635     *nlines = 0;
13636 
13637     xlen = cpl_image_get_size_x(image);
13638     ylen = cpl_image_get_size_y(image);
13639     data = cpl_image_get_data(image);
13640 
13641     buffer = cpl_malloc(ylen * sizeof(double));
13642 
13643     /*
13644      *  Closest pixel to specified wavelength.
13645      */
13646 
13647     position = floor((lambda - startwave) / dispersion + 0.5);
13648 
13649     sp = position - sradius;
13650     ep = position + sradius;
13651 
13652     if (sp < 0 || ep > xlen) {
13653         cpl_free(buffer);
13654         return 0;
13655     }
13656 
13657     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
13658 
13659         /*
13660          *  Search interval for peak. Abort if too close to image border.
13661          */
13662 
13663         radius = mos_lines_width(data + i*xlen + position - sradius, 
13664                                  2*sradius + 1);
13665         if (radius < 5)
13666             radius = 5;
13667 
13668         sp = position - radius;
13669         ep = position + radius;
13670 
13671         if (sp < 0 || ep > xlen) {
13672             cpl_free(buffer);
13673             return 0;
13674         }
13675 
13676 
13677         /*
13678          *  Determine min-max value and position.
13679          */
13680 
13681         maxpos = sp;
13682         min = max = data[sp + i * xlen];
13683         for (j = sp; j < ep; j++) {
13684             if (data[j + i * xlen] > max) {
13685                 max = data[j + i * xlen];
13686                 maxpos = j;
13687             }
13688             if (data[j + i * xlen] < min) {
13689                 min = data[j + i * xlen];
13690             }
13691         }
13692 
13693         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
13694             continue;
13695 
13696         if (max - min < threshold)        /* Low signal... */
13697             continue;
13698 
13699         if (max > saturation)             /* Saturation */
13700             continue;
13701 
13702         /*
13703          *  Determine FWHM counting pixels with value greater than
13704          *  half of the max value, to the right and to the left of
13705          *  the max. Linear interpolation between the pixels where
13706          *  the transition happens.
13707          */
13708 
13709         halfmax = (max + min)/ 2.0;
13710 
13711         fwhm = 0.0;
13712         ifwhm = 0;
13713         for (j = maxpos; j < maxpos + radius; j++) {
13714             if (j < xlen) {
13715                 if (data[j + i * xlen] < halfmax) {
13716                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
13717                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
13718                     break;
13719                 }
13720                 ifwhm++;
13721             }
13722         }
13723 
13724         ifwhm = 0;
13725         for (j = maxpos; j > maxpos - radius; j--) {
13726             if (j >= 0) {
13727                 if (data[j + i * xlen] < halfmax) {
13728                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
13729                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
13730                     break;
13731                 }
13732                 ifwhm++;
13733             }
13734         }
13735 
13736         if (fwhm > 3.0) {
13737             buffer[n] = fwhm - 2.0;
13738             n++;
13739         }
13740 
13741     }
13742 
13743     if (n == 0) {
13744         cpl_free(buffer);
13745         return 0;
13746     }
13747 
13748     vector = cpl_vector_wrap(n, buffer);
13749     value = cpl_vector_get_median_const(vector);
13750     cpl_vector_unwrap(vector);
13751 
13752     rms = 0.0;
13753     for (i = 0, m = 0; i < n; i++) {
13754         if (fabs(buffer[i] - value) < cut) {
13755             rms += fabs(buffer[i] - value);
13756             m++;
13757         }
13758     }
13759 
13760     cpl_free(buffer);
13761 
13762     if (m < 3)
13763         return 0;
13764 
13765     rms /= m;
13766     rms *= 1.25;       /* Factor to convert average deviation to sigma */
13767 
13768     value *= dispersion;
13769     rms *= dispersion;
13770 
13771     *mfwhm = value;
13772     *rmsfwhm = rms;
13773 
13774     *resolution = lambda / value;
13775     *rmsres = *resolution * rms / value;
13776 
13777     *nlines = m;
13778 
13779     return 1;
13780 }
13781 
13782 
13804 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
13805                                 double dispersion, int saturation, 
13806                                 cpl_vector *lines)
13807 {
13808 
13809     cpl_table *table;
13810     double    *line;
13811     double     fwhm;
13812     double     rmsfwhm;
13813     double     resolution;
13814     double     rmsres;
13815     int        nref;
13816     int        nlines;
13817     int        i;
13818 
13819 
13820     nref = cpl_vector_get_size(lines);
13821     line = cpl_vector_get_data(lines);
13822 
13823     table = cpl_table_new(nref);
13824     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
13825     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
13826     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
13827     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
13828     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
13829     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
13830     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
13831     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
13832     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
13833 
13834     for (i = 0; i < nref; i++) {
13835         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
13836                                     saturation, &fwhm, &rmsfwhm, 
13837                                     &resolution, &rmsres, &nlines)) {
13838             cpl_table_set_double(table, "wavelength", i, line[i]);
13839             cpl_table_set_double(table, "fwhm", i, fwhm);
13840             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
13841             cpl_table_set_double(table, "resolution", i, resolution);
13842             cpl_table_set_double(table, "resolution_rms", i, rmsres);
13843             cpl_table_set_int(table, "nlines", i, nlines);
13844         }
13845         else
13846             cpl_table_set_int(table, "nlines", i, 0);
13847     }
13848 
13849     if (cpl_table_has_valid(table, "wavelength"))
13850         return table;
13851 
13852     cpl_table_delete(table);
13853 
13854     return NULL;
13855     
13856 }
13857 
13858 
13876 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
13877                             int ystart, int yend, double wstart, double wend)
13878 {
13879     const char *func = "mos_integrate_signal";
13880 
13881     double sum;
13882     float *sdata;
13883     float *wdata;
13884     int    nx, ny;
13885     int    x, y;
13886     
13887 
13888     if (image == NULL || wavemap == NULL) { 
13889         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13890         return 0.0;
13891     }
13892 
13893     if (ystart > yend || wstart >= wend) {
13894         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13895         return 0.0;
13896     }
13897 
13898     nx = cpl_image_get_size_x(image);
13899     ny = cpl_image_get_size_y(image);
13900 
13901     if (!(nx == cpl_image_get_size_x(wavemap) 
13902         && ny == cpl_image_get_size_y(wavemap))) {
13903         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
13904         return 0.0;
13905     }
13906 
13907     if (ystart < 0 || yend > ny) {
13908         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
13909         return 0.0;
13910     }
13911 
13912     sdata = cpl_image_get_data(image);
13913     wdata = cpl_image_get_data(wavemap);
13914 
13915     sdata += ystart*nx;
13916     wdata += ystart*nx;
13917 
13918     sum = 0.0;
13919     for (y = ystart; y < yend; y++) {
13920         for (x = 0; x < nx; x++) {
13921             if (wdata[x] < wstart || wdata[x] > wend)
13922                 continue;
13923             sum += sdata[x];
13924         }
13925         sdata += nx;
13926         wdata += nx;
13927     }
13928 
13929     return sum;
13930 
13931 }
13932 
13933 /****************************************************************************
13934  * From this point on, the instrument dependent functions are added:
13935  * they are functions that retrieve information that is stored in
13936  * the data headers in some instrument specific way, such as the
13937  * location of overscans, the slits positions on the telescope
13938  * focal plane, the gain factor, etc.
13939  */
13940 
13941 
13964 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
13965 {
13966     const char *func = "mos_load_slits_fors_mxu";
13967 
13968     cpl_table  *slits;
13969     char        keyname[MAX_COLNAME];
13970     const char *instrume;
13971     const char *target_name;
13972     float       slit_x;
13973     float       slit_y;
13974     float       length;
13975 /*    double      arc2mm = 0.53316;         */
13976     double      arc2mm = 0.528;
13977     int         nslits;
13978     int         slit_id;
13979     int         fors;
13980     int         chip;
13981     int         found;
13982 
13983     /*
13984      * The limits below are used to exclude from the loaded slit list
13985      * any slit that surely doesn't belong to the used chip. This is
13986      * a way to reduce the chance of ambiguous slit identification.
13987      */
13988 
13989     float      low_limit1 = 10.0;
13990     float      hig_limit2 = 30.0;
13991 
13992 
13993     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13994         return NULL;
13995     }
13996 
13997     if (header == NULL) {
13998         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13999         return NULL;
14000     }
14001 
14002 
14003     /*
14004      * See if this is FORS1 or FORS2;
14005      */
14006 
14007     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14008 
14009     fors = 0;
14010     if (instrume[4] == '1')
14011         fors = 1;
14012     if (instrume[4] == '2')
14013         fors = 2;
14014 
14015     if (fors != 2) {
14016         cpl_msg_error(func, "Wrong instrument: %s\n"
14017                       "FORS2 is expected for MXU data", instrume);
14018         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14019         return NULL;
14020     }
14021 
14022 
14023     /*
14024      * The master and slave chips can be identified by their positions
14025      * in the chip array in the case of FORS2 data (with fors1 the chip
14026      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14027      */
14028 
14029     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14030 
14031     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14032         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14033                       "in FITS header");
14034         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14035         return NULL;
14036     }
14037 
14038     if (chip != 1 && chip != 2) {
14039         cpl_msg_error(func, "Unexpected chip position in keyword "
14040                       "ESO DET CHIP1 Y: %d", chip);
14041         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14042         return NULL;
14043     }
14044 
14045 
14046     /*
14047      * Count slits in header (excluding reference slits, and the slits
14048      * that _surely_ belong to the other chip)
14049      */
14050 
14051     nslits = 0;
14052     slit_id = 0;
14053     found = 1;
14054 
14055     while (found) {
14056         slit_id++;
14057         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14058         if (cpl_propertylist_has(header, keyname)) {
14059             slit_y = cpl_propertylist_get_double(header, keyname);
14060 
14061             if (chip == 1)
14062                 if (slit_y < low_limit1)
14063                     continue;
14064             if (chip == 2)
14065                 if (slit_y > hig_limit2)
14066                     continue;
14067                 
14068             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14069                      slit_id + 100);
14070             if (cpl_propertylist_has(header, keyname)) {
14071                 target_name = cpl_propertylist_get_string(header, keyname);
14072                 if (strncmp(target_name, "refslit", 7))
14073                     nslits++;
14074             }
14075             else
14076                 nslits++;
14077         }
14078         else
14079             found = 0;
14080     }
14081 
14082     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14083         cpl_msg_error(func, "%s while loading slits coordinates from "
14084                       "FITS header", cpl_error_get_message());
14085         cpl_error_set_where(func);
14086         return NULL;
14087     }
14088 
14089     if (nslits == 0)  {
14090         cpl_msg_error(func, "No slits coordinates found in header");
14091         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14092         return NULL;
14093     }
14094 
14095     slits = cpl_table_new(nslits);
14096     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14097     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14098     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14099     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14100     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14101     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14102     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14103     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14104     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14105 
14106     nslits = 0;
14107     slit_id = 0; 
14108     found = 1;
14109     while (found) {
14110         slit_id++;
14111         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14112         if (cpl_propertylist_has(header, keyname)) {
14113             slit_y = cpl_propertylist_get_double(header, keyname);
14114 
14115             if (chip == 1) 
14116                 if (slit_y < low_limit1)
14117                     continue;
14118             if (chip == 2)
14119                 if (slit_y > hig_limit2)
14120                     continue;
14121 
14122             /*
14123              * Y-flip the slit position, to match CCD pixel coordinate
14124              * convention
14125              */
14126 
14127             slit_y = -slit_y;
14128 
14129             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
14130             slit_x = cpl_propertylist_get_double(header, keyname);
14131             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14132                 cpl_table_delete(slits);
14133                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14134                               keyname);
14135                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14136                 return NULL;
14137             }
14138 
14139             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
14140             length = cpl_propertylist_get_double(header, keyname);
14141             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14142                 cpl_table_delete(slits);
14143                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14144                               keyname);
14145                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14146                 return NULL;
14147             }
14148 
14149             length *= arc2mm;
14150 
14151             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14152                      slit_id + 100);
14153             if (cpl_propertylist_has(header, keyname)) {
14154                 target_name = cpl_propertylist_get_string(header, keyname);
14155                 if (strncmp(target_name, "refslit", 7)) {
14156                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14157                     cpl_table_set(slits, "xtop", nslits, slit_x);
14158                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14159                     cpl_table_set(slits, "xbottom", nslits, slit_x);
14160                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14161                     nslits++;
14162                 }
14163             }
14164             else {
14165                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14166                 cpl_table_set(slits, "xtop", nslits, slit_x);
14167                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14168                 cpl_table_set(slits, "xbottom", nslits, slit_x);
14169                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14170                 nslits++;
14171             }
14172         }
14173         else
14174             found = 0;
14175     }
14176 
14177     return slits;
14178 }
14179 
14180 
14203 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header)
14204 {
14205     const char *func = "mos_load_slits_fors_mos";
14206 
14207     cpl_table  *slits;
14208     char        keyname[MAX_COLNAME];
14209     const char *instrume;
14210     const char *chipname;
14211     float       slit_x;
14212     int         first_slit, last_slit;
14213     int         nslits;
14214     int         slit_id;
14215     int         fors;
14216     int         chip;
14217     int         fors_is_old;
14218 
14219     /*
14220      * The Y coordinates of the slits are fixed
14221      */
14222 
14223     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
14224                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
14225                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
14226                                -102.1 };
14227     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
14228                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
14229                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
14230                                -113.9 };
14231 
14232 
14233     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14234         return NULL;
14235     }
14236 
14237     if (header == NULL) {
14238         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14239         return NULL;
14240     }
14241 
14242 
14243     /*
14244      * See if this is FORS1 or FORS2;
14245      */
14246 
14247     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14248 
14249     fors = 0;
14250     if (instrume[4] == '1')
14251         fors = 1;
14252     if (instrume[4] == '2')
14253         fors = 2;
14254 
14255     if (fors == 0) {
14256         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14257                       instrume);
14258         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14259         return NULL;
14260     }
14261 
14262     /* FIXME:
14263      * This is the way FORS1 data belong to the upgraded chips,
14264      * named "Marlene" and "Norma III". It's a quick solution,
14265      * there are hardcoded values here!!!
14266      */
14267 
14268     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
14269 
14270     if (chipname[0] == 'M' || chipname[0] == 'N')
14271         fors_is_old = 0;
14272     else
14273         fors_is_old = 1;
14274 
14275     if (fors == 1 && fors_is_old) {
14276         first_slit = 1;
14277         last_slit = 19;
14278     }
14279     else {
14280 
14281         /*
14282          * The master and slave chips can be identified by their positions
14283          * in the chip array in the case of FORS2 data: chip = 2 is the 
14284          * master, chip = 1 is the slave.
14285          */
14286 
14287         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14288 
14289         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14290             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14291                           "in FITS header");
14292             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14293             return NULL;
14294         }
14295 
14296         if (chip != 1 && chip != 2) {
14297             cpl_msg_error(func, "Unexpected chip position in keyword "
14298                           "ESO DET CHIP1 Y: %d", chip);
14299             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14300             return NULL;
14301         }
14302 
14303         if (chip == 1) {
14304             first_slit = 12;
14305             last_slit = 19;
14306         }
14307         else {
14308             first_slit = 1;
14309             last_slit = 11;
14310         }
14311     }
14312 
14313 
14314     /*
14315      * Count slits in header (excluding closed slits - i.e. those with
14316      * offsets greater than 115 mm - and the slits that do not belong 
14317      * to this chip)
14318      */
14319 
14320     nslits = 0;
14321     slit_id = 0;
14322     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14323         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14324         if (cpl_propertylist_has(header, keyname)) {
14325             slit_x = cpl_propertylist_get_double(header, keyname);
14326             if (fabs(slit_x) < 115.0)
14327                 nslits++;
14328         }
14329         else {
14330             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
14331             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14332             return NULL;
14333         }
14334     }
14335 
14336     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14337         cpl_msg_error(func, "%s while loading slits coordinates from "
14338                       "FITS header", cpl_error_get_message());
14339         cpl_error_set_where(func);
14340         return NULL;
14341     }
14342 
14343     if (nslits == 0)  {
14344         cpl_msg_error(func, "No slits coordinates found in header");
14345         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14346         return NULL;
14347     }
14348 
14349     slits = cpl_table_new(nslits);
14350     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14351     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14352     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14353     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14354     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14355     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14356     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14357     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14358     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14359 
14360     nslits = 0;
14361     slit_id = 0;
14362     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14363         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14364         slit_x = cpl_propertylist_get_double(header, keyname);
14365         if (fabs(slit_x) < 115.0) {
14366             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14367             cpl_table_set(slits, "xtop", nslits, slit_x);
14368             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
14369             cpl_table_set(slits, "xbottom", nslits, slit_x);
14370             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
14371             nslits++;
14372         }
14373     }
14374 
14375     return slits;
14376 }
14377 
14378 
14402 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
14403 {
14404     const char *func = "mos_load_slits_fors_lss";
14405 
14406     cpl_table  *slits;
14407     char       *slit_name;
14408     const char *instrume;
14409     int         fors;
14410     int         chip;
14411     float       ytop;
14412     float       ybottom;
14413 
14414     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14415         return NULL;
14416     }
14417 
14418     if (header == NULL) {
14419         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14420         return NULL;
14421     }
14422 
14423 
14424     /*
14425      * See if this is FORS1 or FORS2;
14426      */
14427 
14428     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14429 
14430     fors = 0;
14431     if (instrume[4] == '1')
14432         fors = 1;
14433     if (instrume[4] == '2')
14434         fors = 2;
14435 
14436     if (fors == 0) {
14437         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14438                       instrume);
14439         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14440         return NULL;
14441     }
14442 
14443     if (fors == 1) {
14444         ytop = 109.94;
14445         ybottom = -109.94;
14446     }
14447     else {
14448 
14449         /*
14450          * The master and slave chips can be identified by their positions
14451          * in the chip array in the case of FORS2 data: chip = 2 is the 
14452          * master, chip = 1 is the slave.
14453          */
14454 
14455         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14456 
14457         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14458             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14459                           "in FITS header");
14460             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14461             return NULL;
14462         }
14463 
14464         if (chip != 1 && chip != 2) {
14465             cpl_msg_error(func, "Unexpected chip position in keyword "
14466                           "ESO DET CHIP1 Y: %d", chip);
14467             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14468             return NULL;
14469         }
14470 
14471         if (chip == 1) {
14472             ytop = 30.0;
14473             ybottom = -109.94;
14474         }
14475         else {
14476             ytop = 109.94;
14477             ybottom = -20.0;
14478         }
14479     }
14480 
14481 
14482     slits = cpl_table_new(1);
14483     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14484     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14485     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14486     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14487     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14488     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14489     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14490     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14491     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14492 
14493     slit_name = (char *)cpl_propertylist_get_string(header, 
14494                                                     "ESO INS SLIT NAME");
14495 
14496     cpl_table_set(slits, "ytop", 0, ytop);
14497     cpl_table_set(slits, "ybottom", 0, ybottom);
14498 
14499     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14500         cpl_table_set_int(slits, "slit_id", 0, 1);
14501         cpl_table_set(slits, "xbottom", 0, -0.075);
14502         cpl_table_set(slits, "xtop", 0, 0.075);
14503     }
14504     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14505         cpl_table_set_int(slits, "slit_id", 0, 2);
14506         cpl_table_set(slits, "xbottom", 0, 5.895);
14507         cpl_table_set(slits, "xtop", 0, 6.105);
14508     }
14509     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14510         cpl_table_set_int(slits, "slit_id", 0, 3);
14511         cpl_table_set(slits, "xbottom", 0, -6.135);
14512         cpl_table_set(slits, "xtop", 0, -5.865);
14513     }
14514     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14515         cpl_table_set_int(slits, "slit_id", 0, 4);
14516         cpl_table_set(slits, "xbottom", 0, 11.815);
14517         cpl_table_set(slits, "xtop", 0, 12.185);
14518     }
14519     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14520         cpl_table_set_int(slits, "slit_id", 0, 5);
14521         cpl_table_set(slits, "xbottom", 0, -12.265);
14522         cpl_table_set(slits, "xtop", 0, -11.735);
14523     }
14524     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14525         cpl_table_set_int(slits, "slit_id", 0, 6);
14526         cpl_table_set(slits, "xbottom", 0, 17.655);
14527         cpl_table_set(slits, "xtop", 0, 18.345);
14528     }
14529     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14530         cpl_table_set_int(slits, "slit_id", 0, 7);
14531         cpl_table_set(slits, "xbottom", 0, -18.425);
14532         cpl_table_set(slits, "xtop", 0, -17.575);
14533     }
14534     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14535         cpl_table_set_int(slits, "slit_id", 0, 8);
14536         cpl_table_set(slits, "xbottom", 0, 23.475);
14537         cpl_table_set(slits, "xtop", 0, 24.525);
14538     }
14539     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14540         cpl_table_set_int(slits, "slit_id", 0, 9);
14541         cpl_table_set(slits, "xbottom", 0, -24.66);
14542         cpl_table_set(slits, "xtop", 0, -23.34);
14543     }
14544     else {
14545         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14546                       slit_name);
14547         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14548         cpl_table_delete(slits);
14549         return NULL;
14550     }
14551 
14552     return slits;
14553 }
14554 
14555 
14570 double mos_get_gain_vimos(cpl_propertylist *header)
14571 {
14572     const char *func = "mos_get_gain_vimos";
14573 
14574     double gain = -1.0;
14575 
14576 
14577     if (cpl_error_get_code() != CPL_ERROR_NONE)
14578         return gain;
14579 
14580     if (header == NULL) {
14581         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14582         return gain;
14583     }
14584 
14585     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14586     if (cpl_error_get_code()) {
14587         cpl_error_set_where(func);
14588         gain = -1.0;
14589     }
14590 
14591     return gain;
14592 
14593 }
14594 
14595 
14615 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14616 {
14617     const char *func = "mos_load_slits_vimos";
14618 
14619     cpl_table *slits;
14620     char       keyname[MAX_COLNAME];
14621     float      slit_x;
14622     float      slit_y;
14623     float      dim_x;
14624     int        nslits;
14625     int        slit_id;
14626     int        i;
14627 
14628 
14629     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14630         return NULL;
14631     }
14632 
14633     if (header == NULL) {
14634         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14635         return NULL;
14636     }
14637 
14638     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
14639 
14640     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14641         cpl_error_set_where(func);
14642         return NULL;
14643     }
14644 
14645     slits = cpl_table_new(nslits);
14646     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14647     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14648     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14649     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14650     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14651     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14652     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14653     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14654     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14655 
14656     for (i = 0; i < nslits; i++) {
14657         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
14658         slit_id = cpl_propertylist_get_int(header, keyname);
14659         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14660             cpl_error_set_where(func);
14661             return NULL;
14662         }
14663         sprintf(keyname, "ESO INS SLIT%d X", i+1);
14664         slit_x = cpl_propertylist_get_double(header, keyname);
14665         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14666             cpl_error_set_where(func);
14667             return NULL;
14668         }
14669         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
14670         slit_y = cpl_propertylist_get_double(header, keyname);
14671         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14672             cpl_error_set_where(func);
14673             return NULL;
14674         }
14675         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
14676         dim_x = cpl_propertylist_get_double(header, keyname) / 2;
14677         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14678             cpl_error_set_where(func);
14679             return NULL;
14680         }
14681         cpl_table_set_int(slits, "slit_id", i, slit_id);
14682         cpl_table_set(slits, "xtop", i, slit_x - dim_x);
14683         cpl_table_set(slits, "ytop", i, slit_y);
14684         cpl_table_set(slits, "xbottom", i, slit_x + dim_x);
14685         cpl_table_set(slits, "ybottom", i, slit_y);
14686     }
14687 
14688     return slits;
14689 }
14690 
14691 
14718 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
14719                                     int check_consistency)
14720 {
14721     const char *func = "mos_load_overscans_vimos";
14722 
14723     int        nx = 0;
14724     int        ny = 0;
14725     int        px = 0;
14726     int        py = 0;
14727     int        ox = 0;
14728     int        oy = 0;
14729     int        vx = 0;
14730     int        vy = 0;
14731     int        nrows;
14732     cpl_table *overscans;
14733 
14734 
14735     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14736         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
14737         return NULL;
14738     }
14739 
14740     if (header == NULL) {
14741         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14742         return NULL;
14743     }
14744 
14745     if (cpl_propertylist_has(header, "NAXIS1"))
14746         nx = cpl_propertylist_get_int(header, "NAXIS1");
14747     if (cpl_propertylist_has(header, "NAXIS2"))
14748         ny = cpl_propertylist_get_int(header, "NAXIS2");
14749     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
14750         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
14751     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
14752         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
14753     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
14754         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
14755     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
14756         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
14757     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
14758         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
14759     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
14760         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
14761 
14762     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14763         cpl_msg_error(func, "Missing overscan keywords in header");
14764         cpl_error_set_where(func);
14765         return NULL;
14766     }
14767 
14768     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
14769         cpl_msg_error(func, "Missing overscan keywords in header");
14770         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14771         return NULL;
14772     }
14773 
14774     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
14775     if (check_consistency) {
14776         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14777         return NULL;
14778     }
14779     else {
14780         cpl_msg_debug(func, "Overscans description conflicts with "
14781               "reported image sizes, "
14782               "%d + %d + %d != %d or "
14783               "%d + %d + %d != %d",
14784               px, vx, ox, nx,
14785               py, vy, oy, ny);
14786     }
14787     }
14788 
14789     nrows = 0;
14790     if (px > 0)
14791         nrows++;
14792     if (ox > 0)
14793         nrows++;
14794     if (py > 0)
14795         nrows++;
14796     if (oy > 0)
14797         nrows++;
14798 
14799     if (nrows > 2) {
14800         cpl_msg_error(func, "Unexpected overscan regions "
14801                       "(both in X and Y direction)");
14802         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14803         return NULL;
14804     }
14805 
14806 
14807     /*
14808      * A row is added for the description of the valid region of the
14809      * exposure the input header belongs to.
14810      */
14811 
14812     nrows++;
14813 
14814     overscans = cpl_table_new(nrows);
14815     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
14816     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
14817     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
14818     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
14819 
14820     nrows = 0;
14821 
14822     cpl_table_set_int(overscans, "xlow", nrows, px);
14823     cpl_table_set_int(overscans, "ylow", nrows, py);
14824     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
14825     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
14826     nrows++;
14827 
14828     if (px > 0) {
14829         cpl_table_set_int(overscans, "xlow", nrows, 0);
14830         cpl_table_set_int(overscans, "ylow", nrows, 0);
14831         cpl_table_set_int(overscans, "xhig", nrows, px);
14832         cpl_table_set_int(overscans, "yhig", nrows, ny);
14833         nrows++;
14834     }
14835 
14836     if (ox > 0) {
14837         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
14838         cpl_table_set_int(overscans, "ylow", nrows, 0);
14839         cpl_table_set_int(overscans, "xhig", nrows, nx);
14840         cpl_table_set_int(overscans, "yhig", nrows, ny);
14841         nrows++;
14842     }
14843 
14844     if (py > 0) {
14845         cpl_table_set_int(overscans, "xlow", nrows, 0);
14846         cpl_table_set_int(overscans, "ylow", nrows, 0);
14847         cpl_table_set_int(overscans, "xhig", nrows, nx);
14848         cpl_table_set_int(overscans, "yhig", nrows, py);
14849         nrows++;
14850     }
14851 
14852     if (oy > 0) {
14853         cpl_table_set_int(overscans, "xlow", nrows, 0);
14854         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
14855         cpl_table_set_int(overscans, "xhig", nrows, nx);
14856         cpl_table_set_int(overscans, "yhig", nrows, ny);
14857         nrows++;
14858     }
14859 
14860     return overscans;
14861 
14862 }
14863 
14864 
14865 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
14866 {
14867     const char *func = "mos_load_overscans_fors";
14868 
14869     int        nports;
14870     int        nx = 0;
14871     int        ny = 0;
14872     int        px = 0;
14873     int        py = 0;
14874     int        ox = 0;
14875     int        oy = 0;
14876     int        rebin;
14877     int        nrows;
14878     cpl_table *overscans;
14879 
14880 
14881     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14882         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
14883         return NULL;
14884     }
14885 
14886     if (header == NULL) {
14887         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14888         return NULL;
14889     }
14890 
14891     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
14892         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
14893 
14894     if (nports == 4                                        && 
14895         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
14896         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
14897 
14898         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
14899 
14900         overscans = cpl_table_new(3);
14901         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
14902         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
14903         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
14904         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
14905 
14906         px = 16 / rebin;
14907         ox = 16 / rebin;
14908         nx = 2080 / rebin;
14909         ny = 2048 / rebin;
14910         nrows = 0;
14911 
14912         cpl_table_set_int(overscans, "xlow", nrows, px);
14913         cpl_table_set_int(overscans, "ylow", nrows, py);
14914         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
14915         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
14916         nrows++;
14917 
14918         cpl_table_set_int(overscans, "xlow", nrows, 0);
14919         cpl_table_set_int(overscans, "ylow", nrows, 0);
14920         cpl_table_set_int(overscans, "xhig", nrows, px);
14921         cpl_table_set_int(overscans, "yhig", nrows, ny);
14922         nrows++;
14923 
14924         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
14925         cpl_table_set_int(overscans, "ylow", nrows, 0);
14926         cpl_table_set_int(overscans, "xhig", nrows, nx);
14927         cpl_table_set_int(overscans, "yhig", nrows, ny);
14928         nrows++;
14929     }
14930     else {
14931         overscans = mos_load_overscans_vimos(header, 0);
14932     }
14933 
14934     return overscans;
14935 
14936 }
14937 
14969 #define READY 1
14970 #ifdef READY
14971 
14972 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
14973                                        int samples, int order)
14974 {
14975 
14976     const char *func = "mos_montecarlo_polyfit";
14977 
14978     cpl_polynomial *p;
14979     cpl_polynomial *q;
14980     cpl_vector     *listx;
14981     cpl_vector     *listy;
14982     double          err;
14983     double         *x;
14984     double         *px;
14985     double         *x_eval;
14986     double         *px_eval;
14987     double         *sigma;
14988     double         *vy;
14989     double         *dy;
14990     int             npoints, nevaluate;
14991     int             i, j;
14992 
14993 
14994     if (points == NULL || evaluate == NULL) {
14995         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14996         return NULL;
14997     }
14998 
14999     if (!cpl_table_has_column(points, "x")) {
15000         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15001         return NULL;
15002     }
15003 
15004     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
15005         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15006         return NULL;
15007     }
15008 
15009     if (cpl_table_has_invalid(points, "x")) {
15010         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15011         return NULL;
15012     }
15013 
15014     if (!cpl_table_has_column(points, "y")) {
15015         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15016         return NULL;
15017     }
15018 
15019     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
15020         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15021         return NULL;
15022     }
15023 
15024     if (cpl_table_has_invalid(points, "y")) {
15025         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15026         return NULL;
15027     }
15028 
15029     if (cpl_table_has_column(points, "y_err")) {
15030 
15031         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
15032             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15033             return NULL;
15034         }
15035     
15036         if (cpl_table_has_invalid(points, "y_err")) {
15037             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15038             return NULL;
15039         }
15040     }
15041 
15042     if (!cpl_table_has_column(evaluate, "x")) {
15043         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15044         return NULL;
15045     }
15046 
15047     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
15048         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15049         return NULL;
15050     }
15051 
15052     if (cpl_table_has_invalid(evaluate, "x")) {
15053         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15054         return NULL;
15055     }
15056 
15057     if (samples < 2 || order < 0) {
15058         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15059         return NULL;
15060     }
15061 
15062     npoints = cpl_table_get_nrow(points);
15063     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
15064     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
15065 
15066     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
15067 
15068     if (!cpl_table_has_column(points, "y_err")) {
15069         err = sqrt(err);
15070         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
15071         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
15072         cpl_msg_info(func, "Error column not found - set to %f\n", err);
15073     }
15074 
15075     /*
15076      * Create columns containing modeled values at each x
15077      */
15078 
15079     if (cpl_table_has_column(points, "px"))
15080         cpl_table_erase_column(points, "px");
15081     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
15082     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
15083     x = cpl_table_get_data_double(points, "x");
15084     px = cpl_table_get_data_double(points, "px");
15085     for (i = 0; i < npoints; i++)
15086         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
15087 
15088     nevaluate = cpl_table_get_nrow(evaluate);
15089 
15090     if (cpl_table_has_column(evaluate, "px"))
15091         cpl_table_erase_column(evaluate, "px");
15092     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
15093     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
15094     x_eval = cpl_table_get_data_double(evaluate, "x");
15095     px_eval = cpl_table_get_data_double(evaluate, "px");
15096     for (i = 0; i < nevaluate; i++)
15097         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
15098 
15099     /*
15100      * Initialise column with sigma
15101      */
15102 
15103     if (cpl_table_has_column(evaluate, "sigma"))
15104         cpl_table_erase_column(evaluate, "sigma");
15105     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
15106     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
15107     sigma = cpl_table_get_data_double(evaluate, "sigma");
15108 
15109     /*
15110      * Compute varied y cordinates to fit
15111      */
15112 
15113     if (cpl_table_has_column(points, "vy"))
15114         cpl_table_erase_column(points, "vy");
15115     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
15116     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
15117     vy = cpl_table_get_data_double(points, "vy");
15118     dy = cpl_table_get_data_double(points, "y_err");
15119     cpl_vector_unwrap(listy);
15120     listy = cpl_vector_wrap(npoints, vy);
15121 
15122     for (i = 0; i < samples; i++) {
15123         for (j = 0; j < npoints; j++)
15124             vy[j] = px[j] + dy[j] * mos_randg(1);
15125         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
15126         for (j = 0; j < nevaluate; j++)
15127             sigma[j] += fabs(px_eval[j] 
15128                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
15129         cpl_polynomial_delete(q);
15130     }
15131 
15132     /* 
15133      * Factor 1.25 to convert average deviation to sigma 
15134      */
15135 
15136     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
15137     cpl_table_divide_scalar(evaluate, "sigma", samples);
15138 
15139     cpl_vector_unwrap(listx);
15140     cpl_vector_unwrap(listy);
15141 
15142     return p;
15143 }
15144 
15145 #endif
15146 
15156 cpl_error_code mos_refmask_find_gaps(cpl_mask  * refmask,
15157                      cpl_image * master_flat)
15158 {
15159     int          nx     = cpl_mask_get_size_x(refmask);
15160     int          ny     = cpl_mask_get_size_y(refmask);
15161 
15162     int        * xpos   = cpl_calloc(sizeof(int), ny);
15163 
15164     cpl_vector * v      = cpl_vector_new(ny);
15165     cpl_vector * truev;
15166     int          nvalid = 0;
15167     double     * flats  = cpl_vector_get_data(v);
15168 
15169     double       median, stdev;
15170 
15171     int          i;
15172 
15173 
15174     for (i = 1; i <= ny; i++) {
15175     int j = 0;
15176 
15177     do j++;
15178     while (!cpl_mask_get(refmask, j, i) && j < nx);
15179 
15180     if (j < nx) {
15181         int rejected;
15182 
15183             xpos[i - 1] = j;
15184         flats[nvalid] = cpl_image_get(master_flat, j, i, &rejected);
15185             nvalid++;
15186     }
15187         else {
15188             xpos[i - 1] = -1;
15189         }
15190     }
15191 
15192     truev = cpl_vector_wrap(nvalid, flats);
15193 
15194     median = cpl_vector_get_median(truev);
15195     stdev = cpl_vector_get_stdev(truev);
15196 
15197     cpl_vector_unwrap(truev);
15198     cpl_vector_delete(v);
15199 
15200     for (i = 1; i <= ny; i++) {
15201     if (xpos[i - 1] > 0) {
15202         int    rejected;
15203         double kappa = 1.0;
15204         double delta = 
15205         cpl_image_get(master_flat, xpos[i - 1], i, &rejected) - median;
15206 
15207         if (fabs(delta) > stdev * kappa) {
15208         int j = 0;
15209         
15210         while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
15211             cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
15212             j++;
15213         }
15214         }
15215     }
15216     }
15217 
15218     cpl_free(xpos);
15219 
15220     return cpl_error_get_code();
15221 }
15222 
15230 cpl_error_code mos_saturation_process(cpl_image * image)
15231 {
15232     int     nx    = cpl_image_get_size_x(image);
15233     int     ny    = cpl_image_get_size_y(image);
15234     int     npix  = nx * ny;
15235     float * sdata = cpl_image_get_data_float(image);
15236 
15237     int count, i, j, k;
15238 
15239     /*
15240      * This is used to avoid saturation level coded with pixel value zero
15241      * To make it more robust against random 0.0 values, check that also
15242      * next pixel along the spatial direction is 0.0.
15243      */
15244 
15245     for (i = 0; i < npix - nx; i++)
15246         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
15247             sdata[i] = 65535.0;
15248 
15249     for (i = npix - nx; i < npix; i++)
15250         if (sdata[i] == 0.0) 
15251             sdata[i] = 65535.0;
15252 
15253     /*
15254      * This is a dirty trick to overcome saturations (making up a false
15255      * tip on their flat tops). This should be useless with a better
15256      * peak detection algorithm.
15257      */
15258 
15259     for (i = 0; i < npix; i++) {
15260         if (sdata[i] >= 65535.0) {
15261             count = 0;
15262             for (j = i; j < npix; j++) {
15263                 if (sdata[j] < 65535.0) {
15264                     break;
15265                 }
15266                 else {
15267                     count++;
15268                 }
15269             }
15270             if (count < 30 && count > 2) {
15271                 for (j = i; j < i + count/2; j++)
15272                     sdata[j] = sdata[i] + 1000.0 * (j - i);
15273                 if (count % 2 != 0) {
15274                     sdata[j] = sdata[j-1] + 1000.0;
15275                     j++;
15276                 }
15277                 for (k = j; k <= i + count; k++)
15278                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
15279                 i = k;
15280             }
15281         }
15282     }
15283 
15284     return cpl_error_get_code();
15285 }
15286 
15294 cpl_error_code mos_subtract_background(cpl_image * image)
15295 {
15296     /*
15297      * Create and subtract background
15298      */
15299 
15300     cpl_image * bimage = mos_arc_background(image, 15, 15);
15301     cpl_image_subtract(image, bimage);
15302     cpl_image_delete(bimage);
15303 
15304     return cpl_error_get_code();
15305 }
15306 
15321 cpl_error_code mos_object_intersect(cpl_table ** slitss,
15322                     cpl_table * origslits, int nscience)
15323 {
15324     int i, j;
15325 
15326     cpl_table *summary;
15327     int summary_nobjs = 0;
15328  
15329     int nobjs;
15330 
15331     int nmatches;
15332     int nslits = cpl_table_get_nrow(slitss[0]);
15333 
15334     const float tolerance = 5.0;  /* Tolerance in object position (pixel) */
15335 
15336     int maxobjs;
15337     int k, m;
15338     int nstokes, sstokes;
15339 
15340     cpl_table **work;
15341 
15342     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
15343 
15344 
15345     /* 
15346      * First we build a table listing the offset of each detected
15347      * object at each angle and each beam, from the bottom of each 
15348      * slit spectrum, and the pair that slit spectrum belongs to.
15349      * This summary table will have as many rows as objects found 
15350      * in total at all angles.
15351      */
15352 
15353     for (j = 0; j < nscience; j++) {
15354         int c_nobjs = mos_get_nobjects(slitss[j]);
15355         if (!c_nobjs) 
15356             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15357         summary_nobjs += c_nobjs;
15358     }
15359 
15360     summary = cpl_table_new(summary_nobjs);
15361 
15362     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
15363     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
15364 
15365     /*
15366      * Fill the summary table with data from all objects:
15367      */
15368 
15369     nobjs = 0;
15370 
15371     /* Loop on all object tables (one for each angle) */
15372     for (j = 0; j < nscience; j++) {
15373         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
15374 
15375         /* Loop on all slits found on first - i.e., ALL - object table */
15376     for (k = 0; k < nslits; k++) {
15377 
15378             /* Loop on all objects found on each object table */
15379         for (m = 0; m < c_maxobjs; m++) {
15380         int null;
15381         char *name = cpl_sprintf("object_%d", m + 1);
15382         double obj = cpl_table_get_double(slitss[j], name, k, &null);
15383         int pos;
15384                 int pair;
15385 
15386         cpl_free(name);
15387 
15388         if (null) 
15389                     break;  /* No object #m+1 in this slit - go to next slit */
15390 
15391                 /*
15392                  * Copy necessary object data to summary table. Note 
15393                  * that the absolute object position (row) in the
15394                  * rectified image is made relative to the bottom
15395                  * position (row) of the current slit.
15396                  */ 
15397         
15398                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
15399                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
15400                 cpl_table_set(summary, "offset", nobjs, obj - pos);
15401                 cpl_table_set(summary, "pair", nobjs, pair);
15402 
15403                 nobjs++;
15404         }
15405     }
15406     }
15407 
15408 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
15409 
15410     /* 
15411      * Perform the intersection: what are the objects belonging
15412      * to the same slit (same pair ordinary + extraordinary) which 
15413      * are observed at the same offset at all angles? Those are
15414      * the polarimetric objects.
15415      */
15416 
15417     nmatches = 0;
15418     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
15419 
15420     /*
15421      * We loop on the objects of the first-angle object table as 
15422      * reference, and check whether those objects are present also
15423      * at *all* other angles. Note that the loop advances by pairs.
15424      * It is implicitely assumed that the top (k = 0) slit spectrum
15425      * is always from the ordinary beam, and the spectrum below (k+1)
15426      * its extraordinary match. One should always be sure that the
15427      * input slit tables are constructed in this way in other parts 
15428      * of the code!
15429      */
15430 
15431     for (k = 0; k < nslits; k+=2) {
15432         int slitmatches = 0;
15433     for (m = 0; m < maxobjs; m++) {
15434         int null;
15435         char *name = cpl_sprintf("object_%d", m + 1);
15436         double obj = cpl_table_get_double(slitss[0], name, k, &null);
15437         double pos;
15438             int pair;
15439 
15440         char *name_obj;
15441         char *name_start;
15442         char *name_end;
15443             char *name_row;
15444             char *name_row_s;
15445 
15446         char *name_start_o;
15447         char *name_end_o;
15448             char *name_row_o;
15449 
15450             int start, end;
15451             int length;
15452  
15453             int selected;
15454 
15455 
15456         cpl_free(name);
15457 
15458         if (null) 
15459                 break;
15460 
15461             /*
15462              * Each object of the first object table belongs to a
15463              * slit spectrum (k). This slit spectrum has a position
15464              * in the rectified image, and it belongs to a given 
15465              * ordinary + extraordinary pair.
15466              */
15467      
15468             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
15469             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
15470 
15471 
15472             /*
15473              * Now from the summary table we can select all objects
15474              * which have the same offset (obj - pos) within all slit
15475              * spectra belonging to the same ordinary + extraordinary 
15476              * pair (at all angles).
15477              */
15478 
15479         cpl_table_select_all(summary);  /* Reset selection */
15480 
15481             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
15482             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
15483                                           obj - pos + tolerance);
15484         selected = 
15485             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
15486                                           obj - pos - tolerance);
15487 
15488             /*
15489              * If this object were observed at all angles (nscience) and 
15490              * at all beams (2), we should have selected exactly 2*nscience
15491              * objects. If not, this is not a polarimetric object, and it
15492              * is discarded from the intersection.
15493              */
15494         
15495         if (selected != nscience * 2) 
15496                 continue;
15497 
15498             /*
15499              * If we reach this point we have found one valid polarimetric
15500              * object, that must be inserted in the intersection object
15501              * table.
15502              */
15503  
15504             slitmatches++;
15505 
15506             /*
15507              * Names of the columns of the output table where the
15508              * object information needs to be copied. Note that a
15509              * new column is created, the "row_stokes_#", where the
15510              * row number of the extracted polarimetric signal is
15511              * also computed. For the moment this column will be 
15512              * left empty - it will be filled only when all matches 
15513              * are collected.
15514              */
15515 
15516         name_obj   = cpl_sprintf("object_%d",     slitmatches);
15517         name_start = cpl_sprintf("start_%d",      slitmatches);
15518         name_end   = cpl_sprintf("end_%d",        slitmatches);
15519         name_row   = cpl_sprintf("row_%d",        slitmatches);
15520         name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
15521 
15522             /*
15523              * Names of the columns of the input table where the
15524              * object information is available.
15525              */
15526 
15527         name_start_o = cpl_sprintf("start_%d",  m + 1);
15528         name_end_o   = cpl_sprintf("end_%d",    m + 1);
15529         name_row_o   = cpl_sprintf("row_%d",    m + 1);
15530 
15531             /*
15532              * If the output columns do not exist yet, create them.
15533              */
15534  
15535         if (!cpl_table_has_column(origslits, name_obj)) {
15536         cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
15537             cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
15538         cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
15539         cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
15540         cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
15541             }
15542 
15543             /*
15544              * The current slit spectrum is k. The slit spectrum immediately
15545              * below (in the rectified image) is k+1. We need the length of
15546              * the spectrum below for computing the _absolute_ coordinates
15547              * of the objects in the rectified image in both beams.
15548              */
15549  
15550         length = cpl_table_get_int(origslits, "length", k + 1, &null);
15551 
15552             /*
15553              * Read from the first input object table (first angle)
15554              * the spatial window enclosing the object.
15555              */
15556 
15557         start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
15558         end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
15559 
15560             /*
15561              * Write the object coordinates in the same slit, and in the
15562              * slit below. Note that here we assume that all slits were
15563              * traced perfectly, and we compute the theoretical coords
15564              * (obj - length) within the next slit spectrum (k + 1). In
15565              * principle we should read them as well from the input
15566              * table!
15567              */
15568 
15569             cpl_table_set_double(origslits, name_obj,   k,     obj);
15570             cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
15571 
15572             cpl_table_set_int(origslits,    name_start, k,     start);
15573             cpl_table_set_int(origslits,    name_start, k + 1, start - length);
15574 
15575             cpl_table_set_int(origslits,    name_end,   k,     end);
15576             cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
15577 
15578             /*
15579              * "nmatches" is counting at what "reduced" image row the
15580              * extracted spectra are. Note that this is s preliminary
15581              * numbering - which is wrong: other objects may be found
15582              * in the same slit, and then the indeces would not be in
15583              * sequence. What is important is that at the end of this
15584              * loop "nmatches" would be the total number of matching 
15585              * objects. The two cpl_table_set_int() calls made here
15586              * cannot be removed - they "validate" those table elements
15587              * (see ahead). 
15588              */
15589 
15590             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
15591         nmatches++;
15592             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
15593         nmatches++;
15594 
15595         cpl_free(name_obj);
15596         cpl_free(name_start);
15597         cpl_free(name_end);
15598             cpl_free(name_row);
15599             cpl_free(name_row_s);
15600 
15601         cpl_free(name_start_o);
15602         cpl_free(name_end_o);
15603             cpl_free(name_row_o);
15604         }
15605     }
15606 
15607     /*
15608      * The summary table has fulfilled its function. If no matching 
15609      * objects are found, the function returns with an error.
15610      */
15611 
15612     cpl_table_delete(summary);
15613 
15614     if (!nmatches)
15615         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
15616 
15617     /*
15618      * Now we consider the resulting intersection object table,
15619      * listing all matches. As seen, the image row number reported
15620      * in the columns "row_#" was not really performed sequentially.
15621      * We need to renumber sequentially...
15622      * We need also to fill the "row_stokes_#" column the way the
15623      * extracted polarimetric signal will be stored in the 
15624      * reduced_pol_images...
15625      */
15626  
15627     maxobjs = mos_get_maxobjs_per_slit(origslits);
15628     nstokes = nmatches / 2;         /* nmatches is always an even number     */
15629 
15630     for (k = 0; k < nslits; k++) {
15631         if (k % 2) { /* Extraordinary beam */
15632             nstokes = sstokes;      /* Use same start value as for ordinary  */
15633         }
15634         else {       /* Ordinary beam      */
15635             sstokes = nstokes;      /* Memorise start value at ordinary beam */
15636         }
15637 
15638     for (m = 0; m < maxobjs; m++) {
15639         char *name       = cpl_sprintf("row_%d",        m + 1);
15640             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
15641 
15642         if (!cpl_table_is_valid(origslits, name, k)) {
15643             cpl_free(name);
15644                 cpl_free(namestokes);
15645             break;
15646         }
15647             else { 
15648                 nmatches--;
15649                 nstokes--;
15650             cpl_table_set_int(origslits, name, k, nmatches);
15651                 cpl_table_set_int(origslits, namestokes, k, nstokes);
15652         }
15653 
15654         cpl_free(name);
15655             cpl_free(namestokes);
15656     }
15657     }
15658 
15659 
15660     /*
15661      * This is done to avoid the NULL value is zero (it would invalidate
15662      * also the row_# = 0 or start_# = 0 for an object), and to enable 
15663      * working directly with the column data buffers, when using this 
15664      * table afterwards.
15665      */
15666 
15667     for (j = 0; j < maxobjs; j++) {
15668     char *name = cpl_sprintf("object_%d", j + 1);
15669     cpl_table_fill_invalid_double(origslits, name, -1);
15670     cpl_free(name);
15671 
15672     name       = cpl_sprintf("start_%d", j + 1);
15673     cpl_table_fill_invalid_int(origslits, name, -1);
15674     cpl_free(name);
15675 
15676     name       = cpl_sprintf("end_%d", j + 1);
15677     cpl_table_fill_invalid_int(origslits, name, -1);
15678     cpl_free(name);
15679 
15680     name       = cpl_sprintf("row_%d", j + 1);
15681     cpl_table_fill_invalid_int(origslits, name, -1);
15682     cpl_free(name);
15683 
15684     name       = cpl_sprintf("row_stokes_%d", j + 1);
15685     cpl_table_fill_invalid_int(origslits, name, -1);
15686     cpl_free(name);
15687     }
15688 
15689     /*********************************************************************
15690      * This tail has been added to propagate the selection of valid
15691      * objects also to the input slitss[] tables. Just eliminate all
15692      * this final part to suppress this behaviour.
15693      */
15694 
15695     /*
15696      * First of all, make a working copy and remove all columns related 
15697      * to objects from the input object tables. 
15698      */
15699 
15700     for (i = 0; i < nscience; i++) {
15701         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
15702 
15703         work[i] = cpl_table_duplicate(slitss[i]);
15704 
15705         for (m = 0; m < c_maxobjs; m++) {
15706             char *object_o = cpl_sprintf("object_%d", m + 1);
15707             char *start_o  = cpl_sprintf("start_%d",  m + 1);
15708             char *end_o    = cpl_sprintf("end_%d",    m + 1);
15709             char *row_o    = cpl_sprintf("row_%d",    m + 1);
15710 
15711             cpl_table_erase_column(slitss[i], object_o);
15712             cpl_table_erase_column(slitss[i], start_o);
15713             cpl_table_erase_column(slitss[i], end_o);
15714             cpl_table_erase_column(slitss[i], row_o);
15715         }
15716     }
15717 
15718     /* 
15719      * Now just consider all the objects in the intersection table.
15720      */
15721 
15722     for (k = 0; k < nslits; k++) {
15723         for (j = 0; j < maxobjs; j++) {
15724             double object_w, object_r;
15725             int    start_w, start_r;
15726             int    end_w, end_r;
15727             int    row_w, row_r;
15728 
15729         char  *object_i = cpl_sprintf("object_%d", j + 1);
15730         char  *start_i  = cpl_sprintf("start_%d",  j + 1);
15731         char  *end_i    = cpl_sprintf("end_%d",    j + 1);
15732         char  *row_i    = cpl_sprintf("row_%d",    j + 1);
15733 
15734 
15735             if (!cpl_table_is_valid(origslits, object_i, k))
15736                 break;
15737 
15738             /* 
15739              * We have found a valid object (valid because it belongs
15740              * to the intersection). Now we look for this object in each
15741              * one of the original tables, we get its parameters, and
15742              * copy them at the right position (i.e., same position as
15743              * in intersection table). The object will be the one closest
15744              * to the object position (column object_i) in the intersection
15745              * table. Note that we examine the same row, k, in all tables.
15746              */
15747 
15748             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
15749             start_w  = cpl_table_get_int   (origslits, start_i,  k, NULL);
15750             end_w    = cpl_table_get_int   (origslits, end_i,    k, NULL);
15751             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
15752 
15753             for (i = 0; i < nscience; i++) {
15754                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
15755                 int        minpos;
15756                 double     mindiff, diff;
15757             char      *object_o;
15758             char      *start_o;
15759             char      *end_o;
15760             char      *row_o;
15761 
15762                 for (m = 0; m < c_maxobjs; m++) {
15763                 object_o = cpl_sprintf("object_%d", m + 1);
15764                 start_o  = cpl_sprintf("start_%d",  m + 1);
15765                 end_o    = cpl_sprintf("end_%d",    m + 1);
15766                 row_o    = cpl_sprintf("row_%d",    m + 1);
15767 
15768                     if (!cpl_table_is_valid(work[i], object_o, k))
15769                         break;
15770 
15771                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
15772                     start_r  = cpl_table_get_int   (work[i], start_o,  k, NULL);
15773                     end_r    = cpl_table_get_int   (work[i], end_o,    k, NULL);
15774                     row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
15775 
15776                     diff = fabs(object_w - object_r);
15777                     if (m) {
15778                         if (mindiff > diff) {
15779                             mindiff = diff;
15780                             minpos = m;
15781                         }
15782                     }
15783                     else {
15784                         mindiff = diff;
15785                         minpos = 0;
15786                     }
15787 
15788                 cpl_free(object_o);
15789                 cpl_free(start_o);
15790                 cpl_free(end_o);
15791                 cpl_free(row_o);
15792                 }
15793 
15794                 object_o = cpl_sprintf("object_%d", minpos + 1);
15795                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
15796                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
15797                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
15798 
15799                 if (!cpl_table_has_column(slitss[i], object_i)) {
15800                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
15801                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
15802                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
15803                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
15804                 cpl_table_fill_invalid_double(slitss[i], object_i, -1);
15805                 cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
15806                 cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
15807                 cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
15808                 }
15809 
15810                 cpl_table_set_double(slitss[i], object_i, k,
15811                                      cpl_table_get_double(work[i], object_o, 
15812                                                           k, NULL));
15813                 cpl_table_set_int(slitss[i], start_i , k,
15814                                   cpl_table_get_int(work[i], start_o, k, NULL));
15815                 cpl_table_set_int(slitss[i], end_i , k,
15816                                   cpl_table_get_int(work[i], end_o, k, NULL));
15817                 cpl_table_set_int(slitss[i], row_i , k, row_w);
15818 
15819             cpl_free(object_o);
15820             cpl_free(start_o);
15821             cpl_free(end_o);
15822             cpl_free(row_o);
15823             }
15824 
15825         cpl_free(object_i);
15826         cpl_free(start_i);
15827         cpl_free(end_i);
15828         cpl_free(row_i);
15829         }
15830     }
15831 
15832     for (i = 0; i < nscience; i++)
15833         cpl_table_delete(work[i]);
15834 
15835     cpl_free(work);
15836 
15837 
15838     return cpl_error_get_code();
15839 }
15840 
15841 
15849 int mos_get_maxobjs_per_slit(cpl_table * slits)
15850 {
15851     int maxobjs = 1;
15852 
15853     char * colname = cpl_sprintf("object_%d", maxobjs);
15854     
15855     while (cpl_table_has_column(slits, colname)) {
15856         maxobjs++;
15857         cpl_free(colname);
15858         colname = cpl_sprintf("object_%d", maxobjs);
15859     }
15860     
15861     cpl_free(colname);
15862 
15863     maxobjs--;
15864 
15865     return maxobjs;
15866 }
15867 
15875 int mos_get_nobjects(cpl_table * slits)
15876 {
15877     int nobjs = 0;
15878 
15879     int nslits  = cpl_table_get_nrow(slits);
15880     int maxobjs = mos_get_maxobjs_per_slit(slits);
15881 
15882     int k, m;
15883 
15884     for (k = 0; k < nslits; k++) {
15885     for (m = 0; m < maxobjs; m++) {
15886         char * name = cpl_sprintf("object_%d", m + 1);
15887         int    null = !cpl_table_is_valid(slits, name, k);
15888 
15889         cpl_free(name);
15890 
15891         if (null)  break;
15892             else nobjs++;
15893     }
15894     }
15895 
15896     return nobjs;
15897 }
15898 
15906 int mos_check_slits(cpl_table * slits)
15907 {
15908 
15909     cpl_propertylist * sort;
15910 
15911     int nslits  = cpl_table_get_nrow(slits);
15912 
15913     int k, null;
15914 
15915 
15916     for (k = 0; k < nslits; k++) {
15917         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
15918     double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
15919 
15920     double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
15921     double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
15922 
15923     int nmiss = (int)((ytop - ybottom) / 90.0 + 0.5);
15924 
15925     if (nmiss > 1) {
15926         cpl_msg_warning(cpl_func, 
15927                 "Some slits could not be properly detected. "
15928                 "There might be accountable inaccuracies.");
15929         while (nmiss > 1) {
15930         cpl_table_set_size(slits, nslits + 1);
15931 
15932         /* Fill in new slit 'cut' */
15933 
15934         /* x coordinates be the same (acceptable approximation) */
15935         cpl_table_set_double(slits, "xtop",    nslits, xtop);
15936         cpl_table_set_double(slits, "xbottom", nslits, xbottom);
15937 
15938         /* Cut */
15939         if (k == 0) {
15940             cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
15941             cpl_table_set_double(slits, "ytop",    nslits, ybottom
15942                                                                    + 85.0);
15943             ybottom += 90.0;
15944             cpl_table_set_double(slits, "ybottom", k,      ybottom);
15945         } else {
15946             cpl_table_set_double(slits, "ytop",    nslits, ytop);
15947             cpl_table_set_double(slits, "ybottom", nslits, ytop 
15948                                                                    - 85.0);
15949             ytop -= 90.0;
15950             cpl_table_set_double(slits, "ytop",     k,     ytop);
15951         }
15952 
15953         nslits++; nmiss--;
15954         }
15955     }
15956     }
15957 
15958     sort = cpl_propertylist_new();
15959     cpl_propertylist_append_bool(sort, "ytop", 1);
15960     cpl_table_sort(slits, sort);
15961     cpl_propertylist_delete(sort);
15962 
15963     return 0;
15964 }
15965 
15988 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header)
15989 {
15990     int m, null;
15991     int halfsize;
15992 
15993     cpl_propertylist * sort;
15994     cpl_table        * slits; 
15995 
15996     slits    = mos_load_slits_fors_mos(header);
15997     halfsize = cpl_table_get_nrow(slits);
15998 
15999     cpl_table_set_size(slits, 2 * halfsize);
16000 
16001     for (m = 0; m < halfsize; m++) {
16002 
16003     double gap = 1.4;
16004 
16005     double length = 
16006         cpl_table_get(slits, "ytop",    m, &null) -
16007         cpl_table_get(slits, "ybottom", m, &null);
16008 
16009     if (m) {
16010         double interval = 
16011         cpl_table_get(slits, "ybottom", m - 1, &null) -
16012         cpl_table_get(slits, "ytop",    m,     &null);
16013 
16014         gap = (interval - length) / 2;
16015     }
16016 
16017     cpl_table_set(slits, "slit_id", m + halfsize,
16018               cpl_table_get(slits, "slit_id", m, &null) - 1);
16019 
16020     cpl_table_set(slits, "xtop",    m + halfsize,
16021               cpl_table_get(slits, "xtop",    m, &null));
16022 
16023     cpl_table_set(slits, "xbottom", m + halfsize,
16024               cpl_table_get(slits, "xbottom", m, &null));
16025 
16026     cpl_table_set(slits, "ytop",    m + halfsize, 
16027               cpl_table_get(slits, "ytop", m, &null) + gap + length);
16028 
16029     cpl_table_set(slits, "ybottom", m + halfsize,
16030               cpl_table_get(slits, "ytop", m, &null) + gap);
16031     }
16032 
16033     for (m = 0; m < 2 * halfsize; m++) {
16034     cpl_table_set(slits, "ytop",    m, 
16035               cpl_table_get(slits, "ytop",    m, &null) - 5.3);
16036 
16037     cpl_table_set(slits, "ybottom", m,
16038               cpl_table_get(slits, "ybottom", m, &null) - 5.3);
16039 
16040     }
16041 
16042     sort = cpl_propertylist_new();
16043     cpl_propertylist_append_bool(sort, "ytop", 1);
16044     cpl_table_sort(slits, sort);
16045 
16046     cpl_propertylist_delete(sort);
16047 
16048     return slits;
16049 }
16050 
16051 int * fors_get_nobjs_perslit(cpl_table * slits)
16052 {
16053     int nslits  = cpl_table_get_nrow(slits);
16054     int maxobjs = mos_get_maxobjs_per_slit(slits);
16055 
16056     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
16057 
16058     int k, m;
16059 
16060     for (k = 0; k < nslits; k++) {
16061     int nobjs = 0;
16062     for (m = 0; m < maxobjs; m++) {
16063         char * name = cpl_sprintf("object_%d", m + 1);
16064         int    null = !cpl_table_is_valid(slits, name, k);
16065 
16066         cpl_free(name);
16067 
16068         if (null)  break;
16069             else nobjs++;
16070     }
16071     
16072     nobjs_per_slit[k] = nobjs;
16073     }
16074 
16075     return nobjs_per_slit;
16076 }

Generated on Wed Jun 24 14:11:19 2009 for FORS Pipeline Reference Manual by  doxygen 1.4.7