moses.c

00001 /* $Id: moses.c,v 1.5 2008/08/18 14:10:46 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: 2008/08/18 14:10:46 $
00024  * $Revision: 1.5 $
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(float *data, int size, float *position, int minPoints)
00310 {
00311   int    i;
00312   int    count = 0;
00313   float *copy;
00314   float  max, median, level, pos, variance, uniformVariance;
00315   double sum, weights;
00316 
00317 
00318   if (data == NULL)
00319       return 1;
00320 
00321   if (size < 5)         /* Hardcoded, I know... */
00322       return 1;
00323 
00324 
00325   /*
00326    *  Find median level
00327    */
00328 
00329   copy = (float *) cpl_malloc(size*sizeof(float));
00330   for (i = 0; i < size; i++)
00331       copy[i] = data[i];
00332   median = cpl_tools_get_median_float(copy, size);
00333   cpl_free(copy);
00334 
00335 
00336   /*
00337    *  Find max
00338    */
00339 
00340   max = data[0];
00341   for (i = 1; i < size; i++)
00342       if (data[i] > max)
00343           max = data[i];
00344 
00345 
00346   /*
00347    *  If the max equals the median we have a flat input, therefore
00348    *  no peak is found.
00349    */
00350 
00351   if (max-median < 0.00001)
00352       return 1;
00353 
00354 
00355   /*
00356    *  Discrimination level: only pixels with values above this
00357    *  level are considered in baricenter calculation.
00358    */
00359 
00360   level = (max + median) / 2;
00361 
00362 
00363   /*
00364    *  Of the values above this level compute the baricenter and
00365    *  then the variance of the positions used. Note that the weights
00366    *  are taken as the difference between the pixels values and
00367    *  the median level (supposedly the background).
00368    */
00369 
00370   count = 0;
00371   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00372       if (data[i] > level) {
00373           count++;
00374           weights += (data[i] - median);
00375           sum     += i * (data[i] - median);
00376       }
00377   }
00378 
00379 
00380   /*
00381    *  If too few values are above threshold, refuse the position
00382    *  as insignificant
00383    */
00384 
00385   if (count < minPoints)
00386       return 1;
00387 
00388   pos = sum / weights;
00389   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00390       if (data[i] > level) {
00391           weights++;
00392           sum += (i - pos) * (i - pos);
00393       }
00394   }
00395   variance = sqrt(sum / weights);
00396 
00397 
00398  /*
00399   *  The "uniform variance" is the variance that should be obtained
00400   *  in the case of uniform distribution of the points positions in
00401   *  the selected interval. If the real variance is comparable with
00402   *  this value, the peak is considered not found.
00403   */
00404 
00405   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00406 
00407   if (variance > 0.8 * uniformVariance)
00408       return 1;
00409 
00410   *position = pos + 0.5;
00411 
00412   return 0;
00413 }
00414 
00415 
00416 /*
00417  *  The following static function determines the quantity dx to be
00418  *  added to the position of the highest pixel of a fiber profile,
00419  *  to get the true position of the profile maximum. All is needed
00420  *  is the maximum observed value v2 in the profile, and the observed
00421  *  values v1 and v3 of the previous and the next pixels in the profile.
00422  *  
00423  *  The following ratio is defined:
00424  *  
00425  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00426  *      
00427  *  This is a conventional ratio that wouldn't diverge for any set of
00428  *  pixel values, and that would not depend on the presence of background
00429  *  (with the assumption that the background level is the same for the 
00430  *  three pixels). R has also been chosen in such a way that its value
00431  *  is already quite close to the real dx. It should be noted that the
00432  *  following condition should be fulfilled:
00433  *
00434  *           v1  <= v2   and   v3  <  v2
00435  *  or
00436  *           v1  <  v2   and   v3  <=  v2
00437  *
00438  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00439  *  boundary cases, one has:
00440  *
00441  *           v2 = v1   and   R = dx = -0.5
00442  *           v2 = v3   and   R = dx =  0.5
00443  *
00444  *  Another special case is when the observed pixel values are perfectly
00445  *  symmetrical:
00446  *
00447  *           v1 = v3   and   R = dx =  0.0
00448  *
00449  *  In all the intermediate cases the relation between R and dx depends
00450  *  on the shape of the fiber profile, that has been determined elsewhere.
00451  *  Using the accurate reconstruction of the fiber profile obtained by 
00452  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00453  *  that R differs from dx always less than 0.01 pixels. If the condition
00454  *
00455  *           v1  <= v2   and   v3  <  v2
00456  *  or
00457  *           v1  <  v2   and   v3  <=  v2
00458  *
00459  *  is not fulfilled, then this function returns the value 2.0.
00460  */
00461 
00462 static double values_to_dx(double v1, double v2, double v3)
00463 {
00464 
00465   static double epsilon = 0.00000001;
00466   double        r       = 2.0;
00467 
00468 
00469   if (v1 > v2 || v3 > v2)
00470     return r;
00471 
00472   if (2 * v2 - v1 - v3 < epsilon)
00473     return r;
00474 
00475   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00476 
00477   return r;
00478 
00479 }
00480 
00481 
00482 /*
00483  * The following static function passes a min filter of given box
00484  * size on the data buffer. The box size must be a positive odd integer.
00485  */
00486 
00487 static float *min_filter(float *buffer, int length, int size)
00488 {
00489     float *minf  = cpl_calloc(length, sizeof(float));
00490     float  min;
00491     int    start = size / 2;
00492     int    end   = length - size / 2;
00493     int    i, j;
00494 
00495 
00496     for (i = start; i < end; i++) {
00497         min = buffer[i-start];
00498         for (j = i - start + 1; j <= i + start; j++)
00499             if (min > buffer[j])
00500                 min = buffer[j];
00501         minf[i] = min;
00502     }
00503 
00504     for (i = 0; i < start; i++)
00505         minf[i] = minf[start];
00506 
00507     for (i = end; i < length; i++)
00508         minf[i] = minf[end-1];
00509 
00510     return minf;
00511 }
00512 
00513 
00514 /*
00515  * The following static function passes a max filter of given box
00516  * size on the data buffer. The box size must be a positive odd integer.
00517  */
00518  
00519 static float *max_filter(float *buffer, int length, int size)
00520 {
00521     float *maxf  = cpl_calloc(length, sizeof(float));
00522     float  max;
00523     int    start = size / 2;
00524     int    end   = length - size / 2;
00525     int    i, j;
00526 
00527 
00528     for (i = start; i < end; i++) {
00529         max = buffer[i-start];
00530         for (j = i - start + 1; j <= i + start; j++)
00531             if (max < buffer[j])
00532                 max = buffer[j];
00533         maxf[i] = max;
00534     }
00535 
00536     for (i = 0; i < start; i++)
00537         maxf[i] = maxf[start];
00538 
00539     for (i = end; i < length; i++)
00540         maxf[i] = maxf[end-1];
00541 
00542     return maxf;
00543 }
00544 
00545 
00546 /*
00547  * The following static function passes a running average of given box
00548  * size on the data buffer. The box size must be a positive odd integer.
00549  */
00550  
00551 static float *smo_filter(float *buffer, int length, int size)
00552 {
00553     float *smof  = cpl_calloc(length, sizeof(float));
00554     double sum;
00555     int    start = size / 2;
00556     int    end   = length - size / 2;
00557     int    i, j;
00558 
00559 
00560     for (i = start; i < end; i++) {
00561         sum = 0.0;
00562         for (j = i - start; j <= i + start; j++)
00563             sum += buffer[j];
00564         smof[i] = sum / size;
00565     }
00566 
00567     for (i = 0; i < start; i++)
00568         smof[i] = smof[start];
00569 
00570     for (i = end; i < length; i++)
00571         smof[i] = smof[end-1];
00572 
00573     return smof;
00574 }
00575 
00576 /*
00577  * The following two static functions are used to read and write from the 
00578  * global distortion table the different model components. Conventionally
00579  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00580  * storage for model coefficients, and these functions guarantee that the
00581  * coefficients are read in and written out correctly, independent on their
00582  * physical meaning. The first 6 table rows are a description of the IDS
00583  * coefficients, followed by a row containing only the used reference 
00584  * wavelength. The remaining 3 are a description of the spectral curvature.
00585  * The first row is a description of coefficient c0, the second of coefficient
00586  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00587  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00588  * bivariate polynomialx on x,y mask coordinates. If the input table
00589  * to the write routine is NULL, it is allocated and initialised. Also
00590  * the input polynomial could be NULL, and nothing would be written to 
00591  * the table. If both pointers are NULL the function is basically a
00592  * constructor of the global distortion table.
00593  */
00594 
00595 static cpl_polynomial *read_global_distortion(cpl_table *global, int row)
00596 {
00597     cpl_polynomial *poly = NULL;
00598     int             p[2];
00599     int             degree = 2;
00600     int             null;
00601     double          coeff;
00602 
00603     char   name[MAX_COLNAME];
00604 
00605 
00606     for (p[0] = 0; p[0] <= degree; p[0]++) {
00607         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00608             snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00609             coeff = cpl_table_get_double(global, name, row, &null);
00610             if (null)
00611                 continue;
00612             if (poly == NULL)
00613                 poly = cpl_polynomial_new(2);
00614             cpl_polynomial_set_coeff(poly, p, coeff);
00615         }
00616     }
00617 
00618     return poly;
00619 }
00620 
00621 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00622                                           cpl_polynomial *poly)
00623 {
00624     cpl_table *table;
00625     int        p[2];
00626     int        degree = 2;
00627     int        nrow = 10;
00628 
00629     char       name[MAX_COLNAME];
00630 
00631 
00632     if (global) {
00633         table = global;
00634     }
00635     else {
00636         table = cpl_table_new(nrow);
00637         for (p[0] = 0; p[0] <= degree; p[0]++) {
00638             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00639                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00640                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00641             }
00642         }
00643     }
00644 
00645     if (poly) {
00646         for (p[0] = 0; p[0] <= degree; p[0]++) {
00647             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00648                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00649                 cpl_table_set_double(table, name, row, 
00650                                      cpl_polynomial_get_coeff(poly, p));
00651             }
00652         }
00653     }
00654 
00655     return table;
00656 }
00657 
00658 
00659 /*
00660  * The following static function is performing a robust linear fit
00661  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00662  *
00663  *  ----> y = a + b * x
00664  *
00665  * This function return 0 on success.
00666  */
00667 
00668 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00669 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00670                            double *abdev)
00671 {
00672     cpl_vector *vx;
00673     cpl_vector *vy;
00674     cpl_vector *va;
00675 
00676     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00677     double  sx, sy, sxy, sxx, chisq;
00678     double *arr;
00679     double  aa_ls, bb_ls;
00680     double *x;
00681     double *y;
00682     int     np;
00683     int     iter;
00684     int     max_iterate = 30;
00685     int     i;
00686 
00687 
00688     np = cpl_bivector_get_size(list);
00689     vx = cpl_bivector_get_x(list);
00690     vy = cpl_bivector_get_y(list);
00691     x = cpl_vector_get_data(vx);
00692     y = cpl_vector_get_data(vy);
00693 
00694     sx = sy = sxx = sxy = 0.00;
00695     for (i = 0; i < np; i++) {
00696         sx  += x[i];
00697         sy  += y[i];
00698         sxy += x[i] * y[i];
00699         sxx += x[i] * x[i];
00700     }
00701 
00702     del = np * sxx - sx * sx;
00703     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00704     bb_ls = bb = (np * sxy - sx * sy) / del;
00705 
00706     chisq = 0.00;
00707     for (i = 0; i < np; i++) {
00708         temp = y[i] - (aa+bb*x[i]);
00709         temp *= temp;
00710         chisq += temp;
00711     }
00712 
00713     va = cpl_vector_new(np);
00714     arr = cpl_vector_get_data(va);
00715     sigb = sqrt(chisq/del);
00716     b1 = bb;
00717 
00718     bcomp = b1;
00719     sum = 0.00;
00720     for (i = 0; i < np; i++) {
00721         arr[i] = y[i] - bcomp * x[i];
00722     }
00723     aa = cpl_vector_get_median_const(va);
00724     abdevt = 0.0;
00725     for (i = 0; i < np; i++) {
00726         d = y[i] - (bcomp * x[i] + aa);
00727         abdevt += fabs(d);
00728         if (y[i] != 0.0) 
00729             d /= fabs(y[i]);
00730         if (fabs(d) > 1e-7) 
00731             sum += (d >= 0.0 ? x[i] : -x[i]);
00732     }
00733     f1 = sum;
00734 
00735     b2 = bb + SEGNO(3.0 * sigb, f1);
00736 
00737     bcomp = b2;
00738     sum = 0.00;
00739     for (i = 0; i < np; i++) {
00740         arr[i] = y[i] - bcomp * x[i];
00741     }
00742     aa = cpl_vector_get_median_const(va);
00743     abdevt = 0.0;
00744     for (i = 0; i < np; i++) {
00745         d = y[i] - (bcomp * x[i] + aa);
00746         abdevt += fabs(d);
00747         if (y[i] != 0.0) 
00748             d /= fabs(y[i]);
00749         if (fabs(d) > 1e-7) 
00750             sum += (d >= 0.0 ? x[i] : -x[i]);
00751     }
00752     f2 = sum;
00753 
00754     if (fabs(b2-b1)<1e-7) {
00755         *a = aa;
00756         *b = bb;
00757         *abdev = abdevt / (double)np;
00758         cpl_vector_delete(va);
00759         return 0;
00760     }
00761 
00762     iter = 0;
00763     while (f1*f2 > 0.0) {
00764         bb = 2.0*b2-b1;
00765         b1 = b2;
00766         f1 = f2;
00767         b2 = bb;
00768 
00769         bcomp = b2;
00770         sum = 0.00;
00771         for (i = 0; i < np; i++) {
00772             arr[i] = y[i] - bcomp * x[i];
00773         }
00774         aa = cpl_vector_get_median_const(va);
00775         abdevt = 0.0;
00776         for (i = 0; i < np; i++) {
00777             d = y[i] - (bcomp * x[i] + aa);
00778             abdevt += fabs(d);
00779             if (y[i] != 0.0) 
00780                 d /= fabs(y[i]);
00781             if (fabs(d) > 1e-7) 
00782                 sum += (d >= 0.0 ? x[i] : -x[i]);
00783         }
00784         f2 = sum;
00785         iter++;
00786         if (iter >= max_iterate) 
00787             break;
00788     }
00789     if (iter >= max_iterate) {
00790         *a = aa_ls;
00791         *b = bb_ls;
00792         *abdev = -1.0;
00793         cpl_vector_delete(va);
00794         return 1;
00795     }
00796 
00797     sigb = 0.01 * sigb;
00798     while (fabs(b2-b1) > sigb) {
00799         bb = 0.5 * (b1 + b2);
00800         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00801             break;
00802         bcomp = bb;
00803         sum = 0.0;
00804         for (i = 0; i < np; i++) {
00805             arr[i] = y[i] - bcomp * x[i];
00806         }
00807         aa = cpl_vector_get_median_const(va);
00808         abdevt = 0.0;
00809         for (i = 0; i < np; i++) {
00810             d = y[i] - (bcomp * x[i] + aa);
00811             abdevt += fabs(d);
00812             if (y[i] != 0.0) 
00813                 d /= fabs(y[i]);
00814             if (fabs(d) > 1e-7) 
00815                 sum += (d >= 0.0 ? x[i] : -x[i]);
00816         }
00817         f = sum;
00818 
00819         if (f*f1 >= 0.0) {
00820             f1=f;
00821             b1=bb;
00822         } 
00823         else {
00824             f2=f;
00825             b2=bb;
00826         }
00827     }
00828     cpl_vector_delete(va);
00829     *a = aa;
00830     *b = bb;
00831     *abdev = abdevt / np;
00832     return 0;
00833 }
00834 #undef SEGNO
00835 
00836 /*      
00837  * The following static function applies the Hough transform from a table
00838  * of points to another table of points. Given the points p_i = (x_i,y_i)
00839  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00840  * and Y = y_i - X*x_i is computed and added to the output table for each
00841  * p_i, p_j pair. This means that if the input table has N points, the
00842  * output table has N*(N-1)/2 points.
00843  */
00844     
00845 /* static */
00846 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00847 {
00848     cpl_table *output;
00849     double    *xdata;
00850     double    *ydata;
00851     double    *xodata;
00852     double    *yodata;
00853     int        npoints;
00854     int        opoints;
00855     int        i, j, k;
00856 
00857 
00858     npoints = cpl_table_get_nrow(table);
00859     opoints = npoints*(npoints-1)/2;
00860 
00861     output = cpl_table_new(opoints);
00862     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00863     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00864     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00865     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00866 
00867     xodata = cpl_table_get_data_double(output, "m");
00868     yodata = cpl_table_get_data_double(output, "q");
00869     
00870     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00871     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00872 
00873     xdata = cpl_table_get_data_double(table, "x");
00874     ydata = cpl_table_get_data_double(table, "y");
00875 
00876     k = 0;
00877     for (i = 0; i < npoints; i++) {
00878         for (j = i+1; j < npoints; j++) {
00879             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00880             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00881             k++;
00882         }
00883     }
00884 
00885     if (k != opoints)
00886         printf("Assert k = %d, expected %d\n", k, opoints);
00887 
00888     cpl_table_erase_column(table, "x");
00889     cpl_table_erase_column(table, "y");
00890 
00891     return output;
00892 }
00893 
00894 
00895 /*
00896  * The following static function is performing the spectral
00897  * extraction for the function mos_extract_objects()
00898  */
00899 
00900 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00901                            cpl_image *extracted, cpl_image *sky, 
00902                            cpl_image *error, int nobjects, int extraction, 
00903                            double ron, double conad, int ncomb)
00904 {
00905 
00906   cpl_vector *vprofile;
00907   cpl_matrix *kernel;
00908   cpl_image  *smowin;
00909 
00910   int i, j;
00911   int specLen;
00912   int numRows;
00913   int index;
00914   int iter;
00915   int maxIter   = 2;         /* Not less than 2 !!! */
00916   int smoothBox = 31;        /* Not less than 5 !!! */
00917   double nsigma = 5.0;
00918 
00919   double sumWeight, sum, sumSky, sumProf, variance, weight;
00920   double *profile;
00921   double *buffer;
00922   float  *edata;
00923   float  *ekdata;
00924   float  *endata;
00925   float  *sdata;
00926   float  *kdata;
00927   float  *fdata;
00928 
00929   double value;
00930 
00931 
00932   specLen = cpl_image_get_size_x(sciwin);
00933   numRows = cpl_image_get_size_y(sciwin);
00934 
00935   edata = cpl_image_get_data(extracted);
00936   edata += nobjects * specLen;
00937 
00938   ekdata = cpl_image_get_data(sky);
00939   ekdata += nobjects * specLen;
00940 
00941   endata = cpl_image_get_data(error);
00942   endata += nobjects * specLen;
00943 
00944   sdata = cpl_image_get_data(sciwin);
00945   kdata = cpl_image_get_data(skywin);
00946 
00947 
00948   /*
00949    * Initial spectrum estimate
00950       if (sdata[i + j * specLen] > 0.0)
00951    */
00952 
00953   if (extraction && numRows > 5) {
00954       kernel = cpl_matrix_new(3, 3);
00955       cpl_matrix_fill(kernel, 1.0);
00956       smowin = cpl_image_filter_median(sciwin, kernel);
00957       cpl_matrix_delete(kernel);
00958       fdata = cpl_image_get_data(smowin);
00959       for (i = 0; i < specLen; i++)
00960         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00961             edata[i] += fdata[i + j * specLen];
00962       cpl_image_delete(smowin);
00963   }
00964   else {
00965       for (i = 0; i < specLen; i++)
00966         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00967             edata[i] += sdata[i + j * specLen];
00968   }
00969 
00970   if (extraction) {
00971 
00972     profile = cpl_calloc(specLen * numRows, sizeof(double));
00973     buffer  = cpl_calloc(specLen, sizeof(double));
00974 
00975     for (iter = 0; iter < maxIter; iter++) {
00976 
00977       /*
00978        * Normalised spatial profile
00979        */
00980 
00981       for (i = 0; i < specLen; i++) {
00982         for (j = 0; j < numRows; j++) {
00983           index = i + j * specLen;
00984 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00985           if (fabs(edata[i]) > 0.00001)
00986             profile[index] = sdata[index] / edata[i];
00987           else
00988             profile[index] = 0.0;
00989         }
00990       }
00991 
00992       for (j = 0; j < numRows; j++) {
00993 
00994         /*
00995          * Smooth each row in the dispersion direction, and enforce positivity
00996          */
00997 
00998         for (i = 0; i < specLen - smoothBox; i++) {
00999           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01000           value = cpl_vector_get_median_const(vprofile);
01001           cpl_vector_unwrap(vprofile);
01002           if (value < 0)
01003             value = 0.0;
01004           buffer[i + smoothBox / 2] = value;
01005         }
01006 
01007         /*
01008          * Replace the end portions (i.e., not median filtered) with a mean
01009          */
01010 
01011         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01012         value = cpl_vector_get_mean(vprofile);
01013         cpl_vector_unwrap(vprofile);
01014 
01015         if (value < 0)
01016             value = 0.0;
01017 
01018         for (i = 0; i < smoothBox / 2; i++)
01019           buffer[i] = value;
01020 
01021         vprofile = cpl_vector_wrap(smoothBox / 2, 
01022                                    profile + specLen - smoothBox/2 + j*specLen);
01023         value = cpl_vector_get_mean(vprofile);
01024         cpl_vector_unwrap(vprofile);
01025 
01026         if (value < 0)
01027             value = 0.0;
01028 
01029         for (i = 0; i < smoothBox / 2; i++)
01030           buffer[i + specLen - smoothBox / 2] = value;
01031 
01032         for (i = 0; i < specLen; i++)
01033           profile[i + j * specLen] = buffer[i];
01034 
01035       }
01036 
01037       /*
01038        * Enforce normalization of spatial profile after smoothing
01039        */
01040 
01041       for (i = 0; i < specLen; i++) {
01042         for (j = 0, value = 0.0; j < numRows; j++)
01043           value += profile[i + j * specLen];
01044         if (value > 0.00001)
01045           for (j = 0; j < numRows; j++)
01046             profile[i + j * specLen] /= value;
01047         else
01048           for (j = 0; j < numRows; j++)
01049             profile[i + j * specLen] = 0.0;
01050       }
01051 
01052 
01053       /*
01054        * Optimal extraction
01055        */
01056 
01057       for (i = 0; i < specLen; i++) {
01058         sum = 0.0;
01059         sumSky = 0.0;
01060         sumWeight = 0.0;
01061         sumProf = 0.0;
01062         for (j = 0; j < numRows; j++) {
01063           index = i + j * specLen;
01064 /*        
01065 if (sdata[index] > 0.0) {
01066 */
01067             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01068                      / conad;
01069             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01070             value = sdata[index] - edata[i] * profile[index];
01071             if (fabs(value) / sqrt(variance) < nsigma) {
01072               weight = 1000000 * profile[index] / variance;
01073               sum += weight * sdata[index];
01074               sumSky += weight * kdata[index];
01075               sumWeight += weight * profile[index];
01076               sumProf += profile[index];
01077             }
01078 /*
01079 }      
01080 */
01081         }
01082 
01083         if (sumWeight > 0.00001) {
01084           edata[i] = sum / sumWeight;
01085           ekdata[i] = sumSky / sumWeight;
01086           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01087         }
01088         else {
01089 /*
01090           edata[i] = 0.0;
01091           ekdata[i] = 0.0;
01092           endata[i] = 0.0;
01093 */
01094           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01095         }
01096       }
01097     }
01098     cpl_free(profile);
01099     cpl_free(buffer);
01100   }
01101   else {
01102 
01103     /*
01104      * Add sky estimation for the simple aperture extraction.
01105         if (kdata[i + j * specLen] > 0.0)
01106      */
01107 
01108     for (i = 0; i < specLen; i++)
01109       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01110           ekdata[i] += kdata[i + j * specLen];
01111 
01112     /*
01113      * Add error estimation for the simple aperture extraction.
01114      */
01115 
01116     for (i = 0; i < specLen; i++)
01117       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01118 
01119   }
01120 
01121 }
01122 
01123 
01170 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01171                                  cpl_table *ids, cpl_table *crv, 
01172                                  double reference)
01173 {
01174     const char *func = "mos_global_distortion";
01175 
01176     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01177 
01178     cpl_table      *global = NULL;
01179     cpl_table      *coeff;
01180     cpl_table      *dummy;
01181     cpl_vector     *ci;
01182     cpl_vector     *xmask;
01183     cpl_vector     *ymask;
01184     cpl_bivector   *mask;
01185     cpl_vector     *xccd;
01186     cpl_vector     *yccd;
01187     cpl_bivector   *ccd;
01188     cpl_polynomial *poly;
01189     double         *xtop;
01190     double         *ytop;
01191     double         *xbottom;
01192     double         *ybottom;
01193     double         *mxtop;
01194     double         *mytop;
01195     double         *mxbottom;
01196     double         *mybottom;
01197     int            *position;
01198     int            *length;
01199     int            *slit_id;
01200     int            *mslit_id;
01201     int             nslits, nmaskslits, npoints;
01202     int             order;
01203     int             i, j;
01204     int             minslit = 6;    // 12;
01205 
01206 
01207 /* *+
01208 printf("error1: %s\n", cpl_error_get_message());
01209 +* */
01210     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01211         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01212         return NULL;
01213     }
01214 /* *+
01215 printf("error1a: %s\n", cpl_error_get_message());
01216 +* */
01217 
01218     nslits = cpl_table_get_nrow(slits);
01219 /* *+
01220 printf("error1b: %s\n", cpl_error_get_message());
01221 +* */
01222 
01223     if (nslits < minslit) {
01224         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01225                         "distortion model determination", nslits, minslit);
01226         return NULL;
01227     }
01228 /* *+
01229 printf("error1c: %s\n", cpl_error_get_message());
01230 +* */
01231 
01232     nmaskslits = cpl_table_get_nrow(maskslits);
01233 
01234     length   = cpl_table_get_data_int(slits, "length");
01235     position = cpl_table_get_data_int(slits, "position");
01236     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01237     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01238     xtop     = cpl_table_get_data_double(slits, "xtop");
01239     ytop     = cpl_table_get_data_double(slits, "ytop");
01240     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01241     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01242     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01243     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01244     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01245     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01246 
01247 
01248     /*
01249      * Global IDS
01250      */
01251 
01252     coeff = cpl_table_new(nslits);
01253     cpl_table_copy_structure(coeff, ids);
01254     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01255     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01256     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01257     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01258 
01259 /* *+
01260 printf("error2: %s\n", cpl_error_get_message());
01261 +* */
01262     for (i = 0; i < nslits; i++) {
01263         for (j = 0; j < nmaskslits; j++) {
01264             if (slit_id[i] == mslit_id[j]) {
01265                 cpl_table_set_double(coeff, "xmask", i,
01266                                      (mxtop[j] + mxbottom[j]) / 2);
01267                 cpl_table_set_double(coeff, "ymask", i,
01268                                      (mytop[j] + mybottom[j]) / 2);
01269             }
01270         }
01271     }
01272 
01273     if (cpl_table_has_invalid(coeff, "xmask")) {
01274         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01275         cpl_table_delete(coeff);
01276         return NULL;
01277     }
01278 
01279     for (i = 0; i < nslits; i++) {
01280         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01281         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01282     }
01283 
01284 /* *+
01285 printf("error3: %s\n", cpl_error_get_message());
01286 +* */
01287     for (i = 0; i < nslits; i++) {
01288 
01289         if (length[i] == 0)
01290             continue;
01291 
01292         cpl_table_and_selected_window(ids, position[i], length[i]);
01293         dummy = cpl_table_extract_selected(ids);
01294         for (j = 0; j < 6; j++) {
01295             if (cpl_table_has_column(dummy, clab[j])) {
01296                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01297                     cpl_table_set_double(coeff, clab[j], i, 
01298                          cpl_table_get_column_median(dummy, clab[j]));
01299                 }
01300             }
01301         }
01302 
01303         cpl_table_delete(dummy);
01304         cpl_table_select_all(ids);
01305             
01306     }
01307 
01308 /* *+
01309 printf("error4: %s\n", cpl_error_get_message());
01310 +* */
01311     for (j = 0; j < 6; j++) {
01312         if (cpl_table_has_column(coeff, clab[j])) {
01313             cpl_table_and_selected_invalid(coeff, clab[j]);
01314 
01315             if (cpl_table_not_selected(coeff))
01316                 dummy = cpl_table_extract_selected(coeff);
01317             else
01318                 break;
01319 
01320             npoints = cpl_table_get_nrow(dummy);
01321 
01322             if (npoints >= 6) {
01323 
01324                 if (npoints >= 12)
01325                     order = 2;
01326                 else
01327                     order = 1;
01328                    
01329                 ci = cpl_vector_wrap(npoints,
01330                                      cpl_table_get_data_double(dummy, clab[j]));
01331                 if (j) {
01332                     xccd = cpl_vector_wrap(npoints,
01333                                      cpl_table_get_data_double(dummy, "xccd"));
01334                     yccd = cpl_vector_wrap(npoints,
01335                                      cpl_table_get_data_double(dummy, "yccd"));
01336                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01337 
01338 /* %%% */
01339                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01340 
01341                     cpl_bivector_unwrap_vectors(ccd);
01342                     cpl_vector_unwrap(xccd);
01343                     cpl_vector_unwrap(yccd);
01344                     cpl_vector_unwrap(ci);
01345                 }
01346                 else {
01347                     xmask = cpl_vector_wrap(npoints,
01348                                      cpl_table_get_data_double(dummy, "xmask"));
01349                     ymask = cpl_vector_wrap(npoints,
01350                                      cpl_table_get_data_double(dummy, "ymask"));
01351                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01352 
01353 /* %%% */
01354                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01355 
01356                     cpl_bivector_unwrap_vectors(mask);
01357                     cpl_vector_unwrap(xmask);
01358                     cpl_vector_unwrap(ymask);
01359                     cpl_vector_unwrap(ci);
01360                 }
01361             }
01362             else {
01363                 int p[2] = {0, 0};
01364                 poly = cpl_polynomial_new(2);
01365                 cpl_polynomial_set_coeff(poly, p, 
01366                                cpl_table_get_column_median(dummy, clab[j]));
01367             }
01368 
01369             cpl_table_delete(dummy);
01370 
01371             global = write_global_distortion(global, j, poly);
01372 
01373             cpl_polynomial_delete(poly);
01374 
01375             cpl_table_select_all(coeff);
01376         }
01377     }
01378 
01379 /* *+
01380 printf("error5: %s\n", cpl_error_get_message());
01381 +* */
01382     cpl_table_delete(coeff);
01383 /* *+
01384 printf("error6: %s\n", cpl_error_get_message());
01385 +* */
01386 
01387 
01388     /*
01389      * Add model's reference wavelength
01390      */
01391 
01392     cpl_table_set_double(global, "a00", 6, reference);
01393 
01394 
01395     /*
01396      * Global curvature model
01397      */
01398 
01399     coeff = cpl_table_duplicate(crv);
01400     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01401     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01402     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01403     npoints = cpl_table_get_nrow(coeff);
01404 
01405 /* *+
01406 printf("error7: %s\n", cpl_error_get_message());
01407 +* */
01408     for (i = 0; i < npoints; i++) {
01409         for (j = 0; j < nmaskslits; j++) {
01410             if (slit_id[i] == mslit_id[j]) {
01411                 if (i%2) {
01412                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01413                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01414                 }
01415                 else {
01416                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01417                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01418                 }
01419             }
01420         }
01421     }
01422 
01423 /* *+
01424 printf("error8: %s\n", cpl_error_get_message());
01425 +* */
01426     if (cpl_table_has_invalid(coeff, "xmask")) {
01427         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01428         cpl_table_delete(coeff);
01429         return NULL;
01430     }
01431 
01432 /* *+
01433 printf("error9: %s\n", cpl_error_get_message());
01434 +* */
01435     for (j = 0; j < 3; j++) {
01436         if (cpl_table_has_column(coeff, clab[j])) {
01437             cpl_table_and_selected_invalid(coeff, clab[j]);
01438 
01439             if (cpl_table_not_selected(coeff))
01440                 dummy = cpl_table_extract_selected(coeff);
01441             else
01442                 break;
01443 
01444             npoints = cpl_table_get_nrow(dummy);
01445 
01446             if (npoints >= 6) {
01447 
01448                 if (npoints >= 12)
01449                     order = 2;
01450                 else
01451                     order = 1;
01452 
01453                 ci = cpl_vector_wrap(npoints,
01454                                      cpl_table_get_data_double(dummy, clab[j]));
01455                 xmask = cpl_vector_wrap(npoints,
01456                                      cpl_table_get_data_double(dummy, "xmask"));
01457                 ymask = cpl_vector_wrap(npoints,
01458                                      cpl_table_get_data_double(dummy, "ymask"));
01459                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01460 
01461                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01462 
01463                 cpl_bivector_unwrap_vectors(mask);
01464                 cpl_vector_unwrap(ci);
01465                 cpl_vector_unwrap(xmask);
01466                 cpl_vector_unwrap(ymask);
01467             }
01468             else {
01469                 int p[2] = {0, 0};
01470                 poly = cpl_polynomial_new(2);
01471                 cpl_polynomial_set_coeff(poly, p,
01472                                cpl_table_get_column_median(dummy, clab[j]));
01473             }
01474 
01475             cpl_table_delete(dummy);
01476 
01477             global = write_global_distortion(global, j + 7, poly);
01478 
01479             cpl_polynomial_delete(poly);
01480             cpl_table_select_all(coeff);
01481         }
01482     }
01483 
01484 /* *+
01485 printf("error10: %s\n", cpl_error_get_message());
01486 +* */
01487     cpl_table_delete(coeff);
01488 /* *+
01489 printf("error11: %s\n", cpl_error_get_message());
01490 +* */
01491 
01492     return global;
01493 
01494 }
01495 
01496 
01534 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01535                                    int ysize)
01536 {
01537     const char *func = "mos_build_slit_location";
01538 
01539     cpl_propertylist *sort_col;
01540     cpl_polynomial   *ids0;
01541     cpl_polynomial   *crv[3];
01542     cpl_polynomial   *loc_crv;
01543     cpl_vector       *point;
01544     cpl_table        *slits;
01545     int               nslits;
01546     int              *slit_id;
01547     double           *dpoint;
01548     double           *xtop;
01549     double           *ytop;
01550     double           *xbottom;
01551     double           *ybottom;
01552     double           *mxtop;
01553     double           *mytop;
01554     double           *mxbottom;
01555     double           *mybottom;
01556     int               i, j;
01557 
01558 
01559     if (global == NULL || maskslits == NULL) {
01560         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01561         return NULL;
01562     }
01563 
01564     nslits   = cpl_table_get_nrow(maskslits);
01565     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01566     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01567     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01568     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01569     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01570 
01571     slits = cpl_table_duplicate(maskslits);
01572 
01573     xtop    = cpl_table_get_data_double(slits, "xtop");
01574     ytop    = cpl_table_get_data_double(slits, "ytop");
01575     xbottom = cpl_table_get_data_double(slits, "xbottom");
01576     ybottom = cpl_table_get_data_double(slits, "ybottom");
01577 
01578     ids0 = read_global_distortion(global, 0);
01579     crv[0] = read_global_distortion(global, 7);
01580     crv[1] = read_global_distortion(global, 8);
01581     crv[2] = read_global_distortion(global, 9);
01582 
01583     loc_crv = cpl_polynomial_new(1);
01584 
01585     point = cpl_vector_new(2);
01586     dpoint = cpl_vector_get_data(point);
01587 
01588     for (i = 0; i < nslits; i++) {
01589         dpoint[0] = mxtop[i];
01590         dpoint[1] = mytop[i];
01591 
01592         xtop[i] = cpl_polynomial_eval(ids0, point);
01593 
01594         for (j = 0; j < 3; j++)
01595             if (crv[j])
01596                 cpl_polynomial_set_coeff(loc_crv, &j, 
01597                                          cpl_polynomial_eval(crv[j], point));
01598 
01599         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01600 
01601         dpoint[0] = mxbottom[i];
01602         dpoint[1] = mybottom[i];
01603         xbottom[i] = cpl_polynomial_eval(ids0, point);
01604 
01605         for (j = 0; j < 3; j++)
01606             if (crv[j])
01607                 cpl_polynomial_set_coeff(loc_crv, &j,
01608                                          cpl_polynomial_eval(crv[j], point));
01609 
01610         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01611     }
01612 
01613     cpl_vector_delete(point);
01614     cpl_polynomial_delete(ids0);
01615     cpl_polynomial_delete(loc_crv);
01616     for (j = 0; j < 3; j++)
01617         cpl_polynomial_delete(crv[j]);
01618 
01619     sort_col = cpl_propertylist_new();
01620     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01621     cpl_table_sort(slits, sort_col);
01622     cpl_table_sort(maskslits, sort_col);
01623     cpl_propertylist_delete(sort_col);
01624 
01625     /*
01626      * Eliminate slits which are _entirely_ outside the CCD
01627      */
01628 
01629     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01630     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01631     cpl_table_erase_selected(slits);
01632 
01633     nslits = cpl_table_get_nrow(slits);
01634 
01635     if (nslits == 0) {
01636         cpl_msg_warning(func, "No slits found on the CCD");
01637         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01638         cpl_table_delete(slits);
01639         return NULL;
01640     }
01641 
01642     if (nslits > 1)
01643         cpl_msg_info(func, "Slit location: %d slits are entirely or partially "
01644                      "contained in CCD", nslits);
01645     else
01646         cpl_msg_info(func, "Slit location: %d slit is entirely or partially "
01647                      "contained in CCD", nslits);
01648 
01649     return slits;
01650 
01651 }
01652 
01653 
01680 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01681                                 cpl_table *slits)
01682 {
01683     const char *func = "mos_build_curv_coeff";
01684 
01685     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01686                                                  /* Max order is 5 */
01687 
01688     cpl_polynomial *crv[3];
01689     cpl_vector     *point;
01690     cpl_table      *polytraces;
01691     double         *dpoint;
01692     double         *xtop;
01693     double         *ytop;
01694     double         *xbottom;
01695     double         *ybottom;
01696     int            *slit_id;
01697     int            *valid_id;
01698     int             nslits, nvalid;
01699     int             found;
01700     int             i, j, k;
01701 
01702 
01703     if (global == NULL || slits == NULL || maskslits == NULL) {
01704         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01705         return NULL;
01706     }
01707 
01708     nslits  = cpl_table_get_nrow(maskslits);
01709     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01710     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01711     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01712     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01713     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01714 
01715     polytraces = cpl_table_new(2*nslits);
01716     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01717     for (i = 0; i < 3; i++)
01718         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01719 
01720     crv[0] = read_global_distortion(global, 7);
01721     crv[1] = read_global_distortion(global, 8);
01722     crv[2] = read_global_distortion(global, 9);
01723 
01724     point = cpl_vector_new(2);
01725     dpoint = cpl_vector_get_data(point);
01726 
01727     for (i = 0; i < nslits; i++) {
01728         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01729 
01730             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01731 
01732             if (j) {
01733                 dpoint[0] = xbottom[i];
01734                 dpoint[1] = ybottom[i];                
01735             }
01736             else {
01737                 dpoint[0] = xtop[i];
01738                 dpoint[1] = ytop[i];                
01739             }
01740 
01741             for (k = 0; k < 3; k++)
01742                 if (crv[j])
01743                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01744                                          cpl_polynomial_eval(crv[k], point));
01745         }
01746     }
01747 
01748     cpl_vector_delete(point);
01749     for (j = 0; j < 3; j++)
01750         cpl_polynomial_delete(crv[j]);
01751 
01752     /*
01753      * Eliminate slits which are _entirely_ outside the CCD
01754      */
01755  
01756     nvalid  = cpl_table_get_nrow(slits);
01757     valid_id = cpl_table_get_data_int(slits, "slit_id");
01758     cpl_table_unselect_all(polytraces);
01759     for (i = 0; i < nslits; i++) {
01760         found = 0;
01761         for (j = 0; j < nvalid; j++) {
01762             if (slit_id[i] == valid_id[j]) {
01763                 found = 1;
01764                 break;
01765             }
01766         }
01767         if (!found) {
01768             cpl_table_select_row(polytraces, 2*i);
01769             cpl_table_select_row(polytraces, 2*i + 1);
01770         }
01771     }
01772     cpl_table_erase_selected(polytraces);
01773  
01774     nslits = cpl_table_get_nrow(polytraces);
01775 
01776     if (nslits == 0) {
01777         cpl_msg_warning(func, "No slits found on the CCD");
01778         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01779         cpl_table_delete(polytraces);
01780         return NULL;
01781     }
01782 
01783     if (nslits > 2) 
01784         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01785                      "partially contained in CCD", nslits / 2);
01786     else
01787         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01788                      "partially contained in CCD", nslits / 2);
01789 
01790     return polytraces;
01791 }
01792 
01793 
01835 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01836 {
01837     const char *func = "mos_build_disp_coeff";
01838 
01839     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01840 
01841     cpl_polynomial *ids[6];
01842     cpl_vector     *point;
01843     cpl_table      *idscoeff;
01844     double         *dpoint;
01845     double         *xtop;
01846     double         *ytop;
01847     double         *xbottom;
01848     double         *ybottom;
01849     int            *position;
01850     int            *length;
01851     int             nslits;
01852     int             nrows;
01853     int             order;
01854     int             ylow, yhig;
01855     int             i, j, k;
01856 
01857 
01858     if (global == NULL || slits == NULL) {
01859         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01860         return NULL;
01861     }
01862     
01863     nslits   = cpl_table_get_nrow(slits);
01864     position = cpl_table_get_data_int(slits, "position");
01865     length   = cpl_table_get_data_int(slits, "length");
01866     xtop     = cpl_table_get_data_double(slits, "xtop");
01867     ytop     = cpl_table_get_data_double(slits, "ytop");
01868     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01869     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01870 
01871     for (i = 0; i < 6; i++)
01872         ids[i] = read_global_distortion(global, i);
01873 
01874     for (i = 0; i < 6; i++)
01875         if (ids[i] == NULL)
01876             break;
01877 
01878     order = i - 1;
01879 
01880     if (order < 1) {
01881         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01882         return NULL;
01883     }
01884 
01885     nrows = 0;
01886     for (i = 0; i < nslits; i++)
01887         nrows += length[i]; 
01888 
01889     idscoeff = cpl_table_new(nrows);
01890 
01891     for (j = 0; j <= order; j++)
01892         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01893 
01894     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01895     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01896     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01897     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01898 
01899     point = cpl_vector_new(2);
01900     dpoint = cpl_vector_get_data(point);
01901 
01902     for (i = 0; i < nslits; i++) {
01903 
01904         if (length[i] == 0)
01905             continue;
01906 
01907         ylow = position[i];
01908         yhig = ylow + length[i];
01909 
01910         for (j = 0; j <= order; j++) {
01911             if (j) {
01912                 for (k = 0; k < length[i]; k++) {
01913                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01914                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01915                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01916                                          cpl_polynomial_eval(ids[j], point));
01917                 }
01918             }
01919             else {
01920                 for (k = 0; k < length[i]; k++) {
01921                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01922                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01923                 }
01924             }
01925         }
01926     }
01927 
01928     cpl_vector_delete(point);
01929     for (j = 0; j < 6; j++)
01930         cpl_polynomial_delete(ids[j]);
01931 
01932     return idscoeff;
01933 
01934 }
01935 
01936 
01959 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01960                             cpl_table *polytraces, double reference, 
01961                             double blue, double red, double dispersion)
01962 {
01963     const char     *func = "mos_subtract_sky";
01964 
01965     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01966                                                  /* Max order is 5 */
01967 
01968     cpl_image      *sky;
01969     cpl_bivector   *list;
01970     cpl_vector     *listx;
01971     cpl_vector     *listy;
01972     cpl_polynomial *polytop;
01973     cpl_polynomial *polybot;
01974     cpl_polynomial *trend;
01975 
01976     int            *slit_id;
01977     double         *dlistx;
01978     double         *dlisty;
01979     float          *sdata;
01980     float          *kdata;
01981     double          top, bot;
01982     int             itop, ibot;
01983     double          coeff;
01984     double          ytop, ybot;
01985     double          m, q, err;
01986     int             npix;
01987 
01988     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
01989     int             nx, ny;
01990     int             nslits;
01991     int            *length;
01992     int             missing_top, missing_bot;
01993     int             order;
01994     int             null;
01995     int             window = 50;  /* Longer slits have polynomial sky model */
01996     int             count;
01997     int             i, j, k;
01998 
01999 
02000     if (science == NULL || slits == NULL || polytraces == NULL) {
02001         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02002         return NULL;
02003     }
02004  
02005     if (dispersion <= 0.0) {
02006         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02007         return NULL;
02008     }
02009 
02010     if (red - blue < dispersion) {
02011         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02012         return NULL;
02013     }
02014 
02015     nx = cpl_image_get_size_x(science);
02016     ny = cpl_image_get_size_y(science);
02017 
02018     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02019 
02020     sdata = cpl_image_get_data(science);
02021     kdata = cpl_image_get_data(sky);
02022 
02023     nslits   = cpl_table_get_nrow(slits);
02024     order    = cpl_table_get_ncol(polytraces) - 2;
02025     length   = cpl_table_get_data_int(slits, "length");
02026     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02027 
02028     /*
02029      * The spatial resampling is performed for a certain number of
02030      * pixels above and below the position of the reference wavelength:
02031      */
02032     
02033     pixel_above = (red - reference) / dispersion;
02034     pixel_below = (reference - blue) / dispersion;
02035 
02036     for (i = 0; i < nslits; i++) {
02037 
02038         if (length[i] == 0)
02039             continue;
02040 
02041         
02042         /*
02043          * Recover from the table of spectral curvature coefficients
02044          * the curvature polynomials.
02045          */
02046 
02047         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02048 
02049         start_pixel = refpixel - pixel_below;
02050         if (start_pixel < 0)
02051             start_pixel = 0;
02052 
02053         end_pixel = refpixel + pixel_above;
02054         if (end_pixel > nx)
02055             end_pixel = nx;
02056 
02057         missing_top = 0;
02058         polytop = cpl_polynomial_new(1);
02059         for (k = 0; k <= order; k++) {
02060             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02061             if (null) {
02062                 cpl_polynomial_delete(polytop);
02063                 missing_top = 1;
02064                 break;
02065             }
02066             cpl_polynomial_set_coeff(polytop, &k, coeff);
02067         }
02068 
02069         missing_bot = 0;
02070         polybot = cpl_polynomial_new(1);
02071         for (k = 0; k <= order; k++) {
02072             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02073             if (null) {
02074                 cpl_polynomial_delete(polybot);
02075                 missing_bot = 1;
02076                 break;
02077             }
02078             cpl_polynomial_set_coeff(polybot, &k, coeff);
02079         }
02080 
02081         if (missing_top && missing_bot) {
02082             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02083                           slit_id[i]);
02084             continue;
02085         }
02086 
02087         /*
02088          * In case just one of the two edges was not traced, the other
02089          * edge curvature model is duplicated and shifted to the other
02090          * end of the slit: better than nothing!
02091          */
02092 
02093         if (missing_top) {
02094             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02095                           "the spectral curvature of the lower edge "
02096                           "is used instead.", slit_id[i]);
02097             polytop = cpl_polynomial_duplicate(polybot);
02098             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02099             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02100             k = 0;
02101             coeff = cpl_polynomial_get_coeff(polybot, &k);
02102             coeff += ytop - ybot;
02103             cpl_polynomial_set_coeff(polytop, &k, coeff);
02104         }
02105 
02106         if (missing_bot) {
02107             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02108                           "the spectral curvature of the upper edge "
02109                           "is used instead.", slit_id[i]);
02110             polybot = cpl_polynomial_duplicate(polytop);
02111             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02112             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02113             k = 0;
02114             coeff = cpl_polynomial_get_coeff(polytop, &k);
02115             coeff -= ytop - ybot;
02116             cpl_polynomial_set_coeff(polybot, &k, coeff);
02117         }
02118 
02119 
02120         /*
02121          * Now read pixel values along spatial direction, and fit them.
02122          */
02123 
02124         for (j = start_pixel; j < end_pixel; j++) {
02125             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02126             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02127             itop = floor(top + 0.5) + 1;
02128             ibot = floor(bot + 0.5);
02129             if (itop > ny)
02130                 itop = ny;
02131             if (ibot < 0)
02132                 ibot = 0;
02133             npix = itop - ibot;
02134             if (npix < 5)
02135                 break;
02136 
02137             list = cpl_bivector_new(npix);
02138             listx = cpl_bivector_get_x(list);
02139             listy = cpl_bivector_get_y(list);
02140             dlistx = cpl_vector_get_data(listx);
02141             dlisty = cpl_vector_get_data(listy);
02142 
02143             for (k = 0; k < npix; k++) {
02144                 dlistx[k] = k;
02145                 dlisty[k] = sdata[j + (ibot + k)*nx];
02146             }
02147 
02148             if (robustLinearFit(list, &q, &m, &err)) {
02149                 cpl_bivector_delete(list);
02150                 continue;
02151             }
02152 
02153             cpl_bivector_delete(list);
02154 
02155             for (k = 0; k < npix; k++) {
02156                 kdata[j + (ibot + k)*nx] = m*k + q;
02157             }
02158 
02159             if (npix > window) {
02160 
02161                 /*
02162                  * Polynomial iteration
02163                  */
02164 
02165                 err = 3*sqrt(err);
02166 
02167                 count = 0;
02168                 for (k = 0; k < npix; k++)
02169                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02170                         count++;
02171 
02172                 if (count < 10)
02173                     continue;
02174 
02175                 list = cpl_bivector_new(count);
02176                 listx = cpl_bivector_get_x(list);
02177                 listy = cpl_bivector_get_y(list);
02178                 dlistx = cpl_vector_get_data(listx);
02179                 dlisty = cpl_vector_get_data(listy);
02180 
02181                 count = 0;
02182                 for (k = 0; k < npix; k++) {
02183                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02184                         dlistx[count] = k;
02185                         dlisty[count] = sdata[j + (ibot + k)*nx];
02186                         count++;
02187                     }
02188                 }
02189 
02190                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02191  
02192                 cpl_bivector_delete(list);
02193 
02194                 err = 3*sqrt(err);
02195 
02196                 count = 0;
02197                 for (k = 0; k < npix; k++)
02198                     if (fabs(sdata[j + (ibot + k)*nx] 
02199                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02200                         count++;
02201 
02202                 if (count < 10) {
02203                     cpl_polynomial_delete(trend);
02204                     continue;
02205                 }
02206 
02207                 list = cpl_bivector_new(count);
02208                 listx = cpl_bivector_get_x(list);
02209                 listy = cpl_bivector_get_y(list);
02210                 dlistx = cpl_vector_get_data(listx);
02211                 dlisty = cpl_vector_get_data(listy);
02212 
02213                 count = 0;
02214                 for (k = 0; k < npix; k++) {
02215                     if (fabs(sdata[j + (ibot + k)*nx] 
02216                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02217                         dlistx[count] = k;
02218                         dlisty[count] = sdata[j + (ibot + k)*nx];
02219                         count++;
02220                     }
02221                 }
02222 
02223                 cpl_polynomial_delete(trend);
02224 
02225                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02226 
02227                 cpl_bivector_delete(list);
02228  
02229                 for (k = 0; k < npix; k++) {
02230                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02231                                                k, NULL);
02232                 }
02233 
02234                 cpl_polynomial_delete(trend);
02235             }
02236         }
02237         cpl_polynomial_delete(polytop);
02238         cpl_polynomial_delete(polybot);
02239     }
02240 
02241     cpl_image_subtract(science, sky);
02242 
02243     return sky;
02244 }
02245 
02246 
02279 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02280                               cpl_table *slits, cpl_table *polytraces, 
02281                               double reference, double blue, double red, 
02282                               double dispersion, int sradius, int polyorder)
02283 {
02284     const char     *func = "mos_normalise_flat";
02285 
02286     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02287                                                  /* Max order is 5 */
02288 
02289     cpl_image      *rectified;
02290     cpl_image      *smo_flat;
02291     cpl_image      *exslit;
02292     cpl_vector     *positions;
02293     cpl_vector     *flux;
02294     cpl_vector     *smo_flux;
02295     cpl_polynomial *trend;
02296     cpl_polynomial *polytop;
02297     cpl_polynomial *polybot;
02298 
02299     int            *slit_id;
02300     float          *p;
02301     float          *data;
02302     double         *fdata;
02303     double         *pdata;
02304     float          *sdata;
02305     float          *xdata;
02306     float          *wdata;
02307     double          vtop, vbot, value;
02308     double          top, bot;
02309     double          coeff;
02310     double          ytop, ybot;
02311     double          ypos;
02312     double          fvalue;
02313     int             ivalue;
02314     int             yint, yprev;
02315     int             npseudo;
02316 
02317     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02318     int             nx, ny, nsubx, nsuby;
02319     int             xlow, ylow, xhig, yhig;
02320     int             nslits;
02321     int            *position;
02322     int            *length;
02323     int             missing_top, missing_bot;
02324     int             order;
02325     int             npoints;
02326     int             uradius;
02327     int             null;
02328     int             i, j, k;
02329 
02330 /*    int             exclude = 5;     Number of excluded pixels at edges */
02331 
02332     /* For debug puposes only: cpl_image      *smo_rectified; */
02333 
02334 
02335     if (flat == NULL || slits == NULL || polytraces == NULL) {
02336         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02337         return NULL;
02338     }
02339  
02340     if (dispersion <= 0.0) {
02341         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02342         return NULL;
02343     }
02344 
02345     if (red - blue < dispersion) {
02346         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02347         return NULL;
02348     }
02349 
02350     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02351                                         blue, red, dispersion, 0, NULL);
02352 
02353     nx = cpl_image_get_size_x(rectified);
02354     ny = cpl_image_get_size_y(rectified);
02355 
02356     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02357                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02358     wdata = cpl_image_get_data(smo_flat);
02359 
02360     nslits   = cpl_table_get_nrow(slits);
02361     order    = cpl_table_get_ncol(polytraces) - 2;
02362     position = cpl_table_get_data_int(slits, "position");
02363     length   = cpl_table_get_data_int(slits, "length");
02364     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02365 
02366     /*
02367      * The spatial resampling is performed for a certain number of
02368      * pixels above and below the position of the reference wavelength:
02369      */
02370     
02371     pixel_above = (red - reference) / dispersion;
02372     pixel_below = (reference - blue) / dispersion;
02373 
02374     xlow = 1;
02375     xhig = nx;
02376     for (i = 0; i < nslits; i++) {
02377 
02378         if (length[i] == 0)
02379             continue;
02380 
02381         /*
02382          * We DON'T write:
02383          *
02384          * ylow = position[i];
02385          * yhig = ylow + length[i];
02386          *
02387          * because the cpl_image pixels are counted from 1, and because in 
02388          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02389          */
02390 
02391         ylow = position[i] + 1;
02392         yhig = ylow + length[i] - 1;
02393 
02394         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02395 
02396         if (polyorder < 0) {
02397 
02398             cpl_image_turn(exslit, -1);   /* For faster memory access */
02399     
02400             nsubx = cpl_image_get_size_x(exslit);
02401             nsuby = cpl_image_get_size_y(exslit);
02402             data = cpl_image_get_data(exslit);
02403             flux = cpl_vector_new(nsubx);
02404 
02405             uradius = nsubx / 2;
02406             if (uradius > sradius)
02407                 uradius = sradius;
02408 
02409             for (j = 0; j < nsuby; j++) {
02410                 fdata = cpl_vector_get_data(flux);
02411                 p = data;
02412                 for (k = 0; k < nsubx; k++)
02413                     *fdata++ = *p++;
02414                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02415                 fdata = cpl_vector_get_data(smo_flux);
02416                 p = data;
02417                 for (k = 0; k < nsubx; k++)
02418                     *p++ = *fdata++;
02419                 cpl_vector_delete(smo_flux);
02420                 data += nsubx;
02421             }
02422 
02423             cpl_vector_delete(flux);
02424 
02425 
02426             /*
02427              * First fit fluxes along the spatial direction with a low-degree
02428              * polynomial (excluding the first and the last pixels, close to
02429              * the edges)
02430              */
02431 /*
02432             if (nsubx-2*exclude > 10) {
02433                 flux = cpl_vector_new(nsubx-2*exclude);
02434                 fdata = cpl_vector_get_data(flux);
02435                 positions = cpl_vector_new(nsubx-2*exclude);
02436                 for (j = 0; j < nsubx-2*exclude; j++)
02437                     cpl_vector_set(positions, j, j+exclude);
02438         
02439                 for (k = 0; k < nsuby; k++) {
02440                     for (j = 0; j < nsubx-2*exclude; j++)
02441                         fdata[j] = data[j+exclude];
02442                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02443                                                          1, NULL);
02444                     for (j = 0; j < nsubx; j++)
02445                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02446                     cpl_polynomial_delete(trend);
02447                     data += nsubx;
02448                 }
02449 
02450                 cpl_vector_delete(flux);
02451                 cpl_vector_delete(positions);
02452             }
02453 */
02454 
02455             /*
02456              * Now smooth along the dispersion direction 
02457              */
02458 
02459             cpl_image_turn(exslit, 1);   /* For faster memory access */
02460             nsubx = cpl_image_get_size_x(exslit);
02461             nsuby = cpl_image_get_size_y(exslit);
02462             data = cpl_image_get_data(exslit);
02463 
02464             for (j = 0; j < nsuby; j++) {
02465                 flux = cpl_vector_new(nsubx);
02466                 fdata = cpl_vector_get_data(flux);
02467                 p = data;
02468                 for (k = 0; k < nsubx; k++)
02469                     *fdata++ = *p++;
02470                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02471                 cpl_vector_delete(flux);
02472                 fdata = cpl_vector_get_data(smo_flux);
02473                 p = data;
02474                 for (k = 0; k < nsubx; k++)
02475                     *p++ = *fdata++;
02476                 cpl_vector_delete(smo_flux);
02477                 data += nsubx;
02478             }
02479         }
02480         else {
02481 
02482             /*
02483              * Fit with a polynomial the flat field trend row by row.
02484              */
02485 
02486             nsubx = cpl_image_get_size_x(exslit);
02487             nsuby = cpl_image_get_size_y(exslit);
02488             data = cpl_image_get_data(exslit);
02489 
02490             for (j = 0; j < nsuby; j++) {
02491 
02492                 /*
02493                  * First get the size of the vectors to allocate:
02494                  */
02495 
02496                 npoints = 0;
02497                 p = data + j*nsubx;
02498                 for (k = 0; k < nsubx; k++)
02499                     if (p[k] > 1.0)
02500                         npoints++;
02501 
02502                 if (npoints > polyorder + 1) {
02503 
02504                     /*
02505                      * Fill the vectors for the fitting
02506                      */
02507 
02508                     flux = cpl_vector_new(npoints);
02509                     fdata = cpl_vector_get_data(flux);
02510                     positions = cpl_vector_new(npoints);
02511                     pdata = cpl_vector_get_data(positions);
02512 
02513                     npoints = 0;
02514                     p = data + j*nsubx;
02515                     for (k = 0; k < nsubx; k++) {
02516                         if (p[k] > 1.0) {
02517                             fdata[npoints] = p[k];
02518                             pdata[npoints] = k;
02519                             npoints++;
02520                         }
02521                     }
02522     
02523                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02524                                                          polyorder, NULL);
02525 
02526                     cpl_vector_delete(flux);
02527                     cpl_vector_delete(positions);
02528 
02529                     if (trend) {
02530                         p = data + j*nsubx;
02531                         for (k = 0; k < nsubx; k++)
02532                             if (p[k] > 1.0)
02533                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02534                         cpl_polynomial_delete(trend);
02535                     }
02536                     else {
02537                         cpl_msg_warning(func, "Invalid flat field flux fit "
02538                                         "(ignored)");
02539                     }
02540                 }
02541             }
02542         }
02543 
02544         
02545         /*
02546          * Recover from the table of spectral curvature coefficients
02547          * the curvature polynomials.
02548          */
02549 
02550         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02551 
02552         start_pixel = refpixel - pixel_below;
02553         if (start_pixel < 0)
02554             start_pixel = 0;
02555 
02556         end_pixel = refpixel + pixel_above;
02557         if (end_pixel > nx)
02558             end_pixel = nx;
02559 
02560         missing_top = 0;
02561         polytop = cpl_polynomial_new(1);
02562         for (k = 0; k <= order; k++) {
02563             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02564             if (null) {
02565                 cpl_polynomial_delete(polytop);
02566                 missing_top = 1;
02567                 break;
02568             }
02569             cpl_polynomial_set_coeff(polytop, &k, coeff);
02570         }
02571 
02572         missing_bot = 0;
02573         polybot = cpl_polynomial_new(1);
02574         for (k = 0; k <= order; k++) {
02575             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02576             if (null) {
02577                 cpl_polynomial_delete(polybot);
02578                 missing_bot = 1;
02579                 break;
02580             }
02581             cpl_polynomial_set_coeff(polybot, &k, coeff);
02582         }
02583 
02584         if (missing_top && missing_bot) {
02585             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02586                           slit_id[i]);
02587             continue;
02588         }
02589 
02590         /*
02591          * In case just one of the two edges was not traced, the other
02592          * edge curvature model is duplicated and shifted to the other
02593          * end of the slit: better than nothing!
02594          */
02595 
02596         if (missing_top) {
02597             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02598                           "the spectral curvature of the lower edge "
02599                           "is used instead.", slit_id[i]);
02600             polytop = cpl_polynomial_duplicate(polybot);
02601             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02602             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02603             k = 0;
02604             coeff = cpl_polynomial_get_coeff(polybot, &k);
02605             coeff += ytop - ybot;
02606             cpl_polynomial_set_coeff(polytop, &k, coeff);
02607         }
02608 
02609         if (missing_bot) {
02610             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02611                           "the spectral curvature of the upper edge "
02612                           "is used instead.", slit_id[i]);
02613             polybot = cpl_polynomial_duplicate(polytop);
02614             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02615             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02616             k = 0;
02617             coeff = cpl_polynomial_get_coeff(polytop, &k);
02618             coeff -= ytop - ybot;
02619             cpl_polynomial_set_coeff(polybot, &k, coeff);
02620         }
02621 
02622 
02623         /*
02624          * Now map smoothed image to CCD.
02625          * Note that the npseudo value related to this slit is equal
02626          * to the number of spatial pseudo-pixels decreased by 1
02627          * (compare with function mos_spatial_calibration()).
02628          */
02629 
02630         nx = cpl_image_get_size_x(flat);
02631         ny = cpl_image_get_size_y(flat);
02632 
02633         sdata = cpl_image_get_data(spatial);
02634         xdata = cpl_image_get_data(exslit);
02635         npseudo = cpl_image_get_size_y(exslit) - 1;
02636 
02637         /*
02638          * Write interpolated smoothed values to CCD image
02639          */
02640 
02641         for (j = start_pixel; j < end_pixel; j++) {
02642             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02643             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02644             for (k = 0; k <= npseudo; k++) {
02645                 ypos = top - k*(top-bot)/npseudo;
02646                 yint = ypos;
02647 
02648                 /*
02649                  * The line:
02650                  *     value = sdata[j + nx*yint];
02651                  * should be equivalent to:
02652                  *     value = npseudo*(top-yint)/(top-bot);
02653                  */
02654 
02655                 if (yint < 0 || yint >= ny-1) {
02656                     yprev = yint;
02657                     continue;
02658                 }
02659 
02660                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02661                 ivalue = value;               /* Nearest spatial pixels:   */
02662                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02663                 if (ivalue < npseudo && ivalue >= 0) {
02664                     vtop = xdata[j + nx*(npseudo-ivalue)];
02665                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02666                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02667 
02668                     if (k) {
02669 
02670                         /*
02671                          * This is added to recover lost pixels on
02672                          * the CCD image (pixels are lost because
02673                          * the CCD pixels are less than npseudo+1).
02674                          */
02675 
02676                         if (yprev - yint > 1) {
02677                             value = sdata[j + nx*(yint+1)];
02678                             ivalue = value;
02679                             fvalue = value - ivalue;
02680                             if (ivalue < npseudo && ivalue >= 0) {
02681                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02682                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02683                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02684                                                        + vbot*fvalue;
02685                             }
02686                         }
02687                     }
02688                 }
02689                 yprev = yint;
02690             }
02691         }
02692         cpl_polynomial_delete(polytop);
02693         cpl_polynomial_delete(polybot);
02694         cpl_image_delete(exslit);
02695     }
02696 
02697     cpl_image_delete(rectified);
02698 
02699     cpl_image_divide(flat, smo_flat);
02700 
02701     return smo_flat;
02702 }
02703 
02704 
02729 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02730                                   int polyorder)
02731 {
02732     const char     *func = "mos_normalise_longflat";
02733 
02734     cpl_image      *smo_flat;
02735     cpl_image      *profile;
02736     cpl_vector     *flux;
02737     cpl_vector     *smo_flux;
02738     cpl_vector     *positions;
02739     cpl_polynomial *trend;
02740 
02741     float          *level;
02742     float          *p;
02743     float          *data;
02744     double         *fdata;
02745     double         *pdata;
02746 
02747     int             nx, ny;
02748     int             npoints;
02749     int             i, j;
02750 
02751 
02752     if (flat == NULL) {
02753         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02754         return NULL;
02755     }
02756  
02757     if (sradius < 1 || dradius < 1) {
02758         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02759         return NULL;
02760     }
02761 
02762     smo_flat = cpl_image_duplicate(flat);
02763 
02764     if (polyorder < 0) {
02765 
02766         /*
02767          * First smooth along the spatial direction
02768          */
02769 
02770         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02771 
02772         nx = cpl_image_get_size_x(smo_flat);
02773         ny = cpl_image_get_size_y(smo_flat);
02774         data = cpl_image_get_data(smo_flat);
02775     
02776         for (i = 0; i < ny; i++) {
02777             flux = cpl_vector_new(nx);
02778             fdata = cpl_vector_get_data(flux);
02779             p = data;
02780             for (j = 0; j < nx; j++)
02781                 *fdata++ = *p++;
02782             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02783             cpl_vector_delete(flux);
02784             fdata = cpl_vector_get_data(smo_flux);
02785             p = data;
02786             for (j = 0; j < nx; j++)
02787                 *p++ = *fdata++;
02788             cpl_vector_delete(smo_flux);
02789             data += nx;
02790         }
02791 
02792         /*
02793          * Second smooth along the dispersion direction
02794          */
02795 
02796         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02797 
02798         nx = cpl_image_get_size_x(smo_flat);
02799         ny = cpl_image_get_size_y(smo_flat);
02800         data = cpl_image_get_data(smo_flat);
02801 
02802         for (i = 0; i < ny; i++) {
02803             flux = cpl_vector_new(nx);
02804             fdata = cpl_vector_get_data(flux);
02805             p = data;
02806             for (j = 0; j < nx; j++)
02807                 *fdata++ = *p++;
02808             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02809             cpl_vector_delete(flux);
02810             fdata = cpl_vector_get_data(smo_flux);
02811             p = data;
02812             for (j = 0; j < nx; j++)
02813                 *p++ = *fdata++;
02814             cpl_vector_delete(smo_flux);
02815             data += nx;
02816         }
02817     }
02818     else {
02819 
02820         /*
02821          * Fit with a polynomial the flat field trend column by column.
02822          */
02823 
02824         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02825 
02826         nx = cpl_image_get_size_x(smo_flat);
02827         ny = cpl_image_get_size_y(smo_flat);
02828         data = cpl_image_get_data(smo_flat);
02829 
02830         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02831         level = cpl_image_get_data(profile);
02832 
02833         for (i = 0; i < ny; i++) {
02834 
02835             /*
02836              * First get the size of the vectors to allocate:
02837              * eliminate from fit any value more than 20% away
02838              * from median level in current column.
02839              */
02840 
02841             npoints = 0;
02842             p = data + i*nx;
02843             for (j = 0; j < nx; j++)
02844                 if (fabs(p[j]/level[i] - 1) < 0.20)
02845                     npoints++;
02846 
02847             if (npoints > polyorder + 1) {
02848 
02849                 /*
02850                  * Fill the vectors for the fitting
02851                  */
02852 
02853                 flux = cpl_vector_new(npoints);
02854                 fdata = cpl_vector_get_data(flux);
02855                 positions = cpl_vector_new(npoints);
02856                 pdata = cpl_vector_get_data(positions);
02857 
02858                 npoints = 0;
02859                 p = data + i*nx;
02860                 for (j = 0; j < nx; j++) {
02861                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02862                         fdata[npoints] = p[j];
02863                         pdata[npoints] = j;
02864                         npoints++;
02865                     }
02866                 }
02867     
02868                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02869                                                      polyorder, NULL);
02870 
02871                 cpl_vector_delete(flux);
02872                 cpl_vector_delete(positions);
02873 
02874                 if (trend) {
02875                     p = data + i*nx;
02876                     for (j = 0; j < nx; j++)
02877                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02878                     cpl_polynomial_delete(trend);
02879                 }
02880                 else {
02881                     cpl_msg_warning(func, 
02882                                     "Invalid flat field flux fit (ignored)");
02883                 }
02884             }
02885         }
02886 
02887         cpl_image_delete(profile);
02888         cpl_image_turn(smo_flat, 1);
02889 
02890     }
02891 
02892     cpl_image_divide(flat, smo_flat);
02893 
02894     return smo_flat;
02895 }
02896 
02897 
02922 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
02923                                          cpl_image *wavemap, int mode)
02924 {
02925     const char *func = "mos_interpolate_wavecalib";
02926 
02927     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02928                                                  /* Max order is 5 */
02929 
02930     cpl_vector     *wave;
02931     cpl_vector     *positions;
02932     cpl_polynomial *trend;
02933 
02934     float          *p;
02935     float          *data;
02936     double         *wdata;
02937     double         *pdata;
02938 
02939     double          c;
02940     double          mse, ksigma;
02941 
02942     int             order;
02943     int             nrows, first_row, last_row;
02944     int             nx, ny;
02945     int             npoints, rpoints;
02946     int             null;
02947     int             i, j, k;
02948 
02949     int             polyorder = 4;  /* Candidate input argument */
02950 
02951 
02952     if (idscoeff == NULL)
02953         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02954 
02955     if (mode < 0 || mode > 2)
02956         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02957 
02958     if (mode == 0)
02959         return CPL_ERROR_NONE;
02960 
02961     if (wavemap) {
02962 
02963         /*
02964          * Fit with a polynomial the wavelength trend column by column.
02965          */
02966 
02967         cpl_image_turn(wavemap, -1);   /* For faster memory access */
02968 
02969         nx = cpl_image_get_size_x(wavemap);
02970         ny = cpl_image_get_size_y(wavemap);
02971         data = cpl_image_get_data(wavemap);
02972 
02973         for (i = 0; i < ny; i++) {
02974 
02975             /*
02976              * First get the size of the vectors to allocate:
02977              * eliminate from fit any value with "impossible" wavelength.
02978              */
02979 
02980             npoints = 0;
02981             p = data + i*nx;
02982             for (j = 0; j < nx; j++)
02983                 if (p[j] > 1.0)
02984                     npoints++;
02985 
02986             if (npoints > polyorder + 1) {
02987 
02988                 /*
02989                  * Fill the vectors for the fitting
02990                  */
02991 
02992                 wave = cpl_vector_new(npoints);
02993                 wdata = cpl_vector_get_data(wave);
02994                 positions = cpl_vector_new(npoints);
02995                 pdata = cpl_vector_get_data(positions);
02996 
02997                 npoints = 0;
02998                 p = data + i*nx;
02999                 for (j = 0; j < nx; j++) {
03000                     if (p[j] > 1.0) {
03001                         wdata[npoints] = p[j];
03002                         pdata[npoints] = j;
03003                         npoints++;
03004                     }
03005                 }
03006     
03007                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03008                                                      polyorder, &mse);
03009 
03010                 ksigma = 3*sqrt(mse);
03011 
03012                 cpl_vector_delete(wave);
03013                 cpl_vector_delete(positions);
03014 
03015                 if (trend) {
03016 
03017                     /*
03018                      * Apply 3-sigma rejection
03019                      */
03020 
03021                     rpoints = 0;
03022                     p = data + i*nx;
03023                     for (j = 0; j < nx; j++)
03024                         if (p[j] > 1.0)
03025                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03026                                                     - p[j]) < ksigma)
03027                                 rpoints++;
03028 
03029                     if (rpoints < npoints && rpoints > polyorder + 1) {
03030 
03031                         wave = cpl_vector_new(rpoints);
03032                         wdata = cpl_vector_get_data(wave);
03033                         positions = cpl_vector_new(rpoints);
03034                         pdata = cpl_vector_get_data(positions);
03035 
03036                         npoints = 0;
03037                         p = data + i*nx;
03038                         for (j = 0; j < nx; j++) {
03039                             if (p[j] > 1.0) {
03040                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03041                                                                 j, NULL) - p[j])
03042                                                                 < ksigma) {
03043                                     wdata[npoints] = p[j];
03044                                     pdata[npoints] = j;
03045                                     npoints++;
03046                                 }
03047                             }
03048                         }
03049         
03050                         cpl_polynomial_delete(trend);
03051                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03052                                                              polyorder, NULL);
03053 
03054                         cpl_vector_delete(wave);
03055                         cpl_vector_delete(positions);
03056                     }
03057                 }
03058 
03059                 if (trend) {
03060                     p = data + i*nx;
03061                     if (mode == 1) {
03062                         for (j = 0; j < nx; j++)
03063                             if (p[j] < 1.0)
03064                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03065                     }
03066                     else if (mode == 2) {
03067                         for (j = 0; j < nx; j++)
03068                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03069                     }
03070                     cpl_polynomial_delete(trend);
03071                 }
03072                 else {
03073                     cpl_msg_warning(func, 
03074                                     "Invalid wavelength field fit (ignored)");
03075                 }
03076             }
03077     
03078         }
03079 
03080         cpl_image_turn(wavemap, 1);
03081 
03082     }
03083 
03084 
03085     /*
03086      * Interpolating the IDS coefficients
03087      */
03088 
03089     nrows = cpl_table_get_nrow(idscoeff);
03090 
03091     order = 0;
03092     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03093         ++order;
03094     --order;
03095 
03096     first_row = 0;
03097     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03098         first_row++;
03099 
03100     last_row = nrows - 1;
03101     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03102         last_row--;
03103 
03104     for (k = 0; k <= order; k++) {
03105 
03106         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03107         wave = cpl_vector_new(npoints);
03108         wdata = cpl_vector_get_data(wave);
03109         positions = cpl_vector_new(npoints);
03110         pdata = cpl_vector_get_data(positions);
03111 
03112         npoints = 0;
03113         for (i = first_row; i <= last_row; i++) {
03114             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03115             if (null == 0) {
03116                 wdata[npoints] = c;
03117                 pdata[npoints] = i;
03118                 npoints++;
03119             }
03120         }
03121 
03122         trend = cpl_polynomial_fit_1d_create(positions, wave, 2, &mse);
03123 
03124         ksigma = 3*sqrt(mse);
03125 
03126         cpl_vector_delete(wave);
03127         cpl_vector_delete(positions);
03128 
03129         /*
03130          * Iteration
03131          */
03132 
03133         if (trend) {
03134             rpoints = 0;
03135             for (i = first_row; i <= last_row; i++) {
03136                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03137                 if (null == 0) {
03138                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03139                                                                  < ksigma) {
03140                         rpoints++;
03141                     }
03142                 }
03143             }
03144 
03145             if (rpoints > 0 && rpoints < npoints) {
03146                 cpl_msg_debug(func, "%d points rejected from "
03147                               "wavelength calibration fit", 
03148                               npoints - rpoints);
03149 
03150                 wave = cpl_vector_new(rpoints);
03151                 wdata = cpl_vector_get_data(wave);
03152                 positions = cpl_vector_new(rpoints);
03153                 pdata = cpl_vector_get_data(positions);
03154 
03155                 npoints = 0;
03156                 for (i = first_row; i <= last_row; i++) {
03157                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03158                     if (null == 0) {
03159                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03160                                                                  < ksigma) {
03161                             wdata[npoints] = c;
03162                             pdata[npoints] = i;
03163                             npoints++;
03164                         }
03165                     }
03166                 }
03167 
03168                 if (npoints) {
03169                     cpl_polynomial_delete(trend);
03170                     trend = cpl_polynomial_fit_1d_create(positions, 
03171                                                          wave, 2, NULL);
03172                 }
03173 
03174                 cpl_vector_delete(wave);
03175                 cpl_vector_delete(positions);
03176 
03177             }
03178         }
03179 
03180         if (trend) {
03181             for (i = first_row; i <= last_row; i++) {
03182                 if (mode == 1) {
03183                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03184                         cpl_table_set_double(idscoeff, clab[k], i, 
03185                                              cpl_polynomial_eval_1d(trend, i,
03186                                                                     NULL));
03187                     }
03188                 }
03189                 else if (mode == 2) {
03190                     cpl_table_set_double(idscoeff, clab[k], i, 
03191                                      cpl_polynomial_eval_1d(trend, i, NULL));
03192                 }
03193             }
03194             cpl_polynomial_delete(trend);
03195         }
03196         else {
03197             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03198         }
03199 
03200     }
03201 
03202     return CPL_ERROR_NONE;
03203 }
03204 
03205 
03206 
03232 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03233                            cpl_table *overscans)
03234 {
03235     const char *func = "mos_remove_bias";
03236 
03237     cpl_image *unbiased;
03238     cpl_image *overscan;
03239     double     mean_bias_level;
03240     double     mean_overscans_level;
03241     int        count;
03242     int        nrows;
03243     int        xlow, ylow, xhig, yhig;
03244     int        i;
03245 
03246 
03247     if (image == NULL || overscans == NULL) {
03248         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03249         return NULL;
03250     }
03251 
03252     nrows = cpl_table_get_nrow(overscans);
03253 
03254     if (nrows == 0) {
03255         cpl_msg_error(func, "Empty overscan table");
03256         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03257         return NULL;
03258     }
03259 
03260     if (bias) {
03261         if (nrows == 1) {
03262             unbiased = cpl_image_subtract_create(image, bias);
03263             if (unbiased == NULL) {
03264                 cpl_msg_error(func, "Incompatible master bias");
03265                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03266             }
03267             return unbiased;
03268         }
03269         mean_bias_level = cpl_image_get_mean(bias);
03270     }
03271     else {
03272         if (nrows == 1) {
03273             cpl_msg_error(func, "No master bias in input, and no overscan "
03274                           "regions in input image: bias subtraction "
03275                           "cannot be performed!");
03276             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03277             return NULL;
03278         }
03279         mean_bias_level = 0.0;
03280     }
03281 
03282     mean_overscans_level = 0.0;
03283     count = 0;
03284     for (i = 0; i < nrows; i++) {
03285         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03286         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03287         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03288         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03289 
03290         if (i == 0) {
03291             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03292             if (unbiased == NULL) {
03293                 cpl_msg_error(func, "Incompatible overscan table");
03294                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03295                 return NULL;
03296             }
03297             if (bias) {
03298                 if (cpl_image_subtract(unbiased, bias)) {
03299                     cpl_msg_error(func, "Incompatible master bias");
03300                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03301                     cpl_image_delete(unbiased);
03302                     return NULL;
03303                 }
03304             }
03305         }
03306         else {
03307             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03308             if (overscan == NULL) {
03309                 cpl_msg_error(func, "Incompatible overscan table");
03310                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03311                 cpl_image_delete(unbiased);
03312                 return NULL;
03313             }
03314 
03315             mean_overscans_level += cpl_image_get_median(overscan);
03316             count++;
03317 
03318 /***
03319  * Here the mean level was used: not very robust...
03320 
03321             mean_overscans_level += cpl_image_get_flux(overscan);
03322             count += cpl_image_get_size_x(overscan)
03323                    * cpl_image_get_size_y(overscan);
03324 ***/
03325             cpl_image_delete(overscan);
03326         }
03327     }
03328 
03329     /*
03330      * Overscan correction
03331      */
03332 
03333     mean_overscans_level /= count;
03334 
03335     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03336 
03337     return unbiased;
03338 
03339 }
03340 
03341 
03400 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03401                                      int length, int msize, int fsize) 
03402 {
03403     const char *func = "mos_arc_background_1D";
03404 
03405     float  *minf;
03406     float  *maxf;
03407     float  *smof;
03408     int     i;
03409 
03410 
03411     if (spectrum == NULL || back == NULL)
03412         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03413 
03414     if (msize % 2 == 0)
03415         msize++;
03416 
03417     if (fsize % 2 == 0)
03418         fsize++;
03419 
03420     if (msize < 3 || fsize < msize || length < 2*fsize)
03421         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03422 
03423 
03424     minf = min_filter(spectrum, length, msize);
03425     smof = smo_filter(minf, length, fsize);
03426     cpl_free(minf);
03427     maxf = max_filter(smof, length, 2*msize+1);
03428     cpl_free(smof);
03429     smof = smo_filter(maxf, length, 2*fsize+1);
03430     cpl_free(maxf);
03431     minf = min_filter(smof, length, 2*msize+1);
03432     cpl_free(smof);
03433     smof = smo_filter(minf, length, 2*fsize+1);
03434     cpl_free(minf);
03435 
03436     for (i = 0; i < length; i++)
03437         back[i] = smof[i];
03438 
03439     cpl_free(smof);
03440 
03441     return CPL_ERROR_NONE;
03442 
03443 }
03444 
03445 
03502 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03503 {
03504     const char *func = "mos_arc_background";
03505 
03506     cpl_image  *fimage;
03507     cpl_image  *bimage;
03508     cpl_matrix *kernel;
03509     float      *data;
03510     float      *bdata;
03511     float      *row;
03512     float      *brow;
03513     int         nx, ny;
03514     int         i;
03515 
03516 
03517     if (image == NULL) {
03518         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03519         return NULL;
03520     }
03521 
03522     if (msize % 2 == 0)
03523         msize++;
03524 
03525     if (fsize % 2 == 0)
03526         fsize++;
03527 
03528     nx = cpl_image_get_size_x(image);
03529     ny = cpl_image_get_size_y(image);
03530 
03531     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03532 
03533     kernel = cpl_matrix_new(3, 3);
03534     cpl_matrix_fill(kernel, 1.0);
03535     fimage = cpl_image_filter_median(image, kernel);
03536     cpl_matrix_delete(kernel);
03537 
03538     data = cpl_image_get_data_float(fimage);
03539     bdata = cpl_image_get_data_float(bimage);
03540 
03541     for (i = 0; i < ny; i++) {
03542         row = data + i * nx;
03543         brow = bdata + i * nx;
03544         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03545             cpl_error_set_where(func); 
03546             cpl_image_delete(fimage);
03547             cpl_image_delete(bimage);
03548             return NULL;
03549         }
03550     }
03551 
03552     cpl_image_delete(fimage);
03553 
03554     return bimage;
03555 }
03556 
03557 
03578 int mos_lines_width(float *spectrum, int length)
03579 {
03580 
03581   const char *func = "mos_lines_width";
03582 
03583   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03584   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03585 
03586   double  norm, value, max;
03587   int     radius = 20;
03588   int     short_length = length - 2*radius - 1;
03589   int     width;
03590   int     i, j, k;
03591 
03592 
03593   /*
03594    * Derivative, and separation of positive and negative derivatives
03595    */
03596 
03597   for (j = 0, i = 1; i < length; j++, i++) {
03598       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03599       if (profile1[j] < 0)
03600           profile1[j] = 0;
03601       if (profile2[j] > 0)
03602           profile2[j] = 0;
03603       else
03604           profile2[j] = -profile2[j];
03605   }
03606 
03607 
03608   /*
03609    * Profiles normalisation
03610    */
03611 
03612   length--;
03613 
03614   norm = 0;
03615   for (i = 0; i < length; i++)
03616       if (norm < profile1[i])
03617           norm = profile1[i];
03618 
03619   for (i = 0; i < length; i++) {
03620       profile1[i] /= norm;
03621       profile2[i] /= norm;
03622   }
03623 
03624 
03625   /*
03626    * Cross-correlation
03627    */
03628 
03629   max = -1;
03630   for (i = 0; i <= radius; i++) {
03631       value = 0;
03632       for (j = 0; j < short_length; j++) {
03633           k = radius+j;
03634           value += profile1[k] * profile2[k+i];
03635       }
03636       if (max < value) {
03637           max = value;
03638           width = i;
03639       }
03640   }
03641 
03642   cpl_free(profile1);
03643   cpl_free(profile2);
03644 
03645   if (max < 0.0) {
03646       cpl_msg_debug(func, "Cannot estimate line width");
03647       width = 1;
03648   }
03649 
03650   return width;
03651 
03652 }
03653 
03654 
03681 cpl_vector *mos_peak_candidates(float *spectrum, int length, float level, 
03682                                 float exp_width)
03683 { 
03684 
03685   const char *func = "mos_peak_candidates";
03686 
03687   int     i, j;
03688   int     nint   = length - 1;
03689   int     n      = 0;
03690   int     width  = 2 * ceil(exp_width / 2) + 1;
03691   int     start  = width / 2;
03692   int     end    = length - width / 2;
03693   int     step;
03694   float  *smo;
03695   double *data   = cpl_calloc(length/2, sizeof(double));
03696 
03697 
03698   if (spectrum == NULL) {
03699       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03700       return NULL;
03701   }
03702 
03703 
03704   /*
03705    * If lines have a flat top (as in the case of broad slit), smooth
03706    * before determining the max.
03707    */
03708 
03709   if (width > 7) {
03710     smo = cpl_calloc(length, sizeof(float));
03711     start = width / 2;
03712     end = length - width / 2;
03713     for (i = 0; i < start; i++)
03714       smo[i] = spectrum[i];
03715     for (i = start; i < end; i++) {
03716       for (j = i - start; j <= i + start; j++)
03717         smo[i] += spectrum[j];
03718       smo[i] /= width;
03719     }
03720     for (i = end; i < length; i++)
03721       smo[i] = spectrum[i];
03722   }
03723   else {
03724     smo = spectrum;
03725   }
03726 
03727   /*
03728    * Collect all relative maxima along spectrum, that are higher than the
03729    * specified level.
03730    */
03731 
03732   if (width > 20)
03733     step = width / 2;
03734   else
03735     step = 1;
03736 
03737   for (i = step; i < nint - step + 1; i += step) {
03738     if (smo[i] > level) {
03739       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03740         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03741           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03742           ++n;
03743         }
03744       }
03745     }
03746   }
03747 
03748   if (width > 7) {
03749     cpl_free(smo);
03750   }
03751 
03752   if (n == 0) {
03753     cpl_free(data);
03754     return NULL;
03755   }
03756 
03757   return cpl_vector_wrap(n, data);
03758 
03759 }
03760 
03761 
03783 cpl_vector *mos_refine_peaks(float *spectrum, int length, 
03784                              cpl_vector *peaks, int sradius)
03785 {
03786 
03787     const char *func = "mos_refine_peaks";
03788 
03789     double *data;
03790     float   pos;
03791     int     npeaks;
03792     int     startPos, endPos;
03793     int     window = 2*sradius+1;
03794     int     i, j;
03795 
03796 
03797     if (peaks == NULL || spectrum == NULL) {
03798         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03799         return NULL;
03800     }
03801 
03802     npeaks = cpl_vector_get_size(peaks);
03803     data = cpl_vector_unwrap(peaks);
03804 
03805     for (i = 0; i < npeaks; i++) {
03806         startPos = data[i] - window/2;
03807         endPos   = startPos + window;
03808         if (startPos < 0 || endPos >= length)
03809             continue;
03810 
03811         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
03812             pos += startPos;
03813             data[i] = pos;
03814         }
03815     }
03816 
03817     for (i = 1; i < npeaks; i++)
03818         if (data[i] - data[i-1] < 0.5)
03819             data[i-1] = -1.0;
03820 
03821     for (i = 0, j = 0; i < npeaks; i++) {
03822         if (data[i] > 0.0) {
03823             if (i != j)
03824                 data[j] = data[i];
03825             j++;
03826         }
03827     }
03828 
03829     return cpl_vector_wrap(j, data);
03830 
03831 }
03832 
03833 
03887 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
03888                                  double min_disp, double max_disp,
03889                                  double tolerance)
03890 {
03891 
03892   int      i, j, k, l;
03893   int      nlint, npint;
03894   int      minpos;
03895   float    min;
03896   double   lratio, pratio;
03897   double   lo_start, lo_end, hi_start, hi_end, denom;
03898   double   disp, variation, prev_variation;
03899   int      max, maxpos, minl, mink;
03900   int      ambiguous;
03901   int      npeaks_lo, npeaks_hi;
03902   int     *peak_lo;
03903   int     *peak_hi;
03904   int    **ident;
03905   int     *nident;
03906   int     *lident;
03907 
03908   double  *peak;
03909   double  *line;
03910   int      npeaks, nlines;
03911 
03912   double  *xpos;
03913   double  *lambda;
03914   int     *ilambda;
03915   double  *tmp_xpos;
03916   double  *tmp_lambda;
03917   int     *tmp_ilambda;
03918   int     *flag;
03919   int      n = 0;
03920   int      nn;
03921   int      nseq = 0;
03922   int      gap;
03923   int     *seq_length;
03924   int      found;
03925 
03926   peak        = cpl_vector_get_data(peaks);
03927   npeaks      = cpl_vector_get_size(peaks);
03928   line        = cpl_vector_get_data(lines);
03929   nlines      = cpl_vector_get_size(lines);
03930 
03931   if (npeaks < 4)
03932       return NULL;
03933 
03934   peak_lo     = cpl_malloc(npeaks * sizeof(int));
03935   peak_hi     = cpl_malloc(npeaks * sizeof(int));
03936   nident      = cpl_calloc(npeaks, sizeof(int));
03937   lident      = cpl_calloc(nlines, sizeof(int));
03938   xpos        = cpl_calloc(npeaks, sizeof(double));
03939   lambda      = cpl_calloc(npeaks, sizeof(double));
03940   ilambda     = cpl_calloc(npeaks, sizeof(int));
03941   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
03942   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
03943   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
03944   flag        = cpl_calloc(npeaks, sizeof(int));
03945   seq_length  = cpl_calloc(npeaks, sizeof(int));
03946   ident       = cpl_malloc(npeaks * sizeof(int *));
03947   for (i = 0; i < npeaks; i++)
03948     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
03949 
03950   /*
03951    * This is just the number of intervals - one less than the number
03952    * of points (catalog wavelengths, or detected peaks).
03953    */
03954 
03955   nlint = nlines - 1;
03956   npint = npeaks - 1;
03957 
03958 
03959   /*
03960    * Here the big loops on catalog lines begins.
03961    */
03962 
03963   for (i = 1; i < nlint; i++) {
03964 
03965 
03966     /*
03967      * For each catalog wavelength I take the previous and the next one, 
03968      * and compute the ratio of the corresponding wavelength intervals.
03969      * This ratio will be compared to all the ratios obtained doing the
03970      * same with all the detected peaks positions.
03971      */
03972 
03973     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
03974 
03975 
03976     /*
03977      * Here the loop on detected peaks positions begins.
03978      */
03979 
03980     for (j = 1; j < npint; j++) {
03981 
03982       /*
03983        * Not all peaks are used for computing ratios: just the ones
03984        * that are compatible with the expected spectral dispersion
03985        * are taken into consideration. Therefore, I define the pixel
03986        * intervals before and after any peak that are compatible with
03987        * the specified dispersion interval, and select just the peaks
03988        * within such intervals. If either of the two intervals doesn't
03989        * contain any peak, then I skip the current peak and continue
03990        * with the next.
03991        */
03992 
03993       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
03994       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
03995       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
03996       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
03997 
03998       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
03999         if (peak[k] > lo_end)
04000           break;
04001         if (peak[k] > lo_start) {
04002           peak_lo[npeaks_lo] = k;
04003           ++npeaks_lo;
04004         }
04005       }
04006 
04007       if (npeaks_lo == 0)
04008         continue;
04009 
04010       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04011         if (peak[k] > hi_end)
04012           break;
04013         if (peak[k] > hi_start) {
04014           peak_hi[npeaks_hi] = k;
04015           ++npeaks_hi;
04016         }
04017       }
04018 
04019       if (npeaks_hi == 0)
04020         continue;
04021 
04022 
04023       /*
04024        * Now I have all peaks that may help for a local identification.
04025        * peak_lo[k] is the sequence number of the k-th peak of the lower
04026        * interval; peak_hi[l] is the sequence number of the l-th peak of
04027        * the higher interval. j is, of course, the sequence number of the
04028        * current peak (second big loop).
04029        */
04030 
04031       prev_variation = 1000.0;
04032       minl = mink = 0;
04033 
04034       for (k = 0; k < npeaks_lo; k++) {
04035         denom = peak[j] - peak[peak_lo[k]];
04036         for (l = 0; l < npeaks_hi; l++) {
04037 
04038           /*
04039            * For any pair of peaks - one from the lower and the other
04040            * from the higher interval - I compute the same ratio that
04041            * was computed with the current line catalog wavelength.
04042            */
04043 
04044           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04045 
04046           /*
04047            * If the two ratios are compatible within the specified
04048            * tolerance, we have a preliminary identification. This
04049            * will be marked in the matrix ident[][], where the first
04050            * index corresponds to a peak sequence number, and the second
04051            * index is the counter of the identifications made during
04052            * this whole process. The array of counters is nident[].
04053            * If more than one interval pair fulfills the specified
04054            * tolerance, the closest to the expected ratio is selected.
04055            */
04056 
04057           variation = fabs(lratio-pratio) / pratio;
04058 
04059           if (variation < tolerance) {
04060             if (variation < prev_variation) {
04061               prev_variation = variation;
04062               minl = l;
04063               mink = k;
04064             }
04065           }
04066         }
04067       }
04068       if (prev_variation < tolerance) {
04069         ident[j][nident[j]]                         = i;
04070         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04071         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04072         ++nident[j];
04073         ++nident[peak_hi[minl]];
04074         ++nident[peak_lo[mink]];
04075       }
04076     }   /* End loop on positions */
04077   }    /* End loop on lines     */
04078 
04079 
04080   /*
04081    * At this point I have filled the ident matrix with all my preliminary
04082    * identifications. Ambiguous identifications must be eliminated.
04083    */
04084 
04085 
04086   for (i = 0; i < npeaks; i++) {
04087 
04088 
04089     /*
04090      * I don't take into consideration peaks that were never identified.
04091      * They are likely contaminations, or emission lines that were not
04092      * listed in the input wavelength catalog.
04093      */
04094 
04095     if (nident[i] > 1) {
04096 
04097 
04098       /*
04099        * Initialise the histogram of wavelengths assigned to the i-th peak.
04100        */
04101 
04102       for (j = 0; j < nlines; j++)
04103         lident[j] = 0;
04104 
04105 
04106       /*
04107        * Count how many times each catalog wavelength was assigned
04108        * to the i-th peak.
04109        */
04110 
04111       for (j = 0; j < nident[i]; j++)
04112         ++lident[ident[i][j]];
04113 
04114 
04115       /*
04116        * What wavelength was most frequently assigned to the i-th peak?
04117        */
04118 
04119       max = 0;
04120       maxpos = 0;
04121       for (j = 0; j < nlines; j++) {
04122         if (max < lident[j]) {
04123           max = lident[j];
04124           maxpos = j;
04125         }
04126       }
04127 
04128 
04129       /*
04130        * Were there other wavelengths assigned with the same frequency?
04131        * This would be the case of an ambiguous identification. It is
04132        * safer to reject this peak...
04133        */
04134 
04135       ambiguous = 0;
04136 
04137       for (k = maxpos + 1; k < nlines; k++) {
04138         if (lident[k] == max) {
04139           ambiguous = 1;
04140           break;
04141         }
04142       }
04143 
04144       if (ambiguous)
04145         continue;
04146 
04147 
04148       /*
04149        * Otherwise, I assign to the i-th peak the wavelength that was
04150        * most often assigned to it.
04151        */
04152 
04153       tmp_xpos[n]   = peak[i];
04154       tmp_lambda[n] = line[maxpos];
04155       tmp_ilambda[n] = maxpos;
04156 
04157       ++n;
04158 
04159     }
04160 
04161   }
04162 
04163 
04164   /*
04165    * Check on identified peaks. Contaminations from other spectra might 
04166    * be present and should be excluded: this type of contamination 
04167    * consists of peaks that have been _correctly_ identified! The non-
04168    * spectral type of light contamination should have been almost all 
04169    * removed already in the previous steps, but it may still be present.
04170    * Here, the self-consistent sequences of identified peaks are
04171    * separated one from the other. At the moment, just the longest of
04172    * such sequences is selected (in other words, spectral multiplexing
04173    * is ignored).
04174    */
04175 
04176   if (n > 1) {
04177     nn = 0;                  /* Number of peaks in the list of sequences */
04178     nseq = 0;                /* Current sequence */
04179     for (k = 0; k < n; k++) {
04180       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04181         flag[k] = 1;
04182         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04183         lambda[nn] = tmp_lambda[k];
04184         ilambda[nn] = tmp_ilambda[k];
04185         ++seq_length[nseq];
04186         ++nn;
04187 
04188         /*
04189          * Now look for all the following peaks that are compatible
04190          * with the expected spectral dispersion, and add them in 
04191          * sequence to xpos. Note that missing peaks are not a problem...
04192          */
04193          
04194         i = k;
04195         while (i < n - 1) {
04196           found = 0;
04197           for (j = i + 1; j < n; j++) {
04198             if (flag[j] == 0) {
04199               disp = (tmp_lambda[j] - tmp_lambda[i])
04200                    / (tmp_xpos[j] - tmp_xpos[i]);
04201               if (disp >= min_disp && disp <= max_disp) {
04202                 flag[j] = 1;
04203                 xpos[nn] = tmp_xpos[j];
04204                 lambda[nn] = tmp_lambda[j];
04205                 ilambda[nn] = tmp_ilambda[j];
04206                 ++seq_length[nseq];
04207                 ++nn;
04208                 i = j;
04209                 found = 1;
04210                 break;
04211               }
04212             }
04213           }
04214           if (!found)
04215             break;
04216         }
04217 
04218         /*
04219          * Current sequence is completed: begin new sequence on the
04220          * excluded peaks, starting the loop on peaks again.
04221          */
04222 
04223         ++nseq;
04224         k = 0;
04225       }
04226     }
04227 
04228 
04229     /*
04230      * Find the longest sequence of self-consistent peaks.
04231      */
04232 
04233     max = 0;
04234     maxpos = 0;
04235     for (i = 0; i < nseq; i++) {
04236       if (seq_length[i] > max) {
04237         max = seq_length[i];
04238         maxpos = i;
04239       }
04240     }
04241 
04242     /*
04243      * Find where this sequence starts in the whole peak position
04244      * storage.
04245      */
04246 
04247     nn = 0;
04248     for (i = 0; i < maxpos; i++)
04249       nn += seq_length[i];
04250 
04251     /*
04252      * Move the longest sequence at the beginning of the returned lists
04253      */
04254 
04255     n = max;
04256     for (i = 0; i < n; i++, nn++) {
04257       xpos[i] = xpos[nn];
04258       lambda[i] = lambda[nn];
04259       ilambda[i] = ilambda[nn];
04260     }
04261 
04262 
04263     /*
04264      * Are some wavelengths missing? Recover them.
04265      */
04266 
04267     for (i = 1; i < n; i++) {
04268       gap = ilambda[i] - ilambda[i-1];
04269       for (j = 1; j < gap; j++) {
04270 
04271         if (j == 1) {
04272 
04273           /*
04274            * Determine the local dispersion from the current pair of peaks
04275            */
04276   
04277           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04278         }
04279 
04280         /*
04281          * With this, find the expected position of the missing
04282          * peak by linear interpolation.
04283          */
04284 
04285         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04286 
04287         /*
04288          * Is there a peak at that position? Here a peak from the
04289          * original list is searched, that is closer than 2 pixels
04290          * to the expected position. If it is found, insert it at
04291          * the current position on the list of identified peaks,
04292          * and leave immediately the loop (taking the new position
04293          * for the following linear interpolation, in case more
04294          * than one peak is missing in the current interval).
04295          * If it is not found, stay in the loop, looking for 
04296          * the following missing peaks in this interval.
04297          */
04298 
04299         found = 0;
04300         for (k = 0; k < npeaks; k++) {
04301           if (fabs(peak[k] - hi_start) < 2) {
04302             for (l = n; l > i; l--) {
04303               xpos[l] = xpos[l-1];
04304               lambda[l] = lambda[l-1];
04305               ilambda[l] = ilambda[l-1];
04306             }
04307             xpos[i] = peak[k];
04308             lambda[i] = line[ilambda[i-1] + j];
04309             ilambda[i] = ilambda[i-1] + j;
04310             ++n;
04311             found = 1;
04312             break;
04313           }
04314         }
04315         if (found)
04316           break;
04317       }
04318     }
04319 
04320 
04321     /*
04322      * Try to extrapolate forward
04323      */
04324 
04325     found = 1;
04326     while (ilambda[n-1] < nlines - 1 && found) {
04327 
04328       /*
04329        * Determine the local dispersion from the last pair of 
04330        * identified peaks
04331        */
04332 
04333       if (n > 1)
04334           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04335       else
04336           disp = 0.0;
04337 
04338       if (disp > max_disp || disp < min_disp)
04339         break;
04340 
04341 
04342       /*
04343        * With this, find the expected position of the missing
04344        * peak by linear interpolation.
04345        */
04346 
04347       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04348 
04349       /*
04350        * Is there a peak at that position? Here a peak from the
04351        * original list is searched, that is closer than 6 pixels
04352        * to the expected position. If it is found, insert it at
04353        * the end of the list of identified peaks. If it is not
04354        * found, leave the loop.
04355        */
04356 
04357       found = 0;
04358       min = fabs(peak[0] - hi_start);
04359       minpos = 0;
04360       for (k = 1; k < npeaks; k++) {
04361         if (min > fabs(peak[k] - hi_start)) {
04362             min = fabs(peak[k] - hi_start);
04363             minpos = k;
04364         }
04365       }
04366       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04367         xpos[n] = peak[minpos];
04368         lambda[n] = line[ilambda[n-1] + 1];
04369         ilambda[n] = ilambda[n-1] + 1;
04370         ++n;
04371         found = 1;
04372       }
04373     }
04374 
04375 
04376     /*
04377      * Try to extrapolate backward
04378      */
04379 
04380     found = 1;
04381     while (ilambda[0] > 0 && found) {
04382 
04383       /*
04384        * Determine the local dispersion from the first pair of
04385        * identified peaks
04386        */
04387 
04388       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04389 
04390       if (disp > max_disp || disp < min_disp)
04391         break;
04392 
04393 
04394       /*
04395        * With this, find the expected position of the missing
04396        * peak by linear interpolation.
04397        */
04398 
04399       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04400 
04401 
04402       /*
04403        * Is there a peak at that position? Here a peak from the
04404        * original list is searched, that is closer than 6 pixels
04405        * to the expected position. If it is found, insert it at
04406        * the beginning of the list of identified peaks. If it is not
04407        * found, leave the loop.
04408        */
04409 
04410       found = 0;
04411       min = fabs(peak[0] - hi_start);
04412       minpos = 0;
04413       for (k = 1; k < npeaks; k++) {
04414         if (min > fabs(peak[k] - hi_start)) {
04415             min = fabs(peak[k] - hi_start);
04416             minpos = k;
04417         }
04418       }
04419       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04420         for (j = n; j > 0; j--) {
04421           xpos[j] = xpos[j-1];
04422           lambda[j] = lambda[j-1];
04423           ilambda[j] = ilambda[j-1];
04424         }
04425         xpos[0] = peak[minpos];
04426         lambda[0] = line[ilambda[0] - 1];
04427         ilambda[0] = ilambda[0] - 1;
04428         ++n;
04429         found = 1;
04430       }
04431     }
04432   }
04433 
04434 
04435   /*
04436    * At this point all peaks are processed. Free memory, and return
04437    * the result.
04438    */
04439 
04440 /************************************************+
04441   for (i = 0; i < npeaks; i++) {
04442     printf("Peak %d:\n   ", i);
04443     for (j = 0; j < nident[i]; j++)
04444       printf("%.2f, ", line[ident[i][j]]);
04445     printf("\n");
04446   }
04447 
04448   printf("\n");
04449 
04450   for (i = 0; i < n; i++)
04451     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04452 +************************************************/
04453   for (i = 0; i < npeaks; i++)
04454     cpl_free(ident[i]);
04455   cpl_free(ident);
04456   cpl_free(nident);
04457   cpl_free(lident);
04458   cpl_free(ilambda);
04459   cpl_free(tmp_xpos);
04460   cpl_free(tmp_lambda);
04461   cpl_free(tmp_ilambda);
04462   cpl_free(peak_lo);
04463   cpl_free(flag);
04464   cpl_free(seq_length);
04465   cpl_free(peak_hi);
04466 
04467   if (n == 0) {
04468     cpl_free(xpos);
04469     cpl_free(lambda);
04470     return NULL;
04471   }
04472 
04473   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04474                                    cpl_vector_wrap(n, lambda));
04475 }
04476 
04477 
04495 /*
04496 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04497                     double refwave, double pixel)
04498 {
04499     double yellow;
04500     double cpixel;
04501     double tolerance = 0.02;
04502     int    max_iter = 20;
04503     int    iter = 0;
04504 
04505     
04506     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04507         return 0.0;
04508     
04509     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04510         return 0.0;
04511 
04512     yellow = (blue + red) / 2;
04513 
04514     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04515 
04516     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04517 
04518         if (cpixel > pixel)
04519             red = yellow;
04520         else
04521             blue = yellow;
04522 
04523         yellow = (blue + red) / 2;
04524         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04525 
04526         iter++;
04527 
04528     }
04529 
04530     return yellow;
04531 
04532 }
04533 */
04534 
04535 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04536                     double refwave, double pixel)
04537 {
04538     double yellow;
04539     double coeff;
04540     int    zero = 0;
04541 
04542     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04543         return 0.0;
04544 
04545     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04546         return 0.0;
04547 
04548     yellow = (blue + red) / 2 - refwave;
04549 
04550     coeff = cpl_polynomial_get_coeff(ids, &zero);
04551     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04552 
04553     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04554 
04555     cpl_polynomial_set_coeff(ids, &zero, coeff);
04556 
04557     return yellow + refwave;
04558 
04559 }
04560 
04586 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04587                                  double reject, int minlines, 
04588                                  int *nlines, double *err)
04589 {
04590     const char   *func = "mos_poly_wav2pix";
04591 
04592     cpl_bivector *pixwav2;
04593     cpl_vector   *wavel;
04594     cpl_vector   *pixel;
04595     double       *d_wavel;
04596     double       *d_pixel;
04597     double        pixpos;
04598     int           fitlines;
04599     int           rejection = 0;
04600     int           i, j;
04601 
04602     cpl_polynomial *ids;
04603 
04604 
04605     *nlines = 0;
04606     *err = 0;
04607 
04608     if (pixwav == NULL) {
04609         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04610         return NULL;
04611     }
04612 
04613     fitlines = cpl_bivector_get_size(pixwav);
04614 
04615     if (fitlines < minlines) {
04616         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04617         return NULL;
04618     }
04619 
04620 
04621     /*
04622      * If outliers rejection was requested, allocate a working
04623      * vector (that can be modified as soon as outliers are removed)
04624      */
04625 
04626     if (reject > 0.0)
04627         rejection = 1;
04628 
04629     if (rejection)
04630         pixwav2 = cpl_bivector_duplicate(pixwav);
04631     else
04632         pixwav2 = pixwav;
04633 
04634 
04635     /*
04636      * The single vectors are extracted just because the fitting routine
04637      * requires it
04638      */
04639 
04640     pixel = cpl_bivector_get_x(pixwav2);
04641     wavel = cpl_bivector_get_y(pixwav2);
04642 
04643 
04644     /*
04645      * Get rid of the wrapper, in case of duplication
04646      */
04647 
04648     if (rejection)
04649         cpl_bivector_unwrap_vectors(pixwav2);
04650 
04651 
04652     /*
04653      * Here begins the iterative fit of identified lines
04654      */
04655 
04656     while (fitlines >= minlines) {
04657 
04658         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04659         *err = sqrt(*err);
04660     
04661         if (ids == NULL) {
04662             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04663             cpl_msg_debug(func, "Fitting IDS");
04664             cpl_error_set_where(func);
04665             if (rejection) {
04666                 cpl_vector_delete(wavel);
04667                 cpl_vector_delete(pixel);
04668             }
04669             return NULL;
04670         }
04671 
04672         if (rejection) {
04673 
04674 
04675             /*
04676              * Now work directly with the vector data buffers...
04677              */
04678 
04679             d_pixel = cpl_vector_unwrap(pixel);
04680             d_wavel = cpl_vector_unwrap(wavel);
04681 
04682             for (i = 0, j = 0; i < fitlines; i++) {
04683                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04684                 if (fabs(pixpos - d_pixel[i]) < reject) {
04685                     d_pixel[j] = d_pixel[i];
04686                     d_wavel[j] = d_wavel[i];
04687                     j++;
04688                 }
04689             }
04690     
04691             if (j == fitlines) {       /* No rejection in last iteration */
04692                 cpl_free(d_wavel);
04693                 cpl_free(d_pixel);
04694                 *nlines = fitlines;
04695                 return ids;
04696             }
04697             else {                     /* Some lines were rejected       */
04698                 fitlines = j;
04699                 cpl_polynomial_delete(ids);
04700                 if (fitlines >= minlines) {
04701                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04702                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04703                 }
04704                 else {                 /* Too few lines: failure         */
04705                     cpl_free(d_wavel);
04706                     cpl_free(d_pixel);
04707                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04708                     return NULL;
04709                 }
04710             }
04711         }
04712         else {
04713             *nlines = fitlines;
04714             return ids;       /* Exit at first iteration if no rejection */
04715         }
04716     }
04717 
04718     return ids;               /* To avoid compiler warnings */
04719 }
04720 
04721 
04746 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04747                                  double reject, int minlines, 
04748                                  int *nlines, double *err)
04749 {
04750 
04751     cpl_bivector *wavpix;
04752     cpl_vector   *wavel;
04753     cpl_vector   *pixel;
04754 
04755     cpl_polynomial *dds;
04756 
04757 
04758     /*
04759      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
04760      */
04761 
04762     pixel = cpl_bivector_get_x(pixwav);
04763     wavel = cpl_bivector_get_y(pixwav);
04764 
04765     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
04766 
04767     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
04768 
04769     cpl_bivector_unwrap_vectors(wavpix);
04770 
04771     return dds;
04772 
04773 }
04774 
04775 
04798 cpl_bivector *mos_find_peaks(float *spectrum, int length, cpl_vector *lines,
04799                              cpl_polynomial *ids, double refwave, int sradius)
04800 {
04801     const char   *func = "mos_find_peaks";
04802 
04803     double       *data;
04804     double       *d_pixel;
04805     double       *d_wavel;
04806     float         pos;
04807     int           nlines;
04808     int           pixel;
04809     int           i, j;
04810 
04811 
04812     if (spectrum == NULL || lines == NULL || ids == NULL) {
04813         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04814         return NULL;
04815     }
04816 
04817     nlines = cpl_vector_get_size(lines);
04818 
04819     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
04820         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04821         return NULL;
04822     }
04823 
04824     d_wavel = cpl_malloc(nlines * sizeof(double));
04825     d_pixel = cpl_malloc(nlines * sizeof(double));
04826 
04827     data = cpl_vector_get_data(lines);
04828 
04829     for (i = 0, j = 0; i < nlines; i++) {
04830         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
04831         if (pixel - sradius < 0 || pixel + sradius >= length)
04832             continue;
04833         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
04834             pos += pixel - sradius;
04835             d_pixel[j] = pos;
04836             d_wavel[j] = data[i];
04837             j++;
04838         }
04839     }
04840 
04841     if (j > 0) {
04842         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
04843                                          cpl_vector_wrap(j, d_wavel));
04844     }
04845     else {
04846         cpl_free(d_wavel);
04847         cpl_free(d_pixel);
04848         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
04849         return NULL;
04850     }
04851 }
04852 
04853 
04971 cpl_image *mos_wavelength_calibration_raw(cpl_image *image, cpl_vector *lines,
04972                                           double dispersion, float level,
04973                                           int sradius, int order,
04974                                           double reject, double refwave, 
04975                                           double *wavestart, double *waveend,
04976                                           int *nlines, double *error, 
04977                                           cpl_table *idscoeff,
04978                                           cpl_image *calibration,
04979                                           cpl_image *residuals, 
04980                                           cpl_table *restable,
04981                                           cpl_mask *refmask)
04982 {
04983 
04984     const char *func = "mos_wavelength_calibration_raw";
04985 
04986     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
04987                                                  /* Max order is 5 */
04988 
04989     double  tolerance = 20.0;     /* Probably forever...                */
04990     int     step      = 10;       /* Compute restable every "step" rows */
04991 
04992     char            name[MAX_COLNAME];
04993     cpl_image      *resampled;
04994     cpl_image      *bimage;
04995     cpl_bivector   *output;
04996     cpl_bivector   *new_output;
04997     cpl_vector     *peaks;
04998     cpl_vector     *wavel;
04999     cpl_polynomial *ids;
05000     cpl_polynomial *lin;
05001     cpl_matrix     *kernel;
05002     double          ids_err;
05003     double          max_disp, min_disp;
05004     double         *line;
05005     double          firstLambda, lastLambda, lambda;
05006     double          value, wave, pixe;
05007     cpl_binary     *mdata;
05008     float          *sdata;
05009     float          *rdata;
05010     float          *idata;
05011     float          *ddata;
05012     float           v1, v2, vi;
05013     float           fpixel;
05014     int            *have_it;
05015     int             pixstart, pixend;
05016     int             extrapolation;
05017     int             nref;
05018     int             nl, nx, ny, npix, pixel;
05019     int             count;
05020     int             countLines, usedLines;
05021     int             uorder;
05022     int             in, first, last;
05023     int             width, uradius;
05024     int             i, j, k;
05025 
05026 
05027     if (dispersion == 0.0) {
05028         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05029         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05030         return NULL;
05031     }
05032 
05033     if (dispersion < 0.0) {
05034         cpl_msg_error(func, "The expected dispersion must be positive");
05035         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05036         return NULL;
05037     }
05038 
05039     max_disp = dispersion + dispersion * tolerance / 100;
05040     min_disp = dispersion - dispersion * tolerance / 100;
05041 
05042     if (order < 1) {
05043         cpl_msg_error(func, "The order of the fitting polynomial "
05044                       "must be at least 1");
05045         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05046         return NULL;
05047     }
05048 
05049     if (image == NULL || lines == NULL) {
05050         cpl_msg_error(func, "Both spectral exposure and reference line "
05051                       "catalog are required in input");
05052         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05053         return NULL;
05054     }
05055 
05056     nx = cpl_image_get_size_x(image);
05057     ny = cpl_image_get_size_y(image);
05058     sdata = cpl_image_get_data_float(image);
05059 
05060     nref = cpl_vector_get_size(lines);
05061     line = cpl_vector_get_data(lines);
05062 
05063     if (*wavestart < 1.0 && *waveend < 1.0) {
05064         firstLambda = line[0];
05065         lastLambda = line[nref-1];
05066         extrapolation = (lastLambda - firstLambda) / 10;
05067         firstLambda -= extrapolation;
05068         lastLambda += extrapolation;
05069         *wavestart = firstLambda;
05070         *waveend = lastLambda;
05071     }
05072     else {
05073         firstLambda = *wavestart;
05074         lastLambda = *waveend;
05075     }
05076 
05077     nl = (lastLambda - firstLambda) / dispersion;
05078     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05079     rdata = cpl_image_get_data_float(resampled);
05080 
05081     if (calibration)
05082         idata = cpl_image_get_data_float(calibration);
05083 
05084     if (residuals)
05085         ddata = cpl_image_get_data_float(residuals);
05086 
05087     if (idscoeff)
05088         for (j = 0; j <= order; j++)
05089             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05090 
05091     if (restable) {
05092         cpl_table_set_size(restable, nref);
05093         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05094         cpl_table_copy_data_double(restable, "wavelength", line);
05095         for (i = 0; i < ny; i += step) {
05096              snprintf(name, MAX_COLNAME, "r%d", i);
05097              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05098              snprintf(name, MAX_COLNAME, "d%d", i);
05099              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05100              snprintf(name, MAX_COLNAME, "p%d", i);
05101              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05102         }
05103     }
05104 
05105     /*
05106      * This is used to avoid saturation level coded with pixel value zero
05107      * To make it more robust against random 0.0 values, check that also
05108      * next pixel along the spatial direction is 0.0.
05109      */
05110 
05111     npix = nx * ny;
05112 
05113     for (i = 0; i < npix - nx; i++)
05114         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
05115             sdata[i] = 65535.0;
05116 
05117     for (i = npix - nx; i < npix; i++)
05118         if (sdata[i] == 0.0) 
05119             sdata[i] = 65535.0;
05120 
05121     /*
05122      * This is a dirty trick to overcome saturations (making up a false
05123      * tip on their flat tops). This should be useless with a better
05124      * peak detection algorithm.
05125      */
05126 
05127     for (i = 0; i < npix; i++) {
05128         if (sdata[i] >= 65535.0) {
05129             count = 0;
05130             for (j = i; j < npix; j++) {
05131                 if (sdata[j] < 65535.0) {
05132                     break;
05133                 }
05134                 else {
05135                     count++;
05136                 }
05137             }
05138             if (count < 30 && count > 2) {
05139                 for (j = i; j < i + count/2; j++)
05140                     sdata[j] = sdata[i] + 1000.0 * (j - i);
05141                 if (count % 2 != 0) {
05142                     sdata[j] = sdata[j-1] + 1000.0;
05143                     j++;
05144                 }
05145                 for (k = j; k <= i + count; k++)
05146                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
05147                 i = k;
05148             }
05149         }
05150     }
05151 
05152 
05153     /*
05154      * Create and subtract background
05155      */
05156 
05157     bimage = mos_arc_background(image, 15, 15);
05158     cpl_image_subtract(image, bimage);
05159     cpl_image_delete(bimage);
05160 
05161 
05162     /*
05163      * Here is the real thing: detecting and identifying peaks,
05164      * and then fit the transformation from wavelength to pixel
05165      * and from pixel to wavelength.
05166      */
05167 
05168     for (i = 0; i < ny; i++) {
05169         width = mos_lines_width(sdata + i*nx, nx);
05170         if (sradius > 0) {
05171             if (width > sradius) {
05172                 uradius = width;
05173             }
05174             else {
05175                 uradius = sradius;
05176             }
05177         }
05178         if (width < 5)
05179             width = 5;
05180         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05181         if (peaks) {
05182             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05183         }
05184         if (peaks) {
05185             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05186             if (output) {
05187                 countLines = cpl_bivector_get_size(output);
05188                 if (countLines < 4) {
05189                     cpl_bivector_delete(output);
05190                     cpl_vector_delete(peaks);
05191                     if (nlines)
05192                         nlines[i] = 0;
05193                     if (error)
05194                         error[i] = 0.0;
05195                     continue;
05196                 }
05197 
05198                 /*
05199                  * Set reference wavelength as zero point
05200                  */
05201 
05202                 wavel = cpl_bivector_get_y(output);
05203                 cpl_vector_subtract_scalar(wavel, refwave);
05204 
05205                 uorder = countLines / 2 - 1;
05206                 if (uorder > order)
05207                     uorder = order;
05208 
05209 /* This part is now commented out. In case the first-guess iteration
05210  * was requested, the first fit was made with a lower polynomial degree:
05211  * more robust, and still accurate enough to be used as a first-guess.
05212 
05213                 if (sradius > 0 && uorder > 2)
05214                     --uorder;
05215 
05216  * End of commented part */
05217 
05218                 ids = mos_poly_wav2pix(output, uorder, reject,
05219                                        2 * (uorder + 1), &usedLines,
05220                                        &ids_err);
05221 
05222                 if (ids == NULL) {
05223                     cpl_bivector_delete(output);
05224                     cpl_vector_delete(peaks);
05225                     if (nlines)
05226                         nlines[i] = 0;
05227                     if (error)
05228                         error[i] = 0.0;
05229                     cpl_error_reset();
05230                     continue;
05231                 }
05232 
05233                 if (idscoeff) {
05234 
05235                     /*
05236                      * Write it anyway, even in case a first-guess based
05237                      * solution will be searched afterwards: in case of
05238                      * failure, the "blind" solution is kept.
05239                      */
05240 
05241                     for (k = 0; k <= order; k++) {
05242                         if (k > uorder) {
05243                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05244                         }
05245                         else {
05246                             cpl_table_set_double(idscoeff, clab[k], i,
05247                                       cpl_polynomial_get_coeff(ids, &k));
05248                         }
05249                     }
05250                 }
05251 
05252                 if (sradius > 0) {
05253 
05254                     /*
05255                      * Use ids as a first-guess
05256                      */
05257 
05258                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05259                                                 ids, refwave, uradius);
05260 
05261                     if (new_output) {
05262                         cpl_bivector_delete(output);
05263                         output = new_output;
05264                     }
05265                     else
05266                         cpl_error_reset();
05267 
05268 
05269                     cpl_polynomial_delete(ids);
05270 
05271                     countLines = cpl_bivector_get_size(output);
05272 
05273                     if (countLines < 4) {
05274                         cpl_bivector_delete(output);
05275                         cpl_vector_delete(peaks);
05276 
05277                         /* 
05278                          * With the following code a decision is taken:
05279                          * if using the first-guess gives no results,
05280                          * then also the "blind" solution is rejected.
05281                          */
05282 
05283                         if (nlines)
05284                             nlines[i] = 0;
05285                         if (error)
05286                             error[i] = 0.0;
05287                         if (idscoeff)
05288                             for (k = 0; k <= order; k++)
05289                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05290                         continue;
05291                     }
05292 
05293                     wavel = cpl_bivector_get_y(output);
05294                     cpl_vector_subtract_scalar(wavel, refwave);
05295 
05296                     uorder = countLines / 2 - 1;
05297                     if (uorder > order)
05298                         uorder = order;
05299 
05300                     ids = mos_poly_wav2pix(output, uorder, reject,
05301                                            2 * (uorder + 1), &usedLines,
05302                                            &ids_err);
05303 
05304                     if (ids == NULL) {
05305                         cpl_bivector_delete(output);
05306                         cpl_vector_delete(peaks);
05307 
05308                         /* 
05309                          * With the following code a decision is taken:
05310                          * if using the first-guess gives no results,
05311                          * then also the "blind" solution is rejected.
05312                          */
05313 
05314                         if (nlines)
05315                             nlines[i] = 0;
05316                         if (error)
05317                             error[i] = 0.0;
05318                         if (idscoeff)
05319                             for (k = 0; k <= order; k++)
05320                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05321                         cpl_error_reset();
05322                         continue;
05323                     }
05324 
05325                     if (idscoeff) {
05326                         for (k = 0; k <= order; k++) {
05327                             if (k > uorder) {
05328                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05329                             }
05330                             else {
05331                                 cpl_table_set_double(idscoeff, clab[k], i,
05332                                             cpl_polynomial_get_coeff(ids, &k));
05333                             }
05334                         }
05335                     }
05336 
05337                 } /* End of "use ids as a first-guess" */
05338 
05339                 if (nlines)
05340                     nlines[i] = usedLines;
05341                 if (error)
05342                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05343 
05344                 pixstart = cpl_polynomial_eval_1d(ids, 
05345                     cpl_bivector_get_y_data(output)[0], NULL);
05346                 pixend = cpl_polynomial_eval_1d(ids,
05347                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05348                 extrapolation = (pixend - pixstart) / 5;
05349                 pixstart -= extrapolation;
05350                 pixend += extrapolation;
05351                 if (pixstart < 0)
05352                     pixstart = 0;
05353                 if (pixend > nx)
05354                     pixend = nx;
05355 
05356                 /*
05357                  * Wavelength calibrated image (if requested):
05358                  */
05359 
05360                 if (calibration) {
05361                     for (j = pixstart; j < pixend; j++) {
05362                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05363                                                          lastLambda, refwave, 
05364                                                          j);
05365                     }
05366                 }
05367 
05368                 /*
05369                  * Resampled image:
05370                  */
05371 
05372                 for (j = 0; j < nl; j++) {
05373                     lambda = firstLambda + j * dispersion;
05374                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05375                                                     NULL);
05376                     pixel = fpixel;
05377                     if (pixel >= 0 && pixel < nx-1) {
05378                         v1 = (sdata + i*nx)[pixel];
05379                         v2 = (sdata + i*nx)[pixel+1];
05380                         vi = v1 + (v2-v1)*(fpixel-pixel);
05381                         (rdata + i*nl)[j] = vi;
05382                     }
05383                 }
05384 
05385                 /*
05386                  * Residuals image
05387                  */
05388 
05389                 if (residuals || (restable && !(i%step))) {
05390                     if (restable && !(i%step)) {
05391                         lin = cpl_polynomial_new(1);
05392                         for (k = 0; k < 2; k++)
05393                             cpl_polynomial_set_coeff(lin, &k, 
05394                                           cpl_polynomial_get_coeff(ids, &k));
05395                     }
05396                     for (j = 0; j < countLines; j++) {
05397                         pixe = cpl_bivector_get_x_data(output)[j];
05398                         wave = cpl_bivector_get_y_data(output)[j];
05399                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05400                         if (residuals) {
05401                             pixel = pixe + 0.5;
05402                             (ddata + i*nx)[pixel] = value;
05403                         }
05404                         if (restable && !(i%step)) {
05405                             for (k = 0; k < nref; k++) {
05406                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05407                                     snprintf(name, MAX_COLNAME, "r%d", i);
05408                                     cpl_table_set_double(restable, name, 
05409                                                          k, value);
05410                                     value = pixe
05411                                           - cpl_polynomial_eval_1d(lin, wave,
05412                                                                    NULL);
05413                                     snprintf(name, MAX_COLNAME, "d%d", i);
05414                                     cpl_table_set_double(restable, name, 
05415                                                          k, value);
05416                                     snprintf(name, MAX_COLNAME, "p%d", i);
05417                                     cpl_table_set_double(restable, name,
05418                                                          k, pixe);
05419                                     break;
05420                                 }
05421                             }
05422                         }
05423                     }
05424                     if (restable && !(i%step)) {
05425                         cpl_polynomial_delete(lin);
05426                     }
05427                 }
05428 
05429                 /*
05430                  * Mask at reference wavelength
05431                  */
05432 
05433                 if (refmask) {
05434                     mdata = cpl_mask_get_data(refmask);
05435                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05436                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05437                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05438                         mdata[pixel + i*nx] = CPL_BINARY_1;
05439                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05440                     }
05441                 }
05442 
05443                 cpl_polynomial_delete(ids);
05444                 cpl_bivector_delete(output);
05445             }
05446             cpl_vector_delete(peaks);
05447         }
05448     }
05449 
05450     if (refmask) {
05451         kernel = cpl_matrix_new(3, 3);
05452         cpl_matrix_set(kernel, 0, 1, 1.0);
05453         cpl_matrix_set(kernel, 1, 1, 1.0);
05454         cpl_matrix_set(kernel, 2, 1, 1.0);
05455 
05456         cpl_mask_dilation(refmask, kernel);
05457         cpl_mask_erosion(refmask, kernel);
05458         cpl_mask_erosion(refmask, kernel);
05459         cpl_mask_dilation(refmask, kernel);
05460 
05461         cpl_matrix_delete(kernel);
05462 
05463         /*
05464          *  Fill possible gaps
05465          */
05466 
05467         mdata = cpl_mask_get_data(refmask);
05468         have_it = cpl_calloc(ny, sizeof(int));
05469 
05470         for (i = 0; i < ny; i++, mdata += nx) {
05471             for (j = 0; j < nx; j++) {
05472                 if (mdata[j] == CPL_BINARY_1) {
05473                     have_it[i] = j;
05474                     break;
05475                 }
05476             }
05477         }
05478 
05479         mdata = cpl_mask_get_data(refmask);
05480         in = 0;
05481         first = last = 0;
05482 
05483         for (i = 0; i < ny; i++) {
05484             if (have_it[i]) {
05485                 if (!in) {
05486                     in = 1;
05487                     if (first) {
05488                         last = i;
05489                         if (abs(have_it[first] - have_it[last]) < 3) {
05490                             for (j = first; j < last; j++) {
05491                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05492                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05493                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05494                             }
05495                         }
05496                     }
05497                 }
05498             }
05499             else {
05500                 if (in) {
05501                     in = 0;
05502                     first = i - 1;
05503                 }
05504             }
05505         }
05506 
05507         cpl_free(have_it);
05508 
05509     }
05510 
05511 /*
05512     for (i = 0; i < ny; i++) {
05513         if (nlines[i] == 0) {
05514             for (k = 0; k <= order; k++) {
05515                 cpl_table_set_invalid(idscoeff, clab[k], i);
05516             }
05517         }
05518     }
05519 */
05520 
05521     return resampled;
05522 }
05523 
05524 
05546 /*
05547 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05548 {
05549     const char *func = "mos_locate_spectra_bis";
05550 
05551     cpl_apertures    *slits;
05552     cpl_image        *labimage;
05553     cpl_image        *refimage;
05554     cpl_binary       *mdata;
05555     cpl_table        *slitpos;
05556     cpl_propertylist *sort_col;
05557     int               nslits;
05558     int              *have_it;
05559     int               in, first, last;
05560     int               i, j;
05561 
05562 
05563     if (mask == NULL) {
05564         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05565         return NULL;
05566     }
05567 
05568     nx = cpl_mask_get_size_x(mask);
05569     ny = cpl_mask_get_size_y(mask);
05570 
05571     mdata = cpl_mask_get_data(refmask);
05572     have_it = cpl_calloc(ny, sizeof(int));
05573 
05574     for (i = 0; i < ny; i++, mdata += nx) {
05575         for (j = 0; j < nx; j++) {
05576             if (mdata[j] == CPL_BINARY_1) {
05577                 have_it[i] = j + 1;
05578                 break;
05579             }
05580         }
05581     }
05582 
05583     mdata = cpl_mask_get_data(refmask);
05584     in = 0;
05585     first = last = 0;
05586     nslits = 0;
05587 
05588     for (i = 0; i < ny; i++) {
05589         if (have_it[i]) {
05590             if (in) {
05591                 if (i) {
05592                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05593                         nslits++;
05594                     }
05595                 }
05596             }
05597             else {
05598                 in = 1;
05599                 nslits++;
05600             }
05601         }
05602         else {
05603             if (in) {
05604                 in = 0;
05605             }
05606         }
05607     }
05608 }
05609 */
05610 
05611 
05633 cpl_table *mos_locate_spectra(cpl_mask *mask)
05634 {
05635     const char *func = "mos_locate_spectra";
05636 
05637     cpl_apertures    *slits;
05638     cpl_image        *labimage;
05639     cpl_image        *refimage;
05640     cpl_table        *slitpos;
05641     cpl_propertylist *sort_col;
05642     int               nslits;
05643     int               i;
05644 
05645 
05646     if (mask == NULL) {
05647         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05648         return NULL;
05649     }
05650 
05651     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05652 
05653     if (nslits < 1) {
05654         cpl_image_delete(labimage);
05655         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05656         return NULL;
05657     }
05658 
05659     refimage = cpl_image_new_from_mask(mask);
05660 
05661     slits = cpl_apertures_new_from_image(refimage, labimage);
05662 
05663     cpl_image_delete(labimage);
05664     cpl_image_delete(refimage);
05665 
05666     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05667     if (nslits < 1) {
05668         cpl_apertures_delete(slits);
05669         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05670         return NULL;
05671     }
05672 
05673     slitpos = cpl_table_new(nslits);
05674     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05675     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05676     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05677     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05678     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05679     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05680     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05681     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05682 
05683     for (i = 0; i < nslits; i++) {
05684         cpl_table_set_double(slitpos, "xtop", i, 
05685                              cpl_apertures_get_top_x(slits, i+1) - 1);
05686         cpl_table_set_double(slitpos, "ytop", i, 
05687                              cpl_apertures_get_top(slits, i+1));
05688         cpl_table_set_double(slitpos, "xbottom", i, 
05689                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05690         cpl_table_set_double(slitpos, "ybottom", i, 
05691                              cpl_apertures_get_bottom(slits, i+1));
05692     }
05693 
05694     cpl_apertures_delete(slits);
05695 
05696     sort_col = cpl_propertylist_new();
05697     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05698     cpl_table_sort(slitpos, sort_col);
05699     cpl_propertylist_delete(sort_col);
05700 
05701     return slitpos;
05702 
05703 }
05704 
05705 
05721 cpl_error_code mos_validate_slits(cpl_table *slits) 
05722 {
05723     const char *func = "mos_validate_slits";
05724 
05725 
05726     if (slits == NULL)
05727         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05728 
05729     if (1 != cpl_table_has_column(slits, "xtop"))
05730         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05731 
05732     if (1 != cpl_table_has_column(slits, "ytop"))
05733         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05734 
05735     if (1 != cpl_table_has_column(slits, "xbottom"))
05736         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05737 
05738     if (1 != cpl_table_has_column(slits, "ybottom"))
05739         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05740 
05741     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05742         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05743 
05744     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05745         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05746 
05747     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05748         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05749 
05750     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05751         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05752 
05753     return CPL_ERROR_NONE;
05754 }
05755 
05756 
05785 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05786 {
05787     const char *func = "mos_rotate_slits";
05788 
05789     cpl_error_code error;
05790     char aux_name[] = "_0";
05791     int i;
05792 
05793 
05794     rotation %= 4;
05795     if (rotation < 0)
05796         rotation += 4;
05797 
05798     if (rotation == 0)
05799         return CPL_ERROR_NONE;
05800 
05801     error = mos_validate_slits(slits);
05802     if (error)
05803         return cpl_error_set(func, error);
05804 
05805     if (rotation == 1 || rotation == 3) {
05806 
05807         /*
05808          * Swap x and y column names
05809          */
05810 
05811         for (i = 0; i < 77; i++)
05812             if (1 == cpl_table_has_column(slits, aux_name))
05813                 aux_name[1]++;
05814         if (1 == cpl_table_has_column(slits, aux_name))
05815             return cpl_error_set(func, CPL_ERROR_CONTINUE);
05816         cpl_table_name_column(slits, "xtop", aux_name);
05817         cpl_table_name_column(slits, "ytop", "xtop");
05818         cpl_table_name_column(slits, aux_name, "ytop");
05819         cpl_table_name_column(slits, "xbottom", aux_name);
05820         cpl_table_name_column(slits, "ybottom", "xbottom");
05821         cpl_table_name_column(slits, aux_name, "ybottom");
05822     }
05823 
05824     if (rotation == 1 || rotation == 2) {
05825         cpl_table_multiply_scalar(slits, "xtop", -1.0);
05826         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
05827         cpl_table_add_scalar(slits, "xtop", nx);
05828         cpl_table_add_scalar(slits, "xbottom", nx);
05829     }
05830 
05831     if (rotation == 3 || rotation == 2) {
05832         cpl_table_multiply_scalar(slits, "ytop", -1.0);
05833         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
05834         cpl_table_add_scalar(slits, "ytop", ny);
05835         cpl_table_add_scalar(slits, "ybottom", ny);
05836     }
05837 
05838     return CPL_ERROR_NONE;
05839 }
05840 
05841 
05899 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
05900                               cpl_table *global)
05901 {
05902     const char *func = "mos_identify_slits";
05903 
05904     cpl_propertylist *sort_col;
05905     cpl_table        *positions;
05906     cpl_vector       *scales;
05907     cpl_vector       *angles;
05908     cpl_vector       *point;
05909     cpl_vector       *xpos;
05910     cpl_vector       *ypos;
05911     cpl_vector       *xmpos;
05912     cpl_vector       *ympos;
05913     cpl_bivector     *mpos;
05914     cpl_polynomial   *xpoly = NULL;
05915     cpl_polynomial   *ypoly = NULL;
05916     cpl_error_code    error;
05917     int nslits;
05918     int nmaskslits;
05919     int found_slits;
05920     int i, j, k;
05921 
05922     double  dist1, dist2, dist3, dist, mindist;
05923     double  scale, minscale, maxscale;
05924     double  angle, minangle, maxangle;
05925     double *dscale;
05926     double *dangle;
05927     double *dpoint;
05928     double *xtop;
05929     double *ytop;
05930     double *xbottom;
05931     double *ybottom;
05932     double *xcenter;
05933     double *ycenter;
05934     double *xpseudo;
05935     double *ypseudo;
05936     int    *slit_id;
05937     double *xmtop;
05938     double *ymtop;
05939     double *xmbottom;
05940     double *ymbottom;
05941     double *xmcenter;
05942     double *ymcenter;
05943     double *xmpseudo;
05944     double *ympseudo;
05945     double  xmse, ymse;
05946     int    *mslit_id;
05947     int    *good;
05948     int     minpos;
05949     int     degree;
05950 
05951     double  sradius = 0.01;   /* Candidate input argument... */
05952     int     in_sradius;
05953 
05954     double pi = 3.14159265358979323846;
05955 
05956 
05957     error = mos_validate_slits(slits);
05958     if (error) {
05959         cpl_msg_error(func, "CCD slits table validation: %s", 
05960                       cpl_error_get_message());
05961         cpl_error_set(func, error);
05962         return NULL;
05963     }
05964 
05965     error = mos_validate_slits(maskslits);
05966     if (error) {
05967         cpl_msg_error(func, "Mask slits table validation: %s", 
05968                       cpl_error_get_message());
05969         cpl_error_set(func, error);
05970         return NULL;
05971     }
05972 
05973     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
05974         cpl_msg_error(func, "Missing slits identifiers");
05975         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05976         return NULL;
05977     }
05978 
05979     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
05980         cpl_msg_error(func, "Wrong type used for slits identifiers");
05981         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05982         return NULL;
05983     }
05984 
05985     nslits = cpl_table_get_nrow(slits);
05986     nmaskslits = cpl_table_get_nrow(maskslits);
05987 
05988     if (nslits == 0 || nmaskslits == 0) {
05989         cpl_msg_error(func, "Empty slits table");
05990         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05991         return NULL;
05992     }
05993 
05994 
05995     /*
05996      * Compute middle point coordinates for each slit listed in both
05997      * input tables.
05998      */
05999 
06000     if (cpl_table_has_column(slits, "xcenter"))
06001         cpl_table_erase_column(slits, "xcenter");
06002 
06003     if (cpl_table_has_column(slits, "ycenter"))
06004         cpl_table_erase_column(slits, "ycenter");
06005 
06006     if (cpl_table_has_column(maskslits, "xcenter"))
06007         cpl_table_erase_column(maskslits, "xcenter");
06008 
06009     if (cpl_table_has_column(maskslits, "ycenter"))
06010         cpl_table_erase_column(maskslits, "ycenter");
06011 
06012     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06013     cpl_table_add_columns(slits, "xcenter", "xbottom");
06014     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06015     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06016     cpl_table_add_columns(slits, "ycenter", "ybottom");
06017     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06018 
06019     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06020     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06021     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06022     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06023     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06024     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06025 
06026 
06027     /*
06028      * Guarantee that both input tables are sorted in the same way
06029      */
06030 
06031     sort_col = cpl_propertylist_new();
06032     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06033     cpl_table_sort(slits, sort_col);
06034     cpl_table_sort(maskslits, sort_col);
06035     cpl_propertylist_delete(sort_col);
06036 
06037 
06038     /*
06039      * First we handle all the special cases (too few slits...)
06040      */
06041 
06042     if (nslits < 3 && nmaskslits > nslits) {
06043 
06044         /*
06045          * If there are just 1 or 2 slits on the CCD, and more on the
06046          * mask, the ambiguity cannot be solved, and an error is returned.
06047          * This is a case that must be solved with a first-guess relation
06048          * between mask and CCD.
06049          */
06050 
06051         if (nslits > 1)
06052             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06053                             "%d mask slits: process will continue using the "
06054                             "detected CCD slit position", nmaskslits);
06055         else
06056             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06057                             "the %d mask slits: process will continue using "
06058                             "the detected CCD slits positions", nslits, 
06059                             nmaskslits);
06060         return NULL;
06061     }
06062 
06063     if (nslits <= 3 && nslits == nmaskslits) {
06064 
06065         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06066         cpl_msg_warning(func, "Their detected positions are left unchanged");
06067 
06068         /*
06069          * If there are just up to 3 slits, both on the mask and on the CCD,
06070          * we can reasonably hope that those slits were found, and accept 
06071          * that their positions on the CCD cannot be improved. We prepare
06072          * therefore an output position table containing the slits with
06073          * their original positions. We can however give an estimate of
06074          * the platescale if there is more than one slit.
06075          */
06076 
06077         positions = cpl_table_duplicate(slits);
06078         cpl_table_erase_column(slits, "xcenter");
06079         cpl_table_erase_column(slits, "ycenter");
06080         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06081         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06082         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06083         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06084         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06085         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06086         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06087         cpl_table_erase_column(maskslits, "xcenter");
06088         cpl_table_erase_column(maskslits, "ycenter");
06089 
06090         if (nslits > 1) {
06091             xcenter = cpl_table_get_data_double(positions, "xcenter");
06092             ycenter = cpl_table_get_data_double(positions, "ycenter");
06093             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06094             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06095 
06096             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06097                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06098             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06099                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06100             scale = sqrt(dist1/dist2);
06101 
06102             if (nslits == 3) {
06103                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06104                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06105                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06106                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06107                 scale += sqrt(dist1/dist2);
06108                 scale /= 2;
06109             }
06110 
06111             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06112         }
06113 
06114         return positions;
06115     }
06116 
06117     if (nmaskslits < 3 && nslits > nmaskslits) {
06118 
06119         /*
06120          * If there are less than 3 slits on the mask the ambiguity cannot 
06121          * be solved, and an error is returned. This is a case that must 
06122          * be solved with a first-guess relation between mask and CCD.
06123          */
06124 
06125         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06126                         "the %d mask slits: process will continue using "
06127                         "the detected CCD slits positions", nslits, 
06128                         nmaskslits);
06129         return NULL;
06130     }
06131 
06132 
06133     /*
06134      * At this point of the program all the region of the plane
06135      * (nslits, nmaskslits) where either or both mask and CCD display
06136      * less than 3 slits are handled in some way. It would be better
06137      * to add in this place a special handling for identifying slits
06138      * in case of a very reduced number of slits (say, below 6).
06139      * It is also clear that if there are many more slits on the
06140      * mask than on the CCD, or many more on the CCD than on the
06141      * mask, something went deeply wrong with the preliminary 
06142      * wavelength calibration. Such cases should be handled with
06143      * a _complete_ pattern-recognition algorithm based on the
06144      * construction of all possible triangles. For the moment, 
06145      * we go directly to the limited pattern-recognition applied
06146      * below, based on triangles build only for consecutive slits.
06147      * This is reasonably safe, since the preliminary wavelength
06148      * calibration performed by mos_identify_peaks() is generally
06149      * robust.
06150      */
06151 
06152 
06153     /*
06154      * Compute (X, Y) coordinates on pseudo-plane describing the
06155      * different position ratios of successive slits, in both
06156      * input tables.
06157      */
06158 
06159     if (cpl_table_has_column(slits, "xpseudo"))
06160         cpl_table_erase_column(slits, "xpseudo");
06161 
06162     if (cpl_table_has_column(slits, "ypseudo"))
06163         cpl_table_erase_column(slits, "ypseudo");
06164 
06165     if (cpl_table_has_column(maskslits, "xpseudo"))
06166         cpl_table_erase_column(maskslits, "xpseudo");
06167 
06168     if (cpl_table_has_column(maskslits, "ypseudo"))
06169         cpl_table_erase_column(maskslits, "ypseudo");
06170 
06171     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06172     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06173 
06174     xcenter = cpl_table_get_data_double(slits, "xcenter");
06175     ycenter = cpl_table_get_data_double(slits, "ycenter");
06176     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06177     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06178 
06179     for (i = 1; i < nslits - 1; i++) {
06180         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06181               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06182         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06183               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06184         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06185               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06186         xpseudo[i] = sqrt(dist1/dist2);
06187         ypseudo[i] = sqrt(dist3/dist2);
06188     }
06189 
06190     cpl_table_set_invalid(slits, "xpseudo", 0);
06191     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06192     cpl_table_set_invalid(slits, "ypseudo", 0);
06193     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06194 
06195     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06196     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06197 
06198     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06199     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06200     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06201     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06202 
06203     for (i = 1; i < nmaskslits - 1; i++) {
06204         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06205               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06206         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06207               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06208         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06209               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06210         xmpseudo[i] = sqrt(dist1/dist2);
06211         ympseudo[i] = sqrt(dist3/dist2);
06212     }
06213     
06214     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06215     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06216     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06217     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06218 
06219 
06220     /*
06221      * For each (X, Y) on the pseudo-plane related to the mask positions,
06222      * find the closest (X, Y) on the pseudo-plane related to the CCD
06223      * positions. If the closest point is closer than a given search
06224      * radius, a triangle has been found, and 3 slits are identified.
06225      * However, if more than one point is found within the search
06226      * radius, we have an ambiguity and this is rejected.
06227      */
06228 
06229     if (cpl_table_has_column(slits, "slit_id"))
06230         cpl_table_erase_column(slits, "slit_id");
06231     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06232     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06233 
06234     for (i = 1; i < nmaskslits - 1; i++) {
06235         in_sradius = 0;
06236         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06237                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06238         minpos = 1;
06239         if (mindist < sradius*sradius)
06240             in_sradius++;
06241         for (j = 2; j < nslits - 1; j++) {
06242             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06243                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06244             if (dist < sradius*sradius)
06245                 in_sradius++;
06246             if (in_sradius > 1)    /* More than one triangle within radius */
06247                 break;
06248             if (mindist > dist) {
06249                 mindist = dist;
06250                 minpos = j;
06251             }
06252         }
06253 
06254         mindist = sqrt(mindist);
06255 
06256         if (mindist < sradius && in_sradius == 1) {
06257             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06258             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06259             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06260         }
06261     }
06262 
06263 
06264     /*
06265      * At this point, the slit_id column contains invalid elements 
06266      * corresponding to unidentified slits.
06267      */
06268 
06269     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06270 
06271     if (found_slits < 3) {
06272         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06273                         "%d out of %d", found_slits, nslits);
06274         if (nslits == nmaskslits) {
06275             cpl_msg_warning(func, "(this is not an error, it could be caused "
06276                             "by a mask with regularly located slits)");
06277             cpl_msg_warning(func, "The detected slits positions are left "
06278                             "unchanged");
06279 
06280             /*
06281              * If there are less than 3 identified slits, this is probably 
06282              * a mask with regularly spaced slits (leading to an ambiguous
06283              * pattern). Only in the case all expected slits appear to have 
06284              * been found on the CCD we can proceed...
06285              */
06286 
06287             cpl_table_erase_column(slits, "slit_id");
06288             cpl_table_erase_column(slits, "xpseudo");
06289             cpl_table_erase_column(slits, "ypseudo");
06290             positions = cpl_table_duplicate(slits);
06291             cpl_table_erase_column(slits, "xcenter");
06292             cpl_table_erase_column(slits, "ycenter");
06293 
06294             cpl_table_erase_column(maskslits, "xpseudo");
06295             cpl_table_erase_column(maskslits, "ypseudo");
06296             cpl_table_duplicate_column(positions, "xmtop", 
06297                                        maskslits, "xtop");
06298             cpl_table_duplicate_column(positions, "ymtop", 
06299                                        maskslits, "ytop");
06300             cpl_table_duplicate_column(positions, "xmbottom", 
06301                                        maskslits, "xbottom");
06302             cpl_table_duplicate_column(positions, "ymbottom", 
06303                                        maskslits, "ybottom");
06304             cpl_table_duplicate_column(positions, "xmcenter", 
06305                                        maskslits, "xcenter");
06306             cpl_table_duplicate_column(positions, "ymcenter", 
06307                                        maskslits, "ycenter");
06308             cpl_table_duplicate_column(positions, "slit_id", 
06309                                        maskslits, "slit_id");
06310             cpl_table_erase_column(maskslits, "xcenter");
06311             cpl_table_erase_column(maskslits, "ycenter");
06312             return positions;
06313         }
06314         else {
06315             cpl_table_erase_column(slits, "slit_id");
06316             cpl_table_erase_column(slits, "xpseudo");
06317             cpl_table_erase_column(slits, "ypseudo");
06318             positions = cpl_table_duplicate(slits);
06319             cpl_table_erase_column(slits, "xcenter");
06320             cpl_table_erase_column(slits, "ycenter");
06321             cpl_msg_warning(func, "(the failure could be caused "
06322                             "by a mask with regularly located slits)");
06323             return NULL;
06324         }
06325     }
06326     else {
06327         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06328                      "candidates (%d expected)", found_slits, nslits, 
06329                      nmaskslits);
06330     }
06331 
06332 
06333     /*
06334      * Create a table with the coordinates of the preliminarily identified 
06335      * slits, both on CCD and mask. The original order of the slits positions 
06336      * is preserved.
06337      */
06338 
06339     positions = cpl_table_new(found_slits);
06340     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
06341     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
06342     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
06343     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
06344     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
06345     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
06346     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
06347     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
06348     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
06349     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
06350     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
06351     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
06352     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
06353     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
06354     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
06355 
06356     slit_id = cpl_table_get_data_int   (slits, "slit_id");
06357     xtop    = cpl_table_get_data_double(slits, "xtop");
06358     ytop    = cpl_table_get_data_double(slits, "ytop");
06359     xbottom = cpl_table_get_data_double(slits, "xbottom");
06360     ybottom = cpl_table_get_data_double(slits, "ybottom");
06361     xcenter = cpl_table_get_data_double(slits, "xcenter");
06362     ycenter = cpl_table_get_data_double(slits, "ycenter");
06363 
06364     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
06365     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
06366     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
06367     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
06368     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
06369     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
06370     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
06371 
06372 
06373     /*
06374      * Transferring the valid slits information to the new table.
06375      * Note that invalid elements are coded as 0 in the internal
06376      * buffer, and this is the way they are recognised and excluded.
06377      */
06378 
06379     k = 0;
06380     cpl_table_fill_invalid_int(slits, "slit_id", 0);
06381     for (i = 0; i < nmaskslits; i++) {
06382         for (j = 0; j < nslits; j++) {
06383             if (slit_id[j] == 0)
06384                 continue; /* Skip invalid slit */
06385             if (mslit_id[i] == slit_id[j]) {
06386                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
06387 
06388                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
06389                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
06390                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
06391                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
06392                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
06393                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
06394 
06395                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
06396                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
06397                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
06398                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
06399                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
06400                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
06401 
06402                 k++;
06403 
06404                 break;
06405             }
06406         }
06407     }
06408 
06409     found_slits = k;
06410 
06411     cpl_table_erase_column(slits, "slit_id");
06412     cpl_table_erase_column(slits, "xpseudo");
06413     cpl_table_erase_column(slits, "ypseudo");
06414     cpl_table_erase_column(slits, "xcenter");
06415     cpl_table_erase_column(slits, "ycenter");
06416     cpl_table_erase_column(maskslits, "xpseudo");
06417     cpl_table_erase_column(maskslits, "ypseudo");
06418     cpl_table_erase_column(maskslits, "xcenter");
06419     cpl_table_erase_column(maskslits, "ycenter");
06420 
06421 
06422     /*
06423      * Find the median platescale and rotation angle from the identified 
06424      * slits, and then exclude slits outlaying more than 10% from the 
06425      * median platescale, and more than 2 degrees from the median
06426      * rotation angle.
06427      */
06428 
06429     ytop    = cpl_table_get_data_double(positions, "ytop");
06430     ybottom = cpl_table_get_data_double(positions, "ybottom");
06431     xcenter = cpl_table_get_data_double(positions, "xcenter");
06432     ycenter = cpl_table_get_data_double(positions, "ycenter");
06433     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06434     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06435 
06436     scales = cpl_vector_new(found_slits - 1);
06437     dscale = cpl_vector_get_data(scales);
06438     angles = cpl_vector_new(found_slits - 1);
06439     dangle = cpl_vector_get_data(angles);
06440 
06441     for (i = 1; i < found_slits; i++) {
06442         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06443               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06444         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
06445               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
06446         dscale[i-1] = sqrt(dist1/dist2);
06447         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
06448                             xcenter[i-1] - xcenter[i])
06449                     - atan2(ymcenter[i-1] - ymcenter[i], 
06450                             xmcenter[i-1] - xmcenter[i]);
06451         dangle[i-1] *= 180;
06452         dangle[i-1] /= pi;
06453     }
06454 
06455     minscale = cpl_vector_get_min(scales);
06456     scale = cpl_vector_get_median_const(scales);
06457     maxscale = cpl_vector_get_max(scales);
06458 
06459     minangle = cpl_vector_get_min(angles);
06460     angle = cpl_vector_get_median_const(angles);
06461     maxangle = cpl_vector_get_max(angles);
06462 
06463     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
06464     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
06465                  minscale, maxscale);
06466 
06467     cpl_msg_info(func, "Median rotation: %f degrees", angle);
06468     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
06469                  minangle, maxangle);
06470 
06471     good = cpl_table_get_data_int(positions, "good");
06472 
06473     good[0] = good[found_slits - 1] = 1;
06474     for (i = 1; i < found_slits; i++) {
06475         if (fabs((dscale[i-1] - scale)/scale) < 0.10
06476          && fabs(dangle[i-1] - angle) < 2) {
06477             good[i-1]++;
06478             good[i]++;
06479         }
06480     }
06481 
06482     for (i = 0; i < found_slits; i++) {
06483         if (good[i] < 2)
06484             good[i] = 0;
06485         else
06486             good[i] = 1;
06487     }
06488 
06489 /*
06490     for (i = 1; i < found_slits; i++)
06491         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
06492             good[i-1] = good[i] = 1;
06493 */
06494 
06495 /* DEBUG ************+
06496     for (i = 0; i < found_slits; i++) {
06497         if (good[i]) {
06498             if (i == found_slits - 1)
06499                 printf("include slit %d, prev = %f, %f\n", 
06500                        i, dscale[i-1], dangle[i-1]);
06501             else if (i == 0)
06502                 printf("include slit %d, next %f, %f\n", 
06503                        i, dscale[i], dangle[i]);
06504             else
06505                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
06506                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06507         }
06508         else {
06509             if (i == found_slits - 1)
06510                 printf("EXclude slit %d, prev = %f, %f\n", 
06511                        i, dscale[i-1], dangle[i-1]);
06512             else if (i == 0)
06513                 printf("EXclude slit %d, next %f, %f\n", 
06514                        i, dscale[i], dangle[i]);
06515             else
06516                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
06517                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06518         }
06519     }
06520 +*********** DEBUG */
06521 
06522     cpl_vector_delete(scales);
06523     cpl_vector_delete(angles);
06524 
06525     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
06526     cpl_table_erase_selected(positions);
06527     cpl_table_erase_column(positions, "good");
06528     found_slits = cpl_table_get_nrow(positions);
06529 
06530     if (found_slits < 4) {
06531 
06532         /*
06533          * If the self-consistency check gives such a poor result,
06534          * something must have gone really wrong in the preliminary
06535          * wavelength calibration... Nothing can be done.
06536          */
06537 
06538         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
06539                         "candidates (%d expected). Process will continue "
06540                         "using the detected CCD slits positions", found_slits, 
06541                         nslits, nmaskslits);
06542         cpl_table_delete(positions);
06543         return NULL;
06544     }
06545     else {
06546         cpl_msg_info(func, "Safely identified slits: %d out of %d "
06547                      "candidates\n(%d expected)", found_slits, nslits,
06548                      nmaskslits);
06549     }
06550 
06551 
06552     /*
06553      * Now select the central points of the identified slits, and
06554      * fit two bivariate polynomials to determine a first approximate
06555      * relation between positions on the mask and positions on the CCD.
06556      */
06557 
06558     xpos = cpl_vector_wrap(found_slits, 
06559                            cpl_table_get_data_double(positions, "xcenter"));
06560     ypos = cpl_vector_wrap(found_slits, 
06561                            cpl_table_get_data_double(positions, "ycenter"));
06562     xmpos = cpl_vector_wrap(found_slits, 
06563                             cpl_table_get_data_double(positions, "xmcenter"));
06564     ympos = cpl_vector_wrap(found_slits, 
06565                             cpl_table_get_data_double(positions, "ymcenter"));
06566     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
06567 
06568     if (found_slits < 10)
06569         degree = 1;
06570     else
06571         degree = 2;
06572 
06573     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06574     if (xpoly != NULL)
06575         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06576     cpl_bivector_unwrap_vectors(mpos);
06577     cpl_vector_unwrap(xpos);
06578     cpl_vector_unwrap(ypos);
06579     cpl_vector_unwrap(xmpos);
06580     cpl_vector_unwrap(ympos);
06581     if (ypoly == NULL) {
06582         if (found_slits == nmaskslits) {
06583             cpl_msg_warning(func, "Fit failure: the accuracy of the "
06584                             "identified slits positions is not improved.");
06585 
06586             /*
06587              * The determination of the transformation from mask to CCD
06588              * failed, but since all slits have been already identified
06589              * this is not a problem: data can be reduced also without
06590              * such transformation. Simply the accuracy of the slits 
06591              * positions cannot be improved.
06592              */ 
06593 
06594         } else {
06595             cpl_msg_info(func, "Fit failure: not all slits have been "
06596                          "identified. Process will continue using "
06597                          "the detected CCD slits positions");
06598         }
06599 
06600         cpl_polynomial_delete(xpoly);
06601         return positions;
06602     }
06603 
06604     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
06605                  sqrt(xmse), sqrt(ymse));
06606 
06607     if (global) {
06608         write_global_distortion(global, 0, xpoly);
06609         write_global_distortion(global, 7, ypoly);
06610     }
06611 
06612     /*
06613      * The fit was successful: use the polynomials to obtain a new 
06614      * position table with the improved positions of the slits
06615      */
06616 
06617     cpl_table_delete(positions);
06618 
06619     positions = cpl_table_duplicate(maskslits);
06620     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06621     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06622     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06623     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06624 
06625     point = cpl_vector_new(2);
06626     dpoint = cpl_vector_get_data(point);
06627 
06628     for (i = 0; i < nmaskslits; i++) {
06629         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06630         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06631         cpl_table_set_double(positions, "xtop", i, 
06632                              cpl_polynomial_eval(xpoly, point));
06633         cpl_table_set_double(positions, "ytop", i, 
06634                              cpl_polynomial_eval(ypoly, point));
06635         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06636         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06637         cpl_table_set_double(positions, "xbottom", i, 
06638                              cpl_polynomial_eval(xpoly, point));
06639         cpl_table_set_double(positions, "ybottom", i, 
06640                              cpl_polynomial_eval(ypoly, point));
06641     }
06642 
06643     cpl_vector_delete(point);
06644     cpl_polynomial_delete(xpoly);
06645     cpl_polynomial_delete(ypoly);
06646 
06647     cpl_table_erase_column(positions, "xmtop");
06648     cpl_table_erase_column(positions, "ymtop");
06649     cpl_table_erase_column(positions, "xmbottom");
06650     cpl_table_erase_column(positions, "ymbottom");
06651 
06652     if (nmaskslits > nslits)
06653         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
06654                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
06655     else if (nmaskslits < nslits)
06656         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
06657                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
06658     else
06659         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
06660                  nmaskslits, nmaskslits);
06661 
06662     return positions;
06663 }
06664 
06665 
06707 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
06708                           double blue, double red, double dispersion)
06709 {
06710 
06711     const char  *func = "mos_trace_flat";
06712 
06713     cpl_image   *gradient;
06714     cpl_image   *sgradient;
06715     float       *dgradient;
06716     float        level = 500;   /* It was 250... */
06717     cpl_vector  *row;
06718     cpl_vector  *srow;
06719     cpl_vector **peaks;
06720     double      *peak;
06721     int         *slit_id;
06722     float       *g;
06723     double      *r;
06724     double      *xtop;
06725     double      *ytop;
06726     double      *xbottom;
06727     double      *ybottom;
06728     double       min, dist;
06729     double       sradius;
06730     double       tolerance;
06731     double       start_y, prev_y;
06732     int          minpos;
06733     int          nslits;
06734     int          nrows;
06735     int          step = 10;
06736     int          filtbox = 15;
06737     int          nx, ny, npix;
06738     int          pos, ypos;
06739     int          npeaks;
06740     int          pixel_above, pixel_below;
06741     int          i, j, k, l;
06742     char         trace_id[MAX_COLNAME];
06743 
06744     cpl_table   *traces;
06745 
06746 
06747     if (flat == NULL || slits == NULL) {
06748         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
06749         return NULL;
06750     }
06751 
06752     if (dispersion <= 0.0) {
06753         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06754         return NULL;
06755     }
06756 
06757     if (red - blue < dispersion) {
06758         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06759         return NULL;
06760     }
06761 
06762     /*
06763      * Create a dummy slit_id column if it is missing in the
06764      * input slits table
06765      */
06766 
06767     nslits  = cpl_table_get_nrow(slits);
06768     if (1 != cpl_table_has_column(slits, "slit_id")) {
06769         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06770         for (i = 0; i < nslits; i++)
06771             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
06772     }
06773 
06774     slit_id = cpl_table_get_data_int(slits, "slit_id");
06775 
06776     nx = cpl_image_get_size_x(flat);
06777     ny = cpl_image_get_size_y(flat);
06778     npix = nx * ny;
06779 
06780     gradient = cpl_image_duplicate(flat);
06781     dgradient = cpl_image_get_data_float(gradient);
06782 
06783     for (i = 0; i < ny - 1; i++) {
06784         k = i * nx;
06785         for (j = 0; j < nx; j++) {
06786             l = k + j;
06787             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
06788         }
06789     }
06790 
06791     npix--;
06792     for (j = 0; j < nx; j++)
06793         dgradient[npix - j] = 0.0;
06794 
06795     cpl_image_turn(gradient, -1);
06796     nx = cpl_image_get_size_x(gradient);
06797     ny = cpl_image_get_size_y(gradient);
06798     sgradient = mos_image_vertical_median_filter(gradient, 
06799                                                  filtbox, 0, ny, 0, step);
06800     cpl_image_delete(gradient);
06801 
06802 
06803     /*
06804      * Remove background from processed image rows
06805      */
06806 
06807     dgradient = cpl_image_get_data_float(sgradient);
06808 
06809     for (i = 1; i <= ny; i += step) {
06810         row = cpl_vector_new_from_image_row(sgradient, i);
06811         srow = cpl_vector_filter_median_create(row, filtbox);
06812         cpl_vector_subtract(row, srow);
06813         cpl_vector_delete(srow);
06814         g = dgradient + (i-1)*nx;
06815         r = cpl_vector_get_data(row);
06816         for (j = 0; j < nx; j++)
06817             g[j] = r[j];
06818         cpl_vector_delete(row);
06819     }
06820 
06821 
06822     /*
06823      * Rotate (temporarily) the input slits table, to get coordinates
06824      * compatible with the rotated gradient image.
06825      */
06826 
06827     mos_rotate_slits(slits, 1, nx, ny);
06828     xtop    = cpl_table_get_data_double(slits, "xtop");
06829     ytop    = cpl_table_get_data_double(slits, "ytop");
06830     xbottom = cpl_table_get_data_double(slits, "xbottom");
06831     ybottom = cpl_table_get_data_double(slits, "ybottom");
06832 
06833 
06834     /*
06835      * Get positions of peaks candidates for each processed gradient
06836      * image row
06837      */
06838 
06839     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
06840 
06841     for (i = 0; i < ny; i += step) {
06842         g = dgradient + i*nx;
06843         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
06844 
06845         /* I thought this would be required, but apparently I was wrong.
06846          * Check twice... */
06847         if (peaks[i])
06848             cpl_vector_subtract_scalar(peaks[i], 0.5);
06849          
06850     }
06851 
06852     cpl_image_delete(sgradient);
06853 
06854 
06855     /*
06856      * Tracing the flat field spectra edges, starting from the
06857      * slits positions obtained at reference wavelength. The 
06858      * gradient maximum closest to each slits ends is found and
06859      * accepted only within a given search radius:
06860      */
06861 
06862     sradius = 5.0;  /* Pixel */
06863 
06864     /*
06865      * The tracing proceeds along the processed gradient image rows,
06866      * above and below the start position, finding the closest peak
06867      * to the previous obtained position, accepting the new position
06868      * only if it is closer than a given tolerance:
06869      */
06870 
06871     tolerance = 0.9;  /* Pixel */
06872 
06873     /*
06874      * The trace is attempted for a certain number of pixels above
06875      * and below the position of the reference wavelength:
06876      */
06877 
06878     pixel_above = (red - reference) / dispersion;
06879     pixel_below = (reference - blue) / dispersion;
06880 
06881 
06882     /*
06883      * Prepare the structure of the output table:
06884      */
06885 
06886     nrows = (ny-1)/step + 1;
06887     traces = cpl_table_new(nrows);
06888     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
06889     cpl_table_set_column_unit(traces, "x", "pixel");
06890     for (i = 0, j = 0; i < ny; i += step, j++)
06891         cpl_table_set(traces, "x", j, i);
06892 
06893     for (i = 0; i < nslits; i++) {
06894 
06895         /*
06896          * Find the closest processed gradient image row
06897          */
06898 
06899         ypos = ytop[i];
06900 
06901         if (ypos < 0)
06902             ypos = 0;
06903         if (ypos >= ny)
06904             ypos = ny - 1;
06905 
06906         pos = ypos / step;
06907         pos *= step;
06908 
06909         /*
06910          * Find the peak in that row that is closest to xtop[i]
06911          */
06912 
06913         if (peaks[pos]) {
06914             peak = cpl_vector_get_data(peaks[pos]);
06915             npeaks = cpl_vector_get_size(peaks[pos]);
06916 
06917             min = fabs(peak[0] - xtop[i]);
06918             minpos = 0;
06919             for (j = 1; j < npeaks; j++) {
06920                 dist = fabs(peak[j] - xtop[i]);
06921                 if (min > dist) {
06922                     min = dist;
06923                     minpos = j;
06924                 }
06925             }
06926         }
06927         else {
06928             npeaks = 0;
06929         }
06930 
06931         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
06932         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
06933 
06934         if (min > sradius || npeaks == 0) {
06935             cpl_msg_warning(func, "Cannot find spectrum edge for "
06936                             "top (or left) end of slit %d", slit_id[i]);
06937         }
06938         else {
06939 
06940             /*
06941              * Add to output table the start y position. Note that
06942              * y positions are written in coordinates of the unrotated
06943              * image, i.e., with horizontal dispersion. Currently nx
06944              * is the x size of the temporarily rotated image (for
06945              * faster memory access), but it has the sense of a y size.
06946              */
06947 
06948             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
06949             start_y = peak[minpos];
06950 
06951             /*
06952              * Perform the tracing of current edge. Above:
06953              */
06954 
06955             prev_y = start_y;
06956 
06957             for (j = pos + step; j < ny; j += step) {
06958                 if (j - pos > pixel_above)
06959                     break;
06960                 if (peaks[j]) {
06961                     peak = cpl_vector_get_data(peaks[j]);
06962                     npeaks = cpl_vector_get_size(peaks[j]);
06963                     min = fabs(peak[0] - prev_y);
06964                     minpos = 0;
06965                     for (k = 1; k < npeaks; k++) {
06966                         dist = fabs(peak[k] - prev_y);
06967                         if (min > dist) {
06968                             min = dist;
06969                             minpos = k;
06970                         }
06971                     }
06972                     if (min < tolerance) {
06973                         cpl_table_set(traces, trace_id, j/step, 
06974                                       nx - peak[minpos]);
06975                         prev_y = peak[minpos];
06976                     }
06977                 }
06978             }
06979 
06980             /*
06981              * Perform the tracing of current edge. Below:
06982              */
06983 
06984             prev_y = start_y;
06985 
06986             for (j = pos - step; j >= 0; j -= step) {
06987                 if (pos - j > pixel_below)
06988                     break;
06989                 if (peaks[j]) {
06990                     peak = cpl_vector_get_data(peaks[j]);
06991                     npeaks = cpl_vector_get_size(peaks[j]);
06992                     min = fabs(peak[0] - prev_y);
06993                     minpos = 0;
06994                     for (k = 1; k < npeaks; k++) {
06995                         dist = fabs(peak[k] - prev_y);
06996                         if (min > dist) {
06997                             min = dist;
06998                             minpos = k;
06999                         }
07000                     }
07001                     if (min < tolerance) {
07002                         cpl_table_set(traces, trace_id, j/step, 
07003                                       nx - peak[minpos]);
07004                         prev_y = peak[minpos];
07005                     }
07006                 }
07007             }
07008         }
07009 
07010 
07011         /*
07012          * Find the peak in that row that is closest to xbottom[i]
07013          */
07014 
07015         if (peaks[pos]) {
07016             peak = cpl_vector_get_data(peaks[pos]);
07017             npeaks = cpl_vector_get_size(peaks[pos]);
07018     
07019             min = fabs(peak[0] - xbottom[i]);
07020             minpos = 0;
07021             for (j = 1; j < npeaks; j++) {
07022                 dist = fabs(peak[j] - xbottom[i]);
07023                 if (min > dist) {
07024                     min = dist;
07025                     minpos = j;
07026                 }
07027             }
07028         }
07029         else {
07030             npeaks = 0;
07031         }
07032 
07033         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07034         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07035 
07036         if (min > sradius || npeaks == 0) {
07037             cpl_msg_warning(func, "Cannot find spectrum edge for "
07038                             "bottom (or right) end of slit %d", slit_id[i]);
07039         }
07040         else {
07041 
07042             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07043             start_y = peak[minpos]; 
07044 
07045             /*
07046              * Perform the tracing of current edge. Above:
07047              */
07048 
07049             prev_y = start_y;
07050 
07051             for (j = pos + step; j < ny; j += step) {
07052                 if (j - pos > pixel_above)
07053                     break;
07054                 if (peaks[j]) {
07055                     peak = cpl_vector_get_data(peaks[j]);
07056                     npeaks = cpl_vector_get_size(peaks[j]);
07057                     min = fabs(peak[0] - prev_y);
07058                     minpos = 0;
07059                     for (k = 1; k < npeaks; k++) {
07060                         dist = fabs(peak[k] - prev_y);
07061                         if (min > dist) {
07062                             min = dist;
07063                             minpos = k;
07064                         }
07065                     }
07066                     if (min < tolerance) {
07067                         cpl_table_set(traces, trace_id, j/step, 
07068                                       nx - peak[minpos]);
07069                         prev_y = peak[minpos];
07070                     }
07071                 }
07072             }
07073 
07074             /*
07075              * Perform the tracing of current edge. Below:
07076              */
07077 
07078             prev_y = start_y;
07079 
07080             for (j = pos - step; j >= 0; j -= step) {
07081                 if (pos - j > pixel_below)
07082                     break;
07083                 if (peaks[j]) {
07084                     peak = cpl_vector_get_data(peaks[j]);
07085                     npeaks = cpl_vector_get_size(peaks[j]);
07086                     min = fabs(peak[0] - prev_y);
07087                     minpos = 0;
07088                     for (k = 1; k < npeaks; k++) {
07089                         dist = fabs(peak[k] - prev_y);
07090                         if (min > dist) {
07091                             min = dist;
07092                             minpos = k;
07093                         }
07094                     }
07095                     if (min < tolerance) {
07096                         cpl_table_set(traces, trace_id, j/step, 
07097                                       nx - peak[minpos]);
07098                         prev_y = peak[minpos];
07099                     }
07100                 }
07101             }
07102         }
07103 
07104     }   /* End of loop on slits */
07105 
07106     for (i = 0; i < ny; i += step)
07107         cpl_vector_delete(peaks[i]);
07108     cpl_free(peaks);
07109 
07110     /*
07111      * Restore original orientation of slits positions table
07112      */
07113 
07114     mos_rotate_slits(slits, -1, ny, nx);
07115 
07116     return traces;
07117 
07118 }
07119 
07120 
07141 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07142 {
07143     const char *func = "mos_poly_trace";
07144 
07145     cpl_table      *polytraces;
07146     cpl_table      *dummy;
07147     cpl_vector     *x;
07148     cpl_vector     *trace;
07149     cpl_polynomial *polytrace;
07150     char            trace_id[MAX_COLNAME];
07151     char            trace_res[MAX_COLNAME];
07152     char            trace_mod[MAX_COLNAME];
07153     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07154                                                  /* Max order is 5 */
07155     double         *xdata;
07156     int            *slit_id;
07157     int             nslits;
07158     int             nrows;
07159     int             npoints;
07160     int             i, j, k;
07161 
07162 
07163     if (traces == NULL || slits == NULL) {
07164         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07165         return NULL;
07166     }
07167 
07168     if (order > 5) {
07169         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07170         return NULL;
07171     }
07172 
07173     nrows   = cpl_table_get_nrow(traces);
07174     xdata   = cpl_table_get_data_double(traces, "x");
07175     nslits  = cpl_table_get_nrow(slits);
07176     slit_id = cpl_table_get_data_int(slits, "slit_id");
07177 
07178     polytraces = cpl_table_new(2*nslits);
07179     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07180     for (i = 0; i <= order; i++)
07181         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07182 
07183     for (i = 0; i < nslits; i++) {
07184         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07185 
07186             if (j) {
07187                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07188                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07189                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07190             }
07191             else {
07192                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07193                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07194                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07195             }
07196 
07197             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07198 
07199             /*
07200              * The "dummy" table is just a tool for eliminating invalid
07201              * points from the vectors to be fitted.
07202              */
07203 
07204             dummy = cpl_table_new(nrows);
07205             cpl_table_duplicate_column(dummy, "x", traces, "x");
07206             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07207             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07208             if (npoints < 2 * order) {
07209                 cpl_table_delete(dummy);
07210                 continue;
07211             }
07212             cpl_table_erase_invalid(dummy);
07213             x     = cpl_vector_wrap(npoints, 
07214                                     cpl_table_get_data_double(dummy, "x"));
07215             trace = cpl_vector_wrap(npoints, 
07216                                     cpl_table_get_data_double(dummy, trace_id));
07217             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07218             cpl_vector_unwrap(x);
07219             cpl_vector_unwrap(trace);
07220             cpl_table_delete(dummy);
07221 
07222             /*
07223              * Screen bad solutions. At the moment, a primitive screening
07224              * consists in excluding solutions displaying excessive
07225              * curvature (larger than 1E-5 / pixel)
07226              */
07227 
07228             k = 2;
07229             if (cpl_polynomial_get_coeff(polytrace, &k) >  1.E-5) {
07230                 cpl_polynomial_delete(polytrace);
07231                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07232                 cpl_table_duplicate_column(traces, trace_res, traces, 
07233                                            trace_mod);
07234                 if (j) 
07235                     cpl_msg_warning(func, "Exclude bad curvature solution "
07236                            "for bottom (right) edge of slit %d", slit_id[i]);
07237                 else
07238                     cpl_msg_warning(func, "Exclude bad curvature solution "
07239                                 "for top (left) edge of slit %d", slit_id[i]);
07240                 continue;
07241             }
07242 
07243             /*
07244              * Write polynomial coefficients to the output table,
07245              * tagged with the appropriate slit_id
07246              */
07247 
07248             for (k = 0; k <= order; k++)
07249                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07250                                      cpl_polynomial_get_coeff(polytrace, &k));
07251 
07252             /*
07253              * Add column of residuals to input traces table
07254              */
07255 
07256             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07257             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07258 
07259             for (k = 0; k < nrows; k++) {
07260                 cpl_table_set_double(traces, trace_mod, k,
07261                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07262             }
07263 
07264             cpl_polynomial_delete(polytrace);
07265 
07266             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07267             cpl_table_subtract_columns(traces, trace_res, trace_id);
07268             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07269 
07270         }
07271     }
07272 
07273     return polytraces;
07274 
07275 }
07276 
07277 
07301 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07302                                 int mode)
07303 {
07304     const char *func = "mos_global_trace";
07305 
07306     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07307                                                  /* Max order is 5 */
07308     cpl_table      *table;
07309     cpl_vector     *c0;
07310     cpl_vector     *cn;
07311     cpl_bivector   *list;
07312 /* alternative (not robust)
07313     cpl_polynomial *poly;
07314 */
07315 
07316     double *offset;
07317     double  rms, q, m;
07318 
07319     int order, nrows, nslits;
07320     int i, j;
07321 
07322 
07323     if (polytraces == NULL) {
07324         cpl_msg_error(func, "Missing spectral curvature table");
07325         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07326     }
07327 
07328     if (slits == NULL) {
07329         cpl_msg_error(func, "Missing slits positions table");
07330         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07331     }
07332 
07333     nslits = cpl_table_get_nrow(slits);
07334 
07335     table = cpl_table_duplicate(polytraces);
07336     cpl_table_erase_invalid(table);
07337 
07338     nrows = cpl_table_get_nrow(table);
07339 
07340     if (nrows < 4) {
07341         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
07342                       "(%d): the determination of a global curvature model "
07343                       "failed", nrows);
07344         return CPL_ERROR_NONE;
07345     }
07346     
07347     order = cpl_table_get_ncol(polytraces) - 2;
07348 
07349     for (i = 0; i <= order; i++) {
07350         if (!cpl_table_has_column(table, clab[i])) {
07351             cpl_msg_error(func, "Wrong spectral curvature table");
07352             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07353         }
07354     }
07355 
07356 
07357     /*
07358      * Fill in advance the missing offset terms
07359      */
07360 
07361     for (i = 0; i < nslits; i++) {
07362         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
07363             cpl_table_set_double(polytraces, clab[0], 2*i, 
07364                                 cpl_table_get_double(slits, "ytop", i, NULL));
07365         }
07366         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
07367             cpl_table_set_double(polytraces, clab[0], 2*i+1,
07368                              cpl_table_get_double(slits, "ybottom", i, NULL));
07369         }
07370     }
07371 
07372     offset = cpl_table_get_data_double(polytraces, clab[0]);
07373 
07374 
07375     /*
07376      * Fit the global model and modify polytraces table accordingly
07377      */
07378 
07379     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
07380 
07381     for (i = 1; i <= order; i++) {
07382         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
07383         list = cpl_bivector_wrap_vectors(c0, cn);
07384         robustLinearFit(list, &q, &m, &rms);
07385 /* alternative (not robust)
07386         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
07387 */
07388         for (j = 0; j < 2*nslits; j++) {
07389             if (mode == 1)
07390                 if (cpl_table_is_valid(polytraces, clab[i], j))
07391                     continue;
07392             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
07393 /* alternative (not robust)
07394             cpl_table_set_double(polytraces, clab[i], j, 
07395                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
07396 */
07397         }
07398         cpl_bivector_unwrap_vectors(list);
07399 /* alternative (not robust)
07400         cpl_polynomial_delete(poly);
07401 */
07402         cpl_vector_unwrap(cn);
07403     }
07404 
07405     cpl_vector_unwrap(c0);
07406     cpl_table_delete(table);
07407 
07408     return CPL_ERROR_NONE;
07409 
07410 }
07411 
07412 
07482 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
07483                                    cpl_table *polytraces, double reference,
07484                                    double blue, double red, double dispersion,
07485                                    int flux, cpl_image *calibration)
07486 {
07487     const char *func = "mos_spatial_calibration";
07488 
07489     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07490                                                  /* Max order is 5 */
07491     cpl_polynomial *polytop;
07492     cpl_polynomial *polybot;
07493     cpl_image     **exslit;
07494     cpl_image      *resampled;
07495     float          *data;
07496     float          *sdata;
07497     float          *xdata;
07498     double          vtop, vbot, value;
07499     double          top, bot;
07500     double          coeff;
07501     double          ytop, ybot;
07502     double          ypos, yfra;
07503     double          factor;
07504     int             yint, ysize, yprev;
07505     int             nslits;
07506     int             npseudo;
07507     int            *slit_id;
07508     int            *length;
07509     int             nx, ny;
07510     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
07511     int             missing_top, missing_bot;
07512     int             null;
07513     int             order;
07514     int             i, j, k;
07515 
07516     int             create_position = 1;
07517 
07518 
07519     if (spectra == NULL || slits == NULL || polytraces == NULL) {
07520         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07521         return NULL;
07522     }
07523 
07524     if (dispersion <= 0.0) {
07525         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07526         return NULL;
07527     }
07528 
07529     if (red - blue < dispersion) {
07530         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07531         return NULL;
07532     }
07533 
07534     nx = cpl_image_get_size_x(spectra);
07535     ny = cpl_image_get_size_y(spectra);
07536     sdata = cpl_image_get_data(spectra);
07537     if (calibration)
07538         data = cpl_image_get_data(calibration);
07539 
07540     if (cpl_table_has_column(slits, "position"))
07541         create_position = 0;
07542 
07543     if (create_position) {
07544         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
07545         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
07546         cpl_table_set_column_unit(slits, "position", "pixel");
07547         cpl_table_set_column_unit(slits, "length", "pixel");
07548     }
07549     else
07550         length = cpl_table_get_data_int(slits, "length");
07551 
07552     nslits   = cpl_table_get_nrow(slits);
07553     slit_id  = cpl_table_get_data_int(slits, "slit_id");
07554     order    = cpl_table_get_ncol(polytraces) - 2;
07555 
07556     /*
07557      * The spatial resampling is performed for a certain number of 
07558      * pixels above and below the position of the reference wavelength:
07559      */
07560 
07561     pixel_above = (red - reference) / dispersion;
07562     pixel_below = (reference - blue) / dispersion;
07563 
07564     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
07565 
07566     for (i = 0; i < nslits; i++) {
07567         
07568         if (create_position == 0)
07569             if (length[i] == 0)
07570                 continue;
07571 
07572         /*
07573          * Note that the x coordinate of the reference pixels on the CCD
07574          * is taken arbitrarily at the top end of each slit. This wouldn't
07575          * be entirely correct in case of curved slits, or in presence of
07576          * heavy distortions: in such cases the spatial resampling is
07577          * really performed across a wide range of wavelengths. But
07578          * the lag between top and bottom spectral curvature models 
07579          * would introduce even in such cases negligible effects on
07580          * the spectral spatial resampling.
07581          */
07582 
07583         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
07584 
07585         start_pixel = refpixel - pixel_below;
07586         if (start_pixel < 0)
07587             start_pixel = 0;
07588 
07589         end_pixel = refpixel + pixel_above;
07590         if (end_pixel > nx)
07591             end_pixel = nx;
07592 
07593         /*
07594          * Recover from the table of spectral curvature coefficients
07595          * the curvature polynomials.
07596          */
07597 
07598         missing_top = 0;
07599         polytop = cpl_polynomial_new(1);
07600         for (k = 0; k <= order; k++) {
07601             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
07602             if (null) {
07603                 cpl_polynomial_delete(polytop);
07604                 missing_top = 1;
07605                 break;
07606             }
07607             cpl_polynomial_set_coeff(polytop, &k, coeff);
07608         }
07609 
07610         missing_bot = 0;
07611         polybot = cpl_polynomial_new(1);
07612         for (k = 0; k <= order; k++) {
07613             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
07614             if (null) {
07615                 cpl_polynomial_delete(polybot);
07616                 missing_bot = 1;
07617                 break;
07618             }
07619             cpl_polynomial_set_coeff(polybot, &k, coeff);
07620         }
07621 
07622         if (missing_top && missing_bot) {
07623             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
07624                             "traced: no extraction!", 
07625                             slit_id[i]);
07626             continue;
07627         }
07628 
07629         /*
07630          * In case just one of the two edges was not traced, the other
07631          * edge curvature model is duplicated and shifted to the other
07632          * end of the slit: better than nothing!
07633          */
07634 
07635         if (missing_top) {
07636             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
07637                             "the spectral curvature of the lower edge "
07638                             "is used instead.", slit_id[i]);
07639             polytop = cpl_polynomial_duplicate(polybot);
07640             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
07641             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
07642             k = 0;
07643             coeff = cpl_polynomial_get_coeff(polybot, &k);
07644             coeff += ytop - ybot;
07645             cpl_polynomial_set_coeff(polytop, &k, coeff);
07646         }
07647 
07648         if (missing_bot) {
07649             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
07650                             "the spectral curvature of the upper edge "
07651                             "is used instead.", slit_id[i]);
07652             polybot = cpl_polynomial_duplicate(polytop);
07653             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
07654             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
07655             k = 0;
07656             coeff = cpl_polynomial_get_coeff(polytop, &k);
07657             coeff -= ytop - ybot;
07658             cpl_polynomial_set_coeff(polybot, &k, coeff);
07659         }
07660 
07661         /*
07662          * Allocate image for current extracted slit
07663          */
07664 
07665         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
07666         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
07667         npseudo = ceil(top-bot) + 1;
07668 
07669         if (npseudo < 1) {
07670             cpl_polynomial_delete(polytop);
07671             cpl_polynomial_delete(polybot);
07672             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
07673                             slit_id[i]);
07674             continue;
07675         }
07676 
07677         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
07678         xdata = cpl_image_get_data(exslit[i]);
07679 
07680         /*
07681          * Write interpolated values to slit image.
07682          */
07683 
07684         for (j = start_pixel; j < end_pixel; j++) {
07685             top = cpl_polynomial_eval_1d(polytop, j, NULL);
07686             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
07687             factor = (top-bot)/npseudo;
07688             for (k = 0; k <= npseudo; k++) {
07689                 ypos = top - k*factor;
07690                 yint = ypos;
07691                 yfra = ypos - yint;
07692                 if (yint >= 0 && yint < ny-1) {
07693                     vtop = sdata[j + nx*yint];
07694                     vbot = sdata[j + nx*(yint+1)];
07695                     value = vtop*(1-yfra) + vbot*yfra;
07696                     if (flux)
07697                         value *= factor;
07698                     xdata[j + nx*(npseudo-k)] = value;
07699                     if (calibration) {
07700                         data[j + nx*yint] = (top-yint)/factor;
07701                         if (k) {
07702 
07703                             /*
07704                              * This is added to recover lost pixels on
07705                              * the CCD image (pixels are lost because
07706                              * the CCD pixels are less than npseudo+1).
07707                              */
07708 
07709                             if (yprev - yint > 1) {
07710                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
07711                             }
07712                         }
07713                     }
07714                 }
07715                 yprev = yint;
07716             }
07717         }
07718         cpl_polynomial_delete(polytop);
07719         cpl_polynomial_delete(polybot);
07720     }
07721 
07722     /*
07723      * Now all the slits images are copied to a single image
07724      */
07725 
07726     ysize = 0;
07727     for (i = 0; i < nslits; i++)
07728         if (exslit[i])
07729             ysize += cpl_image_get_size_y(exslit[i]);
07730 
07731     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
07732 
07733     yint = -1;
07734     for (i = 0; i < nslits; i++) {
07735         if (exslit[i]) {
07736             yint += cpl_image_get_size_y(exslit[i]);
07737             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
07738             if (create_position) {
07739                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
07740                 cpl_table_set_int(slits, "length", i, 
07741                                   cpl_image_get_size_y(exslit[i]));
07742             }
07743             cpl_image_delete(exslit[i]);
07744         }
07745         else if (create_position) {
07746             cpl_table_set_int(slits, "position", i, -1);
07747             cpl_table_set_int(slits, "length", i, 0);
07748         }
07749     }
07750 
07751 
07752     /*
07753      * Elimination of non-traced slits from slit position table: we cannot do
07754      * it because we would lose sync with polytraces and other slit-oriented
07755      * tables. COMMENTED OUT.
07756      * 
07757 
07758     if (create_position) {
07759 
07760         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
07761             cpl_table_erase_selected(slits);
07762 
07763     }
07764 
07765     */
07766 
07767     cpl_free(exslit);
07768 
07769     return resampled;
07770 
07771 }
07772 
07773 
07880 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
07881                                             cpl_vector *lines,
07882                                             double dispersion, float level,
07883                                             int sradius, int order,
07884                                             double reject, double refwave,
07885                                             double *wavestart, double *waveend,
07886                                             int *nlines, double *error, 
07887                                             cpl_table *idscoeff,
07888                                             cpl_image *calibration,
07889                                             cpl_image *residuals,
07890                                             cpl_table *restable)
07891 {
07892 
07893     const char *func = "mos_wavelength_calibration_final";
07894 
07895     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07896                                                  /* Max order is 5 */
07897 
07898     double  tolerance = 20.0;    /* Probably forever...                */
07899     int     step = 10;           /* Compute restable every "step" rows */
07900 
07901     char            name[MAX_COLNAME];
07902 
07903     cpl_image      *resampled;
07904     cpl_bivector   *output;
07905     cpl_vector     *wavel;
07906     cpl_vector     *peaks;
07907     cpl_polynomial *ids;
07908     cpl_polynomial *lin;
07909     cpl_polynomial *fguess;
07910     cpl_table      *coeff;
07911     double          ids_err;
07912     double          max_disp, min_disp;
07913     double         *line;
07914     double          firstLambda, lastLambda, lambda;
07915     double          wave, pixe, value;
07916     double          c;
07917     float          *sdata;
07918     float          *rdata;
07919     float          *idata;
07920     float          *ddata;
07921     float           v1, v2, vi;
07922     float           fpixel;
07923     int            *length;
07924     int             pixstart, pixend;
07925     int             row_top, row_bot;
07926     int             extrapolation;
07927     int             nref;
07928     int             nslits;
07929     int             nfits;
07930     int             nl, nx, ny, pixel;
07931     int             countLines, usedLines;
07932     int             uorder;
07933     int             missing;
07934     int             null;
07935     int             width, uradius;
07936     int             i, j, k, s;
07937 
07938 
07939     if (dispersion == 0.0) {
07940         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
07941         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07942         return NULL;
07943     }
07944 
07945     if (dispersion < 0.0) {
07946         cpl_msg_error(func, "The expected dispersion must be positive");
07947         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07948         return NULL;
07949     }
07950 
07951     if (idscoeff == NULL) {
07952         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
07953         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07954         return NULL;
07955     }
07956 
07957     max_disp = dispersion + dispersion * tolerance / 100;
07958     min_disp = dispersion - dispersion * tolerance / 100;
07959 
07960     if (order < 1) {
07961         cpl_msg_error(func, "The order of the fitting polynomial "
07962                       "must be at least 1");
07963         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07964         return NULL;
07965     }
07966 
07967     if (image == NULL || lines == NULL) {
07968         cpl_msg_error(func, "Both spectral exposure and reference line "
07969                       "catalog are required in input");
07970         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07971         return NULL;
07972     }
07973 
07974     nx = cpl_image_get_size_x(image);
07975     ny = cpl_image_get_size_y(image);
07976     sdata = cpl_image_get_data_float(image);
07977 
07978     nref = cpl_vector_get_size(lines);
07979     line = cpl_vector_get_data(lines);
07980 
07981     if (*wavestart < 1.0 && *waveend < 1.0) {
07982         firstLambda = line[0];
07983         lastLambda = line[nref-1];
07984         extrapolation = (lastLambda - firstLambda) / 10;
07985         firstLambda -= extrapolation;
07986         lastLambda += extrapolation;
07987         *wavestart = firstLambda;
07988         *waveend = lastLambda;
07989     }
07990     else {
07991         firstLambda = *wavestart;
07992         lastLambda = *waveend;
07993     }
07994 
07995     nl = (lastLambda - firstLambda) / dispersion;
07996     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
07997     rdata = cpl_image_get_data_float(resampled);
07998 
07999     /*
08000      * Allocate total output table of IDS coefficients
08001      */
08002 
08003     for (j = 0; j <= order; j++)
08004         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08005 
08006     if (calibration)
08007         idata = cpl_image_get_data_float(calibration);
08008 
08009     if (residuals)
08010         ddata = cpl_image_get_data_float(residuals);
08011 
08012     if (restable) {
08013         cpl_table_set_size(restable, nref);
08014         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08015         cpl_table_copy_data_double(restable, "wavelength", line);
08016         for (i = 0; i < ny; i += step) {
08017              snprintf(name, MAX_COLNAME, "r%d", i);
08018              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08019              snprintf(name, MAX_COLNAME, "d%d", i);
08020              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08021              snprintf(name, MAX_COLNAME, "p%d", i);
08022              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08023         }
08024     }
08025 
08026 
08027     /*
08028      * Process all slits separately.
08029      */
08030 
08031     nslits   = cpl_table_get_nrow(slits);
08032     length   = cpl_table_get_data_int(slits, "length");
08033 
08034     row_top = ny;
08035     for (s = 0; s < nslits; s++) {
08036 
08037         if (length[s] == 0)
08038             continue;
08039 
08040         /*
08041          * row_top and row_bot define the boundaries of the current slit.
08042          * Here we begin (arbitrarily...) from the top slit.
08043          */
08044 
08045         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08046 
08047         if (sradius > 0) {
08048 
08049             /*
08050              * If a search radius was defined, allocate the table of
08051              * the fitting polynomials coefficients. This table is
08052              * just used to generate the first-guess polynomial made
08053              * of the median coefficients of all polynomials found
08054              * for this slit.
08055              */
08056 
08057             coeff = cpl_table_new(row_top - row_bot);
08058             for (j = 0; j <= order; j++)
08059                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08060         }
08061 
08062         /*
08063          * Here is the loop on all rows of the current slit. They are
08064          * wavelength calibrated one by one.
08065          */
08066 
08067         for (i = row_bot; i < row_top; i++) {
08068             width = mos_lines_width(sdata + i*nx, nx);
08069             if (width < 5)
08070                 width = 5;
08071             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08072             if (peaks) {
08073                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08074             }
08075             if (peaks) {
08076                 output = mos_identify_peaks(peaks, lines, 
08077                                             min_disp, max_disp, 0.05);
08078                 if (output) {
08079                     countLines = cpl_bivector_get_size(output);
08080                     if (countLines < 4) {
08081                         cpl_bivector_delete(output);
08082                         cpl_vector_delete(peaks);
08083                         if (nlines)
08084                             nlines[i] = 0;
08085                         if (error)
08086                             error[i] = 0.0;
08087                         continue;
08088                     }
08089 
08090                     /*
08091                      * Set reference wavelength as zero point
08092                      */
08093 
08094                     wavel = cpl_bivector_get_y(output);
08095                     cpl_vector_subtract_scalar(wavel, refwave);
08096 
08097                     uorder = countLines / 2 - 1;
08098                     if (uorder > order)
08099                         uorder = order;
08100 
08101                     ids = mos_poly_wav2pix(output, uorder, reject,
08102                                            2 * (uorder + 1), &usedLines,
08103                                            &ids_err);
08104 
08105                     if (ids == NULL) {
08106                         cpl_bivector_delete(output);
08107                         cpl_vector_delete(peaks);
08108                         if (nlines)
08109                             nlines[i] = 0;
08110                         if (error)
08111                             error[i] = 0.0;
08112                         cpl_error_reset();
08113                         continue;
08114                     }
08115 
08116                     if (sradius > 0) {
08117                         for (k = 0; k <= order; k++) {
08118                             if (k > uorder) {
08119                                 cpl_table_set_double(coeff, clab[k], 
08120                                 i - row_bot, 0.0);
08121                             }
08122                             else {
08123                                 cpl_table_set_double(coeff, clab[k], 
08124                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08125                             }
08126                         }
08127                     }
08128                /*   else {   */
08129                         if (calibration) {
08130                             pixstart = cpl_polynomial_eval_1d(ids,
08131                               cpl_bivector_get_y_data(output)[0], 
08132                               NULL);
08133                             pixend = cpl_polynomial_eval_1d(ids,
08134                               cpl_bivector_get_y_data(output)[countLines-1],
08135                               NULL);
08136                             extrapolation = (pixend - pixstart) / 5;
08137                             pixstart -= extrapolation;
08138                             pixend += extrapolation;
08139                             if (pixstart < 0)
08140                                 pixstart = 0;
08141                             if (pixend > nx)
08142                                 pixend = nx;
08143    
08144                             for (j = pixstart; j < pixend; j++) {
08145                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08146                                      firstLambda, lastLambda, refwave, j);
08147                             }
08148                         }
08149 
08150                         /*
08151                          * Residuals image
08152                          */
08153         
08154                         if (residuals || (restable && !(i%step))) {
08155                             if (restable && !(i%step)) {
08156                                 lin = cpl_polynomial_new(1);
08157                                 for (k = 0; k < 2; k++)
08158                                     cpl_polynomial_set_coeff(lin, &k,
08159                                           cpl_polynomial_get_coeff(ids, &k));
08160                             }
08161                             for (j = 0; j < countLines; j++) {
08162                                 pixe = cpl_bivector_get_x_data(output)[j];
08163                                 wave = cpl_bivector_get_y_data(output)[j];
08164                                 value = pixe 
08165                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08166                                 if (residuals) {
08167                                     pixel = pixe + 0.5;
08168                                     (ddata + i*nx)[pixel] = value;
08169                                 }
08170                                 if (restable && !(i%step)) {
08171                                     for (k = 0; k < nref; k++) {
08172                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08173                                             snprintf(name, MAX_COLNAME, 
08174                                                      "r%d", i);
08175                                             cpl_table_set_double(restable, name,
08176                                                                  k, value);
08177                                             value = pixe
08178                                                   - cpl_polynomial_eval_1d(lin,
08179                                                               wave, NULL);
08180                                             snprintf(name, MAX_COLNAME, 
08181                                                      "d%d", i);
08182                                             cpl_table_set_double(restable, name,
08183                                                                  k, value);
08184                                             snprintf(name, MAX_COLNAME,
08185                                                      "p%d", i);
08186                                             cpl_table_set_double(restable, name,
08187                                                                  k, pixe);
08188                                             break;
08189                                         }
08190                                     }
08191                                 }
08192                             }
08193                             if (restable && !(i%step)) {
08194                                 cpl_polynomial_delete(lin);
08195                             }
08196 /***
08197                             for (j = 0; j < countLines; j++) {
08198                                 pixel = cpl_bivector_get_x_data(output)[j] 
08199                                       + 0.5;
08200                                 (ddata + i*nx)[pixel] =
08201                                 cpl_bivector_get_x_data(output)[j]
08202                               - cpl_polynomial_eval_1d(ids,
08203                                 cpl_bivector_get_y_data(output)[j], 
08204                                 NULL);
08205                             }
08206 ***/
08207                         }
08208                 /*  }   */
08209 
08210                     /*
08211                      * Write it anyway, even in case a first-guess based
08212                      * solution will be searched afterwards: in case of
08213                      * failure, the "blind" solution is kept.
08214                      */
08215 
08216                     if (nlines)
08217                         nlines[i] = usedLines;
08218                     if (error)
08219                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08220 
08221                     for (k = 0; k <= order; k++) {
08222                         if (k > uorder) {
08223                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08224                         }
08225                         else {
08226                             cpl_table_set_double(idscoeff, clab[k], i,
08227                                       cpl_polynomial_get_coeff(ids, &k));
08228                         }
08229                     }
08230 
08231                     cpl_polynomial_delete(ids);
08232                     cpl_bivector_delete(output);
08233                 }
08234                 cpl_vector_delete(peaks);
08235             }
08236         }       /* End of loop on current slit's rows */
08237 
08238 
08239         if (sradius > 0) {
08240 
08241             /*
08242              * See whether there are valid fits at all...
08243              */
08244     
08245             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08246     
08247             if (nfits) {
08248 
08249                 /*
08250                  * Compute a median IDS polynomial for the current slit
08251                  */
08252 
08253                 fguess = cpl_polynomial_new(1);
08254 
08255                 for (k = 0; k <= order; k++) {
08256                     c = cpl_table_get_column_median(coeff, clab[k]);
08257                     cpl_polynomial_set_coeff(fguess, &k, c);
08258                 }
08259 
08260                 for (i = row_bot; i < row_top; i++) {
08261 
08262                     /*
08263                      * Use first-guess to find the reference lines again
08264                      */
08265 
08266                     width = mos_lines_width(sdata + i*nx, nx);
08267                     if (width > sradius) {
08268                         uradius = width; 
08269                     }
08270                     else {
08271                         uradius = sradius;
08272                     }
08273 
08274                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08275                                             fguess, refwave, uradius);
08276 
08277                     if (output == NULL) {
08278                         cpl_error_reset();
08279                         continue;
08280                     }
08281 
08282                     countLines = cpl_bivector_get_size(output);
08283 
08284                     if (countLines < 4) {
08285                         cpl_bivector_delete(output);
08286                         continue;
08287                     }
08288 
08289                     /*
08290                      * Set reference wavelength as zero point
08291                      */
08292 
08293                     wavel = cpl_bivector_get_y(output);
08294                     cpl_vector_subtract_scalar(wavel, refwave);
08295 
08296                     uorder = countLines / 2 - 1;
08297                     if (uorder > order)
08298                         uorder = order;
08299 
08300                     ids = mos_poly_wav2pix(output, uorder, reject,
08301                                            2 * (uorder + 1), &usedLines,
08302                                            &ids_err);
08303 
08304                     if (ids == NULL) {
08305                         cpl_error_reset();
08306                         cpl_bivector_delete(output);
08307                         continue;
08308                     }
08309 
08310                     if (nlines)
08311                         nlines[i] = usedLines;
08312                     if (error)
08313                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08314 
08315                     if (calibration) {
08316                         pixstart = cpl_polynomial_eval_1d(ids,
08317                            cpl_bivector_get_y_data(output)[0], 
08318                            NULL);
08319                         pixend = cpl_polynomial_eval_1d(ids,
08320                            cpl_bivector_get_y_data(output)[countLines-1], 
08321                            NULL);
08322                         extrapolation = (pixend - pixstart) / 5;
08323                         pixstart -= extrapolation;
08324                         pixend += extrapolation;
08325                         if (pixstart < 0)
08326                             pixstart = 0;
08327                         if (pixend > nx)
08328                             pixend = nx;
08329 
08330                         for (j = pixstart; j < pixend; j++) {
08331                             (idata + i*nx)[j] = mos_eval_dds(ids,
08332                                      firstLambda, lastLambda, refwave, j);
08333                         }
08334                     }
08335 
08336                     /*
08337                      * Residuals image
08338                      */
08339 
08340                     if (residuals || (restable && !(i%step))) {
08341                         if (restable && !(i%step)) {
08342                             lin = cpl_polynomial_new(1);
08343                             for (k = 0; k < 2; k++)
08344                                 cpl_polynomial_set_coeff(lin, &k,
08345                                       cpl_polynomial_get_coeff(ids, &k));
08346                         }
08347                         for (j = 0; j < countLines; j++) {
08348                             pixe = cpl_bivector_get_x_data(output)[j];
08349                             wave = cpl_bivector_get_y_data(output)[j];
08350                             value = pixe
08351                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
08352                             if (residuals) {
08353                                 pixel = pixe + 0.5;
08354                                 (ddata + i*nx)[pixel] = value;
08355                             }
08356                             if (restable && !(i%step)) {
08357                                 for (k = 0; k < nref; k++) {
08358                                     if (fabs(line[k]-refwave-wave) < 0.1) {
08359                                         snprintf(name, MAX_COLNAME,
08360                                                  "r%d", i);
08361                                         cpl_table_set_double(restable, name,
08362                                                              k, value);
08363                                         value = pixe
08364                                               - cpl_polynomial_eval_1d(lin,
08365                                                           wave, NULL);
08366                                         snprintf(name, MAX_COLNAME,
08367                                                  "d%d", i);
08368                                         cpl_table_set_double(restable, name,
08369                                                              k, value);
08370                                         snprintf(name, MAX_COLNAME,
08371                                                  "p%d", i);
08372                                         cpl_table_set_double(restable, name,
08373                                                              k, pixe);
08374                                         break;
08375                                     }
08376                                 }
08377                             }
08378                         }
08379                         if (restable && !(i%step)) {
08380                             cpl_polynomial_delete(lin);
08381                         }
08382 /***
08383                         for (j = 0; j < countLines; j++) {
08384                             pixel = cpl_bivector_get_x_data(output)[j]
08385                                   + 0.5; 
08386                             (ddata + i*nx)[pixel] =
08387                             cpl_bivector_get_x_data(output)[j]
08388                           - cpl_polynomial_eval_1d(ids,
08389                             cpl_bivector_get_y_data(output)[j], 
08390                             NULL);
08391                         }
08392 ***/
08393                     }
08394 
08395                     for (k = 0; k <= order; k++) {
08396                         if (k > uorder) {
08397                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08398                         }
08399                         else {
08400                             cpl_table_set_double(idscoeff, clab[k], i,
08401                                         cpl_polynomial_get_coeff(ids, &k));
08402                         }
08403                     }
08404 
08405                     cpl_bivector_delete(output);
08406                     cpl_polynomial_delete(ids);
08407 
08408                 } /* End of loop "use ids as a first-guess" */
08409 
08410                 cpl_polynomial_delete(fguess);
08411             }
08412 
08413             cpl_table_delete(coeff);
08414 
08415         }
08416 
08417         row_top = row_bot;
08418 
08419     } /* End of loop on slits */
08420 
08421 
08422     /*
08423      * At this point the idscoeff table has been filled with all the 
08424      * fits coefficients obtained for all the rows of the input image.
08425      * Now we apply these coefficients to resample the input image
08426      * at constant wavelength step.
08427      */
08428 
08429     for (i = 0; i < ny; i++) {
08430 
08431         missing = 0;
08432         ids = cpl_polynomial_new(1);
08433         for (k = 0; k <= order; k++) {
08434             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
08435             if (null) {
08436                 cpl_polynomial_delete(ids);
08437                 missing = 1;
08438                 break;
08439             }
08440             cpl_polynomial_set_coeff(ids, &k, c);
08441         }
08442         if (missing)
08443             continue;
08444 
08445         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
08446         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
08447         if (pixstart < 0)
08448             pixstart = 0;
08449         if (pixend > nx)
08450             pixend = nx;
08451 
08452         /*
08453          * Resampled image:
08454          */
08455 
08456         for (j = 0; j < nl; j++) {
08457             lambda = firstLambda + j * dispersion;
08458             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
08459             pixel = fpixel;
08460             if (pixel >= 0 && pixel < nx-1) {
08461                 v1 = (sdata + i*nx)[pixel];
08462                 v2 = (sdata + i*nx)[pixel+1];
08463                 vi = v1 + (v2-v1)*(fpixel-pixel);
08464                 (rdata + i*nl)[j] = vi;
08465             }
08466         }
08467 
08468         cpl_polynomial_delete(ids);
08469     }
08470 
08471     return resampled;
08472 }
08473 
08474 
08501 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
08502                                       double firstLambda, double lastLambda, 
08503                                       double dispersion, cpl_table *idscoeff, 
08504                                       int flux)
08505 {
08506 
08507     const char *func = "mos_wavelength_calibration";
08508 
08509     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08510                                                  /* Max order is 5 */
08511 
08512     cpl_image      *resampled;
08513     cpl_polynomial *ids;
08514     double          pixel_per_lambda;
08515     double          lambda;
08516     double          c;
08517     float          *sdata;
08518     float          *rdata;
08519     float           v0, v1, v2, v3, vi;
08520     float           fpixel;
08521     int             order;
08522     int             pixstart, pixend;
08523     int             nl, nx, ny, pixel;
08524     int             missing;
08525     int             null;
08526     int             i, j, k;
08527 
08528 
08529     if (dispersion <= 0.0) {
08530         cpl_msg_error(func, "The resampling step must be positive");
08531         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08532         return NULL;
08533     }
08534 
08535     if (lastLambda - firstLambda < dispersion) {
08536         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
08537                       firstLambda, lastLambda);
08538         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08539         return NULL;
08540     }
08541 
08542     if (idscoeff == NULL) {
08543         cpl_msg_error(func, "An IDS coeff table must be given");
08544         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08545         return NULL;
08546     }
08547 
08548     if (image == NULL) {
08549         cpl_msg_error(func, "A scientific spectral image must be given");
08550         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08551         return NULL;
08552     }
08553 
08554     nx = cpl_image_get_size_x(image);
08555     ny = cpl_image_get_size_y(image);
08556     sdata = cpl_image_get_data_float(image);
08557 
08558     nl = (lastLambda - firstLambda) / dispersion;
08559     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08560     rdata = cpl_image_get_data_float(resampled);
08561 
08562     order = 0;
08563     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
08564         ++order;
08565     --order;
08566 
08567     for (i = 0; i < ny; i++) {
08568 
08569         missing = 0;
08570         ids = cpl_polynomial_new(1);
08571         for (k = 0; k <= order; k++) {
08572             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
08573             if (null) {
08574                 cpl_polynomial_delete(ids);
08575                 missing = 1;
08576                 break;
08577             }
08578             cpl_polynomial_set_coeff(ids, &k, c);
08579         }
08580         if (missing)
08581             continue;
08582 
08583         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
08584         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
08585         if (pixstart < 0)
08586             pixstart = 0;
08587         if (pixend > nx)
08588             pixend = nx;
08589 
08590         /*
08591          * Resampled image:
08592          */
08593 
08594         for (j = 0; j < nl; j++) {
08595             lambda = firstLambda + j * dispersion;
08596             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
08597                                             &pixel_per_lambda);
08598 
08599             /*
08600              * The local dispersion is 1 / pixel_per_lambda
08601              * and this factor is used for applying the flux
08602              * conservation correction (if requested).
08603              */
08604 
08605             pixel = fpixel;
08606             if (pixel >= 1 && pixel < nx-2) {
08607                 v0 = (sdata + i*nx)[pixel-1];
08608                 v1 = (sdata + i*nx)[pixel];
08609                 v2 = (sdata + i*nx)[pixel+1];
08610                 v3 = (sdata + i*nx)[pixel+2];
08611                 vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
08612                    + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
08613                    + 2*v1;
08614                 vi /= 2;
08615                 if (v1 > v2) {
08616                     if (vi > v1) { 
08617                         vi = v1;
08618                     }
08619                     else if (vi < v2) {
08620                         vi = v2;
08621                     }
08622                 }
08623                 else {
08624                     if (vi > v2) { 
08625                         vi = v2;
08626                     }
08627                     else if (vi < v1) {
08628                         vi = v1;
08629                     }
08630                 }
08631                 if (flux)
08632                     vi *= dispersion * pixel_per_lambda;
08633                 (rdata + i*nl)[j] = vi;
08634             }
08635             else if (pixel >= 0 && pixel < nx-1) {
08636                 v1 = (sdata + i*nx)[pixel];
08637                 v2 = (sdata + i*nx)[pixel+1];
08638                 vi = v1 + (v2-v1)*(fpixel-pixel);
08639                 if (flux)
08640                     vi *= dispersion * pixel_per_lambda;
08641                 (rdata + i*nl)[j] = vi;
08642             }
08643         }
08644 
08645         cpl_polynomial_delete(ids);
08646     }
08647 
08648     return resampled;
08649 }
08650 
08651 
08718 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
08719                                 double refwave, double firstLambda, 
08720                                 double lastLambda, cpl_table *idscoeff,
08721                                 cpl_vector *skylines, int highres, int order,
08722                                 cpl_image *calibration, int sradius)
08723 {
08724     const char *func = "mos_wavelength_align";
08725 
08726     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08727                                                  /* Max order is 5 */
08728     double         *line;
08729     double         *data;
08730     double          expPos, offset;
08731     double          c;
08732     double          lambda1, lambda2;
08733     double          rms;
08734     float           pos;
08735     float          *sdata;
08736     float          *cdata;
08737     int            *idata;
08738     int             startPos, endPos;
08739     int             window = 2*sradius + 1;
08740     int             nlines;
08741     int             nslits;
08742     int             npoints;
08743     int             nrows;
08744     int             nx, ny;
08745     int             xlow, ylow, xhig, yhig;
08746     int             idsorder, uorder;
08747     int            *slit_id;
08748     int            *position;
08749     int            *length;
08750     int             missing;
08751     int             null;
08752     int             i, j, k;
08753 
08754     char            offname[MAX_COLNAME];
08755     char            name[MAX_COLNAME];
08756 
08757     cpl_polynomial *ids;
08758     cpl_polynomial *polycorr;
08759     cpl_image      *exslit;
08760     cpl_image      *sky;
08761     cpl_table      *offsets;
08762     cpl_table      *dummy;
08763     cpl_vector     *wave;
08764     cpl_vector     *offs;
08765     
08766 
08767     if (idscoeff == NULL) {
08768         cpl_msg_error(func, "An IDS coeff table must be given");
08769         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08770         return NULL;
08771     }
08772 
08773     if (image == NULL) {
08774         cpl_msg_error(func, "A scientific spectral image must be given");
08775         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08776         return NULL;
08777     }
08778 
08779     if (slits == NULL) {
08780         cpl_msg_error(func, "A slit position table must be given");
08781         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08782         return NULL;
08783     }
08784 
08785     if (skylines) {
08786         line = cpl_vector_get_data(skylines);
08787         nlines = cpl_vector_get_size(skylines);
08788     }
08789     else {
08790         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
08791                         "given: using internal list of reference sky lines");
08792         if (highres) {
08793            line = default_lines_hi;
08794            nlines = sizeof(default_lines_hi) / sizeof(double);
08795         }
08796         else {
08797            line = default_lines_lo;
08798            nlines = sizeof(default_lines_lo) / sizeof(double);
08799         }
08800     }
08801 
08802     if (calibration)
08803         cdata = cpl_image_get_data(calibration);
08804 
08805     nx = cpl_image_get_size_x(image);
08806     ny = cpl_image_get_size_y(image);
08807 
08808     nslits   = cpl_table_get_nrow(slits);
08809     slit_id  = cpl_table_get_data_int(slits, "slit_id");
08810     position = cpl_table_get_data_int(slits, "position");
08811     length   = cpl_table_get_data_int(slits, "length");
08812 
08813 
08814     /*
08815      * Define the output table of offsets
08816      */
08817 
08818     nrows = 0;
08819     for (i = 0; i < nlines; i++)
08820         if (line[i] > firstLambda && line[i] < lastLambda)
08821             nrows++;
08822 
08823     offsets = cpl_table_new(nrows);
08824     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
08825     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
08826 
08827     nrows = 0;
08828     for (i = 0; i < nlines; i++) {
08829         if (line[i] > firstLambda && line[i] < lastLambda) {
08830             cpl_table_set_double(offsets, "wave", nrows, line[i]);
08831             nrows++;
08832         }
08833     }
08834 
08835     /*
08836      * Here "line" is made to point to the new list of selected wavelengths
08837      */
08838 
08839     line = cpl_table_get_data_double(offsets, "wave");
08840     nlines = nrows;
08841 
08842     idsorder = 0;
08843     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
08844         ++idsorder;
08845     --idsorder;
08846 
08847     xlow = 1;
08848     xhig = nx;
08849     for (i = 0; i < nslits; i++) {
08850 
08851         if (length[i] == 0)
08852             continue;
08853 
08854         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
08855         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
08856 
08857         /* 
08858          * Define the extraction boundaries. We DON'T write:
08859          *
08860          * ylow = position[i];
08861          * yhig = ylow + length[i];
08862          *
08863          * because the cpl_image pixels are counted from 1, and because in
08864          * cpl_image_extract() the coordinates of the last pixel are inclusive.
08865          */
08866 
08867         ylow = position[i] + 1;
08868         yhig = ylow + length[i] - 1;
08869 
08870         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
08871         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
08872         sdata  = cpl_image_get_data(sky);
08873 
08874         cpl_image_delete(exslit);
08875 
08876         /* 
08877          * Return here to a decent way of counting pixels (i.e., starting
08878          * from 0)
08879          */
08880          
08881         ylow--;
08882 
08883         /*
08884          * Allocate a dummy table for collecting all the offsets
08885          * for all the lines: this is only needed for the computation
08886          * of the median offset for each sky line
08887          */
08888 
08889         dummy = cpl_table_new(yhig - ylow);
08890         for (j = 0; j < nlines; j++) {
08891             snprintf(name, MAX_COLNAME, "%d", j);
08892             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
08893         }
08894 
08895         for (j = ylow; j < yhig; j++) {
08896 
08897             /*
08898              * Get the IDS polynomial for the current slit row
08899              */
08900 
08901             missing = 0;
08902             ids = cpl_polynomial_new(1);
08903             for (k = 0; k <= idsorder; k++) {
08904                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
08905                 if (null) {
08906                     cpl_polynomial_delete(ids);
08907                     missing = 1;
08908                     break;
08909                 }
08910                 cpl_polynomial_set_coeff(ids, &k, c);
08911             }
08912             if (missing)
08913                 continue;
08914 
08915             for (k = 0; k < nlines; k++) {
08916                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
08917                 startPos = expPos - sradius;
08918                 endPos   = startPos + window;
08919                 if (startPos < 0 || endPos >= nx)
08920                     continue;
08921            
08922                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
08923                     pos += startPos;
08924                     offset = pos - expPos;
08925                     snprintf(name, MAX_COLNAME, "%d", k);
08926                     cpl_table_set_double(dummy, name, j - ylow, offset);
08927                 }
08928             }
08929 
08930             cpl_polynomial_delete(ids);
08931         }
08932 
08933         cpl_image_delete(sky);
08934 
08935         for (j = 0; j < nlines; j++) {
08936             snprintf(name, MAX_COLNAME, "%d", j);
08937             if (cpl_table_has_valid(dummy, name)) {
08938                 offset = cpl_table_get_column_median(dummy, name);
08939                 cpl_table_set_double(offsets, offname, j, offset);
08940             }
08941         }
08942 
08943         cpl_table_delete(dummy);
08944 
08945     }
08946 
08947 
08948     /*
08949      * In the following the input idscoeff table is modified by simply
08950      * adding the coefficients of the polynomial used to fit the sky
08951      * line residuals to the coefficients of the IDS polynomials.
08952      */
08953 
08954     for (i = 0; i < nslits; i++) {
08955 
08956         if (length[i] == 0)
08957             continue;
08958 
08959         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
08960 
08961         /*
08962          * In the following, the "dummy" table is just a tool for
08963          * eliminating invalid points from the vectors to be fitted.
08964          */
08965 
08966         dummy = cpl_table_new(nlines);
08967         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
08968         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
08969 
08970         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
08971         if (npoints == 0) {
08972             cpl_msg_warning(func, "No sky lines alignment was possible "
08973                             "for slit ID=%d: no sky line found", slit_id[i]);
08974             cpl_table_delete(dummy);
08975             continue;
08976         }
08977 
08978         uorder = order;
08979         if (npoints <= uorder) {
08980             uorder = npoints - 1;
08981             if (uorder) {
08982                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
08983                                 "ID=%d, while a polynomial order %d was "
08984                                 "requested. Using polynomial order %d for "
08985                                 "this slit!", npoints, slit_id[i], order, 
08986                                 uorder);
08987             }
08988             else {
08989                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
08990                                 "ID=%d, while a polynomial order %d was "
08991                                 "requested. Computing a median offset for "
08992                                 "this slit!", npoints, slit_id[i], order);
08993             }
08994         }
08995 
08996         cpl_table_erase_invalid(dummy);
08997 
08998         if (uorder > 1) {
08999 
09000             /*
09001              * Model offsets with polynomial fitting
09002              */
09003 
09004             wave = cpl_vector_wrap(npoints,
09005                                    cpl_table_get_data_double(dummy, "wave"));
09006             offs = cpl_vector_wrap(npoints,
09007                                    cpl_table_get_data_double(dummy, "offset"));
09008 
09009             /*
09010              * Set reference wavelength as zero point
09011              */
09012 
09013             cpl_vector_subtract_scalar(wave, refwave);
09014 
09015             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09016 
09017             rms = sqrt(rms * (uorder + 1) / npoints);
09018 
09019             cpl_vector_unwrap(wave);
09020             cpl_vector_unwrap(offs);
09021             cpl_table_delete(dummy);
09022 
09023             /*
09024              * Now correct the coefficients of the corresponding IDS
09025              * polynomials related to this slit:
09026              */
09027 
09028             ylow = position[i];
09029             yhig = ylow + length[i];
09030 
09031             for (j = 0; j <= uorder; j++) {
09032                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09033                 c = cpl_polynomial_get_coeff(polycorr, &j);
09034                 for (k = ylow; k < yhig; k++)
09035                     data[k] += c;
09036             }
09037 
09038             data = cpl_table_get_data_double(idscoeff, "error");
09039             for (k = ylow; k < yhig; k++)
09040                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09041 
09042             idata = cpl_table_get_data_int(idscoeff, "nlines");
09043             for (k = ylow; k < yhig; k++)
09044                  idata[k] = npoints;
09045 
09046             /*
09047              * If a wavelengths map was provided, correct it to keep
09048              * into account the alignment to skylines:
09049              */
09050 
09051             if (calibration) {
09052                 for (j = ylow; j < yhig; j++) {
09053                     for (k = 1; k < nx; k++) {
09054                         lambda1 = cdata[k - 1 + j*nx];
09055                         lambda2 = cdata[k + j*nx];
09056                         if (lambda1 < 1.0 || lambda2 < 1.0)
09057                             continue;
09058                         offset = cpl_polynomial_eval_1d(polycorr, 
09059                                                         lambda1-refwave, NULL);
09060                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09061                     }
09062                 }
09063             }
09064     
09065             cpl_polynomial_delete(polycorr);
09066         }
09067         else if (uorder == 1) {
09068 
09069             /*
09070              * Model offsets with robust linear fitting
09071              */
09072 
09073             double        q, m;
09074             cpl_bivector *list;
09075 
09076 
09077             wave = cpl_vector_wrap(npoints,
09078                                    cpl_table_get_data_double(dummy, "wave"));
09079             offs = cpl_vector_wrap(npoints,
09080                                    cpl_table_get_data_double(dummy, "offset"));
09081 
09082             list = cpl_bivector_wrap_vectors(wave, offs);
09083 
09084             /*
09085              * Set reference wavelength as zero point
09086              */
09087 
09088             cpl_vector_subtract_scalar(wave, refwave);
09089 
09090             robustLinearFit(list, &q, &m, &rms);
09091 
09092             rms = sqrt(rms * (uorder + 1) / npoints);
09093 
09094             cpl_bivector_unwrap_vectors(list);
09095             cpl_vector_unwrap(wave);
09096             cpl_vector_unwrap(offs);
09097             cpl_table_delete(dummy);
09098 
09099             /*
09100              * Now correct the coefficients of the corresponding IDS
09101              * polynomials related to this slit:
09102              */
09103 
09104             ylow = position[i];
09105             yhig = ylow + length[i];
09106 
09107             for (j = 0; j <= uorder; j++) {
09108                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09109                 if (j)
09110                     c = m;
09111                 else
09112                     c = q;
09113                 for (k = ylow; k < yhig; k++)
09114                     data[k] += c;
09115             }
09116 
09117             data = cpl_table_get_data_double(idscoeff, "error");
09118             for (k = ylow; k < yhig; k++)
09119                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09120 
09121             idata = cpl_table_get_data_int(idscoeff, "nlines");
09122             for (k = ylow; k < yhig; k++)
09123                  idata[k] = npoints;
09124 
09125             /*
09126              * If a wavelengths map was provided, correct it to keep
09127              * into account the alignment to skylines:
09128              */
09129 
09130             if (calibration) {
09131                 for (j = ylow; j < yhig; j++) {
09132                     for (k = 1; k < nx; k++) {
09133                         lambda1 = cdata[k - 1 + j*nx];
09134                         lambda2 = cdata[k + j*nx];
09135                         if (lambda1 < 1.0 || lambda2 < 1.0)
09136                             continue;
09137                         offset = q + m*(lambda1-refwave);
09138                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09139                     }
09140                 }
09141             }
09142         }
09143         else {
09144 
09145             /*
09146              * Just compute median offset
09147              */
09148 
09149             offs = cpl_vector_wrap(npoints,
09150                                    cpl_table_get_data_double(dummy, "offset"));
09151 
09152             offset = cpl_vector_get_median_const(offs);
09153 
09154             if (npoints > 1)
09155                 rms = cpl_table_get_column_stdev(dummy, "offset");
09156             else
09157                 rms = 0.0;
09158 
09159             rms /= sqrt(npoints);
09160 
09161             cpl_vector_unwrap(offs);
09162             cpl_table_delete(dummy);
09163 
09164             /*
09165              * Now correct the constant term of the corresponding IDS
09166              * polynomials related to this slit:
09167              */
09168 
09169             ylow = position[i];
09170             yhig = ylow + length[i];
09171 
09172             data = cpl_table_get_data_double(idscoeff, clab[0]);
09173             for (k = ylow; k < yhig; k++)
09174                 data[k] += offset;
09175 
09176             data = cpl_table_get_data_double(idscoeff, "error");
09177             for (k = ylow; k < yhig; k++)
09178                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09179 
09180             idata = cpl_table_get_data_int(idscoeff, "nlines");
09181             for (k = ylow; k < yhig; k++)
09182                  idata[k] = npoints;
09183 
09184             /*
09185              * If a wavelengths map was provided, correct it to keep
09186              * into account the alignment to skylines. Note that 
09187              * the offset must be converted from pixels to wavelengths.
09188              */
09189 
09190             if (calibration) {
09191                 for (j = ylow; j < yhig; j++) {
09192                     for (k = 1; k < nx; k++) {
09193                         lambda1 = cdata[k - 1 + j*nx];
09194                         lambda2 = cdata[k + j*nx];
09195                         if (lambda1 < 1.0 || lambda2 < 1.0)
09196                             continue; 
09197                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09198                     }
09199                 }
09200             }
09201         }
09202     }
09203 
09204     return offsets;
09205 
09206 }
09207 
09208 
09270 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
09271                                     double firstLambda, double lastLambda, 
09272                                     cpl_table *idscoeff, cpl_vector *skylines, 
09273                                     int highres, int order, 
09274                                     cpl_image *calibration, int sradius)
09275 {
09276     const char *func = "mos_wavelength_align_lss";
09277 
09278     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09279                                                  /* Max order is 5 */
09280     double         *line;
09281     double         *data;
09282     double         *wdata;
09283     double         *odata;
09284     double          expPos, offset;
09285     double          c;
09286     double          lambda1, lambda2;
09287     double          rms;
09288     float           pos;
09289     float          *sdata;
09290     float          *cdata;
09291     int            *idata;
09292     int             startPos, endPos;
09293     int             window = 2*sradius + 1;
09294     int             nlines;
09295     int             npoints;
09296     int             nrows;
09297     int             nx, ny;
09298     int             idsorder, uorder;
09299     int             missing;
09300     int             i, j, k;
09301 
09302     char            name[MAX_COLNAME];
09303     char            fname[MAX_COLNAME];
09304 
09305     cpl_polynomial *ids;
09306     cpl_polynomial *polycorr;
09307     cpl_table      *offsets;
09308     cpl_table      *fittable;
09309     cpl_table      *dummy;
09310     cpl_vector     *wave;
09311     cpl_vector     *offs;
09312     cpl_vector     *row;
09313     
09314 
09315     if (idscoeff == NULL) {
09316         cpl_msg_error(func, "An IDS coeff table must be given");
09317         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09318         return NULL;
09319     }
09320 
09321     if (image == NULL) {
09322         cpl_msg_error(func, "A scientific spectral image must be given");
09323         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09324         return NULL;
09325     }
09326 
09327     if (skylines) {
09328         line = cpl_vector_get_data(skylines);
09329         nlines = cpl_vector_get_size(skylines);
09330     }
09331     else {
09332         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09333                         "given: using internal list of reference sky lines");
09334         if (highres) {
09335            line = default_lines_hi;
09336            nlines = sizeof(default_lines_hi) / sizeof(double);
09337         }
09338         else {
09339            line = default_lines_lo;
09340            nlines = sizeof(default_lines_lo) / sizeof(double);
09341         }
09342     }
09343 
09344     if (calibration)
09345         cdata = cpl_image_get_data(calibration);
09346 
09347     nx = cpl_image_get_size_x(image);
09348     ny = cpl_image_get_size_y(image);
09349 
09350     sdata = cpl_image_get_data(image);
09351     
09352 
09353     /*FIXME: This is a remnant of the adaptation of the function
09354      * mos_wavelength_align(), where an offset table was created.
09355      * I leave it here because I am in a hurry, it is just used to
09356      * hold the list of selected sky lines.
09357      *
09358      * Define table of wavelengths
09359      */
09360 
09361     nrows = 0;
09362     for (i = 0; i < nlines; i++)
09363         if (line[i] > firstLambda && line[i] < lastLambda)
09364             nrows++;
09365 
09366     offsets = cpl_table_new(nrows);
09367     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09368     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09369 
09370     nrows = 0;
09371     for (i = 0; i < nlines; i++) {
09372         if (line[i] > firstLambda && line[i] < lastLambda) {
09373             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09374             nrows++;
09375         }
09376     }
09377 
09378     /*
09379      * Here "line" is made to point to the new list of selected wavelengths
09380      */
09381 
09382     line = cpl_table_get_data_double(offsets, "wave");
09383     nlines = nrows;
09384 
09385     idsorder = 0;
09386     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09387         ++idsorder;
09388     --idsorder;
09389 
09390 
09391     /*
09392      * Allocate a dummy table for collecting all the offsets
09393      * for all the lines
09394      */
09395 
09396     dummy = cpl_table_new(ny);
09397     for (j = 0; j < nlines; j++) {
09398         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09399         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09400         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09401         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
09402     }
09403 
09404     for (j = 0; j < ny; j++, sdata += nx) {
09405 
09406         /*
09407          * Get the IDS polynomial for the current slit row
09408          */
09409 
09410         missing = 0;
09411         ids = cpl_polynomial_new(1);
09412         for (k = 0; k <= idsorder; k++) {
09413             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
09414             if (missing) {
09415                 cpl_polynomial_delete(ids);
09416                 break;
09417             }
09418             cpl_polynomial_set_coeff(ids, &k, c);
09419         }
09420         if (missing)
09421             continue;
09422 
09423         for (k = 0; k < nlines; k++) {
09424             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09425             startPos = expPos - sradius;
09426             endPos   = startPos + window;
09427             if (startPos < 0 || endPos >= nx)
09428                 continue;
09429            
09430             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09431                 pos += startPos;
09432                 offset = pos - expPos;
09433                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
09434                 cpl_table_set_double(dummy, name, j, offset);
09435             }
09436         }
09437 
09438         cpl_polynomial_delete(ids);
09439     }
09440 
09441 
09442     /*
09443      * At this point for each sky line we model its offset along
09444      * the image rows using a robust linear fitting
09445      */
09446 
09447     for (j = 0; j < nlines; j++) {
09448         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09449         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09450         if (cpl_table_has_valid(dummy, name)) {
09451 
09452             /*
09453              * In the following, the "fittable" is just a tool for
09454              * eliminating invalid points from the vectors to be fitted.
09455              */
09456 
09457             double        q, m;
09458             cpl_bivector *list;
09459 
09460             fittable = cpl_table_new(ny);
09461             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
09462             cpl_table_set_column_unit(fittable, "row", "pixel");
09463             for (k = 0; k < ny; k++)
09464                  cpl_table_set_double(fittable, "row", k, k);
09465             cpl_table_duplicate_column(fittable, "offset", dummy, name);
09466             npoints = ny - cpl_table_count_invalid(fittable, "offset");
09467             cpl_table_erase_invalid(fittable);
09468             row = cpl_vector_wrap(npoints,
09469                                cpl_table_get_data_double(fittable, "row"));
09470             offs = cpl_vector_wrap(npoints,
09471                                cpl_table_get_data_double(fittable, "offset"));
09472             list = cpl_bivector_wrap_vectors(row, offs);
09473             robustLinearFit(list, &q, &m, &rms);
09474             cpl_bivector_unwrap_vectors(list);
09475             cpl_vector_unwrap(row);
09476             cpl_vector_unwrap(offs);
09477             cpl_table_delete(fittable);
09478             for (k = 0; k < ny; k++)
09479                  cpl_table_set_double(dummy, fname, k, q + m*k);
09480         }
09481     }
09482 
09483 
09484     /*
09485      * Now each dummy table row consists of a sequence of offsets,
09486      * one for each wavelength. A table row corresponds to an image row.
09487      * We must fit a polynomial to each one of these rows, in order to
09488      * express the offsets as a function of wavelength. The obtained 
09489      * polynomial coefficients are used to correct the IDS coefficients.
09490      */
09491 
09492     for (i = 0; i < ny; i++) {
09493 
09494         if (!cpl_table_is_valid(idscoeff, clab[0], i))
09495             continue;
09496 
09497         npoints = 0;
09498         for (j = 0; j < nlines; j++) {
09499             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09500             if (cpl_table_is_valid(dummy, name, i))
09501                 npoints++;
09502         }
09503 
09504         if (npoints == 0)
09505             continue;
09506 
09507         uorder = order;
09508         if (npoints <= uorder)
09509             uorder = npoints - 1;
09510 
09511         if (uorder > 1) {
09512 
09513             /*
09514              * Model offsets with polynomial fitting
09515              */
09516 
09517             wave = cpl_vector_new(npoints);
09518             wdata = cpl_vector_get_data(wave);
09519             offs = cpl_vector_new(npoints);
09520             odata = cpl_vector_get_data(offs);
09521 
09522             npoints = 0;
09523             for (j = 0; j < nlines; j++) {
09524                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09525                 if (cpl_table_is_valid(dummy, name, i)) {
09526                     wdata[npoints] = line[j] - refwave;
09527                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
09528                     npoints++;
09529                 }
09530             }
09531 
09532             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09533 
09534             rms = sqrt(rms * (uorder + 1) / npoints);
09535 
09536             cpl_vector_delete(wave);
09537             cpl_vector_delete(offs);
09538 
09539             /*
09540              * Now correct the coefficients of the corresponding IDS
09541              * polynomials related to this slit:
09542              */
09543 
09544             for (j = 0; j <= uorder; j++) {
09545                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09546                 c = cpl_polynomial_get_coeff(polycorr, &j);
09547                 data[i] += c;
09548             }
09549 
09550             data = cpl_table_get_data_double(idscoeff, "error");
09551             data[i] = sqrt(data[i]*data[i] + rms*rms);
09552 
09553             idata = cpl_table_get_data_int(idscoeff, "nlines");
09554             idata[i] = npoints;
09555 
09556             /*
09557              * If a wavelengths map was provided, correct it to keep
09558              * into account the alignment to skylines:
09559              */
09560 
09561             if (calibration) {
09562                 for (k = 1; k < nx; k++) {
09563                     lambda1 = cdata[k - 1 + i*nx];
09564                     lambda2 = cdata[k + i*nx];
09565                     if (lambda1 < 1.0 || lambda2 < 1.0)
09566                         continue;
09567                     offset = cpl_polynomial_eval_1d(polycorr,
09568                                                     lambda1-refwave, NULL);
09569                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
09570                 }
09571             }
09572 
09573             cpl_polynomial_delete(polycorr);
09574 
09575         }
09576         else if (uorder == 1) {
09577 
09578             /*
09579              * Model offsets with robust linear fitting
09580              */
09581 
09582             cpl_bivector *list;
09583             double        q, m;
09584 
09585             wave = cpl_vector_new(npoints);
09586             wdata = cpl_vector_get_data(wave);
09587             offs = cpl_vector_new(npoints);
09588             odata = cpl_vector_get_data(offs);
09589 
09590             npoints = 0;
09591             for (j = 0; j < nlines; j++) {
09592                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09593                 if (cpl_table_is_valid(dummy, name, i)) {
09594                     wdata[npoints] = line[j] - refwave;
09595                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
09596                     npoints++;
09597                 }
09598             }
09599 
09600             list = cpl_bivector_wrap_vectors(wave, offs);
09601             robustLinearFit(list, &q, &m, &rms);
09602 
09603             rms = sqrt(rms * (uorder + 1) / npoints);
09604 
09605             cpl_bivector_unwrap_vectors(list);
09606             cpl_vector_delete(wave);
09607             cpl_vector_delete(offs);
09608 
09609             /*
09610              * Now correct the coefficients of the corresponding IDS
09611              * polynomials related to this row:
09612              */
09613 
09614             for (j = 0; j <= uorder; j++) {
09615                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09616                 if (j)
09617                     c = m;
09618                 else
09619                     c = q;
09620                 data[i] += c;
09621             }
09622 
09623             data = cpl_table_get_data_double(idscoeff, "error");
09624             data[i] = sqrt(data[i]*data[i] + rms*rms);
09625 
09626             idata = cpl_table_get_data_int(idscoeff, "nlines");
09627             idata[i] = npoints;
09628 
09629             /*
09630              * If a wavelengths map was provided, correct it to keep
09631              * into account the alignment to skylines:
09632              */
09633 
09634             if (calibration) {
09635                 for (k = 1; k < nx; k++) {
09636                     lambda1 = cdata[k - 1 + i*nx];
09637                     lambda2 = cdata[k + i*nx];
09638                     if (lambda1 < 1.0 || lambda2 < 1.0)
09639                         continue;
09640                     offset = q + m*(lambda1-refwave);
09641                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
09642                 }
09643             }
09644         }
09645         else {
09646 
09647             /*
09648              * Just compute median offset
09649              */
09650 
09651             offs = cpl_vector_new(npoints);
09652             odata = cpl_vector_get_data(offs);
09653 
09654             npoints = 0;
09655             for (j = 0; j < nlines; j++) {
09656                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09657                 if (cpl_table_is_valid(dummy, name, i)) {
09658                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
09659                     npoints++;
09660                 }
09661             }
09662 
09663             offset = cpl_vector_get_median_const(offs);
09664 
09665             if (npoints > 1) {
09666                 rms = cpl_vector_get_stdev(offs);
09667             }
09668             else if (npoints == 1) {
09669                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
09670                 if (cpl_table_has_valid(dummy, name)) {
09671                     rms = cpl_table_get_column_stdev(dummy, name);
09672                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
09673                 }
09674                 else {
09675                     rms = 0.0;
09676                 }
09677             }
09678             else {
09679                 rms = 0.0;
09680             }
09681 
09682             rms /= sqrt(npoints);
09683 
09684             cpl_vector_delete(offs);
09685 
09686             /*
09687              * Now correct the constant term of the corresponding IDS
09688              * polynomials related to this slit:
09689              */
09690 
09691             data = cpl_table_get_data_double(idscoeff, clab[0]);
09692             data[i] += offset;
09693 
09694             data = cpl_table_get_data_double(idscoeff, "error");
09695             data[i] = sqrt(data[i]*data[i] + rms*rms);
09696 
09697             idata = cpl_table_get_data_int(idscoeff, "nlines");
09698             idata[i] = npoints;
09699 
09700             /*
09701              * If a wavelengths map was provided, correct it to keep
09702              * into account the alignment to skylines. Note that
09703              * the offset must be converted from pixels to wavelengths.
09704              */
09705 
09706             if (calibration) {
09707                 for (k = 1; k < nx; k++) {
09708                     lambda1 = cdata[k - 1 + i*nx];
09709                     lambda2 = cdata[k + i*nx];
09710                     if (lambda1 < 1.0 || lambda2 < 1.0)
09711                         continue;
09712                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
09713                 }
09714             }
09715         }
09716     }
09717 
09718     missing = 1;
09719     for (j = 0; j < nlines; j++) {
09720         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09721         if (cpl_table_has_valid(dummy, name)) {
09722             missing = 0;
09723             offset = cpl_table_get_column_median(dummy, name);
09724             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
09725                          line[j], offset);
09726         }
09727         else {
09728             cpl_msg_info(func, 
09729                          "Median offset for %.2f: not available", line[j]);
09730         }
09731     }
09732 
09733     cpl_table_delete(offsets);
09734 
09735     if (missing) {
09736         cpl_table_delete(dummy);
09737         dummy = NULL;
09738     }
09739 
09740     return dummy;
09741 
09742 }
09743 
09744 
09772 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
09773                            double wavestart, double dispersion, int radius,
09774                            int highres)
09775 {
09776 
09777     const char *func = "mos_distortions_rms";
09778 
09779     int xlen;
09780     int ylen;
09781     int numLines;
09782     int cpix, npix, nzero;
09783     int sp, ep;
09784     int i, j, k;
09785     int npeaks, allPeaks;
09786 
09787     float *profile;
09788     float  peak, expectPeak, offset;
09789     double lambda;
09790 
09791     double  average;
09792     double  rms, oneRms;
09793 
09794     float  *sdata;
09795     double *wdata;
09796 
09797   
09798     xlen = cpl_image_get_size_x(rectified);
09799     ylen = cpl_image_get_size_y(rectified);
09800     sdata = cpl_image_get_data(rectified);
09801 
09802     if (lines) {
09803         wdata = cpl_vector_get_data(lines);
09804         numLines = cpl_vector_get_size(lines);
09805     }
09806     else {
09807         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09808                         "given: using internal list of reference sky lines");
09809         if (highres) {
09810            wdata = default_lines_hi;
09811            numLines = sizeof(default_lines_hi) / sizeof(double);
09812         }
09813         else {
09814            wdata = default_lines_lo;
09815            numLines = sizeof(default_lines_lo) / sizeof(double);
09816         }
09817     }
09818 
09819     npix = 2 * radius + 1;
09820     profile = cpl_calloc(npix, sizeof(float));
09821 
09822     rms = 0.0;
09823     allPeaks = 0;
09824 
09825     for (i = 0; i < numLines; i++) {
09826 
09827         /*
09828          *  Expected peak and closest pixel to specified wavelength.
09829          */
09830 
09831         lambda = wdata[i];
09832         expectPeak = (lambda - wavestart) / dispersion;
09833         cpix = floor(expectPeak + 0.5);
09834 
09835         /*
09836          *  Search interval for peak. Abort if too close to image border.
09837          */
09838 
09839         sp = cpix - radius;
09840         ep = cpix + radius;
09841 
09842         if (sp < 0 || ep > xlen)
09843             continue;
09844 
09845         average = 0.0;
09846         npeaks = 0;
09847         oneRms = 0.0;
09848 
09849         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
09850             nzero = 0;
09851             for (k = 0; k < npix; k++) {
09852                 profile[k] = sdata[sp + k + j * xlen];
09853                 if (fabs(profile[k]) < 0.0001)
09854                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
09855             }
09856             if (nzero > 0)
09857                 continue;
09858 
09859             if (peakPosition(profile, npix, &peak, 1) == 0) {
09860                 offset = (sp + peak) - expectPeak;
09861                 average += offset;
09862                 rms += fabs(offset);
09863                 oneRms += fabs(offset);
09864                 npeaks++;
09865                 allPeaks++;
09866             }
09867         }
09868 
09869         if (npeaks)
09870             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
09871                          lambda, oneRms / npeaks * 1.25, npeaks);
09872         else
09873             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
09874     }
09875 
09876     cpl_free(profile);
09877 
09878     if (allPeaks < 10)
09879         return 0.0;
09880 
09881     rms /= allPeaks;
09882     rms *= 1.25;       /* Factor to convert average deviation to sigma */
09883 
09884     return rms;
09885 
09886 }
09887 
09888 
09909 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
09910                          double blue, double red, double dispersion, int trend)
09911 {
09912     const char *func = "mos_map_pixel";
09913 
09914     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09915                                                  /* Max order is 5 */
09916 
09917     cpl_polynomial *ids;
09918     cpl_image      *map;
09919     float          *mdata;
09920     double          lambda;
09921     double          c;
09922     int             order;
09923     int             xsize, ysize;
09924     int             missing;
09925     int             i, j, k;
09926 
09927 
09928     if (idscoeff == NULL) {
09929         cpl_msg_error(func, "An IDS coeff table must be given");
09930         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09931         return NULL;
09932     }
09933 
09934     xsize = (red - blue) / dispersion;
09935     ysize = cpl_table_get_nrow(idscoeff);
09936     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
09937     mdata = cpl_image_get_data(map);
09938 
09939     order = 0;
09940     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
09941         ++order;
09942     --order;
09943 
09944     for (i = 0; i < ysize; i++, mdata += xsize) {
09945 
09946         missing = 0;
09947         ids = cpl_polynomial_new(1);
09948         for (k = trend; k <= order; k++) {
09949             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
09950             if (missing) {
09951                 cpl_polynomial_delete(ids);
09952                 break;
09953             }
09954             cpl_polynomial_set_coeff(ids, &k, c);
09955         }
09956         if (missing)
09957             continue;
09958 
09959         for (j = 0; j < xsize; j++) {
09960             lambda = blue + j*dispersion;
09961             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
09962         }
09963 
09964         cpl_polynomial_delete(ids);
09965     }
09966 
09967     return map;
09968 
09969 }
09970 
09971 
09993 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
09994                             double blue, double red)
09995 {
09996     const char *func = "mos_map_idscoeff";
09997 
09998     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09999                                                  /* Max order is 5 */
10000 
10001     cpl_polynomial *ids;
10002     cpl_image      *map;
10003     float          *mdata;
10004     double          lambda;
10005     double          c;
10006     int             order;
10007     int             ysize;
10008     int             missing;
10009     int             i, j, k;
10010 
10011 
10012     if (idscoeff == NULL) {
10013         cpl_msg_error(func, "An IDS coeff table must be given");
10014         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10015         return NULL;
10016     }
10017 
10018     if (xsize < 1) {
10019         cpl_msg_error(func, "Invalid image size");
10020         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10021         return NULL;
10022     }
10023 
10024     if (xsize < 20 || xsize > 5000) {
10025         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10026                         xsize);
10027     }
10028 
10029     ysize = cpl_table_get_nrow(idscoeff);
10030     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10031     mdata = cpl_image_get_data(map);
10032 
10033     order = 0;
10034     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10035         ++order;
10036     --order;
10037 
10038     for (i = 0; i < ysize; i++, mdata += xsize) {
10039 
10040         missing = 0;
10041         ids = cpl_polynomial_new(1);
10042         for (k = 0; k <= order; k++) {
10043             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10044             if (missing) {
10045                 cpl_polynomial_delete(ids);
10046                 break;
10047             }
10048             cpl_polynomial_set_coeff(ids, &k, c);
10049         }
10050         if (missing)
10051             continue;
10052 
10053         for (j = 0; j < xsize; j++) {
10054             lambda = mos_eval_dds(ids, blue, red, reference, j);
10055             if (lambda >= blue && lambda <= red) {
10056                 mdata[j] = lambda;
10057             }
10058         }
10059 
10060         cpl_polynomial_delete(ids);
10061     }
10062 
10063     return map;
10064 
10065 }
10066 
10067 
10102 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10103                                cpl_table *slits, cpl_table *polytraces, 
10104                                double reference, double blue, double red, 
10105                                double dispersion)
10106 {
10107     const char *func = "mos_map_wavelengths";
10108 
10109     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10110                                                  /* Max order is 5 */
10111     cpl_polynomial *polytop;
10112     cpl_polynomial *polybot;
10113     cpl_image      *remapped;
10114     float          *data;
10115     float          *wdata;
10116     float          *sdata;
10117     float          *xdata;
10118     double          vtop, vbot, value;
10119     double          top, bot;
10120     double          coeff;
10121     double          ytop, ybot;
10122     double          ypos;
10123     double          fvalue;
10124     int             ivalue;
10125     int             yint, ysize, yprev;
10126     int             nslits;
10127     int             npseudo;
10128     int            *slit_id;
10129     int            *position;
10130     int            *length;
10131     int             nx, ny;
10132     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10133     int             missing_top, missing_bot;
10134     int             null;
10135     int             order;
10136     int             i, j, k;
10137 
10138 
10139     if (spatial == NULL || calibration == NULL || 
10140         slits == NULL || polytraces == NULL) {
10141         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10142         return NULL;
10143     }
10144 
10145     if (dispersion <= 0.0) {
10146         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10147         return NULL;
10148     }
10149 
10150     if (red - blue < dispersion) {
10151         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10152         return NULL;
10153     }
10154 
10155     nx = cpl_image_get_size_x(spatial);
10156     ny = cpl_image_get_size_y(spatial);
10157     ysize = cpl_image_get_size_y(calibration);
10158     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10159     data  = cpl_image_get_data(remapped);
10160     sdata = cpl_image_get_data(spatial);
10161     wdata = cpl_image_get_data(calibration);
10162 
10163     nslits   = cpl_table_get_nrow(slits);
10164     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10165     order    = cpl_table_get_ncol(polytraces) - 2;
10166     position = cpl_table_get_data_int(slits, "position");
10167     length   = cpl_table_get_data_int(slits, "length");
10168 
10169     /*
10170      * The spatial resampling is performed for a certain number of 
10171      * pixels above and below the position of the reference wavelength:
10172      */
10173 
10174     pixel_above = (red - reference) / dispersion;
10175     pixel_below = (reference - blue) / dispersion;
10176 
10177     for (i = 0; i < nslits; i++) {
10178 
10179         if (length[i] == 0)
10180             continue;
10181 
10182         /*
10183          * Note that the x coordinate of the reference pixels on the CCD
10184          * is taken arbitrarily at the top end of each slit. This wouldn't
10185          * be entirely correct in case of curved slits, or in presence of
10186          * heavy distortions: in such cases the spatial resampling is
10187          * really performed across a wide range of wavelengths. But
10188          * the lag between top and bottom spectral curvature models 
10189          * would introduce even in such cases negligible effects on
10190          * the spectral spatial resampling.
10191          */
10192 
10193         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10194 
10195         start_pixel = refpixel - pixel_below;
10196         if (start_pixel < 0)
10197             start_pixel = 0;
10198 
10199         end_pixel = refpixel + pixel_above;
10200         if (end_pixel > nx)
10201             end_pixel = nx;
10202 
10203         /*
10204          * Recover from the table of spectral curvature coefficients
10205          * the curvature polynomials.
10206          */
10207 
10208         missing_top = 0;
10209         polytop = cpl_polynomial_new(1);
10210         for (k = 0; k <= order; k++) {
10211             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10212             if (null) {
10213                 cpl_polynomial_delete(polytop);
10214                 missing_top = 1;
10215                 break;
10216             }
10217             cpl_polynomial_set_coeff(polytop, &k, coeff);
10218         }
10219 
10220         missing_bot = 0;
10221         polybot = cpl_polynomial_new(1);
10222         for (k = 0; k <= order; k++) {
10223             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10224             if (null) {
10225                 cpl_polynomial_delete(polybot);
10226                 missing_bot = 1;
10227                 break;
10228             }
10229             cpl_polynomial_set_coeff(polybot, &k, coeff);
10230         }
10231 
10232         if (missing_top && missing_bot) {
10233             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10234                           slit_id[i]);
10235             continue;
10236         }
10237 
10238         /*
10239          * In case just one of the two edges was not traced, the other
10240          * edge curvature model is duplicated and shifted to the other
10241          * end of the slit: better than nothing!
10242          */
10243 
10244         if (missing_top) {
10245             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
10246                           "the spectral curvature of the lower edge "
10247                           "is used instead.", slit_id[i]);
10248             polytop = cpl_polynomial_duplicate(polybot);
10249             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10250             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10251             k = 0;
10252             coeff = cpl_polynomial_get_coeff(polybot, &k);
10253             coeff += ytop - ybot;
10254             cpl_polynomial_set_coeff(polytop, &k, coeff);
10255         }
10256 
10257         if (missing_bot) {
10258             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
10259                           "the spectral curvature of the upper edge "
10260                           "is used instead.", slit_id[i]);
10261             polybot = cpl_polynomial_duplicate(polytop);
10262             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10263             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10264             k = 0;
10265             coeff = cpl_polynomial_get_coeff(polytop, &k);
10266             coeff -= ytop - ybot;
10267             cpl_polynomial_set_coeff(polybot, &k, coeff);
10268         }
10269 
10270         /*
10271          * Point to current slit on wavelength calibration image.
10272          * Note that the npseudo value related to this slit is equal 
10273          * to the number of spatial pseudo-pixels decreased by 1 
10274          * (compare with function mos_spatial_calibration()).
10275          */
10276 
10277         xdata = wdata + nx*position[i];
10278         npseudo = length[i] - 1;
10279 
10280         /*
10281          * Write interpolated wavelengths to CCD image
10282          */
10283 
10284         for (j = start_pixel; j < end_pixel; j++) {
10285             top = cpl_polynomial_eval_1d(polytop, j, NULL);
10286             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
10287             for (k = 0; k <= npseudo; k++) {
10288                 ypos = top - k*(top-bot)/npseudo;
10289                 yint = ypos;
10290 
10291                 /* 
10292                  * The line:
10293                  *     value = sdata[j + nx*yint];
10294                  * should be equivalent to:
10295                  *     value = npseudo*(top-yint)/(top-bot);
10296                  */
10297 
10298                 if (yint < 0 || yint >= ny-1) {
10299                     yprev = yint;
10300                     continue;
10301                 }
10302 
10303                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
10304                 ivalue = value;               /* Nearest spatial pixels:   */
10305                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
10306                 if (ivalue < npseudo && ivalue >= 0) {
10307                     vtop = xdata[j + nx*(npseudo-ivalue)];
10308                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
10309                     if (vtop < 1.0) {  /* Impossible wavelength */
10310                         if (vbot < 1.0) {
10311                             value = 0.0;
10312                         }
10313                         else {
10314                             value = vbot;
10315                         }
10316                     }
10317                     else if (vbot < 1.0) {
10318                         if (k)
10319                             value = vtop;
10320                         else
10321                             value = 0.0;
10322                     }
10323                     else if (fabs(vbot-vtop) > 10*dispersion) {
10324                         value = 0.0;
10325                     }
10326                     else {
10327                         value = vtop*(1-fvalue) + vbot*fvalue;
10328                     }
10329                     data[j + nx*yint] = value;
10330 
10331                     if (k) {
10332 
10333                         /*
10334                          * This is added to recover lost pixels on
10335                          * the CCD image (pixels are lost because
10336                          * the CCD pixels are less than npseudo+1).
10337                          */
10338 
10339                         if (yprev - yint > 1) {
10340                             value = sdata[j + nx*(yint+1)];
10341                             ivalue = value;
10342                             fvalue = value - ivalue;
10343                             if (ivalue < npseudo && ivalue >= 0) {
10344                                 vtop = xdata[j + nx*(npseudo-ivalue)];
10345                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
10346                                 if (vtop < 1.0) {
10347                                     if (vbot < 1.0) {
10348                                         value = data[j + nx*(yint+1)];
10349                                     }
10350                                     else {
10351                                         value = vbot;
10352                                     }
10353                                 }
10354                                 else if (vbot < 1.0) {
10355                                     value = vtop;
10356                                 }
10357                                 else if (fabs(vbot-vtop) > 2*dispersion) {
10358                                     value = vtop;
10359                                 }
10360                                 else {
10361                                     value = vtop*(1-fvalue) + vbot*fvalue;
10362                                 }
10363                                 data[j + nx*(yint+1)] = value;
10364                             }
10365                         }
10366                     }
10367                 }
10368                 yprev = yint;
10369             }
10370         }
10371         cpl_polynomial_delete(polytop);
10372         cpl_polynomial_delete(polybot);
10373     }
10374 
10375     return remapped;
10376 }
10377 
10451 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
10452                             cpl_image *spatial, cpl_table *slits,
10453                             cpl_table *polytraces, double reference,
10454                             double blue, double red, double dispersion,
10455                             int flux)
10456 {
10457     const char *func = "mos_map_spectrum";
10458     
10459     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10460                                                  /* Max order is 5 */
10461     cpl_polynomial *polytop;
10462     cpl_polynomial *polybot;
10463     cpl_image      *remapped;
10464     cpl_image     **exslit;
10465     float          *data;
10466     float          *wdata;
10467     float          *sdata;
10468     float          *xdata;
10469     double          lambda00, lambda01, lambda10, lambda11, lambda;
10470     double          space00, space01, space10, space11, space;
10471     double          value00, value01, value10, value11, value0, value1, value;
10472     double          dL, dS;
10473     double          top, bot;
10474     double          coeff;
10475     double          ytop, ybot;
10476     double          xfrac, yfrac;
10477     int             yint, ysize;
10478     int             itop, ibot;
10479     int             shift;
10480     int             L, S;
10481     int             nslits;
10482     int             npseudo;
10483     int            *slit_id;
10484     int            *position;
10485     int            *length;
10486     int             nx, ny;
10487     int             x, y;
10488     int             nlambda;
10489     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10490     int             missing_top, missing_bot; 
10491     int             null;
10492     int             order;
10493     int             i, k;
10494     
10495 
10496     flux += flux;
10497 
10498     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
10499         slits == NULL || polytraces == NULL) { 
10500         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10501         return NULL;
10502     }
10503 
10504     if (dispersion <= 0.0) {
10505         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10506         return NULL;
10507     }
10508 
10509     if (red - blue < dispersion) {
10510         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10511         return NULL;
10512     }
10513     
10514     nx = cpl_image_get_size_x(spectra);
10515     ny = cpl_image_get_size_y(spectra);
10516 
10517     if (nx != cpl_image_get_size_x(spatial) ||
10518         ny != cpl_image_get_size_y(spatial) ||
10519         nx != cpl_image_get_size_x(wavecalib) ||
10520         ny != cpl_image_get_size_y(wavecalib)) {
10521         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10522         return NULL;
10523     }
10524 
10525     nlambda     = (red - blue) / dispersion;
10526     pixel_above = (red - reference) / dispersion;
10527     pixel_below = (reference - blue) / dispersion;
10528 
10529     data  = cpl_image_get_data(spectra);
10530     sdata = cpl_image_get_data(spatial);
10531     wdata = cpl_image_get_data(wavecalib);
10532     
10533     nslits   = cpl_table_get_nrow(slits);
10534     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10535     order    = cpl_table_get_ncol(polytraces) - 2;
10536     position = cpl_table_get_data_int(slits, "position");
10537     length   = cpl_table_get_data_int(slits, "length");
10538     
10539     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
10540 
10541     for (i = 0; i < nslits; i++) {
10542 
10543          if (length == 0)
10544              continue;
10545 
10546         /*
10547          * Note that the x coordinate of the reference pixels on the CCD
10548          * is taken arbitrarily at the top end of each slit. This wouldn't
10549          * be entirely correct in case of curved slits, or in presence of
10550          * heavy distortions: in such cases the spatial resampling is
10551          * really performed across a wide range of wavelengths. But
10552          * the lag between top and bottom spectral curvature models
10553          * would introduce even in such cases negligible effects on
10554          * the spectral spatial resampling.
10555          */
10556 
10557         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10558 
10559         start_pixel = refpixel - pixel_below;
10560         if (start_pixel < 1)
10561             start_pixel = 1;
10562 
10563         end_pixel = refpixel + pixel_above;
10564         if (end_pixel > nx)
10565             end_pixel = nx;
10566 
10567         /*
10568          * Recover from the table of spectral curvature coefficients
10569          * the curvature polynomials.
10570          */
10571 
10572         missing_top = 0;
10573         polytop = cpl_polynomial_new(1);
10574         for (k = 0; k <= order; k++) {
10575             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10576             if (null) {
10577                 cpl_polynomial_delete(polytop);
10578                 missing_top = 1;
10579                 break;
10580             }
10581             cpl_polynomial_set_coeff(polytop, &k, coeff);
10582         }
10583 
10584         missing_bot = 0;
10585         polybot = cpl_polynomial_new(1);
10586         for (k = 0; k <= order; k++) {
10587             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10588             if (null) {
10589                 cpl_polynomial_delete(polybot);
10590                 missing_bot = 1;
10591                 break;
10592             }
10593             cpl_polynomial_set_coeff(polybot, &k, coeff);
10594         }
10595 
10596         if (missing_top && missing_bot) {
10597             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
10598                           slit_id[i]);
10599             continue;
10600         }
10601 
10602         /*
10603          * In case just one of the two edges was not traced, the other
10604          * edge curvature model is duplicated and shifted to the other
10605          * end of the slit: better than nothing!
10606          */
10607 
10608         if (missing_top) {
10609             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
10610                           "the spectral curvature of the lower edge "
10611                           "is used instead.", slit_id[i]);
10612             polytop = cpl_polynomial_duplicate(polybot);
10613             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10614             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10615             k = 0;
10616             coeff = cpl_polynomial_get_coeff(polybot, &k);
10617             coeff += ytop - ybot;
10618             cpl_polynomial_set_coeff(polytop, &k, coeff);
10619         }
10620 
10621         if (missing_bot) {
10622             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
10623                           "the spectral curvature of the upper edge "
10624                           "is used instead.", slit_id[i]);
10625             polybot = cpl_polynomial_duplicate(polytop);
10626             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10627             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10628             k = 0;
10629             coeff = cpl_polynomial_get_coeff(polytop, &k);
10630             coeff -= ytop - ybot;
10631             cpl_polynomial_set_coeff(polybot, &k, coeff);
10632         }
10633 
10634         /*
10635          * Allocate image for current extracted slit
10636          */
10637 
10638         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
10639         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
10640         npseudo = ceil(top-bot) + 1;
10641 
10642         if (npseudo < 1) {
10643             cpl_polynomial_delete(polytop);
10644             cpl_polynomial_delete(polybot);
10645             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
10646                           slit_id[i]);
10647             continue;
10648         }
10649 
10650         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
10651         xdata = cpl_image_get_data(exslit[i]);
10652 
10653         /*
10654          * Write interpolated spectral values to remapped slit spectrum.
10655          */
10656 
10657         for (x = start_pixel; x < end_pixel; x++) {
10658             top = cpl_polynomial_eval_1d(polytop, x, NULL);
10659             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
10660             itop = top + 1;
10661             ibot = bot;
10662             if (itop < 0)
10663                 itop = 0;
10664             if (itop > ny - 1)
10665                 itop = ny - 1;
10666             if (ibot < 0)
10667                 ibot = 0;
10668             if (ibot > ny - 1)
10669                 ibot = ny - 1;
10670             for (y = ibot; y < itop; y++) {
10671                  lambda11 = wdata[x + y*nx];
10672                  if (lambda11 < 1.0)        /* Impossible wavelength */
10673                      continue;
10674                  space11 = sdata[x + y*nx];
10675                  if (space11 < 0.0)         /* Impossible spatial coordinate */
10676                      continue;
10677                  lambda01 = wdata[x - 1 + y*nx];
10678                  if (lambda01 < 1.0)        /* Impossible wavelength */
10679                      continue;
10680                  space01 = sdata[x - 1 + y*nx];
10681                  if (space01 < 0.0)         /* Impossible spatial coordinate */
10682                      continue;
10683 
10684                  shift = 0;
10685 
10686 /****+
10687                  if (wdata[x + (y+1)*nx] > 1.0) {
10688                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
10689                          shift = -1;
10690                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
10691                              shift--;
10692                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
10693                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
10694                              shift++;
10695                          }
10696                      }
10697                      else {
10698                          shift = 1;
10699                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
10700                              shift++;
10701                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
10702                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
10703                              shift--;
10704                          }
10705                      }
10706                  }
10707 ****/
10708 
10709 /****
10710 printf("y = %d, shift = %d\n", y, shift);
10711 ****/
10712 
10713                  lambda10 = wdata[x + shift + (y+1)*nx];
10714                  if (lambda10 < 1.0)        /* Impossible wavelength */
10715                      continue;
10716                  space10 = sdata[x + shift + (y+1)*nx];
10717                  if (space10 < 0.0)         /* Impossible spatial coordinate */
10718                      continue;
10719                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
10720                  if (lambda00 < 1.0)        /* Impossible wavelength */
10721                      continue;
10722                  space00 = sdata[x - 1 + shift + (y+1)*nx];
10723                  if (space00 < 0.0)         /* Impossible spatial coordinate */
10724                      continue;
10725                  
10726                  /*
10727                   * Find the variation in lambda and space in this
10728                   * position for each CCD pixel (both quantities are 
10729                   * expected to be positive).
10730                   */
10731 
10732                  dL = lambda11 - lambda01;
10733                  dS = space11 - space10;
10734 
10735                  /*
10736                   * Find the position (L,S) of the output pixel 
10737                   * (by integer truncation).
10738                   */
10739 
10740                  L = (lambda11 - blue)/dispersion + 0.5;
10741                  S = space11 + 0.5;                   /* Counted from top! */
10742 
10743                  if (L < 0 || L >= nlambda)
10744                      continue;
10745                  if (S < 0 || S > npseudo)
10746                      continue;
10747 
10748                  /*
10749                   * Find the coordinate of pixel (L,S)
10750                   */
10751 
10752                  lambda = blue + L*dispersion;
10753                  space  = S;
10754 
10755                  /*
10756                   * Find the interpolation point on the CCD: it is
10757                   * defined as the (positive) distance from current
10758                   * CCD pixel (x,y) of the interpolation point (x',y'),
10759                   * measured in CCD pixels. The interpolation point
10760                   * is located between the four CCD pixels selected
10761                   * above.
10762                   */
10763 
10764                  xfrac = (lambda11-lambda)/dL;
10765                  yfrac = (space11-space)/dS;
10766 
10767 /*
10768 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
10769 printf("xyfrac = %f, %f\n", xfrac, yfrac);
10770 */
10771 
10772                  /*
10773                   * Get the four values to interpolate
10774                   */
10775 
10776                  value11 = data[x + y*nx];
10777                  value01 = data[x - 1 + y*nx];
10778                  value10 = data[x + shift + (y+1)*nx];
10779                  value00 = data[x + shift - 1 + (y+1)*nx];
10780 
10781                  /*
10782                   * Interpolation
10783                   */
10784 
10785                  value1 = (1-xfrac)*value11 + xfrac*value01;
10786                  value0 = (1-xfrac)*value10 + xfrac*value00;
10787                  value  = (1-yfrac)*value1  + yfrac*value0;
10788 
10789                  /*
10790                   * Write this value to the appropriate (L,S) coordinate
10791                   * on output slit
10792                   */
10793 
10794                  xdata[L + nlambda*(npseudo-S)] = value;
10795                  
10796             }
10797         }
10798         cpl_polynomial_delete(polytop);
10799         cpl_polynomial_delete(polybot);
10800     }
10801 
10802     /*
10803      * Now all the slits images are copied to a single image
10804      */
10805 
10806     ysize = 0;
10807     for (i = 0; i < nslits; i++)
10808         if (exslit[i])
10809             ysize += cpl_image_get_size_y(exslit[i]);
10810 
10811     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
10812 
10813     yint = -1;
10814     for (i = 0; i < nslits; i++) {
10815         if (exslit[i]) {
10816             yint += cpl_image_get_size_y(exslit[i]);
10817             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
10818             cpl_image_delete(exslit[i]);
10819             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
10820         }
10821     }
10822 
10823     cpl_free(exslit);
10824 
10825     return remapped;
10826 
10827 }
10828 
10829 
10862 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
10863                              double dispersion, double factor, int minpoints,
10864                              cpl_image *skymap)
10865 {
10866     const char *func = "mos_sky_map_super";
10867 
10868     cpl_vector **vector;
10869     cpl_vector **wvector;
10870     double       firstLambda, lastLambda;
10871     double       lambda, lambda1, lambda2;
10872     double       value, value1, value2;
10873     double       frac;
10874     float        min, max;
10875     int         *count;
10876     int          nbin, bin;
10877     int          nx, ny, npix;
10878     int          first_valid, valid_bins;
10879     int          i, j;
10880 
10881     cpl_table   *sky;
10882     double      *sky_spectrum;
10883     double      *sky_wave;
10884     float       *data;
10885     float       *sdata;
10886     float       *kdata;
10887 
10888 
10889     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
10890         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10891         return NULL;
10892     }
10893     
10894     if (dispersion <= 0.0) {
10895         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10896         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
10897         return NULL;
10898     }
10899 
10900     nx = cpl_image_get_size_x(spectra);
10901     ny = cpl_image_get_size_y(spectra);
10902     npix = nx * ny;
10903 
10904     if (nx != cpl_image_get_size_x(wavemap) ||
10905         ny != cpl_image_get_size_y(wavemap) ||
10906         nx != cpl_image_get_size_x(skymap) ||
10907         ny != cpl_image_get_size_y(skymap)) {
10908         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10909         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
10910         return NULL;
10911     }
10912 
10913     if (factor < 1.0) {
10914         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10915         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
10916                       cpl_error_get_message());
10917         return NULL;
10918     }
10919 
10920     if (minpoints < 0) {
10921         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10922         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
10923         return NULL;
10924     }
10925 
10926     dispersion /= factor;
10927 
10928 
10929     /*
10930      * Find bluest and reddest wavelengths in the whole image
10931      */
10932 
10933     data = cpl_image_get_data(wavemap);
10934 
10935     for (i = 0; i < npix; i++) {
10936         if (data[i] > 1.0) {
10937             min = max = data[i];
10938             j = i+1;
10939             break;
10940         }
10941     }
10942 
10943     for (i = j; i < npix; i++) {
10944         if (data[i] < 1.0)      /* Impossible wavelength */
10945             continue;
10946         if (min > data[i])
10947             min = data[i];
10948         if (max < data[i])
10949             max = data[i];
10950     }
10951 
10952     firstLambda = min;
10953     lastLambda = max;
10954 
10955 
10956     /*
10957      * Determine length of median spectrum
10958      */
10959 
10960     nbin = (lastLambda - firstLambda) / dispersion;
10961 
10962     /*
10963      * Count how many values will be found for each spectral bin.
10964      * The ith bin has a wavelength range from firstLambda + i*dispersion
10965      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
10966      * it is assigned to its central wavelength.
10967      */
10968 
10969     count = cpl_calloc(nbin, sizeof(int));
10970 
10971     data = cpl_image_get_data(wavemap);
10972 
10973     for (i = 0; i < npix; i++) {
10974         if (data[i] < 1.0)
10975             continue;
10976         bin = (data[i] - firstLambda) / dispersion;
10977         if (bin < nbin)                               /* Safer */
10978             count[bin]++;
10979     }
10980 
10981     valid_bins = 0;
10982     for (i = 0; i < nbin; i++)
10983         if (count[i] >= minpoints)
10984             valid_bins++;
10985 
10986     if (valid_bins < nbin/3) {
10987         cpl_msg_warning(func, "Cannot determine a good global sky "
10988                         "spectrum from input data");
10989         return NULL;
10990     }
10991 
10992 
10993     /*
10994      * Allocate an array of vectors with the appropriate size, to
10995      * contain a list of all the spectral pixels values. At the same
10996      * time, reset the array of counters (because we will have to
10997      * count again...).
10998      */
10999 
11000     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11001     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11002     for (i = 0; i < nbin; i++) {
11003         if (count[i] >= minpoints) {
11004             vector[i] = cpl_vector_new(count[i]);
11005             wvector[i] = cpl_vector_new(count[i]);
11006         }
11007         count[i] = 0;
11008     }
11009 
11010 
11011     /*
11012      * Read the wavemap and the spectral images, and add the data values
11013      * to the appropriate wavelength bins
11014      */
11015 
11016     data  = cpl_image_get_data(wavemap);
11017     sdata = cpl_image_get_data(spectra);
11018 
11019     for (i = 0; i < npix; i++) {
11020         if (data[i] < 1.0)
11021             continue;
11022         bin = (data[i] - firstLambda) / dispersion;
11023         if (bin < nbin) {                             /* Safer */
11024             if (vector[bin]) {
11025                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11026                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11027             }
11028             count[bin]++;
11029         }
11030     }
11031 
11032 
11033     /*
11034      * Compute the median flux for each wavelength bin, and destroy
11035      * at the same time the used vectors
11036      */
11037 
11038     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11039     sky_wave = cpl_calloc(nbin, sizeof(double));
11040     for (i = 0; i < nbin; i++) {
11041         if (vector[i]) {
11042             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11043             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11044             cpl_vector_delete(vector[i]);
11045             cpl_vector_delete(wvector[i]);
11046         }
11047     }
11048 
11049     cpl_free(vector);
11050     cpl_free(wvector);
11051 
11052 
11053     /*
11054      * Here possible gaps in the final spectrum are filled by interpolation
11055      */
11056 
11057     for (i = 0; i < nbin; i++) {
11058         if (count[i] >= minpoints) {
11059             first_valid = i;
11060             break;
11061         }
11062     }
11063     
11064     for (i = first_valid; i < nbin; i++) {
11065         if (count[i] < minpoints) {
11066             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11067             for (j = i+1; j < nbin; j++) {
11068                 if (count[j] >= minpoints) {
11069                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11070                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11071                                         / 2;
11072                     }
11073                     else {
11074                         frac = (sky_wave[i] - sky_wave[i-1]) 
11075                              / (sky_wave[j] - sky_wave[i-1]);
11076                         sky_spectrum[i] = frac * sky_spectrum[j]
11077                                         + (1 - frac) * sky_spectrum[i-1];
11078                     }
11079                 }
11080             }
11081         }
11082     }
11083 
11084 
11085     /*
11086      * Create the output table
11087      */
11088 
11089     sky = cpl_table_new(nbin);
11090     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11091     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11092     cpl_table_wrap_int(sky, count, "npoints");
11093 
11094 
11095     /*
11096      * Fill the sky map
11097      */
11098 
11099     data  = cpl_image_get_data(wavemap);
11100     sdata = cpl_image_get_data(spectra);
11101     kdata = cpl_image_get_data(skymap);
11102 
11103     for (i = 0; i < npix; i++) {
11104 
11105         /*
11106          * Currently based on linear interpolation
11107          */
11108 
11109         lambda = data[i];
11110         if (lambda < 1.0)
11111             continue;
11112         bin = (lambda - firstLambda) / dispersion;
11113         lambda1 = sky_wave[bin];
11114         value1 = sky_spectrum[bin];
11115         if (lambda1 < lambda) {
11116             bin++;
11117             if (bin < nbin) {
11118                 lambda2 = sky_wave[bin];
11119                 value2  = sky_spectrum[bin];
11120                 if (lambda2 - lambda1 < 0.1) {
11121                     value = (value1 + value2) / 2;
11122                 }
11123                 else {
11124                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11125                     value = frac * value2 + (1 - frac) * value1;
11126                 }
11127             }
11128             else {
11129                 value = value1;
11130             }
11131         }
11132         else {
11133             if (bin > 0) {
11134                 bin--;
11135                 lambda2 = lambda1;
11136                 value2  = value1;
11137                 lambda1 = sky_wave[bin];
11138                 value1  = sky_spectrum[bin];
11139                 if (lambda2 - lambda1 < 0.1) {
11140                     value = (value1 + value2) / 2;
11141                 }
11142                 else {
11143                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11144                     value = frac * value2 + (1 - frac) * value1;
11145                 }
11146             }
11147             else {
11148                 value = value1;
11149             }
11150         }
11151         kdata[i] = value;
11152     }
11153 
11154     if (first_valid)
11155         cpl_table_erase_window(sky, 0, first_valid);
11156 
11157     return sky;
11158 
11159 }
11160 
11161 
11195 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11196                        double dispersion, cpl_image *skymap)
11197 {
11198     const char *func = "mos_sky_map";
11199 
11200     cpl_vector **vector;
11201     double       firstLambda, lastLambda;
11202     double       lambda, lambda1, lambda2;
11203     double       value, value1, value2;
11204     float        min, max;
11205     int         *count;
11206     int          nbin, bin;
11207     int          nx, ny, npix;
11208     int          i, j;
11209 
11210     cpl_table   *sky;
11211     double      *sky_spectrum;
11212     float       *data;
11213     float       *sdata;
11214     float       *kdata;
11215     double      *wdata;
11216 
11217 
11218     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11219         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11220         return NULL;
11221     }
11222     
11223     if (dispersion <= 0.0) {
11224         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11225         return NULL;
11226     }
11227 
11228     nx = cpl_image_get_size_x(spectra);
11229     ny = cpl_image_get_size_y(spectra);
11230     npix = nx * ny;
11231 
11232     if (nx != cpl_image_get_size_x(wavemap) ||
11233         ny != cpl_image_get_size_y(wavemap) ||
11234         nx != cpl_image_get_size_x(skymap) ||
11235         ny != cpl_image_get_size_y(skymap)) {
11236         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11237         return NULL;
11238     }
11239 
11240 
11241     /*
11242      * Find bluest and reddest wavelengths in the whole image
11243      */
11244 
11245     data = cpl_image_get_data(wavemap);
11246 
11247     for (i = 0; i < npix; i++) {
11248         if (data[i] > 1.0) {
11249             min = max = data[i];
11250             j = i+1;
11251             break;
11252         }
11253     }
11254 
11255     for (i = j; i < npix; i++) {
11256         if (data[i] < 1.0)      /* Impossible wavelength */
11257             continue;
11258         if (min > data[i])
11259             min = data[i];
11260         if (max < data[i])
11261             max = data[i];
11262     }
11263 
11264     firstLambda = min;
11265     lastLambda = max;
11266 
11267 
11268     /*
11269      * Determine length of median spectrum
11270      */
11271 
11272     nbin = (lastLambda - firstLambda) / dispersion;
11273 
11274     /*
11275      * Count how many values will be found for each spectral bin.
11276      * The ith bin has a wavelength range from firstLambda + i*dispersion
11277      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11278      * it is assigned to its central wavelength.
11279      */
11280 
11281     count = cpl_calloc(nbin, sizeof(int));
11282 
11283     data = cpl_image_get_data(wavemap);
11284 
11285     for (i = 0; i < npix; i++) {
11286         if (data[i] < 1.0)
11287             continue;
11288         bin = (data[i] - firstLambda) / dispersion;
11289         if (bin < nbin)                               /* Safer */
11290             count[bin]++;
11291     }
11292 
11293 
11294     /*
11295      * Allocate an array of vectors with the appropriate size, to
11296      * contain a list of all the spectral pixels values. At the same
11297      * time, reset the array of counters (because we will have to
11298      * count again...).
11299      */
11300 
11301     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11302     for (i = 0; i < nbin; i++) {
11303         if (count[i])
11304             vector[i] = cpl_vector_new(count[i]);
11305         else
11306             vector[i] = NULL;
11307         count[i] = 0;
11308     }
11309 
11310 
11311     /*
11312      * Read the wavemap and the spectral images, and add the data values
11313      * to the appropriate wavelength bins
11314      */
11315 
11316     data  = cpl_image_get_data(wavemap);
11317     sdata = cpl_image_get_data(spectra);
11318 
11319     for (i = 0; i < npix; i++) {
11320         if (data[i] < 1.0)
11321             continue;
11322         bin = (data[i] - firstLambda) / dispersion;
11323         if (bin < nbin) {                             /* Safer */
11324             cpl_vector_set(vector[bin], count[bin], sdata[i]);
11325             count[bin]++;
11326         }
11327     }
11328 
11329 
11330     /*
11331      * Compute the median flux for each wavelength bin, and destroy
11332      * at the same time the used vectors
11333      */
11334 
11335     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11336     for (i = 0; i < nbin; i++) {
11337         if (vector[i]) {
11338             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11339             cpl_vector_delete(vector[i]);
11340         }
11341     }
11342 
11343     cpl_free(vector);
11344 
11345 
11346     /*
11347      * Here possible gaps in the final spectrum should be filled
11348      * by interpolation
11349      */
11350 
11351     /* ... */
11352 
11353     /*
11354      * Create the output table
11355      */
11356 
11357     sky = cpl_table_new(nbin);
11358     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
11359     cpl_table_set_column_unit(sky, "wavelength", "pixel");
11360     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11361     cpl_table_wrap_int(sky, count, "npoints");
11362     for (i = 0; i < nbin; i++)
11363         cpl_table_set_double(sky, "wavelength", i, 
11364                              firstLambda + (i+0.5)*dispersion);
11365 
11366 
11367     /*
11368      * Fill the sky map
11369      */
11370 
11371     data  = cpl_image_get_data(wavemap);
11372     sdata = cpl_image_get_data(spectra);
11373     kdata = cpl_image_get_data(skymap);
11374     wdata = cpl_table_get_data_double(sky, "wavelength");
11375 
11376     for (i = 0; i < npix; i++) {
11377 
11378         /*
11379          * Currently based on linear interpolation
11380          */
11381 
11382         lambda = data[i];
11383         if (lambda < 1.0)
11384             continue;
11385         bin = (lambda - firstLambda) / dispersion;
11386         lambda1 = wdata[bin];
11387         value1 = sky_spectrum[bin];
11388         if (lambda1 < lambda) {
11389             bin++;
11390             if (bin < nbin) {
11391                 lambda2 = wdata[bin];
11392                 value2  = sky_spectrum[bin];
11393                 value   = ((lambda2 - lambda)*value1 
11394                         +  (lambda - lambda1)*value2) / dispersion;
11395             }
11396             else {
11397                 value = value1;
11398             }
11399         }
11400         else {
11401             if (bin > 0) {
11402                 bin--;
11403                 lambda2 = lambda1;
11404                 value2  = value1;
11405                 lambda1 = wdata[bin];
11406                 value1  = sky_spectrum[bin];
11407                 value   = ((lambda2 - lambda)*value1 
11408                         +  (lambda - lambda1)*value2)/dispersion;
11409             }
11410             else {
11411                 value = value1;
11412             }
11413         }
11414         kdata[i] = value;
11415     }
11416 
11417     return sky;
11418 
11419 }
11420 
11421 
11437 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
11438 {
11439     const char *func = "mos_sky_local_old";
11440 
11441     cpl_image *exslit;
11442     cpl_image *sky;
11443     cpl_image *skymap;
11444     float     *data;
11445     float     *sdata;
11446     int        nx, ny;
11447     int        xlow, ylow, xhig, yhig;
11448     int        nslits;
11449     int       *slit_id;
11450     int       *position;
11451     int       *length;
11452     int        i, j, k;
11453 
11454 
11455     if (spectra == NULL) {
11456         cpl_msg_error(func, 
11457                       "A scientific rectified spectral image must be given");
11458         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11459         return NULL;
11460     }
11461 
11462     if (slits == NULL) {
11463         cpl_msg_error(func, "A slits position table must be given");
11464         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11465         return NULL;
11466     }
11467 
11468     nslits   = cpl_table_get_nrow(slits);
11469     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11470     position = cpl_table_get_data_int(slits, "position");
11471     length   = cpl_table_get_data_int(slits, "length");
11472 
11473     nx = cpl_image_get_size_x(spectra);
11474     ny = cpl_image_get_size_y(spectra);
11475 
11476     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11477 
11478     xlow = 1;
11479     xhig = nx;
11480     for (i = 0; i < nslits; i++) {
11481 
11482         if (length[i] == 0)
11483             continue;
11484 
11485         /*
11486          * Define the extraction boundaries. We DON'T write:
11487          *
11488          * ylow = position[i];
11489          * yhig = ylow + length[i];
11490          *
11491          * because the cpl_image pixels are counted from 1, and because in
11492          * cpl_image_extract() the coordinates of the last pixel are inclusive.
11493          */
11494 
11495         ylow = position[i] + 1;
11496         yhig = ylow + length[i] - 1;
11497 
11498         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
11499         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
11500         cpl_image_delete(exslit);
11501 
11502         data   = cpl_image_get_data(skymap);
11503         data  += nx * position[i];
11504 
11505         for (j = 0; j < length[i]; j++) {
11506             sdata  = cpl_image_get_data(sky);
11507             for (k = 0; k < nx; k++) {
11508                 *data++ = *sdata++;
11509             }
11510         }
11511 
11512         cpl_image_delete(sky);
11513     }
11514 
11515     return skymap;
11516 
11517 }
11518 
11519 
11539 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
11540 {
11541     const char *func = "mos_sky_local";
11542 
11543     char        name[MAX_COLNAME];
11544 
11545     cpl_polynomial *fit;
11546     cpl_vector     *points;
11547     cpl_vector     *values;
11548     cpl_vector     *keep_points;
11549     cpl_vector     *keep_values;
11550     cpl_image      *exslit;
11551     cpl_image      *sky;
11552     cpl_image      *subtracted;
11553     cpl_image      *profile;
11554     cpl_image      *skymap;
11555     cpl_table      *objects;
11556     float          *data;
11557     float          *sdata;
11558     float          *xdata;
11559     double         *vdata;
11560     double         *pdata;
11561     double          median;
11562     int             nx, ny;
11563     int             xlow, ylow, xhig, yhig;
11564     int             nslits;
11565     int            *slit_id;
11566     int            *position;
11567     int            *length;
11568     int            *is_sky;
11569     int             nsky, nbad;
11570     int             maxobjects;
11571     int             margin = 3;
11572     int             radius = 6;
11573     int             i, j, k;
11574 
11575 
11576     if (spectra == NULL) {
11577         cpl_msg_error(func, 
11578                       "A scientific rectified spectral image must be given");
11579         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11580         return NULL;
11581     }
11582 
11583     if (slits == NULL) {
11584         cpl_msg_error(func, "A slits position table must be given");
11585         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11586         return NULL;
11587     }
11588 
11589     if (order < 0) {
11590         cpl_msg_error(func, "Invalid fit order");
11591         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11592         return NULL;
11593     }
11594 
11595     nslits   = cpl_table_get_nrow(slits);
11596     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11597     position = cpl_table_get_data_int(slits, "position");
11598     length   = cpl_table_get_data_int(slits, "length");
11599 
11600     nx = cpl_image_get_size_x(spectra);
11601     ny = cpl_image_get_size_y(spectra);
11602 
11603     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11604 
11605     xlow = 1;
11606     xhig = nx;
11607     for (i = 0; i < nslits; i++) {
11608 
11609         if (length[i] == 0)
11610             continue;
11611 
11612         /*
11613          * Define the extraction boundaries. We DON'T write:
11614          *
11615          * ylow = position[i];
11616          * yhig = ylow + length[i];
11617          *
11618          * because the cpl_image pixels are counted from 1, and because in
11619          * cpl_image_extract() the coordinates of the last pixel are inclusive.
11620          */
11621 
11622         ylow = position[i] + 1;
11623         yhig = ylow + length[i] - 1;
11624 
11625         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
11626         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
11627         cpl_image_delete(exslit);
11628 
11629         data   = cpl_image_get_data(skymap);
11630         data  += nx * position[i];
11631 
11632         for (j = 0; j < length[i]; j++) {
11633             sdata  = cpl_image_get_data(sky);
11634             for (k = 0; k < nx; k++) {
11635                 *data++ = *sdata++;
11636             }
11637         }
11638 
11639         cpl_image_delete(sky);
11640     }
11641 
11642 
11643     /*
11644      * Preliminary sky-subtracted image
11645      */
11646 
11647     subtracted = cpl_image_duplicate(spectra);
11648     cpl_image_subtract(subtracted, skymap);
11649     cpl_image_delete(skymap);
11650 
11651 
11652     /*
11653      * Detect objects positions in all slits
11654      */
11655 
11656     objects = cpl_table_duplicate(slits);
11657     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
11658     cpl_image_delete(profile);
11659     cpl_image_delete(subtracted);
11660 
11661 
11662     /*
11663      * Flag the sky pixels. Note that maxobjects is intentionally 
11664      * the max number of objects increased by one.
11665      */
11666 
11667     maxobjects = 1;
11668     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
11669     while (cpl_table_has_column(objects, name)) {
11670         maxobjects++;
11671         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
11672     }
11673 
11674     is_sky = cpl_calloc(ny, sizeof(int));
11675 
11676     for (i = 0; i < nslits; i++) {
11677 
11678         if (length[i] == 0)
11679             continue;
11680 
11681         ylow = position[i] + margin;
11682         yhig = position[i] + length[i] - margin;
11683 
11684         for (j = ylow; j < yhig; j++)
11685             is_sky[j] = 1;
11686 
11687         for (j = 1; j < maxobjects; j++) {
11688             snprintf(name, MAX_COLNAME, "object_%d", j);
11689             if (cpl_table_is_valid(objects, name, i)) {
11690                 snprintf(name, MAX_COLNAME, "start_%d", j);
11691                 ylow = cpl_table_get_int(objects, name, i, NULL);
11692                 snprintf(name, MAX_COLNAME, "end_%d", j);
11693                 yhig = cpl_table_get_int(objects, name, i, NULL);
11694                 for (k = ylow; k <= yhig; k++)
11695                     is_sky[k] = 0;
11696             }
11697         }
11698 
11699 
11700         /*
11701          * Eliminate isolated sky points
11702          */
11703 
11704         ylow = position[i] + margin + 1;
11705         yhig = position[i] + length[i] - margin - 1;
11706 
11707         for (j = ylow; j < yhig; j++)
11708             if (is_sky[j])
11709                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
11710                     is_sky[j] = 0;
11711 
11712     }
11713 
11714 
11715     /*
11716      * Determination of the sky map
11717      */
11718 
11719     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11720 
11721     for (i = 0; i < nslits; i++) {
11722 
11723         if (length[i] == 0)
11724             continue;
11725 
11726         ylow = position[i];
11727         yhig = ylow + length[i];
11728 
11729         nsky = 0;
11730         for (j = ylow; j < yhig; j++)
11731             if (is_sky[j])
11732                 nsky++;
11733 
11734         if (nsky > order + 1) {
11735             if (order) {
11736                 points = cpl_vector_new(nsky);
11737                 nsky = 0;
11738                 for (j = ylow; j < yhig; j++) {
11739                     if (is_sky[j]) {
11740                         cpl_vector_set(points, nsky, j);
11741                         nsky++;
11742                     }
11743                 }
11744 
11745                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
11746                 xdata = cpl_image_get_data(exslit);
11747                 values = cpl_vector_new(nsky);
11748 
11749                 for (j = 0; j < nx; j++) {
11750                     nsky = 0;
11751                     for (k = ylow; k < yhig; k++) {
11752                         if (is_sky[k]) {
11753                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
11754                             nsky++;
11755                         }
11756                     }
11757 
11758                     /*
11759                      * Eliminate obvious outliers
11760                      */
11761 
11762                     median = cpl_vector_get_median_const(values);
11763                     vdata = cpl_vector_get_data(values);
11764                     pdata = cpl_vector_get_data(points);
11765                     nbad = 0;
11766                     for (k = 0; k < nsky; k++) {
11767                         if (fabs(vdata[k] - median) < 100) {
11768                             if (nbad) {
11769                                 vdata[k-nbad] = vdata[k];
11770                                 pdata[k-nbad] = pdata[k];
11771                             }
11772                         }
11773                         else
11774                             nbad++;
11775                     }
11776 
11777                     if (nsky == nbad)
11778                         continue;
11779 
11780                     if (nbad && nsky - nbad > order + 1) {
11781                         keep_values = values;
11782                         keep_points = points;
11783                         values = cpl_vector_wrap(nsky-nbad, vdata);
11784                         points = cpl_vector_wrap(nsky-nbad, pdata);
11785                     }
11786 
11787                     if (nsky - nbad > order + 1) {
11788 
11789                         fit = cpl_polynomial_fit_1d_create(points, values, 
11790                                                            order, NULL);
11791 
11792                         if (fit) {
11793                             for (k = ylow; k < yhig; k++) {
11794                                 xdata[j+(k-ylow)*nx] = 
11795                                          cpl_polynomial_eval_1d(fit, k, NULL);
11796                             }
11797 
11798                             cpl_polynomial_delete(fit);
11799                         }
11800                         else
11801                             cpl_error_reset();
11802                     }
11803                     else {
11804                         for (k = 0; k < nsky; k++) {
11805                             xdata[j+k*nx] = median;
11806                         }
11807                     }
11808 
11809                     if (nbad && nsky - nbad > order + 1) {
11810                         cpl_vector_unwrap(values);
11811                         cpl_vector_unwrap(points);
11812                         values = keep_values;
11813                         points = keep_points;
11814                     }
11815 
11816                     if (nbad) {
11817                         nsky = 0;
11818                         for (k = ylow; k < yhig; k++) {
11819                             if (is_sky[k]) {
11820                                 cpl_vector_set(points, nsky, k);
11821                                 nsky++;
11822                             }
11823                         }
11824                     }
11825 
11826                 }
11827 
11828                 cpl_vector_delete(values);
11829                 cpl_vector_delete(points);
11830 
11831                 cpl_image_copy(skymap, exslit, 1, ylow+1);
11832                 cpl_image_delete(exslit);
11833 
11834             }
11835             else {
11836                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
11837                 xdata = cpl_image_get_data(exslit);
11838                 values = cpl_vector_new(nsky);
11839 
11840                 for (j = 0; j < nx; j++) {
11841                     nsky = 0;
11842                     for (k = ylow; k < yhig; k++) {
11843                         if (is_sky[k]) {
11844                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
11845                             nsky++;
11846                         }
11847                     }
11848 
11849                     median = cpl_vector_get_median_const(values);
11850 
11851                     for (k = ylow; k < yhig; k++)
11852                         xdata[j+(k-ylow)*nx] = median;
11853 
11854                 }
11855 
11856                 cpl_vector_delete(values);
11857 
11858                 cpl_image_copy(skymap, exslit, 1, ylow+1);
11859                 cpl_image_delete(exslit);
11860             }
11861         }
11862         else
11863             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
11864     }
11865 
11866     cpl_free(is_sky);
11867 
11868     return skymap;
11869 
11870 }
11871 
11872 
11894 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
11895                                  float threshold, float ratio)
11896 {
11897     const char *func = "mos_clean_cosmics";
11898 
11899     cpl_image  *smoothImage;
11900     cpl_table  *table;
11901     cpl_matrix *kernel;
11902     int        *xdata;
11903     int        *ydata;
11904     float      *idata;
11905     float      *sdata;
11906     float       sigma, sum, value, smoothValue;
11907     double      noise;
11908     int         count;
11909     float       fMax;
11910     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
11911     int         xLen;
11912     int         yLen;
11913     int         nPix;
11914     int         first = 1;  /* position of first cosmic ray candidate
11915                                encountered while scanning the image */
11916     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
11917     int         numCosmic = 0;
11918     int         found, foundContiguousCandidate;
11919     int        *cosmic;
11920   
11921 
11922     if (image == NULL)
11923         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11924 
11925 
11926     /*
11927      *  "cosmic" is a flags holder (initialized to zero):
11928      *
11929      *           -1 = candidate for cosmic ray
11930      *            0 = not a cosmic
11931      *            1 = a cosmic ray
11932      *            2 = member of current group of contiguous candidates
11933      *            3 = examined member of current group
11934      */
11935 
11936     xLen = cpl_image_get_size_x(image);
11937     yLen = cpl_image_get_size_y(image);
11938 
11939     if (xLen < 4 || yLen < 4)
11940         return CPL_ERROR_NONE;
11941 
11942     nPix = xLen * yLen;
11943 
11944     /*
11945      * Noise estimation from negative offsets in image. Note that this
11946      * assumes that the background level (skyLevel) has already been 
11947      * subtracted from the data. In this way we estimate the noise due 
11948      * to detector readout and to the background signal level (before 
11949      * it were removed). Theoretically this is given by 
11950      *
11951      *        noise = sqrt(ron^2 + skyLevel/gain)
11952      *
11953      * where ron is the read-out-noise. To this we will sum the noise 
11954      * contribution due to any increase of the signal above the background
11955      * by an amount scienceLevel. Theoretically the total noise is given by
11956      *
11957      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
11958      *
11959      * that is
11960      *
11961      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
11962      *
11963      */
11964 
11965     idata = cpl_image_get_data(image);
11966     noise = 0.0;
11967     count = 0;
11968 
11969     for (i = 0; i < nPix; i++) {
11970         if (idata[i] < -0.00001) {
11971             noise -= idata[i];
11972             count++;
11973         }
11974     }
11975 
11976     noise /= count;
11977     noise *= 1.25;       /* Factor to convert average deviation to sigma */
11978 
11979     cosmic = cpl_calloc(nPix, sizeof(int));
11980 
11981     if (threshold < 0.)
11982         threshold = 4.0;
11983     if (ratio < 0.)
11984         ratio = 2.0;
11985 
11986     kernel = cpl_matrix_new(3, 3);
11987     cpl_matrix_fill(kernel, 1.0);
11988     cpl_matrix_set(kernel, 1, 1, 0.0);
11989     smoothImage = cpl_image_filter_median(image, kernel);
11990     cpl_matrix_delete(kernel);
11991     
11992     /*
11993      *  Loop on images pixels, searching for cosmic rays candidates.
11994      *  Border pixels are currently excluded (they cannot contain
11995      *  candidates), to avoid that the search for groups of contiguous
11996      *  pixels would ever go out of image boundaries. In future we may
11997      *  overcome this limit, adding an appropriate check when contiguous
11998      *  pixels are searched.
11999      */
12000 
12001     sdata = cpl_image_get_data(smoothImage);
12002 
12003     for (j = 1; j < yLen - 1; j++) {
12004         for (i = 1; i < xLen - 1; i++) {
12005             value = idata[i + j * xLen];
12006             smoothValue = sdata[i + j * xLen];
12007             if (smoothValue < 1.0)
12008                 smoothValue = 1.0;
12009             sigma = sqrt(noise * noise + smoothValue / gain);
12010             if (value - smoothValue >= threshold * sigma) 
12011                 cosmic[i + j * xLen] = -1;
12012         }
12013     }
12014 
12015     cpl_image_delete(smoothImage);
12016 
12017 
12018     /*
12019      *  Search for groups of contiguous cosmic rays candidates.
12020      */
12021 
12022     do {
12023         found = 0;
12024         for (pos = first; pos < nPix; pos++) {
12025             if (cosmic[pos] == -1) {
12026                 cosmic[pos] = 2;         /*  Candidate found.  */
12027                 i = pos % xLen;          /*  Its coordinates.  */
12028                 j = pos / xLen;
12029                 first = pos;
12030                 first++;      /* ???  really necessary? */
12031                 found = 1;
12032                 break;
12033             }
12034         }
12035 
12036         if (found) {
12037 
12038             /*
12039              *  Determine new group of contiguous cosmic rays candidates.
12040              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12041              *  and the value of the max pixel and its position, fMax, iPosMax,
12042              *  jPosMax.
12043              */
12044 
12045             iMin = iMax = iPosMax = i;
12046             jMin = jMax = jPosMax = j;
12047             fMax = idata[i + j * xLen];
12048 
12049             do {
12050                 foundContiguousCandidate = 0;
12051                 for (l = 0; l <= 1; l++) {
12052                     for (k = 0; k <= 1; k++) {
12053 
12054                         /*
12055                          *  Looping on 4 pixels to North, East, South and West
12056                          */
12057 
12058                         ii = i + k - l;
12059                         jj = j + k + l - 1;
12060                         if (cosmic[ii + jj * xLen] == -1) {
12061                             foundContiguousCandidate = 1;
12062                             cosmic[ii + jj * xLen] = 2;
12063                                         /* Candidate belongs to current group */
12064                             iii = ii;   /* Keep its position */
12065                             jjj = jj;
12066 
12067                             /*
12068                              * Upgrade search box
12069                              */
12070 
12071                             if (ii < iMin)
12072                                 iMin = ii;
12073                             if (ii > iMax)
12074                                 iMax = ii;
12075                             if (jj < jMin)
12076                                 jMin = jj;
12077                             if (jj > jMax)
12078                                 jMax = jj;
12079 
12080                             if (idata[ii + jj * xLen] > fMax) {
12081                                 fMax = idata[ii + jj * xLen];
12082                                 iPosMax = ii;
12083                                 jPosMax = jj;
12084                             }
12085                         }
12086                     }
12087                 }
12088 
12089                 /*
12090                  *  We are done exploring the "cross". Now mark as "examined"
12091                  *  the current candidate (at the center of the cross):
12092                  */
12093 
12094                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12095 
12096                 if (foundContiguousCandidate) {
12097 
12098                     /*
12099                      * Pass (arbitrarily) the coordinates of the LAST found 
12100                      * candidate
12101                      */
12102 
12103                     i = iii;
12104                     j = jjj;
12105 
12106                     /* 
12107                      * Skip the rest, continue loop on new candidate 
12108                      */
12109 
12110                     continue; 
12111                 }
12112 
12113 
12114                 /*
12115                  *  Look for leftovers in the (growing!) search box
12116                  */
12117 
12118                 for (l = jMin; l <= jMax; l++) {
12119                     for (k = iMin; k <= iMax; k++) {
12120                         if (cosmic[k + l * xLen] == 2) {
12121                             i = k;
12122                             j = l;
12123                             foundContiguousCandidate = 1;
12124                             break;
12125                         }
12126                     }
12127                     if (foundContiguousCandidate) 
12128                         break;
12129                 }
12130             } while (foundContiguousCandidate);
12131 
12132 
12133             /*
12134              *  No more contiguous candidates are found. Decide now
12135              *  whether the current group is a cosmic ray or not.
12136              */
12137 
12138             sum = 0.;                /* Sum of 8 pixels around max position */
12139             for (l = -1; l <= 1; l++) {
12140                 for (k = -1; k <= 1; k++) {
12141                     if (l != 0 || k != 0) {
12142                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12143                     }
12144                 }
12145             }
12146 
12147             sum /= 8.;
12148             if (fMax > ratio * sum) {
12149                 for (l = jMin - 1; l <= jMax + 1; l++) {
12150                     for (k = iMin - 1; k <= iMax + 1; k++) {
12151                         if (cosmic[k + l * xLen] == 3) {
12152                             cosmic[k + l * xLen] = 1;
12153                             numCosmic++;
12154                         }
12155                     }
12156                 }
12157             }
12158             else {
12159                 for (l = jMin - 1; l <= jMax + 1; l++) {
12160                     for (k = iMin - 1; k <= iMax + 1; k++) {
12161                         if (cosmic[k + l * xLen] != -1) {
12162                             if (cosmic[k + l * xLen] == 1) 
12163                                 numCosmic--;
12164                             cosmic[k + l * xLen] = 0;
12165                         }
12166                     }
12167                 }
12168             }
12169         }
12170     } while (found);
12171 
12172 
12173     /*
12174      *  Prepare table containing cosmic rays coordinates. 
12175      */
12176 
12177     table = cpl_table_new(numCosmic);
12178     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12179     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12180     cpl_table_set_column_unit(table, "x", "pixel");
12181     cpl_table_set_column_unit(table, "y", "pixel");
12182     xdata = cpl_table_get_data_int(table, "x");
12183     ydata = cpl_table_get_data_int(table, "y");
12184 
12185     for (pos = 0, i = 0; pos < nPix; pos++) {
12186         if (cosmic[pos] == 1) {
12187             xdata[i] = (pos % xLen);
12188             ydata[i] = (pos / xLen);
12189             i++;
12190         }
12191     }
12192 
12193     mos_clean_bad_pixels(image, table, 1);
12194 
12195     cpl_free(cosmic);
12196     cpl_table_delete(table);
12197 
12198     return CPL_ERROR_NONE;
12199 
12200 }
12201 
12202 
12203 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12204                                     int spectral)
12205 {
12206     const char *func = "mos_clean_cosmics";
12207  
12208     float       *idata;
12209     int         *isBadPix;
12210     int          i, j, k, d;
12211     int          xlen, ylen, totPix;
12212     int          nBadPixels = 0;
12213     int          sign, foundFirst;
12214     int         *xValue = NULL;
12215     int         *yValue = NULL;
12216     float        save = 0.;
12217     double       sumd;
12218     int          cx, cy;
12219     int          nPairs;
12220     float        estimate[4];
12221     int          sx[] = {0, 1, 1, 1};
12222     int          sy[] = {1,-1, 0, 1};
12223     int          searchHorizon = 100;
12224     int          percent = 15;
12225 
12226 
12227     if (image == NULL || table == NULL)
12228         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12229 
12230     if (1 != cpl_table_has_column(table, "x"))
12231         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12232 
12233     if (1 != cpl_table_has_column(table, "y"))
12234         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12235 
12236     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
12237         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12238 
12239     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
12240         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12241 
12242     nBadPixels = cpl_table_get_nrow(table);
12243 
12244     if (nBadPixels) {
12245         xlen = cpl_image_get_size_x(image);
12246         ylen = cpl_image_get_size_y(image);
12247         idata = cpl_image_get_data(image);
12248         totPix = xlen * ylen;
12249         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
12250             isBadPix = cpl_calloc(totPix, sizeof(int));
12251         }
12252         else {
12253             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
12254                             "skip bad pixel correction", percent);
12255             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12256         }
12257     }
12258     else {
12259         cpl_msg_debug(func, "No pixel values to interpolate");
12260         return CPL_ERROR_NONE;
12261     }
12262 
12263     xValue = cpl_table_get_data_int(table, "x");
12264     yValue = cpl_table_get_data_int(table, "y");
12265 
12266     for (i = 0; i < nBadPixels; i++)
12267         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
12268 
12269     for (i = 0; i < nBadPixels; i++) {
12270 
12271         /*
12272          *  Search for the closest good pixel along the 4 fundamental 
12273          *  directions (in both senses):
12274          *                            \ | /
12275          *                             \|/
12276          *                           --- ---
12277          *                             /|\
12278          *                            / | \
12279          *
12280          *  Then collect pairs of values to interpolate linearly.
12281          */
12282 
12283         nPairs = 0;
12284         for (j = 0; j < 4; j++) {
12285 
12286             if (spectral) /* Just horizontal interpolation for spectral data */
12287                 if (j != 2)
12288                     continue;
12289 
12290             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
12291             sumd = 0.;
12292             foundFirst = 0;
12293             for (k = 0; k < 2; k++) {
12294                 sign = 2 * k - 1;
12295                 d = 0;
12296                 cx = xValue[i];
12297                 cy = yValue[i];
12298                 do {
12299                     cx += sign * sx[j];
12300                     cy += sign * sy[j];
12301                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
12302                         break;
12303                     d++;
12304                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
12305 
12306                 if (cx >= 0 && cx < xlen && 
12307                     cy >= 0 && cy < ylen && d < searchHorizon) {
12308 
12309                     /*
12310                      *  In this block is cripted the linear interpolation...
12311                      */
12312 
12313                     save = idata[cx + cy * xlen];
12314                     estimate[nPairs] += save / d;
12315                     sumd += 1. / (double) d;
12316                     if (k) {
12317                         estimate[nPairs] /= sumd;
12318                         nPairs++;
12319                     }
12320                     else {
12321                         foundFirst = 1;
12322                     }
12323                 }
12324                 else {
12325 
12326                     /*
12327                      * Image borders were crossed, incomplete pair of values
12328                      */
12329 
12330                     if (k) {
12331                         if (foundFirst) {
12332                             estimate[nPairs] = save;
12333                             nPairs++;
12334                         }
12335                     }
12336                 }
12337             }
12338         }
12339 
12340         /*
12341          * Replace pixel value of the input image, corresponding to
12342          * the current bad pixel, with the median of the estimates
12343          * resulted from the 4 linear interpolations.
12344          */
12345 
12346         if (nPairs > 2) {
12347             idata[xValue[i] + yValue[i] * xlen] = 
12348                                cpl_tools_get_median_float(estimate, nPairs);
12349         }
12350         else if (nPairs == 2) {
12351             idata[xValue[i] + yValue[i] * xlen] =
12352                                             (estimate[0] + estimate[1]) / 2.;
12353         }
12354         else if (nPairs == 1) {
12355             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
12356         }
12357         else {
12358             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
12359                           xValue[i], yValue[i]);
12360         }
12361     }
12362 
12363     cpl_free(isBadPix);
12364 
12365     return CPL_ERROR_NONE;
12366 }
12367 
12368 
12398 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
12399                            cpl_table *polytraces, double reference,
12400                            double blue, double red, double dispersion)
12401 {
12402     const char *func = "mos_spatial_map";
12403 
12404     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
12405                                                  /* Max order is 5 */
12406     cpl_polynomial *polytop;
12407     cpl_polynomial *polybot;
12408     cpl_image      *calibration;
12409     float          *data;
12410     double          top, bot;
12411     double          coeff;
12412     double          ytop, ybot;
12413     double          ypos, yfra;
12414     double          factor;
12415     int             yint, yprev;
12416     int             nslits;
12417     int             npseudo;
12418     int            *slit_id;
12419     int            *length;
12420     int             nx, ny;
12421     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
12422     int             missing_top, missing_bot;
12423     int             null;
12424     int             order;
12425     int             i, j, k;
12426 
12427 
12428     if (spectra == NULL || slits == NULL || polytraces == NULL) {
12429         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12430         return NULL;
12431     }
12432 
12433     if (dispersion <= 0.0) {
12434         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12435         return NULL;
12436     }
12437 
12438     if (red - blue < dispersion) {
12439         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12440         return NULL;
12441     }
12442 
12443     nx = cpl_image_get_size_x(spectra);
12444     ny = cpl_image_get_size_y(spectra);
12445 
12446     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12447     data = cpl_image_get_data(calibration);
12448 
12449     length  = cpl_table_get_data_int(slits, "length");
12450     nslits  = cpl_table_get_nrow(slits);
12451     slit_id = cpl_table_get_data_int(slits, "slit_id");
12452     order   = cpl_table_get_ncol(polytraces) - 2;
12453 
12454     /*
12455      * The spatial resampling is performed for a certain number of 
12456      * pixels above and below the position of the reference wavelength:
12457      */
12458 
12459     pixel_above = (red - reference) / dispersion;
12460     pixel_below = (reference - blue) / dispersion;
12461 
12462     for (i = 0; i < nslits; i++) {
12463         
12464         if (length[i] == 0)
12465             continue;
12466 
12467         /*
12468          * Note that the x coordinate of the reference pixels on the CCD
12469          * is taken arbitrarily at the top end of each slit. This wouldn't
12470          * be entirely correct in case of curved slits, or in presence of
12471          * heavy distortions: in such cases the spatial resampling is
12472          * really performed across a wide range of wavelengths. But
12473          * the lag between top and bottom spectral curvature models 
12474          * would introduce even in such cases negligible effects on
12475          * the spectral spatial resampling.
12476          */
12477 
12478         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
12479 
12480         start_pixel = refpixel - pixel_below;
12481         if (start_pixel < 0)
12482             start_pixel = 0;
12483 
12484         end_pixel = refpixel + pixel_above;
12485         if (end_pixel > nx)
12486             end_pixel = nx;
12487 
12488         /*
12489          * Recover from the table of spectral curvature coefficients
12490          * the curvature polynomials.
12491          */
12492 
12493         missing_top = 0;
12494         polytop = cpl_polynomial_new(1);
12495         for (k = 0; k <= order; k++) {
12496             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
12497             if (null) {
12498                 cpl_polynomial_delete(polytop);
12499                 missing_top = 1;
12500                 break;
12501             }
12502             cpl_polynomial_set_coeff(polytop, &k, coeff);
12503         }
12504 
12505         missing_bot = 0;
12506         polybot = cpl_polynomial_new(1);
12507         for (k = 0; k <= order; k++) {
12508             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
12509             if (null) {
12510                 cpl_polynomial_delete(polybot);
12511                 missing_bot = 1;
12512                 break;
12513             }
12514             cpl_polynomial_set_coeff(polybot, &k, coeff);
12515         }
12516 
12517         if (missing_top && missing_bot) {
12518             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
12519                             slit_id[i]);
12520             continue;
12521         }
12522 
12523         /*
12524          * In case just one of the two edges was not traced, the other
12525          * edge curvature model is duplicated and shifted to the other
12526          * end of the slit: better than nothing!
12527          */
12528 
12529         if (missing_top) {
12530             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
12531                             "the spectral curvature of the lower edge "
12532                             "is used instead.", slit_id[i]);
12533             polytop = cpl_polynomial_duplicate(polybot);
12534             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12535             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12536             k = 0;
12537             coeff = cpl_polynomial_get_coeff(polybot, &k);
12538             coeff += ytop - ybot;
12539             cpl_polynomial_set_coeff(polytop, &k, coeff);
12540         }
12541 
12542         if (missing_bot) {
12543             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
12544                             "the spectral curvature of the upper edge "
12545                             "is used instead.", slit_id[i]);
12546             polybot = cpl_polynomial_duplicate(polytop);
12547             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12548             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12549             k = 0;
12550             coeff = cpl_polynomial_get_coeff(polytop, &k);
12551             coeff -= ytop - ybot;
12552             cpl_polynomial_set_coeff(polybot, &k, coeff);
12553         }
12554 
12555         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
12556         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
12557         npseudo = ceil(top-bot) + 1;
12558 
12559         if (npseudo < 1) {
12560             cpl_polynomial_delete(polytop);
12561             cpl_polynomial_delete(polybot);
12562             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
12563                             slit_id[i]);
12564             continue;
12565         }
12566 
12567         for (j = start_pixel; j < end_pixel; j++) {
12568             top = cpl_polynomial_eval_1d(polytop, j, NULL);
12569             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
12570             factor = (top-bot)/npseudo;
12571             for (k = 0; k <= npseudo; k++) {
12572                 ypos = top - k*factor;
12573                 yint = ypos;
12574                 yfra = ypos - yint;
12575                 if (yint >= 0 && yint < ny-1) {
12576                     data[j + nx*yint] = (top-yint)/factor;
12577                     if (k) {
12578 
12579                         /*
12580                          * This is added to recover lost pixels on
12581                          * the CCD image (pixels are lost because
12582                          * the CCD pixels are less than npseudo+1).
12583                          */
12584 
12585                         if (yprev - yint > 1) {
12586                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
12587                         }
12588                     }
12589                 }
12590                 yprev = yint;
12591             }
12592         }
12593         cpl_polynomial_delete(polytop);
12594         cpl_polynomial_delete(polybot);
12595     }
12596 
12597     return calibration;
12598 }
12599 
12600 
12663 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
12664                               int maxradius, int conradius)
12665 {
12666     const char *func = "mos_detect_objects";
12667 
12668     cpl_image  *profile;
12669     float      *pdata;
12670     float      *p;
12671 
12672     char        name[MAX_COLNAME];
12673 
12674     int         nslits;
12675     int         npeaks;
12676     int         nobjects, objpos, totobj;
12677     int         maxobjects;
12678     int        *position;
12679     int        *length;
12680     int        *reject;
12681     double     *place;
12682     double     *bright;
12683     double      mindistance;
12684     int         pos, count;
12685     int         up;
12686     int         low, hig;
12687     int         row;
12688     int         i, j, k;
12689 
12690     const int   min_pixels = 10;
12691 
12692     
12693     if (cpl_error_get_code() != CPL_ERROR_NONE)
12694         return NULL;
12695  
12696     if (image == NULL || slits == NULL) { 
12697         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12698         return NULL;
12699     }
12700 
12701     if (margin < 0)
12702         margin = 0;
12703 
12704     if (maxradius < 0) {
12705         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12706         return NULL;
12707     }
12708 
12709     if (conradius < 0) {
12710         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12711         return NULL;
12712     }
12713 
12714     nslits   = cpl_table_get_nrow(slits);
12715     position = cpl_table_get_data_int(slits, "position");
12716     length   = cpl_table_get_data_int(slits, "length");
12717 
12718     profile = cpl_image_collapse_create(image, 1);
12719     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
12720     pdata = cpl_image_get_data(profile);
12721 
12722     row = 1;
12723     maxobjects = 0;
12724     totobj = 0;
12725     for (i = 0; i < nslits; i++) {
12726 
12727         if (length[i] == 0)
12728             continue;
12729 
12730         pos = position[i] + margin;
12731         count = length[i] - 2*margin;
12732 
12733         if (count < min_pixels)
12734             continue;
12735 
12736         p = pdata + pos;
12737 
12738 
12739         /*
12740          * Count peaks candidates
12741          */
12742 
12743         npeaks = 0;
12744         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
12745             npeaks++;
12746         }
12747 
12748         up = 0;
12749         for (j = 0; j < count - 3; j++) {
12750             if (p[j] > 0) {
12751                 if (p[j+1] > p[j]) {
12752                     up++;
12753                 }
12754                 else {
12755                     if (up > 2) {
12756                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
12757                             if (p[j] > 5)
12758                                 npeaks++;
12759                         }
12760                     }
12761                     else if (up > 1) {
12762                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
12763                             if (p[j] > 5)
12764                                 npeaks++;
12765                         }
12766                     }
12767                     up = 0;
12768                 }
12769             }
12770             else {
12771                 up = 0;
12772             }
12773         }
12774 
12775         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
12776             && p[count-3] > p[count-4] && p[count-4] > 0) {
12777             npeaks++;
12778         }
12779 
12780         if (npeaks == 0)
12781             continue;
12782 
12783 
12784         /*
12785          * Get candidates parameters
12786          */
12787 
12788         reject = cpl_calloc(npeaks, sizeof(int));
12789         bright = cpl_calloc(npeaks, sizeof(double));
12790         place  = cpl_calloc(npeaks, sizeof(double));
12791 
12792         npeaks = 0;
12793         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
12794             bright[0] = p[0];
12795             place[0] = position[i] + margin;
12796             npeaks++;
12797         }
12798 
12799         up = 0;
12800         for (j = 0; j < count - 3; j++) {
12801             if (p[j] > 0) {
12802                 if (p[j+1] > p[j]) {
12803                     up++;
12804                 }
12805                 else {
12806                     if (up > 2) {
12807                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
12808                             if (p[j] > 5) {
12809                                 bright[npeaks] = p[j];
12810                                 place[npeaks] = position[i] + margin + j + 1
12811                                        + values_to_dx(p[j-1], p[j], p[j+1]);
12812                                 npeaks++;
12813                             }
12814                         }
12815                     }
12816                     else if (up > 1) {
12817                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
12818                             if (p[j] > 5) {
12819                                 bright[npeaks] = p[j];
12820                                 place[npeaks] = position[i] + margin + j + 1
12821                                        + values_to_dx(p[j-1], p[j], p[j+1]);
12822                                 npeaks++;
12823                             }
12824                         }
12825                     }
12826                     up = 0;
12827                 }
12828             }
12829             else {
12830                 up = 0;
12831             }
12832         }
12833 
12834         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
12835             && p[count-3] > p[count-4] && p[count-4] > 0) {
12836             bright[npeaks] = p[count-1];
12837             place[npeaks] = position[i] + count;
12838             npeaks++;
12839         }
12840 
12841 
12842         /*
12843          * Now select the uncontaminated peaks
12844          */
12845 
12846         if (fabs(place[0] - pos) < 1.0)
12847             reject[0] = 1;
12848         if (fabs(place[npeaks-1] - pos - count) < 1.0)
12849             reject[npeaks-1] = 1;
12850         for (j = 0; j < npeaks; j++) {
12851             for (k = 0; k < npeaks; k++) {
12852                 if (k == j)
12853                     continue;
12854                 mindistance = conradius * bright[k] / bright[j] 
12855                                         * bright[k] / bright[j];
12856                 if (fabs(place[j] - place[k]) < mindistance)
12857                     reject[j] = 1;
12858             }
12859         }
12860 
12861 /* new part */
12862         for (j = 0; j < npeaks; j++) {
12863             if (reject[j])
12864                 continue;
12865             if (j) {
12866                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
12867                     / (bright[j-1] + bright[j]) + 1;
12868             }
12869             else {
12870                 low = pos;
12871             }
12872             if (j < npeaks - 1) {
12873                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
12874                     / (bright[j+1] + bright[j]) + 1;
12875             }
12876             else {
12877                 hig = pos + count;
12878             }
12879 
12880             if (low < pos)
12881                 low = pos;
12882             if (hig > pos + count)
12883                 hig = pos + count;
12884             if (place[j] - low > maxradius)
12885                 low = place[j] - maxradius;
12886             if (hig - place[j] > maxradius)
12887                 hig = place[j] + maxradius;
12888             if (hig == low)
12889                 reject[j] = 1;
12890         }
12891 /* end new part */
12892 
12893         nobjects = npeaks;
12894         for (j = 0; j < npeaks; j++)
12895             if (reject[j])
12896                 nobjects--;
12897 
12898         for (j = 0; j < nobjects; j++) {
12899             snprintf(name, MAX_COLNAME, "object_%d", j+1);
12900             if (cpl_table_has_column(slits, name))
12901                 continue;
12902             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
12903             snprintf(name, MAX_COLNAME, "start_%d", j+1);
12904             cpl_table_new_column(slits, name, CPL_TYPE_INT);
12905             cpl_table_set_column_unit(slits, name, "pixel");
12906             snprintf(name, MAX_COLNAME, "end_%d", j+1);
12907             cpl_table_new_column(slits, name, CPL_TYPE_INT);
12908             cpl_table_set_column_unit(slits, name, "pixel");
12909             snprintf(name, MAX_COLNAME, "row_%d", j+1);
12910             cpl_table_new_column(slits, name, CPL_TYPE_INT);
12911             cpl_table_set_column_unit(slits, name, "pixel");
12912         }
12913 
12914         objpos = nobjects;
12915         for (j = 0; j < npeaks; j++) {
12916             if (reject[j])
12917                 continue;
12918             if (j) {
12919                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
12920                     / (bright[j-1] + bright[j]) + 1;
12921             }
12922             else {
12923                 low = pos;
12924             }
12925             if (j < npeaks - 1) {
12926                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
12927                     / (bright[j+1] + bright[j]) + 1;
12928             }
12929             else {
12930                 hig = pos + count;
12931             }
12932 
12933             if (low < pos)
12934                 low = pos;
12935             if (hig > pos + count)
12936                 hig = pos + count;
12937             if (place[j] - low > maxradius)
12938                 low = place[j] - maxradius;
12939             if (hig - place[j] > maxradius)
12940                 hig = place[j] + maxradius;
12941 
12942             snprintf(name, MAX_COLNAME, "object_%d", objpos);
12943             cpl_table_set_double(slits, name, i, place[j]);
12944             snprintf(name, MAX_COLNAME, "start_%d", objpos);
12945             cpl_table_set_int(slits, name, i, low);
12946             snprintf(name, MAX_COLNAME, "end_%d", objpos);
12947             cpl_table_set_int(slits, name, i, hig);
12948             snprintf(name, MAX_COLNAME, "row_%d", objpos);
12949             cpl_table_set_int(slits, name, i, row + objpos - 1);
12950             totobj++;
12951             objpos--;
12952         }
12953 
12954         row += nobjects;
12955 
12956         if (maxobjects < nobjects)
12957             maxobjects = nobjects;
12958 
12959         cpl_free(reject);
12960         cpl_free(bright);
12961         cpl_free(place);
12962 
12963     }
12964 
12965 /*    nobjects = row - nobjects;     A bug, I think... */
12966     row = cpl_table_get_nrow(slits);
12967 
12968     for (i = 0; i < row; i++) {
12969         for (j = 0; j < maxobjects; j++) {
12970             snprintf(name, MAX_COLNAME, "row_%d", j+1);
12971             if (cpl_table_is_valid(slits, name, i))
12972                 cpl_table_set_int(slits, name, i, totobj -
12973                                   cpl_table_get_int(slits, name, i, NULL));
12974         }
12975     }
12976 
12977     for (i = 0; i < maxobjects; i++) {
12978         snprintf(name, MAX_COLNAME, "start_%d", i+1);
12979         cpl_table_fill_invalid_int(slits, name, -1);
12980         snprintf(name, MAX_COLNAME, "end_%d", i+1);
12981         cpl_table_fill_invalid_int(slits, name, -1);
12982         snprintf(name, MAX_COLNAME, "row_%d", i+1);
12983         cpl_table_fill_invalid_int(slits, name, -1);
12984     }
12985 
12986     return profile;
12987 }
12988 
12989 
13014 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13015                                 cpl_table *objects, int extraction, double ron,
13016                                 double gain, int ncombined)
13017 {
13018     const char *func = "mos_extract_objects";
13019 
13020     char        name[MAX_COLNAME];
13021 
13022     cpl_image **output;
13023     cpl_image  *extracted;
13024     cpl_image  *extr_sky;
13025     cpl_image  *error;
13026     cpl_image  *sciwin;
13027     cpl_image  *skywin;
13028     int         nslits;
13029     int         nobjects;
13030     int         maxobjects;
13031     int         nx, ny;
13032     int         ylow, yhig;
13033     int         i, j;
13034 
13035 
13036     if (science == NULL || sky == NULL) {
13037         cpl_msg_error(func, "Both scientific exposures are required in input");
13038         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13039         return NULL;
13040     }
13041 
13042     if (objects == NULL) {
13043         cpl_msg_error(func, "An object table is required in input");
13044         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13045         return NULL;
13046     }
13047 
13048     if (extraction < 0 || extraction > 1) {
13049         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13050                       "either 0 or 1", extraction); 
13051         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13052         return NULL;
13053     }
13054 
13055     if (ron < 0.0) {
13056         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13057         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13058         return NULL;
13059     }
13060 
13061     if (gain < 0.1) {
13062         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13063         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13064         return NULL;
13065     }
13066 
13067     if (ncombined < 1) {
13068         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13069                       "it should be at least 1", ncombined);
13070         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13071         return NULL;
13072     }
13073 
13074 
13075     /*
13076      * Count the max number of objects per slit. Note that maxobjects 
13077      * is intentionally the max number of objects increased by one.
13078      */
13079 
13080     maxobjects = 1;
13081     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13082     while (cpl_table_has_column(objects, name)) {
13083         maxobjects++;
13084         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13085     }
13086 
13087 
13088     /*
13089      * Count objects to extract
13090      */
13091 
13092     nobjects = 0;
13093     nslits = cpl_table_get_nrow(objects);
13094 
13095     for (i = 0; i < nslits; i++) {
13096         for (j = 1; j < maxobjects; j++) {
13097             snprintf(name, MAX_COLNAME, "object_%d", j);
13098             if (cpl_table_is_valid(objects, name, i))
13099                 nobjects++;
13100         }
13101     }
13102 
13103     if (nobjects == 0)
13104         return NULL;
13105 
13106     nx = cpl_image_get_size_x(science);
13107     ny = cpl_image_get_size_x(science);
13108 
13109     output = cpl_calloc(3, sizeof(cpl_image *));
13110     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13111     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13112     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13113 
13114 
13115     /*
13116      * Extract objects
13117      */
13118 
13119     nobjects = 0;
13120     for (i = 0; i < nslits; i++) {
13121         for (j = 1; j < maxobjects; j++) {
13122             snprintf(name, MAX_COLNAME, "object_%d", j);
13123             if (cpl_table_is_valid(objects, name, i)) {
13124                 snprintf(name, MAX_COLNAME, "start_%d", j);
13125                 ylow = cpl_table_get_int(objects, name, i, NULL);
13126                 snprintf(name, MAX_COLNAME, "end_%d", j);
13127                 yhig = cpl_table_get_int(objects, name, i, NULL);
13128                 snprintf(name, MAX_COLNAME, "row_%d", j);
13129                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13130                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13131                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13132 /*
13133  * Cleaning the cosmics locally was really NOT a good idea...
13134  * I leave it here, commented out, to never forget this mistake!
13135 
13136                 if (extraction) {
13137                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13138                 }
13139  */
13140                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13141                                nobjects, extraction, ron, gain, ncombined);
13142                 cpl_image_delete(sciwin);
13143                 cpl_image_delete(skywin);
13144                 nobjects++;
13145             }
13146         }
13147     }
13148 
13149     return output;
13150 
13151 }
13152 
13153 
13176 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13177                             double dispersion, int saturation, 
13178                             double *mfwhm, double *rmsfwhm,
13179                             double *resolution, double *rmsres, int *nlines)
13180 {
13181     cpl_vector *vector;
13182 
13183     int     i, j, n, m;
13184     int     position, maxpos;
13185     int     xlen, ylen;
13186     int     sp, ep;
13187     int     radius;
13188     int     sradius = 40;
13189     int     threshold = 250;    /* Peak must be so many ADUs above min */
13190 
13191     int     ifwhm;
13192     double  fwhm;
13193     double *buffer;
13194     double  min, max, halfmax;
13195     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
13196     double  value, rms;
13197 
13198     float  *data;
13199 
13200 
13201     *resolution = 0.0;
13202     *rmsres = 0.0;
13203     *nlines = 0;
13204 
13205     xlen = cpl_image_get_size_x(image);
13206     ylen = cpl_image_get_size_y(image);
13207     data = cpl_image_get_data(image);
13208 
13209     buffer = cpl_malloc(ylen * sizeof(double));
13210 
13211     /*
13212      *  Closest pixel to specified wavelength.
13213      */
13214 
13215     position = floor((lambda - startwave) / dispersion + 0.5);
13216 
13217     sp = position - sradius;
13218     ep = position + sradius;
13219 
13220     if (sp < 0 || ep > xlen) {
13221         cpl_free(buffer);
13222         return 0;
13223     }
13224 
13225     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
13226 
13227         /*
13228          *  Search interval for peak. Abort if too close to image border.
13229          */
13230 
13231         radius = mos_lines_width(data + i*xlen + position - sradius, 
13232                                  2*sradius + 1);
13233         if (radius < 5)
13234             radius = 5;
13235 
13236         sp = position - radius;
13237         ep = position + radius;
13238 
13239         if (sp < 0 || ep > xlen) {
13240             cpl_free(buffer);
13241             return 0;
13242         }
13243 
13244 
13245         /*
13246          *  Determine min-max value and position.
13247          */
13248 
13249         maxpos = sp;
13250         min = max = data[sp + i * xlen];
13251         for (j = sp; j < ep; j++) {
13252             if (data[j + i * xlen] > max) {
13253                 max = data[j + i * xlen];
13254                 maxpos = j;
13255             }
13256             if (data[j + i * xlen] < min) {
13257                 min = data[j + i * xlen];
13258             }
13259         }
13260 
13261         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
13262             continue;
13263 
13264         if (max - min < threshold)        /* Low signal... */
13265             continue;
13266 
13267         if (max > saturation)             /* Saturation */
13268             continue;
13269 
13270         /*
13271          *  Determine FWHM counting pixels with value greater than
13272          *  half of the max value, to the right and to the left of
13273          *  the max. Linear interpolation between the pixels where
13274          *  the transition happens.
13275          */
13276 
13277         halfmax = (max + min)/ 2.0;
13278 
13279         fwhm = 0.0;
13280         ifwhm = 0;
13281         for (j = maxpos; j < maxpos + radius; j++) {
13282             if (j < xlen) {
13283                 if (data[j + i * xlen] < halfmax) {
13284                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
13285                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
13286                     break;
13287                 }
13288                 ifwhm++;
13289             }
13290         }
13291 
13292         ifwhm = 0;
13293         for (j = maxpos; j > maxpos - radius; j--) {
13294             if (j >= 0) {
13295                 if (data[j + i * xlen] < halfmax) {
13296                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
13297                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
13298                     break;
13299                 }
13300                 ifwhm++;
13301             }
13302         }
13303 
13304         if (fwhm > 3.0) {
13305             buffer[n] = fwhm - 2.0;
13306             n++;
13307         }
13308 
13309     }
13310 
13311     if (n == 0) {
13312         cpl_free(buffer);
13313         return 0;
13314     }
13315 
13316     vector = cpl_vector_wrap(n, buffer);
13317     value = cpl_vector_get_median_const(vector);
13318     cpl_vector_unwrap(vector);
13319 
13320     rms = 0.0;
13321     for (i = 0, m = 0; i < n; i++) {
13322         if (fabs(buffer[i] - value) < cut) {
13323             rms += fabs(buffer[i] - value);
13324             m++;
13325         }
13326     }
13327 
13328     cpl_free(buffer);
13329 
13330     if (m < 3)
13331         return 0;
13332 
13333     rms /= m;
13334     rms *= 1.25;       /* Factor to convert average deviation to sigma */
13335 
13336     value *= dispersion;
13337     rms *= dispersion;
13338 
13339     *mfwhm = value;
13340     *rmsfwhm = rms;
13341 
13342     *resolution = lambda / value;
13343     *rmsres = *resolution * rms / value;
13344 
13345     *nlines = m;
13346 
13347     return 1;
13348 }
13349 
13350 
13372 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
13373                                 double dispersion, int saturation, 
13374                                 cpl_vector *lines)
13375 {
13376 
13377     cpl_table *table;
13378     double    *line;
13379     double     fwhm;
13380     double     rmsfwhm;
13381     double     resolution;
13382     double     rmsres;
13383     int        nref;
13384     int        nlines;
13385     int        i;
13386 
13387 
13388     nref = cpl_vector_get_size(lines);
13389     line = cpl_vector_get_data(lines);
13390 
13391     table = cpl_table_new(nref);
13392     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
13393     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
13394     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
13395     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
13396     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
13397     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
13398     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
13399     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
13400     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
13401 
13402     for (i = 0; i < nref; i++) {
13403         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
13404                                     saturation, &fwhm, &rmsfwhm, 
13405                                     &resolution, &rmsres, &nlines)) {
13406             cpl_table_set_double(table, "wavelength", i, line[i]);
13407             cpl_table_set_double(table, "fwhm", i, fwhm);
13408             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
13409             cpl_table_set_double(table, "resolution", i, resolution);
13410             cpl_table_set_double(table, "resolution_rms", i, rmsres);
13411             cpl_table_set_int(table, "nlines", i, nlines);
13412         }
13413         else
13414             cpl_table_set_int(table, "nlines", i, 0);
13415     }
13416 
13417     if (cpl_table_has_valid(table, "wavelength"))
13418         return table;
13419 
13420     cpl_table_delete(table);
13421 
13422     return NULL;
13423     
13424 }
13425 
13426 
13444 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
13445                             int ystart, int yend, double wstart, double wend)
13446 {
13447     const char *func = "mos_integrate_signal";
13448 
13449     double sum;
13450     float *sdata;
13451     float *wdata;
13452     int    nx, ny;
13453     int    x, y;
13454     
13455 
13456     if (image == NULL || wavemap == NULL) { 
13457         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13458         return 0.0;
13459     }
13460 
13461     if (ystart > yend || wstart >= wend) {
13462         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13463         return 0.0;
13464     }
13465 
13466     nx = cpl_image_get_size_x(image);
13467     ny = cpl_image_get_size_y(image);
13468 
13469     if (!(nx == cpl_image_get_size_x(wavemap) 
13470         && ny == cpl_image_get_size_y(wavemap))) {
13471         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
13472         return 0.0;
13473     }
13474 
13475     if (ystart < 0 || yend > ny) {
13476         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
13477         return 0.0;
13478     }
13479 
13480     sdata = cpl_image_get_data(image);
13481     wdata = cpl_image_get_data(wavemap);
13482 
13483     sdata += ystart*nx;
13484     wdata += ystart*nx;
13485 
13486     sum = 0.0;
13487     for (y = ystart; y < yend; y++) {
13488         for (x = 0; x < nx; x++) {
13489             if (wdata[x] < wstart || wdata[x] > wend)
13490                 continue;
13491             sum += sdata[x];
13492         }
13493         sdata += nx;
13494         wdata += nx;
13495     }
13496 
13497     return sum;
13498 
13499 }
13500 
13501 /****************************************************************************
13502  * From this point on, the instrument dependent functions are added:
13503  * they are functions that retrieve information that is stored in
13504  * the data headers in some instrument specific way, such as the
13505  * location of overscans, the slits positions on the telescope
13506  * focal plane, the gain factor, etc.
13507  */
13508 
13509 
13532 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
13533 {
13534     const char *func = "mos_load_slits_fors_mxu";
13535 
13536     cpl_table  *slits;
13537     char        keyname[MAX_COLNAME];
13538     const char *instrume;
13539     const char *target_name;
13540     float       slit_x;
13541     float       slit_y;
13542     float       length;
13543 /*    double      arc2mm = 0.53316;         */
13544     double      arc2mm = 0.528;
13545     int         nslits;
13546     int         slit_id;
13547     int         fors;
13548     int         chip;
13549     int         found;
13550 
13551     /*
13552      * The limits below are used to exclude from the loaded slit list
13553      * any slit that surely doesn't belong to the used chip. This is
13554      * a way to reduce the chance of ambiguous slit identification.
13555      */
13556 
13557     float      low_limit1 = 10.0;
13558     float      hig_limit2 = 30.0;
13559 
13560 
13561     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13562         return NULL;
13563     }
13564 
13565     if (header == NULL) {
13566         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13567         return NULL;
13568     }
13569 
13570 
13571     /*
13572      * See if this is FORS1 or FORS2;
13573      */
13574 
13575     instrume = cpl_propertylist_get_string(header, "INSTRUME");
13576 
13577     fors = 0;
13578     if (instrume[4] == '1')
13579         fors = 1;
13580     if (instrume[4] == '2')
13581         fors = 2;
13582 
13583     if (fors != 2) {
13584         cpl_msg_error(func, "Wrong instrument: %s\n"
13585                       "FORS2 is expected for MXU data", instrume);
13586         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13587         return NULL;
13588     }
13589 
13590 
13591     /*
13592      * The master and slave chips can be identified by their positions
13593      * in the chip array in the case of FORS2 data (with fors1 the chip
13594      * is always 1). chip = 2 is the master, chip = 1 is the slave.
13595      */
13596 
13597     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
13598 
13599     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13600         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
13601                       "in FITS header");
13602         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13603         return NULL;
13604     }
13605 
13606     if (chip != 1 && chip != 2) {
13607         cpl_msg_error(func, "Unexpected chip position in keyword "
13608                       "ESO DET CHIP1 Y: %d", chip);
13609         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13610         return NULL;
13611     }
13612 
13613 
13614     /*
13615      * Count slits in header (excluding reference slits, and the slits
13616      * that _surely_ belong to the other chip)
13617      */
13618 
13619     nslits = 0;
13620     slit_id = 0;
13621     found = 1;
13622 
13623     while (found) {
13624         slit_id++;
13625         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
13626         if (cpl_propertylist_has(header, keyname)) {
13627             slit_y = cpl_propertylist_get_double(header, keyname);
13628 
13629             if (chip == 1)
13630                 if (slit_y < low_limit1)
13631                     continue;
13632             if (chip == 2)
13633                 if (slit_y > hig_limit2)
13634                     continue;
13635                 
13636             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
13637                      slit_id + 100);
13638             if (cpl_propertylist_has(header, keyname)) {
13639                 target_name = cpl_propertylist_get_string(header, keyname);
13640                 if (strncmp(target_name, "refslit", 7))
13641                     nslits++;
13642             }
13643             else
13644                 nslits++;
13645         }
13646         else
13647             found = 0;
13648     }
13649 
13650     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13651         cpl_msg_error(func, "%s while loading slits coordinates from "
13652                       "FITS header", cpl_error_get_message());
13653         cpl_error_set_where(func);
13654         return NULL;
13655     }
13656 
13657     if (nslits == 0)  {
13658         cpl_msg_error(func, "No slits coordinates found in header");
13659         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13660         return NULL;
13661     }
13662 
13663     slits = cpl_table_new(nslits);
13664     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
13665     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
13666     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
13667     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
13668     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
13669     cpl_table_set_column_unit(slits, "xtop",    "pixel");
13670     cpl_table_set_column_unit(slits, "ytop",    "pixel");
13671     cpl_table_set_column_unit(slits, "xbottom", "pixel");
13672     cpl_table_set_column_unit(slits, "ybottom", "pixel");
13673 
13674     nslits = 0;
13675     slit_id = 0; 
13676     found = 1;
13677     while (found) {
13678         slit_id++;
13679         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
13680         if (cpl_propertylist_has(header, keyname)) {
13681             slit_y = cpl_propertylist_get_double(header, keyname);
13682 
13683             if (chip == 1) 
13684                 if (slit_y < low_limit1)
13685                     continue;
13686             if (chip == 2)
13687                 if (slit_y > hig_limit2)
13688                     continue;
13689 
13690             /*
13691              * Y-flip the slit position, to match CCD pixel coordinate
13692              * convention
13693              */
13694 
13695             slit_y = -slit_y;
13696 
13697             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
13698             slit_x = cpl_propertylist_get_double(header, keyname);
13699             if (cpl_error_get_code() != CPL_ERROR_NONE) {
13700                 cpl_table_delete(slits);
13701                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
13702                               keyname);
13703                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13704                 return NULL;
13705             }
13706 
13707             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
13708             length = cpl_propertylist_get_double(header, keyname);
13709             if (cpl_error_get_code() != CPL_ERROR_NONE) {
13710                 cpl_table_delete(slits);
13711                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
13712                               keyname);
13713                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13714                 return NULL;
13715             }
13716 
13717             length *= arc2mm;
13718 
13719             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
13720                      slit_id + 100);
13721             if (cpl_propertylist_has(header, keyname)) {
13722                 target_name = cpl_propertylist_get_string(header, keyname);
13723                 if (strncmp(target_name, "refslit", 7)) {
13724                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
13725                     cpl_table_set(slits, "xtop", nslits, slit_x);
13726                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
13727                     cpl_table_set(slits, "xbottom", nslits, slit_x);
13728                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
13729                     nslits++;
13730                 }
13731             }
13732             else {
13733                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
13734                 cpl_table_set(slits, "xtop", nslits, slit_x);
13735                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
13736                 cpl_table_set(slits, "xbottom", nslits, slit_x);
13737                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
13738                 nslits++;
13739             }
13740         }
13741         else
13742             found = 0;
13743     }
13744 
13745     return slits;
13746 }
13747 
13748 
13771 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header)
13772 {
13773     const char *func = "mos_load_slits_fors_mos";
13774 
13775     cpl_table  *slits;
13776     char        keyname[MAX_COLNAME];
13777     const char *instrume;
13778     const char *chipname;
13779     float       slit_x;
13780     int         first_slit, last_slit;
13781     int         nslits;
13782     int         slit_id;
13783     int         fors;
13784     int         chip;
13785     int         fors_is_old;
13786 
13787     /*
13788      * The Y coordinates of the slits are fixed
13789      */
13790 
13791     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
13792                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
13793                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
13794                                -102.1 };
13795     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
13796                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
13797                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
13798                                -113.9 };
13799 
13800 
13801     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13802         return NULL;
13803     }
13804 
13805     if (header == NULL) {
13806         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13807         return NULL;
13808     }
13809 
13810 
13811     /*
13812      * See if this is FORS1 or FORS2;
13813      */
13814 
13815     instrume = cpl_propertylist_get_string(header, "INSTRUME");
13816 
13817     fors = 0;
13818     if (instrume[4] == '1')
13819         fors = 1;
13820     if (instrume[4] == '2')
13821         fors = 2;
13822 
13823     if (fors == 0) {
13824         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
13825                       instrume);
13826         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13827         return NULL;
13828     }
13829 
13830     /* FIXME:
13831      * This is the way FORS1 data belong to the upgraded chips,
13832      * named "Marlene" and "Norma III". It's a quick solution,
13833      * there are hardcoded values here!!!
13834      */
13835 
13836     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
13837 
13838     if (chipname[0] == 'M' || chipname[0] == 'N')
13839         fors_is_old = 0;
13840     else
13841         fors_is_old = 1;
13842 
13843     if (fors == 1 && fors_is_old) {
13844         first_slit = 1;
13845         last_slit = 19;
13846     }
13847     else {
13848 
13849         /*
13850          * The master and slave chips can be identified by their positions
13851          * in the chip array in the case of FORS2 data: chip = 2 is the 
13852          * master, chip = 1 is the slave.
13853          */
13854 
13855         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
13856 
13857         if (cpl_error_get_code() != CPL_ERROR_NONE) {
13858             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
13859                           "in FITS header");
13860             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13861             return NULL;
13862         }
13863 
13864         if (chip != 1 && chip != 2) {
13865             cpl_msg_error(func, "Unexpected chip position in keyword "
13866                           "ESO DET CHIP1 Y: %d", chip);
13867             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13868             return NULL;
13869         }
13870 
13871         if (chip == 1) {
13872             first_slit = 12;
13873             last_slit = 19;
13874         }
13875         else {
13876             first_slit = 1;
13877             last_slit = 11;
13878         }
13879     }
13880 
13881 
13882     /*
13883      * Count slits in header (excluding closed slits - i.e. those with
13884      * offsets greater than 115 mm - and the slits that do not belong 
13885      * to this chip)
13886      */
13887 
13888     nslits = 0;
13889     slit_id = 0;
13890     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
13891         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
13892         if (cpl_propertylist_has(header, keyname)) {
13893             slit_x = cpl_propertylist_get_double(header, keyname);
13894             if (fabs(slit_x) < 115.0)
13895                 nslits++;
13896         }
13897         else {
13898             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
13899             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13900             return NULL;
13901         }
13902     }
13903 
13904     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13905         cpl_msg_error(func, "%s while loading slits coordinates from "
13906                       "FITS header", cpl_error_get_message());
13907         cpl_error_set_where(func);
13908         return NULL;
13909     }
13910 
13911     if (nslits == 0)  {
13912         cpl_msg_error(func, "No slits coordinates found in header");
13913         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
13914         return NULL;
13915     }
13916 
13917     slits = cpl_table_new(nslits);
13918     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
13919     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
13920     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
13921     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
13922     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
13923     cpl_table_set_column_unit(slits, "xtop",    "pixel");
13924     cpl_table_set_column_unit(slits, "ytop",    "pixel");
13925     cpl_table_set_column_unit(slits, "xbottom", "pixel");
13926     cpl_table_set_column_unit(slits, "ybottom", "pixel");
13927 
13928     nslits = 0;
13929     slit_id = 0;
13930     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
13931         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
13932         slit_x = cpl_propertylist_get_double(header, keyname);
13933         if (fabs(slit_x) < 115.0) {
13934             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
13935             cpl_table_set(slits, "xtop", nslits, slit_x);
13936             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
13937             cpl_table_set(slits, "xbottom", nslits, slit_x);
13938             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
13939             nslits++;
13940         }
13941     }
13942 
13943     return slits;
13944 }
13945 
13946 
13970 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
13971 {
13972     const char *func = "mos_load_slits_fors_lss";
13973 
13974     cpl_table  *slits;
13975     char       *slit_name;
13976     const char *instrume;
13977     int         fors;
13978     int         chip;
13979     float       ytop;
13980     float       ybottom;
13981 
13982     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13983         return NULL;
13984     }
13985 
13986     if (header == NULL) {
13987         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13988         return NULL;
13989     }
13990 
13991 
13992     /*
13993      * See if this is FORS1 or FORS2;
13994      */
13995 
13996     instrume = cpl_propertylist_get_string(header, "INSTRUME");
13997 
13998     fors = 0;
13999     if (instrume[4] == '1')
14000         fors = 1;
14001     if (instrume[4] == '2')
14002         fors = 2;
14003 
14004     if (fors == 0) {
14005         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14006                       instrume);
14007         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14008         return NULL;
14009     }
14010 
14011     if (fors == 1) {
14012         ytop = 109.94;
14013         ybottom = -109.94;
14014     }
14015     else {
14016 
14017         /*
14018          * The master and slave chips can be identified by their positions
14019          * in the chip array in the case of FORS2 data: chip = 2 is the 
14020          * master, chip = 1 is the slave.
14021          */
14022 
14023         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14024 
14025         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14026             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14027                           "in FITS header");
14028             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14029             return NULL;
14030         }
14031 
14032         if (chip != 1 && chip != 2) {
14033             cpl_msg_error(func, "Unexpected chip position in keyword "
14034                           "ESO DET CHIP1 Y: %d", chip);
14035             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14036             return NULL;
14037         }
14038 
14039         if (chip == 1) {
14040             ytop = 30.0;
14041             ybottom = -109.94;
14042         }
14043         else {
14044             ytop = 109.94;
14045             ybottom = -20.0;
14046         }
14047     }
14048 
14049 
14050     slits = cpl_table_new(1);
14051     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14052     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14053     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14054     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14055     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14056     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14057     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14058     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14059     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14060 
14061     slit_name = (char *)cpl_propertylist_get_string(header, 
14062                                                     "ESO INS SLIT NAME");
14063 
14064     cpl_table_set(slits, "ytop", 0, ytop);
14065     cpl_table_set(slits, "ybottom", 0, ybottom);
14066 
14067     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14068         cpl_table_set_int(slits, "slit_id", 0, 1);
14069         cpl_table_set(slits, "xbottom", 0, -0.075);
14070         cpl_table_set(slits, "xtop", 0, 0.075);
14071     }
14072     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14073         cpl_table_set_int(slits, "slit_id", 0, 2);
14074         cpl_table_set(slits, "xbottom", 0, 5.895);
14075         cpl_table_set(slits, "xtop", 0, 6.105);
14076     }
14077     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14078         cpl_table_set_int(slits, "slit_id", 0, 3);
14079         cpl_table_set(slits, "xbottom", 0, -6.135);
14080         cpl_table_set(slits, "xtop", 0, -5.865);
14081     }
14082     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14083         cpl_table_set_int(slits, "slit_id", 0, 4);
14084         cpl_table_set(slits, "xbottom", 0, 11.815);
14085         cpl_table_set(slits, "xtop", 0, 12.185);
14086     }
14087     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14088         cpl_table_set_int(slits, "slit_id", 0, 5);
14089         cpl_table_set(slits, "xbottom", 0, -12.265);
14090         cpl_table_set(slits, "xtop", 0, -11.735);
14091     }
14092     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14093         cpl_table_set_int(slits, "slit_id", 0, 6);
14094         cpl_table_set(slits, "xbottom", 0, 17.655);
14095         cpl_table_set(slits, "xtop", 0, 18.345);
14096     }
14097     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14098         cpl_table_set_int(slits, "slit_id", 0, 7);
14099         cpl_table_set(slits, "xbottom", 0, -18.425);
14100         cpl_table_set(slits, "xtop", 0, -17.575);
14101     }
14102     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14103         cpl_table_set_int(slits, "slit_id", 0, 8);
14104         cpl_table_set(slits, "xbottom", 0, 23.475);
14105         cpl_table_set(slits, "xtop", 0, 24.525);
14106     }
14107     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14108         cpl_table_set_int(slits, "slit_id", 0, 9);
14109         cpl_table_set(slits, "xbottom", 0, -24.66);
14110         cpl_table_set(slits, "xtop", 0, -23.34);
14111     }
14112     else {
14113         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14114                       slit_name);
14115         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14116         cpl_table_delete(slits);
14117         return NULL;
14118     }
14119 
14120     return slits;
14121 }
14122 
14123 
14138 double mos_get_gain_vimos(cpl_propertylist *header)
14139 {
14140     const char *func = "mos_get_gain_vimos";
14141 
14142     double gain = -1.0;
14143 
14144 
14145     if (cpl_error_get_code() != CPL_ERROR_NONE)
14146         return gain;
14147 
14148     if (header == NULL) {
14149         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14150         return gain;
14151     }
14152 
14153     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14154     if (cpl_error_get_code()) {
14155         cpl_error_set_where(func);
14156         gain = -1.0;
14157     }
14158 
14159     return gain;
14160 
14161 }
14162 
14163 
14183 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14184 {
14185     const char *func = "mos_load_slits_vimos";
14186 
14187     cpl_table *slits;
14188     char       keyname[MAX_COLNAME];
14189     float      slit_x;
14190     float      slit_y;
14191     float      dim_x;
14192     int        nslits;
14193     int        slit_id;
14194     int        i;
14195 
14196 
14197     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14198         return NULL;
14199     }
14200 
14201     if (header == NULL) {
14202         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14203         return NULL;
14204     }
14205 
14206     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
14207 
14208     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14209         cpl_error_set_where(func);
14210         return NULL;
14211     }
14212 
14213     slits = cpl_table_new(nslits);
14214     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14215     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14216     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14217     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14218     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14219     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14220     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14221     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14222     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14223 
14224     for (i = 0; i < nslits; i++) {
14225         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
14226         slit_id = cpl_propertylist_get_int(header, keyname);
14227         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14228             cpl_error_set_where(func);
14229             return NULL;
14230         }
14231         sprintf(keyname, "ESO INS SLIT%d X", i+1);
14232         slit_x = cpl_propertylist_get_double(header, keyname);
14233         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14234             cpl_error_set_where(func);
14235             return NULL;
14236         }
14237         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
14238         slit_y = cpl_propertylist_get_double(header, keyname);
14239         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14240             cpl_error_set_where(func);
14241             return NULL;
14242         }
14243         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
14244         dim_x = cpl_propertylist_get_double(header, keyname) / 2;
14245         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14246             cpl_error_set_where(func);
14247             return NULL;
14248         }
14249         cpl_table_set_int(slits, "slit_id", i, slit_id);
14250         cpl_table_set(slits, "xtop", i, slit_x - dim_x);
14251         cpl_table_set(slits, "ytop", i, slit_y);
14252         cpl_table_set(slits, "xbottom", i, slit_x + dim_x);
14253         cpl_table_set(slits, "ybottom", i, slit_y);
14254     }
14255 
14256     return slits;
14257 }
14258 
14259 
14286 cpl_table *mos_load_overscans_vimos(cpl_propertylist *header, int check_consistency)
14287 {
14288     const char *func = "mos_load_overscans_vimos";
14289 
14290     int        nx = 0;
14291     int        ny = 0;
14292     int        px = 0;
14293     int        py = 0;
14294     int        ox = 0;
14295     int        oy = 0;
14296     int        vx = 0;
14297     int        vy = 0;
14298     int        nrows;
14299     cpl_table *overscans;
14300 
14301 
14302     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14303         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
14304         return NULL;
14305     }
14306 
14307     if (header == NULL) {
14308         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14309         return NULL;
14310     }
14311 
14312     if (cpl_propertylist_has(header, "NAXIS1"))
14313         nx = cpl_propertylist_get_int(header, "NAXIS1");
14314     if (cpl_propertylist_has(header, "NAXIS2"))
14315         ny = cpl_propertylist_get_int(header, "NAXIS2");
14316     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
14317         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
14318     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
14319         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
14320     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
14321         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
14322     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
14323         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
14324     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
14325         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
14326     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
14327         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
14328 
14329     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14330         cpl_msg_error(func, "Missing overscan keywords in header");
14331         cpl_error_set_where(func);
14332         return NULL;
14333     }
14334 
14335     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
14336         cpl_msg_error(func, "Missing overscan keywords in header");
14337         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14338         return NULL;
14339     }
14340 
14341     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
14342     if (check_consistency) {
14343         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14344         return NULL;
14345     }
14346     else {
14347         cpl_msg_debug(func, "Overscans description conflicts with "
14348               "reported image sizes, "
14349               "%d + %d + %d != %d or "
14350               "%d + %d + %d != %d",
14351               px, vx, ox, nx,
14352               py, vy, oy, ny);
14353     }
14354     }
14355 
14356     nrows = 0;
14357     if (px > 0)
14358         nrows++;
14359     if (ox > 0)
14360         nrows++;
14361     if (py > 0)
14362         nrows++;
14363     if (oy > 0)
14364         nrows++;
14365 
14366     if (nrows > 2) {
14367         cpl_msg_error(func, "Unexpected overscan regions "
14368                       "(both in X and Y direction)");
14369         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14370         return NULL;
14371     }
14372 
14373 
14374     /*
14375      * A row is added for the description of the valid region of the
14376      * exposure the input header belongs to.
14377      */
14378 
14379     nrows++;
14380 
14381     overscans = cpl_table_new(nrows);
14382     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
14383     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
14384     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
14385     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
14386 
14387     nrows = 0;
14388 
14389     cpl_table_set_int(overscans, "xlow", nrows, px);
14390     cpl_table_set_int(overscans, "ylow", nrows, py);
14391     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
14392     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
14393     nrows++;
14394 
14395     if (px > 0) {
14396         cpl_table_set_int(overscans, "xlow", nrows, 0);
14397         cpl_table_set_int(overscans, "ylow", nrows, 0);
14398         cpl_table_set_int(overscans, "xhig", nrows, px);
14399         cpl_table_set_int(overscans, "yhig", nrows, ny);
14400         nrows++;
14401     }
14402 
14403     if (ox > 0) {
14404         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
14405         cpl_table_set_int(overscans, "ylow", nrows, 0);
14406         cpl_table_set_int(overscans, "xhig", nrows, nx);
14407         cpl_table_set_int(overscans, "yhig", nrows, ny);
14408         nrows++;
14409     }
14410 
14411     if (py > 0) {
14412         cpl_table_set_int(overscans, "xlow", nrows, 0);
14413         cpl_table_set_int(overscans, "ylow", nrows, 0);
14414         cpl_table_set_int(overscans, "xhig", nrows, nx);
14415         cpl_table_set_int(overscans, "yhig", nrows, py);
14416         nrows++;
14417     }
14418 
14419     if (oy > 0) {
14420         cpl_table_set_int(overscans, "xlow", nrows, 0);
14421         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
14422         cpl_table_set_int(overscans, "xhig", nrows, nx);
14423         cpl_table_set_int(overscans, "yhig", nrows, ny);
14424         nrows++;
14425     }
14426 
14427     return overscans;
14428 
14429 }
14430 
14431 
14463 #define READY 1
14464 #ifdef READY
14465 
14466 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
14467                                        int samples, int order)
14468 {
14469 
14470     const char *func = "mos_montecarlo_polyfit";
14471 
14472     cpl_polynomial *p;
14473     cpl_polynomial *q;
14474     cpl_vector     *listx;
14475     cpl_vector     *listy;
14476     double          err;
14477     double         *x;
14478     double         *px;
14479     double         *x_eval;
14480     double         *px_eval;
14481     double         *sigma;
14482     double         *vy;
14483     double         *dy;
14484     int             npoints, nevaluate;
14485     int             i, j;
14486 
14487 
14488     if (points == NULL || evaluate == NULL) {
14489         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14490         return NULL;
14491     }
14492 
14493     if (!cpl_table_has_column(points, "x")) {
14494         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14495         return NULL;
14496     }
14497 
14498     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
14499         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14500         return NULL;
14501     }
14502 
14503     if (cpl_table_has_invalid(points, "x")) {
14504         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14505         return NULL;
14506     }
14507 
14508     if (!cpl_table_has_column(points, "y")) {
14509         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14510         return NULL;
14511     }
14512 
14513     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
14514         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14515         return NULL;
14516     }
14517 
14518     if (cpl_table_has_invalid(points, "y")) {
14519         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14520         return NULL;
14521     }
14522 
14523     if (cpl_table_has_column(points, "y_err")) {
14524 
14525         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
14526             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14527             return NULL;
14528         }
14529     
14530         if (cpl_table_has_invalid(points, "y_err")) {
14531             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14532             return NULL;
14533         }
14534     }
14535 
14536     if (!cpl_table_has_column(evaluate, "x")) {
14537         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14538         return NULL;
14539     }
14540 
14541     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
14542         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14543         return NULL;
14544     }
14545 
14546     if (cpl_table_has_invalid(evaluate, "x")) {
14547         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14548         return NULL;
14549     }
14550 
14551     if (samples < 2 || order < 0) {
14552         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14553         return NULL;
14554     }
14555 
14556     npoints = cpl_table_get_nrow(points);
14557     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
14558     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
14559 
14560     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
14561 
14562     if (!cpl_table_has_column(points, "y_err")) {
14563         err = sqrt(err);
14564         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
14565         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
14566         cpl_msg_info(func, "Error column not found - set to %f\n", err);
14567     }
14568 
14569     /*
14570      * Create columns containing modeled values at each x
14571      */
14572 
14573     if (cpl_table_has_column(points, "px"))
14574         cpl_table_erase_column(points, "px");
14575     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
14576     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
14577     x = cpl_table_get_data_double(points, "x");
14578     px = cpl_table_get_data_double(points, "px");
14579     for (i = 0; i < npoints; i++)
14580         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
14581 
14582     nevaluate = cpl_table_get_nrow(evaluate);
14583 
14584     if (cpl_table_has_column(evaluate, "px"))
14585         cpl_table_erase_column(evaluate, "px");
14586     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
14587     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
14588     x_eval = cpl_table_get_data_double(evaluate, "x");
14589     px_eval = cpl_table_get_data_double(evaluate, "px");
14590     for (i = 0; i < nevaluate; i++)
14591         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
14592 
14593     /*
14594      * Initialise column with sigma
14595      */
14596 
14597     if (cpl_table_has_column(evaluate, "sigma"))
14598         cpl_table_erase_column(evaluate, "sigma");
14599     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
14600     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
14601     sigma = cpl_table_get_data_double(evaluate, "sigma");
14602 
14603     /*
14604      * Compute varied y cordinates to fit
14605      */
14606 
14607     if (cpl_table_has_column(points, "vy"))
14608         cpl_table_erase_column(points, "vy");
14609     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
14610     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
14611     vy = cpl_table_get_data_double(points, "vy");
14612     dy = cpl_table_get_data_double(points, "y_err");
14613     cpl_vector_unwrap(listy);
14614     listy = cpl_vector_wrap(npoints, vy);
14615 
14616     for (i = 0; i < samples; i++) {
14617         for (j = 0; j < npoints; j++)
14618             vy[j] = px[j] + dy[j] * mos_randg(1);
14619         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
14620         for (j = 0; j < nevaluate; j++)
14621             sigma[j] += fabs(px_eval[j] 
14622                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
14623         cpl_polynomial_delete(q);
14624     }
14625 
14626     /* 
14627      * Factor 1.25 to convert average deviation to sigma 
14628      */
14629 
14630     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
14631     cpl_table_divide_scalar(evaluate, "sigma", samples);
14632 
14633     cpl_vector_unwrap(listx);
14634     cpl_vector_unwrap(listy);
14635 
14636     return p;
14637 }
14638 
14639 #endif
14640 

Generated on Wed Sep 10 07:31:54 2008 for FORS Pipeline Reference Manual by  doxygen 1.4.6