moses.c

00001 /* $Id: moses.c,v 1.35 2009/05/14 11:39:42 cizzo Exp $
00002  *
00003  * This file is part of the MOSES library
00004  * Copyright (C) 2002-2006 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cizzo $
00023  * $Date: 2009/05/14 11:39:42 $
00024  * $Revision: 1.35 $
00025  * $Name:  $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <math.h>
00037 #include <time.h>
00038 
00039 #include <moses.h>
00040 
00041 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
00042  * even if cpl_tool.h is not public. It should be removed as soon as 
00043  * an image median filtering with generic kernel will be implemented
00044  * in the CPL, or as soon as this module will be moved into the CPL. */
00045 
00046 float cpl_tools_get_median_float(float *, int);
00047 
00048 #define MAX_COLNAME      (80)
00049 
00050 static double default_lines_hi[] = {   /* Default sky line catalog */
00051                     5577.338,          /* for high res data        */
00052                     5889.953,
00053                     5895.923,
00054                     5915.301,
00055                     5932.862,
00056                     5953.420,
00057                     6257.961,
00058                     6287.434,
00059                     6300.304,
00060                     6306.869,
00061                     6363.780,
00062                     6498.729,
00063                     6533.044,
00064                     6553.617,
00065                     6841.945,
00066                     6863.955,
00067                     6870.994,
00068                     6889.288,
00069                     6900.833,
00070                     6912.623,
00071                     6923.220,
00072                     6939.521,
00073                     6969.930,
00074                     7003.858,
00075                     7244.907,
00076                     7276.405,
00077                     7284.439,
00078                     7316.282,
00079                     7329.148,
00080                     7340.885,
00081                     7358.659,
00082                     7571.746,
00083                     7750.640,
00084                     7759.996,
00085                     7794.112,
00086                     7808.467,
00087                     7821.503,
00088                     7841.266,
00089                     7913.708,
00090                     7949.204,
00091                     7964.650,
00092                     7993.332,
00093                     8014.059,
00094                     8310.719,
00095                     8344.602,
00096                     8382.392,
00097                     8399.170,
00098                     8415.231,
00099                     8430.174,
00100                     8452.250,
00101                     8493.389,
00102                     8791.186,
00103                     8827.096,
00104                     8885.850,
00105                     8903.114,
00106                     8943.395,
00107                     8988.366
00108                     };
00109 
00110 static double default_lines_lo[] = {   /* Default sky line catalog */
00111                     5577.338,          /* for low res data         */
00112                     6300.304,
00113                     6863.955,
00114                     7571.746,
00115                     7964.650,
00116                     7993.332
00117                     };
00118 
00119 
00129 /*
00130  * The following macros and function for finding the k-th smallest
00131  * value on a float array will be accessible from cpl_tools once
00132  * this module will be moved into the CPL.
00133  */
00134 
00135 /****
00136 
00137 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00138 
00139 static float kthSmallest(float a[], int n, int k)
00140 {
00141   register int i,j,l,m;
00142   register float x;
00143 
00144   l = 0;
00145   m = n-1;
00146   while (l < m) {
00147     x = a[k];
00148     i = l;
00149     j = m;
00150     do {
00151       while (a[i] < x) {
00152         i++;
00153       }
00154       while (x < a[j]) {
00155         j--;
00156       }
00157       if (i <= j) {
00158         PIX_SWAP(a[i],a[j]);
00159         i++;
00160         j--;
00161       }
00162     } while (i <= j);
00163 
00164     if (j < k) {
00165       l = i;
00166     }
00167     if (k < i) {
00168       m = j;
00169     }
00170 
00171   }
00172   return(a[k]);
00173 }
00174 
00175 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00176 
00177 ****/
00178 
00179 /* 
00180  * Return random number with gaussian distribution (mean = 0, variance = 1)
00181  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00182  * indicating whether to "seed" or not the sequence of generated random 
00183  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00184  * and at further calls the input argument is ignored. This function
00185  * generates two random numbers at each call, returning the first one
00186  * at odd calls, and the second one at even calls.
00187  */
00188 
00189 static void mos_seed(void)
00190 {
00191     srand((unsigned int)time((time_t *)0));
00192 }
00193 
00194 static double mos_randg(int seme)
00195 {
00196     static int doit = 1;
00197     static int gotit = 1;
00198     double x1, x2, w, y1;
00199     static double y2;
00200 
00201     if (gotit && seme) {
00202         mos_seed();
00203         gotit = 0;
00204     }
00205 
00206     if (doit) {
00207         doit = 0;
00208         do {
00209             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00210             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00211             w = x1 * x1 + x2 * x2;
00212         } while (w >= 1.0 || w == 0.0);
00213     
00214         w = sqrt( (-2.0 * log(w)) / w);
00215     
00216         y1 = x1 * w;
00217         y2 = x2 * w;
00218         return y1;
00219     }
00220 
00221     doit = 1;
00222     return y2;
00223 }
00224 
00225 /* 
00226  * This function contained a dependency on the VIMOS library
00227  * (function medianPixelvalue()): it should be removed as soon as an 
00228  * image median filtering with generic kernel will be implemented
00229  * in the CPL. Currently it has been solved by a direct call to
00230  * a cpl_tool function.
00231  */
00232 
00233 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00234                                             int filtsizey, int refrow,
00235                                             int above, int below, int step)
00236 {
00237 
00238   const char *func = "mos_image_general_median_filter";
00239 
00240   cpl_image  *filt_img = NULL;
00241   int         col, row;
00242   float      *buf = NULL;
00243   float      *data;
00244   float      *fdata;
00245   int         upright_y, loleft_y;
00246   int         j;
00247   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00248   int         f2y;
00249   int         nx = cpl_image_get_size_x(ima_in);
00250   int         ny = cpl_image_get_size_y(ima_in);
00251   int         firstRow;
00252 
00253 
00254   if (yIsEven) filtsizey++;
00255 
00256   if (ny <= filtsizey) {
00257     cpl_msg_error(func, 
00258                   "Median filter size: %d, image size: %d", filtsizey, ny);
00259     return NULL;
00260   }
00261 
00262   f2y = filtsizey / 2;
00263 
00264   filt_img = cpl_image_duplicate(ima_in);
00265   buf = cpl_malloc(filtsizey * sizeof(float));
00266   data = cpl_image_get_data(ima_in);
00267   fdata = cpl_image_get_data(filt_img);
00268 
00269   firstRow = refrow - step * (below / step);
00270   if (firstRow < f2y)
00271     firstRow += step;
00272 
00273   for (col = 0; col < nx; col++) {
00274     for (row = firstRow; row < refrow + above; row += step) {
00275       if (row >= ny - f2y)
00276         break;
00277       loleft_y = row - f2y;
00278       upright_y = row + f2y + 1;
00279       for (j = loleft_y; j < upright_y; j++)
00280         buf[j - loleft_y] = data[col + j * nx];
00281 
00282       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00283     }
00284   }
00285 
00286   cpl_free(buf);
00287 
00288   return filt_img;
00289 
00290 }
00291 
00292 
00293 /*
00294  * The following static function is used to find an accurate position
00295  * of a peak within a short interval (however at least 5 pixels long). 
00296  * The basic idea is to find the baricenter of all the pixel values 
00297  * that pass a threshold level between the median value and the maximum
00298  * value within the examined interval (in case such levels are equal,
00299  * the input is considered flat and no position is returned). At least
00300  * minPoints must pass this threshold, or no position is computed. To
00301  * evaluate the significance of the computed baricenter, the variance 
00302  * of the contributing positions (relative to the found baricenter) is 
00303  * also evaluated, and compared with the expected variance for a uniform 
00304  * distribution of positions. If the observed variance is greater than 
00305  * 80% of the variance of the uniform distribution, the found position 
00306  * is rejected.
00307  */
00308 
00309 static int peakPosition(const float *data, int size, float *position,
00310             int minPoints)
00311 {
00312   int    i;
00313   int    count = 0;
00314   float *copy;
00315   float  max, median, level, pos, variance, uniformVariance;
00316   double sum, weights;
00317 
00318 
00319   if (data == NULL)
00320       return 1;
00321 
00322   if (size < 5)         /* Hardcoded, I know... */
00323       return 1;
00324 
00325 
00326   /*
00327    *  Find median level
00328    */
00329 
00330   copy = (float *) cpl_malloc(size*sizeof(float));
00331   for (i = 0; i < size; i++)
00332       copy[i] = data[i];
00333   median = cpl_tools_get_median_float(copy, size);
00334   cpl_free(copy);
00335 
00336 
00337   /*
00338    *  Find max
00339    */
00340 
00341   max = data[0];
00342   for (i = 1; i < size; i++)
00343       if (data[i] > max)
00344           max = data[i];
00345 
00346 
00347   /*
00348    *  If the max equals the median we have a flat input, therefore
00349    *  no peak is found.
00350    */
00351 
00352   if (max-median < 0.00001)
00353       return 1;
00354 
00355 
00356   /*
00357    *  Discrimination level: only pixels with values above this
00358    *  level are considered in baricenter calculation.
00359    */
00360 
00361   level = (max + median) / 2;
00362 
00363 
00364   /*
00365    *  Of the values above this level compute the baricenter and
00366    *  then the variance of the positions used. Note that the weights
00367    *  are taken as the difference between the pixels values and
00368    *  the median level (supposedly the background).
00369    */
00370 
00371   count = 0;
00372   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00373       if (data[i] > level) {
00374           count++;
00375           weights += (data[i] - median);
00376           sum     += i * (data[i] - median);
00377       }
00378   }
00379 
00380 
00381   /*
00382    *  If too few values are above threshold, refuse the position
00383    *  as insignificant
00384    */
00385 
00386   if (count < minPoints)
00387       return 1;
00388 
00389   pos = sum / weights;
00390   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00391       if (data[i] > level) {
00392           weights++;
00393           sum += (i - pos) * (i - pos);
00394       }
00395   }
00396   variance = sqrt(sum / weights);
00397 
00398 
00399  /*
00400   *  The "uniform variance" is the variance that should be obtained
00401   *  in the case of uniform distribution of the points positions in
00402   *  the selected interval. If the real variance is comparable with
00403   *  this value, the peak is considered not found.
00404   */
00405 
00406   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00407 
00408   if (variance > 0.8 * uniformVariance)
00409       return 1;
00410 
00411   *position = pos + 0.5;
00412 
00413   return 0;
00414 }
00415 
00416 
00417 /*
00418  *  The following static function determines the quantity dx to be
00419  *  added to the position of the highest pixel of a fiber profile,
00420  *  to get the true position of the profile maximum. All is needed
00421  *  is the maximum observed value v2 in the profile, and the observed
00422  *  values v1 and v3 of the previous and the next pixels in the profile.
00423  *  
00424  *  The following ratio is defined:
00425  *  
00426  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00427  *      
00428  *  This is a conventional ratio that wouldn't diverge for any set of
00429  *  pixel values, and that would not depend on the presence of background
00430  *  (with the assumption that the background level is the same for the 
00431  *  three pixels). R has also been chosen in such a way that its value
00432  *  is already quite close to the real dx. It should be noted that the
00433  *  following condition should be fulfilled:
00434  *
00435  *           v1  <= v2   and   v3  <  v2
00436  *  or
00437  *           v1  <  v2   and   v3  <=  v2
00438  *
00439  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00440  *  boundary cases, one has:
00441  *
00442  *           v2 = v1   and   R = dx = -0.5
00443  *           v2 = v3   and   R = dx =  0.5
00444  *
00445  *  Another special case is when the observed pixel values are perfectly
00446  *  symmetrical:
00447  *
00448  *           v1 = v3   and   R = dx =  0.0
00449  *
00450  *  In all the intermediate cases the relation between R and dx depends
00451  *  on the shape of the fiber profile, that has been determined elsewhere.
00452  *  Using the accurate reconstruction of the fiber profile obtained by 
00453  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00454  *  that R differs from dx always less than 0.01 pixels. If the condition
00455  *
00456  *           v1  <= v2   and   v3  <  v2
00457  *  or
00458  *           v1  <  v2   and   v3  <=  v2
00459  *
00460  *  is not fulfilled, then this function returns the value 2.0.
00461  */
00462 
00463 static double values_to_dx(double v1, double v2, double v3)
00464 {
00465 
00466   static double epsilon = 0.00000001;
00467   double        r       = 2.0;
00468 
00469 
00470   if (v1 > v2 || v3 > v2)
00471     return r;
00472 
00473   if (2 * v2 - v1 - v3 < epsilon)
00474     return r;
00475 
00476   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00477 
00478   return r;
00479 
00480 }
00481 
00482 
00483 /*
00484  * The following static function passes a min filter of given box
00485  * size on the data buffer. The box size must be a positive odd integer.
00486  */
00487 
00488 static float *min_filter(float *buffer, int length, int size)
00489 {
00490     float *minf  = cpl_calloc(length, sizeof(float));
00491     float  min;
00492     int    start = size / 2;
00493     int    end   = length - size / 2;
00494     int    i, j;
00495 
00496 
00497     for (i = start; i < end; i++) {
00498         min = buffer[i-start];
00499         for (j = i - start + 1; j <= i + start; j++)
00500             if (min > buffer[j])
00501                 min = buffer[j];
00502         minf[i] = min;
00503     }
00504 
00505     for (i = 0; i < start; i++)
00506         minf[i] = minf[start];
00507 
00508     for (i = end; i < length; i++)
00509         minf[i] = minf[end-1];
00510 
00511     return minf;
00512 }
00513 
00514 
00515 /*
00516  * The following static function passes a max filter of given box
00517  * size on the data buffer. The box size must be a positive odd integer.
00518  */
00519  
00520 static float *max_filter(float *buffer, int length, int size)
00521 {
00522     float *maxf  = cpl_calloc(length, sizeof(float));
00523     float  max;
00524     int    start = size / 2;
00525     int    end   = length - size / 2;
00526     int    i, j;
00527 
00528 
00529     for (i = start; i < end; i++) {
00530         max = buffer[i-start];
00531         for (j = i - start + 1; j <= i + start; j++)
00532             if (max < buffer[j])
00533                 max = buffer[j];
00534         maxf[i] = max;
00535     }
00536 
00537     for (i = 0; i < start; i++)
00538         maxf[i] = maxf[start];
00539 
00540     for (i = end; i < length; i++)
00541         maxf[i] = maxf[end-1];
00542 
00543     return maxf;
00544 }
00545 
00546 
00547 /*
00548  * The following static function passes a running average of given box
00549  * size on the data buffer. The box size must be a positive odd integer.
00550  */
00551  
00552 static float *smo_filter(float *buffer, int length, int size)
00553 {
00554     float *smof  = cpl_calloc(length, sizeof(float));
00555     double sum;
00556     int    start = size / 2;
00557     int    end   = length - size / 2;
00558     int    i, j;
00559 
00560 
00561     for (i = start; i < end; i++) {
00562         sum = 0.0;
00563         for (j = i - start; j <= i + start; j++)
00564             sum += buffer[j];
00565         smof[i] = sum / size;
00566     }
00567 
00568     for (i = 0; i < start; i++)
00569         smof[i] = smof[start];
00570 
00571     for (i = end; i < length; i++)
00572         smof[i] = smof[end-1];
00573 
00574     return smof;
00575 }
00576 
00577 /*
00578  * The following two static functions are used to read and write from the 
00579  * global distortion table the different model components. Conventionally
00580  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00581  * storage for model coefficients, and these functions guarantee that the
00582  * coefficients are read in and written out correctly, independent on their
00583  * physical meaning. The first 6 table rows are a description of the IDS
00584  * coefficients, followed by a row containing only the used reference 
00585  * wavelength. The remaining 3 are a description of the spectral curvature.
00586  * The first row is a description of coefficient c0, the second of coefficient
00587  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00588  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00589  * bivariate polynomialx on x,y mask coordinates. If the input table
00590  * to the write routine is NULL, it is allocated and initialised. Also
00591  * the input polynomial could be NULL, and nothing would be written to 
00592  * the table. If both pointers are NULL the function is basically a
00593  * constructor of the global distortion table.
00594  */
00595 
00596 static cpl_polynomial *read_global_distortion(cpl_table *global, int row)
00597 {
00598     cpl_polynomial *poly = NULL;
00599     int             p[2];
00600     int             degree = 2;
00601     int             null;
00602     double          coeff;
00603 
00604     char   name[MAX_COLNAME];
00605 
00606 
00607     for (p[0] = 0; p[0] <= degree; p[0]++) {
00608         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00609             snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00610             coeff = cpl_table_get_double(global, name, row, &null);
00611             if (null)
00612                 continue;
00613             if (poly == NULL)
00614                 poly = cpl_polynomial_new(2);
00615             cpl_polynomial_set_coeff(poly, p, coeff);
00616         }
00617     }
00618 
00619     return poly;
00620 }
00621 
00622 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00623                                           cpl_polynomial *poly)
00624 {
00625     cpl_table *table;
00626     int        p[2];
00627     int        degree = 2;
00628     int        nrow = 10;
00629 
00630     char       name[MAX_COLNAME];
00631 
00632 
00633     if (global) {
00634         table = global;
00635     }
00636     else {
00637         table = cpl_table_new(nrow);
00638         for (p[0] = 0; p[0] <= degree; p[0]++) {
00639             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00640                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00641                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00642             }
00643         }
00644     }
00645 
00646     if (poly) {
00647         for (p[0] = 0; p[0] <= degree; p[0]++) {
00648             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00649                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00650                 cpl_table_set_double(table, name, row, 
00651                                      cpl_polynomial_get_coeff(poly, p));
00652             }
00653         }
00654     }
00655 
00656     return table;
00657 }
00658 
00659 
00660 /*
00661  * The following static function is performing a robust linear fit
00662  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00663  *
00664  *  ----> y = a + b * x
00665  *
00666  * This function return 0 on success.
00667  */
00668 
00669 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00670 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00671                            double *abdev)
00672 {
00673     cpl_vector *vx;
00674     cpl_vector *vy;
00675     cpl_vector *va;
00676 
00677     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00678     double  sx, sy, sxy, sxx, chisq;
00679     double *arr;
00680     double  aa_ls, bb_ls;
00681     double *x;
00682     double *y;
00683     int     np;
00684     int     iter;
00685     int     max_iterate = 30;
00686     int     i;
00687 
00688 
00689     np = cpl_bivector_get_size(list);
00690     vx = cpl_bivector_get_x(list);
00691     vy = cpl_bivector_get_y(list);
00692     x = cpl_vector_get_data(vx);
00693     y = cpl_vector_get_data(vy);
00694 
00695     sx = sy = sxx = sxy = 0.00;
00696     for (i = 0; i < np; i++) {
00697         sx  += x[i];
00698         sy  += y[i];
00699         sxy += x[i] * y[i];
00700         sxx += x[i] * x[i];
00701     }
00702 
00703     del = np * sxx - sx * sx;
00704     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00705     bb_ls = bb = (np * sxy - sx * sy) / del;
00706 
00707     chisq = 0.00;
00708     for (i = 0; i < np; i++) {
00709         temp = y[i] - (aa+bb*x[i]);
00710         temp *= temp;
00711         chisq += temp;
00712     }
00713 
00714     va = cpl_vector_new(np);
00715     arr = cpl_vector_get_data(va);
00716     sigb = sqrt(chisq/del);
00717     b1 = bb;
00718 
00719     bcomp = b1;
00720     sum = 0.00;
00721     for (i = 0; i < np; i++) {
00722         arr[i] = y[i] - bcomp * x[i];
00723     }
00724     aa = cpl_vector_get_median_const(va);
00725     abdevt = 0.0;
00726     for (i = 0; i < np; i++) {
00727         d = y[i] - (bcomp * x[i] + aa);
00728         abdevt += fabs(d);
00729         if (y[i] != 0.0) 
00730             d /= fabs(y[i]);
00731         if (fabs(d) > 1e-7) 
00732             sum += (d >= 0.0 ? x[i] : -x[i]);
00733     }
00734     f1 = sum;
00735 
00736     b2 = bb + SEGNO(3.0 * sigb, f1);
00737 
00738     bcomp = b2;
00739     sum = 0.00;
00740     for (i = 0; i < np; i++) {
00741         arr[i] = y[i] - bcomp * x[i];
00742     }
00743     aa = cpl_vector_get_median_const(va);
00744     abdevt = 0.0;
00745     for (i = 0; i < np; i++) {
00746         d = y[i] - (bcomp * x[i] + aa);
00747         abdevt += fabs(d);
00748         if (y[i] != 0.0) 
00749             d /= fabs(y[i]);
00750         if (fabs(d) > 1e-7) 
00751             sum += (d >= 0.0 ? x[i] : -x[i]);
00752     }
00753     f2 = sum;
00754 
00755     if (fabs(b2-b1)<1e-7) {
00756         *a = aa;
00757         *b = bb;
00758         *abdev = abdevt / (double)np;
00759         cpl_vector_delete(va);
00760         return 0;
00761     }
00762 
00763     iter = 0;
00764     while (f1*f2 > 0.0) {
00765         bb = 2.0*b2-b1;
00766         b1 = b2;
00767         f1 = f2;
00768         b2 = bb;
00769 
00770         bcomp = b2;
00771         sum = 0.00;
00772         for (i = 0; i < np; i++) {
00773             arr[i] = y[i] - bcomp * x[i];
00774         }
00775         aa = cpl_vector_get_median_const(va);
00776         abdevt = 0.0;
00777         for (i = 0; i < np; i++) {
00778             d = y[i] - (bcomp * x[i] + aa);
00779             abdevt += fabs(d);
00780             if (y[i] != 0.0) 
00781                 d /= fabs(y[i]);
00782             if (fabs(d) > 1e-7) 
00783                 sum += (d >= 0.0 ? x[i] : -x[i]);
00784         }
00785         f2 = sum;
00786         iter++;
00787         if (iter >= max_iterate) 
00788             break;
00789     }
00790     if (iter >= max_iterate) {
00791         *a = aa_ls;
00792         *b = bb_ls;
00793         *abdev = -1.0;
00794         cpl_vector_delete(va);
00795         return 1;
00796     }
00797 
00798     sigb = 0.01 * sigb;
00799     while (fabs(b2-b1) > sigb) {
00800         bb = 0.5 * (b1 + b2);
00801         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00802             break;
00803         bcomp = bb;
00804         sum = 0.0;
00805         for (i = 0; i < np; i++) {
00806             arr[i] = y[i] - bcomp * x[i];
00807         }
00808         aa = cpl_vector_get_median_const(va);
00809         abdevt = 0.0;
00810         for (i = 0; i < np; i++) {
00811             d = y[i] - (bcomp * x[i] + aa);
00812             abdevt += fabs(d);
00813             if (y[i] != 0.0) 
00814                 d /= fabs(y[i]);
00815             if (fabs(d) > 1e-7) 
00816                 sum += (d >= 0.0 ? x[i] : -x[i]);
00817         }
00818         f = sum;
00819 
00820         if (f*f1 >= 0.0) {
00821             f1=f;
00822             b1=bb;
00823         } 
00824         else {
00825             f2=f;
00826             b2=bb;
00827         }
00828     }
00829     cpl_vector_delete(va);
00830     *a = aa;
00831     *b = bb;
00832     *abdev = abdevt / np;
00833     return 0;
00834 }
00835 #undef SEGNO
00836 
00837 /*      
00838  * The following static function applies the Hough transform from a table
00839  * of points to another table of points. Given the points p_i = (x_i,y_i)
00840  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00841  * and Y = y_i - X*x_i is computed and added to the output table for each
00842  * p_i, p_j pair. This means that if the input table has N points, the
00843  * output table has N*(N-1)/2 points.
00844  */
00845     
00846 /* static */
00847 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00848 {
00849     cpl_table *output;
00850     double    *xdata;
00851     double    *ydata;
00852     double    *xodata;
00853     double    *yodata;
00854     int        npoints;
00855     int        opoints;
00856     int        i, j, k;
00857 
00858 
00859     npoints = cpl_table_get_nrow(table);
00860     opoints = npoints*(npoints-1)/2;
00861 
00862     output = cpl_table_new(opoints);
00863     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00864     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00865     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00866     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00867 
00868     xodata = cpl_table_get_data_double(output, "m");
00869     yodata = cpl_table_get_data_double(output, "q");
00870     
00871     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00872     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00873 
00874     xdata = cpl_table_get_data_double(table, "x");
00875     ydata = cpl_table_get_data_double(table, "y");
00876 
00877     k = 0;
00878     for (i = 0; i < npoints; i++) {
00879         for (j = i+1; j < npoints; j++) {
00880             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00881             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00882             k++;
00883         }
00884     }
00885 
00886     if (k != opoints)
00887         printf("Assert k = %d, expected %d\n", k, opoints);
00888 
00889     cpl_table_erase_column(table, "x");
00890     cpl_table_erase_column(table, "y");
00891 
00892     return output;
00893 }
00894 
00895 
00896 /*
00897  * The following static function is performing the spectral
00898  * extraction for the function mos_extract_objects()
00899  */
00900 
00901 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00902                            cpl_image *extracted, cpl_image *sky, 
00903                            cpl_image *error, int nobjects, int extraction, 
00904                            double ron, double conad, int ncomb)
00905 {
00906 
00907   cpl_vector *vprofile;
00908   cpl_matrix *kernel;
00909   cpl_image  *smowin;
00910 
00911   int i, j;
00912   int specLen;
00913   int numRows;
00914   int index;
00915   int iter;
00916   int maxIter   = 2;         /* Not less than 2 !!! */
00917   int smoothBox = 31;        /* Not less than 5 !!! */
00918   double nsigma = 5.0;
00919 
00920   double sumWeight, sum, sumSky, sumProf, variance, weight;
00921   double *profile;
00922   double *buffer;
00923   float  *edata;
00924   float  *ekdata;
00925   float  *endata;
00926   float  *sdata;
00927   float  *kdata;
00928   float  *fdata;
00929 
00930   double value;
00931 
00932 
00933   specLen = cpl_image_get_size_x(sciwin);
00934   numRows = cpl_image_get_size_y(sciwin);
00935 
00936   edata = cpl_image_get_data(extracted);
00937   edata += nobjects * specLen;
00938 
00939   ekdata = cpl_image_get_data(sky);
00940   ekdata += nobjects * specLen;
00941 
00942   endata = cpl_image_get_data(error);
00943   endata += nobjects * specLen;
00944 
00945   sdata = cpl_image_get_data(sciwin);
00946   kdata = cpl_image_get_data(skywin);
00947 
00948 
00949   /*
00950    * Initial spectrum estimate
00951       if (sdata[i + j * specLen] > 0.0)
00952    */
00953 
00954   if (extraction && numRows > 5) {
00955       kernel = cpl_matrix_new(3, 3);
00956       cpl_matrix_fill(kernel, 1.0);
00957       smowin = cpl_image_filter_median(sciwin, kernel);
00958       cpl_matrix_delete(kernel);
00959       fdata = cpl_image_get_data(smowin);
00960       for (i = 0; i < specLen; i++)
00961         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00962             edata[i] += fdata[i + j * specLen];
00963       cpl_image_delete(smowin);
00964   }
00965   else {
00966       for (i = 0; i < specLen; i++)
00967         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00968             edata[i] += sdata[i + j * specLen];
00969   }
00970 
00971   if (extraction) {
00972 
00973     profile = cpl_calloc(specLen * numRows, sizeof(double));
00974     buffer  = cpl_calloc(specLen, sizeof(double));
00975 
00976     for (iter = 0; iter < maxIter; iter++) {
00977 
00978       /*
00979        * Normalised spatial profile
00980        */
00981 
00982       for (i = 0; i < specLen; i++) {
00983         for (j = 0; j < numRows; j++) {
00984           index = i + j * specLen;
00985 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00986           if (fabs(edata[i]) > 0.00001)
00987             profile[index] = sdata[index] / edata[i];
00988           else
00989             profile[index] = 0.0;
00990         }
00991       }
00992 
00993       for (j = 0; j < numRows; j++) {
00994 
00995         /*
00996          * Smooth each row in the dispersion direction, and enforce positivity
00997          */
00998 
00999         for (i = 0; i < specLen - smoothBox; i++) {
01000           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01001           value = cpl_vector_get_median_const(vprofile);
01002           cpl_vector_unwrap(vprofile);
01003           if (value < 0)
01004             value = 0.0;
01005           buffer[i + smoothBox / 2] = value;
01006         }
01007 
01008         /*
01009          * Replace the end portions (i.e., not median filtered) with a mean
01010          */
01011 
01012         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01013         value = cpl_vector_get_mean(vprofile);
01014         cpl_vector_unwrap(vprofile);
01015 
01016         if (value < 0)
01017             value = 0.0;
01018 
01019         for (i = 0; i < smoothBox / 2; i++)
01020           buffer[i] = value;
01021 
01022         vprofile = cpl_vector_wrap(smoothBox / 2, 
01023                                    profile + specLen - smoothBox/2 + j*specLen);
01024         value = cpl_vector_get_mean(vprofile);
01025         cpl_vector_unwrap(vprofile);
01026 
01027         if (value < 0)
01028             value = 0.0;
01029 
01030         for (i = 0; i < smoothBox / 2; i++)
01031           buffer[i + specLen - smoothBox / 2] = value;
01032 
01033         for (i = 0; i < specLen; i++)
01034           profile[i + j * specLen] = buffer[i];
01035 
01036       }
01037 
01038       /*
01039        * Enforce normalization of spatial profile after smoothing
01040        */
01041 
01042       for (i = 0; i < specLen; i++) {
01043         for (j = 0, value = 0.0; j < numRows; j++)
01044           value += profile[i + j * specLen];
01045         if (value > 0.00001)
01046           for (j = 0; j < numRows; j++)
01047             profile[i + j * specLen] /= value;
01048         else
01049           for (j = 0; j < numRows; j++)
01050             profile[i + j * specLen] = 0.0;
01051       }
01052 
01053 
01054       /*
01055        * Optimal extraction
01056        */
01057 
01058       for (i = 0; i < specLen; i++) {
01059         sum = 0.0;
01060         sumSky = 0.0;
01061         sumWeight = 0.0;
01062         sumProf = 0.0;
01063         for (j = 0; j < numRows; j++) {
01064           index = i + j * specLen;
01065 /*        
01066 if (sdata[index] > 0.0) {
01067 */
01068             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01069                      / conad;
01070             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01071             value = sdata[index] - edata[i] * profile[index];
01072             if (fabs(value) / sqrt(variance) < nsigma) {
01073               weight = 1000000 * profile[index] / variance;
01074               sum += weight * sdata[index];
01075               sumSky += weight * kdata[index];
01076               sumWeight += weight * profile[index];
01077               sumProf += profile[index];
01078             }
01079 /*
01080 }      
01081 */
01082         }
01083 
01084         if (sumWeight > 0.00001) {
01085           edata[i] = sum / sumWeight;
01086           ekdata[i] = sumSky / sumWeight;
01087           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01088         }
01089         else {
01090 /*
01091           edata[i] = 0.0;
01092           ekdata[i] = 0.0;
01093           endata[i] = 0.0;
01094 */
01095           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01096         }
01097       }
01098     }
01099     cpl_free(profile);
01100     cpl_free(buffer);
01101   }
01102   else {
01103 
01104     /*
01105      * Add sky estimation for the simple aperture extraction.
01106         if (kdata[i + j * specLen] > 0.0)
01107      */
01108 
01109     for (i = 0; i < specLen; i++)
01110       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01111           ekdata[i] += kdata[i + j * specLen];
01112 
01113     /*
01114      * Add error estimation for the simple aperture extraction.
01115      */
01116 
01117     for (i = 0; i < specLen; i++)
01118       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01119 
01120   }
01121 
01122 }
01123 
01124 
01171 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01172                                  cpl_table *ids, cpl_table *crv, 
01173                                  double reference)
01174 {
01175     const char *func = "mos_global_distortion";
01176 
01177     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01178 
01179     cpl_table      *global = NULL;
01180     cpl_table      *coeff;
01181     cpl_table      *dummy;
01182     cpl_vector     *ci;
01183     cpl_vector     *xmask;
01184     cpl_vector     *ymask;
01185     cpl_bivector   *mask;
01186     cpl_vector     *xccd;
01187     cpl_vector     *yccd;
01188     cpl_bivector   *ccd;
01189     cpl_polynomial *poly;
01190     double         *xtop;
01191     double         *ytop;
01192     double         *xbottom;
01193     double         *ybottom;
01194     double         *mxtop;
01195     double         *mytop;
01196     double         *mxbottom;
01197     double         *mybottom;
01198     int            *position;
01199     int            *length;
01200     int            *slit_id;
01201     int            *mslit_id;
01202     int             nslits, nmaskslits, npoints;
01203     int             order;
01204     int             i, j;
01205     int             minslit = 6;    // 12;
01206 
01207 
01208 /* *+
01209 printf("error1: %s\n", cpl_error_get_message());
01210 +* */
01211     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01212         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01213         return NULL;
01214     }
01215 /* *+
01216 printf("error1a: %s\n", cpl_error_get_message());
01217 +* */
01218 
01219     nslits = cpl_table_get_nrow(slits);
01220 /* *+
01221 printf("error1b: %s\n", cpl_error_get_message());
01222 +* */
01223 
01224     if (nslits < minslit) {
01225         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01226                         "distortion model determination", nslits, minslit);
01227         return NULL;
01228     }
01229 /* *+
01230 printf("error1c: %s\n", cpl_error_get_message());
01231 +* */
01232 
01233     nmaskslits = cpl_table_get_nrow(maskslits);
01234 
01235     length   = cpl_table_get_data_int(slits, "length");
01236     position = cpl_table_get_data_int(slits, "position");
01237     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01238     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01239     xtop     = cpl_table_get_data_double(slits, "xtop");
01240     ytop     = cpl_table_get_data_double(slits, "ytop");
01241     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01242     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01243     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01244     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01245     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01246     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01247 
01248 
01249     /*
01250      * Global IDS
01251      */
01252 
01253     coeff = cpl_table_new(nslits);
01254     cpl_table_copy_structure(coeff, ids);
01255     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01256     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01257     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01258     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01259 
01260 /* *+
01261 printf("error2: %s\n", cpl_error_get_message());
01262 +* */
01263     for (i = 0; i < nslits; i++) {
01264         for (j = 0; j < nmaskslits; j++) {
01265             if (slit_id[i] == mslit_id[j]) {
01266                 cpl_table_set_double(coeff, "xmask", i,
01267                                      (mxtop[j] + mxbottom[j]) / 2);
01268                 cpl_table_set_double(coeff, "ymask", i,
01269                                      (mytop[j] + mybottom[j]) / 2);
01270             }
01271         }
01272     }
01273 
01274     if (cpl_table_has_invalid(coeff, "xmask")) {
01275         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01276         cpl_table_delete(coeff);
01277         return NULL;
01278     }
01279 
01280     for (i = 0; i < nslits; i++) {
01281         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01282         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01283     }
01284 
01285 /* *+
01286 printf("error3: %s\n", cpl_error_get_message());
01287 +* */
01288     for (i = 0; i < nslits; i++) {
01289 
01290         if (length[i] == 0)
01291             continue;
01292 
01293         cpl_table_and_selected_window(ids, position[i], length[i]);
01294         dummy = cpl_table_extract_selected(ids);
01295         for (j = 0; j < 6; j++) {
01296             if (cpl_table_has_column(dummy, clab[j])) {
01297                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01298                     cpl_table_set_double(coeff, clab[j], i, 
01299                          cpl_table_get_column_median(dummy, clab[j]));
01300                 }
01301             }
01302         }
01303 
01304         cpl_table_delete(dummy);
01305         cpl_table_select_all(ids);
01306             
01307     }
01308 
01309 /* *+
01310 printf("error4: %s\n", cpl_error_get_message());
01311 +* */
01312     for (j = 0; j < 6; j++) {
01313         if (cpl_table_has_column(coeff, clab[j])) {
01314             cpl_table_and_selected_invalid(coeff, clab[j]);
01315 
01316             if (cpl_table_not_selected(coeff))
01317                 dummy = cpl_table_extract_selected(coeff);
01318             else
01319                 break;
01320 
01321             npoints = cpl_table_get_nrow(dummy);
01322 
01323             if (npoints >= 6) {
01324 
01325                 if (npoints >= 12)
01326                     order = 2;
01327                 else
01328                     order = 1;
01329                    
01330                 ci = cpl_vector_wrap(npoints,
01331                                      cpl_table_get_data_double(dummy, clab[j]));
01332                 if (j) {
01333                     xccd = cpl_vector_wrap(npoints,
01334                                      cpl_table_get_data_double(dummy, "xccd"));
01335                     yccd = cpl_vector_wrap(npoints,
01336                                      cpl_table_get_data_double(dummy, "yccd"));
01337                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01338 
01339 /* %%% */
01340                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01341 
01342                     cpl_bivector_unwrap_vectors(ccd);
01343                     cpl_vector_unwrap(xccd);
01344                     cpl_vector_unwrap(yccd);
01345                     cpl_vector_unwrap(ci);
01346                 }
01347                 else {
01348                     xmask = cpl_vector_wrap(npoints,
01349                                      cpl_table_get_data_double(dummy, "xmask"));
01350                     ymask = cpl_vector_wrap(npoints,
01351                                      cpl_table_get_data_double(dummy, "ymask"));
01352                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01353 
01354 /* %%% */
01355                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01356 
01357                     cpl_bivector_unwrap_vectors(mask);
01358                     cpl_vector_unwrap(xmask);
01359                     cpl_vector_unwrap(ymask);
01360                     cpl_vector_unwrap(ci);
01361                 }
01362             }
01363             else {
01364                 int p[2] = {0, 0};
01365                 poly = cpl_polynomial_new(2);
01366                 cpl_polynomial_set_coeff(poly, p, 
01367                                cpl_table_get_column_median(dummy, clab[j]));
01368             }
01369 
01370             cpl_table_delete(dummy);
01371 
01372             global = write_global_distortion(global, j, poly);
01373 
01374             cpl_polynomial_delete(poly);
01375 
01376             cpl_table_select_all(coeff);
01377         }
01378     }
01379 
01380 /* *+
01381 printf("error5: %s\n", cpl_error_get_message());
01382 +* */
01383     cpl_table_delete(coeff);
01384 /* *+
01385 printf("error6: %s\n", cpl_error_get_message());
01386 +* */
01387 
01388 
01389     /*
01390      * Add model's reference wavelength
01391      */
01392 
01393     cpl_table_set_double(global, "a00", 6, reference);
01394 
01395 
01396     /*
01397      * Global curvature model
01398      */
01399 
01400     coeff = cpl_table_duplicate(crv);
01401     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01402     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01403     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01404     npoints = cpl_table_get_nrow(coeff);
01405 
01406 /* *+
01407 printf("error7: %s\n", cpl_error_get_message());
01408 +* */
01409     for (i = 0; i < npoints; i++) {
01410         for (j = 0; j < nmaskslits; j++) {
01411             if (slit_id[i] == mslit_id[j]) {
01412                 if (i%2) {
01413                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01414                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01415                 }
01416                 else {
01417                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01418                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01419                 }
01420             }
01421         }
01422     }
01423 
01424 /* *+
01425 printf("error8: %s\n", cpl_error_get_message());
01426 +* */
01427     if (cpl_table_has_invalid(coeff, "xmask")) {
01428         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01429         cpl_table_delete(coeff);
01430         return NULL;
01431     }
01432 
01433 /* *+
01434 printf("error9: %s\n", cpl_error_get_message());
01435 +* */
01436     for (j = 0; j < 3; j++) {
01437         if (cpl_table_has_column(coeff, clab[j])) {
01438             cpl_table_and_selected_invalid(coeff, clab[j]);
01439 
01440             if (cpl_table_not_selected(coeff))
01441                 dummy = cpl_table_extract_selected(coeff);
01442             else
01443                 break;
01444 
01445             npoints = cpl_table_get_nrow(dummy);
01446 
01447             if (npoints >= 6) {
01448 
01449                 if (npoints >= 12)
01450                     order = 2;
01451                 else
01452                     order = 1;
01453 
01454                 ci = cpl_vector_wrap(npoints,
01455                                      cpl_table_get_data_double(dummy, clab[j]));
01456                 xmask = cpl_vector_wrap(npoints,
01457                                      cpl_table_get_data_double(dummy, "xmask"));
01458                 ymask = cpl_vector_wrap(npoints,
01459                                      cpl_table_get_data_double(dummy, "ymask"));
01460                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01461 
01462                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01463 
01464                 cpl_bivector_unwrap_vectors(mask);
01465                 cpl_vector_unwrap(ci);
01466                 cpl_vector_unwrap(xmask);
01467                 cpl_vector_unwrap(ymask);
01468             }
01469             else {
01470                 int p[2] = {0, 0};
01471                 poly = cpl_polynomial_new(2);
01472                 cpl_polynomial_set_coeff(poly, p,
01473                                cpl_table_get_column_median(dummy, clab[j]));
01474             }
01475 
01476             cpl_table_delete(dummy);
01477 
01478             global = write_global_distortion(global, j + 7, poly);
01479 
01480             cpl_polynomial_delete(poly);
01481             cpl_table_select_all(coeff);
01482         }
01483     }
01484 
01485 /* *+
01486 printf("error10: %s\n", cpl_error_get_message());
01487 +* */
01488     cpl_table_delete(coeff);
01489 /* *+
01490 printf("error11: %s\n", cpl_error_get_message());
01491 +* */
01492 
01493     return global;
01494 
01495 }
01496 
01497 
01535 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01536                                    int ysize)
01537 {
01538     const char *func = "mos_build_slit_location";
01539 
01540     cpl_propertylist *sort_col;
01541     cpl_polynomial   *ids0;
01542     cpl_polynomial   *crv[3];
01543     cpl_polynomial   *loc_crv;
01544     cpl_vector       *point;
01545     cpl_table        *slits;
01546     int               nslits;
01547     int              *slit_id;
01548     double           *dpoint;
01549     double           *xtop;
01550     double           *ytop;
01551     double           *xbottom;
01552     double           *ybottom;
01553     double           *mxtop;
01554     double           *mytop;
01555     double           *mxbottom;
01556     double           *mybottom;
01557     int               i, j;
01558 
01559 
01560     if (global == NULL || maskslits == NULL) {
01561         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01562         return NULL;
01563     }
01564 
01565     nslits   = cpl_table_get_nrow(maskslits);
01566     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01567     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01568     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01569     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01570     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01571 
01572     slits = cpl_table_duplicate(maskslits);
01573 
01574     xtop    = cpl_table_get_data_double(slits, "xtop");
01575     ytop    = cpl_table_get_data_double(slits, "ytop");
01576     xbottom = cpl_table_get_data_double(slits, "xbottom");
01577     ybottom = cpl_table_get_data_double(slits, "ybottom");
01578 
01579     ids0 = read_global_distortion(global, 0);
01580     crv[0] = read_global_distortion(global, 7);
01581     crv[1] = read_global_distortion(global, 8);
01582     crv[2] = read_global_distortion(global, 9);
01583 
01584     loc_crv = cpl_polynomial_new(1);
01585 
01586     point = cpl_vector_new(2);
01587     dpoint = cpl_vector_get_data(point);
01588 
01589     for (i = 0; i < nslits; i++) {
01590         dpoint[0] = mxtop[i];
01591         dpoint[1] = mytop[i];
01592 
01593         xtop[i] = cpl_polynomial_eval(ids0, point);
01594 
01595         for (j = 0; j < 3; j++)
01596             if (crv[j])
01597                 cpl_polynomial_set_coeff(loc_crv, &j, 
01598                                          cpl_polynomial_eval(crv[j], point));
01599 
01600         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01601 
01602         dpoint[0] = mxbottom[i];
01603         dpoint[1] = mybottom[i];
01604         xbottom[i] = cpl_polynomial_eval(ids0, point);
01605 
01606         for (j = 0; j < 3; j++)
01607             if (crv[j])
01608                 cpl_polynomial_set_coeff(loc_crv, &j,
01609                                          cpl_polynomial_eval(crv[j], point));
01610 
01611         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01612     }
01613 
01614     cpl_vector_delete(point);
01615     cpl_polynomial_delete(ids0);
01616     cpl_polynomial_delete(loc_crv);
01617     for (j = 0; j < 3; j++)
01618         cpl_polynomial_delete(crv[j]);
01619 
01620     sort_col = cpl_propertylist_new();
01621     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01622     cpl_table_sort(slits, sort_col);
01623     cpl_table_sort(maskslits, sort_col);
01624     cpl_propertylist_delete(sort_col);
01625 
01626     /*
01627      * Eliminate slits which are _entirely_ outside the CCD
01628      */
01629 
01630     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01631     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01632     cpl_table_erase_selected(slits);
01633 
01634     nslits = cpl_table_get_nrow(slits);
01635 
01636     if (nslits == 0) {
01637         cpl_msg_warning(func, "No slits found on the CCD");
01638         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01639         cpl_table_delete(slits);
01640         return NULL;
01641     }
01642 
01643     if (nslits > 1)
01644         cpl_msg_info(func, "Slit location: %d slits are entirely or partially "
01645                      "contained in CCD", nslits);
01646     else
01647         cpl_msg_info(func, "Slit location: %d slit is entirely or partially "
01648                      "contained in CCD", nslits);
01649 
01650     return slits;
01651 
01652 }
01653 
01654 
01681 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01682                                 cpl_table *slits)
01683 {
01684     const char *func = "mos_build_curv_coeff";
01685 
01686     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01687                                                  /* Max order is 5 */
01688 
01689     cpl_polynomial *crv[3];
01690     cpl_vector     *point;
01691     cpl_table      *polytraces;
01692     double         *dpoint;
01693     double         *xtop;
01694     double         *ytop;
01695     double         *xbottom;
01696     double         *ybottom;
01697     int            *slit_id;
01698     int            *valid_id;
01699     int             nslits, nvalid;
01700     int             found;
01701     int             i, j, k;
01702 
01703 
01704     if (global == NULL || slits == NULL || maskslits == NULL) {
01705         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01706         return NULL;
01707     }
01708 
01709     nslits  = cpl_table_get_nrow(maskslits);
01710     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01711     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01712     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01713     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01714     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01715 
01716     polytraces = cpl_table_new(2*nslits);
01717     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01718     for (i = 0; i < 3; i++)
01719         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01720 
01721     crv[0] = read_global_distortion(global, 7);
01722     crv[1] = read_global_distortion(global, 8);
01723     crv[2] = read_global_distortion(global, 9);
01724 
01725     point = cpl_vector_new(2);
01726     dpoint = cpl_vector_get_data(point);
01727 
01728     for (i = 0; i < nslits; i++) {
01729         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01730 
01731             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01732 
01733             if (j) {
01734                 dpoint[0] = xbottom[i];
01735                 dpoint[1] = ybottom[i];                
01736             }
01737             else {
01738                 dpoint[0] = xtop[i];
01739                 dpoint[1] = ytop[i];                
01740             }
01741 
01742             for (k = 0; k < 3; k++)
01743                 if (crv[j])
01744                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01745                                          cpl_polynomial_eval(crv[k], point));
01746         }
01747     }
01748 
01749     cpl_vector_delete(point);
01750     for (j = 0; j < 3; j++)
01751         cpl_polynomial_delete(crv[j]);
01752 
01753     /*
01754      * Eliminate slits which are _entirely_ outside the CCD
01755      */
01756  
01757     nvalid  = cpl_table_get_nrow(slits);
01758     valid_id = cpl_table_get_data_int(slits, "slit_id");
01759     cpl_table_unselect_all(polytraces);
01760     for (i = 0; i < nslits; i++) {
01761         found = 0;
01762         for (j = 0; j < nvalid; j++) {
01763             if (slit_id[i] == valid_id[j]) {
01764                 found = 1;
01765                 break;
01766             }
01767         }
01768         if (!found) {
01769             cpl_table_select_row(polytraces, 2*i);
01770             cpl_table_select_row(polytraces, 2*i + 1);
01771         }
01772     }
01773     cpl_table_erase_selected(polytraces);
01774  
01775     nslits = cpl_table_get_nrow(polytraces);
01776 
01777     if (nslits == 0) {
01778         cpl_msg_warning(func, "No slits found on the CCD");
01779         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01780         cpl_table_delete(polytraces);
01781         return NULL;
01782     }
01783 
01784     if (nslits > 2) 
01785         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01786                      "partially contained in CCD", nslits / 2);
01787     else
01788         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01789                      "partially contained in CCD", nslits / 2);
01790 
01791     return polytraces;
01792 }
01793 
01794 
01836 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01837 {
01838     const char *func = "mos_build_disp_coeff";
01839 
01840     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01841 
01842     cpl_polynomial *ids[6];
01843     cpl_vector     *point;
01844     cpl_table      *idscoeff;
01845     double         *dpoint;
01846     double         *xtop;
01847     double         *ytop;
01848     double         *xbottom;
01849     double         *ybottom;
01850     int            *position;
01851     int            *length;
01852     int             nslits;
01853     int             nrows;
01854     int             order;
01855     int             ylow, yhig;
01856     int             i, j, k;
01857 
01858 
01859     if (global == NULL || slits == NULL) {
01860         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01861         return NULL;
01862     }
01863     
01864     nslits   = cpl_table_get_nrow(slits);
01865     position = cpl_table_get_data_int(slits, "position");
01866     length   = cpl_table_get_data_int(slits, "length");
01867     xtop     = cpl_table_get_data_double(slits, "xtop");
01868     ytop     = cpl_table_get_data_double(slits, "ytop");
01869     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01870     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01871 
01872     for (i = 0; i < 6; i++)
01873         ids[i] = read_global_distortion(global, i);
01874 
01875     for (i = 0; i < 6; i++)
01876         if (ids[i] == NULL)
01877             break;
01878 
01879     order = i - 1;
01880 
01881     if (order < 1) {
01882         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01883         return NULL;
01884     }
01885 
01886     nrows = 0;
01887     for (i = 0; i < nslits; i++)
01888         nrows += length[i]; 
01889 
01890     idscoeff = cpl_table_new(nrows);
01891 
01892     for (j = 0; j <= order; j++)
01893         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01894 
01895     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01896     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01897     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01898     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01899 
01900     point = cpl_vector_new(2);
01901     dpoint = cpl_vector_get_data(point);
01902 
01903     for (i = 0; i < nslits; i++) {
01904 
01905         if (length[i] == 0)
01906             continue;
01907 
01908         ylow = position[i];
01909         yhig = ylow + length[i];
01910 
01911         for (j = 0; j <= order; j++) {
01912             if (j) {
01913                 for (k = 0; k < length[i]; k++) {
01914                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01915                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01916                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01917                                          cpl_polynomial_eval(ids[j], point));
01918                 }
01919             }
01920             else {
01921                 for (k = 0; k < length[i]; k++) {
01922                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01923                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01924                 }
01925             }
01926         }
01927     }
01928 
01929     cpl_vector_delete(point);
01930     for (j = 0; j < 6; j++)
01931         cpl_polynomial_delete(ids[j]);
01932 
01933     return idscoeff;
01934 
01935 }
01936 
01937 
01960 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01961                             cpl_table *polytraces, double reference, 
01962                             double blue, double red, double dispersion)
01963 {
01964     const char     *func = "mos_subtract_sky";
01965 
01966     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01967                                                  /* Max order is 5 */
01968 
01969     cpl_image      *sky;
01970     cpl_bivector   *list;
01971     cpl_vector     *listx;
01972     cpl_vector     *listy;
01973     cpl_polynomial *polytop;
01974     cpl_polynomial *polybot;
01975     cpl_polynomial *trend;
01976 
01977     int            *slit_id;
01978     double         *dlistx;
01979     double         *dlisty;
01980     float          *sdata;
01981     float          *kdata;
01982     double          top, bot;
01983     int             itop, ibot;
01984     double          coeff;
01985     double          ytop, ybot;
01986     double          m, q, err;
01987     int             npix;
01988 
01989     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
01990     int             nx, ny;
01991     int             nslits;
01992     int            *length;
01993     int             missing_top, missing_bot;
01994     int             order;
01995     int             null;
01996     int             window = 50;  /* Longer slits have polynomial sky model */
01997     int             count;
01998     int             i, j, k;
01999 
02000 
02001     if (science == NULL || slits == NULL || polytraces == NULL) {
02002         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02003         return NULL;
02004     }
02005  
02006     if (dispersion <= 0.0) {
02007         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02008         return NULL;
02009     }
02010 
02011     if (red - blue < dispersion) {
02012         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02013         return NULL;
02014     }
02015 
02016     nx = cpl_image_get_size_x(science);
02017     ny = cpl_image_get_size_y(science);
02018 
02019     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02020 
02021     sdata = cpl_image_get_data(science);
02022     kdata = cpl_image_get_data(sky);
02023 
02024     nslits   = cpl_table_get_nrow(slits);
02025     order    = cpl_table_get_ncol(polytraces) - 2;
02026     length   = cpl_table_get_data_int(slits, "length");
02027     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02028 
02029     /*
02030      * The spatial resampling is performed for a certain number of
02031      * pixels above and below the position of the reference wavelength:
02032      */
02033     
02034     pixel_above = (red - reference) / dispersion;
02035     pixel_below = (reference - blue) / dispersion;
02036 
02037     for (i = 0; i < nslits; i++) {
02038 
02039         if (length[i] == 0)
02040             continue;
02041 
02042         
02043         /*
02044          * Recover from the table of spectral curvature coefficients
02045          * the curvature polynomials.
02046          */
02047 
02048         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02049 
02050         start_pixel = refpixel - pixel_below;
02051         if (start_pixel < 0)
02052             start_pixel = 0;
02053 
02054         end_pixel = refpixel + pixel_above;
02055         if (end_pixel > nx)
02056             end_pixel = nx;
02057 
02058         missing_top = 0;
02059         polytop = cpl_polynomial_new(1);
02060         for (k = 0; k <= order; k++) {
02061             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02062             if (null) {
02063                 cpl_polynomial_delete(polytop);
02064                 missing_top = 1;
02065                 break;
02066             }
02067             cpl_polynomial_set_coeff(polytop, &k, coeff);
02068         }
02069 
02070         missing_bot = 0;
02071         polybot = cpl_polynomial_new(1);
02072         for (k = 0; k <= order; k++) {
02073             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02074             if (null) {
02075                 cpl_polynomial_delete(polybot);
02076                 missing_bot = 1;
02077                 break;
02078             }
02079             cpl_polynomial_set_coeff(polybot, &k, coeff);
02080         }
02081 
02082         if (missing_top && missing_bot) {
02083             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02084                           slit_id[i]);
02085             continue;
02086         }
02087 
02088         /*
02089          * In case just one of the two edges was not traced, the other
02090          * edge curvature model is duplicated and shifted to the other
02091          * end of the slit: better than nothing!
02092          */
02093 
02094         if (missing_top) {
02095             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02096                           "the spectral curvature of the lower edge "
02097                           "is used instead.", slit_id[i]);
02098             polytop = cpl_polynomial_duplicate(polybot);
02099             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02100             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02101             k = 0;
02102             coeff = cpl_polynomial_get_coeff(polybot, &k);
02103             coeff += ytop - ybot;
02104             cpl_polynomial_set_coeff(polytop, &k, coeff);
02105         }
02106 
02107         if (missing_bot) {
02108             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02109                           "the spectral curvature of the upper edge "
02110                           "is used instead.", slit_id[i]);
02111             polybot = cpl_polynomial_duplicate(polytop);
02112             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02113             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02114             k = 0;
02115             coeff = cpl_polynomial_get_coeff(polytop, &k);
02116             coeff -= ytop - ybot;
02117             cpl_polynomial_set_coeff(polybot, &k, coeff);
02118         }
02119 
02120 
02121         /*
02122          * Now read pixel values along spatial direction, and fit them.
02123          */
02124 
02125         for (j = start_pixel; j < end_pixel; j++) {
02126             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02127             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02128             itop = floor(top + 0.5) + 1;
02129             ibot = floor(bot + 0.5);
02130             if (itop > ny)
02131                 itop = ny;
02132             if (ibot < 0)
02133                 ibot = 0;
02134             npix = itop - ibot;
02135             if (npix < 5)
02136                 break;
02137 
02138             list = cpl_bivector_new(npix);
02139             listx = cpl_bivector_get_x(list);
02140             listy = cpl_bivector_get_y(list);
02141             dlistx = cpl_vector_get_data(listx);
02142             dlisty = cpl_vector_get_data(listy);
02143 
02144             for (k = 0; k < npix; k++) {
02145                 dlistx[k] = k;
02146                 dlisty[k] = sdata[j + (ibot + k)*nx];
02147             }
02148 
02149             if (robustLinearFit(list, &q, &m, &err)) {
02150                 cpl_bivector_delete(list);
02151                 continue;
02152             }
02153 
02154             cpl_bivector_delete(list);
02155 
02156             for (k = 0; k < npix; k++) {
02157                 kdata[j + (ibot + k)*nx] = m*k + q;
02158             }
02159 
02160             if (npix > window) {
02161 
02162                 /*
02163                  * Polynomial iteration
02164                  */
02165 
02166                 err = 3*sqrt(err);
02167 
02168                 count = 0;
02169                 for (k = 0; k < npix; k++)
02170                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02171                         count++;
02172 
02173                 if (count < 10)
02174                     continue;
02175 
02176                 list = cpl_bivector_new(count);
02177                 listx = cpl_bivector_get_x(list);
02178                 listy = cpl_bivector_get_y(list);
02179                 dlistx = cpl_vector_get_data(listx);
02180                 dlisty = cpl_vector_get_data(listy);
02181 
02182                 count = 0;
02183                 for (k = 0; k < npix; k++) {
02184                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02185                         dlistx[count] = k;
02186                         dlisty[count] = sdata[j + (ibot + k)*nx];
02187                         count++;
02188                     }
02189                 }
02190 
02191                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02192  
02193                 cpl_bivector_delete(list);
02194 
02195                 err = 3*sqrt(err);
02196 
02197                 count = 0;
02198                 for (k = 0; k < npix; k++)
02199                     if (fabs(sdata[j + (ibot + k)*nx] 
02200                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02201                         count++;
02202 
02203                 if (count < 10) {
02204                     cpl_polynomial_delete(trend);
02205                     continue;
02206                 }
02207 
02208                 list = cpl_bivector_new(count);
02209                 listx = cpl_bivector_get_x(list);
02210                 listy = cpl_bivector_get_y(list);
02211                 dlistx = cpl_vector_get_data(listx);
02212                 dlisty = cpl_vector_get_data(listy);
02213 
02214                 count = 0;
02215                 for (k = 0; k < npix; k++) {
02216                     if (fabs(sdata[j + (ibot + k)*nx] 
02217                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02218                         dlistx[count] = k;
02219                         dlisty[count] = sdata[j + (ibot + k)*nx];
02220                         count++;
02221                     }
02222                 }
02223 
02224                 cpl_polynomial_delete(trend);
02225 
02226                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02227 
02228                 cpl_bivector_delete(list);
02229  
02230                 for (k = 0; k < npix; k++) {
02231                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02232                                                k, NULL);
02233                 }
02234 
02235                 cpl_polynomial_delete(trend);
02236             }
02237         }
02238         cpl_polynomial_delete(polytop);
02239         cpl_polynomial_delete(polybot);
02240     }
02241 
02242     cpl_image_subtract(science, sky);
02243 
02244     return sky;
02245 }
02246 
02247 
02280 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02281                               cpl_table *slits, cpl_table *polytraces, 
02282                               double reference, double blue, double red, 
02283                               double dispersion, int sradius, int polyorder)
02284 {
02285     const char     *func = "mos_normalise_flat";
02286 
02287     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02288                                                  /* Max order is 5 */
02289 
02290     cpl_image      *rectified;
02291     cpl_image      *smo_flat;
02292     cpl_image      *exslit;
02293     cpl_vector     *positions;
02294     cpl_vector     *flux;
02295     cpl_vector     *smo_flux;
02296     cpl_polynomial *trend;
02297     cpl_polynomial *polytop;
02298     cpl_polynomial *polybot;
02299 
02300     int            *slit_id;
02301     float          *p;
02302     float          *data;
02303     double         *fdata;
02304     double         *pdata;
02305     float          *sdata;
02306     float          *xdata;
02307     float          *wdata;
02308     double          vtop, vbot, value;
02309     double          top, bot;
02310     double          coeff;
02311     double          ytop, ybot;
02312     double          ypos;
02313     double          fvalue;
02314     int             ivalue;
02315     int             yint, yprev;
02316     int             npseudo;
02317 
02318     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02319     int             nx, ny, nsubx, nsuby;
02320     int             xlow, ylow, xhig, yhig;
02321     int             nslits;
02322     int            *position;
02323     int            *length;
02324     int             missing_top, missing_bot;
02325     int             order;
02326     int             npoints;
02327     int             uradius;
02328     int             null;
02329     int             i, j, k;
02330 
02331 /*    int             exclude = 5;     Number of excluded pixels at edges */
02332 
02333     /* For debug puposes only: cpl_image      *smo_rectified; */
02334 
02335 
02336     if (flat == NULL || slits == NULL || polytraces == NULL) {
02337         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02338         return NULL;
02339     }
02340  
02341     if (dispersion <= 0.0) {
02342         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02343         return NULL;
02344     }
02345 
02346     if (red - blue < dispersion) {
02347         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02348         return NULL;
02349     }
02350 
02351     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02352                                         blue, red, dispersion, 0, NULL);
02353 
02354     nx = cpl_image_get_size_x(rectified);
02355     ny = cpl_image_get_size_y(rectified);
02356 
02357     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02358                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02359     wdata = cpl_image_get_data(smo_flat);
02360 
02361     nslits   = cpl_table_get_nrow(slits);
02362     order    = cpl_table_get_ncol(polytraces) - 2;
02363     position = cpl_table_get_data_int(slits, "position");
02364     length   = cpl_table_get_data_int(slits, "length");
02365     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02366 
02367     /*
02368      * The spatial resampling is performed for a certain number of
02369      * pixels above and below the position of the reference wavelength:
02370      */
02371     
02372     pixel_above = (red - reference) / dispersion;
02373     pixel_below = (reference - blue) / dispersion;
02374 
02375     xlow = 1;
02376     xhig = nx;
02377     for (i = 0; i < nslits; i++) {
02378 
02379         if (length[i] == 0)
02380             continue;
02381 
02382         /*
02383          * We DON'T write:
02384          *
02385          * ylow = position[i];
02386          * yhig = ylow + length[i];
02387          *
02388          * because the cpl_image pixels are counted from 1, and because in 
02389          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02390          */
02391 
02392         ylow = position[i] + 1;
02393         yhig = ylow + length[i] - 1;
02394 
02395         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02396 
02397         if (polyorder < 0) {
02398 
02399             cpl_image_turn(exslit, -1);   /* For faster memory access */
02400     
02401             nsubx = cpl_image_get_size_x(exslit);
02402             nsuby = cpl_image_get_size_y(exslit);
02403             data = cpl_image_get_data(exslit);
02404             flux = cpl_vector_new(nsubx);
02405 
02406             uradius = nsubx / 2;
02407             if (uradius > sradius)
02408                 uradius = sradius;
02409 
02410             for (j = 0; j < nsuby; j++) {
02411                 fdata = cpl_vector_get_data(flux);
02412                 p = data;
02413                 for (k = 0; k < nsubx; k++)
02414                     *fdata++ = *p++;
02415                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02416                 fdata = cpl_vector_get_data(smo_flux);
02417                 p = data;
02418                 for (k = 0; k < nsubx; k++)
02419                     *p++ = *fdata++;
02420                 cpl_vector_delete(smo_flux);
02421                 data += nsubx;
02422             }
02423 
02424             cpl_vector_delete(flux);
02425 
02426 
02427             /*
02428              * First fit fluxes along the spatial direction with a low-degree
02429              * polynomial (excluding the first and the last pixels, close to
02430              * the edges)
02431              */
02432 /*
02433             if (nsubx-2*exclude > 10) {
02434                 flux = cpl_vector_new(nsubx-2*exclude);
02435                 fdata = cpl_vector_get_data(flux);
02436                 positions = cpl_vector_new(nsubx-2*exclude);
02437                 for (j = 0; j < nsubx-2*exclude; j++)
02438                     cpl_vector_set(positions, j, j+exclude);
02439         
02440                 for (k = 0; k < nsuby; k++) {
02441                     for (j = 0; j < nsubx-2*exclude; j++)
02442                         fdata[j] = data[j+exclude];
02443                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02444                                                          1, NULL);
02445                     for (j = 0; j < nsubx; j++)
02446                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02447                     cpl_polynomial_delete(trend);
02448                     data += nsubx;
02449                 }
02450 
02451                 cpl_vector_delete(flux);
02452                 cpl_vector_delete(positions);
02453             }
02454 */
02455 
02456             /*
02457              * Now smooth along the dispersion direction 
02458              */
02459 
02460             cpl_image_turn(exslit, 1);   /* For faster memory access */
02461             nsubx = cpl_image_get_size_x(exslit);
02462             nsuby = cpl_image_get_size_y(exslit);
02463             data = cpl_image_get_data(exslit);
02464 
02465             for (j = 0; j < nsuby; j++) {
02466                 flux = cpl_vector_new(nsubx);
02467                 fdata = cpl_vector_get_data(flux);
02468                 p = data;
02469                 for (k = 0; k < nsubx; k++)
02470                     *fdata++ = *p++;
02471                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02472                 cpl_vector_delete(flux);
02473                 fdata = cpl_vector_get_data(smo_flux);
02474                 p = data;
02475                 for (k = 0; k < nsubx; k++)
02476                     *p++ = *fdata++;
02477                 cpl_vector_delete(smo_flux);
02478                 data += nsubx;
02479             }
02480         }
02481         else {
02482 
02483             /*
02484              * Fit with a polynomial the flat field trend row by row.
02485              */
02486 
02487             nsubx = cpl_image_get_size_x(exslit);
02488             nsuby = cpl_image_get_size_y(exslit);
02489             data = cpl_image_get_data(exslit);
02490 
02491             for (j = 0; j < nsuby; j++) {
02492 
02493                 /*
02494                  * First get the size of the vectors to allocate:
02495                  */
02496 
02497                 npoints = 0;
02498                 p = data + j*nsubx;
02499                 for (k = 0; k < nsubx; k++)
02500                     if (p[k] > 1.0)
02501                         npoints++;
02502 
02503                 if (npoints > polyorder + 1) {
02504 
02505                     /*
02506                      * Fill the vectors for the fitting
02507                      */
02508 
02509                     flux = cpl_vector_new(npoints);
02510                     fdata = cpl_vector_get_data(flux);
02511                     positions = cpl_vector_new(npoints);
02512                     pdata = cpl_vector_get_data(positions);
02513 
02514                     npoints = 0;
02515                     p = data + j*nsubx;
02516                     for (k = 0; k < nsubx; k++) {
02517                         if (p[k] > 1.0) {
02518                             fdata[npoints] = p[k];
02519                             pdata[npoints] = k;
02520                             npoints++;
02521                         }
02522                     }
02523     
02524                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02525                                                          polyorder, NULL);
02526 
02527                     cpl_vector_delete(flux);
02528                     cpl_vector_delete(positions);
02529 
02530                     if (trend) {
02531                         p = data + j*nsubx;
02532                         for (k = 0; k < nsubx; k++)
02533                             if (p[k] > 1.0)
02534                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02535                         cpl_polynomial_delete(trend);
02536                     }
02537                     else {
02538                         cpl_msg_warning(func, "Invalid flat field flux fit "
02539                                         "(ignored)");
02540                     }
02541                 }
02542             }
02543         }
02544 
02545         
02546         /*
02547          * Recover from the table of spectral curvature coefficients
02548          * the curvature polynomials.
02549          */
02550 
02551         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02552 
02553         start_pixel = refpixel - pixel_below;
02554         if (start_pixel < 0)
02555             start_pixel = 0;
02556 
02557         end_pixel = refpixel + pixel_above;
02558         if (end_pixel > nx)
02559             end_pixel = nx;
02560 
02561         missing_top = 0;
02562         polytop = cpl_polynomial_new(1);
02563         for (k = 0; k <= order; k++) {
02564             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02565             if (null) {
02566                 cpl_polynomial_delete(polytop);
02567                 missing_top = 1;
02568                 break;
02569             }
02570             cpl_polynomial_set_coeff(polytop, &k, coeff);
02571         }
02572 
02573         missing_bot = 0;
02574         polybot = cpl_polynomial_new(1);
02575         for (k = 0; k <= order; k++) {
02576             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02577             if (null) {
02578                 cpl_polynomial_delete(polybot);
02579                 missing_bot = 1;
02580                 break;
02581             }
02582             cpl_polynomial_set_coeff(polybot, &k, coeff);
02583         }
02584 
02585         if (missing_top && missing_bot) {
02586             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02587                           slit_id[i]);
02588             continue;
02589         }
02590 
02591         /*
02592          * In case just one of the two edges was not traced, the other
02593          * edge curvature model is duplicated and shifted to the other
02594          * end of the slit: better than nothing!
02595          */
02596 
02597         if (missing_top) {
02598             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02599                           "the spectral curvature of the lower edge "
02600                           "is used instead.", slit_id[i]);
02601             polytop = cpl_polynomial_duplicate(polybot);
02602             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02603             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02604             k = 0;
02605             coeff = cpl_polynomial_get_coeff(polybot, &k);
02606             coeff += ytop - ybot;
02607             cpl_polynomial_set_coeff(polytop, &k, coeff);
02608         }
02609 
02610         if (missing_bot) {
02611             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02612                           "the spectral curvature of the upper edge "
02613                           "is used instead.", slit_id[i]);
02614             polybot = cpl_polynomial_duplicate(polytop);
02615             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02616             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02617             k = 0;
02618             coeff = cpl_polynomial_get_coeff(polytop, &k);
02619             coeff -= ytop - ybot;
02620             cpl_polynomial_set_coeff(polybot, &k, coeff);
02621         }
02622 
02623 
02624         /*
02625          * Now map smoothed image to CCD.
02626          * Note that the npseudo value related to this slit is equal
02627          * to the number of spatial pseudo-pixels decreased by 1
02628          * (compare with function mos_spatial_calibration()).
02629          */
02630 
02631         nx = cpl_image_get_size_x(flat);
02632         ny = cpl_image_get_size_y(flat);
02633 
02634         sdata = cpl_image_get_data(spatial);
02635         xdata = cpl_image_get_data(exslit);
02636         npseudo = cpl_image_get_size_y(exslit) - 1;
02637 
02638         /*
02639          * Write interpolated smoothed values to CCD image
02640          */
02641 
02642         for (j = start_pixel; j < end_pixel; j++) {
02643             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02644             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02645             for (k = 0; k <= npseudo; k++) {
02646                 ypos = top - k*(top-bot)/npseudo;
02647                 yint = ypos;
02648 
02649                 /*
02650                  * The line:
02651                  *     value = sdata[j + nx*yint];
02652                  * should be equivalent to:
02653                  *     value = npseudo*(top-yint)/(top-bot);
02654                  */
02655 
02656                 if (yint < 0 || yint >= ny-1) {
02657                     yprev = yint;
02658                     continue;
02659                 }
02660 
02661                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02662                 ivalue = value;               /* Nearest spatial pixels:   */
02663                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02664                 if (ivalue < npseudo && ivalue >= 0) {
02665                     vtop = xdata[j + nx*(npseudo-ivalue)];
02666                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02667                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02668 
02669                     if (k) {
02670 
02671                         /*
02672                          * This is added to recover lost pixels on
02673                          * the CCD image (pixels are lost because
02674                          * the CCD pixels are less than npseudo+1).
02675                          */
02676 
02677                         if (yprev - yint > 1) {
02678                             value = sdata[j + nx*(yint+1)];
02679                             ivalue = value;
02680                             fvalue = value - ivalue;
02681                             if (ivalue < npseudo && ivalue >= 0) {
02682                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02683                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02684                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02685                                                        + vbot*fvalue;
02686                             }
02687                         }
02688                     }
02689                 }
02690                 yprev = yint;
02691             }
02692         }
02693         cpl_polynomial_delete(polytop);
02694         cpl_polynomial_delete(polybot);
02695         cpl_image_delete(exslit);
02696     }
02697 
02698     cpl_image_delete(rectified);
02699 
02700     cpl_image_divide(flat, smo_flat);
02701 
02702     return smo_flat;
02703 }
02704 
02705 
02730 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02731                                   int polyorder)
02732 {
02733     const char     *func = "mos_normalise_longflat";
02734 
02735     cpl_image      *smo_flat;
02736     cpl_image      *profile;
02737     cpl_vector     *flux;
02738     cpl_vector     *smo_flux;
02739     cpl_vector     *positions;
02740     cpl_polynomial *trend;
02741 
02742     float          *level;
02743     float          *p;
02744     float          *data;
02745     double         *fdata;
02746     double         *pdata;
02747 
02748     int             nx, ny;
02749     int             npoints;
02750     int             i, j;
02751 
02752 
02753     if (flat == NULL) {
02754         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02755         return NULL;
02756     }
02757  
02758     if (sradius < 1 || dradius < 1) {
02759         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02760         return NULL;
02761     }
02762 
02763     smo_flat = cpl_image_duplicate(flat);
02764 
02765     if (polyorder < 0) {
02766 
02767         /*
02768          * First smooth along the spatial direction
02769          */
02770 
02771         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02772 
02773         nx = cpl_image_get_size_x(smo_flat);
02774         ny = cpl_image_get_size_y(smo_flat);
02775         data = cpl_image_get_data(smo_flat);
02776     
02777         for (i = 0; i < ny; i++) {
02778             flux = cpl_vector_new(nx);
02779             fdata = cpl_vector_get_data(flux);
02780             p = data;
02781             for (j = 0; j < nx; j++)
02782                 *fdata++ = *p++;
02783             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02784             cpl_vector_delete(flux);
02785             fdata = cpl_vector_get_data(smo_flux);
02786             p = data;
02787             for (j = 0; j < nx; j++)
02788                 *p++ = *fdata++;
02789             cpl_vector_delete(smo_flux);
02790             data += nx;
02791         }
02792 
02793         /*
02794          * Second smooth along the dispersion direction
02795          */
02796 
02797         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02798 
02799         nx = cpl_image_get_size_x(smo_flat);
02800         ny = cpl_image_get_size_y(smo_flat);
02801         data = cpl_image_get_data(smo_flat);
02802 
02803         for (i = 0; i < ny; i++) {
02804             flux = cpl_vector_new(nx);
02805             fdata = cpl_vector_get_data(flux);
02806             p = data;
02807             for (j = 0; j < nx; j++)
02808                 *fdata++ = *p++;
02809             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02810             cpl_vector_delete(flux);
02811             fdata = cpl_vector_get_data(smo_flux);
02812             p = data;
02813             for (j = 0; j < nx; j++)
02814                 *p++ = *fdata++;
02815             cpl_vector_delete(smo_flux);
02816             data += nx;
02817         }
02818     }
02819     else {
02820 
02821         /*
02822          * Fit with a polynomial the flat field trend column by column.
02823          */
02824 
02825         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02826 
02827         nx = cpl_image_get_size_x(smo_flat);
02828         ny = cpl_image_get_size_y(smo_flat);
02829         data = cpl_image_get_data(smo_flat);
02830 
02831         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02832         level = cpl_image_get_data(profile);
02833 
02834         for (i = 0; i < ny; i++) {
02835 
02836             /*
02837              * First get the size of the vectors to allocate:
02838              * eliminate from fit any value more than 20% away
02839              * from median level in current column.
02840              */
02841 
02842             npoints = 0;
02843             p = data + i*nx;
02844             for (j = 0; j < nx; j++)
02845                 if (fabs(p[j]/level[i] - 1) < 0.20)
02846                     npoints++;
02847 
02848             if (npoints > polyorder + 1) {
02849 
02850                 /*
02851                  * Fill the vectors for the fitting
02852                  */
02853 
02854                 flux = cpl_vector_new(npoints);
02855                 fdata = cpl_vector_get_data(flux);
02856                 positions = cpl_vector_new(npoints);
02857                 pdata = cpl_vector_get_data(positions);
02858 
02859                 npoints = 0;
02860                 p = data + i*nx;
02861                 for (j = 0; j < nx; j++) {
02862                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02863                         fdata[npoints] = p[j];
02864                         pdata[npoints] = j;
02865                         npoints++;
02866                     }
02867                 }
02868     
02869                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02870                                                      polyorder, NULL);
02871 
02872                 cpl_vector_delete(flux);
02873                 cpl_vector_delete(positions);
02874 
02875                 if (trend) {
02876                     p = data + i*nx;
02877                     for (j = 0; j < nx; j++)
02878                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02879                     cpl_polynomial_delete(trend);
02880                 }
02881                 else {
02882                     cpl_msg_warning(func, 
02883                                     "Invalid flat field flux fit (ignored)");
02884                 }
02885             }
02886         }
02887 
02888         cpl_image_delete(profile);
02889         cpl_image_turn(smo_flat, 1);
02890 
02891     }
02892 
02893     cpl_image_divide(flat, smo_flat);
02894 
02895     return smo_flat;
02896 }
02897 
02898 
02923 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
02924                                          cpl_image *wavemap, int mode)
02925 {
02926     const char *func = "mos_interpolate_wavecalib";
02927 
02928     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02929                                                  /* Max order is 5 */
02930 
02931     cpl_vector     *wave;
02932     cpl_vector     *positions;
02933     cpl_polynomial *trend;
02934 
02935     float          *p;
02936     float          *data;
02937     double         *wdata;
02938     double         *pdata;
02939 
02940     double          c;
02941     double          mse, ksigma;
02942 
02943     int             order;
02944     int             nrows, first_row, last_row;
02945     int             nx, ny;
02946     int             npoints, rpoints;
02947     int             null;
02948     int             i, j, k;
02949 
02950     int             polyorder = 4;  /* Candidate input argument */
02951 
02952 
02953     if (idscoeff == NULL)
02954         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02955 
02956     if (mode < 0 || mode > 2)
02957         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02958 
02959     if (mode == 0)
02960         return CPL_ERROR_NONE;
02961 
02962     if (wavemap) {
02963 
02964         /*
02965          * Fit with a polynomial the wavelength trend column by column.
02966          */
02967 
02968         cpl_image_turn(wavemap, -1);   /* For faster memory access */
02969 
02970         nx = cpl_image_get_size_x(wavemap);
02971         ny = cpl_image_get_size_y(wavemap);
02972         data = cpl_image_get_data(wavemap);
02973 
02974         for (i = 0; i < ny; i++) {
02975 
02976             /*
02977              * First get the size of the vectors to allocate:
02978              * eliminate from fit any value with "impossible" wavelength.
02979              */
02980 
02981             npoints = 0;
02982             p = data + i*nx;
02983             for (j = 0; j < nx; j++)
02984                 if (p[j] > 1.0)
02985                     npoints++;
02986 
02987             if (npoints > polyorder + 1) {
02988 
02989                 /*
02990                  * Fill the vectors for the fitting
02991                  */
02992 
02993                 wave = cpl_vector_new(npoints);
02994                 wdata = cpl_vector_get_data(wave);
02995                 positions = cpl_vector_new(npoints);
02996                 pdata = cpl_vector_get_data(positions);
02997 
02998                 npoints = 0;
02999                 p = data + i*nx;
03000                 for (j = 0; j < nx; j++) {
03001                     if (p[j] > 1.0) {
03002                         wdata[npoints] = p[j];
03003                         pdata[npoints] = j;
03004                         npoints++;
03005                     }
03006                 }
03007     
03008                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03009                                                      polyorder, &mse);
03010 
03011                 ksigma = 3*sqrt(mse);
03012 
03013                 cpl_vector_delete(wave);
03014                 cpl_vector_delete(positions);
03015 
03016                 if (trend) {
03017 
03018                     /*
03019                      * Apply 3-sigma rejection
03020                      */
03021 
03022                     rpoints = 0;
03023                     p = data + i*nx;
03024                     for (j = 0; j < nx; j++)
03025                         if (p[j] > 1.0)
03026                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03027                                                     - p[j]) < ksigma)
03028                                 rpoints++;
03029 
03030                     if (rpoints < npoints && rpoints > polyorder + 1) {
03031 
03032                         wave = cpl_vector_new(rpoints);
03033                         wdata = cpl_vector_get_data(wave);
03034                         positions = cpl_vector_new(rpoints);
03035                         pdata = cpl_vector_get_data(positions);
03036 
03037                         npoints = 0;
03038                         p = data + i*nx;
03039                         for (j = 0; j < nx; j++) {
03040                             if (p[j] > 1.0) {
03041                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03042                                                                 j, NULL) - p[j])
03043                                                                 < ksigma) {
03044                                     wdata[npoints] = p[j];
03045                                     pdata[npoints] = j;
03046                                     npoints++;
03047                                 }
03048                             }
03049                         }
03050         
03051                         cpl_polynomial_delete(trend);
03052                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03053                                                              polyorder, NULL);
03054 
03055                         cpl_vector_delete(wave);
03056                         cpl_vector_delete(positions);
03057                     }
03058                 }
03059 
03060                 if (trend) {
03061                     p = data + i*nx;
03062                     if (mode == 1) {
03063                         for (j = 0; j < nx; j++)
03064                             if (p[j] < 1.0)
03065                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03066                     }
03067                     else if (mode == 2) {
03068                         for (j = 0; j < nx; j++)
03069                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03070                     }
03071                     cpl_polynomial_delete(trend);
03072                 }
03073                 else {
03074                     cpl_msg_warning(func, 
03075                                     "Invalid wavelength field fit (ignored)");
03076                 }
03077             }
03078     
03079         }
03080 
03081         cpl_image_turn(wavemap, 1);
03082 
03083     }
03084 
03085 
03086     /*
03087      * Interpolating the IDS coefficients
03088      */
03089 
03090     nrows = cpl_table_get_nrow(idscoeff);
03091 
03092     order = 0;
03093     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03094         ++order;
03095     --order;
03096 
03097     first_row = 0;
03098     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03099         first_row++;
03100 
03101     last_row = nrows - 1;
03102     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03103         last_row--;
03104 
03105     for (k = 0; k <= order; k++) {
03106 
03107         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03108         wave = cpl_vector_new(npoints);
03109         wdata = cpl_vector_get_data(wave);
03110         positions = cpl_vector_new(npoints);
03111         pdata = cpl_vector_get_data(positions);
03112 
03113         npoints = 0;
03114         for (i = first_row; i <= last_row; i++) {
03115             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03116             if (null == 0) {
03117                 wdata[npoints] = c;
03118                 pdata[npoints] = i;
03119                 npoints++;
03120             }
03121         }
03122 
03123         trend = cpl_polynomial_fit_1d_create(positions, wave, 2, &mse);
03124 
03125         ksigma = 3*sqrt(mse);
03126 
03127         cpl_vector_delete(wave);
03128         cpl_vector_delete(positions);
03129 
03130         /*
03131          * Iteration
03132          */
03133 
03134         if (trend) {
03135             rpoints = 0;
03136             for (i = first_row; i <= last_row; i++) {
03137                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03138                 if (null == 0) {
03139                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03140                                                                  < ksigma) {
03141                         rpoints++;
03142                     }
03143                 }
03144             }
03145 
03146             if (rpoints > 0 && rpoints < npoints) {
03147                 cpl_msg_debug(func, "%d points rejected from "
03148                               "wavelength calibration fit", 
03149                               npoints - rpoints);
03150 
03151                 wave = cpl_vector_new(rpoints);
03152                 wdata = cpl_vector_get_data(wave);
03153                 positions = cpl_vector_new(rpoints);
03154                 pdata = cpl_vector_get_data(positions);
03155 
03156                 npoints = 0;
03157                 for (i = first_row; i <= last_row; i++) {
03158                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03159                     if (null == 0) {
03160                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03161                                                                  < ksigma) {
03162                             wdata[npoints] = c;
03163                             pdata[npoints] = i;
03164                             npoints++;
03165                         }
03166                     }
03167                 }
03168 
03169                 if (npoints) {
03170                     cpl_polynomial_delete(trend);
03171                     trend = cpl_polynomial_fit_1d_create(positions, 
03172                                                          wave, 2, NULL);
03173                 }
03174 
03175                 cpl_vector_delete(wave);
03176                 cpl_vector_delete(positions);
03177 
03178             }
03179         }
03180 
03181         if (trend) {
03182             for (i = first_row; i <= last_row; i++) {
03183                 if (mode == 1) {
03184                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03185                         cpl_table_set_double(idscoeff, clab[k], i, 
03186                                              cpl_polynomial_eval_1d(trend, i,
03187                                                                     NULL));
03188                     }
03189                 }
03190                 else if (mode == 2) {
03191                     cpl_table_set_double(idscoeff, clab[k], i, 
03192                                      cpl_polynomial_eval_1d(trend, i, NULL));
03193                 }
03194             }
03195             cpl_polynomial_delete(trend);
03196         }
03197         else {
03198             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03199         }
03200 
03201     }
03202 
03203     return CPL_ERROR_NONE;
03204 }
03205 
03206 
03207 
03233 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03234                            cpl_table *overscans)
03235 {
03236     const char *func = "mos_remove_bias";
03237 
03238     cpl_image *unbiased;
03239     cpl_image *overscan;
03240     double     mean_bias_level;
03241     double     mean_overscans_level;
03242     int        count;
03243     int        nrows;
03244     int        xlow, ylow, xhig, yhig;
03245     int        i;
03246 
03247 
03248     if (image == NULL || overscans == NULL) {
03249         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03250         return NULL;
03251     }
03252 
03253     nrows = cpl_table_get_nrow(overscans);
03254 
03255     if (nrows == 0) {
03256         cpl_msg_error(func, "Empty overscan table");
03257         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03258         return NULL;
03259     }
03260 
03261     if (bias) {
03262         if (nrows == 1) {
03263             unbiased = cpl_image_subtract_create(image, bias);
03264             if (unbiased == NULL) {
03265                 cpl_msg_error(func, "Incompatible master bias");
03266                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03267             }
03268             return unbiased;
03269         }
03270         mean_bias_level = cpl_image_get_mean(bias);
03271     }
03272     else {
03273         if (nrows == 1) {
03274             cpl_msg_error(func, "No master bias in input, and no overscan "
03275                           "regions in input image: bias subtraction "
03276                           "cannot be performed!");
03277             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03278             return NULL;
03279         }
03280         mean_bias_level = 0.0;
03281     }
03282 
03283     mean_overscans_level = 0.0;
03284     count = 0;
03285     for (i = 0; i < nrows; i++) {
03286         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03287         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03288         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03289         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03290 
03291         if (i == 0) {
03292             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03293             if (unbiased == NULL) {
03294                 cpl_msg_error(func, "Incompatible overscan table");
03295                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03296                 return NULL;
03297             }
03298             if (bias) {
03299                 if (cpl_image_subtract(unbiased, bias)) {
03300                     cpl_msg_error(func, "Incompatible master bias");
03301                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03302                     cpl_image_delete(unbiased);
03303                     return NULL;
03304                 }
03305             }
03306         }
03307         else {
03308             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03309             if (overscan == NULL) {
03310                 cpl_msg_error(func, "Incompatible overscan table");
03311                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03312                 cpl_image_delete(unbiased);
03313                 return NULL;
03314             }
03315 
03316             mean_overscans_level += cpl_image_get_median(overscan);
03317             count++;
03318 
03319 /***
03320  * Here the mean level was used: not very robust...
03321 
03322             mean_overscans_level += cpl_image_get_flux(overscan);
03323             count += cpl_image_get_size_x(overscan)
03324                    * cpl_image_get_size_y(overscan);
03325 ***/
03326             cpl_image_delete(overscan);
03327         }
03328     }
03329 
03330     /*
03331      * Overscan correction
03332      */
03333 
03334     mean_overscans_level /= count;
03335 
03336     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03337 
03338     return unbiased;
03339 
03340 }
03341 
03342 
03401 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03402                                      int length, int msize, int fsize) 
03403 {
03404     const char *func = "mos_arc_background_1D";
03405 
03406     float  *minf;
03407     float  *maxf;
03408     float  *smof;
03409     int     i;
03410 
03411 
03412     if (spectrum == NULL || back == NULL)
03413         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03414 
03415     if (msize % 2 == 0)
03416         msize++;
03417 
03418     if (fsize % 2 == 0)
03419         fsize++;
03420 
03421     if (msize < 3 || fsize < msize || length < 2*fsize)
03422         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03423 
03424 
03425     minf = min_filter(spectrum, length, msize);
03426     smof = smo_filter(minf, length, fsize);
03427     cpl_free(minf);
03428     maxf = max_filter(smof, length, 2*msize+1);
03429     cpl_free(smof);
03430     smof = smo_filter(maxf, length, 2*fsize+1);
03431     cpl_free(maxf);
03432     minf = min_filter(smof, length, 2*msize+1);
03433     cpl_free(smof);
03434     smof = smo_filter(minf, length, 2*fsize+1);
03435     cpl_free(minf);
03436 
03437     for (i = 0; i < length; i++)
03438         back[i] = smof[i];
03439 
03440     cpl_free(smof);
03441 
03442     return CPL_ERROR_NONE;
03443 
03444 }
03445 
03446 
03503 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03504 {
03505     const char *func = "mos_arc_background";
03506 
03507     cpl_image  *fimage;
03508     cpl_image  *bimage;
03509     cpl_matrix *kernel;
03510     float      *data;
03511     float      *bdata;
03512     float      *row;
03513     float      *brow;
03514     int         nx, ny;
03515     int         i;
03516 
03517 
03518     if (image == NULL) {
03519         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03520         return NULL;
03521     }
03522 
03523     if (msize % 2 == 0)
03524         msize++;
03525 
03526     if (fsize % 2 == 0)
03527         fsize++;
03528 
03529     nx = cpl_image_get_size_x(image);
03530     ny = cpl_image_get_size_y(image);
03531 
03532     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03533 
03534     kernel = cpl_matrix_new(3, 3);
03535     cpl_matrix_fill(kernel, 1.0);
03536     fimage = cpl_image_filter_median(image, kernel);
03537     cpl_matrix_delete(kernel);
03538 
03539     data = cpl_image_get_data_float(fimage);
03540     bdata = cpl_image_get_data_float(bimage);
03541 
03542     for (i = 0; i < ny; i++) {
03543         row = data + i * nx;
03544         brow = bdata + i * nx;
03545         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03546             cpl_error_set_where(func); 
03547             cpl_image_delete(fimage);
03548             cpl_image_delete(bimage);
03549             return NULL;
03550         }
03551     }
03552 
03553     cpl_image_delete(fimage);
03554 
03555     return bimage;
03556 }
03557 
03558 
03579 int mos_lines_width(const float *spectrum, int length)
03580 {
03581 
03582   const char *func = "mos_lines_width";
03583 
03584   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03585   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03586 
03587   double  norm, value, max;
03588   int     radius = 20;
03589   int     short_length = length - 2*radius - 1;
03590   int     width;
03591   int     i, j, k;
03592 
03593 
03594   /*
03595    * Derivative, and separation of positive and negative derivatives
03596    */
03597 
03598   for (j = 0, i = 1; i < length; j++, i++) {
03599       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03600       if (profile1[j] < 0)
03601           profile1[j] = 0;
03602       if (profile2[j] > 0)
03603           profile2[j] = 0;
03604       else
03605           profile2[j] = -profile2[j];
03606   }
03607 
03608 
03609   /*
03610    * Profiles normalisation
03611    */
03612 
03613   length--;
03614 
03615   norm = 0;
03616   for (i = 0; i < length; i++)
03617       if (norm < profile1[i])
03618           norm = profile1[i];
03619 
03620   for (i = 0; i < length; i++) {
03621       profile1[i] /= norm;
03622       profile2[i] /= norm;
03623   }
03624 
03625 
03626   /*
03627    * Cross-correlation
03628    */
03629 
03630   max = -1;
03631   for (i = 0; i <= radius; i++) {
03632       value = 0;
03633       for (j = 0; j < short_length; j++) {
03634           k = radius+j;
03635           value += profile1[k] * profile2[k+i];
03636       }
03637       if (max < value) {
03638           max = value;
03639           width = i;
03640       }
03641   }
03642 
03643   cpl_free(profile1);
03644   cpl_free(profile2);
03645 
03646   if (max < 0.0) {
03647       cpl_msg_debug(func, "Cannot estimate line width");
03648       width = 1;
03649   }
03650 
03651   return width;
03652 
03653 }
03654 
03655 
03682 cpl_vector *mos_peak_candidates(const float *spectrum, 
03683                 int length, float level, 
03684                                 float exp_width)
03685 { 
03686 
03687   const char *func = "mos_peak_candidates";
03688 
03689   int     i, j;
03690   int     nint   = length - 1;
03691   int     n      = 0;
03692   int     width  = 2 * ceil(exp_width / 2) + 1;
03693   int     start  = width / 2;
03694   int     end    = length - width / 2;
03695   int     step;
03696   float  *smo;
03697   double *data   = cpl_calloc(length/2, sizeof(double));
03698 
03699 
03700   if (spectrum == NULL) {
03701       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03702       return NULL;
03703   }
03704 
03705 
03706   /*
03707    * If lines have a flat top (as in the case of broad slit), smooth
03708    * before determining the max.
03709    */
03710 
03711   if (width > 7) {
03712     smo = cpl_calloc(length, sizeof(float));
03713     start = width / 2;
03714     end = length - width / 2;
03715     for (i = 0; i < start; i++)
03716       smo[i] = spectrum[i];
03717     for (i = start; i < end; i++) {
03718       for (j = i - start; j <= i + start; j++)
03719         smo[i] += spectrum[j];
03720       smo[i] /= width;
03721     }
03722     for (i = end; i < length; i++)
03723       smo[i] = spectrum[i];
03724   }
03725   else {
03726       smo = (float *)spectrum;
03727   }
03728 
03729   /*
03730    * Collect all relative maxima along spectrum, that are higher than the
03731    * specified level.
03732    */
03733 
03734   if (width > 20)
03735     step = width / 2;
03736   else
03737     step = 1;
03738 
03739   for (i = step; i < nint - step + 1; i += step) {
03740     if (smo[i] > level) {
03741       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03742         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03743           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03744           ++n;
03745         }
03746       }
03747     }
03748   }
03749 
03750   if (width > 7) {
03751     cpl_free(smo);
03752   }
03753 
03754   if (n == 0) {
03755     cpl_free(data);
03756     return NULL;
03757   }
03758 
03759   return cpl_vector_wrap(n, data);
03760 
03761 }
03762 
03763 
03785 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
03786                              cpl_vector *peaks, int sradius)
03787 {
03788 
03789     const char *func = "mos_refine_peaks";
03790 
03791     double *data;
03792     float   pos;
03793     int     npeaks;
03794     int     startPos, endPos;
03795     int     window = 2*sradius+1;
03796     int     i, j;
03797 
03798 
03799     if (peaks == NULL || spectrum == NULL) {
03800         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03801         return NULL;
03802     }
03803 
03804     npeaks = cpl_vector_get_size(peaks);
03805     data = cpl_vector_unwrap(peaks);
03806 
03807     for (i = 0; i < npeaks; i++) {
03808         startPos = data[i] - window/2;
03809         endPos   = startPos + window;
03810         if (startPos < 0 || endPos >= length)
03811             continue;
03812 
03813         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
03814             pos += startPos;
03815             data[i] = pos;
03816         }
03817     }
03818 
03819     for (i = 1; i < npeaks; i++)
03820         if (data[i] - data[i-1] < 0.5)
03821             data[i-1] = -1.0;
03822 
03823     for (i = 0, j = 0; i < npeaks; i++) {
03824         if (data[i] > 0.0) {
03825             if (i != j)
03826                 data[j] = data[i];
03827             j++;
03828         }
03829     }
03830 
03831     return cpl_vector_wrap(j, data);
03832 
03833 }
03834 
03835 
03889 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
03890                                  double min_disp, double max_disp,
03891                                  double tolerance)
03892 {
03893 
03894   int      i, j, k, l;
03895   int      nlint, npint;
03896   int      minpos;
03897   float    min;
03898   double   lratio, pratio;
03899   double   lo_start, lo_end, hi_start, hi_end, denom;
03900   double   disp, variation, prev_variation;
03901   int      max, maxpos, minl, mink;
03902   int      ambiguous;
03903   int      npeaks_lo, npeaks_hi;
03904   int     *peak_lo;
03905   int     *peak_hi;
03906   int    **ident;
03907   int     *nident;
03908   int     *lident;
03909 
03910   double  *peak;
03911   double  *line;
03912   int      npeaks, nlines;
03913 
03914   double  *xpos;
03915   double  *lambda;
03916   int     *ilambda;
03917   double  *tmp_xpos;
03918   double  *tmp_lambda;
03919   int     *tmp_ilambda;
03920   int     *flag;
03921   int      n = 0;
03922   int      nn;
03923   int      nseq = 0;
03924   int      gap;
03925   int     *seq_length;
03926   int      found;
03927 
03928   peak        = cpl_vector_get_data(peaks);
03929   npeaks      = cpl_vector_get_size(peaks);
03930   line        = cpl_vector_get_data(lines);
03931   nlines      = cpl_vector_get_size(lines);
03932 
03933   if (npeaks < 4)
03934       return NULL;
03935 
03936   peak_lo     = cpl_malloc(npeaks * sizeof(int));
03937   peak_hi     = cpl_malloc(npeaks * sizeof(int));
03938   nident      = cpl_calloc(npeaks, sizeof(int));
03939   lident      = cpl_calloc(nlines, sizeof(int));
03940   xpos        = cpl_calloc(npeaks, sizeof(double));
03941   lambda      = cpl_calloc(npeaks, sizeof(double));
03942   ilambda     = cpl_calloc(npeaks, sizeof(int));
03943   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
03944   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
03945   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
03946   flag        = cpl_calloc(npeaks, sizeof(int));
03947   seq_length  = cpl_calloc(npeaks, sizeof(int));
03948   ident       = cpl_malloc(npeaks * sizeof(int *));
03949   for (i = 0; i < npeaks; i++)
03950     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
03951 
03952   /*
03953    * This is just the number of intervals - one less than the number
03954    * of points (catalog wavelengths, or detected peaks).
03955    */
03956 
03957   nlint = nlines - 1;
03958   npint = npeaks - 1;
03959 
03960 
03961   /*
03962    * Here the big loops on catalog lines begins.
03963    */
03964 
03965   for (i = 1; i < nlint; i++) {
03966 
03967 
03968     /*
03969      * For each catalog wavelength I take the previous and the next one, 
03970      * and compute the ratio of the corresponding wavelength intervals.
03971      * This ratio will be compared to all the ratios obtained doing the
03972      * same with all the detected peaks positions.
03973      */
03974 
03975     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
03976 
03977 
03978     /*
03979      * Here the loop on detected peaks positions begins.
03980      */
03981 
03982     for (j = 1; j < npint; j++) {
03983 
03984       /*
03985        * Not all peaks are used for computing ratios: just the ones
03986        * that are compatible with the expected spectral dispersion
03987        * are taken into consideration. Therefore, I define the pixel
03988        * intervals before and after any peak that are compatible with
03989        * the specified dispersion interval, and select just the peaks
03990        * within such intervals. If either of the two intervals doesn't
03991        * contain any peak, then I skip the current peak and continue
03992        * with the next.
03993        */
03994 
03995       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
03996       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
03997       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
03998       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
03999 
04000       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04001         if (peak[k] > lo_end)
04002           break;
04003         if (peak[k] > lo_start) {
04004           peak_lo[npeaks_lo] = k;
04005           ++npeaks_lo;
04006         }
04007       }
04008 
04009       if (npeaks_lo == 0)
04010         continue;
04011 
04012       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04013         if (peak[k] > hi_end)
04014           break;
04015         if (peak[k] > hi_start) {
04016           peak_hi[npeaks_hi] = k;
04017           ++npeaks_hi;
04018         }
04019       }
04020 
04021       if (npeaks_hi == 0)
04022         continue;
04023 
04024 
04025       /*
04026        * Now I have all peaks that may help for a local identification.
04027        * peak_lo[k] is the sequence number of the k-th peak of the lower
04028        * interval; peak_hi[l] is the sequence number of the l-th peak of
04029        * the higher interval. j is, of course, the sequence number of the
04030        * current peak (second big loop).
04031        */
04032 
04033       prev_variation = 1000.0;
04034       minl = mink = 0;
04035 
04036       for (k = 0; k < npeaks_lo; k++) {
04037         denom = peak[j] - peak[peak_lo[k]];
04038         for (l = 0; l < npeaks_hi; l++) {
04039 
04040           /*
04041            * For any pair of peaks - one from the lower and the other
04042            * from the higher interval - I compute the same ratio that
04043            * was computed with the current line catalog wavelength.
04044            */
04045 
04046           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04047 
04048           /*
04049            * If the two ratios are compatible within the specified
04050            * tolerance, we have a preliminary identification. This
04051            * will be marked in the matrix ident[][], where the first
04052            * index corresponds to a peak sequence number, and the second
04053            * index is the counter of the identifications made during
04054            * this whole process. The array of counters is nident[].
04055            * If more than one interval pair fulfills the specified
04056            * tolerance, the closest to the expected ratio is selected.
04057            */
04058 
04059           variation = fabs(lratio-pratio) / pratio;
04060 
04061           if (variation < tolerance) {
04062             if (variation < prev_variation) {
04063               prev_variation = variation;
04064               minl = l;
04065               mink = k;
04066             }
04067           }
04068         }
04069       }
04070       if (prev_variation < tolerance) {
04071         ident[j][nident[j]]                         = i;
04072         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04073         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04074         ++nident[j];
04075         ++nident[peak_hi[minl]];
04076         ++nident[peak_lo[mink]];
04077       }
04078     }   /* End loop on positions */
04079   }    /* End loop on lines     */
04080 
04081 
04082   /*
04083    * At this point I have filled the ident matrix with all my preliminary
04084    * identifications. Ambiguous identifications must be eliminated.
04085    */
04086 
04087 
04088   for (i = 0; i < npeaks; i++) {
04089 
04090 
04091     /*
04092      * I don't take into consideration peaks that were never identified.
04093      * They are likely contaminations, or emission lines that were not
04094      * listed in the input wavelength catalog.
04095      */
04096 
04097     if (nident[i] > 1) {
04098 
04099 
04100       /*
04101        * Initialise the histogram of wavelengths assigned to the i-th peak.
04102        */
04103 
04104       for (j = 0; j < nlines; j++)
04105         lident[j] = 0;
04106 
04107 
04108       /*
04109        * Count how many times each catalog wavelength was assigned
04110        * to the i-th peak.
04111        */
04112 
04113       for (j = 0; j < nident[i]; j++)
04114         ++lident[ident[i][j]];
04115 
04116 
04117       /*
04118        * What wavelength was most frequently assigned to the i-th peak?
04119        */
04120 
04121       max = 0;
04122       maxpos = 0;
04123       for (j = 0; j < nlines; j++) {
04124         if (max < lident[j]) {
04125           max = lident[j];
04126           maxpos = j;
04127         }
04128       }
04129 
04130 
04131       /*
04132        * Were there other wavelengths assigned with the same frequency?
04133        * This would be the case of an ambiguous identification. It is
04134        * safer to reject this peak...
04135        */
04136 
04137       ambiguous = 0;
04138 
04139       for (k = maxpos + 1; k < nlines; k++) {
04140         if (lident[k] == max) {
04141           ambiguous = 1;
04142           break;
04143         }
04144       }
04145 
04146       if (ambiguous)
04147         continue;
04148 
04149 
04150       /*
04151        * Otherwise, I assign to the i-th peak the wavelength that was
04152        * most often assigned to it.
04153        */
04154 
04155       tmp_xpos[n]   = peak[i];
04156       tmp_lambda[n] = line[maxpos];
04157       tmp_ilambda[n] = maxpos;
04158 
04159       ++n;
04160 
04161     }
04162 
04163   }
04164 
04165 
04166   /*
04167    * Check on identified peaks. Contaminations from other spectra might 
04168    * be present and should be excluded: this type of contamination 
04169    * consists of peaks that have been _correctly_ identified! The non-
04170    * spectral type of light contamination should have been almost all 
04171    * removed already in the previous steps, but it may still be present.
04172    * Here, the self-consistent sequences of identified peaks are
04173    * separated one from the other. At the moment, just the longest of
04174    * such sequences is selected (in other words, spectral multiplexing
04175    * is ignored).
04176    */
04177 
04178   if (n > 1) {
04179     nn = 0;                  /* Number of peaks in the list of sequences */
04180     nseq = 0;                /* Current sequence */
04181     for (k = 0; k < n; k++) {
04182       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04183         flag[k] = 1;
04184         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04185         lambda[nn] = tmp_lambda[k];
04186         ilambda[nn] = tmp_ilambda[k];
04187         ++seq_length[nseq];
04188         ++nn;
04189 
04190         /*
04191          * Now look for all the following peaks that are compatible
04192          * with the expected spectral dispersion, and add them in 
04193          * sequence to xpos. Note that missing peaks are not a problem...
04194          */
04195          
04196         i = k;
04197         while (i < n - 1) {
04198           found = 0;
04199           for (j = i + 1; j < n; j++) {
04200             if (flag[j] == 0) {
04201               disp = (tmp_lambda[j] - tmp_lambda[i])
04202                    / (tmp_xpos[j] - tmp_xpos[i]);
04203               if (disp >= min_disp && disp <= max_disp) {
04204                 flag[j] = 1;
04205                 xpos[nn] = tmp_xpos[j];
04206                 lambda[nn] = tmp_lambda[j];
04207                 ilambda[nn] = tmp_ilambda[j];
04208                 ++seq_length[nseq];
04209                 ++nn;
04210                 i = j;
04211                 found = 1;
04212                 break;
04213               }
04214             }
04215           }
04216           if (!found)
04217             break;
04218         }
04219 
04220         /*
04221          * Current sequence is completed: begin new sequence on the
04222          * excluded peaks, starting the loop on peaks again.
04223          */
04224 
04225         ++nseq;
04226         k = 0;
04227       }
04228     }
04229 
04230 
04231     /*
04232      * Find the longest sequence of self-consistent peaks.
04233      */
04234 
04235     max = 0;
04236     maxpos = 0;
04237     for (i = 0; i < nseq; i++) {
04238       if (seq_length[i] > max) {
04239         max = seq_length[i];
04240         maxpos = i;
04241       }
04242     }
04243 
04244     /*
04245      * Find where this sequence starts in the whole peak position
04246      * storage.
04247      */
04248 
04249     nn = 0;
04250     for (i = 0; i < maxpos; i++)
04251       nn += seq_length[i];
04252 
04253     /*
04254      * Move the longest sequence at the beginning of the returned lists
04255      */
04256 
04257     n = max;
04258     for (i = 0; i < n; i++, nn++) {
04259       xpos[i] = xpos[nn];
04260       lambda[i] = lambda[nn];
04261       ilambda[i] = ilambda[nn];
04262     }
04263 
04264 
04265     /*
04266      * Are some wavelengths missing? Recover them.
04267      */
04268 
04269     for (i = 1; i < n; i++) {
04270       gap = ilambda[i] - ilambda[i-1];
04271       for (j = 1; j < gap; j++) {
04272 
04273         if (j == 1) {
04274 
04275           /*
04276            * Determine the local dispersion from the current pair of peaks
04277            */
04278   
04279           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04280         }
04281 
04282         /*
04283          * With this, find the expected position of the missing
04284          * peak by linear interpolation.
04285          */
04286 
04287         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04288 
04289         /*
04290          * Is there a peak at that position? Here a peak from the
04291          * original list is searched, that is closer than 2 pixels
04292          * to the expected position. If it is found, insert it at
04293          * the current position on the list of identified peaks,
04294          * and leave immediately the loop (taking the new position
04295          * for the following linear interpolation, in case more
04296          * than one peak is missing in the current interval).
04297          * If it is not found, stay in the loop, looking for 
04298          * the following missing peaks in this interval.
04299          */
04300 
04301         found = 0;
04302         for (k = 0; k < npeaks; k++) {
04303           if (fabs(peak[k] - hi_start) < 2) {
04304             for (l = n; l > i; l--) {
04305               xpos[l] = xpos[l-1];
04306               lambda[l] = lambda[l-1];
04307               ilambda[l] = ilambda[l-1];
04308             }
04309             xpos[i] = peak[k];
04310             lambda[i] = line[ilambda[i-1] + j];
04311             ilambda[i] = ilambda[i-1] + j;
04312             ++n;
04313             found = 1;
04314             break;
04315           }
04316         }
04317         if (found)
04318           break;
04319       }
04320     }
04321 
04322 
04323     /*
04324      * Try to extrapolate forward
04325      */
04326 
04327     found = 1;
04328     while (ilambda[n-1] < nlines - 1 && found) {
04329 
04330       /*
04331        * Determine the local dispersion from the last pair of 
04332        * identified peaks
04333        */
04334 
04335       if (n > 1)
04336           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04337       else
04338           disp = 0.0;
04339 
04340       if (disp > max_disp || disp < min_disp)
04341         break;
04342 
04343 
04344       /*
04345        * With this, find the expected position of the missing
04346        * peak by linear interpolation.
04347        */
04348 
04349       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04350 
04351       /*
04352        * Is there a peak at that position? Here a peak from the
04353        * original list is searched, that is closer than 6 pixels
04354        * to the expected position. If it is found, insert it at
04355        * the end of the list of identified peaks. If it is not
04356        * found, leave the loop.
04357        */
04358 
04359       found = 0;
04360       min = fabs(peak[0] - hi_start);
04361       minpos = 0;
04362       for (k = 1; k < npeaks; k++) {
04363         if (min > fabs(peak[k] - hi_start)) {
04364             min = fabs(peak[k] - hi_start);
04365             minpos = k;
04366         }
04367       }
04368       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04369         xpos[n] = peak[minpos];
04370         lambda[n] = line[ilambda[n-1] + 1];
04371         ilambda[n] = ilambda[n-1] + 1;
04372         ++n;
04373         found = 1;
04374       }
04375     }
04376 
04377 
04378     /*
04379      * Try to extrapolate backward
04380      */
04381 
04382     found = 1;
04383     while (ilambda[0] > 0 && found) {
04384 
04385       /*
04386        * Determine the local dispersion from the first pair of
04387        * identified peaks
04388        */
04389 
04390       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04391 
04392       if (disp > max_disp || disp < min_disp)
04393         break;
04394 
04395 
04396       /*
04397        * With this, find the expected position of the missing
04398        * peak by linear interpolation.
04399        */
04400 
04401       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04402 
04403 
04404       /*
04405        * Is there a peak at that position? Here a peak from the
04406        * original list is searched, that is closer than 6 pixels
04407        * to the expected position. If it is found, insert it at
04408        * the beginning of the list of identified peaks. If it is not
04409        * found, leave the loop.
04410        */
04411 
04412       found = 0;
04413       min = fabs(peak[0] - hi_start);
04414       minpos = 0;
04415       for (k = 1; k < npeaks; k++) {
04416         if (min > fabs(peak[k] - hi_start)) {
04417             min = fabs(peak[k] - hi_start);
04418             minpos = k;
04419         }
04420       }
04421       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04422         for (j = n; j > 0; j--) {
04423           xpos[j] = xpos[j-1];
04424           lambda[j] = lambda[j-1];
04425           ilambda[j] = ilambda[j-1];
04426         }
04427         xpos[0] = peak[minpos];
04428         lambda[0] = line[ilambda[0] - 1];
04429         ilambda[0] = ilambda[0] - 1;
04430         ++n;
04431         found = 1;
04432       }
04433     }
04434   }
04435 
04436 
04437   /*
04438    * At this point all peaks are processed. Free memory, and return
04439    * the result.
04440    */
04441 
04442 /************************************************+
04443   for (i = 0; i < npeaks; i++) {
04444     printf("Peak %d:\n   ", i);
04445     for (j = 0; j < nident[i]; j++)
04446       printf("%.2f, ", line[ident[i][j]]);
04447     printf("\n");
04448   }
04449 
04450   printf("\n");
04451 
04452   for (i = 0; i < n; i++)
04453     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04454 +************************************************/
04455   for (i = 0; i < npeaks; i++)
04456     cpl_free(ident[i]);
04457   cpl_free(ident);
04458   cpl_free(nident);
04459   cpl_free(lident);
04460   cpl_free(ilambda);
04461   cpl_free(tmp_xpos);
04462   cpl_free(tmp_lambda);
04463   cpl_free(tmp_ilambda);
04464   cpl_free(peak_lo);
04465   cpl_free(flag);
04466   cpl_free(seq_length);
04467   cpl_free(peak_hi);
04468 
04469   if (n == 0) {
04470     cpl_free(xpos);
04471     cpl_free(lambda);
04472     return NULL;
04473   }
04474 
04475   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04476                                    cpl_vector_wrap(n, lambda));
04477 }
04478 
04479 
04497 /*
04498 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04499                     double refwave, double pixel)
04500 {
04501     double yellow;
04502     double cpixel;
04503     double tolerance = 0.02;
04504     int    max_iter = 20;
04505     int    iter = 0;
04506 
04507     
04508     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04509         return 0.0;
04510     
04511     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04512         return 0.0;
04513 
04514     yellow = (blue + red) / 2;
04515 
04516     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04517 
04518     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04519 
04520         if (cpixel > pixel)
04521             red = yellow;
04522         else
04523             blue = yellow;
04524 
04525         yellow = (blue + red) / 2;
04526         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04527 
04528         iter++;
04529 
04530     }
04531 
04532     return yellow;
04533 
04534 }
04535 */
04536 
04537 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04538                     double refwave, double pixel)
04539 {
04540     double yellow;
04541     double coeff;
04542     int    zero = 0;
04543 
04544     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04545         return 0.0;
04546 
04547     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04548         return 0.0;
04549 
04550     yellow = (blue + red) / 2 - refwave;
04551 
04552     coeff = cpl_polynomial_get_coeff(ids, &zero);
04553     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04554 
04555     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04556 
04557     cpl_polynomial_set_coeff(ids, &zero, coeff);
04558 
04559     return yellow + refwave;
04560 
04561 }
04562 
04588 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04589                                  double reject, int minlines, 
04590                                  int *nlines, double *err)
04591 {
04592     const char   *func = "mos_poly_wav2pix";
04593 
04594     cpl_bivector *pixwav2;
04595     cpl_vector   *wavel;
04596     cpl_vector   *pixel;
04597     double       *d_wavel;
04598     double       *d_pixel;
04599     double        pixpos;
04600     int           fitlines;
04601     int           rejection = 0;
04602     int           i, j;
04603 
04604     cpl_polynomial *ids;
04605 
04606 
04607     *nlines = 0;
04608     *err = 0;
04609 
04610     if (pixwav == NULL) {
04611         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04612         return NULL;
04613     }
04614 
04615     fitlines = cpl_bivector_get_size(pixwav);
04616 
04617     if (fitlines < minlines) {
04618         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04619         return NULL;
04620     }
04621 
04622 
04623     /*
04624      * If outliers rejection was requested, allocate a working
04625      * vector (that can be modified as soon as outliers are removed)
04626      */
04627 
04628     if (reject > 0.0)
04629         rejection = 1;
04630 
04631     if (rejection)
04632         pixwav2 = cpl_bivector_duplicate(pixwav);
04633     else
04634         pixwav2 = pixwav;
04635 
04636 
04637     /*
04638      * The single vectors are extracted just because the fitting routine
04639      * requires it
04640      */
04641 
04642     pixel = cpl_bivector_get_x(pixwav2);
04643     wavel = cpl_bivector_get_y(pixwav2);
04644 
04645 
04646     /*
04647      * Get rid of the wrapper, in case of duplication
04648      */
04649 
04650     if (rejection)
04651         cpl_bivector_unwrap_vectors(pixwav2);
04652 
04653 
04654     /*
04655      * Here begins the iterative fit of identified lines
04656      */
04657 
04658     while (fitlines >= minlines) {
04659 
04660         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04661         *err = sqrt(*err);
04662     
04663         if (ids == NULL) {
04664             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04665             cpl_msg_debug(func, "Fitting IDS");
04666             cpl_error_set_where(func);
04667             if (rejection) {
04668                 cpl_vector_delete(wavel);
04669                 cpl_vector_delete(pixel);
04670             }
04671             return NULL;
04672         }
04673 
04674         if (rejection) {
04675 
04676 
04677             /*
04678              * Now work directly with the vector data buffers...
04679              */
04680 
04681             d_pixel = cpl_vector_unwrap(pixel);
04682             d_wavel = cpl_vector_unwrap(wavel);
04683 
04684             for (i = 0, j = 0; i < fitlines; i++) {
04685                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04686                 if (fabs(pixpos - d_pixel[i]) < reject) {
04687                     d_pixel[j] = d_pixel[i];
04688                     d_wavel[j] = d_wavel[i];
04689                     j++;
04690                 }
04691             }
04692     
04693             if (j == fitlines) {       /* No rejection in last iteration */
04694                 cpl_free(d_wavel);
04695                 cpl_free(d_pixel);
04696                 *nlines = fitlines;
04697                 return ids;
04698             }
04699             else {                     /* Some lines were rejected       */
04700                 fitlines = j;
04701                 cpl_polynomial_delete(ids);
04702                 if (fitlines >= minlines) {
04703                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04704                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04705                 }
04706                 else {                 /* Too few lines: failure         */
04707                     cpl_free(d_wavel);
04708                     cpl_free(d_pixel);
04709                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04710                     return NULL;
04711                 }
04712             }
04713         }
04714         else {
04715             *nlines = fitlines;
04716             return ids;       /* Exit at first iteration if no rejection */
04717         }
04718     }
04719 
04720     return ids;               /* To avoid compiler warnings */
04721 }
04722 
04723 
04748 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04749                                  double reject, int minlines, 
04750                                  int *nlines, double *err)
04751 {
04752 
04753     cpl_bivector *wavpix;
04754     cpl_vector   *wavel;
04755     cpl_vector   *pixel;
04756 
04757     cpl_polynomial *dds;
04758 
04759 
04760     /*
04761      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
04762      */
04763 
04764     pixel = cpl_bivector_get_x(pixwav);
04765     wavel = cpl_bivector_get_y(pixwav);
04766 
04767     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
04768 
04769     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
04770 
04771     cpl_bivector_unwrap_vectors(wavpix);
04772 
04773     return dds;
04774 
04775 }
04776 
04777 
04800 cpl_bivector *mos_find_peaks(const float *spectrum, int length, cpl_vector *lines,
04801                              cpl_polynomial *ids, double refwave, int sradius)
04802 {
04803     const char   *func = "mos_find_peaks";
04804 
04805     double       *data;
04806     double       *d_pixel;
04807     double       *d_wavel;
04808     float         pos;
04809     int           nlines;
04810     int           pixel;
04811     int           i, j;
04812 
04813 
04814     if (spectrum == NULL || lines == NULL || ids == NULL) {
04815         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04816         return NULL;
04817     }
04818 
04819     nlines = cpl_vector_get_size(lines);
04820 
04821     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
04822         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04823         return NULL;
04824     }
04825 
04826     d_wavel = cpl_malloc(nlines * sizeof(double));
04827     d_pixel = cpl_malloc(nlines * sizeof(double));
04828 
04829     data = cpl_vector_get_data(lines);
04830 
04831     for (i = 0, j = 0; i < nlines; i++) {
04832         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
04833         if (pixel - sradius < 0 || pixel + sradius >= length)
04834             continue;
04835         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
04836             pos += pixel - sradius;
04837             d_pixel[j] = pos;
04838             d_wavel[j] = data[i];
04839             j++;
04840         }
04841     }
04842 
04843     if (j > 0) {
04844         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
04845                                          cpl_vector_wrap(j, d_wavel));
04846     }
04847     else {
04848         cpl_free(d_wavel);
04849         cpl_free(d_pixel);
04850         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
04851         return NULL;
04852     }
04853 }
04854 
04855 
04973 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
04974                       cpl_vector *lines,
04975                                           double dispersion, float level,
04976                                           int sradius, int order,
04977                                           double reject, double refwave, 
04978                                           double *wavestart, double *waveend,
04979                                           int *nlines, double *error, 
04980                                           cpl_table *idscoeff,
04981                                           cpl_image *calibration,
04982                                           cpl_image *residuals, 
04983                                           cpl_table *restable,
04984                                           cpl_mask *refmask)
04985 {
04986 
04987     const char *func = "mos_wavelength_calibration_raw";
04988 
04989     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
04990                                                  /* Max order is 5 */
04991 
04992     double  tolerance = 20.0;     /* Probably forever...                */
04993     int     step      = 10;       /* Compute restable every "step" rows */
04994 
04995     char            name[MAX_COLNAME];
04996     cpl_image      *resampled;
04997     cpl_bivector   *output;
04998     cpl_bivector   *new_output;
04999     cpl_vector     *peaks;
05000     cpl_vector     *wavel;
05001     cpl_polynomial *ids;
05002     cpl_polynomial *lin;
05003     cpl_matrix     *kernel;
05004     double          ids_err;
05005     double          max_disp, min_disp;
05006     double         *line;
05007     double          firstLambda, lastLambda, lambda;
05008     double          value, wave, pixe;
05009     cpl_binary     *mdata;
05010     const float    *sdata;
05011     float          *rdata;
05012     float          *idata;
05013     float          *ddata;
05014     float           v1, v2, vi;
05015     float           fpixel;
05016     int            *have_it;
05017     int             pixstart, pixend;
05018     int             extrapolation;
05019     int             nref;
05020     int             nl, nx, ny, pixel;
05021     int             countLines, usedLines;
05022     int             uorder;
05023     int             in, first, last;
05024     int             width, uradius;
05025     int             i, j, k;
05026 
05027 
05028     if (dispersion == 0.0) {
05029         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05030         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05031         return NULL;
05032     }
05033 
05034     if (dispersion < 0.0) {
05035         cpl_msg_error(func, "The expected dispersion must be positive");
05036         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05037         return NULL;
05038     }
05039 
05040     max_disp = dispersion + dispersion * tolerance / 100;
05041     min_disp = dispersion - dispersion * tolerance / 100;
05042 
05043     if (order < 1) {
05044         cpl_msg_error(func, "The order of the fitting polynomial "
05045                       "must be at least 1");
05046         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05047         return NULL;
05048     }
05049 
05050     if (image == NULL || lines == NULL) {
05051         cpl_msg_error(func, "Both spectral exposure and reference line "
05052                       "catalog are required in input");
05053         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05054         return NULL;
05055     }
05056 
05057     nx = cpl_image_get_size_x(image);
05058     ny = cpl_image_get_size_y(image);
05059     sdata = cpl_image_get_data_float_const(image);
05060 
05061     nref = cpl_vector_get_size(lines);
05062     line = cpl_vector_get_data(lines);
05063 
05064     if (*wavestart < 1.0 && *waveend < 1.0) {
05065         firstLambda = line[0];
05066         lastLambda = line[nref-1];
05067         extrapolation = (lastLambda - firstLambda) / 10;
05068         firstLambda -= extrapolation;
05069         lastLambda += extrapolation;
05070         *wavestart = firstLambda;
05071         *waveend = lastLambda;
05072     }
05073     else {
05074         firstLambda = *wavestart;
05075         lastLambda = *waveend;
05076     }
05077 
05078     nl = (lastLambda - firstLambda) / dispersion;
05079     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05080     rdata = cpl_image_get_data_float(resampled);
05081 
05082     if (calibration)
05083         idata = cpl_image_get_data_float(calibration);
05084 
05085     if (residuals)
05086         ddata = cpl_image_get_data_float(residuals);
05087 
05088     if (idscoeff)
05089         for (j = 0; j <= order; j++)
05090             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05091 
05092     if (restable) {
05093         cpl_table_set_size(restable, nref);
05094         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05095         cpl_table_copy_data_double(restable, "wavelength", line);
05096         for (i = 0; i < ny; i += step) {
05097              snprintf(name, MAX_COLNAME, "r%d", i);
05098              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05099              snprintf(name, MAX_COLNAME, "d%d", i);
05100              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05101              snprintf(name, MAX_COLNAME, "p%d", i);
05102              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05103         }
05104     }
05105 
05106     /*
05107      * Here is the real thing: detecting and identifying peaks,
05108      * and then fit the transformation from wavelength to pixel
05109      * and from pixel to wavelength.
05110      */
05111 
05112     for (i = 0; i < ny; i++) {
05113         width = mos_lines_width(sdata + i*nx, nx);
05114         if (sradius > 0) {
05115             if (width > sradius) {
05116                 uradius = width;
05117             }
05118             else {
05119                 uradius = sradius;
05120             }
05121         }
05122         if (width < 5)
05123             width = 5;
05124         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05125         if (peaks) {
05126             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05127         }
05128         if (peaks) {
05129             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05130             if (output) {
05131                 countLines = cpl_bivector_get_size(output);
05132                 if (countLines < 4) {
05133                     cpl_bivector_delete(output);
05134                     cpl_vector_delete(peaks);
05135                     if (nlines)
05136                         nlines[i] = 0;
05137                     if (error)
05138                         error[i] = 0.0;
05139                     continue;
05140                 }
05141 
05142                 /*
05143                  * Set reference wavelength as zero point
05144                  */
05145 
05146                 wavel = cpl_bivector_get_y(output);
05147                 cpl_vector_subtract_scalar(wavel, refwave);
05148 
05149                 uorder = countLines / 2 - 1;
05150                 if (uorder > order)
05151                     uorder = order;
05152 
05153 /* This part is now commented out. In case the first-guess iteration
05154  * was requested, the first fit was made with a lower polynomial degree:
05155  * more robust, and still accurate enough to be used as a first-guess.
05156 
05157                 if (sradius > 0 && uorder > 2)
05158                     --uorder;
05159 
05160  * End of commented part */
05161 
05162                 ids = mos_poly_wav2pix(output, uorder, reject,
05163                                        2 * (uorder + 1), &usedLines,
05164                                        &ids_err);
05165 
05166                 if (ids == NULL) {
05167                     cpl_bivector_delete(output);
05168                     cpl_vector_delete(peaks);
05169                     if (nlines)
05170                         nlines[i] = 0;
05171                     if (error)
05172                         error[i] = 0.0;
05173                     cpl_error_reset();
05174                     continue;
05175                 }
05176 
05177                 if (idscoeff) {
05178 
05179                     /*
05180                      * Write it anyway, even in case a first-guess based
05181                      * solution will be searched afterwards: in case of
05182                      * failure, the "blind" solution is kept.
05183                      */
05184 
05185                     for (k = 0; k <= order; k++) {
05186                         if (k > uorder) {
05187                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05188                         }
05189                         else {
05190                             cpl_table_set_double(idscoeff, clab[k], i,
05191                                       cpl_polynomial_get_coeff(ids, &k));
05192                         }
05193                     }
05194                 }
05195 
05196                 if (sradius > 0) {
05197 
05198                     /*
05199                      * Use ids as a first-guess
05200                      */
05201 
05202                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05203                                                 ids, refwave, uradius);
05204 
05205                     if (new_output) {
05206                         cpl_bivector_delete(output);
05207                         output = new_output;
05208                     }
05209                     else
05210                         cpl_error_reset();
05211 
05212 
05213                     cpl_polynomial_delete(ids);
05214 
05215                     countLines = cpl_bivector_get_size(output);
05216 
05217                     if (countLines < 4) {
05218                         cpl_bivector_delete(output);
05219                         cpl_vector_delete(peaks);
05220 
05221                         /* 
05222                          * With the following code a decision is taken:
05223                          * if using the first-guess gives no results,
05224                          * then also the "blind" solution is rejected.
05225                          */
05226 
05227                         if (nlines)
05228                             nlines[i] = 0;
05229                         if (error)
05230                             error[i] = 0.0;
05231                         if (idscoeff)
05232                             for (k = 0; k <= order; k++)
05233                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05234                         continue;
05235                     }
05236 
05237                     wavel = cpl_bivector_get_y(output);
05238                     cpl_vector_subtract_scalar(wavel, refwave);
05239 
05240                     uorder = countLines / 2 - 1;
05241                     if (uorder > order)
05242                         uorder = order;
05243 
05244                     ids = mos_poly_wav2pix(output, uorder, reject,
05245                                            2 * (uorder + 1), &usedLines,
05246                                            &ids_err);
05247 
05248                     if (ids == NULL) {
05249                         cpl_bivector_delete(output);
05250                         cpl_vector_delete(peaks);
05251 
05252                         /* 
05253                          * With the following code a decision is taken:
05254                          * if using the first-guess gives no results,
05255                          * then also the "blind" solution is rejected.
05256                          */
05257 
05258                         if (nlines)
05259                             nlines[i] = 0;
05260                         if (error)
05261                             error[i] = 0.0;
05262                         if (idscoeff)
05263                             for (k = 0; k <= order; k++)
05264                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05265                         cpl_error_reset();
05266                         continue;
05267                     }
05268 
05269                     if (idscoeff) {
05270                         for (k = 0; k <= order; k++) {
05271                             if (k > uorder) {
05272                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05273                             }
05274                             else {
05275                                 cpl_table_set_double(idscoeff, clab[k], i,
05276                                             cpl_polynomial_get_coeff(ids, &k));
05277                             }
05278                         }
05279                     }
05280 
05281                 } /* End of "use ids as a first-guess" */
05282 
05283                 if (nlines)
05284                     nlines[i] = usedLines;
05285                 if (error)
05286                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05287 
05288                 pixstart = cpl_polynomial_eval_1d(ids, 
05289                     cpl_bivector_get_y_data(output)[0], NULL);
05290                 pixend = cpl_polynomial_eval_1d(ids,
05291                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05292                 extrapolation = (pixend - pixstart) / 5;
05293                 pixstart -= extrapolation;
05294                 pixend += extrapolation;
05295                 if (pixstart < 0)
05296                     pixstart = 0;
05297                 if (pixend > nx)
05298                     pixend = nx;
05299 
05300                 /*
05301                  * Wavelength calibrated image (if requested):
05302                  */
05303 
05304                 if (calibration) {
05305                     for (j = pixstart; j < pixend; j++) {
05306                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05307                                                          lastLambda, refwave, 
05308                                                          j);
05309                     }
05310                 }
05311 
05312                 /*
05313                  * Resampled image:
05314                  */
05315 
05316                 for (j = 0; j < nl; j++) {
05317                     lambda = firstLambda + j * dispersion;
05318                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05319                                                     NULL);
05320                     pixel = fpixel;
05321                     if (pixel >= 0 && pixel < nx-1) {
05322                         v1 = (sdata + i*nx)[pixel];
05323                         v2 = (sdata + i*nx)[pixel+1];
05324                         vi = v1 + (v2-v1)*(fpixel-pixel);
05325                         (rdata + i*nl)[j] = vi;
05326                     }
05327                 }
05328 
05329                 /*
05330                  * Residuals image
05331                  */
05332 
05333                 if (residuals || (restable && !(i%step))) {
05334                     if (restable && !(i%step)) {
05335                         lin = cpl_polynomial_new(1);
05336                         for (k = 0; k < 2; k++)
05337                             cpl_polynomial_set_coeff(lin, &k, 
05338                                           cpl_polynomial_get_coeff(ids, &k));
05339                     }
05340                     for (j = 0; j < countLines; j++) {
05341                         pixe = cpl_bivector_get_x_data(output)[j];
05342                         wave = cpl_bivector_get_y_data(output)[j];
05343                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05344                         if (residuals) {
05345                             pixel = pixe + 0.5;
05346                             (ddata + i*nx)[pixel] = value;
05347                         }
05348                         if (restable && !(i%step)) {
05349                             for (k = 0; k < nref; k++) {
05350                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05351                                     snprintf(name, MAX_COLNAME, "r%d", i);
05352                                     cpl_table_set_double(restable, name, 
05353                                                          k, value);
05354                                     value = pixe
05355                                           - cpl_polynomial_eval_1d(lin, wave,
05356                                                                    NULL);
05357                                     snprintf(name, MAX_COLNAME, "d%d", i);
05358                                     cpl_table_set_double(restable, name, 
05359                                                          k, value);
05360                                     snprintf(name, MAX_COLNAME, "p%d", i);
05361                                     cpl_table_set_double(restable, name,
05362                                                          k, pixe);
05363                                     break;
05364                                 }
05365                             }
05366                         }
05367                     }
05368                     if (restable && !(i%step)) {
05369                         cpl_polynomial_delete(lin);
05370                     }
05371                 }
05372 
05373                 /*
05374                  * Mask at reference wavelength
05375                  */
05376 
05377                 if (refmask) {
05378                     mdata = cpl_mask_get_data(refmask);
05379                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05380                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05381                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05382                         mdata[pixel + i*nx] = CPL_BINARY_1;
05383                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05384                     }
05385                 }
05386 
05387                 cpl_polynomial_delete(ids);
05388                 cpl_bivector_delete(output);
05389             }
05390             cpl_vector_delete(peaks);
05391         }
05392     }
05393 
05394     if (refmask) {
05395         kernel = cpl_matrix_new(3, 3);
05396         cpl_matrix_set(kernel, 0, 1, 1.0);
05397         cpl_matrix_set(kernel, 1, 1, 1.0);
05398         cpl_matrix_set(kernel, 2, 1, 1.0);
05399 
05400         cpl_mask_dilation(refmask, kernel);
05401         cpl_mask_erosion(refmask, kernel);
05402         cpl_mask_erosion(refmask, kernel);
05403         cpl_mask_dilation(refmask, kernel);
05404 
05405         cpl_matrix_delete(kernel);
05406 
05407         /*
05408          *  Fill possible gaps
05409          */
05410 
05411         mdata = cpl_mask_get_data(refmask);
05412         have_it = cpl_calloc(ny, sizeof(int));
05413 
05414         for (i = 0; i < ny; i++, mdata += nx) {
05415             for (j = 0; j < nx; j++) {
05416                 if (mdata[j] == CPL_BINARY_1) {
05417                     have_it[i] = j;
05418                     break;
05419                 }
05420             }
05421         }
05422 
05423         mdata = cpl_mask_get_data(refmask);
05424         in = 0;
05425         first = last = 0;
05426 
05427         for (i = 0; i < ny; i++) {
05428             if (have_it[i]) {
05429                 if (!in) {
05430                     in = 1;
05431                     if (first) {
05432                         last = i;
05433                         if (abs(have_it[first] - have_it[last]) < 3) {
05434                             for (j = first; j < last; j++) {
05435                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05436                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05437                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05438                             }
05439                         }
05440                     }
05441                 }
05442             }
05443             else {
05444                 if (in) {
05445                     in = 0;
05446                     first = i - 1;
05447                 }
05448             }
05449         }
05450 
05451         cpl_free(have_it);
05452 
05453     }
05454 
05455 /*
05456     for (i = 0; i < ny; i++) {
05457         if (nlines[i] == 0) {
05458             for (k = 0; k <= order; k++) {
05459                 cpl_table_set_invalid(idscoeff, clab[k], i);
05460             }
05461         }
05462     }
05463 */
05464 
05465     return resampled;
05466 }
05467 
05468 
05490 /*
05491 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05492 {
05493     const char *func = "mos_locate_spectra_bis";
05494 
05495     cpl_apertures    *slits;
05496     cpl_image        *labimage;
05497     cpl_image        *refimage;
05498     cpl_binary       *mdata;
05499     cpl_table        *slitpos;
05500     cpl_propertylist *sort_col;
05501     int               nslits;
05502     int              *have_it;
05503     int               in, first, last;
05504     int               i, j;
05505 
05506 
05507     if (mask == NULL) {
05508         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05509         return NULL;
05510     }
05511 
05512     nx = cpl_mask_get_size_x(mask);
05513     ny = cpl_mask_get_size_y(mask);
05514 
05515     mdata = cpl_mask_get_data(refmask);
05516     have_it = cpl_calloc(ny, sizeof(int));
05517 
05518     for (i = 0; i < ny; i++, mdata += nx) {
05519         for (j = 0; j < nx; j++) {
05520             if (mdata[j] == CPL_BINARY_1) {
05521                 have_it[i] = j + 1;
05522                 break;
05523             }
05524         }
05525     }
05526 
05527     mdata = cpl_mask_get_data(refmask);
05528     in = 0;
05529     first = last = 0;
05530     nslits = 0;
05531 
05532     for (i = 0; i < ny; i++) {
05533         if (have_it[i]) {
05534             if (in) {
05535                 if (i) {
05536                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05537                         nslits++;
05538                     }
05539                 }
05540             }
05541             else {
05542                 in = 1;
05543                 nslits++;
05544             }
05545         }
05546         else {
05547             if (in) {
05548                 in = 0;
05549             }
05550         }
05551     }
05552 }
05553 */
05554 
05555 
05577 cpl_table *mos_locate_spectra(cpl_mask *mask)
05578 {
05579     const char *func = "mos_locate_spectra";
05580 
05581     cpl_apertures    *slits;
05582     cpl_image        *labimage;
05583     cpl_image        *refimage;
05584     cpl_table        *slitpos;
05585     cpl_propertylist *sort_col;
05586     int               nslits;
05587     int               i;
05588 
05589 
05590     if (mask == NULL) {
05591         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05592         return NULL;
05593     }
05594 
05595     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05596 
05597     if (nslits < 1) {
05598         cpl_image_delete(labimage);
05599         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05600         return NULL;
05601     }
05602 
05603     refimage = cpl_image_new_from_mask(mask);
05604 
05605     slits = cpl_apertures_new_from_image(refimage, labimage);
05606 
05607     cpl_image_delete(labimage);
05608     cpl_image_delete(refimage);
05609 
05610     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05611     if (nslits < 1) {
05612         cpl_apertures_delete(slits);
05613         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05614         return NULL;
05615     }
05616 
05617     slitpos = cpl_table_new(nslits);
05618     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05619     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05620     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05621     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05622     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05623     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05624     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05625     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05626 
05627     for (i = 0; i < nslits; i++) {
05628         cpl_table_set_double(slitpos, "xtop", i, 
05629                              cpl_apertures_get_top_x(slits, i+1) - 1);
05630         cpl_table_set_double(slitpos, "ytop", i, 
05631                              cpl_apertures_get_top(slits, i+1));
05632         cpl_table_set_double(slitpos, "xbottom", i, 
05633                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05634         cpl_table_set_double(slitpos, "ybottom", i, 
05635                              cpl_apertures_get_bottom(slits, i+1));
05636     }
05637 
05638     cpl_apertures_delete(slits);
05639 
05640     sort_col = cpl_propertylist_new();
05641     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05642     cpl_table_sort(slitpos, sort_col);
05643     cpl_propertylist_delete(sort_col);
05644 
05645     return slitpos;
05646 
05647 }
05648 
05649 
05665 cpl_error_code mos_validate_slits(cpl_table *slits) 
05666 {
05667     const char *func = "mos_validate_slits";
05668 
05669 
05670     if (slits == NULL)
05671         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05672 
05673     if (1 != cpl_table_has_column(slits, "xtop"))
05674         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05675 
05676     if (1 != cpl_table_has_column(slits, "ytop"))
05677         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05678 
05679     if (1 != cpl_table_has_column(slits, "xbottom"))
05680         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05681 
05682     if (1 != cpl_table_has_column(slits, "ybottom"))
05683         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05684 
05685     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05686         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05687 
05688     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05689         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05690 
05691     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05692         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05693 
05694     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05695         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05696 
05697     return CPL_ERROR_NONE;
05698 }
05699 
05700 
05729 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05730 {
05731     const char *func = "mos_rotate_slits";
05732 
05733     cpl_error_code error;
05734     char aux_name[] = "_0";
05735     int i;
05736 
05737 
05738     rotation %= 4;
05739     if (rotation < 0)
05740         rotation += 4;
05741 
05742     if (rotation == 0)
05743         return CPL_ERROR_NONE;
05744 
05745     error = mos_validate_slits(slits);
05746     if (error)
05747         return cpl_error_set(func, error);
05748 
05749     if (rotation == 1 || rotation == 3) {
05750 
05751         /*
05752          * Swap x and y column names
05753          */
05754 
05755         for (i = 0; i < 77; i++)
05756             if (1 == cpl_table_has_column(slits, aux_name))
05757                 aux_name[1]++;
05758         if (1 == cpl_table_has_column(slits, aux_name))
05759             return cpl_error_set(func, CPL_ERROR_CONTINUE);
05760         cpl_table_name_column(slits, "xtop", aux_name);
05761         cpl_table_name_column(slits, "ytop", "xtop");
05762         cpl_table_name_column(slits, aux_name, "ytop");
05763         cpl_table_name_column(slits, "xbottom", aux_name);
05764         cpl_table_name_column(slits, "ybottom", "xbottom");
05765         cpl_table_name_column(slits, aux_name, "ybottom");
05766     }
05767 
05768     if (rotation == 1 || rotation == 2) {
05769         cpl_table_multiply_scalar(slits, "xtop", -1.0);
05770         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
05771         cpl_table_add_scalar(slits, "xtop", nx);
05772         cpl_table_add_scalar(slits, "xbottom", nx);
05773     }
05774 
05775     if (rotation == 3 || rotation == 2) {
05776         cpl_table_multiply_scalar(slits, "ytop", -1.0);
05777         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
05778         cpl_table_add_scalar(slits, "ytop", ny);
05779         cpl_table_add_scalar(slits, "ybottom", ny);
05780     }
05781 
05782     return CPL_ERROR_NONE;
05783 }
05784 
05785 
05843 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
05844                               cpl_table *global)
05845 {
05846     cpl_array        *top_ident;
05847     cpl_array        *bot_ident;
05848     cpl_matrix       *mdata;
05849     cpl_matrix       *mpattern;
05850     cpl_matrix       *top_data;
05851     cpl_matrix       *top_pattern;
05852     cpl_matrix       *top_mdata;
05853     cpl_matrix       *top_mpattern;
05854     cpl_matrix       *bot_data;
05855     cpl_matrix       *bot_pattern;
05856     cpl_matrix       *bot_mdata;
05857     cpl_matrix       *bot_mpattern;
05858     cpl_propertylist *sort_col;
05859     double           *xtop;
05860     double           *ytop;
05861     double           *xmtop;
05862     double           *ymtop;
05863     double           *xbot;
05864     double           *ybot;
05865     double           *xmbot;
05866     double           *ymbot;
05867     double            top_scale, bot_scale;
05868     double            angle, top_angle, bot_angle;
05869     double            xmse, ymse;
05870     double            xrms, top_xrms, bot_xrms;
05871     double            yrms, top_yrms, bot_yrms;
05872     int               nslits;
05873     int               nmaskslits, use_pattern;
05874     int               found_slits, found_slits_top, found_slits_bot;
05875     int               degree;
05876     int               i;
05877     cpl_table        *positions;
05878     cpl_error_code    error;
05879 
05880     cpl_vector       *point;
05881     double           *dpoint;
05882     cpl_vector       *xpos;
05883     cpl_vector       *ypos;
05884     cpl_vector       *xmpos;
05885     cpl_vector       *ympos;
05886     cpl_bivector     *mpos;
05887     cpl_polynomial   *xpoly     = NULL;
05888     cpl_polynomial   *ypoly     = NULL;
05889     cpl_polynomial   *top_xpoly = NULL;
05890     cpl_polynomial   *top_ypoly = NULL;
05891     cpl_polynomial   *bot_xpoly = NULL;
05892     cpl_polynomial   *bot_ypoly = NULL;
05893 
05894 
05895 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 3, 0)
05896     positions = mos_identify_slits_fast(slits, maskslits, global);
05897     if (positions == NULL)
05898         cpl_error_set_where(cpl_func);
05899     return positions;
05900 #endif
05901 
05902     error = mos_validate_slits(slits);
05903     if (error) {
05904         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
05905                       cpl_error_get_message());
05906         cpl_error_set(cpl_func, error);
05907         return NULL;
05908     }
05909 
05910     error = mos_validate_slits(maskslits);
05911     if (error) {
05912         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
05913                       cpl_error_get_message());
05914         cpl_error_set(cpl_func, error);
05915         return NULL;
05916     }
05917 
05918     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
05919         cpl_msg_error(cpl_func, "Missing slits identifiers");
05920         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
05921         return NULL;
05922     }
05923 
05924     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
05925         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
05926         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
05927         return NULL;
05928     }
05929 
05930     nslits = cpl_table_get_nrow(slits);
05931     nmaskslits = cpl_table_get_nrow(maskslits);
05932 
05933     if (nslits == 0 || nmaskslits == 0) {
05934         cpl_msg_error(cpl_func, "Empty slits table");
05935         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
05936         return NULL;
05937     }
05938 
05939     if (nslits > 25) {
05940         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
05941         positions = mos_identify_slits_fast(slits, maskslits, global);
05942         if (positions == NULL)
05943             cpl_error_set_where(cpl_func);
05944         return positions;
05945     }
05946 
05947     /*
05948      * Guarantee that both input tables are sorted in the same way
05949      */
05950 
05951     sort_col = cpl_propertylist_new();
05952     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05953     cpl_table_sort(slits, sort_col);
05954     cpl_table_sort(maskslits, sort_col);
05955     cpl_propertylist_delete(sort_col);
05956 
05957     /*
05958      * First we handle all the special cases (too few slits...)
05959      */
05960 
05961     if (nslits < 3 && nmaskslits > nslits) {
05962 
05963         /*
05964          * If there are just 1 or 2 slits on the CCD, and more on the
05965          * mask, the ambiguity cannot be solved, and an error is returned.
05966          * This is a case that must be solved with a first-guess relation
05967          * between mask and CCD.
05968          */
05969 
05970         if (nslits > 1)
05971             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
05972                             "with the %d mask slits: process will continue "
05973                             "using the detected CCD slits positions", nslits,
05974                             nmaskslits);
05975         else
05976             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
05977                             "the %d mask slits: process will continue using "
05978                             "the detected CCD slit position", nmaskslits);
05979         return NULL;
05980     }
05981 
05982     if (nmaskslits < 3 && nslits > nmaskslits) {
05983 
05984         /*
05985          * If there are less than 3 slits on the mask the ambiguity cannot
05986          * be solved, and an error is returned. This is a case that must
05987          * be solved with a first-guess relation between mask and CCD.
05988          */
05989 
05990         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
05991                         "the %d mask slits: process will continue using "
05992                         "the detected CCD slits positions", nslits,
05993                         nmaskslits);
05994         return NULL;
05995     }
05996 
05997     /*
05998      * Pattern matching related operations begin here. Two pattern
05999      * matching will be run, one based on the "top" and another one
06000      * based on the "bottom" slit coordinates. The one with the
06001      * smallest rms in the Y coordinate will be chosen.
06002      */
06003 
06004     xtop  = cpl_table_get_data_double(slits, "xtop");
06005     ytop  = cpl_table_get_data_double(slits, "ytop");
06006     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06007     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06008 
06009     xbot  = cpl_table_get_data_double(slits, "xbottom");
06010     ybot  = cpl_table_get_data_double(slits, "ybottom");
06011     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06012     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06013 
06014     top_data    = cpl_matrix_new(2, nslits);
06015     top_pattern = cpl_matrix_new(2, nmaskslits);
06016     bot_data    = cpl_matrix_new(2, nslits);
06017     bot_pattern = cpl_matrix_new(2, nmaskslits);
06018 
06019     for (i = 0; i < nslits; i++)
06020         cpl_matrix_set(top_data, 0, i, xtop[i]);
06021 
06022     for (i = 0; i < nslits; i++)
06023         cpl_matrix_set(top_data, 1, i, ytop[i]);
06024 
06025     for (i = 0; i < nmaskslits; i++)
06026         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06027 
06028     for (i = 0; i < nmaskslits; i++)
06029         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06030 
06031     for (i = 0; i < nslits; i++)
06032         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06033 
06034     for (i = 0; i < nslits; i++)
06035         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06036 
06037     for (i = 0; i < nmaskslits; i++)
06038         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06039 
06040     for (i = 0; i < nmaskslits; i++)
06041         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06042 
06043     if (nmaskslits > nslits)
06044         use_pattern = nslits;
06045     else
06046         use_pattern = nmaskslits;
06047 
06048 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 3, 0)
06049     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06050                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06051                                      &top_mpattern, &top_scale, &top_angle);
06052 
06053     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06054                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06055                                      &bot_mpattern, &bot_scale, &bot_angle);
06056 #else
06057     top_ident = NULL;
06058     bot_ident = NULL;
06059 #endif
06060 
06061     if (top_ident == NULL && bot_ident == NULL) {
06062         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06063                         "the %d found CCD slits with the %d mask slits: "
06064                         "process will continue using the detected CCD "
06065                         "slits positions", nslits, nmaskslits);
06066         return NULL;
06067     }
06068 
06069     found_slits_top = 0;
06070     found_slits_bot = 0;
06071     if (top_ident && bot_ident) {
06072         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06073                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06074         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06075                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06076         if (fabs(top_angle) < fabs(bot_angle))
06077             angle = fabs(top_angle);
06078         else
06079             angle = fabs(bot_angle);
06080         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06081         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06082     }
06083     else if (top_ident) {
06084         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06085         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06086         angle = fabs(top_angle);
06087         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06088     }
06089     else {
06090         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06091         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06092         angle = fabs(bot_angle);
06093         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06094     }
06095 
06096     cpl_array_delete(top_ident);
06097     cpl_array_delete(bot_ident);
06098 
06099     if (angle > 4.0) {
06100         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06101                         "angle is expected to be around zero. This match is "
06102                         "rejected: the process will continue using the %d "
06103                         "detected CCD slits positions", nslits);
06104         return NULL;
06105     }
06106 
06107     found_slits = found_slits_top;
06108     if (found_slits < found_slits_bot)
06109         found_slits = found_slits_bot;     /* Max value */
06110 
06111     if (found_slits < 4) {
06112         cpl_msg_warning(cpl_func,
06113                         "Too few safely identified slits: %d out of %d "
06114                         "candidates (%d expected). Process will continue "
06115                         "using the detected CCD slits positions", found_slits,
06116                         nslits, nmaskslits);
06117         return NULL;
06118     }
06119 
06120     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06121                  "candidates\n(%d expected)", found_slits, nslits,
06122                  nmaskslits);
06123 
06124     if (found_slits_top < 4)
06125         found_slits_top = 0;
06126 
06127     if (found_slits_bot < 4)
06128         found_slits_bot = 0;
06129 
06130     /*
06131      * Now for each set select the points of the identified slits, and fit
06132      * two bivariate polynomials to determine a first approximate relation
06133      * between positions on the mask and positions on the CCD.
06134      */
06135 
06136     for (i = 0; i < 2; i++) {
06137         if (i) {
06138             found_slits = found_slits_top;
06139             mdata = top_mdata;
06140             mpattern = top_mpattern;
06141         }
06142         else {
06143             found_slits = found_slits_bot;
06144             mdata = bot_mdata;
06145             mpattern = bot_mpattern;
06146         }
06147 
06148         if (found_slits == 0)
06149             continue;
06150         else if (found_slits < 10)
06151             degree = 1;
06152         else
06153             degree = 2;
06154 
06155         xpos  = cpl_vector_wrap(found_slits,
06156                                 cpl_matrix_get_data(mdata)              );
06157         ypos  = cpl_vector_wrap(found_slits,
06158                                 cpl_matrix_get_data(mdata) + found_slits);
06159         xmpos = cpl_vector_wrap(found_slits,
06160                                 cpl_matrix_get_data(mpattern)              );
06161         ympos = cpl_vector_wrap(found_slits,
06162                                 cpl_matrix_get_data(mpattern) + found_slits);
06163         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06164         xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06165         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06166 
06167         cpl_bivector_unwrap_vectors(mpos);
06168         cpl_vector_unwrap(xpos);
06169         cpl_vector_unwrap(ypos);
06170         cpl_vector_unwrap(xmpos);
06171         cpl_vector_unwrap(ympos);
06172         cpl_matrix_delete(mdata);
06173         cpl_matrix_delete(mpattern);
06174 
06175         if (i) {
06176             top_xpoly = xpoly;
06177             top_ypoly = ypoly;
06178             top_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06179             top_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06180         }
06181         else {
06182             bot_xpoly = xpoly;
06183             bot_ypoly = ypoly;
06184             bot_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06185             bot_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06186         }
06187     }
06188 
06189     if (top_xpoly && bot_xpoly) {
06190         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06191             xrms = top_xrms;
06192             xpoly = top_xpoly;
06193             cpl_polynomial_delete(bot_xpoly);
06194         }
06195         else {                      /* bottom X solution wins... */
06196             xrms = bot_xrms;
06197             xpoly = bot_xpoly;
06198             cpl_polynomial_delete(top_xpoly);
06199         }
06200     }
06201     else if (top_xpoly) {
06202         xrms = top_xrms;
06203         xpoly = top_xpoly;
06204     }
06205     else {
06206         xrms = bot_xrms;
06207         xpoly = bot_xpoly;
06208     }
06209 
06210     if (top_ypoly && bot_ypoly) {
06211         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06212             yrms = top_yrms;
06213             ypoly = top_ypoly;
06214             cpl_polynomial_delete(bot_ypoly);
06215         }
06216         else {                      /* bottom Y solution wins... */
06217             yrms = bot_yrms;
06218             ypoly = bot_ypoly;
06219             cpl_polynomial_delete(top_ypoly);
06220         }
06221     }
06222     else if (top_ypoly) {
06223         yrms = top_yrms;
06224         ypoly = top_ypoly;
06225     }
06226     else {
06227         yrms = bot_yrms;
06228         ypoly = bot_ypoly;
06229     }
06230 
06231     if (xpoly == NULL || ypoly == NULL) {
06232         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06233                         "identified slits positions cannot be improved.");
06234         cpl_polynomial_delete(xpoly);
06235         cpl_polynomial_delete(ypoly);
06236         cpl_error_reset();
06237         return NULL;
06238     }
06239 
06240     cpl_msg_info(cpl_func,
06241                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06242                  xrms, yrms);
06243 
06244     if (global) {
06245         write_global_distortion(global, 0, xpoly);
06246         write_global_distortion(global, 7, ypoly);
06247     }
06248 
06249     /*
06250      * The fit was successful: use the polynomials to obtain a new
06251      * position table with the improved positions of the slits
06252      */
06253 
06254     positions = cpl_table_duplicate(maskslits);
06255     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06256     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06257     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06258     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06259 
06260     point = cpl_vector_new(2);
06261     dpoint = cpl_vector_get_data(point);
06262 
06263     for (i = 0; i < nmaskslits; i++) {
06264         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06265         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06266         cpl_table_set_double(positions, "xtop", i,
06267                              cpl_polynomial_eval(xpoly, point));
06268         cpl_table_set_double(positions, "ytop", i,
06269                              cpl_polynomial_eval(ypoly, point));
06270         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06271         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06272         cpl_table_set_double(positions, "xbottom", i,
06273                              cpl_polynomial_eval(xpoly, point));
06274         cpl_table_set_double(positions, "ybottom", i,
06275                              cpl_polynomial_eval(ypoly, point));
06276     }
06277 
06278     cpl_vector_delete(point);
06279     cpl_polynomial_delete(xpoly);
06280     cpl_polynomial_delete(ypoly);
06281 
06282     cpl_table_erase_column(positions, "xmtop");
06283     cpl_table_erase_column(positions, "ymtop");
06284     cpl_table_erase_column(positions, "xmbottom");
06285     cpl_table_erase_column(positions, "ymbottom");
06286 
06287     if (nmaskslits > nslits)
06288         cpl_msg_info(cpl_func,
06289                      "Finally identified slits: %d out of %d expected\n"
06290                      "(%d recovered)", nmaskslits, nmaskslits,
06291                      nmaskslits - nslits);
06292     else if (nmaskslits < nslits)
06293         cpl_msg_info(cpl_func,
06294                      "Finally identified slits: %d out of %d expected\n"
06295                      "(%d rejected)", nmaskslits, nmaskslits,
06296                      nslits - nmaskslits);
06297     else
06298         cpl_msg_info(cpl_func,
06299                      "Finally identified slits: %d out of %d expected",
06300                      nmaskslits, nmaskslits);
06301 
06302     return positions;
06303 
06304 }
06305 
06306 
06307 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06308                                    cpl_table *global)
06309 {
06310     const char *func = "mos_identify_slits_fast";
06311 
06312     cpl_propertylist *sort_col;
06313     cpl_table        *positions;
06314     cpl_vector       *scales;
06315     cpl_vector       *angles;
06316     cpl_vector       *point;
06317     cpl_vector       *xpos;
06318     cpl_vector       *ypos;
06319     cpl_vector       *xmpos;
06320     cpl_vector       *ympos;
06321     cpl_bivector     *mpos;
06322     cpl_polynomial   *xpoly = NULL;
06323     cpl_polynomial   *ypoly = NULL;
06324     cpl_error_code    error;
06325     int nslits;
06326     int nmaskslits;
06327     int found_slits;
06328     int i, j, k;
06329 
06330     double  dist1, dist2, dist3, dist, mindist;
06331     double  scale, minscale, maxscale;
06332     double  angle, minangle, maxangle;
06333     double *dscale;
06334     double *dangle;
06335     double *dpoint;
06336     double *xtop;
06337     double *ytop;
06338     double *xbottom;
06339     double *ybottom;
06340     double *xcenter;
06341     double *ycenter;
06342     double *xpseudo;
06343     double *ypseudo;
06344     int    *slit_id;
06345     double *xmtop;
06346     double *ymtop;
06347     double *xmbottom;
06348     double *ymbottom;
06349     double *xmcenter;
06350     double *ymcenter;
06351     double *xmpseudo;
06352     double *ympseudo;
06353     double  xmse, ymse;
06354     int    *mslit_id;
06355     int    *good;
06356     int     minpos;
06357     int     degree;
06358 
06359     double  sradius = 0.01;   /* Candidate input argument... */
06360     int     in_sradius;
06361 
06362     double pi = 3.14159265358979323846;
06363 
06364 
06365     error = mos_validate_slits(slits);
06366     if (error) {
06367         cpl_msg_error(func, "CCD slits table validation: %s", 
06368                       cpl_error_get_message());
06369         cpl_error_set(func, error);
06370         return NULL;
06371     }
06372 
06373     error = mos_validate_slits(maskslits);
06374     if (error) {
06375         cpl_msg_error(func, "Mask slits table validation: %s", 
06376                       cpl_error_get_message());
06377         cpl_error_set(func, error);
06378         return NULL;
06379     }
06380 
06381     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06382         cpl_msg_error(func, "Missing slits identifiers");
06383         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06384         return NULL;
06385     }
06386 
06387     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06388         cpl_msg_error(func, "Wrong type used for slits identifiers");
06389         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06390         return NULL;
06391     }
06392 
06393     nslits = cpl_table_get_nrow(slits);
06394     nmaskslits = cpl_table_get_nrow(maskslits);
06395 
06396     if (nslits == 0 || nmaskslits == 0) {
06397         cpl_msg_error(func, "Empty slits table");
06398         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06399         return NULL;
06400     }
06401 
06402 
06403     /*
06404      * Compute middle point coordinates for each slit listed in both
06405      * input tables.
06406      */
06407 
06408     if (cpl_table_has_column(slits, "xcenter"))
06409         cpl_table_erase_column(slits, "xcenter");
06410 
06411     if (cpl_table_has_column(slits, "ycenter"))
06412         cpl_table_erase_column(slits, "ycenter");
06413 
06414     if (cpl_table_has_column(maskslits, "xcenter"))
06415         cpl_table_erase_column(maskslits, "xcenter");
06416 
06417     if (cpl_table_has_column(maskslits, "ycenter"))
06418         cpl_table_erase_column(maskslits, "ycenter");
06419 
06420     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06421     cpl_table_add_columns(slits, "xcenter", "xbottom");
06422     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06423     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06424     cpl_table_add_columns(slits, "ycenter", "ybottom");
06425     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06426 
06427     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06428     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06429     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06430     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06431     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06432     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06433 
06434 
06435     /*
06436      * Guarantee that both input tables are sorted in the same way
06437      */
06438 
06439     sort_col = cpl_propertylist_new();
06440     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06441     cpl_table_sort(slits, sort_col);
06442     cpl_table_sort(maskslits, sort_col);
06443     cpl_propertylist_delete(sort_col);
06444 
06445 
06446     /*
06447      * First we handle all the special cases (too few slits...)
06448      */
06449 
06450     if (nslits < 3 && nmaskslits > nslits) {
06451 
06452         /*
06453          * If there are just 1 or 2 slits on the CCD, and more on the
06454          * mask, the ambiguity cannot be solved, and an error is returned.
06455          * This is a case that must be solved with a first-guess relation
06456          * between mask and CCD.
06457          */
06458 
06459         if (nslits > 1)
06460             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06461                             "%d mask slits: process will continue using the "
06462                             "detected CCD slit position", nmaskslits);
06463         else
06464             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06465                             "the %d mask slits: process will continue using "
06466                             "the detected CCD slits positions", nslits, 
06467                             nmaskslits);
06468         return NULL;
06469     }
06470 
06471     if (nslits <= 3 && nslits == nmaskslits) {
06472 
06473         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06474         cpl_msg_warning(func, "Their detected positions are left unchanged");
06475 
06476         /*
06477          * If there are just up to 3 slits, both on the mask and on the CCD,
06478          * we can reasonably hope that those slits were found, and accept 
06479          * that their positions on the CCD cannot be improved. We prepare
06480          * therefore an output position table containing the slits with
06481          * their original positions. We can however give an estimate of
06482          * the platescale if there is more than one slit.
06483          */
06484 
06485         positions = cpl_table_duplicate(slits);
06486         cpl_table_erase_column(slits, "xcenter");
06487         cpl_table_erase_column(slits, "ycenter");
06488         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06489         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06490         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06491         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06492         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06493         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06494         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06495         cpl_table_erase_column(maskslits, "xcenter");
06496         cpl_table_erase_column(maskslits, "ycenter");
06497 
06498         if (nslits > 1) {
06499             xcenter = cpl_table_get_data_double(positions, "xcenter");
06500             ycenter = cpl_table_get_data_double(positions, "ycenter");
06501             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06502             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06503 
06504             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06505                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06506             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06507                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06508             scale = sqrt(dist1/dist2);
06509 
06510             if (nslits == 3) {
06511                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06512                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06513                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06514                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06515                 scale += sqrt(dist1/dist2);
06516                 scale /= 2;
06517             }
06518 
06519             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06520         }
06521 
06522         return positions;
06523     }
06524 
06525     if (nmaskslits < 3 && nslits > nmaskslits) {
06526 
06527         /*
06528          * If there are less than 3 slits on the mask the ambiguity cannot 
06529          * be solved, and an error is returned. This is a case that must 
06530          * be solved with a first-guess relation between mask and CCD.
06531          */
06532 
06533         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06534                         "the %d mask slits: process will continue using "
06535                         "the detected CCD slits positions", nslits, 
06536                         nmaskslits);
06537         return NULL;
06538     }
06539 
06540 
06541     /*
06542      * At this point of the program all the region of the plane
06543      * (nslits, nmaskslits) where either or both mask and CCD display
06544      * less than 3 slits are handled in some way. It would be better
06545      * to add in this place a special handling for identifying slits
06546      * in case of a very reduced number of slits (say, below 6).
06547      * It is also clear that if there are many more slits on the
06548      * mask than on the CCD, or many more on the CCD than on the
06549      * mask, something went deeply wrong with the preliminary 
06550      * wavelength calibration. Such cases should be handled with
06551      * a _complete_ pattern-recognition algorithm based on the
06552      * construction of all possible triangles. For the moment, 
06553      * we go directly to the limited pattern-recognition applied
06554      * below, based on triangles build only for consecutive slits.
06555      * This is reasonably safe, since the preliminary wavelength
06556      * calibration performed by mos_identify_peaks() is generally
06557      * robust.
06558      */
06559 
06560 
06561     /*
06562      * Compute (X, Y) coordinates on pseudo-plane describing the
06563      * different position ratios of successive slits, in both
06564      * input tables.
06565      */
06566 
06567     if (cpl_table_has_column(slits, "xpseudo"))
06568         cpl_table_erase_column(slits, "xpseudo");
06569 
06570     if (cpl_table_has_column(slits, "ypseudo"))
06571         cpl_table_erase_column(slits, "ypseudo");
06572 
06573     if (cpl_table_has_column(maskslits, "xpseudo"))
06574         cpl_table_erase_column(maskslits, "xpseudo");
06575 
06576     if (cpl_table_has_column(maskslits, "ypseudo"))
06577         cpl_table_erase_column(maskslits, "ypseudo");
06578 
06579     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06580     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06581 
06582     xcenter = cpl_table_get_data_double(slits, "xcenter");
06583     ycenter = cpl_table_get_data_double(slits, "ycenter");
06584     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06585     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06586 
06587     for (i = 1; i < nslits - 1; i++) {
06588         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06589               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06590         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06591               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06592         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06593               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06594         xpseudo[i] = sqrt(dist1/dist2);
06595         ypseudo[i] = sqrt(dist3/dist2);
06596     }
06597 
06598     cpl_table_set_invalid(slits, "xpseudo", 0);
06599     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06600     cpl_table_set_invalid(slits, "ypseudo", 0);
06601     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06602 
06603     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06604     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06605 
06606     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06607     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06608     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06609     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06610 
06611     for (i = 1; i < nmaskslits - 1; i++) {
06612         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06613               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06614         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06615               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06616         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06617               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06618         xmpseudo[i] = sqrt(dist1/dist2);
06619         ympseudo[i] = sqrt(dist3/dist2);
06620     }
06621     
06622     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06623     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06624     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06625     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06626 
06627 
06628     /*
06629      * For each (X, Y) on the pseudo-plane related to the mask positions,
06630      * find the closest (X, Y) on the pseudo-plane related to the CCD
06631      * positions. If the closest point is closer than a given search
06632      * radius, a triangle has been found, and 3 slits are identified.
06633      * However, if more than one point is found within the search
06634      * radius, we have an ambiguity and this is rejected.
06635      */
06636 
06637     if (cpl_table_has_column(slits, "slit_id"))
06638         cpl_table_erase_column(slits, "slit_id");
06639     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06640     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06641 
06642     for (i = 1; i < nmaskslits - 1; i++) {
06643         in_sradius = 0;
06644         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06645                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06646         minpos = 1;
06647         if (mindist < sradius*sradius)
06648             in_sradius++;
06649         for (j = 2; j < nslits - 1; j++) {
06650             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06651                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06652             if (dist < sradius*sradius)
06653                 in_sradius++;
06654             if (in_sradius > 1)    /* More than one triangle within radius */
06655                 break;
06656             if (mindist > dist) {
06657                 mindist = dist;
06658                 minpos = j;
06659             }
06660         }
06661 
06662         mindist = sqrt(mindist);
06663 
06664         if (mindist < sradius && in_sradius == 1) {
06665             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06666             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06667             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06668         }
06669     }
06670 
06671 
06672     /*
06673      * At this point, the slit_id column contains invalid elements 
06674      * corresponding to unidentified slits.
06675      */
06676 
06677     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06678 
06679     if (found_slits < 3) {
06680         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06681                         "%d out of %d", found_slits, nslits);
06682         if (nslits == nmaskslits) {
06683             cpl_msg_warning(func, "(this is not an error, it could be caused "
06684                             "by a mask with regularly located slits)");
06685             cpl_msg_warning(func, "The detected slits positions are left "
06686                             "unchanged");
06687 
06688             /*
06689              * If there are less than 3 identified slits, this is probably 
06690              * a mask with regularly spaced slits (leading to an ambiguous
06691              * pattern). Only in the case all expected slits appear to have 
06692              * been found on the CCD we can proceed...
06693              */
06694 
06695             cpl_table_erase_column(slits, "slit_id");
06696             cpl_table_erase_column(slits, "xpseudo");
06697             cpl_table_erase_column(slits, "ypseudo");
06698             positions = cpl_table_duplicate(slits);
06699             cpl_table_erase_column(slits, "xcenter");
06700             cpl_table_erase_column(slits, "ycenter");
06701 
06702             cpl_table_erase_column(maskslits, "xpseudo");
06703             cpl_table_erase_column(maskslits, "ypseudo");
06704             cpl_table_duplicate_column(positions, "xmtop", 
06705                                        maskslits, "xtop");
06706             cpl_table_duplicate_column(positions, "ymtop", 
06707                                        maskslits, "ytop");
06708             cpl_table_duplicate_column(positions, "xmbottom", 
06709                                        maskslits, "xbottom");
06710             cpl_table_duplicate_column(positions, "ymbottom", 
06711                                        maskslits, "ybottom");
06712             cpl_table_duplicate_column(positions, "xmcenter", 
06713                                        maskslits, "xcenter");
06714             cpl_table_duplicate_column(positions, "ymcenter", 
06715                                        maskslits, "ycenter");
06716             cpl_table_duplicate_column(positions, "slit_id", 
06717                                        maskslits, "slit_id");
06718             cpl_table_erase_column(maskslits, "xcenter");
06719             cpl_table_erase_column(maskslits, "ycenter");
06720             return positions;
06721         }
06722         else {
06723             cpl_table_erase_column(slits, "slit_id");
06724             cpl_table_erase_column(slits, "xpseudo");
06725             cpl_table_erase_column(slits, "ypseudo");
06726             positions = cpl_table_duplicate(slits);
06727             cpl_table_erase_column(slits, "xcenter");
06728             cpl_table_erase_column(slits, "ycenter");
06729             cpl_msg_warning(func, "(the failure could be caused "
06730                             "by a mask with regularly located slits)");
06731             return NULL;
06732         }
06733     }
06734     else {
06735         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06736                      "candidates (%d expected)", found_slits, nslits, 
06737                      nmaskslits);
06738     }
06739 
06740 
06741     /*
06742      * Create a table with the coordinates of the preliminarily identified 
06743      * slits, both on CCD and mask. The original order of the slits positions 
06744      * is preserved.
06745      */
06746 
06747     positions = cpl_table_new(found_slits);
06748     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
06749     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
06750     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
06751     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
06752     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
06753     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
06754     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
06755     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
06756     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
06757     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
06758     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
06759     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
06760     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
06761     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
06762     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
06763 
06764     slit_id = cpl_table_get_data_int   (slits, "slit_id");
06765     xtop    = cpl_table_get_data_double(slits, "xtop");
06766     ytop    = cpl_table_get_data_double(slits, "ytop");
06767     xbottom = cpl_table_get_data_double(slits, "xbottom");
06768     ybottom = cpl_table_get_data_double(slits, "ybottom");
06769     xcenter = cpl_table_get_data_double(slits, "xcenter");
06770     ycenter = cpl_table_get_data_double(slits, "ycenter");
06771 
06772     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
06773     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
06774     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
06775     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
06776     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
06777     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
06778     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
06779 
06780 
06781     /*
06782      * Transferring the valid slits information to the new table.
06783      * Note that invalid elements are coded as 0 in the internal
06784      * buffer, and this is the way they are recognised and excluded.
06785      */
06786 
06787     k = 0;
06788     cpl_table_fill_invalid_int(slits, "slit_id", 0);
06789     for (i = 0; i < nmaskslits; i++) {
06790         for (j = 0; j < nslits; j++) {
06791             if (slit_id[j] == 0)
06792                 continue; /* Skip invalid slit */
06793             if (mslit_id[i] == slit_id[j]) {
06794                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
06795 
06796                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
06797                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
06798                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
06799                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
06800                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
06801                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
06802 
06803                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
06804                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
06805                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
06806                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
06807                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
06808                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
06809 
06810                 k++;
06811 
06812                 break;
06813             }
06814         }
06815     }
06816 
06817     found_slits = k;
06818 
06819     cpl_table_erase_column(slits, "slit_id");
06820     cpl_table_erase_column(slits, "xpseudo");
06821     cpl_table_erase_column(slits, "ypseudo");
06822     cpl_table_erase_column(slits, "xcenter");
06823     cpl_table_erase_column(slits, "ycenter");
06824     cpl_table_erase_column(maskslits, "xpseudo");
06825     cpl_table_erase_column(maskslits, "ypseudo");
06826     cpl_table_erase_column(maskslits, "xcenter");
06827     cpl_table_erase_column(maskslits, "ycenter");
06828 
06829 
06830     /*
06831      * Find the median platescale and rotation angle from the identified 
06832      * slits, and then exclude slits outlaying more than 10% from the 
06833      * median platescale, and more than 2 degrees from the median
06834      * rotation angle.
06835      */
06836 
06837     ytop    = cpl_table_get_data_double(positions, "ytop");
06838     ybottom = cpl_table_get_data_double(positions, "ybottom");
06839     xcenter = cpl_table_get_data_double(positions, "xcenter");
06840     ycenter = cpl_table_get_data_double(positions, "ycenter");
06841     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06842     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06843 
06844     scales = cpl_vector_new(found_slits - 1);
06845     dscale = cpl_vector_get_data(scales);
06846     angles = cpl_vector_new(found_slits - 1);
06847     dangle = cpl_vector_get_data(angles);
06848 
06849     for (i = 1; i < found_slits; i++) {
06850         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06851               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06852         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
06853               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
06854         dscale[i-1] = sqrt(dist1/dist2);
06855         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
06856                             xcenter[i-1] - xcenter[i])
06857                     - atan2(ymcenter[i-1] - ymcenter[i], 
06858                             xmcenter[i-1] - xmcenter[i]);
06859         dangle[i-1] *= 180;
06860         dangle[i-1] /= pi;
06861     }
06862 
06863     minscale = cpl_vector_get_min(scales);
06864     scale = cpl_vector_get_median_const(scales);
06865     maxscale = cpl_vector_get_max(scales);
06866 
06867     minangle = cpl_vector_get_min(angles);
06868     angle = cpl_vector_get_median_const(angles);
06869     maxangle = cpl_vector_get_max(angles);
06870 
06871     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
06872     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
06873                  minscale, maxscale);
06874 
06875     cpl_msg_info(func, "Median rotation: %f degrees", angle);
06876     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
06877                  minangle, maxangle);
06878 
06879     good = cpl_table_get_data_int(positions, "good");
06880 
06881     good[0] = good[found_slits - 1] = 1;
06882     for (i = 1; i < found_slits; i++) {
06883         if (fabs((dscale[i-1] - scale)/scale) < 0.10
06884          && fabs(dangle[i-1] - angle) < 2) {
06885             good[i-1]++;
06886             good[i]++;
06887         }
06888     }
06889 
06890     for (i = 0; i < found_slits; i++) {
06891         if (good[i] < 2)
06892             good[i] = 0;
06893         else
06894             good[i] = 1;
06895     }
06896 
06897 /*
06898     for (i = 1; i < found_slits; i++)
06899         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
06900             good[i-1] = good[i] = 1;
06901 */
06902 
06903 /* DEBUG ************+
06904     for (i = 0; i < found_slits; i++) {
06905         if (good[i]) {
06906             if (i == found_slits - 1)
06907                 printf("include slit %d, prev = %f, %f\n", 
06908                        i, dscale[i-1], dangle[i-1]);
06909             else if (i == 0)
06910                 printf("include slit %d, next %f, %f\n", 
06911                        i, dscale[i], dangle[i]);
06912             else
06913                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
06914                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06915         }
06916         else {
06917             if (i == found_slits - 1)
06918                 printf("EXclude slit %d, prev = %f, %f\n", 
06919                        i, dscale[i-1], dangle[i-1]);
06920             else if (i == 0)
06921                 printf("EXclude slit %d, next %f, %f\n", 
06922                        i, dscale[i], dangle[i]);
06923             else
06924                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
06925                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
06926         }
06927     }
06928 +*********** DEBUG */
06929 
06930     cpl_vector_delete(scales);
06931     cpl_vector_delete(angles);
06932 
06933     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
06934     cpl_table_erase_selected(positions);
06935     cpl_table_erase_column(positions, "good");
06936     found_slits = cpl_table_get_nrow(positions);
06937 
06938     if (found_slits < 4) {
06939 
06940         /*
06941          * If the self-consistency check gives such a poor result,
06942          * something must have gone really wrong in the preliminary
06943          * wavelength calibration... Nothing can be done.
06944          */
06945 
06946         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
06947                         "candidates (%d expected). Process will continue "
06948                         "using the detected CCD slits positions", found_slits, 
06949                         nslits, nmaskslits);
06950         cpl_table_delete(positions);
06951         return NULL;
06952     }
06953     else {
06954         cpl_msg_info(func, "Safely identified slits: %d out of %d "
06955                      "candidates\n(%d expected)", found_slits, nslits,
06956                      nmaskslits);
06957     }
06958 
06959 
06960     /*
06961      * Now select the central points of the identified slits, and
06962      * fit two bivariate polynomials to determine a first approximate
06963      * relation between positions on the mask and positions on the CCD.
06964      */
06965 
06966     xpos = cpl_vector_wrap(found_slits, 
06967                            cpl_table_get_data_double(positions, "xcenter"));
06968     ypos = cpl_vector_wrap(found_slits, 
06969                            cpl_table_get_data_double(positions, "ycenter"));
06970     xmpos = cpl_vector_wrap(found_slits, 
06971                             cpl_table_get_data_double(positions, "xmcenter"));
06972     ympos = cpl_vector_wrap(found_slits, 
06973                             cpl_table_get_data_double(positions, "ymcenter"));
06974     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
06975 
06976     if (found_slits < 10)
06977         degree = 1;
06978     else
06979         degree = 2;
06980 
06981     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06982     if (xpoly != NULL)
06983         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06984     cpl_bivector_unwrap_vectors(mpos);
06985     cpl_vector_unwrap(xpos);
06986     cpl_vector_unwrap(ypos);
06987     cpl_vector_unwrap(xmpos);
06988     cpl_vector_unwrap(ympos);
06989     if (ypoly == NULL) {
06990         if (found_slits == nmaskslits) {
06991             cpl_msg_warning(func, "Fit failure: the accuracy of the "
06992                             "identified slits positions is not improved.");
06993 
06994             /*
06995              * The determination of the transformation from mask to CCD
06996              * failed, but since all slits have been already identified
06997              * this is not a problem: data can be reduced also without
06998              * such transformation. Simply the accuracy of the slits 
06999              * positions cannot be improved.
07000              */ 
07001 
07002         } else {
07003             cpl_msg_info(func, "Fit failure: not all slits have been "
07004                          "identified. Process will continue using "
07005                          "the detected CCD slits positions");
07006         }
07007 
07008         cpl_polynomial_delete(xpoly);
07009         return positions;
07010     }
07011 
07012     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07013                  sqrt(xmse), sqrt(ymse));
07014 
07015     if (global) {
07016         write_global_distortion(global, 0, xpoly);
07017         write_global_distortion(global, 7, ypoly);
07018     }
07019 
07020     /*
07021      * The fit was successful: use the polynomials to obtain a new 
07022      * position table with the improved positions of the slits
07023      */
07024 
07025     cpl_table_delete(positions);
07026 
07027     positions = cpl_table_duplicate(maskslits);
07028     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07029     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07030     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07031     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07032 
07033     point = cpl_vector_new(2);
07034     dpoint = cpl_vector_get_data(point);
07035 
07036     for (i = 0; i < nmaskslits; i++) {
07037         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07038         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07039         cpl_table_set_double(positions, "xtop", i, 
07040                              cpl_polynomial_eval(xpoly, point));
07041         cpl_table_set_double(positions, "ytop", i, 
07042                              cpl_polynomial_eval(ypoly, point));
07043         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07044         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07045         cpl_table_set_double(positions, "xbottom", i, 
07046                              cpl_polynomial_eval(xpoly, point));
07047         cpl_table_set_double(positions, "ybottom", i, 
07048                              cpl_polynomial_eval(ypoly, point));
07049     }
07050 
07051     cpl_vector_delete(point);
07052     cpl_polynomial_delete(xpoly);
07053     cpl_polynomial_delete(ypoly);
07054 
07055     cpl_table_erase_column(positions, "xmtop");
07056     cpl_table_erase_column(positions, "ymtop");
07057     cpl_table_erase_column(positions, "xmbottom");
07058     cpl_table_erase_column(positions, "ymbottom");
07059 
07060     if (nmaskslits > nslits)
07061         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07062                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07063     else if (nmaskslits < nslits)
07064         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07065                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07066     else
07067         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07068                  nmaskslits, nmaskslits);
07069 
07070     return positions;
07071 }
07072 
07073 
07115 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07116                           double blue, double red, double dispersion)
07117 {
07118 
07119     const char  *func = "mos_trace_flat";
07120 
07121     cpl_image   *gradient;
07122     cpl_image   *sgradient;
07123     float       *dgradient;
07124     float        level = 500;   /* It was 250... */
07125     cpl_vector  *row;
07126     cpl_vector  *srow;
07127     cpl_vector **peaks;
07128     double      *peak;
07129     int         *slit_id;
07130     float       *g;
07131     double      *r;
07132     double      *xtop;
07133     double      *ytop;
07134     double      *xbottom;
07135     double      *ybottom;
07136     double       min, dist;
07137     double       sradius;
07138     double       tolerance;
07139     double       start_y, prev_y;
07140     int          minpos;
07141     int          nslits;
07142     int          nrows;
07143     int          step = 10;
07144     int          filtbox = 15;
07145     int          nx, ny, npix;
07146     int          pos, ypos;
07147     int          npeaks;
07148     int          pixel_above, pixel_below;
07149     int          i, j, k, l;
07150     char         trace_id[MAX_COLNAME];
07151 
07152     cpl_table   *traces;
07153 
07154 
07155     if (flat == NULL || slits == NULL) {
07156         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07157         return NULL;
07158     }
07159 
07160     if (dispersion <= 0.0) {
07161         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07162         return NULL;
07163     }
07164 
07165     if (red - blue < dispersion) {
07166         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07167         return NULL;
07168     }
07169 
07170     /*
07171      * Create a dummy slit_id column if it is missing in the
07172      * input slits table
07173      */
07174 
07175     nslits  = cpl_table_get_nrow(slits);
07176     if (1 != cpl_table_has_column(slits, "slit_id")) {
07177         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07178         for (i = 0; i < nslits; i++)
07179             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07180     }
07181 
07182     slit_id = cpl_table_get_data_int(slits, "slit_id");
07183 
07184     nx = cpl_image_get_size_x(flat);
07185     ny = cpl_image_get_size_y(flat);
07186     npix = nx * ny;
07187 
07188     gradient = cpl_image_duplicate(flat);
07189     dgradient = cpl_image_get_data_float(gradient);
07190 
07191     for (i = 0; i < ny - 1; i++) {
07192         k = i * nx;
07193         for (j = 0; j < nx; j++) {
07194             l = k + j;
07195             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07196         }
07197     }
07198 
07199     npix--;
07200     for (j = 0; j < nx; j++)
07201         dgradient[npix - j] = 0.0;
07202 
07203     cpl_image_turn(gradient, -1);
07204     nx = cpl_image_get_size_x(gradient);
07205     ny = cpl_image_get_size_y(gradient);
07206     sgradient = mos_image_vertical_median_filter(gradient, 
07207                                                  filtbox, 0, ny, 0, step);
07208     cpl_image_delete(gradient);
07209 
07210 
07211     /*
07212      * Remove background from processed image rows
07213      */
07214 
07215     dgradient = cpl_image_get_data_float(sgradient);
07216 
07217     for (i = 1; i <= ny; i += step) {
07218         row = cpl_vector_new_from_image_row(sgradient, i);
07219         srow = cpl_vector_filter_median_create(row, filtbox);
07220         cpl_vector_subtract(row, srow);
07221         cpl_vector_delete(srow);
07222         g = dgradient + (i-1)*nx;
07223         r = cpl_vector_get_data(row);
07224         for (j = 0; j < nx; j++)
07225             g[j] = r[j];
07226         cpl_vector_delete(row);
07227     }
07228 
07229 
07230     /*
07231      * Rotate (temporarily) the input slits table, to get coordinates
07232      * compatible with the rotated gradient image.
07233      */
07234 
07235     mos_rotate_slits(slits, 1, nx, ny);
07236     xtop    = cpl_table_get_data_double(slits, "xtop");
07237     ytop    = cpl_table_get_data_double(slits, "ytop");
07238     xbottom = cpl_table_get_data_double(slits, "xbottom");
07239     ybottom = cpl_table_get_data_double(slits, "ybottom");
07240 
07241 
07242     /*
07243      * Get positions of peaks candidates for each processed gradient
07244      * image row
07245      */
07246 
07247     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07248 
07249     for (i = 0; i < ny; i += step) {
07250         g = dgradient + i*nx;
07251         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07252 
07253         /* I thought this would be required, but apparently I was wrong.
07254          * Check twice... */
07255         if (peaks[i])
07256             cpl_vector_subtract_scalar(peaks[i], 0.5);
07257          
07258     }
07259 
07260     cpl_image_delete(sgradient);
07261 
07262 
07263     /*
07264      * Tracing the flat field spectra edges, starting from the
07265      * slits positions obtained at reference wavelength. The 
07266      * gradient maximum closest to each slits ends is found and
07267      * accepted only within a given search radius:
07268      */
07269 
07270     sradius = 5.0;  /* Pixel */
07271 
07272     /*
07273      * The tracing proceeds along the processed gradient image rows,
07274      * above and below the start position, finding the closest peak
07275      * to the previous obtained position, accepting the new position
07276      * only if it is closer than a given tolerance:
07277      */
07278 
07279     tolerance = 0.9;  /* Pixel */
07280 
07281     /*
07282      * The trace is attempted for a certain number of pixels above
07283      * and below the position of the reference wavelength:
07284      */
07285 
07286     pixel_above = (red - reference) / dispersion;
07287     pixel_below = (reference - blue) / dispersion;
07288 
07289 
07290     /*
07291      * Prepare the structure of the output table:
07292      */
07293 
07294     nrows = (ny-1)/step + 1;
07295     traces = cpl_table_new(nrows);
07296     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07297     cpl_table_set_column_unit(traces, "x", "pixel");
07298     for (i = 0, j = 0; i < ny; i += step, j++)
07299         cpl_table_set(traces, "x", j, i);
07300 
07301     for (i = 0; i < nslits; i++) {
07302 
07303         /*
07304          * Find the closest processed gradient image row
07305          */
07306 
07307         ypos = ytop[i];
07308 
07309         if (ypos < 0)
07310             ypos = 0;
07311         if (ypos >= ny)
07312             ypos = ny - 1;
07313 
07314         pos = ypos / step;
07315         pos *= step;
07316 
07317         /*
07318          * Find the peak in that row that is closest to xtop[i]
07319          */
07320 
07321         if (peaks[pos]) {
07322             peak = cpl_vector_get_data(peaks[pos]);
07323             npeaks = cpl_vector_get_size(peaks[pos]);
07324 
07325             min = fabs(peak[0] - xtop[i]);
07326             minpos = 0;
07327             for (j = 1; j < npeaks; j++) {
07328                 dist = fabs(peak[j] - xtop[i]);
07329                 if (min > dist) {
07330                     min = dist;
07331                     minpos = j;
07332                 }
07333             }
07334         }
07335         else {
07336             npeaks = 0;
07337         }
07338 
07339         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07340         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07341 
07342         if (min > sradius || npeaks == 0) {
07343             cpl_msg_warning(func, "Cannot find spectrum edge for "
07344                             "top (or left) end of slit %d", slit_id[i]);
07345         }
07346         else {
07347 
07348             /*
07349              * Add to output table the start y position. Note that
07350              * y positions are written in coordinates of the unrotated
07351              * image, i.e., with horizontal dispersion. Currently nx
07352              * is the x size of the temporarily rotated image (for
07353              * faster memory access), but it has the sense of a y size.
07354              */
07355 
07356             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07357             start_y = peak[minpos];
07358 
07359             /*
07360              * Perform the tracing of current edge. Above:
07361              */
07362 
07363             prev_y = start_y;
07364 
07365             for (j = pos + step; j < ny; j += step) {
07366                 if (j - pos > pixel_above)
07367                     break;
07368                 if (peaks[j]) {
07369                     peak = cpl_vector_get_data(peaks[j]);
07370                     npeaks = cpl_vector_get_size(peaks[j]);
07371                     min = fabs(peak[0] - prev_y);
07372                     minpos = 0;
07373                     for (k = 1; k < npeaks; k++) {
07374                         dist = fabs(peak[k] - prev_y);
07375                         if (min > dist) {
07376                             min = dist;
07377                             minpos = k;
07378                         }
07379                     }
07380                     if (min < tolerance) {
07381                         cpl_table_set(traces, trace_id, j/step, 
07382                                       nx - peak[minpos]);
07383                         prev_y = peak[minpos];
07384                     }
07385                 }
07386             }
07387 
07388             /*
07389              * Perform the tracing of current edge. Below:
07390              */
07391 
07392             prev_y = start_y;
07393 
07394             for (j = pos - step; j >= 0; j -= step) {
07395                 if (pos - j > pixel_below)
07396                     break;
07397                 if (peaks[j]) {
07398                     peak = cpl_vector_get_data(peaks[j]);
07399                     npeaks = cpl_vector_get_size(peaks[j]);
07400                     min = fabs(peak[0] - prev_y);
07401                     minpos = 0;
07402                     for (k = 1; k < npeaks; k++) {
07403                         dist = fabs(peak[k] - prev_y);
07404                         if (min > dist) {
07405                             min = dist;
07406                             minpos = k;
07407                         }
07408                     }
07409                     if (min < tolerance) {
07410                         cpl_table_set(traces, trace_id, j/step, 
07411                                       nx - peak[minpos]);
07412                         prev_y = peak[minpos];
07413                     }
07414                 }
07415             }
07416         }
07417 
07418 
07419         /*
07420          * Find the peak in that row that is closest to xbottom[i]
07421          */
07422 
07423         if (peaks[pos]) {
07424             peak = cpl_vector_get_data(peaks[pos]);
07425             npeaks = cpl_vector_get_size(peaks[pos]);
07426     
07427             min = fabs(peak[0] - xbottom[i]);
07428             minpos = 0;
07429             for (j = 1; j < npeaks; j++) {
07430                 dist = fabs(peak[j] - xbottom[i]);
07431                 if (min > dist) {
07432                     min = dist;
07433                     minpos = j;
07434                 }
07435             }
07436         }
07437         else {
07438             npeaks = 0;
07439         }
07440 
07441         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07442         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07443 
07444         if (min > sradius || npeaks == 0) {
07445             cpl_msg_warning(func, "Cannot find spectrum edge for "
07446                             "bottom (or right) end of slit %d", slit_id[i]);
07447         }
07448         else {
07449 
07450             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07451             start_y = peak[minpos]; 
07452 
07453             /*
07454              * Perform the tracing of current edge. Above:
07455              */
07456 
07457             prev_y = start_y;
07458 
07459             for (j = pos + step; j < ny; j += step) {
07460                 if (j - pos > pixel_above)
07461                     break;
07462                 if (peaks[j]) {
07463                     peak = cpl_vector_get_data(peaks[j]);
07464                     npeaks = cpl_vector_get_size(peaks[j]);
07465                     min = fabs(peak[0] - prev_y);
07466                     minpos = 0;
07467                     for (k = 1; k < npeaks; k++) {
07468                         dist = fabs(peak[k] - prev_y);
07469                         if (min > dist) {
07470                             min = dist;
07471                             minpos = k;
07472                         }
07473                     }
07474                     if (min < tolerance) {
07475                         cpl_table_set(traces, trace_id, j/step, 
07476                                       nx - peak[minpos]);
07477                         prev_y = peak[minpos];
07478                     }
07479                 }
07480             }
07481 
07482             /*
07483              * Perform the tracing of current edge. Below:
07484              */
07485 
07486             prev_y = start_y;
07487 
07488             for (j = pos - step; j >= 0; j -= step) {
07489                 if (pos - j > pixel_below)
07490                     break;
07491                 if (peaks[j]) {
07492                     peak = cpl_vector_get_data(peaks[j]);
07493                     npeaks = cpl_vector_get_size(peaks[j]);
07494                     min = fabs(peak[0] - prev_y);
07495                     minpos = 0;
07496                     for (k = 1; k < npeaks; k++) {
07497                         dist = fabs(peak[k] - prev_y);
07498                         if (min > dist) {
07499                             min = dist;
07500                             minpos = k;
07501                         }
07502                     }
07503                     if (min < tolerance) {
07504                         cpl_table_set(traces, trace_id, j/step, 
07505                                       nx - peak[minpos]);
07506                         prev_y = peak[minpos];
07507                     }
07508                 }
07509             }
07510         }
07511 
07512     }   /* End of loop on slits */
07513 
07514     for (i = 0; i < ny; i += step)
07515         cpl_vector_delete(peaks[i]);
07516     cpl_free(peaks);
07517 
07518     /*
07519      * Restore original orientation of slits positions table
07520      */
07521 
07522     mos_rotate_slits(slits, -1, ny, nx);
07523 
07524     return traces;
07525 
07526 }
07527 
07528 
07549 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07550 {
07551     const char *func = "mos_poly_trace";
07552 
07553     cpl_table      *polytraces;
07554     cpl_table      *dummy;
07555     cpl_vector     *x;
07556     cpl_vector     *trace;
07557     cpl_polynomial *polytrace;
07558     char            trace_id[MAX_COLNAME];
07559     char            trace_res[MAX_COLNAME];
07560     char            trace_mod[MAX_COLNAME];
07561     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07562                                                  /* Max order is 5 */
07563     double         *xdata;
07564     int            *slit_id;
07565     int             nslits;
07566     int             nrows;
07567     int             npoints;
07568     int             i, j, k;
07569 
07570 
07571     if (traces == NULL || slits == NULL) {
07572         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07573         return NULL;
07574     }
07575 
07576     if (order > 5) {
07577         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07578         return NULL;
07579     }
07580 
07581     nrows   = cpl_table_get_nrow(traces);
07582     xdata   = cpl_table_get_data_double(traces, "x");
07583     nslits  = cpl_table_get_nrow(slits);
07584     slit_id = cpl_table_get_data_int(slits, "slit_id");
07585 
07586     polytraces = cpl_table_new(2*nslits);
07587     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07588     for (i = 0; i <= order; i++)
07589         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07590 
07591     for (i = 0; i < nslits; i++) {
07592         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07593 
07594             if (j) {
07595                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07596                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07597                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07598             }
07599             else {
07600                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07601                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07602                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07603             }
07604 
07605             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07606 
07607             /*
07608              * The "dummy" table is just a tool for eliminating invalid
07609              * points from the vectors to be fitted.
07610              */
07611 
07612             dummy = cpl_table_new(nrows);
07613             cpl_table_duplicate_column(dummy, "x", traces, "x");
07614             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07615             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07616             if (npoints < 2 * order) {
07617                 cpl_table_delete(dummy);
07618                 continue;
07619             }
07620             cpl_table_erase_invalid(dummy);
07621             x     = cpl_vector_wrap(npoints, 
07622                                     cpl_table_get_data_double(dummy, "x"));
07623             trace = cpl_vector_wrap(npoints, 
07624                                     cpl_table_get_data_double(dummy, trace_id));
07625             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07626             cpl_vector_unwrap(x);
07627             cpl_vector_unwrap(trace);
07628             cpl_table_delete(dummy);
07629 
07630             /*
07631              * Screen bad solutions. At the moment, a primitive screening
07632              * consists in excluding solutions displaying excessive
07633              * curvature (larger than 1E-5 / pixel)
07634              */
07635 
07636             k = 2;
07637             if (cpl_polynomial_get_coeff(polytrace, &k) >  1.E-5) {
07638                 cpl_polynomial_delete(polytrace);
07639                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07640                 cpl_table_duplicate_column(traces, trace_res, traces, 
07641                                            trace_mod);
07642                 if (j) 
07643                     cpl_msg_warning(func, "Exclude bad curvature solution "
07644                            "for bottom (right) edge of slit %d", slit_id[i]);
07645                 else
07646                     cpl_msg_warning(func, "Exclude bad curvature solution "
07647                                 "for top (left) edge of slit %d", slit_id[i]);
07648                 continue;
07649             }
07650 
07651             /*
07652              * Write polynomial coefficients to the output table,
07653              * tagged with the appropriate slit_id
07654              */
07655 
07656             for (k = 0; k <= order; k++)
07657                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07658                                      cpl_polynomial_get_coeff(polytrace, &k));
07659 
07660             /*
07661              * Add column of residuals to input traces table
07662              */
07663 
07664             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07665             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07666 
07667             for (k = 0; k < nrows; k++) {
07668                 cpl_table_set_double(traces, trace_mod, k,
07669                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07670             }
07671 
07672             cpl_polynomial_delete(polytrace);
07673 
07674             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07675             cpl_table_subtract_columns(traces, trace_res, trace_id);
07676             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07677 
07678         }
07679     }
07680 
07681     return polytraces;
07682 
07683 }
07684 
07685 
07709 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07710                                 int mode)
07711 {
07712     const char *func = "mos_global_trace";
07713 
07714     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07715                                                  /* Max order is 5 */
07716     cpl_table      *table;
07717     cpl_vector     *c0;
07718     cpl_vector     *cn;
07719     cpl_bivector   *list;
07720 /* alternative (not robust)
07721     cpl_polynomial *poly;
07722 */
07723 
07724     double *offset;
07725     double  rms, q, m;
07726 
07727     int order, nrows, nslits;
07728     int i, j;
07729 
07730 
07731     if (polytraces == NULL) {
07732         cpl_msg_error(func, "Missing spectral curvature table");
07733         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07734     }
07735 
07736     if (slits == NULL) {
07737         cpl_msg_error(func, "Missing slits positions table");
07738         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07739     }
07740 
07741     nslits = cpl_table_get_nrow(slits);
07742 
07743     table = cpl_table_duplicate(polytraces);
07744     cpl_table_erase_invalid(table);
07745 
07746     nrows = cpl_table_get_nrow(table);
07747 
07748     if (nrows < 4) {
07749         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
07750                       "(%d): the determination of a global curvature model "
07751                       "failed", nrows);
07752         return CPL_ERROR_NONE;
07753     }
07754     
07755     order = cpl_table_get_ncol(polytraces) - 2;
07756 
07757     for (i = 0; i <= order; i++) {
07758         if (!cpl_table_has_column(table, clab[i])) {
07759             cpl_msg_error(func, "Wrong spectral curvature table");
07760             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07761         }
07762     }
07763 
07764 
07765     /*
07766      * Fill in advance the missing offset terms
07767      */
07768 
07769     for (i = 0; i < nslits; i++) {
07770         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
07771             cpl_table_set_double(polytraces, clab[0], 2*i, 
07772                                 cpl_table_get_double(slits, "ytop", i, NULL));
07773         }
07774         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
07775             cpl_table_set_double(polytraces, clab[0], 2*i+1,
07776                              cpl_table_get_double(slits, "ybottom", i, NULL));
07777         }
07778     }
07779 
07780     offset = cpl_table_get_data_double(polytraces, clab[0]);
07781 
07782 
07783     /*
07784      * Fit the global model and modify polytraces table accordingly
07785      */
07786 
07787     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
07788 
07789     for (i = 1; i <= order; i++) {
07790         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
07791         list = cpl_bivector_wrap_vectors(c0, cn);
07792         robustLinearFit(list, &q, &m, &rms);
07793 /* alternative (not robust)
07794         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
07795 */
07796         for (j = 0; j < 2*nslits; j++) {
07797             if (mode == 1)
07798                 if (cpl_table_is_valid(polytraces, clab[i], j))
07799                     continue;
07800             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
07801 /* alternative (not robust)
07802             cpl_table_set_double(polytraces, clab[i], j, 
07803                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
07804 */
07805         }
07806         cpl_bivector_unwrap_vectors(list);
07807 /* alternative (not robust)
07808         cpl_polynomial_delete(poly);
07809 */
07810         cpl_vector_unwrap(cn);
07811     }
07812 
07813     cpl_vector_unwrap(c0);
07814     cpl_table_delete(table);
07815 
07816     return CPL_ERROR_NONE;
07817 
07818 }
07819 
07820 
07890 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
07891                                    cpl_table *polytraces, double reference,
07892                                    double blue, double red, double dispersion,
07893                                    int flux, cpl_image *calibration)
07894 {
07895     const char *func = "mos_spatial_calibration";
07896 
07897     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07898                                                  /* Max order is 5 */
07899     cpl_polynomial *polytop;
07900     cpl_polynomial *polybot;
07901     cpl_image     **exslit;
07902     cpl_image      *resampled;
07903     float          *data;
07904     float          *sdata;
07905     float          *xdata;
07906     double          vtop, vbot, value;
07907     double          top, bot;
07908     double          coeff;
07909     double          ytop, ybot;
07910     double          ypos, yfra;
07911     double          factor;
07912     int             yint, ysize, yprev;
07913     int             nslits;
07914     int             npseudo;
07915     int            *slit_id;
07916     int            *length;
07917     int             nx, ny;
07918     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
07919     int             missing_top, missing_bot;
07920     int             null;
07921     int             order;
07922     int             i, j, k;
07923 
07924     int             create_position = 1;
07925 
07926 
07927     if (spectra == NULL || slits == NULL || polytraces == NULL) {
07928         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07929         return NULL;
07930     }
07931 
07932     if (dispersion <= 0.0) {
07933         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07934         return NULL;
07935     }
07936 
07937     if (red - blue < dispersion) {
07938         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07939         return NULL;
07940     }
07941 
07942     nx = cpl_image_get_size_x(spectra);
07943     ny = cpl_image_get_size_y(spectra);
07944     sdata = cpl_image_get_data(spectra);
07945     if (calibration)
07946         data = cpl_image_get_data(calibration);
07947 
07948     if (cpl_table_has_column(slits, "position"))
07949         create_position = 0;
07950 
07951     if (create_position) {
07952         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
07953         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
07954         cpl_table_set_column_unit(slits, "position", "pixel");
07955         cpl_table_set_column_unit(slits, "length", "pixel");
07956     }
07957     else
07958         length = cpl_table_get_data_int(slits, "length");
07959 
07960     nslits   = cpl_table_get_nrow(slits);
07961     slit_id  = cpl_table_get_data_int(slits, "slit_id");
07962     order    = cpl_table_get_ncol(polytraces) - 2;
07963 
07964     /*
07965      * The spatial resampling is performed for a certain number of 
07966      * pixels above and below the position of the reference wavelength:
07967      */
07968 
07969     pixel_above = (red - reference) / dispersion;
07970     pixel_below = (reference - blue) / dispersion;
07971 
07972     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
07973 
07974     for (i = 0; i < nslits; i++) {
07975         
07976         if (create_position == 0)
07977             if (length[i] == 0)
07978                 continue;
07979 
07980         /*
07981          * Note that the x coordinate of the reference pixels on the CCD
07982          * is taken arbitrarily at the top end of each slit. This wouldn't
07983          * be entirely correct in case of curved slits, or in presence of
07984          * heavy distortions: in such cases the spatial resampling is
07985          * really performed across a wide range of wavelengths. But
07986          * the lag between top and bottom spectral curvature models 
07987          * would introduce even in such cases negligible effects on
07988          * the spectral spatial resampling.
07989          */
07990 
07991         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
07992 
07993         start_pixel = refpixel - pixel_below;
07994         if (start_pixel < 0)
07995             start_pixel = 0;
07996 
07997         end_pixel = refpixel + pixel_above;
07998         if (end_pixel > nx)
07999             end_pixel = nx;
08000 
08001         /*
08002          * Recover from the table of spectral curvature coefficients
08003          * the curvature polynomials.
08004          */
08005 
08006         missing_top = 0;
08007         polytop = cpl_polynomial_new(1);
08008         for (k = 0; k <= order; k++) {
08009             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08010             if (null) {
08011                 cpl_polynomial_delete(polytop);
08012                 missing_top = 1;
08013                 break;
08014             }
08015             cpl_polynomial_set_coeff(polytop, &k, coeff);
08016         }
08017 
08018         missing_bot = 0;
08019         polybot = cpl_polynomial_new(1);
08020         for (k = 0; k <= order; k++) {
08021             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08022             if (null) {
08023                 cpl_polynomial_delete(polybot);
08024                 missing_bot = 1;
08025                 break;
08026             }
08027             cpl_polynomial_set_coeff(polybot, &k, coeff);
08028         }
08029 
08030         if (missing_top && missing_bot) {
08031             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08032                             "traced: no extraction!", 
08033                             slit_id[i]);
08034             continue;
08035         }
08036 
08037         /*
08038          * In case just one of the two edges was not traced, the other
08039          * edge curvature model is duplicated and shifted to the other
08040          * end of the slit: better than nothing!
08041          */
08042 
08043         if (missing_top) {
08044             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08045                             "the spectral curvature of the lower edge "
08046                             "is used instead.", slit_id[i]);
08047             polytop = cpl_polynomial_duplicate(polybot);
08048             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08049             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08050             k = 0;
08051             coeff = cpl_polynomial_get_coeff(polybot, &k);
08052             coeff += ytop - ybot;
08053             cpl_polynomial_set_coeff(polytop, &k, coeff);
08054         }
08055 
08056         if (missing_bot) {
08057             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08058                             "the spectral curvature of the upper edge "
08059                             "is used instead.", slit_id[i]);
08060             polybot = cpl_polynomial_duplicate(polytop);
08061             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08062             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08063             k = 0;
08064             coeff = cpl_polynomial_get_coeff(polytop, &k);
08065             coeff -= ytop - ybot;
08066             cpl_polynomial_set_coeff(polybot, &k, coeff);
08067         }
08068 
08069         /*
08070          * Allocate image for current extracted slit
08071          */
08072 
08073         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08074         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08075         npseudo = ceil(top-bot) + 1;
08076 
08077         if (npseudo < 1) {
08078             cpl_polynomial_delete(polytop);
08079             cpl_polynomial_delete(polybot);
08080             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08081                             slit_id[i]);
08082             continue;
08083         }
08084 
08085         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08086         xdata = cpl_image_get_data(exslit[i]);
08087 
08088         /*
08089          * Write interpolated values to slit image.
08090          */
08091 
08092         for (j = start_pixel; j < end_pixel; j++) {
08093             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08094             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08095             factor = (top-bot)/npseudo;
08096             for (k = 0; k <= npseudo; k++) {
08097                 ypos = top - k*factor;
08098                 yint = ypos;
08099                 yfra = ypos - yint;
08100                 if (yint >= 0 && yint < ny-1) {
08101                     vtop = sdata[j + nx*yint];
08102                     vbot = sdata[j + nx*(yint+1)];
08103                     value = vtop*(1-yfra) + vbot*yfra;
08104                     if (flux)
08105                         value *= factor;
08106                     xdata[j + nx*(npseudo-k)] = value;
08107                     if (calibration) {
08108                         data[j + nx*yint] = (top-yint)/factor;
08109                         if (k) {
08110 
08111                             /*
08112                              * This is added to recover lost pixels on
08113                              * the CCD image (pixels are lost because
08114                              * the CCD pixels are less than npseudo+1).
08115                              */
08116 
08117                             if (yprev - yint > 1) {
08118                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08119                             }
08120                         }
08121                     }
08122                 }
08123                 yprev = yint;
08124             }
08125         }
08126         cpl_polynomial_delete(polytop);
08127         cpl_polynomial_delete(polybot);
08128     }
08129 
08130     /*
08131      * Now all the slits images are copied to a single image
08132      */
08133 
08134     ysize = 0;
08135     for (i = 0; i < nslits; i++)
08136         if (exslit[i])
08137             ysize += cpl_image_get_size_y(exslit[i]);
08138 
08139     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08140 
08141     yint = -1;
08142     for (i = 0; i < nslits; i++) {
08143         if (exslit[i]) {
08144             yint += cpl_image_get_size_y(exslit[i]);
08145             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08146             if (create_position) {
08147                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08148                 cpl_table_set_int(slits, "length", i, 
08149                                   cpl_image_get_size_y(exslit[i]));
08150             }
08151             cpl_image_delete(exslit[i]);
08152         }
08153         else if (create_position) {
08154             cpl_table_set_int(slits, "position", i, -1);
08155             cpl_table_set_int(slits, "length", i, 0);
08156         }
08157     }
08158 
08159 
08160     /*
08161      * Elimination of non-traced slits from slit position table: we cannot do
08162      * it because we would lose sync with polytraces and other slit-oriented
08163      * tables. COMMENTED OUT.
08164      * 
08165 
08166     if (create_position) {
08167 
08168         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08169             cpl_table_erase_selected(slits);
08170 
08171     }
08172 
08173     */
08174 
08175     cpl_free(exslit);
08176 
08177     return resampled;
08178 
08179 }
08180 
08181 
08288 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08289                                             cpl_vector *lines,
08290                                             double dispersion, float level,
08291                                             int sradius, int order,
08292                                             double reject, double refwave,
08293                                             double *wavestart, double *waveend,
08294                                             int *nlines, double *error, 
08295                                             cpl_table *idscoeff,
08296                                             cpl_image *calibration,
08297                                             cpl_image *residuals,
08298                                             cpl_table *restable)
08299 {
08300 
08301     const char *func = "mos_wavelength_calibration_final";
08302 
08303     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08304                                                  /* Max order is 5 */
08305 
08306     double  tolerance = 20.0;    /* Probably forever...                */
08307     int     step = 10;           /* Compute restable every "step" rows */
08308 
08309     char            name[MAX_COLNAME];
08310 
08311     cpl_image      *resampled;
08312     cpl_bivector   *output;
08313     cpl_vector     *wavel;
08314     cpl_vector     *peaks;
08315     cpl_polynomial *ids;
08316     cpl_polynomial *lin;
08317     cpl_polynomial *fguess;
08318     cpl_table      *coeff;
08319     double          ids_err;
08320     double          max_disp, min_disp;
08321     double         *line;
08322     double          firstLambda, lastLambda, lambda;
08323     double          wave, pixe, value;
08324     double          c;
08325     float          *sdata;
08326     float          *rdata;
08327     float          *idata;
08328     float          *ddata;
08329     float           v1, v2, vi;
08330     float           fpixel;
08331     int            *length;
08332     int             pixstart, pixend;
08333     int             row_top, row_bot;
08334     int             extrapolation;
08335     int             nref;
08336     int             nslits;
08337     int             nfits;
08338     int             nl, nx, ny, pixel;
08339     int             countLines, usedLines;
08340     int             uorder;
08341     int             missing;
08342     int             null;
08343     int             width, uradius;
08344     int             i, j, k, s;
08345 
08346 
08347     if (dispersion == 0.0) {
08348         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
08349         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08350         return NULL;
08351     }
08352 
08353     if (dispersion < 0.0) {
08354         cpl_msg_error(func, "The expected dispersion must be positive");
08355         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08356         return NULL;
08357     }
08358 
08359     if (idscoeff == NULL) {
08360         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
08361         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08362         return NULL;
08363     }
08364 
08365     max_disp = dispersion + dispersion * tolerance / 100;
08366     min_disp = dispersion - dispersion * tolerance / 100;
08367 
08368     if (order < 1) {
08369         cpl_msg_error(func, "The order of the fitting polynomial "
08370                       "must be at least 1");
08371         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08372         return NULL;
08373     }
08374 
08375     if (image == NULL || lines == NULL) {
08376         cpl_msg_error(func, "Both spectral exposure and reference line "
08377                       "catalog are required in input");
08378         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08379         return NULL;
08380     }
08381 
08382     nx = cpl_image_get_size_x(image);
08383     ny = cpl_image_get_size_y(image);
08384     sdata = cpl_image_get_data_float(image);
08385 
08386     nref = cpl_vector_get_size(lines);
08387     line = cpl_vector_get_data(lines);
08388 
08389     if (*wavestart < 1.0 && *waveend < 1.0) {
08390         firstLambda = line[0];
08391         lastLambda = line[nref-1];
08392         extrapolation = (lastLambda - firstLambda) / 10;
08393         firstLambda -= extrapolation;
08394         lastLambda += extrapolation;
08395         *wavestart = firstLambda;
08396         *waveend = lastLambda;
08397     }
08398     else {
08399         firstLambda = *wavestart;
08400         lastLambda = *waveend;
08401     }
08402 
08403     nl = (lastLambda - firstLambda) / dispersion;
08404     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08405     rdata = cpl_image_get_data_float(resampled);
08406 
08407     /*
08408      * Allocate total output table of IDS coefficients
08409      */
08410 
08411     for (j = 0; j <= order; j++)
08412         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08413 
08414     if (calibration)
08415         idata = cpl_image_get_data_float(calibration);
08416 
08417     if (residuals)
08418         ddata = cpl_image_get_data_float(residuals);
08419 
08420     if (restable) {
08421         cpl_table_set_size(restable, nref);
08422         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08423         cpl_table_copy_data_double(restable, "wavelength", line);
08424         for (i = 0; i < ny; i += step) {
08425              snprintf(name, MAX_COLNAME, "r%d", i);
08426              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08427              snprintf(name, MAX_COLNAME, "d%d", i);
08428              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08429              snprintf(name, MAX_COLNAME, "p%d", i);
08430              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08431         }
08432     }
08433 
08434 
08435     /*
08436      * Process all slits separately.
08437      */
08438 
08439     nslits   = cpl_table_get_nrow(slits);
08440     length   = cpl_table_get_data_int(slits, "length");
08441 
08442     row_top = ny;
08443     for (s = 0; s < nslits; s++) {
08444 
08445         if (length[s] == 0)
08446             continue;
08447 
08448         /*
08449          * row_top and row_bot define the boundaries of the current slit.
08450          * Here we begin (arbitrarily...) from the top slit.
08451          */
08452 
08453         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08454 
08455         if (sradius > 0) {
08456 
08457             /*
08458              * If a search radius was defined, allocate the table of
08459              * the fitting polynomials coefficients. This table is
08460              * just used to generate the first-guess polynomial made
08461              * of the median coefficients of all polynomials found
08462              * for this slit.
08463              */
08464 
08465             coeff = cpl_table_new(row_top - row_bot);
08466             for (j = 0; j <= order; j++)
08467                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08468         }
08469 
08470         /*
08471          * Here is the loop on all rows of the current slit. They are
08472          * wavelength calibrated one by one.
08473          */
08474 
08475         for (i = row_bot; i < row_top; i++) {
08476             width = mos_lines_width(sdata + i*nx, nx);
08477             if (width < 5)
08478                 width = 5;
08479             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08480             if (peaks) {
08481                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08482             }
08483             if (peaks) {
08484                 output = mos_identify_peaks(peaks, lines, 
08485                                             min_disp, max_disp, 0.05);
08486                 if (output) {
08487                     countLines = cpl_bivector_get_size(output);
08488                     if (countLines < 4) {
08489                         cpl_bivector_delete(output);
08490                         cpl_vector_delete(peaks);
08491                         if (nlines)
08492                             nlines[i] = 0;
08493                         if (error)
08494                             error[i] = 0.0;
08495                         continue;
08496                     }
08497 
08498                     /*
08499                      * Set reference wavelength as zero point
08500                      */
08501 
08502                     wavel = cpl_bivector_get_y(output);
08503                     cpl_vector_subtract_scalar(wavel, refwave);
08504 
08505                     uorder = countLines / 2 - 1;
08506                     if (uorder > order)
08507                         uorder = order;
08508 
08509                     ids = mos_poly_wav2pix(output, uorder, reject,
08510                                            2 * (uorder + 1), &usedLines,
08511                                            &ids_err);
08512 
08513                     if (ids == NULL) {
08514                         cpl_bivector_delete(output);
08515                         cpl_vector_delete(peaks);
08516                         if (nlines)
08517                             nlines[i] = 0;
08518                         if (error)
08519                             error[i] = 0.0;
08520                         cpl_error_reset();
08521                         continue;
08522                     }
08523 
08524                     if (sradius > 0) {
08525                         for (k = 0; k <= order; k++) {
08526                             if (k > uorder) {
08527                                 cpl_table_set_double(coeff, clab[k], 
08528                                 i - row_bot, 0.0);
08529                             }
08530                             else {
08531                                 cpl_table_set_double(coeff, clab[k], 
08532                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08533                             }
08534                         }
08535                     }
08536                /*   else {   */
08537                         if (calibration) {
08538                             pixstart = cpl_polynomial_eval_1d(ids,
08539                               cpl_bivector_get_y_data(output)[0], 
08540                               NULL);
08541                             pixend = cpl_polynomial_eval_1d(ids,
08542                               cpl_bivector_get_y_data(output)[countLines-1],
08543                               NULL);
08544                             extrapolation = (pixend - pixstart) / 5;
08545                             pixstart -= extrapolation;
08546                             pixend += extrapolation;
08547                             if (pixstart < 0)
08548                                 pixstart = 0;
08549                             if (pixend > nx)
08550                                 pixend = nx;
08551    
08552                             for (j = pixstart; j < pixend; j++) {
08553                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08554                                      firstLambda, lastLambda, refwave, j);
08555                             }
08556                         }
08557 
08558                         /*
08559                          * Residuals image
08560                          */
08561         
08562                         if (residuals || (restable && !(i%step))) {
08563                             if (restable && !(i%step)) {
08564                                 lin = cpl_polynomial_new(1);
08565                                 for (k = 0; k < 2; k++)
08566                                     cpl_polynomial_set_coeff(lin, &k,
08567                                           cpl_polynomial_get_coeff(ids, &k));
08568                             }
08569                             for (j = 0; j < countLines; j++) {
08570                                 pixe = cpl_bivector_get_x_data(output)[j];
08571                                 wave = cpl_bivector_get_y_data(output)[j];
08572                                 value = pixe 
08573                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08574                                 if (residuals) {
08575                                     pixel = pixe + 0.5;
08576                                     (ddata + i*nx)[pixel] = value;
08577                                 }
08578                                 if (restable && !(i%step)) {
08579                                     for (k = 0; k < nref; k++) {
08580                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08581                                             snprintf(name, MAX_COLNAME, 
08582                                                      "r%d", i);
08583                                             cpl_table_set_double(restable, name,
08584                                                                  k, value);
08585                                             value = pixe
08586                                                   - cpl_polynomial_eval_1d(lin,
08587                                                               wave, NULL);
08588                                             snprintf(name, MAX_COLNAME, 
08589                                                      "d%d", i);
08590                                             cpl_table_set_double(restable, name,
08591                                                                  k, value);
08592                                             snprintf(name, MAX_COLNAME,
08593                                                      "p%d", i);
08594                                             cpl_table_set_double(restable, name,
08595                                                                  k, pixe);
08596                                             break;
08597                                         }
08598                                     }
08599                                 }
08600                             }
08601                             if (restable && !(i%step)) {
08602                                 cpl_polynomial_delete(lin);
08603                             }
08604 /***
08605                             for (j = 0; j < countLines; j++) {
08606                                 pixel = cpl_bivector_get_x_data(output)[j] 
08607                                       + 0.5;
08608                                 (ddata + i*nx)[pixel] =
08609                                 cpl_bivector_get_x_data(output)[j]
08610                               - cpl_polynomial_eval_1d(ids,
08611                                 cpl_bivector_get_y_data(output)[j], 
08612                                 NULL);
08613                             }
08614 ***/
08615                         }
08616                 /*  }   */
08617 
08618                     /*
08619                      * Write it anyway, even in case a first-guess based
08620                      * solution will be searched afterwards: in case of
08621                      * failure, the "blind" solution is kept.
08622                      */
08623 
08624                     if (nlines)
08625                         nlines[i] = usedLines;
08626                     if (error)
08627                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08628 
08629                     for (k = 0; k <= order; k++) {
08630                         if (k > uorder) {
08631                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08632                         }
08633                         else {
08634                             cpl_table_set_double(idscoeff, clab[k], i,
08635                                       cpl_polynomial_get_coeff(ids, &k));
08636                         }
08637                     }
08638 
08639                     cpl_polynomial_delete(ids);
08640                     cpl_bivector_delete(output);
08641                 }
08642                 cpl_vector_delete(peaks);
08643             }
08644         }       /* End of loop on current slit's rows */
08645 
08646 
08647         if (sradius > 0) {
08648 
08649             /*
08650              * See whether there are valid fits at all...
08651              */
08652     
08653             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08654     
08655             if (nfits) {
08656 
08657                 /*
08658                  * Compute a median IDS polynomial for the current slit
08659                  */
08660 
08661                 fguess = cpl_polynomial_new(1);
08662 
08663                 for (k = 0; k <= order; k++) {
08664                     c = cpl_table_get_column_median(coeff, clab[k]);
08665                     cpl_polynomial_set_coeff(fguess, &k, c);
08666                 }
08667 
08668                 for (i = row_bot; i < row_top; i++) {
08669 
08670                     /*
08671                      * Use first-guess to find the reference lines again
08672                      */
08673 
08674                     width = mos_lines_width(sdata + i*nx, nx);
08675                     if (width > sradius) {
08676                         uradius = width; 
08677                     }
08678                     else {
08679                         uradius = sradius;
08680                     }
08681 
08682                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08683                                             fguess, refwave, uradius);
08684 
08685                     if (output == NULL) {
08686                         cpl_error_reset();
08687                         continue;
08688                     }
08689 
08690                     countLines = cpl_bivector_get_size(output);
08691 
08692                     if (countLines < 4) {
08693                         cpl_bivector_delete(output);
08694                         continue;
08695                     }
08696 
08697                     /*
08698                      * Set reference wavelength as zero point
08699                      */
08700 
08701                     wavel = cpl_bivector_get_y(output);
08702                     cpl_vector_subtract_scalar(wavel, refwave);
08703 
08704                     uorder = countLines / 2 - 1;
08705                     if (uorder > order)
08706                         uorder = order;
08707 
08708                     ids = mos_poly_wav2pix(output, uorder, reject,
08709                                            2 * (uorder + 1), &usedLines,
08710                                            &ids_err);
08711 
08712                     if (ids == NULL) {
08713                         cpl_error_reset();
08714                         cpl_bivector_delete(output);
08715                         continue;
08716                     }
08717 
08718                     if (nlines)
08719                         nlines[i] = usedLines;
08720                     if (error)
08721                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08722 
08723                     if (calibration) {
08724                         pixstart = cpl_polynomial_eval_1d(ids,
08725                            cpl_bivector_get_y_data(output)[0], 
08726                            NULL);
08727                         pixend = cpl_polynomial_eval_1d(ids,
08728                            cpl_bivector_get_y_data(output)[countLines-1], 
08729                            NULL);
08730                         extrapolation = (pixend - pixstart) / 5;
08731                         pixstart -= extrapolation;
08732                         pixend += extrapolation;
08733                         if (pixstart < 0)
08734                             pixstart = 0;
08735                         if (pixend > nx)
08736                             pixend = nx;
08737 
08738                         for (j = pixstart; j < pixend; j++) {
08739                             (idata + i*nx)[j] = mos_eval_dds(ids,
08740                                      firstLambda, lastLambda, refwave, j);
08741                         }
08742                     }
08743 
08744                     /*
08745                      * Residuals image
08746                      */
08747 
08748                     if (residuals || (restable && !(i%step))) {
08749                         if (restable && !(i%step)) {
08750                             lin = cpl_polynomial_new(1);
08751                             for (k = 0; k < 2; k++)
08752                                 cpl_polynomial_set_coeff(lin, &k,
08753                                       cpl_polynomial_get_coeff(ids, &k));
08754                         }
08755                         for (j = 0; j < countLines; j++) {
08756                             pixe = cpl_bivector_get_x_data(output)[j];
08757                             wave = cpl_bivector_get_y_data(output)[j];
08758                             value = pixe
08759                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
08760                             if (residuals) {
08761                                 pixel = pixe + 0.5;
08762                                 (ddata + i*nx)[pixel] = value;
08763                             }
08764                             if (restable && !(i%step)) {
08765                                 for (k = 0; k < nref; k++) {
08766                                     if (fabs(line[k]-refwave-wave) < 0.1) {
08767                                         snprintf(name, MAX_COLNAME,
08768                                                  "r%d", i);
08769                                         cpl_table_set_double(restable, name,
08770                                                              k, value);
08771                                         value = pixe
08772                                               - cpl_polynomial_eval_1d(lin,
08773                                                           wave, NULL);
08774                                         snprintf(name, MAX_COLNAME,
08775                                                  "d%d", i);
08776                                         cpl_table_set_double(restable, name,
08777                                                              k, value);
08778                                         snprintf(name, MAX_COLNAME,
08779                                                  "p%d", i);
08780                                         cpl_table_set_double(restable, name,
08781                                                              k, pixe);
08782                                         break;
08783                                     }
08784                                 }
08785                             }
08786                         }
08787                         if (restable && !(i%step)) {
08788                             cpl_polynomial_delete(lin);
08789                         }
08790 /***
08791                         for (j = 0; j < countLines; j++) {
08792                             pixel = cpl_bivector_get_x_data(output)[j]
08793                                   + 0.5; 
08794                             (ddata + i*nx)[pixel] =
08795                             cpl_bivector_get_x_data(output)[j]
08796                           - cpl_polynomial_eval_1d(ids,
08797                             cpl_bivector_get_y_data(output)[j], 
08798                             NULL);
08799                         }
08800 ***/
08801                     }
08802 
08803                     for (k = 0; k <= order; k++) {
08804                         if (k > uorder) {
08805                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08806                         }
08807                         else {
08808                             cpl_table_set_double(idscoeff, clab[k], i,
08809                                         cpl_polynomial_get_coeff(ids, &k));
08810                         }
08811                     }
08812 
08813                     cpl_bivector_delete(output);
08814                     cpl_polynomial_delete(ids);
08815 
08816                 } /* End of loop "use ids as a first-guess" */
08817 
08818                 cpl_polynomial_delete(fguess);
08819             }
08820 
08821             cpl_table_delete(coeff);
08822 
08823         }
08824 
08825         row_top = row_bot;
08826 
08827     } /* End of loop on slits */
08828 
08829 
08830     /*
08831      * At this point the idscoeff table has been filled with all the 
08832      * fits coefficients obtained for all the rows of the input image.
08833      * Now we apply these coefficients to resample the input image
08834      * at constant wavelength step.
08835      */
08836 
08837     for (i = 0; i < ny; i++) {
08838 
08839         missing = 0;
08840         ids = cpl_polynomial_new(1);
08841         for (k = 0; k <= order; k++) {
08842             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
08843             if (null) {
08844                 cpl_polynomial_delete(ids);
08845                 missing = 1;
08846                 break;
08847             }
08848             cpl_polynomial_set_coeff(ids, &k, c);
08849         }
08850         if (missing)
08851             continue;
08852 
08853         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
08854         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
08855         if (pixstart < 0)
08856             pixstart = 0;
08857         if (pixend > nx)
08858             pixend = nx;
08859 
08860         /*
08861          * Resampled image:
08862          */
08863 
08864         for (j = 0; j < nl; j++) {
08865             lambda = firstLambda + j * dispersion;
08866             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
08867             pixel = fpixel;
08868             if (pixel >= 0 && pixel < nx-1) {
08869                 v1 = (sdata + i*nx)[pixel];
08870                 v2 = (sdata + i*nx)[pixel+1];
08871                 vi = v1 + (v2-v1)*(fpixel-pixel);
08872                 (rdata + i*nl)[j] = vi;
08873             }
08874         }
08875 
08876         cpl_polynomial_delete(ids);
08877     }
08878 
08879     return resampled;
08880 }
08881 
08882 
08909 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
08910                                       double firstLambda, double lastLambda, 
08911                                       double dispersion, cpl_table *idscoeff, 
08912                                       int flux)
08913 {
08914 
08915     const char *func = "mos_wavelength_calibration";
08916 
08917     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08918                                                  /* Max order is 5 */
08919 
08920     cpl_image      *resampled;
08921     cpl_polynomial *ids;
08922     double          pixel_per_lambda;
08923     double          lambda;
08924     double          c;
08925     float          *sdata;
08926     float          *rdata;
08927     float           v0, v1, v2, v3, vi;
08928     float           fpixel;
08929     int             order;
08930     int             pixstart, pixend;
08931     int             nl, nx, ny, pixel;
08932     int             missing;
08933     int             null;
08934     int             i, j, k;
08935 
08936 
08937     if (dispersion <= 0.0) {
08938         cpl_msg_error(func, "The resampling step must be positive");
08939         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08940         return NULL;
08941     }
08942 
08943     if (lastLambda - firstLambda < dispersion) {
08944         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
08945                       firstLambda, lastLambda);
08946         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08947         return NULL;
08948     }
08949 
08950     if (idscoeff == NULL) {
08951         cpl_msg_error(func, "An IDS coeff table must be given");
08952         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08953         return NULL;
08954     }
08955 
08956     if (image == NULL) {
08957         cpl_msg_error(func, "A scientific spectral image must be given");
08958         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08959         return NULL;
08960     }
08961 
08962     nx = cpl_image_get_size_x(image);
08963     ny = cpl_image_get_size_y(image);
08964     sdata = cpl_image_get_data_float(image);
08965 
08966     nl = (lastLambda - firstLambda) / dispersion;
08967     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08968     rdata = cpl_image_get_data_float(resampled);
08969 
08970     order = 0;
08971     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
08972         ++order;
08973     --order;
08974 
08975     for (i = 0; i < ny; i++) {
08976 
08977         missing = 0;
08978         ids = cpl_polynomial_new(1);
08979         for (k = 0; k <= order; k++) {
08980             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
08981             if (null) {
08982                 cpl_polynomial_delete(ids);
08983                 missing = 1;
08984                 break;
08985             }
08986             cpl_polynomial_set_coeff(ids, &k, c);
08987         }
08988         if (missing)
08989             continue;
08990 
08991         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
08992         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
08993         if (pixstart < 0)
08994             pixstart = 0;
08995         if (pixend > nx)
08996             pixend = nx;
08997 
08998         /*
08999          * Resampled image:
09000          */
09001 
09002         for (j = 0; j < nl; j++) {
09003             lambda = firstLambda + j * dispersion;
09004             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09005                                             &pixel_per_lambda);
09006 
09007             /*
09008              * The local dispersion is 1 / pixel_per_lambda
09009              * and this factor is used for applying the flux
09010              * conservation correction (if requested).
09011              */
09012 
09013             pixel = fpixel;
09014             if (pixel >= 1 && pixel < nx-2) {
09015                 v0 = (sdata + i*nx)[pixel-1];
09016                 v1 = (sdata + i*nx)[pixel];
09017                 v2 = (sdata + i*nx)[pixel+1];
09018                 v3 = (sdata + i*nx)[pixel+2];
09019                 vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09020                    + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09021                    + 2*v1;
09022                 vi /= 2;
09023                 if (v1 > v2) {
09024                     if (vi > v1) { 
09025                         vi = v1;
09026                     }
09027                     else if (vi < v2) {
09028                         vi = v2;
09029                     }
09030                 }
09031                 else {
09032                     if (vi > v2) { 
09033                         vi = v2;
09034                     }
09035                     else if (vi < v1) {
09036                         vi = v1;
09037                     }
09038                 }
09039                 if (flux)
09040                     vi *= dispersion * pixel_per_lambda;
09041                 (rdata + i*nl)[j] = vi;
09042             }
09043             else if (pixel >= 0 && pixel < nx-1) {
09044                 v1 = (sdata + i*nx)[pixel];
09045                 v2 = (sdata + i*nx)[pixel+1];
09046                 vi = v1 + (v2-v1)*(fpixel-pixel);
09047                 if (flux)
09048                     vi *= dispersion * pixel_per_lambda;
09049                 (rdata + i*nl)[j] = vi;
09050             }
09051         }
09052 
09053         cpl_polynomial_delete(ids);
09054     }
09055 
09056     return resampled;
09057 }
09058 
09059 
09126 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
09127                                 double refwave, double firstLambda, 
09128                                 double lastLambda, cpl_table *idscoeff,
09129                                 cpl_vector *skylines, int highres, int order,
09130                                 cpl_image *calibration, int sradius)
09131 {
09132     const char *func = "mos_wavelength_align";
09133 
09134     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09135                                                  /* Max order is 5 */
09136     double         *line;
09137     double         *data;
09138     double          expPos, offset;
09139     double          c;
09140     double          lambda1, lambda2;
09141     double          rms;
09142     float           pos;
09143     float          *sdata;
09144     float          *cdata;
09145     int            *idata;
09146     int             startPos, endPos;
09147     int             window = 2*sradius + 1;
09148     int             nlines;
09149     int             nslits;
09150     int             npoints;
09151     int             nrows;
09152     int             nx, ny;
09153     int             xlow, ylow, xhig, yhig;
09154     int             idsorder, uorder;
09155     int            *slit_id;
09156     int            *position;
09157     int            *length;
09158     int             missing;
09159     int             null;
09160     int             i, j, k;
09161 
09162     char            offname[MAX_COLNAME];
09163     char            name[MAX_COLNAME];
09164 
09165     cpl_polynomial *ids;
09166     cpl_polynomial *polycorr;
09167     cpl_image      *exslit;
09168     cpl_image      *sky;
09169     cpl_table      *offsets;
09170     cpl_table      *dummy;
09171     cpl_vector     *wave;
09172     cpl_vector     *offs;
09173     
09174 
09175     if (idscoeff == NULL) {
09176         cpl_msg_error(func, "An IDS coeff table must be given");
09177         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09178         return NULL;
09179     }
09180 
09181     if (image == NULL) {
09182         cpl_msg_error(func, "A scientific spectral image must be given");
09183         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09184         return NULL;
09185     }
09186 
09187     if (slits == NULL) {
09188         cpl_msg_error(func, "A slit position table must be given");
09189         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09190         return NULL;
09191     }
09192 
09193     if (skylines) {
09194         line = cpl_vector_get_data(skylines);
09195         nlines = cpl_vector_get_size(skylines);
09196     }
09197     else {
09198         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09199                         "given: using internal list of reference sky lines");
09200         if (highres) {
09201            line = default_lines_hi;
09202            nlines = sizeof(default_lines_hi) / sizeof(double);
09203         }
09204         else {
09205            line = default_lines_lo;
09206            nlines = sizeof(default_lines_lo) / sizeof(double);
09207         }
09208     }
09209 
09210     if (calibration)
09211         cdata = cpl_image_get_data(calibration);
09212 
09213     nx = cpl_image_get_size_x(image);
09214     ny = cpl_image_get_size_y(image);
09215 
09216     nslits   = cpl_table_get_nrow(slits);
09217     slit_id  = cpl_table_get_data_int(slits, "slit_id");
09218     position = cpl_table_get_data_int(slits, "position");
09219     length   = cpl_table_get_data_int(slits, "length");
09220 
09221 
09222     /*
09223      * Define the output table of offsets
09224      */
09225 
09226     nrows = 0;
09227     for (i = 0; i < nlines; i++)
09228         if (line[i] > firstLambda && line[i] < lastLambda)
09229             nrows++;
09230 
09231     offsets = cpl_table_new(nrows);
09232     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09233     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09234 
09235     nrows = 0;
09236     for (i = 0; i < nlines; i++) {
09237         if (line[i] > firstLambda && line[i] < lastLambda) {
09238             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09239             nrows++;
09240         }
09241     }
09242 
09243     /*
09244      * Here "line" is made to point to the new list of selected wavelengths
09245      */
09246 
09247     line = cpl_table_get_data_double(offsets, "wave");
09248     nlines = nrows;
09249 
09250     idsorder = 0;
09251     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09252         ++idsorder;
09253     --idsorder;
09254 
09255     xlow = 1;
09256     xhig = nx;
09257     for (i = 0; i < nslits; i++) {
09258 
09259         if (length[i] == 0)
09260             continue;
09261 
09262         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09263         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
09264 
09265         /* 
09266          * Define the extraction boundaries. We DON'T write:
09267          *
09268          * ylow = position[i];
09269          * yhig = ylow + length[i];
09270          *
09271          * because the cpl_image pixels are counted from 1, and because in
09272          * cpl_image_extract() the coordinates of the last pixel are inclusive.
09273          */
09274 
09275         ylow = position[i] + 1;
09276         yhig = ylow + length[i] - 1;
09277 
09278         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
09279         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
09280         sdata  = cpl_image_get_data(sky);
09281 
09282         cpl_image_delete(exslit);
09283 
09284         /* 
09285          * Return here to a decent way of counting pixels (i.e., starting
09286          * from 0)
09287          */
09288          
09289         ylow--;
09290 
09291         /*
09292          * Allocate a dummy table for collecting all the offsets
09293          * for all the lines: this is only needed for the computation
09294          * of the median offset for each sky line
09295          */
09296 
09297         dummy = cpl_table_new(yhig - ylow);
09298         for (j = 0; j < nlines; j++) {
09299             snprintf(name, MAX_COLNAME, "%d", j);
09300             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09301         }
09302 
09303         for (j = ylow; j < yhig; j++) {
09304 
09305             /*
09306              * Get the IDS polynomial for the current slit row
09307              */
09308 
09309             missing = 0;
09310             ids = cpl_polynomial_new(1);
09311             for (k = 0; k <= idsorder; k++) {
09312                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
09313                 if (null) {
09314                     cpl_polynomial_delete(ids);
09315                     missing = 1;
09316                     break;
09317                 }
09318                 cpl_polynomial_set_coeff(ids, &k, c);
09319             }
09320             if (missing)
09321                 continue;
09322 
09323             for (k = 0; k < nlines; k++) {
09324                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09325                 startPos = expPos - sradius;
09326                 endPos   = startPos + window;
09327                 if (startPos < 0 || endPos >= nx)
09328                     continue;
09329            
09330                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09331                     pos += startPos;
09332                     offset = pos - expPos;
09333                     snprintf(name, MAX_COLNAME, "%d", k);
09334                     cpl_table_set_double(dummy, name, j - ylow, offset);
09335                 }
09336             }
09337 
09338             cpl_polynomial_delete(ids);
09339         }
09340 
09341         cpl_image_delete(sky);
09342 
09343         for (j = 0; j < nlines; j++) {
09344             snprintf(name, MAX_COLNAME, "%d", j);
09345             if (cpl_table_has_valid(dummy, name)) {
09346                 offset = cpl_table_get_column_median(dummy, name);
09347                 cpl_table_set_double(offsets, offname, j, offset);
09348             }
09349         }
09350 
09351         cpl_table_delete(dummy);
09352 
09353     }
09354 
09355 
09356     /*
09357      * In the following the input idscoeff table is modified by simply
09358      * adding the coefficients of the polynomial used to fit the sky
09359      * line residuals to the coefficients of the IDS polynomials.
09360      */
09361 
09362     for (i = 0; i < nslits; i++) {
09363 
09364         if (length[i] == 0)
09365             continue;
09366 
09367         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09368 
09369         /*
09370          * In the following, the "dummy" table is just a tool for
09371          * eliminating invalid points from the vectors to be fitted.
09372          */
09373 
09374         dummy = cpl_table_new(nlines);
09375         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
09376         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
09377 
09378         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
09379         if (npoints == 0) {
09380             cpl_msg_warning(func, "No sky lines alignment was possible "
09381                             "for slit ID=%d: no sky line found", slit_id[i]);
09382             cpl_table_delete(dummy);
09383             continue;
09384         }
09385 
09386         uorder = order;
09387         if (npoints <= uorder) {
09388             uorder = npoints - 1;
09389             if (uorder) {
09390                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09391                                 "ID=%d, while a polynomial order %d was "
09392                                 "requested. Using polynomial order %d for "
09393                                 "this slit!", npoints, slit_id[i], order, 
09394                                 uorder);
09395             }
09396             else {
09397                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09398                                 "ID=%d, while a polynomial order %d was "
09399                                 "requested. Computing a median offset for "
09400                                 "this slit!", npoints, slit_id[i], order);
09401             }
09402         }
09403 
09404         cpl_table_erase_invalid(dummy);
09405 
09406         if (uorder > 1) {
09407 
09408             /*
09409              * Model offsets with polynomial fitting
09410              */
09411 
09412             wave = cpl_vector_wrap(npoints,
09413                                    cpl_table_get_data_double(dummy, "wave"));
09414             offs = cpl_vector_wrap(npoints,
09415                                    cpl_table_get_data_double(dummy, "offset"));
09416 
09417             /*
09418              * Set reference wavelength as zero point
09419              */
09420 
09421             cpl_vector_subtract_scalar(wave, refwave);
09422 
09423             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09424 
09425             rms = sqrt(rms * (uorder + 1) / npoints);
09426 
09427             cpl_vector_unwrap(wave);
09428             cpl_vector_unwrap(offs);
09429             cpl_table_delete(dummy);
09430 
09431             /*
09432              * Now correct the coefficients of the corresponding IDS
09433              * polynomials related to this slit:
09434              */
09435 
09436             ylow = position[i];
09437             yhig = ylow + length[i];
09438 
09439             for (j = 0; j <= uorder; j++) {
09440                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09441                 c = cpl_polynomial_get_coeff(polycorr, &j);
09442                 for (k = ylow; k < yhig; k++)
09443                     data[k] += c;
09444             }
09445 
09446             data = cpl_table_get_data_double(idscoeff, "error");
09447             for (k = ylow; k < yhig; k++)
09448                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09449 
09450             idata = cpl_table_get_data_int(idscoeff, "nlines");
09451             for (k = ylow; k < yhig; k++)
09452                  idata[k] = npoints;
09453 
09454             /*
09455              * If a wavelengths map was provided, correct it to keep
09456              * into account the alignment to skylines:
09457              */
09458 
09459             if (calibration) {
09460                 for (j = ylow; j < yhig; j++) {
09461                     for (k = 1; k < nx; k++) {
09462                         lambda1 = cdata[k - 1 + j*nx];
09463                         lambda2 = cdata[k + j*nx];
09464                         if (lambda1 < 1.0 || lambda2 < 1.0)
09465                             continue;
09466                         offset = cpl_polynomial_eval_1d(polycorr, 
09467                                                         lambda1-refwave, NULL);
09468                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09469                     }
09470                 }
09471             }
09472     
09473             cpl_polynomial_delete(polycorr);
09474         }
09475         else if (uorder == 1) {
09476 
09477             /*
09478              * Model offsets with robust linear fitting
09479              */
09480 
09481             double        q, m;
09482             cpl_bivector *list;
09483 
09484 
09485             wave = cpl_vector_wrap(npoints,
09486                                    cpl_table_get_data_double(dummy, "wave"));
09487             offs = cpl_vector_wrap(npoints,
09488                                    cpl_table_get_data_double(dummy, "offset"));
09489 
09490             list = cpl_bivector_wrap_vectors(wave, offs);
09491 
09492             /*
09493              * Set reference wavelength as zero point
09494              */
09495 
09496             cpl_vector_subtract_scalar(wave, refwave);
09497 
09498             robustLinearFit(list, &q, &m, &rms);
09499 
09500             rms = sqrt(rms * (uorder + 1) / npoints);
09501 
09502             cpl_bivector_unwrap_vectors(list);
09503             cpl_vector_unwrap(wave);
09504             cpl_vector_unwrap(offs);
09505             cpl_table_delete(dummy);
09506 
09507             /*
09508              * Now correct the coefficients of the corresponding IDS
09509              * polynomials related to this slit:
09510              */
09511 
09512             ylow = position[i];
09513             yhig = ylow + length[i];
09514 
09515             for (j = 0; j <= uorder; j++) {
09516                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09517                 if (j)
09518                     c = m;
09519                 else
09520                     c = q;
09521                 for (k = ylow; k < yhig; k++)
09522                     data[k] += c;
09523             }
09524 
09525             data = cpl_table_get_data_double(idscoeff, "error");
09526             for (k = ylow; k < yhig; k++)
09527                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09528 
09529             idata = cpl_table_get_data_int(idscoeff, "nlines");
09530             for (k = ylow; k < yhig; k++)
09531                  idata[k] = npoints;
09532 
09533             /*
09534              * If a wavelengths map was provided, correct it to keep
09535              * into account the alignment to skylines:
09536              */
09537 
09538             if (calibration) {
09539                 for (j = ylow; j < yhig; j++) {
09540                     for (k = 1; k < nx; k++) {
09541                         lambda1 = cdata[k - 1 + j*nx];
09542                         lambda2 = cdata[k + j*nx];
09543                         if (lambda1 < 1.0 || lambda2 < 1.0)
09544                             continue;
09545                         offset = q + m*(lambda1-refwave);
09546                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09547                     }
09548                 }
09549             }
09550         }
09551         else {
09552 
09553             /*
09554              * Just compute median offset
09555              */
09556 
09557             offs = cpl_vector_wrap(npoints,
09558                                    cpl_table_get_data_double(dummy, "offset"));
09559 
09560             offset = cpl_vector_get_median_const(offs);
09561 
09562             if (npoints > 1)
09563                 rms = cpl_table_get_column_stdev(dummy, "offset");
09564             else
09565                 rms = 0.0;
09566 
09567             rms /= sqrt(npoints);
09568 
09569             cpl_vector_unwrap(offs);
09570             cpl_table_delete(dummy);
09571 
09572             /*
09573              * Now correct the constant term of the corresponding IDS
09574              * polynomials related to this slit:
09575              */
09576 
09577             ylow = position[i];
09578             yhig = ylow + length[i];
09579 
09580             data = cpl_table_get_data_double(idscoeff, clab[0]);
09581             for (k = ylow; k < yhig; k++)
09582                 data[k] += offset;
09583 
09584             data = cpl_table_get_data_double(idscoeff, "error");
09585             for (k = ylow; k < yhig; k++)
09586                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09587 
09588             idata = cpl_table_get_data_int(idscoeff, "nlines");
09589             for (k = ylow; k < yhig; k++)
09590                  idata[k] = npoints;
09591 
09592             /*
09593              * If a wavelengths map was provided, correct it to keep
09594              * into account the alignment to skylines. Note that 
09595              * the offset must be converted from pixels to wavelengths.
09596              */
09597 
09598             if (calibration) {
09599                 for (j = ylow; j < yhig; j++) {
09600                     for (k = 1; k < nx; k++) {
09601                         lambda1 = cdata[k - 1 + j*nx];
09602                         lambda2 = cdata[k + j*nx];
09603                         if (lambda1 < 1.0 || lambda2 < 1.0)
09604                             continue; 
09605                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09606                     }
09607                 }
09608             }
09609         }
09610     }
09611 
09612     return offsets;
09613 
09614 }
09615 
09616 
09678 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
09679                                     double firstLambda, double lastLambda, 
09680                                     cpl_table *idscoeff, cpl_vector *skylines, 
09681                                     int highres, int order, 
09682                                     cpl_image *calibration, int sradius)
09683 {
09684     const char *func = "mos_wavelength_align_lss";
09685 
09686     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09687                                                  /* Max order is 5 */
09688     double         *line;
09689     double         *data;
09690     double         *wdata;
09691     double         *odata;
09692     double          expPos, offset;
09693     double          c;
09694     double          lambda1, lambda2;
09695     double          rms;
09696     float           pos;
09697     float          *sdata;
09698     float          *cdata;
09699     int            *idata;
09700     int             startPos, endPos;
09701     int             window = 2*sradius + 1;
09702     int             nlines;
09703     int             npoints;
09704     int             nrows;
09705     int             nx, ny;
09706     int             idsorder, uorder;
09707     int             missing;
09708     int             i, j, k;
09709 
09710     char            name[MAX_COLNAME];
09711     char            fname[MAX_COLNAME];
09712 
09713     cpl_polynomial *ids;
09714     cpl_polynomial *polycorr;
09715     cpl_table      *offsets;
09716     cpl_table      *fittable;
09717     cpl_table      *dummy;
09718     cpl_vector     *wave;
09719     cpl_vector     *offs;
09720     cpl_vector     *row;
09721     
09722 
09723     if (idscoeff == NULL) {
09724         cpl_msg_error(func, "An IDS coeff table must be given");
09725         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09726         return NULL;
09727     }
09728 
09729     if (image == NULL) {
09730         cpl_msg_error(func, "A scientific spectral image must be given");
09731         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09732         return NULL;
09733     }
09734 
09735     if (skylines) {
09736         line = cpl_vector_get_data(skylines);
09737         nlines = cpl_vector_get_size(skylines);
09738     }
09739     else {
09740         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09741                         "given: using internal list of reference sky lines");
09742         if (highres) {
09743            line = default_lines_hi;
09744            nlines = sizeof(default_lines_hi) / sizeof(double);
09745         }
09746         else {
09747            line = default_lines_lo;
09748            nlines = sizeof(default_lines_lo) / sizeof(double);
09749         }
09750     }
09751 
09752     if (calibration)
09753         cdata = cpl_image_get_data(calibration);
09754 
09755     nx = cpl_image_get_size_x(image);
09756     ny = cpl_image_get_size_y(image);
09757 
09758     sdata = cpl_image_get_data(image);
09759     
09760 
09761     /*FIXME: This is a remnant of the adaptation of the function
09762      * mos_wavelength_align(), where an offset table was created.
09763      * I leave it here because I am in a hurry, it is just used to
09764      * hold the list of selected sky lines.
09765      *
09766      * Define table of wavelengths
09767      */
09768 
09769     nrows = 0;
09770     for (i = 0; i < nlines; i++)
09771         if (line[i] > firstLambda && line[i] < lastLambda)
09772             nrows++;
09773 
09774     offsets = cpl_table_new(nrows);
09775     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09776     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09777 
09778     nrows = 0;
09779     for (i = 0; i < nlines; i++) {
09780         if (line[i] > firstLambda && line[i] < lastLambda) {
09781             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09782             nrows++;
09783         }
09784     }
09785 
09786     /*
09787      * Here "line" is made to point to the new list of selected wavelengths
09788      */
09789 
09790     line = cpl_table_get_data_double(offsets, "wave");
09791     nlines = nrows;
09792 
09793     idsorder = 0;
09794     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09795         ++idsorder;
09796     --idsorder;
09797 
09798 
09799     /*
09800      * Allocate a dummy table for collecting all the offsets
09801      * for all the lines
09802      */
09803 
09804     dummy = cpl_table_new(ny);
09805     for (j = 0; j < nlines; j++) {
09806         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09807         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09808         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09809         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
09810     }
09811 
09812     for (j = 0; j < ny; j++, sdata += nx) {
09813 
09814         /*
09815          * Get the IDS polynomial for the current slit row
09816          */
09817 
09818         missing = 0;
09819         ids = cpl_polynomial_new(1);
09820         for (k = 0; k <= idsorder; k++) {
09821             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
09822             if (missing) {
09823                 cpl_polynomial_delete(ids);
09824                 break;
09825             }
09826             cpl_polynomial_set_coeff(ids, &k, c);
09827         }
09828         if (missing)
09829             continue;
09830 
09831         for (k = 0; k < nlines; k++) {
09832             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09833             startPos = expPos - sradius;
09834             endPos   = startPos + window;
09835             if (startPos < 0 || endPos >= nx)
09836                 continue;
09837            
09838             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09839                 pos += startPos;
09840                 offset = pos - expPos;
09841                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
09842                 cpl_table_set_double(dummy, name, j, offset);
09843             }
09844         }
09845 
09846         cpl_polynomial_delete(ids);
09847     }
09848 
09849 
09850     /*
09851      * At this point for each sky line we model its offset along
09852      * the image rows using a robust linear fitting
09853      */
09854 
09855     for (j = 0; j < nlines; j++) {
09856         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
09857         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
09858         if (cpl_table_has_valid(dummy, name)) {
09859 
09860             /*
09861              * In the following, the "fittable" is just a tool for
09862              * eliminating invalid points from the vectors to be fitted.
09863              */
09864 
09865             double        q, m;
09866             cpl_bivector *list;
09867 
09868             fittable = cpl_table_new(ny);
09869             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
09870             cpl_table_set_column_unit(fittable, "row", "pixel");
09871             for (k = 0; k < ny; k++)
09872                  cpl_table_set_double(fittable, "row", k, k);
09873             cpl_table_duplicate_column(fittable, "offset", dummy, name);
09874             npoints = ny - cpl_table_count_invalid(fittable, "offset");
09875             cpl_table_erase_invalid(fittable);
09876             row = cpl_vector_wrap(npoints,
09877                                cpl_table_get_data_double(fittable, "row"));
09878             offs = cpl_vector_wrap(npoints,
09879                                cpl_table_get_data_double(fittable, "offset"));
09880             list = cpl_bivector_wrap_vectors(row, offs);
09881             robustLinearFit(list, &q, &m, &rms);
09882             cpl_bivector_unwrap_vectors(list);
09883             cpl_vector_unwrap(row);
09884             cpl_vector_unwrap(offs);
09885             cpl_table_delete(fittable);
09886             for (k = 0; k < ny; k++)
09887                  cpl_table_set_double(dummy, fname, k, q + m*k);
09888         }
09889     }
09890 
09891 
09892     /*
09893      * Now each dummy table row consists of a sequence of offsets,
09894      * one for each wavelength. A table row corresponds to an image row.
09895      * We must fit a polynomial to each one of these rows, in order to
09896      * express the offsets as a function of wavelength. The obtained 
09897      * polynomial coefficients are used to correct the IDS coefficients.
09898      */
09899 
09900     for (i = 0; i < ny; i++) {
09901 
09902         if (!cpl_table_is_valid(idscoeff, clab[0], i))
09903             continue;
09904 
09905         npoints = 0;
09906         for (j = 0; j < nlines; j++) {
09907             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09908             if (cpl_table_is_valid(dummy, name, i))
09909                 npoints++;
09910         }
09911 
09912         if (npoints == 0)
09913             continue;
09914 
09915         uorder = order;
09916         if (npoints <= uorder)
09917             uorder = npoints - 1;
09918 
09919         if (uorder > 1) {
09920 
09921             /*
09922              * Model offsets with polynomial fitting
09923              */
09924 
09925             wave = cpl_vector_new(npoints);
09926             wdata = cpl_vector_get_data(wave);
09927             offs = cpl_vector_new(npoints);
09928             odata = cpl_vector_get_data(offs);
09929 
09930             npoints = 0;
09931             for (j = 0; j < nlines; j++) {
09932                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
09933                 if (cpl_table_is_valid(dummy, name, i)) {
09934                     wdata[npoints] = line[j] - refwave;
09935                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
09936                     npoints++;
09937                 }
09938             }
09939 
09940             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09941 
09942             rms = sqrt(rms * (uorder + 1) / npoints);
09943 
09944             cpl_vector_delete(wave);
09945             cpl_vector_delete(offs);
09946 
09947             /*
09948              * Now correct the coefficients of the corresponding IDS
09949              * polynomials related to this slit:
09950              */
09951 
09952             for (j = 0; j <= uorder; j++) {
09953                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09954                 c = cpl_polynomial_get_coeff(polycorr, &j);
09955                 data[i] += c;
09956             }
09957 
09958             data = cpl_table_get_data_double(idscoeff, "error");
09959             data[i] = sqrt(data[i]*data[i] + rms*rms);
09960 
09961             idata = cpl_table_get_data_int(idscoeff, "nlines");
09962             idata[i] = npoints;
09963 
09964             /*
09965              * If a wavelengths map was provided, correct it to keep
09966              * into account the alignment to skylines:
09967              */
09968 
09969             if (calibration) {
09970                 for (k = 1; k < nx; k++) {
09971                     lambda1 = cdata[k - 1 + i*nx];
09972                     lambda2 = cdata[k + i*nx];
09973                     if (lambda1 < 1.0 || lambda2 < 1.0)
09974                         continue;
09975                     offset = cpl_polynomial_eval_1d(polycorr,
09976                                                     lambda1-refwave, NULL);
09977                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
09978                 }
09979             }
09980 
09981             cpl_polynomial_delete(polycorr);
09982 
09983         }
09984         else if (uorder == 1) {
09985 
09986             /*
09987              * Model offsets with robust linear fitting
09988              */
09989 
09990             cpl_bivector *list;
09991             double        q, m;
09992 
09993             wave = cpl_vector_new(npoints);
09994             wdata = cpl_vector_get_data(wave);
09995             offs = cpl_vector_new(npoints);
09996             odata = cpl_vector_get_data(offs);
09997 
09998             npoints = 0;
09999             for (j = 0; j < nlines; j++) {
10000                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10001                 if (cpl_table_is_valid(dummy, name, i)) {
10002                     wdata[npoints] = line[j] - refwave;
10003                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10004                     npoints++;
10005                 }
10006             }
10007 
10008             list = cpl_bivector_wrap_vectors(wave, offs);
10009             robustLinearFit(list, &q, &m, &rms);
10010 
10011             rms = sqrt(rms * (uorder + 1) / npoints);
10012 
10013             cpl_bivector_unwrap_vectors(list);
10014             cpl_vector_delete(wave);
10015             cpl_vector_delete(offs);
10016 
10017             /*
10018              * Now correct the coefficients of the corresponding IDS
10019              * polynomials related to this row:
10020              */
10021 
10022             for (j = 0; j <= uorder; j++) {
10023                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10024                 if (j)
10025                     c = m;
10026                 else
10027                     c = q;
10028                 data[i] += c;
10029             }
10030 
10031             data = cpl_table_get_data_double(idscoeff, "error");
10032             data[i] = sqrt(data[i]*data[i] + rms*rms);
10033 
10034             idata = cpl_table_get_data_int(idscoeff, "nlines");
10035             idata[i] = npoints;
10036 
10037             /*
10038              * If a wavelengths map was provided, correct it to keep
10039              * into account the alignment to skylines:
10040              */
10041 
10042             if (calibration) {
10043                 for (k = 1; k < nx; k++) {
10044                     lambda1 = cdata[k - 1 + i*nx];
10045                     lambda2 = cdata[k + i*nx];
10046                     if (lambda1 < 1.0 || lambda2 < 1.0)
10047                         continue;
10048                     offset = q + m*(lambda1-refwave);
10049                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10050                 }
10051             }
10052         }
10053         else {
10054 
10055             /*
10056              * Just compute median offset
10057              */
10058 
10059             offs = cpl_vector_new(npoints);
10060             odata = cpl_vector_get_data(offs);
10061 
10062             npoints = 0;
10063             for (j = 0; j < nlines; j++) {
10064                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10065                 if (cpl_table_is_valid(dummy, name, i)) {
10066                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10067                     npoints++;
10068                 }
10069             }
10070 
10071             offset = cpl_vector_get_median_const(offs);
10072 
10073             if (npoints > 1) {
10074                 rms = cpl_vector_get_stdev(offs);
10075             }
10076             else if (npoints == 1) {
10077                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10078                 if (cpl_table_has_valid(dummy, name)) {
10079                     rms = cpl_table_get_column_stdev(dummy, name);
10080                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10081                 }
10082                 else {
10083                     rms = 0.0;
10084                 }
10085             }
10086             else {
10087                 rms = 0.0;
10088             }
10089 
10090             rms /= sqrt(npoints);
10091 
10092             cpl_vector_delete(offs);
10093 
10094             /*
10095              * Now correct the constant term of the corresponding IDS
10096              * polynomials related to this slit:
10097              */
10098 
10099             data = cpl_table_get_data_double(idscoeff, clab[0]);
10100             data[i] += offset;
10101 
10102             data = cpl_table_get_data_double(idscoeff, "error");
10103             data[i] = sqrt(data[i]*data[i] + rms*rms);
10104 
10105             idata = cpl_table_get_data_int(idscoeff, "nlines");
10106             idata[i] = npoints;
10107 
10108             /*
10109              * If a wavelengths map was provided, correct it to keep
10110              * into account the alignment to skylines. Note that
10111              * the offset must be converted from pixels to wavelengths.
10112              */
10113 
10114             if (calibration) {
10115                 for (k = 1; k < nx; k++) {
10116                     lambda1 = cdata[k - 1 + i*nx];
10117                     lambda2 = cdata[k + i*nx];
10118                     if (lambda1 < 1.0 || lambda2 < 1.0)
10119                         continue;
10120                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10121                 }
10122             }
10123         }
10124     }
10125 
10126     missing = 1;
10127     for (j = 0; j < nlines; j++) {
10128         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10129         if (cpl_table_has_valid(dummy, name)) {
10130             missing = 0;
10131             offset = cpl_table_get_column_median(dummy, name);
10132             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
10133                          line[j], offset);
10134         }
10135         else {
10136             cpl_msg_info(func, 
10137                          "Median offset for %.2f: not available", line[j]);
10138         }
10139     }
10140 
10141     cpl_table_delete(offsets);
10142 
10143     if (missing) {
10144         cpl_table_delete(dummy);
10145         dummy = NULL;
10146     }
10147 
10148     return dummy;
10149 
10150 }
10151 
10152 
10180 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
10181                            double wavestart, double dispersion, int radius,
10182                            int highres)
10183 {
10184 
10185     const char *func = "mos_distortions_rms";
10186 
10187     int xlen;
10188     int ylen;
10189     int numLines;
10190     int cpix, npix, nzero;
10191     int sp, ep;
10192     int i, j, k;
10193     int npeaks, allPeaks;
10194 
10195     float *profile;
10196     float  peak, expectPeak, offset;
10197     double lambda;
10198 
10199     double  average;
10200     double  rms, oneRms;
10201 
10202     float  *sdata;
10203     double *wdata;
10204 
10205   
10206     xlen = cpl_image_get_size_x(rectified);
10207     ylen = cpl_image_get_size_y(rectified);
10208     sdata = cpl_image_get_data(rectified);
10209 
10210     if (lines) {
10211         wdata = cpl_vector_get_data(lines);
10212         numLines = cpl_vector_get_size(lines);
10213     }
10214     else {
10215         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10216                         "given: using internal list of reference sky lines");
10217         if (highres) {
10218            wdata = default_lines_hi;
10219            numLines = sizeof(default_lines_hi) / sizeof(double);
10220         }
10221         else {
10222            wdata = default_lines_lo;
10223            numLines = sizeof(default_lines_lo) / sizeof(double);
10224         }
10225     }
10226 
10227     npix = 2 * radius + 1;
10228     profile = cpl_calloc(npix, sizeof(float));
10229 
10230     rms = 0.0;
10231     allPeaks = 0;
10232 
10233     for (i = 0; i < numLines; i++) {
10234 
10235         /*
10236          *  Expected peak and closest pixel to specified wavelength.
10237          */
10238 
10239         lambda = wdata[i];
10240         expectPeak = (lambda - wavestart) / dispersion;
10241         cpix = floor(expectPeak + 0.5);
10242 
10243         /*
10244          *  Search interval for peak. Abort if too close to image border.
10245          */
10246 
10247         sp = cpix - radius;
10248         ep = cpix + radius;
10249 
10250         if (sp < 0 || ep > xlen)
10251             continue;
10252 
10253         average = 0.0;
10254         npeaks = 0;
10255         oneRms = 0.0;
10256 
10257         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
10258             nzero = 0;
10259             for (k = 0; k < npix; k++) {
10260                 profile[k] = sdata[sp + k + j * xlen];
10261                 if (fabs(profile[k]) < 0.0001)
10262                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
10263             }
10264             if (nzero > 0)
10265                 continue;
10266 
10267             if (peakPosition(profile, npix, &peak, 1) == 0) {
10268                 offset = (sp + peak) - expectPeak;
10269                 average += offset;
10270                 rms += fabs(offset);
10271                 oneRms += fabs(offset);
10272                 npeaks++;
10273                 allPeaks++;
10274             }
10275         }
10276 
10277         if (npeaks)
10278             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
10279                          lambda, oneRms / npeaks * 1.25, npeaks);
10280         else
10281             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
10282     }
10283 
10284     cpl_free(profile);
10285 
10286     if (allPeaks < 10)
10287         return 0.0;
10288 
10289     rms /= allPeaks;
10290     rms *= 1.25;       /* Factor to convert average deviation to sigma */
10291 
10292     return rms;
10293 
10294 }
10295 
10296 
10317 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
10318                          double blue, double red, double dispersion, int trend)
10319 {
10320     const char *func = "mos_map_pixel";
10321 
10322     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10323                                                  /* Max order is 5 */
10324 
10325     cpl_polynomial *ids;
10326     cpl_image      *map;
10327     float          *mdata;
10328     double          lambda;
10329     double          c;
10330     int             order;
10331     int             xsize, ysize;
10332     int             missing;
10333     int             i, j, k;
10334 
10335 
10336     if (idscoeff == NULL) {
10337         cpl_msg_error(func, "An IDS coeff table must be given");
10338         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10339         return NULL;
10340     }
10341 
10342     xsize = (red - blue) / dispersion;
10343     ysize = cpl_table_get_nrow(idscoeff);
10344     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10345     mdata = cpl_image_get_data(map);
10346 
10347     order = 0;
10348     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10349         ++order;
10350     --order;
10351 
10352     for (i = 0; i < ysize; i++, mdata += xsize) {
10353 
10354         missing = 0;
10355         ids = cpl_polynomial_new(1);
10356         for (k = trend; k <= order; k++) {
10357             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10358             if (missing) {
10359                 cpl_polynomial_delete(ids);
10360                 break;
10361             }
10362             cpl_polynomial_set_coeff(ids, &k, c);
10363         }
10364         if (missing)
10365             continue;
10366 
10367         for (j = 0; j < xsize; j++) {
10368             lambda = blue + j*dispersion;
10369             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
10370         }
10371 
10372         cpl_polynomial_delete(ids);
10373     }
10374 
10375     return map;
10376 
10377 }
10378 
10379 
10401 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
10402                             double blue, double red)
10403 {
10404     const char *func = "mos_map_idscoeff";
10405 
10406     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10407                                                  /* Max order is 5 */
10408 
10409     cpl_polynomial *ids;
10410     cpl_image      *map;
10411     float          *mdata;
10412     double          lambda;
10413     double          c;
10414     int             order;
10415     int             ysize;
10416     int             missing;
10417     int             i, j, k;
10418 
10419 
10420     if (idscoeff == NULL) {
10421         cpl_msg_error(func, "An IDS coeff table must be given");
10422         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10423         return NULL;
10424     }
10425 
10426     if (xsize < 1) {
10427         cpl_msg_error(func, "Invalid image size");
10428         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10429         return NULL;
10430     }
10431 
10432     if (xsize < 20 || xsize > 5000) {
10433         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10434                         xsize);
10435     }
10436 
10437     ysize = cpl_table_get_nrow(idscoeff);
10438     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10439     mdata = cpl_image_get_data(map);
10440 
10441     order = 0;
10442     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10443         ++order;
10444     --order;
10445 
10446     for (i = 0; i < ysize; i++, mdata += xsize) {
10447 
10448         missing = 0;
10449         ids = cpl_polynomial_new(1);
10450         for (k = 0; k <= order; k++) {
10451             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10452             if (missing) {
10453                 cpl_polynomial_delete(ids);
10454                 break;
10455             }
10456             cpl_polynomial_set_coeff(ids, &k, c);
10457         }
10458         if (missing)
10459             continue;
10460 
10461         for (j = 0; j < xsize; j++) {
10462             lambda = mos_eval_dds(ids, blue, red, reference, j);
10463             if (lambda >= blue && lambda <= red) {
10464                 mdata[j] = lambda;
10465             }
10466         }
10467 
10468         cpl_polynomial_delete(ids);
10469     }
10470 
10471     return map;
10472 
10473 }
10474 
10475 
10510 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10511                                cpl_table *slits, cpl_table *polytraces, 
10512                                double reference, double blue, double red, 
10513                                double dispersion)
10514 {
10515     const char *func = "mos_map_wavelengths";
10516 
10517     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10518                                                  /* Max order is 5 */
10519     cpl_polynomial *polytop;
10520     cpl_polynomial *polybot;
10521     cpl_image      *remapped;
10522     float          *data;
10523     float          *wdata;
10524     float          *sdata;
10525     float          *xdata;
10526     double          vtop, vbot, value;
10527     double          top, bot;
10528     double          coeff;
10529     double          ytop, ybot;
10530     double          ypos;
10531     double          fvalue;
10532     int             ivalue;
10533     int             yint, ysize, yprev;
10534     int             nslits;
10535     int             npseudo;
10536     int            *slit_id;
10537     int            *position;
10538     int            *length;
10539     int             nx, ny;
10540     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10541     int             missing_top, missing_bot;
10542     int             null;
10543     int             order;
10544     int             i, j, k;
10545 
10546 
10547     if (spatial == NULL || calibration == NULL || 
10548         slits == NULL || polytraces == NULL) {
10549         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10550         return NULL;
10551     }
10552 
10553     if (dispersion <= 0.0) {
10554         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10555         return NULL;
10556     }
10557 
10558     if (red - blue < dispersion) {
10559         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10560         return NULL;
10561     }
10562 
10563     nx = cpl_image_get_size_x(spatial);
10564     ny = cpl_image_get_size_y(spatial);
10565     ysize = cpl_image_get_size_y(calibration);
10566     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10567     data  = cpl_image_get_data(remapped);
10568     sdata = cpl_image_get_data(spatial);
10569     wdata = cpl_image_get_data(calibration);
10570 
10571     nslits   = cpl_table_get_nrow(slits);
10572     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10573     order    = cpl_table_get_ncol(polytraces) - 2;
10574     position = cpl_table_get_data_int(slits, "position");
10575     length   = cpl_table_get_data_int(slits, "length");
10576 
10577     /*
10578      * The spatial resampling is performed for a certain number of 
10579      * pixels above and below the position of the reference wavelength:
10580      */
10581 
10582     pixel_above = (red - reference) / dispersion;
10583     pixel_below = (reference - blue) / dispersion;
10584 
10585     for (i = 0; i < nslits; i++) {
10586 
10587         if (length[i] == 0)
10588             continue;
10589 
10590         /*
10591          * Note that the x coordinate of the reference pixels on the CCD
10592          * is taken arbitrarily at the top end of each slit. This wouldn't
10593          * be entirely correct in case of curved slits, or in presence of
10594          * heavy distortions: in such cases the spatial resampling is
10595          * really performed across a wide range of wavelengths. But
10596          * the lag between top and bottom spectral curvature models 
10597          * would introduce even in such cases negligible effects on
10598          * the spectral spatial resampling.
10599          */
10600 
10601         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10602 
10603         start_pixel = refpixel - pixel_below;
10604         if (start_pixel < 0)
10605             start_pixel = 0;
10606 
10607         end_pixel = refpixel + pixel_above;
10608         if (end_pixel > nx)
10609             end_pixel = nx;
10610 
10611         /*
10612          * Recover from the table of spectral curvature coefficients
10613          * the curvature polynomials.
10614          */
10615 
10616         missing_top = 0;
10617         polytop = cpl_polynomial_new(1);
10618         for (k = 0; k <= order; k++) {
10619             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10620             if (null) {
10621                 cpl_polynomial_delete(polytop);
10622                 missing_top = 1;
10623                 break;
10624             }
10625             cpl_polynomial_set_coeff(polytop, &k, coeff);
10626         }
10627 
10628         missing_bot = 0;
10629         polybot = cpl_polynomial_new(1);
10630         for (k = 0; k <= order; k++) {
10631             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10632             if (null) {
10633                 cpl_polynomial_delete(polybot);
10634                 missing_bot = 1;
10635                 break;
10636             }
10637             cpl_polynomial_set_coeff(polybot, &k, coeff);
10638         }
10639 
10640         if (missing_top && missing_bot) {
10641             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10642                           slit_id[i]);
10643             continue;
10644         }
10645 
10646         /*
10647          * In case just one of the two edges was not traced, the other
10648          * edge curvature model is duplicated and shifted to the other
10649          * end of the slit: better than nothing!
10650          */
10651 
10652         if (missing_top) {
10653             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
10654                           "the spectral curvature of the lower edge "
10655                           "is used instead.", slit_id[i]);
10656             polytop = cpl_polynomial_duplicate(polybot);
10657             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10658             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10659             k = 0;
10660             coeff = cpl_polynomial_get_coeff(polybot, &k);
10661             coeff += ytop - ybot;
10662             cpl_polynomial_set_coeff(polytop, &k, coeff);
10663         }
10664 
10665         if (missing_bot) {
10666             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
10667                           "the spectral curvature of the upper edge "
10668                           "is used instead.", slit_id[i]);
10669             polybot = cpl_polynomial_duplicate(polytop);
10670             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10671             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10672             k = 0;
10673             coeff = cpl_polynomial_get_coeff(polytop, &k);
10674             coeff -= ytop - ybot;
10675             cpl_polynomial_set_coeff(polybot, &k, coeff);
10676         }
10677 
10678         /*
10679          * Point to current slit on wavelength calibration image.
10680          * Note that the npseudo value related to this slit is equal 
10681          * to the number of spatial pseudo-pixels decreased by 1 
10682          * (compare with function mos_spatial_calibration()).
10683          */
10684 
10685         xdata = wdata + nx*position[i];
10686         npseudo = length[i] - 1;
10687 
10688         /*
10689          * Write interpolated wavelengths to CCD image
10690          */
10691 
10692         for (j = start_pixel; j < end_pixel; j++) {
10693             top = cpl_polynomial_eval_1d(polytop, j, NULL);
10694             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
10695             for (k = 0; k <= npseudo; k++) {
10696                 ypos = top - k*(top-bot)/npseudo;
10697                 yint = ypos;
10698 
10699                 /* 
10700                  * The line:
10701                  *     value = sdata[j + nx*yint];
10702                  * should be equivalent to:
10703                  *     value = npseudo*(top-yint)/(top-bot);
10704                  */
10705 
10706                 if (yint < 0 || yint >= ny-1) {
10707                     yprev = yint;
10708                     continue;
10709                 }
10710 
10711                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
10712                 ivalue = value;               /* Nearest spatial pixels:   */
10713                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
10714                 if (ivalue < npseudo && ivalue >= 0) {
10715                     vtop = xdata[j + nx*(npseudo-ivalue)];
10716                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
10717                     if (vtop < 1.0) {  /* Impossible wavelength */
10718                         if (vbot < 1.0) {
10719                             value = 0.0;
10720                         }
10721                         else {
10722                             value = vbot;
10723                         }
10724                     }
10725                     else if (vbot < 1.0) {
10726                         if (k)
10727                             value = vtop;
10728                         else
10729                             value = 0.0;
10730                     }
10731                     else if (fabs(vbot-vtop) > 10*dispersion) {
10732                         value = 0.0;
10733                     }
10734                     else {
10735                         value = vtop*(1-fvalue) + vbot*fvalue;
10736                     }
10737                     data[j + nx*yint] = value;
10738 
10739                     if (k) {
10740 
10741                         /*
10742                          * This is added to recover lost pixels on
10743                          * the CCD image (pixels are lost because
10744                          * the CCD pixels are less than npseudo+1).
10745                          */
10746 
10747                         if (yprev - yint > 1) {
10748                             value = sdata[j + nx*(yint+1)];
10749                             ivalue = value;
10750                             fvalue = value - ivalue;
10751                             if (ivalue < npseudo && ivalue >= 0) {
10752                                 vtop = xdata[j + nx*(npseudo-ivalue)];
10753                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
10754                                 if (vtop < 1.0) {
10755                                     if (vbot < 1.0) {
10756                                         value = data[j + nx*(yint+1)];
10757                                     }
10758                                     else {
10759                                         value = vbot;
10760                                     }
10761                                 }
10762                                 else if (vbot < 1.0) {
10763                                     value = vtop;
10764                                 }
10765                                 else if (fabs(vbot-vtop) > 2*dispersion) {
10766                                     value = vtop;
10767                                 }
10768                                 else {
10769                                     value = vtop*(1-fvalue) + vbot*fvalue;
10770                                 }
10771                                 data[j + nx*(yint+1)] = value;
10772                             }
10773                         }
10774                     }
10775                 }
10776                 yprev = yint;
10777             }
10778         }
10779         cpl_polynomial_delete(polytop);
10780         cpl_polynomial_delete(polybot);
10781     }
10782 
10783     return remapped;
10784 }
10785 
10859 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
10860                             cpl_image *spatial, cpl_table *slits,
10861                             cpl_table *polytraces, double reference,
10862                             double blue, double red, double dispersion,
10863                             int flux)
10864 {
10865     const char *func = "mos_map_spectrum";
10866     
10867     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10868                                                  /* Max order is 5 */
10869     cpl_polynomial *polytop;
10870     cpl_polynomial *polybot;
10871     cpl_image      *remapped;
10872     cpl_image     **exslit;
10873     float          *data;
10874     float          *wdata;
10875     float          *sdata;
10876     float          *xdata;
10877     double          lambda00, lambda01, lambda10, lambda11, lambda;
10878     double          space00, space01, space10, space11, space;
10879     double          value00, value01, value10, value11, value0, value1, value;
10880     double          dL, dS;
10881     double          top, bot;
10882     double          coeff;
10883     double          ytop, ybot;
10884     double          xfrac, yfrac;
10885     int             yint, ysize;
10886     int             itop, ibot;
10887     int             shift;
10888     int             L, S;
10889     int             nslits;
10890     int             npseudo;
10891     int            *slit_id;
10892     int            *position;
10893     int            *length;
10894     int             nx, ny;
10895     int             x, y;
10896     int             nlambda;
10897     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10898     int             missing_top, missing_bot; 
10899     int             null;
10900     int             order;
10901     int             i, k;
10902     
10903 
10904     flux += flux;
10905 
10906     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
10907         slits == NULL || polytraces == NULL) { 
10908         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10909         return NULL;
10910     }
10911 
10912     if (dispersion <= 0.0) {
10913         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10914         return NULL;
10915     }
10916 
10917     if (red - blue < dispersion) {
10918         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10919         return NULL;
10920     }
10921     
10922     nx = cpl_image_get_size_x(spectra);
10923     ny = cpl_image_get_size_y(spectra);
10924 
10925     if (nx != cpl_image_get_size_x(spatial) ||
10926         ny != cpl_image_get_size_y(spatial) ||
10927         nx != cpl_image_get_size_x(wavecalib) ||
10928         ny != cpl_image_get_size_y(wavecalib)) {
10929         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10930         return NULL;
10931     }
10932 
10933     nlambda     = (red - blue) / dispersion;
10934     pixel_above = (red - reference) / dispersion;
10935     pixel_below = (reference - blue) / dispersion;
10936 
10937     data  = cpl_image_get_data(spectra);
10938     sdata = cpl_image_get_data(spatial);
10939     wdata = cpl_image_get_data(wavecalib);
10940     
10941     nslits   = cpl_table_get_nrow(slits);
10942     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10943     order    = cpl_table_get_ncol(polytraces) - 2;
10944     position = cpl_table_get_data_int(slits, "position");
10945     length   = cpl_table_get_data_int(slits, "length");
10946     
10947     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
10948 
10949     for (i = 0; i < nslits; i++) {
10950 
10951          if (length == 0)
10952              continue;
10953 
10954         /*
10955          * Note that the x coordinate of the reference pixels on the CCD
10956          * is taken arbitrarily at the top end of each slit. This wouldn't
10957          * be entirely correct in case of curved slits, or in presence of
10958          * heavy distortions: in such cases the spatial resampling is
10959          * really performed across a wide range of wavelengths. But
10960          * the lag between top and bottom spectral curvature models
10961          * would introduce even in such cases negligible effects on
10962          * the spectral spatial resampling.
10963          */
10964 
10965         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10966 
10967         start_pixel = refpixel - pixel_below;
10968         if (start_pixel < 1)
10969             start_pixel = 1;
10970 
10971         end_pixel = refpixel + pixel_above;
10972         if (end_pixel > nx)
10973             end_pixel = nx;
10974 
10975         /*
10976          * Recover from the table of spectral curvature coefficients
10977          * the curvature polynomials.
10978          */
10979 
10980         missing_top = 0;
10981         polytop = cpl_polynomial_new(1);
10982         for (k = 0; k <= order; k++) {
10983             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10984             if (null) {
10985                 cpl_polynomial_delete(polytop);
10986                 missing_top = 1;
10987                 break;
10988             }
10989             cpl_polynomial_set_coeff(polytop, &k, coeff);
10990         }
10991 
10992         missing_bot = 0;
10993         polybot = cpl_polynomial_new(1);
10994         for (k = 0; k <= order; k++) {
10995             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10996             if (null) {
10997                 cpl_polynomial_delete(polybot);
10998                 missing_bot = 1;
10999                 break;
11000             }
11001             cpl_polynomial_set_coeff(polybot, &k, coeff);
11002         }
11003 
11004         if (missing_top && missing_bot) {
11005             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11006                           slit_id[i]);
11007             continue;
11008         }
11009 
11010         /*
11011          * In case just one of the two edges was not traced, the other
11012          * edge curvature model is duplicated and shifted to the other
11013          * end of the slit: better than nothing!
11014          */
11015 
11016         if (missing_top) {
11017             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11018                           "the spectral curvature of the lower edge "
11019                           "is used instead.", slit_id[i]);
11020             polytop = cpl_polynomial_duplicate(polybot);
11021             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11022             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11023             k = 0;
11024             coeff = cpl_polynomial_get_coeff(polybot, &k);
11025             coeff += ytop - ybot;
11026             cpl_polynomial_set_coeff(polytop, &k, coeff);
11027         }
11028 
11029         if (missing_bot) {
11030             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11031                           "the spectral curvature of the upper edge "
11032                           "is used instead.", slit_id[i]);
11033             polybot = cpl_polynomial_duplicate(polytop);
11034             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11035             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11036             k = 0;
11037             coeff = cpl_polynomial_get_coeff(polytop, &k);
11038             coeff -= ytop - ybot;
11039             cpl_polynomial_set_coeff(polybot, &k, coeff);
11040         }
11041 
11042         /*
11043          * Allocate image for current extracted slit
11044          */
11045 
11046         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11047         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11048         npseudo = ceil(top-bot) + 1;
11049 
11050         if (npseudo < 1) {
11051             cpl_polynomial_delete(polytop);
11052             cpl_polynomial_delete(polybot);
11053             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11054                           slit_id[i]);
11055             continue;
11056         }
11057 
11058         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11059         xdata = cpl_image_get_data(exslit[i]);
11060 
11061         /*
11062          * Write interpolated spectral values to remapped slit spectrum.
11063          */
11064 
11065         for (x = start_pixel; x < end_pixel; x++) {
11066             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11067             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11068             itop = top + 1;
11069             ibot = bot;
11070             if (itop < 0)
11071                 itop = 0;
11072             if (itop > ny - 1)
11073                 itop = ny - 1;
11074             if (ibot < 0)
11075                 ibot = 0;
11076             if (ibot > ny - 1)
11077                 ibot = ny - 1;
11078             for (y = ibot; y < itop; y++) {
11079                  lambda11 = wdata[x + y*nx];
11080                  if (lambda11 < 1.0)        /* Impossible wavelength */
11081                      continue;
11082                  space11 = sdata[x + y*nx];
11083                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11084                      continue;
11085                  lambda01 = wdata[x - 1 + y*nx];
11086                  if (lambda01 < 1.0)        /* Impossible wavelength */
11087                      continue;
11088                  space01 = sdata[x - 1 + y*nx];
11089                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11090                      continue;
11091 
11092                  shift = 0;
11093 
11094 /****+
11095                  if (wdata[x + (y+1)*nx] > 1.0) {
11096                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11097                          shift = -1;
11098                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11099                              shift--;
11100                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11101                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11102                              shift++;
11103                          }
11104                      }
11105                      else {
11106                          shift = 1;
11107                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11108                              shift++;
11109                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
11110                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11111                              shift--;
11112                          }
11113                      }
11114                  }
11115 ****/
11116 
11117 /****
11118 printf("y = %d, shift = %d\n", y, shift);
11119 ****/
11120 
11121                  lambda10 = wdata[x + shift + (y+1)*nx];
11122                  if (lambda10 < 1.0)        /* Impossible wavelength */
11123                      continue;
11124                  space10 = sdata[x + shift + (y+1)*nx];
11125                  if (space10 < 0.0)         /* Impossible spatial coordinate */
11126                      continue;
11127                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
11128                  if (lambda00 < 1.0)        /* Impossible wavelength */
11129                      continue;
11130                  space00 = sdata[x - 1 + shift + (y+1)*nx];
11131                  if (space00 < 0.0)         /* Impossible spatial coordinate */
11132                      continue;
11133                  
11134                  /*
11135                   * Find the variation in lambda and space in this
11136                   * position for each CCD pixel (both quantities are 
11137                   * expected to be positive).
11138                   */
11139 
11140                  dL = lambda11 - lambda01;
11141                  dS = space11 - space10;
11142 
11143                  /*
11144                   * Find the position (L,S) of the output pixel 
11145                   * (by integer truncation).
11146                   */
11147 
11148                  L = (lambda11 - blue)/dispersion + 0.5;
11149                  S = space11 + 0.5;                   /* Counted from top! */
11150 
11151                  if (L < 0 || L >= nlambda)
11152                      continue;
11153                  if (S < 0 || S > npseudo)
11154                      continue;
11155 
11156                  /*
11157                   * Find the coordinate of pixel (L,S)
11158                   */
11159 
11160                  lambda = blue + L*dispersion;
11161                  space  = S;
11162 
11163                  /*
11164                   * Find the interpolation point on the CCD: it is
11165                   * defined as the (positive) distance from current
11166                   * CCD pixel (x,y) of the interpolation point (x',y'),
11167                   * measured in CCD pixels. The interpolation point
11168                   * is located between the four CCD pixels selected
11169                   * above.
11170                   */
11171 
11172                  xfrac = (lambda11-lambda)/dL;
11173                  yfrac = (space11-space)/dS;
11174 
11175 /*
11176 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
11177 printf("xyfrac = %f, %f\n", xfrac, yfrac);
11178 */
11179 
11180                  /*
11181                   * Get the four values to interpolate
11182                   */
11183 
11184                  value11 = data[x + y*nx];
11185                  value01 = data[x - 1 + y*nx];
11186                  value10 = data[x + shift + (y+1)*nx];
11187                  value00 = data[x + shift - 1 + (y+1)*nx];
11188 
11189                  /*
11190                   * Interpolation
11191                   */
11192 
11193                  value1 = (1-xfrac)*value11 + xfrac*value01;
11194                  value0 = (1-xfrac)*value10 + xfrac*value00;
11195                  value  = (1-yfrac)*value1  + yfrac*value0;
11196 
11197                  /*
11198                   * Write this value to the appropriate (L,S) coordinate
11199                   * on output slit
11200                   */
11201 
11202                  xdata[L + nlambda*(npseudo-S)] = value;
11203                  
11204             }
11205         }
11206         cpl_polynomial_delete(polytop);
11207         cpl_polynomial_delete(polybot);
11208     }
11209 
11210     /*
11211      * Now all the slits images are copied to a single image
11212      */
11213 
11214     ysize = 0;
11215     for (i = 0; i < nslits; i++)
11216         if (exslit[i])
11217             ysize += cpl_image_get_size_y(exslit[i]);
11218 
11219     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
11220 
11221     yint = -1;
11222     for (i = 0; i < nslits; i++) {
11223         if (exslit[i]) {
11224             yint += cpl_image_get_size_y(exslit[i]);
11225             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
11226             cpl_image_delete(exslit[i]);
11227             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
11228         }
11229     }
11230 
11231     cpl_free(exslit);
11232 
11233     return remapped;
11234 
11235 }
11236 
11237 
11270 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
11271                              double dispersion, double factor, int minpoints,
11272                              cpl_image *skymap)
11273 {
11274     const char *func = "mos_sky_map_super";
11275 
11276     cpl_vector **vector;
11277     cpl_vector **wvector;
11278     double       firstLambda, lastLambda;
11279     double       lambda, lambda1, lambda2;
11280     double       value, value1, value2;
11281     double       frac;
11282     float        min, max;
11283     int         *count;
11284     int          nbin, bin;
11285     int          nx, ny, npix;
11286     int          first_valid, valid_bins;
11287     int          i, j;
11288 
11289     cpl_table   *sky;
11290     double      *sky_spectrum;
11291     double      *sky_wave;
11292     float       *data;
11293     float       *sdata;
11294     float       *kdata;
11295 
11296 
11297     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11298         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11299         return NULL;
11300     }
11301     
11302     if (dispersion <= 0.0) {
11303         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11304         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
11305         return NULL;
11306     }
11307 
11308     nx = cpl_image_get_size_x(spectra);
11309     ny = cpl_image_get_size_y(spectra);
11310     npix = nx * ny;
11311 
11312     if (nx != cpl_image_get_size_x(wavemap) ||
11313         ny != cpl_image_get_size_y(wavemap) ||
11314         nx != cpl_image_get_size_x(skymap) ||
11315         ny != cpl_image_get_size_y(skymap)) {
11316         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11317         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
11318         return NULL;
11319     }
11320 
11321     if (factor < 1.0) {
11322         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11323         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
11324                       cpl_error_get_message());
11325         return NULL;
11326     }
11327 
11328     if (minpoints < 0) {
11329         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11330         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
11331         return NULL;
11332     }
11333 
11334     dispersion /= factor;
11335 
11336 
11337     /*
11338      * Find bluest and reddest wavelengths in the whole image
11339      */
11340 
11341     data = cpl_image_get_data(wavemap);
11342 
11343     for (i = 0; i < npix; i++) {
11344         if (data[i] > 1.0) {
11345             min = max = data[i];
11346             j = i+1;
11347             break;
11348         }
11349     }
11350 
11351     for (i = j; i < npix; i++) {
11352         if (data[i] < 1.0)      /* Impossible wavelength */
11353             continue;
11354         if (min > data[i])
11355             min = data[i];
11356         if (max < data[i])
11357             max = data[i];
11358     }
11359 
11360     firstLambda = min;
11361     lastLambda = max;
11362 
11363 
11364     /*
11365      * Determine length of median spectrum
11366      */
11367 
11368     nbin = (lastLambda - firstLambda) / dispersion;
11369 
11370     /*
11371      * Count how many values will be found for each spectral bin.
11372      * The ith bin has a wavelength range from firstLambda + i*dispersion
11373      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11374      * it is assigned to its central wavelength.
11375      */
11376 
11377     count = cpl_calloc(nbin, sizeof(int));
11378 
11379     data = cpl_image_get_data(wavemap);
11380 
11381     for (i = 0; i < npix; i++) {
11382         if (data[i] < 1.0)
11383             continue;
11384         bin = (data[i] - firstLambda) / dispersion;
11385         if (bin < nbin)                               /* Safer */
11386             count[bin]++;
11387     }
11388 
11389     valid_bins = 0;
11390     for (i = 0; i < nbin; i++)
11391         if (count[i] >= minpoints)
11392             valid_bins++;
11393 
11394     if (valid_bins < nbin/3) {
11395         cpl_msg_warning(func, "Cannot determine a good global sky "
11396                         "spectrum from input data");
11397         return NULL;
11398     }
11399 
11400 
11401     /*
11402      * Allocate an array of vectors with the appropriate size, to
11403      * contain a list of all the spectral pixels values. At the same
11404      * time, reset the array of counters (because we will have to
11405      * count again...).
11406      */
11407 
11408     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11409     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11410     for (i = 0; i < nbin; i++) {
11411         if (count[i] >= minpoints) {
11412             vector[i] = cpl_vector_new(count[i]);
11413             wvector[i] = cpl_vector_new(count[i]);
11414         }
11415         count[i] = 0;
11416     }
11417 
11418 
11419     /*
11420      * Read the wavemap and the spectral images, and add the data values
11421      * to the appropriate wavelength bins
11422      */
11423 
11424     data  = cpl_image_get_data(wavemap);
11425     sdata = cpl_image_get_data(spectra);
11426 
11427     for (i = 0; i < npix; i++) {
11428         if (data[i] < 1.0)
11429             continue;
11430         bin = (data[i] - firstLambda) / dispersion;
11431         if (bin < nbin) {                             /* Safer */
11432             if (vector[bin]) {
11433                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11434                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11435             }
11436             count[bin]++;
11437         }
11438     }
11439 
11440 
11441     /*
11442      * Compute the median flux for each wavelength bin, and destroy
11443      * at the same time the used vectors
11444      */
11445 
11446     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11447     sky_wave = cpl_calloc(nbin, sizeof(double));
11448     for (i = 0; i < nbin; i++) {
11449         if (vector[i]) {
11450             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11451             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11452             cpl_vector_delete(vector[i]);
11453             cpl_vector_delete(wvector[i]);
11454         }
11455     }
11456 
11457     cpl_free(vector);
11458     cpl_free(wvector);
11459 
11460 
11461     /*
11462      * Here possible gaps in the final spectrum are filled by interpolation
11463      */
11464 
11465     for (i = 0; i < nbin; i++) {
11466         if (count[i] >= minpoints) {
11467             first_valid = i;
11468             break;
11469         }
11470     }
11471     
11472     for (i = first_valid; i < nbin; i++) {
11473         if (count[i] < minpoints) {
11474             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11475             for (j = i+1; j < nbin; j++) {
11476                 if (count[j] >= minpoints) {
11477                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11478                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11479                                         / 2;
11480                     }
11481                     else {
11482                         frac = (sky_wave[i] - sky_wave[i-1]) 
11483                              / (sky_wave[j] - sky_wave[i-1]);
11484                         sky_spectrum[i] = frac * sky_spectrum[j]
11485                                         + (1 - frac) * sky_spectrum[i-1];
11486                     }
11487                 }
11488             }
11489         }
11490     }
11491 
11492 
11493     /*
11494      * Create the output table
11495      */
11496 
11497     sky = cpl_table_new(nbin);
11498     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11499     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11500     cpl_table_wrap_int(sky, count, "npoints");
11501 
11502 
11503     /*
11504      * Fill the sky map
11505      */
11506 
11507     data  = cpl_image_get_data(wavemap);
11508     sdata = cpl_image_get_data(spectra);
11509     kdata = cpl_image_get_data(skymap);
11510 
11511     for (i = 0; i < npix; i++) {
11512 
11513         /*
11514          * Currently based on linear interpolation
11515          */
11516 
11517         lambda = data[i];
11518         if (lambda < 1.0)
11519             continue;
11520         bin = (lambda - firstLambda) / dispersion;
11521         lambda1 = sky_wave[bin];
11522         value1 = sky_spectrum[bin];
11523         if (lambda1 < lambda) {
11524             bin++;
11525             if (bin < nbin) {
11526                 lambda2 = sky_wave[bin];
11527                 value2  = sky_spectrum[bin];
11528                 if (lambda2 - lambda1 < 0.1) {
11529                     value = (value1 + value2) / 2;
11530                 }
11531                 else {
11532                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11533                     value = frac * value2 + (1 - frac) * value1;
11534                 }
11535             }
11536             else {
11537                 value = value1;
11538             }
11539         }
11540         else {
11541             if (bin > 0) {
11542                 bin--;
11543                 lambda2 = lambda1;
11544                 value2  = value1;
11545                 lambda1 = sky_wave[bin];
11546                 value1  = sky_spectrum[bin];
11547                 if (lambda2 - lambda1 < 0.1) {
11548                     value = (value1 + value2) / 2;
11549                 }
11550                 else {
11551                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11552                     value = frac * value2 + (1 - frac) * value1;
11553                 }
11554             }
11555             else {
11556                 value = value1;
11557             }
11558         }
11559         kdata[i] = value;
11560     }
11561 
11562     if (first_valid)
11563         cpl_table_erase_window(sky, 0, first_valid);
11564 
11565     return sky;
11566 
11567 }
11568 
11569 
11603 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11604                        double dispersion, cpl_image *skymap)
11605 {
11606     const char *func = "mos_sky_map";
11607 
11608     cpl_vector **vector;
11609     double       firstLambda, lastLambda;
11610     double       lambda, lambda1, lambda2;
11611     double       value, value1, value2;
11612     float        min, max;
11613     int         *count;
11614     int          nbin, bin;
11615     int          nx, ny, npix;
11616     int          i, j;
11617 
11618     cpl_table   *sky;
11619     double      *sky_spectrum;
11620     float       *data;
11621     float       *sdata;
11622     float       *kdata;
11623     double      *wdata;
11624 
11625 
11626     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11627         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11628         return NULL;
11629     }
11630     
11631     if (dispersion <= 0.0) {
11632         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11633         return NULL;
11634     }
11635 
11636     nx = cpl_image_get_size_x(spectra);
11637     ny = cpl_image_get_size_y(spectra);
11638     npix = nx * ny;
11639 
11640     if (nx != cpl_image_get_size_x(wavemap) ||
11641         ny != cpl_image_get_size_y(wavemap) ||
11642         nx != cpl_image_get_size_x(skymap) ||
11643         ny != cpl_image_get_size_y(skymap)) {
11644         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11645         return NULL;
11646     }
11647 
11648 
11649     /*
11650      * Find bluest and reddest wavelengths in the whole image
11651      */
11652 
11653     data = cpl_image_get_data(wavemap);
11654 
11655     for (i = 0; i < npix; i++) {
11656         if (data[i] > 1.0) {
11657             min = max = data[i];
11658             j = i+1;
11659             break;
11660         }
11661     }
11662 
11663     for (i = j; i < npix; i++) {
11664         if (data[i] < 1.0)      /* Impossible wavelength */
11665             continue;
11666         if (min > data[i])
11667             min = data[i];
11668         if (max < data[i])
11669             max = data[i];
11670     }
11671 
11672     firstLambda = min;
11673     lastLambda = max;
11674 
11675 
11676     /*
11677      * Determine length of median spectrum
11678      */
11679 
11680     nbin = (lastLambda - firstLambda) / dispersion;
11681 
11682     /*
11683      * Count how many values will be found for each spectral bin.
11684      * The ith bin has a wavelength range from firstLambda + i*dispersion
11685      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11686      * it is assigned to its central wavelength.
11687      */
11688 
11689     count = cpl_calloc(nbin, sizeof(int));
11690 
11691     data = cpl_image_get_data(wavemap);
11692 
11693     for (i = 0; i < npix; i++) {
11694         if (data[i] < 1.0)
11695             continue;
11696         bin = (data[i] - firstLambda) / dispersion;
11697         if (bin < nbin)                               /* Safer */
11698             count[bin]++;
11699     }
11700 
11701 
11702     /*
11703      * Allocate an array of vectors with the appropriate size, to
11704      * contain a list of all the spectral pixels values. At the same
11705      * time, reset the array of counters (because we will have to
11706      * count again...).
11707      */
11708 
11709     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11710     for (i = 0; i < nbin; i++) {
11711         if (count[i])
11712             vector[i] = cpl_vector_new(count[i]);
11713         else
11714             vector[i] = NULL;
11715         count[i] = 0;
11716     }
11717 
11718 
11719     /*
11720      * Read the wavemap and the spectral images, and add the data values
11721      * to the appropriate wavelength bins
11722      */
11723 
11724     data  = cpl_image_get_data(wavemap);
11725     sdata = cpl_image_get_data(spectra);
11726 
11727     for (i = 0; i < npix; i++) {
11728         if (data[i] < 1.0)
11729             continue;
11730         bin = (data[i] - firstLambda) / dispersion;
11731         if (bin < nbin) {                             /* Safer */
11732             cpl_vector_set(vector[bin], count[bin], sdata[i]);
11733             count[bin]++;
11734         }
11735     }
11736 
11737 
11738     /*
11739      * Compute the median flux for each wavelength bin, and destroy
11740      * at the same time the used vectors
11741      */
11742 
11743     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11744     for (i = 0; i < nbin; i++) {
11745         if (vector[i]) {
11746             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11747             cpl_vector_delete(vector[i]);
11748         }
11749     }
11750 
11751     cpl_free(vector);
11752 
11753 
11754     /*
11755      * Here possible gaps in the final spectrum should be filled
11756      * by interpolation
11757      */
11758 
11759     /* ... */
11760 
11761     /*
11762      * Create the output table
11763      */
11764 
11765     sky = cpl_table_new(nbin);
11766     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
11767     cpl_table_set_column_unit(sky, "wavelength", "pixel");
11768     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11769     cpl_table_wrap_int(sky, count, "npoints");
11770     for (i = 0; i < nbin; i++)
11771         cpl_table_set_double(sky, "wavelength", i, 
11772                              firstLambda + (i+0.5)*dispersion);
11773 
11774 
11775     /*
11776      * Fill the sky map
11777      */
11778 
11779     data  = cpl_image_get_data(wavemap);
11780     sdata = cpl_image_get_data(spectra);
11781     kdata = cpl_image_get_data(skymap);
11782     wdata = cpl_table_get_data_double(sky, "wavelength");
11783 
11784     for (i = 0; i < npix; i++) {
11785 
11786         /*
11787          * Currently based on linear interpolation
11788          */
11789 
11790         lambda = data[i];
11791         if (lambda < 1.0)
11792             continue;
11793         bin = (lambda - firstLambda) / dispersion;
11794         lambda1 = wdata[bin];
11795         value1 = sky_spectrum[bin];
11796         if (lambda1 < lambda) {
11797             bin++;
11798             if (bin < nbin) {
11799                 lambda2 = wdata[bin];
11800                 value2  = sky_spectrum[bin];
11801                 value   = ((lambda2 - lambda)*value1 
11802                         +  (lambda - lambda1)*value2) / dispersion;
11803             }
11804             else {
11805                 value = value1;
11806             }
11807         }
11808         else {
11809             if (bin > 0) {
11810                 bin--;
11811                 lambda2 = lambda1;
11812                 value2  = value1;
11813                 lambda1 = wdata[bin];
11814                 value1  = sky_spectrum[bin];
11815                 value   = ((lambda2 - lambda)*value1 
11816                         +  (lambda - lambda1)*value2)/dispersion;
11817             }
11818             else {
11819                 value = value1;
11820             }
11821         }
11822         kdata[i] = value;
11823     }
11824 
11825     return sky;
11826 
11827 }
11828 
11829 
11845 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
11846 {
11847     const char *func = "mos_sky_local_old";
11848 
11849     cpl_image *exslit;
11850     cpl_image *sky;
11851     cpl_image *skymap;
11852     float     *data;
11853     float     *sdata;
11854     int        nx, ny;
11855     int        xlow, ylow, xhig, yhig;
11856     int        nslits;
11857     int       *slit_id;
11858     int       *position;
11859     int       *length;
11860     int        i, j, k;
11861 
11862 
11863     if (spectra == NULL) {
11864         cpl_msg_error(func, 
11865                       "A scientific rectified spectral image must be given");
11866         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11867         return NULL;
11868     }
11869 
11870     if (slits == NULL) {
11871         cpl_msg_error(func, "A slits position table must be given");
11872         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11873         return NULL;
11874     }
11875 
11876     nslits   = cpl_table_get_nrow(slits);
11877     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11878     position = cpl_table_get_data_int(slits, "position");
11879     length   = cpl_table_get_data_int(slits, "length");
11880 
11881     nx = cpl_image_get_size_x(spectra);
11882     ny = cpl_image_get_size_y(spectra);
11883 
11884     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
11885 
11886     xlow = 1;
11887     xhig = nx;
11888     for (i = 0; i < nslits; i++) {
11889 
11890         if (length[i] == 0)
11891             continue;
11892 
11893         /*
11894          * Define the extraction boundaries. We DON'T write:
11895          *
11896          * ylow = position[i];
11897          * yhig = ylow + length[i];
11898          *
11899          * because the cpl_image pixels are counted from 1, and because in
11900          * cpl_image_extract() the coordinates of the last pixel are inclusive.
11901          */
11902 
11903         ylow = position[i] + 1;
11904         yhig = ylow + length[i] - 1;
11905 
11906         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
11907         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
11908         cpl_image_delete(exslit);
11909 
11910         data   = cpl_image_get_data(skymap);
11911         data  += nx * position[i];
11912 
11913         for (j = 0; j < length[i]; j++) {
11914             sdata  = cpl_image_get_data(sky);
11915             for (k = 0; k < nx; k++) {
11916                 *data++ = *sdata++;
11917             }
11918         }
11919 
11920         cpl_image_delete(sky);
11921     }
11922 
11923     return skymap;
11924 
11925 }
11926 
11927 
11947 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
11948 {
11949     const char *func = "mos_sky_local";
11950 
11951     char        name[MAX_COLNAME];
11952 
11953     cpl_polynomial *fit;
11954     cpl_vector     *points;
11955     cpl_vector     *values;
11956     cpl_vector     *keep_points;
11957     cpl_vector     *keep_values;
11958     cpl_image      *exslit;
11959     cpl_image      *sky;
11960     cpl_image      *subtracted;
11961     cpl_image      *profile;
11962     cpl_image      *skymap;
11963     cpl_table      *objects;
11964     float          *data;
11965     float          *sdata;
11966     float          *xdata;
11967     double         *vdata;
11968     double         *pdata;
11969     double          median;
11970     int             nx, ny;
11971     int             xlow, ylow, xhig, yhig;
11972     int             nslits;
11973     int            *slit_id;
11974     int            *position;
11975     int            *length;
11976     int            *is_sky;
11977     int             nsky, nbad;
11978     int             maxobjects;
11979     int             margin = 3;
11980     int             radius = 6;
11981     int             i, j, k;
11982 
11983 
11984     if (spectra == NULL) {
11985         cpl_msg_error(func, 
11986                       "A scientific rectified spectral image must be given");
11987         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11988         return NULL;
11989     }
11990 
11991     if (slits == NULL) {
11992         cpl_msg_error(func, "A slits position table must be given");
11993         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11994         return NULL;
11995     }
11996 
11997     if (order < 0) {
11998         cpl_msg_error(func, "Invalid fit order");
11999         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12000         return NULL;
12001     }
12002 
12003     nslits   = cpl_table_get_nrow(slits);
12004     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12005     position = cpl_table_get_data_int(slits, "position");
12006     length   = cpl_table_get_data_int(slits, "length");
12007 
12008     nx = cpl_image_get_size_x(spectra);
12009     ny = cpl_image_get_size_y(spectra);
12010 
12011     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12012 
12013     xlow = 1;
12014     xhig = nx;
12015     for (i = 0; i < nslits; i++) {
12016 
12017         if (length[i] == 0)
12018             continue;
12019 
12020         /*
12021          * Define the extraction boundaries. We DON'T write:
12022          *
12023          * ylow = position[i];
12024          * yhig = ylow + length[i];
12025          *
12026          * because the cpl_image pixels are counted from 1, and because in
12027          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12028          */
12029 
12030         ylow = position[i] + 1;
12031         yhig = ylow + length[i] - 1;
12032 
12033         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12034         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12035         cpl_image_delete(exslit);
12036 
12037         data   = cpl_image_get_data(skymap);
12038         data  += nx * position[i];
12039 
12040         for (j = 0; j < length[i]; j++) {
12041             sdata  = cpl_image_get_data(sky);
12042             for (k = 0; k < nx; k++) {
12043                 *data++ = *sdata++;
12044             }
12045         }
12046 
12047         cpl_image_delete(sky);
12048     }
12049 
12050 
12051     /*
12052      * Preliminary sky-subtracted image
12053      */
12054 
12055     subtracted = cpl_image_duplicate(spectra);
12056     cpl_image_subtract(subtracted, skymap);
12057     cpl_image_delete(skymap);
12058 
12059 
12060     /*
12061      * Detect objects positions in all slits
12062      */
12063 
12064     objects = cpl_table_duplicate(slits);
12065     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12066     cpl_image_delete(profile);
12067     cpl_image_delete(subtracted);
12068 
12069 
12070     /*
12071      * Flag the sky pixels. Note that maxobjects is intentionally 
12072      * the max number of objects increased by one.
12073      */
12074 
12075     maxobjects = 1;
12076     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12077     while (cpl_table_has_column(objects, name)) {
12078         maxobjects++;
12079         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12080     }
12081 
12082     is_sky = cpl_calloc(ny, sizeof(int));
12083 
12084     for (i = 0; i < nslits; i++) {
12085 
12086         if (length[i] == 0)
12087             continue;
12088 
12089         ylow = position[i] + margin;
12090         yhig = position[i] + length[i] - margin;
12091 
12092         for (j = ylow; j < yhig; j++)
12093             is_sky[j] = 1;
12094 
12095         for (j = 1; j < maxobjects; j++) {
12096             snprintf(name, MAX_COLNAME, "object_%d", j);
12097             if (cpl_table_is_valid(objects, name, i)) {
12098                 snprintf(name, MAX_COLNAME, "start_%d", j);
12099                 ylow = cpl_table_get_int(objects, name, i, NULL);
12100                 snprintf(name, MAX_COLNAME, "end_%d", j);
12101                 yhig = cpl_table_get_int(objects, name, i, NULL);
12102                 for (k = ylow; k <= yhig; k++)
12103                     is_sky[k] = 0;
12104             }
12105         }
12106 
12107 
12108         /*
12109          * Eliminate isolated sky points
12110          */
12111 
12112         ylow = position[i] + margin + 1;
12113         yhig = position[i] + length[i] - margin - 1;
12114 
12115         for (j = ylow; j < yhig; j++)
12116             if (is_sky[j])
12117                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12118                     is_sky[j] = 0;
12119 
12120     }
12121 
12122 
12123     /*
12124      * Determination of the sky map
12125      */
12126 
12127     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12128 
12129     for (i = 0; i < nslits; i++) {
12130 
12131         if (length[i] == 0)
12132             continue;
12133 
12134         ylow = position[i];
12135         yhig = ylow + length[i];
12136 
12137         nsky = 0;
12138         for (j = ylow; j < yhig; j++)
12139             if (is_sky[j])
12140                 nsky++;
12141 
12142         if (nsky > order + 1) {
12143             if (order) {
12144                 points = cpl_vector_new(nsky);
12145                 nsky = 0;
12146                 for (j = ylow; j < yhig; j++) {
12147                     if (is_sky[j]) {
12148                         cpl_vector_set(points, nsky, j);
12149                         nsky++;
12150                     }
12151                 }
12152 
12153                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12154                 xdata = cpl_image_get_data(exslit);
12155                 values = cpl_vector_new(nsky);
12156 
12157                 for (j = 0; j < nx; j++) {
12158                     nsky = 0;
12159                     for (k = ylow; k < yhig; k++) {
12160                         if (is_sky[k]) {
12161                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12162                             nsky++;
12163                         }
12164                     }
12165 
12166                     /*
12167                      * Eliminate obvious outliers
12168                      */
12169 
12170                     median = cpl_vector_get_median_const(values);
12171                     vdata = cpl_vector_get_data(values);
12172                     pdata = cpl_vector_get_data(points);
12173                     nbad = 0;
12174                     for (k = 0; k < nsky; k++) {
12175                         if (fabs(vdata[k] - median) < 100) {
12176                             if (nbad) {
12177                                 vdata[k-nbad] = vdata[k];
12178                                 pdata[k-nbad] = pdata[k];
12179                             }
12180                         }
12181                         else
12182                             nbad++;
12183                     }
12184 
12185                     if (nsky == nbad)
12186                         continue;
12187 
12188                     if (nbad && nsky - nbad > order + 1) {
12189                         keep_values = values;
12190                         keep_points = points;
12191                         values = cpl_vector_wrap(nsky-nbad, vdata);
12192                         points = cpl_vector_wrap(nsky-nbad, pdata);
12193                     }
12194 
12195                     if (nsky - nbad > order + 1) {
12196 
12197                         fit = cpl_polynomial_fit_1d_create(points, values, 
12198                                                            order, NULL);
12199 
12200                         if (fit) {
12201                             for (k = ylow; k < yhig; k++) {
12202                                 xdata[j+(k-ylow)*nx] = 
12203                                          cpl_polynomial_eval_1d(fit, k, NULL);
12204                             }
12205 
12206                             cpl_polynomial_delete(fit);
12207                         }
12208                         else
12209                             cpl_error_reset();
12210                     }
12211                     else {
12212                         for (k = 0; k < nsky; k++) {
12213                             xdata[j+k*nx] = median;
12214                         }
12215                     }
12216 
12217                     if (nbad && nsky - nbad > order + 1) {
12218                         cpl_vector_unwrap(values);
12219                         cpl_vector_unwrap(points);
12220                         values = keep_values;
12221                         points = keep_points;
12222                     }
12223 
12224                     if (nbad) {
12225                         nsky = 0;
12226                         for (k = ylow; k < yhig; k++) {
12227                             if (is_sky[k]) {
12228                                 cpl_vector_set(points, nsky, k);
12229                                 nsky++;
12230                             }
12231                         }
12232                     }
12233 
12234                 }
12235 
12236                 cpl_vector_delete(values);
12237                 cpl_vector_delete(points);
12238 
12239                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12240                 cpl_image_delete(exslit);
12241 
12242             }
12243             else {
12244                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12245                 xdata = cpl_image_get_data(exslit);
12246                 values = cpl_vector_new(nsky);
12247 
12248                 for (j = 0; j < nx; j++) {
12249                     nsky = 0;
12250                     for (k = ylow; k < yhig; k++) {
12251                         if (is_sky[k]) {
12252                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12253                             nsky++;
12254                         }
12255                     }
12256 
12257                     median = cpl_vector_get_median_const(values);
12258 
12259                     for (k = ylow; k < yhig; k++)
12260                         xdata[j+(k-ylow)*nx] = median;
12261 
12262                 }
12263 
12264                 cpl_vector_delete(values);
12265 
12266                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12267                 cpl_image_delete(exslit);
12268             }
12269         }
12270         else
12271             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
12272     }
12273 
12274     cpl_free(is_sky);
12275 
12276     return skymap;
12277 
12278 }
12279 
12280 
12302 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
12303                                  float threshold, float ratio)
12304 {
12305     const char *func = "mos_clean_cosmics";
12306 
12307     cpl_image  *smoothImage;
12308     cpl_table  *table;
12309     cpl_matrix *kernel;
12310     int        *xdata;
12311     int        *ydata;
12312     float      *idata;
12313     float      *sdata;
12314     float       sigma, sum, value, smoothValue;
12315     double      noise;
12316     int         count;
12317     float       fMax;
12318     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
12319     int         xLen;
12320     int         yLen;
12321     int         nPix;
12322     int         first = 1;  /* position of first cosmic ray candidate
12323                                encountered while scanning the image */
12324     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
12325     int         numCosmic = 0;
12326     int         found, foundContiguousCandidate;
12327     int        *cosmic;
12328   
12329 
12330     if (image == NULL)
12331         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12332 
12333 
12334     /*
12335      *  "cosmic" is a flags holder (initialized to zero):
12336      *
12337      *           -1 = candidate for cosmic ray
12338      *            0 = not a cosmic
12339      *            1 = a cosmic ray
12340      *            2 = member of current group of contiguous candidates
12341      *            3 = examined member of current group
12342      */
12343 
12344     xLen = cpl_image_get_size_x(image);
12345     yLen = cpl_image_get_size_y(image);
12346 
12347     if (xLen < 4 || yLen < 4)
12348         return CPL_ERROR_NONE;
12349 
12350     nPix = xLen * yLen;
12351 
12352     /*
12353      * Noise estimation from negative offsets in image. Note that this
12354      * assumes that the background level (skyLevel) has already been 
12355      * subtracted from the data. In this way we estimate the noise due 
12356      * to detector readout and to the background signal level (before 
12357      * it were removed). Theoretically this is given by 
12358      *
12359      *        noise = sqrt(ron^2 + skyLevel/gain)
12360      *
12361      * where ron is the read-out-noise. To this we will sum the noise 
12362      * contribution due to any increase of the signal above the background
12363      * by an amount scienceLevel. Theoretically the total noise is given by
12364      *
12365      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
12366      *
12367      * that is
12368      *
12369      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
12370      *
12371      */
12372 
12373     idata = cpl_image_get_data(image);
12374     noise = 0.0;
12375     count = 0;
12376 
12377     for (i = 0; i < nPix; i++) {
12378         if (idata[i] < -0.00001) {
12379             noise -= idata[i];
12380             count++;
12381         }
12382     }
12383 
12384     noise /= count;
12385     noise *= 1.25;       /* Factor to convert average deviation to sigma */
12386 
12387     cosmic = cpl_calloc(nPix, sizeof(int));
12388 
12389     if (threshold < 0.)
12390         threshold = 4.0;
12391     if (ratio < 0.)
12392         ratio = 2.0;
12393 
12394     kernel = cpl_matrix_new(3, 3);
12395     cpl_matrix_fill(kernel, 1.0);
12396     cpl_matrix_set(kernel, 1, 1, 0.0);
12397     smoothImage = cpl_image_filter_median(image, kernel);
12398     cpl_matrix_delete(kernel);
12399     
12400     /*
12401      *  Loop on images pixels, searching for cosmic rays candidates.
12402      *  Border pixels are currently excluded (they cannot contain
12403      *  candidates), to avoid that the search for groups of contiguous
12404      *  pixels would ever go out of image boundaries. In future we may
12405      *  overcome this limit, adding an appropriate check when contiguous
12406      *  pixels are searched.
12407      */
12408 
12409     sdata = cpl_image_get_data(smoothImage);
12410 
12411     for (j = 1; j < yLen - 1; j++) {
12412         for (i = 1; i < xLen - 1; i++) {
12413             value = idata[i + j * xLen];
12414             smoothValue = sdata[i + j * xLen];
12415             if (smoothValue < 1.0)
12416                 smoothValue = 1.0;
12417             sigma = sqrt(noise * noise + smoothValue / gain);
12418             if (value - smoothValue >= threshold * sigma) 
12419                 cosmic[i + j * xLen] = -1;
12420         }
12421     }
12422 
12423     cpl_image_delete(smoothImage);
12424 
12425 
12426     /*
12427      *  Search for groups of contiguous cosmic rays candidates.
12428      */
12429 
12430     do {
12431         found = 0;
12432         for (pos = first; pos < nPix; pos++) {
12433             if (cosmic[pos] == -1) {
12434                 cosmic[pos] = 2;         /*  Candidate found.  */
12435                 i = pos % xLen;          /*  Its coordinates.  */
12436                 j = pos / xLen;
12437                 first = pos;
12438                 first++;      /* ???  really necessary? */
12439                 found = 1;
12440                 break;
12441             }
12442         }
12443 
12444         if (found) {
12445 
12446             /*
12447              *  Determine new group of contiguous cosmic rays candidates.
12448              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12449              *  and the value of the max pixel and its position, fMax, iPosMax,
12450              *  jPosMax.
12451              */
12452 
12453             iMin = iMax = iPosMax = i;
12454             jMin = jMax = jPosMax = j;
12455             fMax = idata[i + j * xLen];
12456 
12457             do {
12458                 foundContiguousCandidate = 0;
12459                 for (l = 0; l <= 1; l++) {
12460                     for (k = 0; k <= 1; k++) {
12461 
12462                         /*
12463                          *  Looping on 4 pixels to North, East, South and West
12464                          */
12465 
12466                         ii = i + k - l;
12467                         jj = j + k + l - 1;
12468                         if (cosmic[ii + jj * xLen] == -1) {
12469                             foundContiguousCandidate = 1;
12470                             cosmic[ii + jj * xLen] = 2;
12471                                         /* Candidate belongs to current group */
12472                             iii = ii;   /* Keep its position */
12473                             jjj = jj;
12474 
12475                             /*
12476                              * Upgrade search box
12477                              */
12478 
12479                             if (ii < iMin)
12480                                 iMin = ii;
12481                             if (ii > iMax)
12482                                 iMax = ii;
12483                             if (jj < jMin)
12484                                 jMin = jj;
12485                             if (jj > jMax)
12486                                 jMax = jj;
12487 
12488                             if (idata[ii + jj * xLen] > fMax) {
12489                                 fMax = idata[ii + jj * xLen];
12490                                 iPosMax = ii;
12491                                 jPosMax = jj;
12492                             }
12493                         }
12494                     }
12495                 }
12496 
12497                 /*
12498                  *  We are done exploring the "cross". Now mark as "examined"
12499                  *  the current candidate (at the center of the cross):
12500                  */
12501 
12502                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12503 
12504                 if (foundContiguousCandidate) {
12505 
12506                     /*
12507                      * Pass (arbitrarily) the coordinates of the LAST found 
12508                      * candidate
12509                      */
12510 
12511                     i = iii;
12512                     j = jjj;
12513 
12514                     /* 
12515                      * Skip the rest, continue loop on new candidate 
12516                      */
12517 
12518                     continue; 
12519                 }
12520 
12521 
12522                 /*
12523                  *  Look for leftovers in the (growing!) search box
12524                  */
12525 
12526                 for (l = jMin; l <= jMax; l++) {
12527                     for (k = iMin; k <= iMax; k++) {
12528                         if (cosmic[k + l * xLen] == 2) {
12529                             i = k;
12530                             j = l;
12531                             foundContiguousCandidate = 1;
12532                             break;
12533                         }
12534                     }
12535                     if (foundContiguousCandidate) 
12536                         break;
12537                 }
12538             } while (foundContiguousCandidate);
12539 
12540 
12541             /*
12542              *  No more contiguous candidates are found. Decide now
12543              *  whether the current group is a cosmic ray or not.
12544              */
12545 
12546             sum = 0.;                /* Sum of 8 pixels around max position */
12547             for (l = -1; l <= 1; l++) {
12548                 for (k = -1; k <= 1; k++) {
12549                     if (l != 0 || k != 0) {
12550                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12551                     }
12552                 }
12553             }
12554 
12555             sum /= 8.;
12556             if (fMax > ratio * sum) {
12557                 for (l = jMin - 1; l <= jMax + 1; l++) {
12558                     for (k = iMin - 1; k <= iMax + 1; k++) {
12559                         if (cosmic[k + l * xLen] == 3) {
12560                             cosmic[k + l * xLen] = 1;
12561                             numCosmic++;
12562                         }
12563                     }
12564                 }
12565             }
12566             else {
12567                 for (l = jMin - 1; l <= jMax + 1; l++) {
12568                     for (k = iMin - 1; k <= iMax + 1; k++) {
12569                         if (cosmic[k + l * xLen] != -1) {
12570                             if (cosmic[k + l * xLen] == 1) 
12571                                 numCosmic--;
12572                             cosmic[k + l * xLen] = 0;
12573                         }
12574                     }
12575                 }
12576             }
12577         }
12578     } while (found);
12579 
12580 
12581     /*
12582      *  Prepare table containing cosmic rays coordinates. 
12583      */
12584 
12585     table = cpl_table_new(numCosmic);
12586     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12587     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12588     cpl_table_set_column_unit(table, "x", "pixel");
12589     cpl_table_set_column_unit(table, "y", "pixel");
12590     xdata = cpl_table_get_data_int(table, "x");
12591     ydata = cpl_table_get_data_int(table, "y");
12592 
12593     for (pos = 0, i = 0; pos < nPix; pos++) {
12594         if (cosmic[pos] == 1) {
12595             xdata[i] = (pos % xLen);
12596             ydata[i] = (pos / xLen);
12597             i++;
12598         }
12599     }
12600 
12601     mos_clean_bad_pixels(image, table, 1);
12602 
12603     cpl_free(cosmic);
12604     cpl_table_delete(table);
12605 
12606     return CPL_ERROR_NONE;
12607 
12608 }
12609 
12610 
12611 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12612                                     int spectral)
12613 {
12614     const char *func = "mos_clean_cosmics";
12615  
12616     float       *idata;
12617     int         *isBadPix;
12618     int          i, j, k, d;
12619     int          xlen, ylen, totPix;
12620     int          nBadPixels = 0;
12621     int          sign, foundFirst;
12622     int         *xValue = NULL;
12623     int         *yValue = NULL;
12624     float        save = 0.;
12625     double       sumd;
12626     int          cx, cy;
12627     int          nPairs;
12628     float        estimate[4];
12629     int          sx[] = {0, 1, 1, 1};
12630     int          sy[] = {1,-1, 0, 1};
12631     int          searchHorizon = 100;
12632     int          percent = 15;
12633 
12634 
12635     if (image == NULL || table == NULL)
12636         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12637 
12638     if (1 != cpl_table_has_column(table, "x"))
12639         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12640 
12641     if (1 != cpl_table_has_column(table, "y"))
12642         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12643 
12644     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
12645         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12646 
12647     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
12648         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12649 
12650     nBadPixels = cpl_table_get_nrow(table);
12651 
12652     if (nBadPixels) {
12653         xlen = cpl_image_get_size_x(image);
12654         ylen = cpl_image_get_size_y(image);
12655         idata = cpl_image_get_data(image);
12656         totPix = xlen * ylen;
12657         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
12658             isBadPix = cpl_calloc(totPix, sizeof(int));
12659         }
12660         else {
12661             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
12662                             "skip bad pixel correction", percent);
12663             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12664         }
12665     }
12666     else {
12667         cpl_msg_debug(func, "No pixel values to interpolate");
12668         return CPL_ERROR_NONE;
12669     }
12670 
12671     xValue = cpl_table_get_data_int(table, "x");
12672     yValue = cpl_table_get_data_int(table, "y");
12673 
12674     for (i = 0; i < nBadPixels; i++)
12675         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
12676 
12677     for (i = 0; i < nBadPixels; i++) {
12678 
12679         /*
12680          *  Search for the closest good pixel along the 4 fundamental 
12681          *  directions (in both senses):
12682          *                            \ | /
12683          *                             \|/
12684          *                           --- ---
12685          *                             /|\
12686          *                            / | \
12687          *
12688          *  Then collect pairs of values to interpolate linearly.
12689          */
12690 
12691         nPairs = 0;
12692         for (j = 0; j < 4; j++) {
12693 
12694             if (spectral) /* Just horizontal interpolation for spectral data */
12695                 if (j != 2)
12696                     continue;
12697 
12698             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
12699             sumd = 0.;
12700             foundFirst = 0;
12701             for (k = 0; k < 2; k++) {
12702                 sign = 2 * k - 1;
12703                 d = 0;
12704                 cx = xValue[i];
12705                 cy = yValue[i];
12706                 do {
12707                     cx += sign * sx[j];
12708                     cy += sign * sy[j];
12709                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
12710                         break;
12711                     d++;
12712                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
12713 
12714                 if (cx >= 0 && cx < xlen && 
12715                     cy >= 0 && cy < ylen && d < searchHorizon) {
12716 
12717                     /*
12718                      *  In this block is cripted the linear interpolation...
12719                      */
12720 
12721                     save = idata[cx + cy * xlen];
12722                     estimate[nPairs] += save / d;
12723                     sumd += 1. / (double) d;
12724                     if (k) {
12725                         estimate[nPairs] /= sumd;
12726                         nPairs++;
12727                     }
12728                     else {
12729                         foundFirst = 1;
12730                     }
12731                 }
12732                 else {
12733 
12734                     /*
12735                      * Image borders were crossed, incomplete pair of values
12736                      */
12737 
12738                     if (k) {
12739                         if (foundFirst) {
12740                             estimate[nPairs] = save;
12741                             nPairs++;
12742                         }
12743                     }
12744                 }
12745             }
12746         }
12747 
12748         /*
12749          * Replace pixel value of the input image, corresponding to
12750          * the current bad pixel, with the median of the estimates
12751          * resulted from the 4 linear interpolations.
12752          */
12753 
12754         if (nPairs > 2) {
12755             idata[xValue[i] + yValue[i] * xlen] = 
12756                                cpl_tools_get_median_float(estimate, nPairs);
12757         }
12758         else if (nPairs == 2) {
12759             idata[xValue[i] + yValue[i] * xlen] =
12760                                             (estimate[0] + estimate[1]) / 2.;
12761         }
12762         else if (nPairs == 1) {
12763             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
12764         }
12765         else {
12766             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
12767                           xValue[i], yValue[i]);
12768         }
12769     }
12770 
12771     cpl_free(isBadPix);
12772 
12773     return CPL_ERROR_NONE;
12774 }
12775 
12776 
12806 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
12807                            cpl_table *polytraces, double reference,
12808                            double blue, double red, double dispersion)
12809 {
12810     const char *func = "mos_spatial_map";
12811 
12812     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
12813                                                  /* Max order is 5 */
12814     cpl_polynomial *polytop;
12815     cpl_polynomial *polybot;
12816     cpl_image      *calibration;
12817     float          *data;
12818     double          top, bot;
12819     double          coeff;
12820     double          ytop, ybot;
12821     double          ypos, yfra;
12822     double          factor;
12823     int             yint, yprev;
12824     int             nslits;
12825     int             npseudo;
12826     int            *slit_id;
12827     int            *length;
12828     int             nx, ny;
12829     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
12830     int             missing_top, missing_bot;
12831     int             null;
12832     int             order;
12833     int             i, j, k;
12834 
12835 
12836     if (spectra == NULL || slits == NULL || polytraces == NULL) {
12837         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12838         return NULL;
12839     }
12840 
12841     if (dispersion <= 0.0) {
12842         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12843         return NULL;
12844     }
12845 
12846     if (red - blue < dispersion) {
12847         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12848         return NULL;
12849     }
12850 
12851     nx = cpl_image_get_size_x(spectra);
12852     ny = cpl_image_get_size_y(spectra);
12853 
12854     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12855     data = cpl_image_get_data(calibration);
12856 
12857     length  = cpl_table_get_data_int(slits, "length");
12858     nslits  = cpl_table_get_nrow(slits);
12859     slit_id = cpl_table_get_data_int(slits, "slit_id");
12860     order   = cpl_table_get_ncol(polytraces) - 2;
12861 
12862     /*
12863      * The spatial resampling is performed for a certain number of 
12864      * pixels above and below the position of the reference wavelength:
12865      */
12866 
12867     pixel_above = (red - reference) / dispersion;
12868     pixel_below = (reference - blue) / dispersion;
12869 
12870     for (i = 0; i < nslits; i++) {
12871         
12872         if (length[i] == 0)
12873             continue;
12874 
12875         /*
12876          * Note that the x coordinate of the reference pixels on the CCD
12877          * is taken arbitrarily at the top end of each slit. This wouldn't
12878          * be entirely correct in case of curved slits, or in presence of
12879          * heavy distortions: in such cases the spatial resampling is
12880          * really performed across a wide range of wavelengths. But
12881          * the lag between top and bottom spectral curvature models 
12882          * would introduce even in such cases negligible effects on
12883          * the spectral spatial resampling.
12884          */
12885 
12886         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
12887 
12888         start_pixel = refpixel - pixel_below;
12889         if (start_pixel < 0)
12890             start_pixel = 0;
12891 
12892         end_pixel = refpixel + pixel_above;
12893         if (end_pixel > nx)
12894             end_pixel = nx;
12895 
12896         /*
12897          * Recover from the table of spectral curvature coefficients
12898          * the curvature polynomials.
12899          */
12900 
12901         missing_top = 0;
12902         polytop = cpl_polynomial_new(1);
12903         for (k = 0; k <= order; k++) {
12904             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
12905             if (null) {
12906                 cpl_polynomial_delete(polytop);
12907                 missing_top = 1;
12908                 break;
12909             }
12910             cpl_polynomial_set_coeff(polytop, &k, coeff);
12911         }
12912 
12913         missing_bot = 0;
12914         polybot = cpl_polynomial_new(1);
12915         for (k = 0; k <= order; k++) {
12916             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
12917             if (null) {
12918                 cpl_polynomial_delete(polybot);
12919                 missing_bot = 1;
12920                 break;
12921             }
12922             cpl_polynomial_set_coeff(polybot, &k, coeff);
12923         }
12924 
12925         if (missing_top && missing_bot) {
12926             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
12927                             slit_id[i]);
12928             continue;
12929         }
12930 
12931         /*
12932          * In case just one of the two edges was not traced, the other
12933          * edge curvature model is duplicated and shifted to the other
12934          * end of the slit: better than nothing!
12935          */
12936 
12937         if (missing_top) {
12938             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
12939                             "the spectral curvature of the lower edge "
12940                             "is used instead.", slit_id[i]);
12941             polytop = cpl_polynomial_duplicate(polybot);
12942             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12943             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12944             k = 0;
12945             coeff = cpl_polynomial_get_coeff(polybot, &k);
12946             coeff += ytop - ybot;
12947             cpl_polynomial_set_coeff(polytop, &k, coeff);
12948         }
12949 
12950         if (missing_bot) {
12951             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
12952                             "the spectral curvature of the upper edge "
12953                             "is used instead.", slit_id[i]);
12954             polybot = cpl_polynomial_duplicate(polytop);
12955             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
12956             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
12957             k = 0;
12958             coeff = cpl_polynomial_get_coeff(polytop, &k);
12959             coeff -= ytop - ybot;
12960             cpl_polynomial_set_coeff(polybot, &k, coeff);
12961         }
12962 
12963         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
12964         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
12965         npseudo = ceil(top-bot) + 1;
12966 
12967         if (npseudo < 1) {
12968             cpl_polynomial_delete(polytop);
12969             cpl_polynomial_delete(polybot);
12970             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
12971                             slit_id[i]);
12972             continue;
12973         }
12974 
12975         for (j = start_pixel; j < end_pixel; j++) {
12976             top = cpl_polynomial_eval_1d(polytop, j, NULL);
12977             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
12978             factor = (top-bot)/npseudo;
12979             for (k = 0; k <= npseudo; k++) {
12980                 ypos = top - k*factor;
12981                 yint = ypos;
12982                 yfra = ypos - yint;
12983                 if (yint >= 0 && yint < ny-1) {
12984                     data[j + nx*yint] = (top-yint)/factor;
12985                     if (k) {
12986 
12987                         /*
12988                          * This is added to recover lost pixels on
12989                          * the CCD image (pixels are lost because
12990                          * the CCD pixels are less than npseudo+1).
12991                          */
12992 
12993                         if (yprev - yint > 1) {
12994                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
12995                         }
12996                     }
12997                 }
12998                 yprev = yint;
12999             }
13000         }
13001         cpl_polynomial_delete(polytop);
13002         cpl_polynomial_delete(polybot);
13003     }
13004 
13005     return calibration;
13006 }
13007 
13008 
13071 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13072                               int maxradius, int conradius)
13073 {
13074     const char *func = "mos_detect_objects";
13075 
13076     cpl_image  *profile;
13077     float      *pdata;
13078     float      *p;
13079 
13080     char        name[MAX_COLNAME];
13081 
13082     int         nslits;
13083     int         npeaks;
13084     int         nobjects, objpos, totobj;
13085     int         maxobjects;
13086     int        *position;
13087     int        *length;
13088     int        *reject;
13089     double     *place;
13090     double     *bright;
13091     double      mindistance;
13092     int         pos, count;
13093     int         up;
13094     int         low, hig;
13095     int         row;
13096     int         i, j, k;
13097 
13098     const int   min_pixels = 10;
13099 
13100     
13101     if (cpl_error_get_code() != CPL_ERROR_NONE)
13102         return NULL;
13103  
13104     if (image == NULL || slits == NULL) { 
13105         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13106         return NULL;
13107     }
13108 
13109     if (margin < 0)
13110         margin = 0;
13111 
13112     if (maxradius < 0) {
13113         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13114         return NULL;
13115     }
13116 
13117     if (conradius < 0) {
13118         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13119         return NULL;
13120     }
13121 
13122     nslits   = cpl_table_get_nrow(slits);
13123     position = cpl_table_get_data_int(slits, "position");
13124     length   = cpl_table_get_data_int(slits, "length");
13125 
13126     profile = cpl_image_collapse_create(image, 1);
13127     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
13128     pdata = cpl_image_get_data(profile);
13129 
13130     row = 1;
13131     maxobjects = 0;
13132     totobj = 0;
13133     for (i = 0; i < nslits; i++) {
13134 
13135         if (length[i] == 0)
13136             continue;
13137 
13138         pos = position[i] + margin;
13139         count = length[i] - 2*margin;
13140 
13141         if (count < min_pixels)
13142             continue;
13143 
13144         p = pdata + pos;
13145 
13146 
13147         /*
13148          * Count peaks candidates
13149          */
13150 
13151         npeaks = 0;
13152         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13153             npeaks++;
13154         }
13155 
13156         up = 0;
13157         for (j = 0; j < count - 3; j++) {
13158             if (p[j] > 0) {
13159                 if (p[j+1] > p[j]) {
13160                     up++;
13161                 }
13162                 else {
13163                     if (up > 2) {
13164                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13165                             if (p[j] > 5)
13166                                 npeaks++;
13167                         }
13168                     }
13169                     else if (up > 1) {
13170                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13171                             if (p[j] > 5)
13172                                 npeaks++;
13173                         }
13174                     }
13175                     up = 0;
13176                 }
13177             }
13178             else {
13179                 up = 0;
13180             }
13181         }
13182 
13183         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
13184             && p[count-3] > p[count-4] && p[count-4] > 0) {
13185             npeaks++;
13186         }
13187 
13188         if (npeaks == 0)
13189             continue;
13190 
13191 
13192         /*
13193          * Get candidates parameters
13194          */
13195 
13196         reject = cpl_calloc(npeaks, sizeof(int));
13197         bright = cpl_calloc(npeaks, sizeof(double));
13198         place  = cpl_calloc(npeaks, sizeof(double));
13199 
13200         npeaks = 0;
13201         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13202             bright[0] = p[0];
13203             place[0] = position[i] + margin;
13204             npeaks++;
13205         }
13206 
13207         up = 0;
13208         for (j = 0; j < count - 3; j++) {
13209             if (p[j] > 0) {
13210                 if (p[j+1] > p[j]) {
13211                     up++;
13212                 }
13213                 else {
13214                     if (up > 2) {
13215                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13216                             if (p[j] > 5) {
13217                                 bright[npeaks] = p[j];
13218                                 place[npeaks] = position[i] + margin + j + 1
13219                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13220                                 npeaks++;
13221                             }
13222                         }
13223                     }
13224                     else if (up > 1) {
13225                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13226                             if (p[j] > 5) {
13227                                 bright[npeaks] = p[j];
13228                                 place[npeaks] = position[i] + margin + j + 1
13229                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13230                                 npeaks++;
13231                             }
13232                         }
13233                     }
13234                     up = 0;
13235                 }
13236             }
13237             else {
13238                 up = 0;
13239             }
13240         }
13241 
13242         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
13243             && p[count-3] > p[count-4] && p[count-4] > 0) {
13244             bright[npeaks] = p[count-1];
13245             place[npeaks] = position[i] + count;
13246             npeaks++;
13247         }
13248 
13249 
13250         /*
13251          * Now select the uncontaminated peaks
13252          */
13253 
13254         if (fabs(place[0] - pos) < 1.0)
13255             reject[0] = 1;
13256         if (fabs(place[npeaks-1] - pos - count) < 1.0)
13257             reject[npeaks-1] = 1;
13258         for (j = 0; j < npeaks; j++) {
13259             for (k = 0; k < npeaks; k++) {
13260                 if (k == j)
13261                     continue;
13262                 mindistance = conradius * bright[k] / bright[j] 
13263                                         * bright[k] / bright[j];
13264                 if (fabs(place[j] - place[k]) < mindistance)
13265                     reject[j] = 1;
13266             }
13267         }
13268 
13269 /* new part */
13270         for (j = 0; j < npeaks; j++) {
13271             if (reject[j])
13272                 continue;
13273             if (j) {
13274                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13275                     / (bright[j-1] + bright[j]) + 1;
13276             }
13277             else {
13278                 low = pos;
13279             }
13280             if (j < npeaks - 1) {
13281                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13282                     / (bright[j+1] + bright[j]) + 1;
13283             }
13284             else {
13285                 hig = pos + count;
13286             }
13287 
13288             if (low < pos)
13289                 low = pos;
13290             if (hig > pos + count)
13291                 hig = pos + count;
13292             if (place[j] - low > maxradius)
13293                 low = place[j] - maxradius;
13294             if (hig - place[j] > maxradius)
13295                 hig = place[j] + maxradius;
13296             if (hig == low)
13297                 reject[j] = 1;
13298         }
13299 /* end new part */
13300 
13301         nobjects = npeaks;
13302         for (j = 0; j < npeaks; j++)
13303             if (reject[j])
13304                 nobjects--;
13305 
13306         for (j = 0; j < nobjects; j++) {
13307             snprintf(name, MAX_COLNAME, "object_%d", j+1);
13308             if (cpl_table_has_column(slits, name))
13309                 continue;
13310             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
13311             snprintf(name, MAX_COLNAME, "start_%d", j+1);
13312             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13313             cpl_table_set_column_unit(slits, name, "pixel");
13314             snprintf(name, MAX_COLNAME, "end_%d", j+1);
13315             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13316             cpl_table_set_column_unit(slits, name, "pixel");
13317             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13318             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13319             cpl_table_set_column_unit(slits, name, "pixel");
13320         }
13321 
13322         objpos = nobjects;
13323         for (j = 0; j < npeaks; j++) {
13324             if (reject[j])
13325                 continue;
13326             if (j) {
13327                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13328                     / (bright[j-1] + bright[j]) + 1;
13329             }
13330             else {
13331                low = pos;
13332             }
13333             if (j < npeaks - 1) {
13334                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13335                     / (bright[j+1] + bright[j]) + 1;
13336             }
13337             else {
13338                 hig = pos + count;
13339             }
13340 
13341             if (low < pos)
13342                 low = pos;
13343             if (hig > pos + count)
13344                 hig = pos + count;
13345             if (place[j] - low > maxradius)
13346                 low = place[j] - maxradius;
13347             if (hig - place[j] > maxradius)
13348                 hig = place[j] + maxradius;
13349 
13350             snprintf(name, MAX_COLNAME, "object_%d", objpos);
13351             cpl_table_set_double(slits, name, i, place[j]);
13352             snprintf(name, MAX_COLNAME, "start_%d", objpos);
13353             cpl_table_set_int(slits, name, i, low);
13354             snprintf(name, MAX_COLNAME, "end_%d", objpos);
13355             cpl_table_set_int(slits, name, i, hig);
13356             snprintf(name, MAX_COLNAME, "row_%d", objpos);
13357             cpl_table_set_int(slits, name, i, row + objpos - 1);
13358             totobj++;
13359             objpos--;
13360         }
13361 
13362         row += nobjects;
13363 
13364         if (maxobjects < nobjects)
13365             maxobjects = nobjects;
13366 
13367         cpl_free(reject);
13368         cpl_free(bright);
13369         cpl_free(place);
13370 
13371     }
13372 
13373 /*    nobjects = row - nobjects;     A bug, I think... */
13374     row = cpl_table_get_nrow(slits);
13375 
13376     for (i = 0; i < row; i++) {
13377         for (j = 0; j < maxobjects; j++) {
13378             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13379             if (cpl_table_is_valid(slits, name, i))
13380                 cpl_table_set_int(slits, name, i, totobj -
13381                                   cpl_table_get_int(slits, name, i, NULL));
13382         }
13383     }
13384 
13385     for (i = 0; i < maxobjects; i++) {
13386         snprintf(name, MAX_COLNAME, "start_%d", i+1);
13387         cpl_table_fill_invalid_int(slits, name, -1);
13388         snprintf(name, MAX_COLNAME, "end_%d", i+1);
13389         cpl_table_fill_invalid_int(slits, name, -1);
13390         snprintf(name, MAX_COLNAME, "row_%d", i+1);
13391         cpl_table_fill_invalid_int(slits, name, -1);
13392     }
13393 
13394     return profile;
13395 }
13396 
13397 
13422 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13423                                 cpl_table *objects, int extraction, double ron,
13424                                 double gain, int ncombined)
13425 {
13426     const char *func = "mos_extract_objects";
13427 
13428     char        name[MAX_COLNAME];
13429 
13430     cpl_image **output;
13431     cpl_image  *extracted;
13432     cpl_image  *extr_sky;
13433     cpl_image  *error;
13434     cpl_image  *sciwin;
13435     cpl_image  *skywin;
13436     int         nslits;
13437     int         nobjects;
13438     int         maxobjects;
13439     int         nx, ny;
13440     int         ylow, yhig;
13441     int         i, j;
13442 
13443 
13444     if (science == NULL || sky == NULL) {
13445         cpl_msg_error(func, "Both scientific exposures are required in input");
13446         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13447         return NULL;
13448     }
13449 
13450     if (objects == NULL) {
13451         cpl_msg_error(func, "An object table is required in input");
13452         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13453         return NULL;
13454     }
13455 
13456     if (extraction < 0 || extraction > 1) {
13457         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13458                       "either 0 or 1", extraction); 
13459         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13460         return NULL;
13461     }
13462 
13463     if (ron < 0.0) {
13464         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13465         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13466         return NULL;
13467     }
13468 
13469     if (gain < 0.1) {
13470         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13471         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13472         return NULL;
13473     }
13474 
13475     if (ncombined < 1) {
13476         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13477                       "it should be at least 1", ncombined);
13478         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13479         return NULL;
13480     }
13481 
13482 
13483     /*
13484      * Count the max number of objects per slit. Note that maxobjects 
13485      * is intentionally the max number of objects increased by one.
13486      */
13487 
13488     maxobjects = 1;
13489     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13490     while (cpl_table_has_column(objects, name)) {
13491         maxobjects++;
13492         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13493     }
13494 
13495 
13496     /*
13497      * Count objects to extract
13498      */
13499 
13500     nobjects = 0;
13501     nslits = cpl_table_get_nrow(objects);
13502 
13503     for (i = 0; i < nslits; i++) {
13504         for (j = 1; j < maxobjects; j++) {
13505             snprintf(name, MAX_COLNAME, "object_%d", j);
13506             if (cpl_table_is_valid(objects, name, i))
13507                 nobjects++;
13508         }
13509     }
13510 
13511     if (nobjects == 0)
13512         return NULL;
13513 
13514     nx = cpl_image_get_size_x(science);
13515     ny = cpl_image_get_size_x(science);
13516 
13517     output = cpl_calloc(3, sizeof(cpl_image *));
13518     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13519     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13520     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13521 
13522 
13523     /*
13524      * Extract objects
13525      */
13526 
13527     nobjects = 0;
13528     for (i = 0; i < nslits; i++) {
13529         for (j = 1; j < maxobjects; j++) {
13530             snprintf(name, MAX_COLNAME, "object_%d", j);
13531             if (cpl_table_is_valid(objects, name, i)) {
13532                 snprintf(name, MAX_COLNAME, "start_%d", j);
13533                 ylow = cpl_table_get_int(objects, name, i, NULL);
13534                 snprintf(name, MAX_COLNAME, "end_%d", j);
13535                 yhig = cpl_table_get_int(objects, name, i, NULL);
13536                 snprintf(name, MAX_COLNAME, "row_%d", j);
13537                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13538                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13539                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13540 /*
13541  * Cleaning the cosmics locally was really NOT a good idea...
13542  * I leave it here, commented out, to never forget this mistake!
13543 
13544                 if (extraction) {
13545                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13546                 }
13547  */
13548                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13549                                nobjects, extraction, ron, gain, ncombined);
13550                 cpl_image_delete(sciwin);
13551                 cpl_image_delete(skywin);
13552                 nobjects++;
13553             }
13554         }
13555     }
13556 
13557     return output;
13558 
13559 }
13560 
13561 
13584 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13585                             double dispersion, int saturation, 
13586                             double *mfwhm, double *rmsfwhm,
13587                             double *resolution, double *rmsres, int *nlines)
13588 {
13589     cpl_vector *vector;
13590 
13591     int     i, j, n, m;
13592     int     position, maxpos;
13593     int     xlen, ylen;
13594     int     sp, ep;
13595     int     radius;
13596     int     sradius = 40;
13597     int     threshold = 250;    /* Peak must be so many ADUs above min */
13598 
13599     int     ifwhm;
13600     double  fwhm;
13601     double *buffer;
13602     double  min, max, halfmax;
13603     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
13604     double  value, rms;
13605 
13606     float  *data;
13607 
13608 
13609     *resolution = 0.0;
13610     *rmsres = 0.0;
13611     *nlines = 0;
13612 
13613     xlen = cpl_image_get_size_x(image);
13614     ylen = cpl_image_get_size_y(image);
13615     data = cpl_image_get_data(image);
13616 
13617     buffer = cpl_malloc(ylen * sizeof(double));
13618 
13619     /*
13620      *  Closest pixel to specified wavelength.
13621      */
13622 
13623     position = floor((lambda - startwave) / dispersion + 0.5);
13624 
13625     sp = position - sradius;
13626     ep = position + sradius;
13627 
13628     if (sp < 0 || ep > xlen) {
13629         cpl_free(buffer);
13630         return 0;
13631     }
13632 
13633     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
13634 
13635         /*
13636          *  Search interval for peak. Abort if too close to image border.
13637          */
13638 
13639         radius = mos_lines_width(data + i*xlen + position - sradius, 
13640                                  2*sradius + 1);
13641         if (radius < 5)
13642             radius = 5;
13643 
13644         sp = position - radius;
13645         ep = position + radius;
13646 
13647         if (sp < 0 || ep > xlen) {
13648             cpl_free(buffer);
13649             return 0;
13650         }
13651 
13652 
13653         /*
13654          *  Determine min-max value and position.
13655          */
13656 
13657         maxpos = sp;
13658         min = max = data[sp + i * xlen];
13659         for (j = sp; j < ep; j++) {
13660             if (data[j + i * xlen] > max) {
13661                 max = data[j + i * xlen];
13662                 maxpos = j;
13663             }
13664             if (data[j + i * xlen] < min) {
13665                 min = data[j + i * xlen];
13666             }
13667         }
13668 
13669         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
13670             continue;
13671 
13672         if (max - min < threshold)        /* Low signal... */
13673             continue;
13674 
13675         if (max > saturation)             /* Saturation */
13676             continue;
13677 
13678         /*
13679          *  Determine FWHM counting pixels with value greater than
13680          *  half of the max value, to the right and to the left of
13681          *  the max. Linear interpolation between the pixels where
13682          *  the transition happens.
13683          */
13684 
13685         halfmax = (max + min)/ 2.0;
13686 
13687         fwhm = 0.0;
13688         ifwhm = 0;
13689         for (j = maxpos; j < maxpos + radius; j++) {
13690             if (j < xlen) {
13691                 if (data[j + i * xlen] < halfmax) {
13692                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
13693                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
13694                     break;
13695                 }
13696                 ifwhm++;
13697             }
13698         }
13699 
13700         ifwhm = 0;
13701         for (j = maxpos; j > maxpos - radius; j--) {
13702             if (j >= 0) {
13703                 if (data[j + i * xlen] < halfmax) {
13704                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
13705                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
13706                     break;
13707                 }
13708                 ifwhm++;
13709             }
13710         }
13711 
13712         if (fwhm > 3.0) {
13713             buffer[n] = fwhm - 2.0;
13714             n++;
13715         }
13716 
13717     }
13718 
13719     if (n == 0) {
13720         cpl_free(buffer);
13721         return 0;
13722     }
13723 
13724     vector = cpl_vector_wrap(n, buffer);
13725     value = cpl_vector_get_median_const(vector);
13726     cpl_vector_unwrap(vector);
13727 
13728     rms = 0.0;
13729     for (i = 0, m = 0; i < n; i++) {
13730         if (fabs(buffer[i] - value) < cut) {
13731             rms += fabs(buffer[i] - value);
13732             m++;
13733         }
13734     }
13735 
13736     cpl_free(buffer);
13737 
13738     if (m < 3)
13739         return 0;
13740 
13741     rms /= m;
13742     rms *= 1.25;       /* Factor to convert average deviation to sigma */
13743 
13744     value *= dispersion;
13745     rms *= dispersion;
13746 
13747     *mfwhm = value;
13748     *rmsfwhm = rms;
13749 
13750     *resolution = lambda / value;
13751     *rmsres = *resolution * rms / value;
13752 
13753     *nlines = m;
13754 
13755     return 1;
13756 }
13757 
13758 
13780 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
13781                                 double dispersion, int saturation, 
13782                                 cpl_vector *lines)
13783 {
13784 
13785     cpl_table *table;
13786     double    *line;
13787     double     fwhm;
13788     double     rmsfwhm;
13789     double     resolution;
13790     double     rmsres;
13791     int        nref;
13792     int        nlines;
13793     int        i;
13794 
13795 
13796     nref = cpl_vector_get_size(lines);
13797     line = cpl_vector_get_data(lines);
13798 
13799     table = cpl_table_new(nref);
13800     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
13801     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
13802     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
13803     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
13804     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
13805     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
13806     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
13807     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
13808     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
13809 
13810     for (i = 0; i < nref; i++) {
13811         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
13812                                     saturation, &fwhm, &rmsfwhm, 
13813                                     &resolution, &rmsres, &nlines)) {
13814             cpl_table_set_double(table, "wavelength", i, line[i]);
13815             cpl_table_set_double(table, "fwhm", i, fwhm);
13816             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
13817             cpl_table_set_double(table, "resolution", i, resolution);
13818             cpl_table_set_double(table, "resolution_rms", i, rmsres);
13819             cpl_table_set_int(table, "nlines", i, nlines);
13820         }
13821         else
13822             cpl_table_set_int(table, "nlines", i, 0);
13823     }
13824 
13825     if (cpl_table_has_valid(table, "wavelength"))
13826         return table;
13827 
13828     cpl_table_delete(table);
13829 
13830     return NULL;
13831     
13832 }
13833 
13834 
13852 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
13853                             int ystart, int yend, double wstart, double wend)
13854 {
13855     const char *func = "mos_integrate_signal";
13856 
13857     double sum;
13858     float *sdata;
13859     float *wdata;
13860     int    nx, ny;
13861     int    x, y;
13862     
13863 
13864     if (image == NULL || wavemap == NULL) { 
13865         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13866         return 0.0;
13867     }
13868 
13869     if (ystart > yend || wstart >= wend) {
13870         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13871         return 0.0;
13872     }
13873 
13874     nx = cpl_image_get_size_x(image);
13875     ny = cpl_image_get_size_y(image);
13876 
13877     if (!(nx == cpl_image_get_size_x(wavemap) 
13878         && ny == cpl_image_get_size_y(wavemap))) {
13879         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
13880         return 0.0;
13881     }
13882 
13883     if (ystart < 0 || yend > ny) {
13884         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
13885         return 0.0;
13886     }
13887 
13888     sdata = cpl_image_get_data(image);
13889     wdata = cpl_image_get_data(wavemap);
13890 
13891     sdata += ystart*nx;
13892     wdata += ystart*nx;
13893 
13894     sum = 0.0;
13895     for (y = ystart; y < yend; y++) {
13896         for (x = 0; x < nx; x++) {
13897             if (wdata[x] < wstart || wdata[x] > wend)
13898                 continue;
13899             sum += sdata[x];
13900         }
13901         sdata += nx;
13902         wdata += nx;
13903     }
13904 
13905     return sum;
13906 
13907 }
13908 
13909 /****************************************************************************
13910  * From this point on, the instrument dependent functions are added:
13911  * they are functions that retrieve information that is stored in
13912  * the data headers in some instrument specific way, such as the
13913  * location of overscans, the slits positions on the telescope
13914  * focal plane, the gain factor, etc.
13915  */
13916 
13917 
13940 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
13941 {
13942     const char *func = "mos_load_slits_fors_mxu";
13943 
13944     cpl_table  *slits;
13945     char        keyname[MAX_COLNAME];
13946     const char *instrume;
13947     const char *target_name;
13948     float       slit_x;
13949     float       slit_y;
13950     float       length;
13951 /*    double      arc2mm = 0.53316;         */
13952     double      arc2mm = 0.528;
13953     int         nslits;
13954     int         slit_id;
13955     int         fors;
13956     int         chip;
13957     int         found;
13958 
13959     /*
13960      * The limits below are used to exclude from the loaded slit list
13961      * any slit that surely doesn't belong to the used chip. This is
13962      * a way to reduce the chance of ambiguous slit identification.
13963      */
13964 
13965     float      low_limit1 = 10.0;
13966     float      hig_limit2 = 30.0;
13967 
13968 
13969     if (cpl_error_get_code() != CPL_ERROR_NONE) {
13970         return NULL;
13971     }
13972 
13973     if (header == NULL) {
13974         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13975         return NULL;
13976     }
13977 
13978 
13979     /*
13980      * See if this is FORS1 or FORS2;
13981      */
13982 
13983     instrume = cpl_propertylist_get_string(header, "INSTRUME");
13984 
13985     fors = 0;
13986     if (instrume[4] == '1')
13987         fors = 1;
13988     if (instrume[4] == '2')
13989         fors = 2;
13990 
13991     if (fors != 2) {
13992         cpl_msg_error(func, "Wrong instrument: %s\n"
13993                       "FORS2 is expected for MXU data", instrume);
13994         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13995         return NULL;
13996     }
13997 
13998 
13999     /*
14000      * The master and slave chips can be identified by their positions
14001      * in the chip array in the case of FORS2 data (with fors1 the chip
14002      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14003      */
14004 
14005     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14006 
14007     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14008         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14009                       "in FITS header");
14010         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14011         return NULL;
14012     }
14013 
14014     if (chip != 1 && chip != 2) {
14015         cpl_msg_error(func, "Unexpected chip position in keyword "
14016                       "ESO DET CHIP1 Y: %d", chip);
14017         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14018         return NULL;
14019     }
14020 
14021 
14022     /*
14023      * Count slits in header (excluding reference slits, and the slits
14024      * that _surely_ belong to the other chip)
14025      */
14026 
14027     nslits = 0;
14028     slit_id = 0;
14029     found = 1;
14030 
14031     while (found) {
14032         slit_id++;
14033         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14034         if (cpl_propertylist_has(header, keyname)) {
14035             slit_y = cpl_propertylist_get_double(header, keyname);
14036 
14037             if (chip == 1)
14038                 if (slit_y < low_limit1)
14039                     continue;
14040             if (chip == 2)
14041                 if (slit_y > hig_limit2)
14042                     continue;
14043                 
14044             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14045                      slit_id + 100);
14046             if (cpl_propertylist_has(header, keyname)) {
14047                 target_name = cpl_propertylist_get_string(header, keyname);
14048                 if (strncmp(target_name, "refslit", 7))
14049                     nslits++;
14050             }
14051             else
14052                 nslits++;
14053         }
14054         else
14055             found = 0;
14056     }
14057 
14058     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14059         cpl_msg_error(func, "%s while loading slits coordinates from "
14060                       "FITS header", cpl_error_get_message());
14061         cpl_error_set_where(func);
14062         return NULL;
14063     }
14064 
14065     if (nslits == 0)  {
14066         cpl_msg_error(func, "No slits coordinates found in header");
14067         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14068         return NULL;
14069     }
14070 
14071     slits = cpl_table_new(nslits);
14072     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14073     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14074     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14075     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14076     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14077     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14078     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14079     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14080     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14081 
14082     nslits = 0;
14083     slit_id = 0; 
14084     found = 1;
14085     while (found) {
14086         slit_id++;
14087         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14088         if (cpl_propertylist_has(header, keyname)) {
14089             slit_y = cpl_propertylist_get_double(header, keyname);
14090 
14091             if (chip == 1) 
14092                 if (slit_y < low_limit1)
14093                     continue;
14094             if (chip == 2)
14095                 if (slit_y > hig_limit2)
14096                     continue;
14097 
14098             /*
14099              * Y-flip the slit position, to match CCD pixel coordinate
14100              * convention
14101              */
14102 
14103             slit_y = -slit_y;
14104 
14105             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
14106             slit_x = cpl_propertylist_get_double(header, keyname);
14107             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14108                 cpl_table_delete(slits);
14109                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14110                               keyname);
14111                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14112                 return NULL;
14113             }
14114 
14115             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
14116             length = cpl_propertylist_get_double(header, keyname);
14117             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14118                 cpl_table_delete(slits);
14119                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14120                               keyname);
14121                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14122                 return NULL;
14123             }
14124 
14125             length *= arc2mm;
14126 
14127             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14128                      slit_id + 100);
14129             if (cpl_propertylist_has(header, keyname)) {
14130                 target_name = cpl_propertylist_get_string(header, keyname);
14131                 if (strncmp(target_name, "refslit", 7)) {
14132                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14133                     cpl_table_set(slits, "xtop", nslits, slit_x);
14134                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14135                     cpl_table_set(slits, "xbottom", nslits, slit_x);
14136                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14137                     nslits++;
14138                 }
14139             }
14140             else {
14141                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14142                 cpl_table_set(slits, "xtop", nslits, slit_x);
14143                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14144                 cpl_table_set(slits, "xbottom", nslits, slit_x);
14145                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14146                 nslits++;
14147             }
14148         }
14149         else
14150             found = 0;
14151     }
14152 
14153     return slits;
14154 }
14155 
14156 
14179 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header)
14180 {
14181     const char *func = "mos_load_slits_fors_mos";
14182 
14183     cpl_table  *slits;
14184     char        keyname[MAX_COLNAME];
14185     const char *instrume;
14186     const char *chipname;
14187     float       slit_x;
14188     int         first_slit, last_slit;
14189     int         nslits;
14190     int         slit_id;
14191     int         fors;
14192     int         chip;
14193     int         fors_is_old;
14194 
14195     /*
14196      * The Y coordinates of the slits are fixed
14197      */
14198 
14199     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
14200                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
14201                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
14202                                -102.1 };
14203     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
14204                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
14205                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
14206                                -113.9 };
14207 
14208 
14209     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14210         return NULL;
14211     }
14212 
14213     if (header == NULL) {
14214         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14215         return NULL;
14216     }
14217 
14218 
14219     /*
14220      * See if this is FORS1 or FORS2;
14221      */
14222 
14223     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14224 
14225     fors = 0;
14226     if (instrume[4] == '1')
14227         fors = 1;
14228     if (instrume[4] == '2')
14229         fors = 2;
14230 
14231     if (fors == 0) {
14232         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14233                       instrume);
14234         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14235         return NULL;
14236     }
14237 
14238     /* FIXME:
14239      * This is the way FORS1 data belong to the upgraded chips,
14240      * named "Marlene" and "Norma III". It's a quick solution,
14241      * there are hardcoded values here!!!
14242      */
14243 
14244     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
14245 
14246     if (chipname[0] == 'M' || chipname[0] == 'N')
14247         fors_is_old = 0;
14248     else
14249         fors_is_old = 1;
14250 
14251     if (fors == 1 && fors_is_old) {
14252         first_slit = 1;
14253         last_slit = 19;
14254     }
14255     else {
14256 
14257         /*
14258          * The master and slave chips can be identified by their positions
14259          * in the chip array in the case of FORS2 data: chip = 2 is the 
14260          * master, chip = 1 is the slave.
14261          */
14262 
14263         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14264 
14265         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14266             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14267                           "in FITS header");
14268             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14269             return NULL;
14270         }
14271 
14272         if (chip != 1 && chip != 2) {
14273             cpl_msg_error(func, "Unexpected chip position in keyword "
14274                           "ESO DET CHIP1 Y: %d", chip);
14275             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14276             return NULL;
14277         }
14278 
14279         if (chip == 1) {
14280             first_slit = 12;
14281             last_slit = 19;
14282         }
14283         else {
14284             first_slit = 1;
14285             last_slit = 11;
14286         }
14287     }
14288 
14289 
14290     /*
14291      * Count slits in header (excluding closed slits - i.e. those with
14292      * offsets greater than 115 mm - and the slits that do not belong 
14293      * to this chip)
14294      */
14295 
14296     nslits = 0;
14297     slit_id = 0;
14298     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14299         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14300         if (cpl_propertylist_has(header, keyname)) {
14301             slit_x = cpl_propertylist_get_double(header, keyname);
14302             if (fabs(slit_x) < 115.0)
14303                 nslits++;
14304         }
14305         else {
14306             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
14307             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14308             return NULL;
14309         }
14310     }
14311 
14312     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14313         cpl_msg_error(func, "%s while loading slits coordinates from "
14314                       "FITS header", cpl_error_get_message());
14315         cpl_error_set_where(func);
14316         return NULL;
14317     }
14318 
14319     if (nslits == 0)  {
14320         cpl_msg_error(func, "No slits coordinates found in header");
14321         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14322         return NULL;
14323     }
14324 
14325     slits = cpl_table_new(nslits);
14326     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14327     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14328     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14329     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14330     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14331     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14332     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14333     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14334     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14335 
14336     nslits = 0;
14337     slit_id = 0;
14338     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14339         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14340         slit_x = cpl_propertylist_get_double(header, keyname);
14341         if (fabs(slit_x) < 115.0) {
14342             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14343             cpl_table_set(slits, "xtop", nslits, slit_x);
14344             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
14345             cpl_table_set(slits, "xbottom", nslits, slit_x);
14346             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
14347             nslits++;
14348         }
14349     }
14350 
14351     return slits;
14352 }
14353 
14354 
14378 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
14379 {
14380     const char *func = "mos_load_slits_fors_lss";
14381 
14382     cpl_table  *slits;
14383     char       *slit_name;
14384     const char *instrume;
14385     int         fors;
14386     int         chip;
14387     float       ytop;
14388     float       ybottom;
14389 
14390     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14391         return NULL;
14392     }
14393 
14394     if (header == NULL) {
14395         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14396         return NULL;
14397     }
14398 
14399 
14400     /*
14401      * See if this is FORS1 or FORS2;
14402      */
14403 
14404     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14405 
14406     fors = 0;
14407     if (instrume[4] == '1')
14408         fors = 1;
14409     if (instrume[4] == '2')
14410         fors = 2;
14411 
14412     if (fors == 0) {
14413         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14414                       instrume);
14415         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14416         return NULL;
14417     }
14418 
14419     if (fors == 1) {
14420         ytop = 109.94;
14421         ybottom = -109.94;
14422     }
14423     else {
14424 
14425         /*
14426          * The master and slave chips can be identified by their positions
14427          * in the chip array in the case of FORS2 data: chip = 2 is the 
14428          * master, chip = 1 is the slave.
14429          */
14430 
14431         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14432 
14433         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14434             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14435                           "in FITS header");
14436             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14437             return NULL;
14438         }
14439 
14440         if (chip != 1 && chip != 2) {
14441             cpl_msg_error(func, "Unexpected chip position in keyword "
14442                           "ESO DET CHIP1 Y: %d", chip);
14443             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14444             return NULL;
14445         }
14446 
14447         if (chip == 1) {
14448             ytop = 30.0;
14449             ybottom = -109.94;
14450         }
14451         else {
14452             ytop = 109.94;
14453             ybottom = -20.0;
14454         }
14455     }
14456 
14457 
14458     slits = cpl_table_new(1);
14459     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14460     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14461     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14462     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14463     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14464     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14465     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14466     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14467     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14468 
14469     slit_name = (char *)cpl_propertylist_get_string(header, 
14470                                                     "ESO INS SLIT NAME");
14471 
14472     cpl_table_set(slits, "ytop", 0, ytop);
14473     cpl_table_set(slits, "ybottom", 0, ybottom);
14474 
14475     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14476         cpl_table_set_int(slits, "slit_id", 0, 1);
14477         cpl_table_set(slits, "xbottom", 0, -0.075);
14478         cpl_table_set(slits, "xtop", 0, 0.075);
14479     }
14480     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14481         cpl_table_set_int(slits, "slit_id", 0, 2);
14482         cpl_table_set(slits, "xbottom", 0, 5.895);
14483         cpl_table_set(slits, "xtop", 0, 6.105);
14484     }
14485     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14486         cpl_table_set_int(slits, "slit_id", 0, 3);
14487         cpl_table_set(slits, "xbottom", 0, -6.135);
14488         cpl_table_set(slits, "xtop", 0, -5.865);
14489     }
14490     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14491         cpl_table_set_int(slits, "slit_id", 0, 4);
14492         cpl_table_set(slits, "xbottom", 0, 11.815);
14493         cpl_table_set(slits, "xtop", 0, 12.185);
14494     }
14495     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14496         cpl_table_set_int(slits, "slit_id", 0, 5);
14497         cpl_table_set(slits, "xbottom", 0, -12.265);
14498         cpl_table_set(slits, "xtop", 0, -11.735);
14499     }
14500     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14501         cpl_table_set_int(slits, "slit_id", 0, 6);
14502         cpl_table_set(slits, "xbottom", 0, 17.655);
14503         cpl_table_set(slits, "xtop", 0, 18.345);
14504     }
14505     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14506         cpl_table_set_int(slits, "slit_id", 0, 7);
14507         cpl_table_set(slits, "xbottom", 0, -18.425);
14508         cpl_table_set(slits, "xtop", 0, -17.575);
14509     }
14510     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14511         cpl_table_set_int(slits, "slit_id", 0, 8);
14512         cpl_table_set(slits, "xbottom", 0, 23.475);
14513         cpl_table_set(slits, "xtop", 0, 24.525);
14514     }
14515     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14516         cpl_table_set_int(slits, "slit_id", 0, 9);
14517         cpl_table_set(slits, "xbottom", 0, -24.66);
14518         cpl_table_set(slits, "xtop", 0, -23.34);
14519     }
14520     else {
14521         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14522                       slit_name);
14523         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14524         cpl_table_delete(slits);
14525         return NULL;
14526     }
14527 
14528     return slits;
14529 }
14530 
14531 
14546 double mos_get_gain_vimos(cpl_propertylist *header)
14547 {
14548     const char *func = "mos_get_gain_vimos";
14549 
14550     double gain = -1.0;
14551 
14552 
14553     if (cpl_error_get_code() != CPL_ERROR_NONE)
14554         return gain;
14555 
14556     if (header == NULL) {
14557         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14558         return gain;
14559     }
14560 
14561     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14562     if (cpl_error_get_code()) {
14563         cpl_error_set_where(func);
14564         gain = -1.0;
14565     }
14566 
14567     return gain;
14568 
14569 }
14570 
14571 
14591 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14592 {
14593     const char *func = "mos_load_slits_vimos";
14594 
14595     cpl_table *slits;
14596     char       keyname[MAX_COLNAME];
14597     float      slit_x;
14598     float      slit_y;
14599     float      dim_x;
14600     int        nslits;
14601     int        slit_id;
14602     int        i;
14603 
14604 
14605     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14606         return NULL;
14607     }
14608 
14609     if (header == NULL) {
14610         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14611         return NULL;
14612     }
14613 
14614     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
14615 
14616     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14617         cpl_error_set_where(func);
14618         return NULL;
14619     }
14620 
14621     slits = cpl_table_new(nslits);
14622     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14623     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14624     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14625     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14626     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14627     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14628     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14629     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14630     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14631 
14632     for (i = 0; i < nslits; i++) {
14633         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
14634         slit_id = cpl_propertylist_get_int(header, keyname);
14635         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14636             cpl_error_set_where(func);
14637             return NULL;
14638         }
14639         sprintf(keyname, "ESO INS SLIT%d X", i+1);
14640         slit_x = cpl_propertylist_get_double(header, keyname);
14641         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14642             cpl_error_set_where(func);
14643             return NULL;
14644         }
14645         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
14646         slit_y = cpl_propertylist_get_double(header, keyname);
14647         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14648             cpl_error_set_where(func);
14649             return NULL;
14650         }
14651         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
14652         dim_x = cpl_propertylist_get_double(header, keyname) / 2;
14653         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14654             cpl_error_set_where(func);
14655             return NULL;
14656         }
14657         cpl_table_set_int(slits, "slit_id", i, slit_id);
14658         cpl_table_set(slits, "xtop", i, slit_x - dim_x);
14659         cpl_table_set(slits, "ytop", i, slit_y);
14660         cpl_table_set(slits, "xbottom", i, slit_x + dim_x);
14661         cpl_table_set(slits, "ybottom", i, slit_y);
14662     }
14663 
14664     return slits;
14665 }
14666 
14667 
14694 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
14695                                     int check_consistency)
14696 {
14697     const char *func = "mos_load_overscans_vimos";
14698 
14699     int        nx = 0;
14700     int        ny = 0;
14701     int        px = 0;
14702     int        py = 0;
14703     int        ox = 0;
14704     int        oy = 0;
14705     int        vx = 0;
14706     int        vy = 0;
14707     int        nrows;
14708     cpl_table *overscans;
14709 
14710 
14711     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14712         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
14713         return NULL;
14714     }
14715 
14716     if (header == NULL) {
14717         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14718         return NULL;
14719     }
14720 
14721     if (cpl_propertylist_has(header, "NAXIS1"))
14722         nx = cpl_propertylist_get_int(header, "NAXIS1");
14723     if (cpl_propertylist_has(header, "NAXIS2"))
14724         ny = cpl_propertylist_get_int(header, "NAXIS2");
14725     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
14726         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
14727     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
14728         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
14729     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
14730         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
14731     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
14732         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
14733     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
14734         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
14735     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
14736         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
14737 
14738     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14739         cpl_msg_error(func, "Missing overscan keywords in header");
14740         cpl_error_set_where(func);
14741         return NULL;
14742     }
14743 
14744     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
14745         cpl_msg_error(func, "Missing overscan keywords in header");
14746         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14747         return NULL;
14748     }
14749 
14750     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
14751     if (check_consistency) {
14752         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14753         return NULL;
14754     }
14755     else {
14756         cpl_msg_debug(func, "Overscans description conflicts with "
14757               "reported image sizes, "
14758               "%d + %d + %d != %d or "
14759               "%d + %d + %d != %d",
14760               px, vx, ox, nx,
14761               py, vy, oy, ny);
14762     }
14763     }
14764 
14765     nrows = 0;
14766     if (px > 0)
14767         nrows++;
14768     if (ox > 0)
14769         nrows++;
14770     if (py > 0)
14771         nrows++;
14772     if (oy > 0)
14773         nrows++;
14774 
14775     if (nrows > 2) {
14776         cpl_msg_error(func, "Unexpected overscan regions "
14777                       "(both in X and Y direction)");
14778         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14779         return NULL;
14780     }
14781 
14782 
14783     /*
14784      * A row is added for the description of the valid region of the
14785      * exposure the input header belongs to.
14786      */
14787 
14788     nrows++;
14789 
14790     overscans = cpl_table_new(nrows);
14791     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
14792     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
14793     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
14794     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
14795 
14796     nrows = 0;
14797 
14798     cpl_table_set_int(overscans, "xlow", nrows, px);
14799     cpl_table_set_int(overscans, "ylow", nrows, py);
14800     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
14801     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
14802     nrows++;
14803 
14804     if (px > 0) {
14805         cpl_table_set_int(overscans, "xlow", nrows, 0);
14806         cpl_table_set_int(overscans, "ylow", nrows, 0);
14807         cpl_table_set_int(overscans, "xhig", nrows, px);
14808         cpl_table_set_int(overscans, "yhig", nrows, ny);
14809         nrows++;
14810     }
14811 
14812     if (ox > 0) {
14813         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
14814         cpl_table_set_int(overscans, "ylow", nrows, 0);
14815         cpl_table_set_int(overscans, "xhig", nrows, nx);
14816         cpl_table_set_int(overscans, "yhig", nrows, ny);
14817         nrows++;
14818     }
14819 
14820     if (py > 0) {
14821         cpl_table_set_int(overscans, "xlow", nrows, 0);
14822         cpl_table_set_int(overscans, "ylow", nrows, 0);
14823         cpl_table_set_int(overscans, "xhig", nrows, nx);
14824         cpl_table_set_int(overscans, "yhig", nrows, py);
14825         nrows++;
14826     }
14827 
14828     if (oy > 0) {
14829         cpl_table_set_int(overscans, "xlow", nrows, 0);
14830         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
14831         cpl_table_set_int(overscans, "xhig", nrows, nx);
14832         cpl_table_set_int(overscans, "yhig", nrows, ny);
14833         nrows++;
14834     }
14835 
14836     return overscans;
14837 
14838 }
14839 
14840 
14841 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
14842 {
14843     const char *func = "mos_load_overscans_fors";
14844 
14845     int        nports;
14846     int        nx = 0;
14847     int        ny = 0;
14848     int        px = 0;
14849     int        py = 0;
14850     int        ox = 0;
14851     int        oy = 0;
14852     int        rebin;
14853     int        nrows;
14854     cpl_table *overscans;
14855 
14856 
14857     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14858         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
14859         return NULL;
14860     }
14861 
14862     if (header == NULL) {
14863         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14864         return NULL;
14865     }
14866 
14867     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
14868         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
14869 
14870     if (nports == 4                                        && 
14871         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
14872         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
14873 
14874         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
14875 
14876         overscans = cpl_table_new(3);
14877         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
14878         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
14879         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
14880         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
14881 
14882         px = 16 / rebin;
14883         ox = 16 / rebin;
14884         nx = 2080 / rebin;
14885         ny = 2048 / rebin;
14886         nrows = 0;
14887 
14888         cpl_table_set_int(overscans, "xlow", nrows, px);
14889         cpl_table_set_int(overscans, "ylow", nrows, py);
14890         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
14891         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
14892         nrows++;
14893 
14894         cpl_table_set_int(overscans, "xlow", nrows, 0);
14895         cpl_table_set_int(overscans, "ylow", nrows, 0);
14896         cpl_table_set_int(overscans, "xhig", nrows, px);
14897         cpl_table_set_int(overscans, "yhig", nrows, ny);
14898         nrows++;
14899 
14900         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
14901         cpl_table_set_int(overscans, "ylow", nrows, 0);
14902         cpl_table_set_int(overscans, "xhig", nrows, nx);
14903         cpl_table_set_int(overscans, "yhig", nrows, ny);
14904         nrows++;
14905     }
14906     else {
14907         overscans = mos_load_overscans_vimos(header, 0);
14908     }
14909 
14910     return overscans;
14911 
14912 }
14913 
14945 #define READY 1
14946 #ifdef READY
14947 
14948 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
14949                                        int samples, int order)
14950 {
14951 
14952     const char *func = "mos_montecarlo_polyfit";
14953 
14954     cpl_polynomial *p;
14955     cpl_polynomial *q;
14956     cpl_vector     *listx;
14957     cpl_vector     *listy;
14958     double          err;
14959     double         *x;
14960     double         *px;
14961     double         *x_eval;
14962     double         *px_eval;
14963     double         *sigma;
14964     double         *vy;
14965     double         *dy;
14966     int             npoints, nevaluate;
14967     int             i, j;
14968 
14969 
14970     if (points == NULL || evaluate == NULL) {
14971         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14972         return NULL;
14973     }
14974 
14975     if (!cpl_table_has_column(points, "x")) {
14976         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14977         return NULL;
14978     }
14979 
14980     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
14981         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14982         return NULL;
14983     }
14984 
14985     if (cpl_table_has_invalid(points, "x")) {
14986         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14987         return NULL;
14988     }
14989 
14990     if (!cpl_table_has_column(points, "y")) {
14991         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14992         return NULL;
14993     }
14994 
14995     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
14996         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
14997         return NULL;
14998     }
14999 
15000     if (cpl_table_has_invalid(points, "y")) {
15001         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15002         return NULL;
15003     }
15004 
15005     if (cpl_table_has_column(points, "y_err")) {
15006 
15007         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
15008             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15009             return NULL;
15010         }
15011     
15012         if (cpl_table_has_invalid(points, "y_err")) {
15013             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15014             return NULL;
15015         }
15016     }
15017 
15018     if (!cpl_table_has_column(evaluate, "x")) {
15019         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15020         return NULL;
15021     }
15022 
15023     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
15024         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15025         return NULL;
15026     }
15027 
15028     if (cpl_table_has_invalid(evaluate, "x")) {
15029         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15030         return NULL;
15031     }
15032 
15033     if (samples < 2 || order < 0) {
15034         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15035         return NULL;
15036     }
15037 
15038     npoints = cpl_table_get_nrow(points);
15039     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
15040     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
15041 
15042     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
15043 
15044     if (!cpl_table_has_column(points, "y_err")) {
15045         err = sqrt(err);
15046         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
15047         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
15048         cpl_msg_info(func, "Error column not found - set to %f\n", err);
15049     }
15050 
15051     /*
15052      * Create columns containing modeled values at each x
15053      */
15054 
15055     if (cpl_table_has_column(points, "px"))
15056         cpl_table_erase_column(points, "px");
15057     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
15058     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
15059     x = cpl_table_get_data_double(points, "x");
15060     px = cpl_table_get_data_double(points, "px");
15061     for (i = 0; i < npoints; i++)
15062         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
15063 
15064     nevaluate = cpl_table_get_nrow(evaluate);
15065 
15066     if (cpl_table_has_column(evaluate, "px"))
15067         cpl_table_erase_column(evaluate, "px");
15068     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
15069     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
15070     x_eval = cpl_table_get_data_double(evaluate, "x");
15071     px_eval = cpl_table_get_data_double(evaluate, "px");
15072     for (i = 0; i < nevaluate; i++)
15073         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
15074 
15075     /*
15076      * Initialise column with sigma
15077      */
15078 
15079     if (cpl_table_has_column(evaluate, "sigma"))
15080         cpl_table_erase_column(evaluate, "sigma");
15081     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
15082     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
15083     sigma = cpl_table_get_data_double(evaluate, "sigma");
15084 
15085     /*
15086      * Compute varied y cordinates to fit
15087      */
15088 
15089     if (cpl_table_has_column(points, "vy"))
15090         cpl_table_erase_column(points, "vy");
15091     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
15092     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
15093     vy = cpl_table_get_data_double(points, "vy");
15094     dy = cpl_table_get_data_double(points, "y_err");
15095     cpl_vector_unwrap(listy);
15096     listy = cpl_vector_wrap(npoints, vy);
15097 
15098     for (i = 0; i < samples; i++) {
15099         for (j = 0; j < npoints; j++)
15100             vy[j] = px[j] + dy[j] * mos_randg(1);
15101         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
15102         for (j = 0; j < nevaluate; j++)
15103             sigma[j] += fabs(px_eval[j] 
15104                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
15105         cpl_polynomial_delete(q);
15106     }
15107 
15108     /* 
15109      * Factor 1.25 to convert average deviation to sigma 
15110      */
15111 
15112     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
15113     cpl_table_divide_scalar(evaluate, "sigma", samples);
15114 
15115     cpl_vector_unwrap(listx);
15116     cpl_vector_unwrap(listy);
15117 
15118     return p;
15119 }
15120 
15121 #endif
15122 
15132 cpl_error_code mos_refmask_find_gaps(cpl_mask  * refmask,
15133                      cpl_image * master_flat)
15134 {
15135     int          nx     = cpl_mask_get_size_x(refmask);
15136     int          ny     = cpl_mask_get_size_y(refmask);
15137 
15138     int        * xpos   = cpl_calloc(sizeof(int), ny);
15139 
15140     cpl_vector * v      = cpl_vector_new(ny);
15141     cpl_vector * truev;
15142     int          nvalid = 0;
15143     double     * flats  = cpl_vector_get_data(v);
15144 
15145     double       median, stdev;
15146 
15147     int          i;
15148 
15149 
15150     for (i = 1; i <= ny; i++) {
15151     int j = 0;
15152 
15153     do j++;
15154     while (!cpl_mask_get(refmask, j, i) && j < nx);
15155 
15156     if (j < nx) {
15157         int rejected;
15158 
15159             xpos[i - 1] = j;
15160         flats[nvalid] = cpl_image_get(master_flat, j, i, &rejected);
15161             nvalid++;
15162     }
15163         else {
15164             xpos[i - 1] = -1;
15165         }
15166     }
15167 
15168     truev = cpl_vector_wrap(nvalid, flats);
15169 
15170     median = cpl_vector_get_median(truev);
15171     stdev = cpl_vector_get_stdev(truev);
15172 
15173     cpl_vector_unwrap(truev);
15174     cpl_vector_delete(v);
15175 
15176     for (i = 1; i <= ny; i++) {
15177     if (xpos[i - 1] > 0) {
15178         int    rejected;
15179         double kappa = 1.0;
15180         double delta = 
15181         cpl_image_get(master_flat, xpos[i - 1], i, &rejected) - median;
15182 
15183         if (fabs(delta) > stdev * kappa) {
15184         int j = 0;
15185         
15186         while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
15187             cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
15188             j++;
15189         }
15190         }
15191     }
15192     }
15193 
15194     cpl_free(xpos);
15195 
15196     return cpl_error_get_code();
15197 }
15198 
15206 cpl_error_code mos_saturation_process(cpl_image * image)
15207 {
15208     int     nx    = cpl_image_get_size_x(image);
15209     int     ny    = cpl_image_get_size_y(image);
15210     int     npix  = nx * ny;
15211     float * sdata = cpl_image_get_data_float(image);
15212 
15213     int count, i, j, k;
15214 
15215     /*
15216      * This is used to avoid saturation level coded with pixel value zero
15217      * To make it more robust against random 0.0 values, check that also
15218      * next pixel along the spatial direction is 0.0.
15219      */
15220 
15221     for (i = 0; i < npix - nx; i++)
15222         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
15223             sdata[i] = 65535.0;
15224 
15225     for (i = npix - nx; i < npix; i++)
15226         if (sdata[i] == 0.0) 
15227             sdata[i] = 65535.0;
15228 
15229     /*
15230      * This is a dirty trick to overcome saturations (making up a false
15231      * tip on their flat tops). This should be useless with a better
15232      * peak detection algorithm.
15233      */
15234 
15235     for (i = 0; i < npix; i++) {
15236         if (sdata[i] >= 65535.0) {
15237             count = 0;
15238             for (j = i; j < npix; j++) {
15239                 if (sdata[j] < 65535.0) {
15240                     break;
15241                 }
15242                 else {
15243                     count++;
15244                 }
15245             }
15246             if (count < 30 && count > 2) {
15247                 for (j = i; j < i + count/2; j++)
15248                     sdata[j] = sdata[i] + 1000.0 * (j - i);
15249                 if (count % 2 != 0) {
15250                     sdata[j] = sdata[j-1] + 1000.0;
15251                     j++;
15252                 }
15253                 for (k = j; k <= i + count; k++)
15254                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
15255                 i = k;
15256             }
15257         }
15258     }
15259 
15260     return cpl_error_get_code();
15261 }
15262 
15270 cpl_error_code mos_subtract_background(cpl_image * image)
15271 {
15272     /*
15273      * Create and subtract background
15274      */
15275 
15276     cpl_image * bimage = mos_arc_background(image, 15, 15);
15277     cpl_image_subtract(image, bimage);
15278     cpl_image_delete(bimage);
15279 
15280     return cpl_error_get_code();
15281 }
15282 
15297 cpl_error_code mos_object_intersect(cpl_table ** slitss,
15298                     cpl_table * origslits, int nscience)
15299 {
15300     int i, j;
15301 
15302     cpl_table *summary;
15303     int summary_nobjs = 0;
15304  
15305     int nobjs;
15306 
15307     int nmatches;
15308     int nslits = cpl_table_get_nrow(slitss[0]);
15309 
15310     const float tolerance = 5.0;  /* Tolerance in object position (pixel) */
15311 
15312     int maxobjs;
15313     int k, m;
15314     int nstokes, sstokes;
15315 
15316     cpl_table **work;
15317 
15318     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
15319 
15320 
15321     /* 
15322      * First we build a table listing the offset of each detected
15323      * object at each angle and each beam, from the bottom of each 
15324      * slit spectrum, and the pair that slit spectrum belongs to.
15325      * This summary table will have as many rows as objects found 
15326      * in total at all angles.
15327      */
15328 
15329     for (j = 0; j < nscience; j++) {
15330         int c_nobjs = mos_get_nobjects(slitss[j]);
15331         if (!c_nobjs) 
15332             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15333         summary_nobjs += c_nobjs;
15334     }
15335 
15336     summary = cpl_table_new(summary_nobjs);
15337 
15338     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
15339     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
15340 
15341     /*
15342      * Fill the summary table with data from all objects:
15343      */
15344 
15345     nobjs = 0;
15346 
15347     /* Loop on all object tables (one for each angle) */
15348     for (j = 0; j < nscience; j++) {
15349         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
15350 
15351         /* Loop on all slits found on first - i.e., ALL - object table */
15352     for (k = 0; k < nslits; k++) {
15353 
15354             /* Loop on all objects found on each object table */
15355         for (m = 0; m < c_maxobjs; m++) {
15356         int null;
15357         char *name = cpl_sprintf("object_%d", m + 1);
15358         double obj = cpl_table_get_double(slitss[j], name, k, &null);
15359         int pos;
15360                 int pair;
15361 
15362         cpl_free(name);
15363 
15364         if (null) 
15365                     break;  /* No object #m+1 in this slit - go to next slit */
15366 
15367                 /*
15368                  * Copy necessary object data to summary table. Note 
15369                  * that the absolute object position (row) in the
15370                  * rectified image is made relative to the bottom
15371                  * position (row) of the current slit.
15372                  */ 
15373         
15374                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
15375                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
15376                 cpl_table_set(summary, "offset", nobjs, obj - pos);
15377                 cpl_table_set(summary, "pair", nobjs, pair);
15378 
15379                 nobjs++;
15380         }
15381     }
15382     }
15383 
15384 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
15385 
15386     /* 
15387      * Perform the intersection: what are the objects belonging
15388      * to the same slit (same pair ordinary + extraordinary) which 
15389      * are observed at the same offset at all angles? Those are
15390      * the polarimetric objects.
15391      */
15392 
15393     nmatches = 0;
15394     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
15395 
15396     /*
15397      * We loop on the objects of the first-angle object table as 
15398      * reference, and check whether those objects are present also
15399      * at *all* other angles. Note that the loop advances by pairs.
15400      * It is implicitely assumed that the top (k = 0) slit spectrum
15401      * is always from the ordinary beam, and the spectrum below (k+1)
15402      * its extraordinary match. One should always be sure that the
15403      * input slit tables are constructed in this way in other parts 
15404      * of the code!
15405      */
15406 
15407     for (k = 0; k < nslits; k+=2) {
15408         int slitmatches = 0;
15409     for (m = 0; m < maxobjs; m++) {
15410         int null;
15411         char *name = cpl_sprintf("object_%d", m + 1);
15412         double obj = cpl_table_get_double(slitss[0], name, k, &null);
15413         double pos;
15414             int pair;
15415 
15416         char *name_obj;
15417         char *name_start;
15418         char *name_end;
15419             char *name_row;
15420             char *name_row_s;
15421 
15422         char *name_start_o;
15423         char *name_end_o;
15424             char *name_row_o;
15425 
15426             int start, end;
15427             int length;
15428  
15429             int selected;
15430 
15431 
15432         cpl_free(name);
15433 
15434         if (null) 
15435                 break;
15436 
15437             /*
15438              * Each object of the first object table belongs to a
15439              * slit spectrum (k). This slit spectrum has a position
15440              * in the rectified image, and it belongs to a given 
15441              * ordinary + extraordinary pair.
15442              */
15443      
15444             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
15445             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
15446 
15447 
15448             /*
15449              * Now from the summary table we can select all objects
15450              * which have the same offset (obj - pos) within all slit
15451              * spectra belonging to the same ordinary + extraordinary 
15452              * pair (at all angles).
15453              */
15454 
15455         cpl_table_select_all(summary);  /* Reset selection */
15456 
15457             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
15458             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
15459                                           obj - pos + tolerance);
15460         selected = 
15461             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
15462                                           obj - pos - tolerance);
15463 
15464             /*
15465              * If this object were observed at all angles (nscience) and 
15466              * at all beams (2), we should have selected exactly 2*nscience
15467              * objects. If not, this is not a polarimetric object, and it
15468              * is discarded from the intersection.
15469              */
15470         
15471         if (selected != nscience * 2) 
15472                 continue;
15473 
15474             /*
15475              * If we reach this point we have found one valid polarimetric
15476              * object, that must be inserted in the intersection object
15477              * table.
15478              */
15479  
15480             slitmatches++;
15481 
15482             /*
15483              * Names of the columns of the output table where the
15484              * object information needs to be copied. Note that a
15485              * new column is created, the "row_stokes_#", where the
15486              * row number of the extracted polarimetric signal is
15487              * also computed. For the moment this column will be 
15488              * left empty - it will be filled only when all matches 
15489              * are collected.
15490              */
15491 
15492         name_obj   = cpl_sprintf("object_%d",     slitmatches);
15493         name_start = cpl_sprintf("start_%d",      slitmatches);
15494         name_end   = cpl_sprintf("end_%d",        slitmatches);
15495         name_row   = cpl_sprintf("row_%d",        slitmatches);
15496         name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
15497 
15498             /*
15499              * Names of the columns of the input table where the
15500              * object information is available.
15501              */
15502 
15503         name_start_o = cpl_sprintf("start_%d",  m + 1);
15504         name_end_o   = cpl_sprintf("end_%d",    m + 1);
15505         name_row_o   = cpl_sprintf("row_%d",    m + 1);
15506 
15507             /*
15508              * If the output columns do not exist yet, create them.
15509              */
15510  
15511         if (!cpl_table_has_column(origslits, name_obj)) {
15512         cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
15513             cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
15514         cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
15515         cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
15516         cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
15517             }
15518 
15519             /*
15520              * The current slit spectrum is k. The slit spectrum immediately
15521              * below (in the rectified image) is k+1. We need the length of
15522              * the spectrum below for computing the _absolute_ coordinates
15523              * of the objects in the rectified image in both beams.
15524              */
15525  
15526         length = cpl_table_get_int(origslits, "length", k + 1, &null);
15527 
15528             /*
15529              * Read from the first input object table (first angle)
15530              * the spatial window enclosing the object.
15531              */
15532 
15533         start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
15534         end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
15535 
15536             /*
15537              * Write the object coordinates in the same slit, and in the
15538              * slit below. Note that here we assume that all slits were
15539              * traced perfectly, and we compute the theoretical coords
15540              * (obj - length) within the next slit spectrum (k + 1). In
15541              * principle we should read them as well from the input
15542              * table!
15543              */
15544 
15545             cpl_table_set_double(origslits, name_obj,   k,     obj);
15546             cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
15547 
15548             cpl_table_set_int(origslits,    name_start, k,     start);
15549             cpl_table_set_int(origslits,    name_start, k + 1, start - length);
15550 
15551             cpl_table_set_int(origslits,    name_end,   k,     end);
15552             cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
15553 
15554             /*
15555              * "nmatches" is counting at what "reduced" image row the
15556              * extracted spectra are. Note that this is s preliminary
15557              * numbering - which is wrong: other objects may be found
15558              * in the same slit, and then the indeces would not be in
15559              * sequence. What is important is that at the end of this
15560              * loop "nmatches" would be the total number of matching 
15561              * objects. The two cpl_table_set_int() calls made here
15562              * cannot be removed - they "validate" those table elements
15563              * (see ahead). 
15564              */
15565 
15566             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
15567         nmatches++;
15568             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
15569         nmatches++;
15570 
15571         cpl_free(name_obj);
15572         cpl_free(name_start);
15573         cpl_free(name_end);
15574             cpl_free(name_row);
15575             cpl_free(name_row_s);
15576 
15577         cpl_free(name_start_o);
15578         cpl_free(name_end_o);
15579             cpl_free(name_row_o);
15580         }
15581     }
15582 
15583     /*
15584      * The summary table has fulfilled its function. If no matching 
15585      * objects are found, the function returns with an error.
15586      */
15587 
15588     cpl_table_delete(summary);
15589 
15590     if (!nmatches)
15591         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
15592 
15593     /*
15594      * Now we consider the resulting intersection object table,
15595      * listing all matches. As seen, the image row number reported
15596      * in the columns "row_#" was not really performed sequentially.
15597      * We need to renumber sequentially...
15598      * We need also to fill the "row_stokes_#" column the way the
15599      * extracted polarimetric signal will be stored in the 
15600      * reduced_pol_images...
15601      */
15602  
15603     maxobjs = mos_get_maxobjs_per_slit(origslits);
15604     nstokes = nmatches / 2;         /* nmatches is always an even number     */
15605 
15606     for (k = 0; k < nslits; k++) {
15607         if (k % 2) { /* Extraordinary beam */
15608             nstokes = sstokes;      /* Use same start value as for ordinary  */
15609         }
15610         else {       /* Ordinary beam      */
15611             sstokes = nstokes;      /* Memorise start value at ordinary beam */
15612         }
15613 
15614     for (m = 0; m < maxobjs; m++) {
15615         char *name       = cpl_sprintf("row_%d",        m + 1);
15616             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
15617 
15618         if (!cpl_table_is_valid(origslits, name, k)) {
15619             cpl_free(name);
15620                 cpl_free(namestokes);
15621             break;
15622         }
15623             else { 
15624                 nmatches--;
15625                 nstokes--;
15626             cpl_table_set_int(origslits, name, k, nmatches);
15627                 cpl_table_set_int(origslits, namestokes, k, nstokes);
15628         }
15629 
15630         cpl_free(name);
15631             cpl_free(namestokes);
15632     }
15633     }
15634 
15635 
15636     /*
15637      * This is done to avoid the NULL value is zero (it would invalidate
15638      * also the row_# = 0 or start_# = 0 for an object), and to enable 
15639      * working directly with the column data buffers, when using this 
15640      * table afterwards.
15641      */
15642 
15643     for (j = 0; j < maxobjs; j++) {
15644     char *name = cpl_sprintf("object_%d", j + 1);
15645     cpl_table_fill_invalid_double(origslits, name, -1);
15646     cpl_free(name);
15647 
15648     name       = cpl_sprintf("start_%d", j + 1);
15649     cpl_table_fill_invalid_int(origslits, name, -1);
15650     cpl_free(name);
15651 
15652     name       = cpl_sprintf("end_%d", j + 1);
15653     cpl_table_fill_invalid_int(origslits, name, -1);
15654     cpl_free(name);
15655 
15656     name       = cpl_sprintf("row_%d", j + 1);
15657     cpl_table_fill_invalid_int(origslits, name, -1);
15658     cpl_free(name);
15659 
15660     name       = cpl_sprintf("row_stokes_%d", j + 1);
15661     cpl_table_fill_invalid_int(origslits, name, -1);
15662     cpl_free(name);
15663     }
15664 
15665     /*********************************************************************
15666      * This tail has been added to propagate the selection of valid
15667      * objects also to the input slitss[] tables. Just eliminate all
15668      * this final part to suppress this behaviour.
15669      */
15670 
15671     /*
15672      * First of all, make a working copy and remove all columns related 
15673      * to objects from the input object tables. 
15674      */
15675 
15676     for (i = 0; i < nscience; i++) {
15677         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
15678 
15679         work[i] = cpl_table_duplicate(slitss[i]);
15680 
15681         for (m = 0; m < c_maxobjs; m++) {
15682             char *object_o = cpl_sprintf("object_%d", m + 1);
15683             char *start_o  = cpl_sprintf("start_%d",  m + 1);
15684             char *end_o    = cpl_sprintf("end_%d",    m + 1);
15685             char *row_o    = cpl_sprintf("row_%d",    m + 1);
15686 
15687             cpl_table_erase_column(slitss[i], object_o);
15688             cpl_table_erase_column(slitss[i], start_o);
15689             cpl_table_erase_column(slitss[i], end_o);
15690             cpl_table_erase_column(slitss[i], row_o);
15691         }
15692     }
15693 
15694     /* 
15695      * Now just consider all the objects in the intersection table.
15696      */
15697 
15698     for (k = 0; k < nslits; k++) {
15699         for (j = 0; j < maxobjs; j++) {
15700             double object_w, object_r;
15701             int    start_w, start_r;
15702             int    end_w, end_r;
15703             int    row_w, row_r;
15704 
15705         char  *object_i = cpl_sprintf("object_%d", j + 1);
15706         char  *start_i  = cpl_sprintf("start_%d",  j + 1);
15707         char  *end_i    = cpl_sprintf("end_%d",    j + 1);
15708         char  *row_i    = cpl_sprintf("row_%d",    j + 1);
15709 
15710 
15711             if (!cpl_table_is_valid(origslits, object_i, k))
15712                 break;
15713 
15714             /* 
15715              * We have found a valid object (valid because it belongs
15716              * to the intersection). Now we look for this object in each
15717              * one of the original tables, we get its parameters, and
15718              * copy them at the right position (i.e., same position as
15719              * in intersection table). The object will be the one closest
15720              * to the object position (column object_i) in the intersection
15721              * table. Note that we examine the same row, k, in all tables.
15722              */
15723 
15724             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
15725             start_w  = cpl_table_get_int   (origslits, start_i,  k, NULL);
15726             end_w    = cpl_table_get_int   (origslits, end_i,    k, NULL);
15727             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
15728 
15729             for (i = 0; i < nscience; i++) {
15730                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
15731                 int        minpos;
15732                 double     mindiff, diff;
15733             char      *object_o;
15734             char      *start_o;
15735             char      *end_o;
15736             char      *row_o;
15737 
15738                 for (m = 0; m < c_maxobjs; m++) {
15739                 object_o = cpl_sprintf("object_%d", m + 1);
15740                 start_o  = cpl_sprintf("start_%d",  m + 1);
15741                 end_o    = cpl_sprintf("end_%d",    m + 1);
15742                 row_o    = cpl_sprintf("row_%d",    m + 1);
15743 
15744                     if (!cpl_table_is_valid(work[i], object_o, k))
15745                         break;
15746 
15747                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
15748                     start_r  = cpl_table_get_int   (work[i], start_o,  k, NULL);
15749                     end_r    = cpl_table_get_int   (work[i], end_o,    k, NULL);
15750                     row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
15751 
15752                     diff = fabs(object_w - object_r);
15753                     if (m) {
15754                         if (mindiff > diff) {
15755                             mindiff = diff;
15756                             minpos = m;
15757                         }
15758                     }
15759                     else {
15760                         mindiff = diff;
15761                         minpos = 0;
15762                     }
15763 
15764                 cpl_free(object_o);
15765                 cpl_free(start_o);
15766                 cpl_free(end_o);
15767                 cpl_free(row_o);
15768                 }
15769 
15770                 object_o = cpl_sprintf("object_%d", minpos + 1);
15771                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
15772                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
15773                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
15774 
15775                 if (!cpl_table_has_column(slitss[i], object_i)) {
15776                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
15777                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
15778                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
15779                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
15780                 cpl_table_fill_invalid_double(slitss[i], object_i, -1);
15781                 cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
15782                 cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
15783                 cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
15784                 }
15785 
15786                 cpl_table_set_double(slitss[i], object_i, k,
15787                                      cpl_table_get_double(work[i], object_o, 
15788                                                           k, NULL));
15789                 cpl_table_set_int(slitss[i], start_i , k,
15790                                   cpl_table_get_int(work[i], start_o, k, NULL));
15791                 cpl_table_set_int(slitss[i], end_i , k,
15792                                   cpl_table_get_int(work[i], end_o, k, NULL));
15793                 cpl_table_set_int(slitss[i], row_i , k, row_w);
15794 
15795             cpl_free(object_o);
15796             cpl_free(start_o);
15797             cpl_free(end_o);
15798             cpl_free(row_o);
15799             }
15800 
15801         cpl_free(object_i);
15802         cpl_free(start_i);
15803         cpl_free(end_i);
15804         cpl_free(row_i);
15805         }
15806     }
15807 
15808     for (i = 0; i < nscience; i++)
15809         cpl_table_delete(work[i]);
15810 
15811     cpl_free(work);
15812 
15813 
15814     return cpl_error_get_code();
15815 }
15816 
15817 
15825 int mos_get_maxobjs_per_slit(cpl_table * slits)
15826 {
15827     int maxobjs = 1;
15828 
15829     char * colname = cpl_sprintf("object_%d", maxobjs);
15830     
15831     while (cpl_table_has_column(slits, colname)) {
15832         maxobjs++;
15833         cpl_free(colname);
15834         colname = cpl_sprintf("object_%d", maxobjs);
15835     }
15836     
15837     cpl_free(colname);
15838 
15839     maxobjs--;
15840 
15841     return maxobjs;
15842 }
15843 
15851 int mos_get_nobjects(cpl_table * slits)
15852 {
15853     int nobjs = 0;
15854 
15855     int nslits  = cpl_table_get_nrow(slits);
15856     int maxobjs = mos_get_maxobjs_per_slit(slits);
15857 
15858     int k, m;
15859 
15860     for (k = 0; k < nslits; k++) {
15861     for (m = 0; m < maxobjs; m++) {
15862         char * name = cpl_sprintf("object_%d", m + 1);
15863         int    null = !cpl_table_is_valid(slits, name, k);
15864 
15865         cpl_free(name);
15866 
15867         if (null)  break;
15868             else nobjs++;
15869     }
15870     }
15871 
15872     return nobjs;
15873 }
15874 
15882 int mos_check_slits(cpl_table * slits)
15883 {
15884 
15885     cpl_propertylist * sort;
15886 
15887     int nslits  = cpl_table_get_nrow(slits);
15888 
15889     int k, null;
15890 
15891 
15892     for (k = 0; k < nslits; k++) {
15893         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
15894     double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
15895 
15896     double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
15897     double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
15898 
15899     int nmiss = (int)((ytop - ybottom) / 90.0 + 0.5);
15900 
15901     if (nmiss > 1) {
15902         cpl_msg_warning(cpl_func, 
15903                 "Some slits could not be properly detected. "
15904                 "There might be accountable inaccuracies.");
15905         while (nmiss > 1) {
15906         cpl_table_set_size(slits, nslits + 1);
15907 
15908         /* Fill in new slit 'cut' */
15909 
15910         /* x coordinates be the same (acceptable approximation) */
15911         cpl_table_set_double(slits, "xtop",    nslits, xtop);
15912         cpl_table_set_double(slits, "xbottom", nslits, xbottom);
15913 
15914         /* Cut */
15915         if (k == 0) {
15916             cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
15917             cpl_table_set_double(slits, "ytop",    nslits, ybottom
15918                                                                    + 85.0);
15919             ybottom += 90.0;
15920             cpl_table_set_double(slits, "ybottom", k,      ybottom);
15921         } else {
15922             cpl_table_set_double(slits, "ytop",    nslits, ytop);
15923             cpl_table_set_double(slits, "ybottom", nslits, ytop 
15924                                                                    - 85.0);
15925             ytop -= 90.0;
15926             cpl_table_set_double(slits, "ytop",     k,     ytop);
15927         }
15928 
15929         nslits++; nmiss--;
15930         }
15931     }
15932     }
15933 
15934     sort = cpl_propertylist_new();
15935     cpl_propertylist_append_bool(sort, "ytop", 1);
15936     cpl_table_sort(slits, sort);
15937     cpl_propertylist_delete(sort);
15938 
15939     return 0;
15940 }
15941 
15964 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header)
15965 {
15966     int m, null;
15967     int halfsize;
15968 
15969     cpl_propertylist * sort;
15970     cpl_table        * slits; 
15971 
15972     slits    = mos_load_slits_fors_mos(header);
15973     halfsize = cpl_table_get_nrow(slits);
15974 
15975     cpl_table_set_size(slits, 2 * halfsize);
15976 
15977     for (m = 0; m < halfsize; m++) {
15978 
15979     double gap = 1.4;
15980 
15981     double length = 
15982         cpl_table_get(slits, "ytop",    m, &null) -
15983         cpl_table_get(slits, "ybottom", m, &null);
15984 
15985     if (m) {
15986         double interval = 
15987         cpl_table_get(slits, "ybottom", m - 1, &null) -
15988         cpl_table_get(slits, "ytop",    m,     &null);
15989 
15990         gap = (interval - length) / 2;
15991     }
15992 
15993     cpl_table_set(slits, "slit_id", m + halfsize,
15994               cpl_table_get(slits, "slit_id", m, &null) - 1);
15995 
15996     cpl_table_set(slits, "xtop",    m + halfsize,
15997               cpl_table_get(slits, "xtop",    m, &null));
15998 
15999     cpl_table_set(slits, "xbottom", m + halfsize,
16000               cpl_table_get(slits, "xbottom", m, &null));
16001 
16002     cpl_table_set(slits, "ytop",    m + halfsize, 
16003               cpl_table_get(slits, "ytop", m, &null) + gap + length);
16004 
16005     cpl_table_set(slits, "ybottom", m + halfsize,
16006               cpl_table_get(slits, "ytop", m, &null) + gap);
16007     }
16008 
16009     for (m = 0; m < 2 * halfsize; m++) {
16010     cpl_table_set(slits, "ytop",    m, 
16011               cpl_table_get(slits, "ytop",    m, &null) - 5.3);
16012 
16013     cpl_table_set(slits, "ybottom", m,
16014               cpl_table_get(slits, "ybottom", m, &null) - 5.3);
16015 
16016     }
16017 
16018     sort = cpl_propertylist_new();
16019     cpl_propertylist_append_bool(sort, "ytop", 1);
16020     cpl_table_sort(slits, sort);
16021 
16022     cpl_propertylist_delete(sort);
16023 
16024     return slits;
16025 }
16026 
16027 int * fors_get_nobjs_perslit(cpl_table * slits)
16028 {
16029     int nslits  = cpl_table_get_nrow(slits);
16030     int maxobjs = mos_get_maxobjs_per_slit(slits);
16031 
16032     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
16033 
16034     int k, m;
16035 
16036     for (k = 0; k < nslits; k++) {
16037     int nobjs = 0;
16038     for (m = 0; m < maxobjs; m++) {
16039         char * name = cpl_sprintf("object_%d", m + 1);
16040         int    null = !cpl_table_is_valid(slits, name, k);
16041 
16042         cpl_free(name);
16043 
16044         if (null)  break;
16045             else nobjs++;
16046     }
16047     
16048     nobjs_per_slit[k] = nobjs;
16049     }
16050 
16051     return nobjs_per_slit;
16052 }

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