moses.c

00001 /* $Id: moses.c,v 1.93 2011/03/03 15:28:24 cizzo Exp $
00002  *
00003  * This file is part of the MOSES library
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cizzo $
00023  * $Date: 2011/03/03 15:28:24 $
00024  * $Revision: 1.93 $
00025  * $Name: fors-4_8_6 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <math.h>
00037 #include <time.h>
00038 
00039 #include <fors_utils.h>
00040 #include <moses.h>
00041 
00042 /* Cheating: moving here the cpl_tools_get_median_float() prototype,
00043  * even if cpl_tool.h is not public. It should be removed as soon as 
00044  * an image median filtering with generic kernel will be implemented
00045  * in the CPL, or as soon as this module will be moved into the CPL. */
00046 
00047 float cpl_tools_get_median_float(float *, int);
00048 
00049 #define MAX_COLNAME      (80)
00050 #define STRETCH_FACTOR   (1.20)
00051 
00052 // Related to mos_identify_peaks(), used in multiplex mode
00053 
00054 static int mos_multiplex   = -1;
00055 static int mos_region_size = 800;
00056 
00057 static double default_lines_hi[] = {   /* Default sky line catalog */
00058                     5577.338,          /* for high res data        */
00059                     5889.953,
00060                     5895.923,
00061                     5915.301,
00062                     5932.862,
00063                     5953.420,
00064                     6257.961,
00065                     6287.434,
00066                     6300.304,
00067                     6306.869,
00068                     6363.780,
00069                     6498.729,
00070                     6533.044,
00071                     6553.617,
00072                     6841.945,
00073                     6863.955,
00074                     6870.994,
00075                     6889.288,
00076                     6900.833,
00077                     6912.623,
00078                     6923.220,
00079                     6939.521,
00080                     6969.930,
00081                     7003.858,
00082                     7244.907,
00083                     7276.405,
00084                     7284.439,
00085                     7316.282,
00086                     7329.148,
00087                     7340.885,
00088                     7358.659,
00089                     7571.746,
00090                     7750.640,
00091                     7759.996,
00092                     7794.112,
00093                     7808.467,
00094                     7821.503,
00095                     7841.266,
00096                     7913.708,
00097                     7949.204,
00098                     7964.650,
00099                     7993.332,
00100                     8014.059,
00101                     8310.719,
00102                     8344.602,
00103                     8382.392,
00104                     8399.170,
00105                     8415.231,
00106                     8430.174,
00107                     8452.250,
00108                     8493.389,
00109                     8791.186,
00110                     8827.096,
00111                     8885.850,
00112                     8903.114,
00113                     8943.395,
00114                     8988.366
00115                     };
00116 
00117 static double default_lines_lo[] = {   /* Default sky line catalog */
00118                     5577.338,          /* for low res data         */
00119                     6300.304,
00120                     6863.955,
00121                     7571.746,
00122                     7964.650,
00123                     7993.332
00124                     };
00125 
00126 
00136 /*
00137  * The following macros and function for finding the k-th smallest
00138  * value on a float array will be accessible from cpl_tools once
00139  * this module will be moved into the CPL.
00140  */
00141 
00142 /****
00143 
00144 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
00145 
00146 static float kthSmallest(float a[], int n, int k)
00147 {
00148   register int i,j,l,m;
00149   register float x;
00150 
00151   l = 0;
00152   m = n-1;
00153   while (l < m) {
00154     x = a[k];
00155     i = l;
00156     j = m;
00157     do {
00158       while (a[i] < x) {
00159         i++;
00160       }
00161       while (x < a[j]) {
00162         j--;
00163       }
00164       if (i <= j) {
00165         PIX_SWAP(a[i],a[j]);
00166         i++;
00167         j--;
00168       }
00169     } while (i <= j);
00170 
00171     if (j < k) {
00172       l = i;
00173     }
00174     if (k < i) {
00175       m = j;
00176     }
00177 
00178   }
00179   return(a[k]);
00180 }
00181 
00182 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1)))
00183 
00184 ****/
00185 
00186 /* 
00187  * Return random number with gaussian distribution (mean = 0, variance = 1)
00188  * (Box-Mueller method). The mos_randg() argument is either true or false, 
00189  * indicating whether to "seed" or not the sequence of generated random 
00190  * numbers. The "seeding" is performed just at the first mos_randg(1) call, 
00191  * and at further calls the input argument is ignored. This function
00192  * generates two random numbers at each call, returning the first one
00193  * at odd calls, and the second one at even calls.
00194  */
00195 
00196 static void mos_seed(void)
00197 {
00198     srand((unsigned int)time((time_t *)0));
00199 }
00200 
00201 static double mos_randg(int seme)
00202 {
00203     static int doit = 1;
00204     static int gotit = 1;
00205     double x1, x2, w, y1;
00206     static double y2;
00207 
00208     if (gotit && seme) {
00209         mos_seed();
00210         gotit = 0;
00211     }
00212 
00213     if (doit) {
00214         doit = 0;
00215         do {
00216             x1 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00217             x2 = 2.0 * (double)rand() / RAND_MAX - 1.0;
00218             w = x1 * x1 + x2 * x2;
00219         } while (w >= 1.0 || w == 0.0);
00220     
00221         w = sqrt( (-2.0 * log(w)) / w);
00222     
00223         y1 = x1 * w;
00224         y2 = x2 * w;
00225         return y1;
00226     }
00227 
00228     doit = 1;
00229     return y2;
00230 }
00231 
00232 /* 
00233  * This function contained a dependency on the VIMOS library
00234  * (function medianPixelvalue()): it should be removed as soon as an 
00235  * image median filtering with generic kernel will be implemented
00236  * in the CPL. Currently it has been solved by a direct call to
00237  * a cpl_tool function.
00238  */
00239 
00240 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in,
00241                                             int filtsizey, int refrow,
00242                                             int above, int below, int step)
00243 {
00244 
00245   const char *func = "mos_image_general_median_filter";
00246 
00247   cpl_image  *filt_img = NULL;
00248   int         col, row;
00249   float      *buf = NULL;
00250   float      *data;
00251   float      *fdata;
00252   int         upright_y, loleft_y;
00253   int         j;
00254   int         yIsEven = !(filtsizey - (filtsizey/2)*2);
00255   int         f2y;
00256   int         nx = cpl_image_get_size_x(ima_in);
00257   int         ny = cpl_image_get_size_y(ima_in);
00258   int         firstRow;
00259 
00260 
00261   if (yIsEven) filtsizey++;
00262 
00263   if (ny <= filtsizey) {
00264     cpl_msg_error(func, 
00265                   "Median filter size: %d, image size: %d", filtsizey, ny);
00266     return NULL;
00267   }
00268 
00269   f2y = filtsizey / 2;
00270 
00271   filt_img = cpl_image_duplicate(ima_in);
00272   buf = cpl_malloc(filtsizey * sizeof(float));
00273   data = cpl_image_get_data(ima_in);
00274   fdata = cpl_image_get_data(filt_img);
00275 
00276   firstRow = refrow - step * (below / step);
00277   if (firstRow < f2y)
00278     firstRow += step;
00279 
00280   for (col = 0; col < nx; col++) {
00281     for (row = firstRow; row < refrow + above; row += step) {
00282       if (row >= ny - f2y)
00283         break;
00284       loleft_y = row - f2y;
00285       upright_y = row + f2y + 1;
00286       for (j = loleft_y; j < upright_y; j++)
00287         buf[j - loleft_y] = data[col + j * nx];
00288 
00289       fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey);
00290     }
00291   }
00292 
00293   cpl_free(buf);
00294 
00295   return filt_img;
00296 
00297 }
00298 
00299 
00300 /*
00301  * The following static function is used to find an accurate position
00302  * of a peak within a short interval (however at least 5 pixels long). 
00303  * The basic idea is to find the baricenter of all the pixel values 
00304  * that pass a threshold level between the median value and the maximum
00305  * value within the examined interval (in case such levels are equal,
00306  * the input is considered flat and no position is returned). At least
00307  * minPoints must pass this threshold, or no position is computed. To
00308  * evaluate the significance of the computed baricenter, the variance 
00309  * of the contributing positions (relative to the found baricenter) is 
00310  * also evaluated, and compared with the expected variance for a uniform 
00311  * distribution of positions. If the observed variance is greater than 
00312  * 80% of the variance of the uniform distribution, the found position 
00313  * is rejected.
00314  */
00315 
00316 static int peakPosition(const float *data, int size, float *position,
00317                         int minPoints)
00318 {
00319   int    i;
00320   int    count = 0;
00321   float *copy;
00322   float  max, median, level, pos, variance, uniformVariance;
00323   double sum, weights;
00324 
00325 
00326   if (data == NULL)
00327       return 1;
00328 
00329   if (size < 5)         /* Hardcoded, I know... */
00330       return 1;
00331 
00332 
00333   /*
00334    *  Find median level
00335    */
00336 
00337   copy = (float *) cpl_malloc(size*sizeof(float));
00338   for (i = 0; i < size; i++)
00339       copy[i] = data[i];
00340   median = cpl_tools_get_median_float(copy, size);
00341   cpl_free(copy);
00342 
00343 
00344   /*
00345    *  Find max
00346    */
00347 
00348   max = data[0];
00349   for (i = 1; i < size; i++)
00350       if (data[i] > max)
00351           max = data[i];
00352 
00353 
00354   /*
00355    *  If the max equals the median we have a flat input, therefore
00356    *  no peak is found.
00357    */
00358 
00359   if (max-median < 0.00001)
00360       return 1;
00361 
00362 
00363   /*
00364    *  Discrimination level: only pixels with values above this
00365    *  level are considered in baricenter calculation.
00366    */
00367 
00368   level = (max + median) / 2;
00369 
00370 
00371   /*
00372    *  Of the values above this level compute the baricenter and
00373    *  then the variance of the positions used. Note that the weights
00374    *  are taken as the difference between the pixels values and
00375    *  the median level (supposedly the background).
00376    */
00377 
00378   count = 0;
00379   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00380       if (data[i] > level) {
00381           count++;
00382           weights += (data[i] - median);
00383           sum     += i * (data[i] - median);
00384       }
00385   }
00386 
00387 
00388   /*
00389    *  If too few values are above threshold, refuse the position
00390    *  as insignificant
00391    */
00392 
00393   if (count < minPoints)
00394       return 1;
00395 
00396   pos = sum / weights;
00397   for (i = 0, sum = 0., weights = 0.; i < size; i++) {
00398       if (data[i] > level) {
00399           weights++;
00400           sum += (i - pos) * (i - pos);
00401       }
00402   }
00403   variance = sqrt(sum / weights);
00404 
00405 
00406  /*
00407   *  The "uniform variance" is the variance that should be obtained
00408   *  in the case of uniform distribution of the points positions in
00409   *  the selected interval. If the real variance is comparable with
00410   *  this value, the peak is considered not found.
00411   */
00412 
00413   uniformVariance = sqrt(size*size/3 - pos*size + pos*pos);
00414 
00415   if (variance > 0.8 * uniformVariance)
00416       return 1;
00417 
00418   *position = pos + 0.5;
00419 
00420   return 0;
00421 }
00422 
00423 
00424 /*
00425  *  The following static function determines the quantity dx to be
00426  *  added to the position of the highest pixel of a fiber profile,
00427  *  to get the true position of the profile maximum. All is needed
00428  *  is the maximum observed value v2 in the profile, and the observed
00429  *  values v1 and v3 of the previous and the next pixels in the profile.
00430  *  
00431  *  The following ratio is defined:
00432  *  
00433  *      R = 0.5 (v3 - v1) / (2*v2 - v3 - v1)
00434  *      
00435  *  This is a conventional ratio that wouldn't diverge for any set of
00436  *  pixel values, and that would not depend on the presence of background
00437  *  (with the assumption that the background level is the same for the 
00438  *  three pixels). R has also been chosen in such a way that its value
00439  *  is already quite close to the real dx. It should be noted that the
00440  *  following condition should be fulfilled:
00441  *
00442  *           v1  <= v2   and   v3  <  v2
00443  *  or
00444  *           v1  <  v2   and   v3  <=  v2
00445  *
00446  *  This implies that dx varies between -0.5 and 0.5 pixels. In such
00447  *  boundary cases, one has:
00448  *
00449  *           v2 = v1   and   R = dx = -0.5
00450  *           v2 = v3   and   R = dx =  0.5
00451  *
00452  *  Another special case is when the observed pixel values are perfectly
00453  *  symmetrical:
00454  *
00455  *           v1 = v3   and   R = dx =  0.0
00456  *
00457  *  In all the intermediate cases the relation between R and dx depends
00458  *  on the shape of the fiber profile, that has been determined elsewhere.
00459  *  Using the accurate reconstruction of the fiber profile obtained by 
00460  *  the *  functions ifuProfile() and rebinProfile(), it can be shown 
00461  *  that R differs from dx always less than 0.01 pixels. If the condition
00462  *
00463  *           v1  <= v2   and   v3  <  v2
00464  *  or
00465  *           v1  <  v2   and   v3  <=  v2
00466  *
00467  *  is not fulfilled, then this function returns the value 2.0.
00468  */
00469 
00470 static double values_to_dx(double v1, double v2, double v3)
00471 {
00472 
00473   static double epsilon = 0.00000001;
00474   double        r       = 2.0;
00475 
00476 
00477   if (v1 > v2 || v3 > v2)
00478     return r;
00479 
00480   if (2 * v2 - v1 - v3 < epsilon)
00481     return r;
00482 
00483   r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1);
00484 
00485   return r;
00486 
00487 }
00488 
00489 
00490 /*
00491  * The following static function passes a min filter of given box
00492  * size on the data buffer. The box size must be a positive odd integer.
00493  */
00494 
00495 static float *min_filter(float *buffer, int length, int size)
00496 {
00497     float *minf  = cpl_calloc(length, sizeof(float));
00498     float  min;
00499     int    start = size / 2;
00500     int    end   = length - size / 2;
00501     int    i, j;
00502 
00503 
00504     for (i = start; i < end; i++) {
00505         min = buffer[i-start];
00506         for (j = i - start + 1; j <= i + start; j++)
00507             if (min > buffer[j])
00508                 min = buffer[j];
00509         minf[i] = min;
00510     }
00511 
00512     for (i = 0; i < start; i++)
00513         minf[i] = minf[start];
00514 
00515     for (i = end; i < length; i++)
00516         minf[i] = minf[end-1];
00517 
00518     return minf;
00519 }
00520 
00521 
00522 /*
00523  * The following static function passes a max filter of given box
00524  * size on the data buffer. The box size must be a positive odd integer.
00525  */
00526  
00527 static float *max_filter(float *buffer, int length, int size)
00528 {
00529     float *maxf  = cpl_calloc(length, sizeof(float));
00530     float  max;
00531     int    start = size / 2;
00532     int    end   = length - size / 2;
00533     int    i, j;
00534 
00535 
00536     for (i = start; i < end; i++) {
00537         max = buffer[i-start];
00538         for (j = i - start + 1; j <= i + start; j++)
00539             if (max < buffer[j])
00540                 max = buffer[j];
00541         maxf[i] = max;
00542     }
00543 
00544     for (i = 0; i < start; i++)
00545         maxf[i] = maxf[start];
00546 
00547     for (i = end; i < length; i++)
00548         maxf[i] = maxf[end-1];
00549 
00550     return maxf;
00551 }
00552 
00553 
00554 /*
00555  * The following static function passes a running average of given box
00556  * size on the data buffer. The box size must be a positive odd integer.
00557  */
00558  
00559 static float *smo_filter(float *buffer, int length, int size)
00560 {
00561     float *smof  = cpl_calloc(length, sizeof(float));
00562     double sum;
00563     int    start = size / 2;
00564     int    end   = length - size / 2;
00565     int    i, j;
00566 
00567 
00568     for (i = start; i < end; i++) {
00569         sum = 0.0;
00570         for (j = i - start; j <= i + start; j++)
00571             sum += buffer[j];
00572         smof[i] = sum / size;
00573     }
00574 
00575     for (i = 0; i < start; i++)
00576         smof[i] = smof[start];
00577 
00578     for (i = end; i < length; i++)
00579         smof[i] = smof[end-1];
00580 
00581     return smof;
00582 }
00583 
00584 /*
00585  * The following two static functions are used to read and write from the 
00586  * global distortion table the different model components. Conventionally
00587  * the table consists of 6 columns and 10 rows. Each row is just ordered 
00588  * storage for model coefficients, and these functions guarantee that the
00589  * coefficients are read in and written out correctly, independent on their
00590  * physical meaning. The first 6 table rows are a description of the IDS
00591  * coefficients, followed by a row containing only the used reference 
00592  * wavelength. The remaining 3 are a description of the spectral curvature.
00593  * The first row is a description of coefficient c0, the second of coefficient
00594  * c1, etc., of the IDS. The 8th row is a description of coefficient c0,
00595  * the 9th of coefficient c1, etc., of the spectral curvature. All are
00596  * bivariate polynomialx on x,y mask coordinates. If the input table
00597  * to the write routine is NULL, it is allocated and initialised. Also
00598  * the input polynomial could be NULL, and nothing would be written to 
00599  * the table. If both pointers are NULL the function is basically a
00600  * constructor of the global distortion table.
00601  */
00602 
00603 static cpl_polynomial *read_global_distortion(cpl_table *global, int row)
00604 {
00605     cpl_polynomial *poly = NULL;
00606     int             p[2];
00607     int             degree = 2;
00608     int             null;
00609     double          coeff;
00610 
00611     char   name[MAX_COLNAME];
00612 
00613 
00614     for (p[0] = 0; p[0] <= degree; p[0]++) {
00615         for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00616             snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00617             coeff = cpl_table_get_double(global, name, row, &null);
00618             if (null)
00619                 continue;
00620             if (poly == NULL)
00621                 poly = cpl_polynomial_new(2);
00622             cpl_polynomial_set_coeff(poly, p, coeff);
00623         }
00624     }
00625 
00626     return poly;
00627 }
00628 
00629 static cpl_table *write_global_distortion(cpl_table *global, int row, 
00630                                           cpl_polynomial *poly)
00631 {
00632     cpl_table *table;
00633     int        p[2];
00634     int        degree = 2;
00635     int        nrow = 10;
00636 
00637     char       name[MAX_COLNAME];
00638 
00639 
00640     if (global) {
00641         table = global;
00642     }
00643     else {
00644         table = cpl_table_new(nrow);
00645         for (p[0] = 0; p[0] <= degree; p[0]++) {
00646             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00647                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00648                 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE);
00649             }
00650         }
00651     }
00652 
00653     if (poly) {
00654         for (p[0] = 0; p[0] <= degree; p[0]++) {
00655             for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) {
00656                 snprintf(name, MAX_COLNAME, "a%d%d", p[0], p[1]);
00657                 cpl_table_set_double(table, name, row, 
00658                                      cpl_polynomial_get_coeff(poly, p));
00659             }
00660         }
00661     }
00662 
00663     return table;
00664 }
00665 
00666 
00667 /*
00668  * The following static function is performing a robust linear fit
00669  * (drawn from the VIMOS library, and originally from ESO-Eclipse).
00670  *
00671  *  ----> y = a + b * x
00672  *
00673  * This function return 0 on success.
00674  */
00675 
00676 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
00677 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 
00678                            double *abdev)
00679 {
00680     cpl_vector *vx;
00681     cpl_vector *vy;
00682     cpl_vector *va;
00683 
00684     double  aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum;
00685     double  sx, sy, sxy, sxx, chisq;
00686     double *arr;
00687     double  aa_ls, bb_ls;
00688     double *x;
00689     double *y;
00690     int     np;
00691     int     iter;
00692     int     max_iterate = 30;
00693     int     i;
00694 
00695 
00696     np = cpl_bivector_get_size(list);
00697     vx = cpl_bivector_get_x(list);
00698     vy = cpl_bivector_get_y(list);
00699     x = cpl_vector_get_data(vx);
00700     y = cpl_vector_get_data(vy);
00701 
00702     sx = sy = sxx = sxy = 0.00;
00703     for (i = 0; i < np; i++) {
00704         sx  += x[i];
00705         sy  += y[i];
00706         sxy += x[i] * y[i];
00707         sxx += x[i] * x[i];
00708     }
00709 
00710     del = np * sxx - sx * sx;
00711     aa_ls = aa = (sxx * sy - sx * sxy) / del;
00712     bb_ls = bb = (np * sxy - sx * sy) / del;
00713 
00714     chisq = 0.00;
00715     for (i = 0; i < np; i++) {
00716         temp = y[i] - (aa+bb*x[i]);
00717         temp *= temp;
00718         chisq += temp;
00719     }
00720 
00721     va = cpl_vector_new(np);
00722     arr = cpl_vector_get_data(va);
00723     sigb = sqrt(chisq/del);
00724     b1 = bb;
00725 
00726     bcomp = b1;
00727     sum = 0.00;
00728     for (i = 0; i < np; i++) {
00729         arr[i] = y[i] - bcomp * x[i];
00730     }
00731     aa = cpl_vector_get_median_const(va);
00732     abdevt = 0.0;
00733     for (i = 0; i < np; i++) {
00734         d = y[i] - (bcomp * x[i] + aa);
00735         abdevt += fabs(d);
00736         if (y[i] != 0.0) 
00737             d /= fabs(y[i]);
00738         if (fabs(d) > 1e-7) 
00739             sum += (d >= 0.0 ? x[i] : -x[i]);
00740     }
00741     f1 = sum;
00742 
00743     b2 = bb + SEGNO(3.0 * sigb, f1);
00744 
00745     bcomp = b2;
00746     sum = 0.00;
00747     for (i = 0; i < np; i++) {
00748         arr[i] = y[i] - bcomp * x[i];
00749     }
00750     aa = cpl_vector_get_median_const(va);
00751     abdevt = 0.0;
00752     for (i = 0; i < np; i++) {
00753         d = y[i] - (bcomp * x[i] + aa);
00754         abdevt += fabs(d);
00755         if (y[i] != 0.0) 
00756             d /= fabs(y[i]);
00757         if (fabs(d) > 1e-7) 
00758             sum += (d >= 0.0 ? x[i] : -x[i]);
00759     }
00760     f2 = sum;
00761 
00762     if (fabs(b2-b1)<1e-7) {
00763         *a = aa;
00764         *b = bb;
00765         *abdev = abdevt / (double)np;
00766         cpl_vector_delete(va);
00767         return 0;
00768     }
00769 
00770     iter = 0;
00771     while (f1*f2 > 0.0) {
00772         bb = 2.0*b2-b1;
00773         b1 = b2;
00774         f1 = f2;
00775         b2 = bb;
00776 
00777         bcomp = b2;
00778         sum = 0.00;
00779         for (i = 0; i < np; i++) {
00780             arr[i] = y[i] - bcomp * x[i];
00781         }
00782         aa = cpl_vector_get_median_const(va);
00783         abdevt = 0.0;
00784         for (i = 0; i < np; i++) {
00785             d = y[i] - (bcomp * x[i] + aa);
00786             abdevt += fabs(d);
00787             if (y[i] != 0.0) 
00788                 d /= fabs(y[i]);
00789             if (fabs(d) > 1e-7) 
00790                 sum += (d >= 0.0 ? x[i] : -x[i]);
00791         }
00792         f2 = sum;
00793         iter++;
00794         if (iter >= max_iterate) 
00795             break;
00796     }
00797     if (iter >= max_iterate) {
00798         *a = aa_ls;
00799         *b = bb_ls;
00800         *abdev = -1.0;
00801         cpl_vector_delete(va);
00802         return 1;
00803     }
00804 
00805     sigb = 0.01 * sigb;
00806     while (fabs(b2-b1) > sigb) {
00807         bb = 0.5 * (b1 + b2);
00808         if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 
00809             break;
00810         bcomp = bb;
00811         sum = 0.0;
00812         for (i = 0; i < np; i++) {
00813             arr[i] = y[i] - bcomp * x[i];
00814         }
00815         aa = cpl_vector_get_median_const(va);
00816         abdevt = 0.0;
00817         for (i = 0; i < np; i++) {
00818             d = y[i] - (bcomp * x[i] + aa);
00819             abdevt += fabs(d);
00820             if (y[i] != 0.0) 
00821                 d /= fabs(y[i]);
00822             if (fabs(d) > 1e-7) 
00823                 sum += (d >= 0.0 ? x[i] : -x[i]);
00824         }
00825         f = sum;
00826 
00827         if (f*f1 >= 0.0) {
00828             f1=f;
00829             b1=bb;
00830         } 
00831         else {
00832             f2=f;
00833             b2=bb;
00834         }
00835     }
00836     cpl_vector_delete(va);
00837     *a = aa;
00838     *b = bb;
00839     *abdev = abdevt / np;
00840     return 0;
00841 }
00842 #undef SEGNO
00843 
00844 /*      
00845  * The following static function applies the Hough transform from a table
00846  * of points to another table of points. Given the points p_i = (x_i,y_i)
00847  * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j)
00848  * and Y = y_i - X*x_i is computed and added to the output table for each
00849  * p_i, p_j pair. This means that if the input table has N points, the
00850  * output table has N*(N-1)/2 points.
00851  */
00852     
00853 /* static */
00854 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y)
00855 {
00856     cpl_table *output;
00857     double    *xdata;
00858     double    *ydata;
00859     double    *xodata;
00860     double    *yodata;
00861     int        npoints;
00862     int        opoints;
00863     int        i, j, k;
00864 
00865 
00866     npoints = cpl_table_get_nrow(table);
00867     opoints = npoints*(npoints-1)/2;
00868 
00869     output = cpl_table_new(opoints);
00870     cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE);
00871     cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE);
00872     cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0);
00873     cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0);
00874 
00875     xodata = cpl_table_get_data_double(output, "m");
00876     yodata = cpl_table_get_data_double(output, "q");
00877     
00878     cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE);
00879     cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE);
00880 
00881     xdata = cpl_table_get_data_double(table, "x");
00882     ydata = cpl_table_get_data_double(table, "y");
00883 
00884     k = 0;
00885     for (i = 0; i < npoints; i++) {
00886         for (j = i+1; j < npoints; j++) {
00887             xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]);
00888             yodata[k] = ydata[i] - xodata[k] * xdata[i];
00889             k++;
00890         }
00891     }
00892 
00893     if (k != opoints)
00894         printf("Assert k = %d, expected %d\n", k, opoints);
00895 
00896     cpl_table_erase_column(table, "x");
00897     cpl_table_erase_column(table, "y");
00898 
00899     return output;
00900 }
00901 
00902 
00903 /*
00904  * The following static function is performing the spectral
00905  * extraction for the function mos_extract_objects()
00906  */
00907 
00908 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 
00909                            cpl_image *extracted, cpl_image *sky, 
00910                            cpl_image *error, int nobjects, int extraction, 
00911                            double ron, double conad, int ncomb)
00912 {
00913 
00914   cpl_vector *vprofile;
00915   cpl_matrix *kernel;
00916   cpl_image  *smowin;
00917 
00918   int i, j;
00919   int specLen;
00920   int numRows;
00921   int index;
00922   int iter;
00923   int maxIter   = 2;         /* Not less than 2 !!! */
00924   int smoothBox = 31;        /* Not less than 5 !!! */
00925   double nsigma = 5.0;
00926 
00927   double sumWeight, sum, sumSky, sumProf, variance, weight;
00928   double *profile;
00929   double *buffer;
00930   float  *edata;
00931   float  *ekdata;
00932   float  *endata;
00933   float  *sdata;
00934   float  *kdata;
00935   float  *fdata;
00936 
00937   double value;
00938 
00939 
00940   specLen = cpl_image_get_size_x(sciwin);
00941   numRows = cpl_image_get_size_y(sciwin);
00942 
00943   edata = cpl_image_get_data(extracted);
00944   edata += nobjects * specLen;
00945 
00946   ekdata = cpl_image_get_data(sky);
00947   ekdata += nobjects * specLen;
00948 
00949   endata = cpl_image_get_data(error);
00950   endata += nobjects * specLen;
00951 
00952   sdata = cpl_image_get_data(sciwin);
00953   kdata = cpl_image_get_data(skywin);
00954 
00955 
00956   /*
00957    * Initial spectrum estimate
00958       if (sdata[i + j * specLen] > 0.0)
00959    */
00960 
00961   if (extraction && numRows > 5) {
00962       smowin = mos_image_filter_median(sciwin, 3, 3);
00963       fdata = cpl_image_get_data(smowin);
00964       for (i = 0; i < specLen; i++)
00965         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00966             edata[i] += fdata[i + j * specLen];
00967       cpl_image_delete(smowin);
00968   }
00969   else {
00970       for (i = 0; i < specLen; i++)
00971         for (j = 0, edata[i] = 0.0; j < numRows; j++)
00972             edata[i] += sdata[i + j * specLen];
00973   }
00974 
00975   if (extraction) {
00976 
00977     profile = cpl_calloc(specLen * numRows, sizeof(double));
00978     buffer  = cpl_calloc(specLen, sizeof(double));
00979 
00980     for (iter = 0; iter < maxIter; iter++) {
00981 
00982       /*
00983        * Normalised spatial profile
00984        */
00985 
00986       for (i = 0; i < specLen; i++) {
00987         for (j = 0; j < numRows; j++) {
00988           index = i + j * specLen;
00989 /*          if (sdata[index] > 0.0 && edata[i] > 0.00001)     */
00990           if (fabs(edata[i]) > 0.00001)
00991             profile[index] = sdata[index] / edata[i];
00992           else
00993             profile[index] = 0.0;
00994         }
00995       }
00996 
00997       for (j = 0; j < numRows; j++) {
00998 
00999         /*
01000          * Smooth each row in the dispersion direction, and enforce positivity
01001          */
01002 
01003         for (i = 0; i < specLen - smoothBox; i++) {
01004           vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen);
01005           value = cpl_vector_get_median_const(vprofile);
01006           cpl_vector_unwrap(vprofile);
01007           if (value < 0)
01008             value = 0.0;
01009           buffer[i + smoothBox / 2] = value;
01010         }
01011 
01012         /*
01013          * Replace the end portions (i.e., not median filtered) with a mean
01014          */
01015 
01016         vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen);
01017         value = cpl_vector_get_mean(vprofile);
01018         cpl_vector_unwrap(vprofile);
01019 
01020         if (value < 0)
01021             value = 0.0;
01022 
01023         for (i = 0; i < smoothBox / 2; i++)
01024           buffer[i] = value;
01025 
01026         vprofile = cpl_vector_wrap(smoothBox / 2, 
01027                                    profile + specLen - smoothBox/2 + j*specLen);
01028         value = cpl_vector_get_mean(vprofile);
01029         cpl_vector_unwrap(vprofile);
01030 
01031         if (value < 0)
01032             value = 0.0;
01033 
01034         for (i = 0; i < smoothBox / 2; i++)
01035           buffer[i + specLen - smoothBox / 2] = value;
01036 
01037         for (i = 0; i < specLen; i++)
01038           profile[i + j * specLen] = buffer[i];
01039 
01040       }
01041 
01042       /*
01043        * Enforce normalization of spatial profile after smoothing
01044        */
01045 
01046       for (i = 0; i < specLen; i++) {
01047         for (j = 0, value = 0.0; j < numRows; j++)
01048           value += profile[i + j * specLen];
01049         if (value > 0.00001)
01050           for (j = 0; j < numRows; j++)
01051             profile[i + j * specLen] /= value;
01052         else
01053           for (j = 0; j < numRows; j++)
01054             profile[i + j * specLen] = 0.0;
01055       }
01056 
01057 
01058       /*
01059        * Optimal extraction
01060        */
01061 
01062       for (i = 0; i < specLen; i++) {
01063         sum = 0.0;
01064         sumSky = 0.0;
01065         sumWeight = 0.0;
01066         sumProf = 0.0;
01067         for (j = 0; j < numRows; j++) {
01068           index = i + j * specLen;
01069 /*        
01070 if (sdata[index] > 0.0) {
01071 */
01072             variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index])
01073                      / conad;
01074             variance /= ncomb;  /* If input dataset is sum of ncomb images */
01075             value = sdata[index] - edata[i] * profile[index];
01076             if (fabs(value) / sqrt(variance) < nsigma) {
01077               weight = 1000000 * profile[index] / variance;
01078               sum += weight * sdata[index];
01079               sumSky += weight * kdata[index];
01080               sumWeight += weight * profile[index];
01081               sumProf += profile[index];
01082             }
01083 /*
01084 }      
01085 */
01086         }
01087 
01088         if (sumWeight > 0.00001) {
01089           edata[i] = sum / sumWeight;
01090           ekdata[i] = sumSky / sumWeight;
01091           endata[i] = 1000 * sqrt(sumProf / sumWeight);
01092         }
01093         else {
01094 /*
01095           edata[i] = 0.0;
01096           ekdata[i] = 0.0;
01097           endata[i] = 0.0;
01098 */
01099           endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01100         }
01101       }
01102     }
01103     cpl_free(profile);
01104     cpl_free(buffer);
01105   }
01106   else {
01107 
01108     /*
01109      * Add sky estimation for the simple aperture extraction.
01110         if (kdata[i + j * specLen] > 0.0)
01111      */
01112 
01113     for (i = 0; i < specLen; i++)
01114       for (j = 0, ekdata[i] = 0.0; j < numRows; j++)
01115           ekdata[i] += kdata[i + j * specLen];
01116 
01117     /*
01118      * Add error estimation for the simple aperture extraction.
01119      */
01120 
01121     for (i = 0; i < specLen; i++)
01122       endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad);
01123 
01124   }
01125 
01126 }
01127 
01128 
01175 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits,
01176                                  cpl_table *ids, cpl_table *crv, 
01177                                  double reference)
01178 {
01179     const char *func = "mos_global_distortion";
01180 
01181     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01182 
01183     cpl_table      *global = NULL;
01184     cpl_table      *coeff;
01185     cpl_table      *dummy;
01186     cpl_vector     *ci;
01187     cpl_vector     *xmask;
01188     cpl_vector     *ymask;
01189     cpl_bivector   *mask;
01190     cpl_vector     *xccd;
01191     cpl_vector     *yccd;
01192     cpl_bivector   *ccd;
01193     cpl_polynomial *poly;
01194     double         *xtop;
01195     double         *ytop;
01196     double         *xbottom;
01197     double         *ybottom;
01198     double         *mxtop;
01199     double         *mytop;
01200     double         *mxbottom;
01201     double         *mybottom;
01202     int            *position;
01203     int            *length;
01204     int            *slit_id;
01205     int            *mslit_id;
01206     int             nslits, nmaskslits, npoints;
01207     int             order;
01208     int             i, j;
01209     int             minslit = 6;    // 12;
01210 
01211 
01212 /* *+
01213 printf("error1: %s\n", cpl_error_get_message());
01214 +* */
01215     if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) {
01216         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01217         return NULL;
01218     }
01219 /* *+
01220 printf("error1a: %s\n", cpl_error_get_message());
01221 +* */
01222 
01223     nslits = cpl_table_get_nrow(slits);
01224 /* *+
01225 printf("error1b: %s\n", cpl_error_get_message());
01226 +* */
01227 
01228     if (nslits < minslit) {
01229         cpl_msg_warning(func, "Too few slits (%d < %d) for global "
01230                         "distortion model determination", nslits, minslit);
01231         return NULL;
01232     }
01233 /* *+
01234 printf("error1c: %s\n", cpl_error_get_message());
01235 +* */
01236 
01237     nmaskslits = cpl_table_get_nrow(maskslits);
01238 
01239     length   = cpl_table_get_data_int(slits, "length");
01240     position = cpl_table_get_data_int(slits, "position");
01241     slit_id  = cpl_table_get_data_int(slits, "slit_id");
01242     mslit_id = cpl_table_get_data_int(maskslits, "slit_id");
01243     xtop     = cpl_table_get_data_double(slits, "xtop");
01244     ytop     = cpl_table_get_data_double(slits, "ytop");
01245     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01246     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01247     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01248     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01249     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01250     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01251 
01252 
01253     /*
01254      * Global IDS
01255      */
01256 
01257     coeff = cpl_table_new(nslits);
01258     cpl_table_copy_structure(coeff, ids);
01259     cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE);
01260     cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE);
01261     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01262     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01263 
01264 /* *+
01265 printf("error2: %s\n", cpl_error_get_message());
01266 +* */
01267     for (i = 0; i < nslits; i++) {
01268         for (j = 0; j < nmaskslits; j++) {
01269             if (slit_id[i] == mslit_id[j]) {
01270                 cpl_table_set_double(coeff, "xmask", i,
01271                                      (mxtop[j] + mxbottom[j]) / 2);
01272                 cpl_table_set_double(coeff, "ymask", i,
01273                                      (mytop[j] + mybottom[j]) / 2);
01274             }
01275         }
01276     }
01277 
01278     if (cpl_table_has_invalid(coeff, "xmask")) {
01279         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01280         cpl_table_delete(coeff);
01281         return NULL;
01282     }
01283 
01284     for (i = 0; i < nslits; i++) {
01285         cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2);
01286         cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2);
01287     }
01288 
01289 /* *+
01290 printf("error3: %s\n", cpl_error_get_message());
01291 +* */
01292     for (i = 0; i < nslits; i++) {
01293 
01294         if (length[i] == 0)
01295             continue;
01296 
01297         cpl_table_and_selected_window(ids, position[i], length[i]);
01298         dummy = cpl_table_extract_selected(ids);
01299         for (j = 0; j < 6; j++) {
01300             if (cpl_table_has_column(dummy, clab[j])) {
01301                 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) {
01302                     cpl_table_set_double(coeff, clab[j], i, 
01303                          cpl_table_get_column_median(dummy, clab[j]));
01304                 }
01305             }
01306         }
01307 
01308         cpl_table_delete(dummy);
01309         cpl_table_select_all(ids);
01310             
01311     }
01312 
01313 /* *+
01314 printf("error4: %s\n", cpl_error_get_message());
01315 +* */
01316     for (j = 0; j < 6; j++) {
01317         if (cpl_table_has_column(coeff, clab[j])) {
01318             cpl_table_and_selected_invalid(coeff, clab[j]);
01319 
01320             if (cpl_table_not_selected(coeff))
01321                 dummy = cpl_table_extract_selected(coeff);
01322             else
01323                 break;
01324 
01325             npoints = cpl_table_get_nrow(dummy);
01326 
01327             if (npoints >= 6) {
01328 
01329                 if (npoints >= 12)
01330                     order = 2;
01331                 else
01332                     order = 1;
01333                    
01334                 ci = cpl_vector_wrap(npoints,
01335                                      cpl_table_get_data_double(dummy, clab[j]));
01336                 if (j) {
01337                     xccd = cpl_vector_wrap(npoints,
01338                                      cpl_table_get_data_double(dummy, "xccd"));
01339                     yccd = cpl_vector_wrap(npoints,
01340                                      cpl_table_get_data_double(dummy, "yccd"));
01341                     ccd = cpl_bivector_wrap_vectors(xccd, yccd);
01342 
01343 /* %%% */
01344                     poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL);
01345 
01346                     cpl_bivector_unwrap_vectors(ccd);
01347                     cpl_vector_unwrap(xccd);
01348                     cpl_vector_unwrap(yccd);
01349                     cpl_vector_unwrap(ci);
01350                 }
01351                 else {
01352                     xmask = cpl_vector_wrap(npoints,
01353                                      cpl_table_get_data_double(dummy, "xmask"));
01354                     ymask = cpl_vector_wrap(npoints,
01355                                      cpl_table_get_data_double(dummy, "ymask"));
01356                     mask = cpl_bivector_wrap_vectors(xmask, ymask);
01357 
01358 /* %%% */
01359                     poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01360 
01361                     cpl_bivector_unwrap_vectors(mask);
01362                     cpl_vector_unwrap(xmask);
01363                     cpl_vector_unwrap(ymask);
01364                     cpl_vector_unwrap(ci);
01365                 }
01366             }
01367             else {
01368                 int p[2] = {0, 0};
01369                 poly = cpl_polynomial_new(2);
01370                 cpl_polynomial_set_coeff(poly, p, 
01371                                cpl_table_get_column_median(dummy, clab[j]));
01372             }
01373 
01374             cpl_table_delete(dummy);
01375 
01376             global = write_global_distortion(global, j, poly);
01377 
01378             cpl_polynomial_delete(poly);
01379 
01380             cpl_table_select_all(coeff);
01381         }
01382     }
01383 
01384 /* *+
01385 printf("error5: %s\n", cpl_error_get_message());
01386 +* */
01387     cpl_table_delete(coeff);
01388 /* *+
01389 printf("error6: %s\n", cpl_error_get_message());
01390 +* */
01391 
01392 
01393     /*
01394      * Add model's reference wavelength
01395      */
01396 
01397     cpl_table_set_double(global, "a00", 6, reference);
01398 
01399 
01400     /*
01401      * Global curvature model
01402      */
01403 
01404     coeff = cpl_table_duplicate(crv);
01405     cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE);
01406     cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE);
01407     slit_id = cpl_table_get_data_int(coeff, "slit_id");
01408     npoints = cpl_table_get_nrow(coeff);
01409 
01410 /* *+
01411 printf("error7: %s\n", cpl_error_get_message());
01412 +* */
01413     for (i = 0; i < npoints; i++) {
01414         for (j = 0; j < nmaskslits; j++) {
01415             if (slit_id[i] == mslit_id[j]) {
01416                 if (i%2) {
01417                     cpl_table_set_double(coeff, "xmask", i, mxbottom[j]);
01418                     cpl_table_set_double(coeff, "ymask", i, mybottom[j]);
01419                 }
01420                 else {
01421                     cpl_table_set_double(coeff, "xmask", i, mxtop[j]);
01422                     cpl_table_set_double(coeff, "ymask", i, mytop[j]);
01423                 }
01424             }
01425         }
01426     }
01427 
01428 /* *+
01429 printf("error8: %s\n", cpl_error_get_message());
01430 +* */
01431     if (cpl_table_has_invalid(coeff, "xmask")) {
01432         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
01433         cpl_table_delete(coeff);
01434         return NULL;
01435     }
01436 
01437 /* *+
01438 printf("error9: %s\n", cpl_error_get_message());
01439 +* */
01440     for (j = 0; j < 3; j++) {
01441         if (cpl_table_has_column(coeff, clab[j])) {
01442             cpl_table_and_selected_invalid(coeff, clab[j]);
01443 
01444             if (cpl_table_not_selected(coeff))
01445                 dummy = cpl_table_extract_selected(coeff);
01446             else
01447                 break;
01448 
01449             npoints = cpl_table_get_nrow(dummy);
01450 
01451             if (npoints >= 6) {
01452 
01453                 if (npoints >= 12)
01454                     order = 2;
01455                 else
01456                     order = 1;
01457 
01458                 ci = cpl_vector_wrap(npoints,
01459                                      cpl_table_get_data_double(dummy, clab[j]));
01460                 xmask = cpl_vector_wrap(npoints,
01461                                      cpl_table_get_data_double(dummy, "xmask"));
01462                 ymask = cpl_vector_wrap(npoints,
01463                                      cpl_table_get_data_double(dummy, "ymask"));
01464                 mask = cpl_bivector_wrap_vectors(xmask, ymask);
01465 
01466                 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL);
01467 
01468                 cpl_bivector_unwrap_vectors(mask);
01469                 cpl_vector_unwrap(ci);
01470                 cpl_vector_unwrap(xmask);
01471                 cpl_vector_unwrap(ymask);
01472             }
01473             else {
01474                 int p[2] = {0, 0};
01475                 poly = cpl_polynomial_new(2);
01476                 cpl_polynomial_set_coeff(poly, p,
01477                                cpl_table_get_column_median(dummy, clab[j]));
01478             }
01479 
01480             cpl_table_delete(dummy);
01481 
01482             global = write_global_distortion(global, j + 7, poly);
01483 
01484             cpl_polynomial_delete(poly);
01485             cpl_table_select_all(coeff);
01486         }
01487     }
01488 
01489 /* *+
01490 printf("error10: %s\n", cpl_error_get_message());
01491 +* */
01492     cpl_table_delete(coeff);
01493 /* *+
01494 printf("error11: %s\n", cpl_error_get_message());
01495 +* */
01496 
01497     return global;
01498 
01499 }
01500 
01501 
01539 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits,
01540                                    int ysize)
01541 {
01542     const char *func = "mos_build_slit_location";
01543 
01544     cpl_propertylist *sort_col;
01545     cpl_polynomial   *ids0;
01546     cpl_polynomial   *crv[3];
01547     cpl_polynomial   *loc_crv;
01548     cpl_vector       *point;
01549     cpl_table        *slits;
01550     int               nslits;
01551     int              *slit_id;
01552     double           *dpoint;
01553     double           *xtop;
01554     double           *ytop;
01555     double           *xbottom;
01556     double           *ybottom;
01557     double           *mxtop;
01558     double           *mytop;
01559     double           *mxbottom;
01560     double           *mybottom;
01561     int               i, j;
01562 
01563 
01564     if (global == NULL || maskslits == NULL) {
01565         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01566         return NULL;
01567     }
01568 
01569     nslits   = cpl_table_get_nrow(maskslits);
01570     slit_id  = cpl_table_get_data_int(maskslits, "slit_id");
01571     mxtop    = cpl_table_get_data_double(maskslits, "xtop");
01572     mytop    = cpl_table_get_data_double(maskslits, "ytop");
01573     mxbottom = cpl_table_get_data_double(maskslits, "xbottom");
01574     mybottom = cpl_table_get_data_double(maskslits, "ybottom");
01575 
01576     slits = cpl_table_duplicate(maskslits);
01577 
01578     xtop    = cpl_table_get_data_double(slits, "xtop");
01579     ytop    = cpl_table_get_data_double(slits, "ytop");
01580     xbottom = cpl_table_get_data_double(slits, "xbottom");
01581     ybottom = cpl_table_get_data_double(slits, "ybottom");
01582 
01583     ids0 = read_global_distortion(global, 0);
01584     crv[0] = read_global_distortion(global, 7);
01585     crv[1] = read_global_distortion(global, 8);
01586     crv[2] = read_global_distortion(global, 9);
01587 
01588     loc_crv = cpl_polynomial_new(1);
01589 
01590     point = cpl_vector_new(2);
01591     dpoint = cpl_vector_get_data(point);
01592 
01593     for (i = 0; i < nslits; i++) {
01594         dpoint[0] = mxtop[i];
01595         dpoint[1] = mytop[i];
01596 
01597         xtop[i] = cpl_polynomial_eval(ids0, point);
01598 
01599         for (j = 0; j < 3; j++)
01600             if (crv[j])
01601                 cpl_polynomial_set_coeff(loc_crv, &j, 
01602                                          cpl_polynomial_eval(crv[j], point));
01603 
01604         ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL);
01605 
01606         dpoint[0] = mxbottom[i];
01607         dpoint[1] = mybottom[i];
01608         xbottom[i] = cpl_polynomial_eval(ids0, point);
01609 
01610         for (j = 0; j < 3; j++)
01611             if (crv[j])
01612                 cpl_polynomial_set_coeff(loc_crv, &j,
01613                                          cpl_polynomial_eval(crv[j], point));
01614 
01615         ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL);
01616     }
01617 
01618     cpl_vector_delete(point);
01619     cpl_polynomial_delete(ids0);
01620     cpl_polynomial_delete(loc_crv);
01621     for (j = 0; j < 3; j++)
01622         cpl_polynomial_delete(crv[j]);
01623 
01624     sort_col = cpl_propertylist_new();
01625     cpl_propertylist_append_bool(sort_col, "ytop", 1);
01626     cpl_table_sort(slits, sort_col);
01627     cpl_table_sort(maskslits, sort_col);
01628     cpl_propertylist_delete(sort_col);
01629 
01630     /*
01631      * Eliminate slits which are _entirely_ outside the CCD
01632      */
01633 
01634     cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1);
01635     cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0);
01636     cpl_table_erase_selected(slits);
01637 
01638     nslits = cpl_table_get_nrow(slits);
01639 
01640     if (nslits == 0) {
01641         cpl_msg_warning(func, "No slits found on the CCD");
01642         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01643         cpl_table_delete(slits);
01644         return NULL;
01645     }
01646 
01647     if (nslits > 1)
01648         cpl_msg_info(func, "Slit location: %d slits are entirely or partially "
01649                      "contained in CCD", nslits);
01650     else
01651         cpl_msg_info(func, "Slit location: %d slit is entirely or partially "
01652                      "contained in CCD", nslits);
01653 
01654     return slits;
01655 
01656 }
01657 
01658 
01685 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits,
01686                                 cpl_table *slits)
01687 {
01688     const char *func = "mos_build_curv_coeff";
01689 
01690     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01691                                                  /* Max order is 5 */
01692 
01693     cpl_polynomial *crv[3];
01694     cpl_vector     *point;
01695     cpl_table      *polytraces;
01696     double         *dpoint;
01697     double         *xtop;
01698     double         *ytop;
01699     double         *xbottom;
01700     double         *ybottom;
01701     int            *slit_id;
01702     int            *valid_id;
01703     int             nslits, nvalid;
01704     int             found;
01705     int             i, j, k;
01706 
01707 
01708     if (global == NULL || slits == NULL || maskslits == NULL) {
01709         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01710         return NULL;
01711     }
01712 
01713     nslits  = cpl_table_get_nrow(maskslits);
01714     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
01715     xtop    = cpl_table_get_data_double(maskslits, "xtop");
01716     ytop    = cpl_table_get_data_double(maskslits, "ytop");
01717     xbottom = cpl_table_get_data_double(maskslits, "xbottom");
01718     ybottom = cpl_table_get_data_double(maskslits, "ybottom");
01719 
01720     polytraces = cpl_table_new(2*nslits);
01721     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
01722     for (i = 0; i < 3; i++)
01723         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
01724 
01725     crv[0] = read_global_distortion(global, 7);
01726     crv[1] = read_global_distortion(global, 8);
01727     crv[2] = read_global_distortion(global, 9);
01728 
01729     point = cpl_vector_new(2);
01730     dpoint = cpl_vector_get_data(point);
01731 
01732     for (i = 0; i < nslits; i++) {
01733         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
01734 
01735             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
01736 
01737             if (j) {
01738                 dpoint[0] = xbottom[i];
01739                 dpoint[1] = ybottom[i];                
01740             }
01741             else {
01742                 dpoint[0] = xtop[i];
01743                 dpoint[1] = ytop[i];                
01744             }
01745 
01746             for (k = 0; k < 3; k++)
01747                 if (crv[j])
01748                     cpl_table_set_double(polytraces, clab[k], 2*i+j,
01749                                          cpl_polynomial_eval(crv[k], point));
01750         }
01751     }
01752 
01753     cpl_vector_delete(point);
01754     for (j = 0; j < 3; j++)
01755         cpl_polynomial_delete(crv[j]);
01756 
01757     /*
01758      * Eliminate slits which are _entirely_ outside the CCD
01759      */
01760  
01761     nvalid  = cpl_table_get_nrow(slits);
01762     valid_id = cpl_table_get_data_int(slits, "slit_id");
01763     cpl_table_unselect_all(polytraces);
01764     for (i = 0; i < nslits; i++) {
01765         found = 0;
01766         for (j = 0; j < nvalid; j++) {
01767             if (slit_id[i] == valid_id[j]) {
01768                 found = 1;
01769                 break;
01770             }
01771         }
01772         if (!found) {
01773             cpl_table_select_row(polytraces, 2*i);
01774             cpl_table_select_row(polytraces, 2*i + 1);
01775         }
01776     }
01777     cpl_table_erase_selected(polytraces);
01778  
01779     nslits = cpl_table_get_nrow(polytraces);
01780 
01781     if (nslits == 0) {
01782         cpl_msg_warning(func, "No slits found on the CCD");
01783         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01784         cpl_table_delete(polytraces);
01785         return NULL;
01786     }
01787 
01788     if (nslits > 2) 
01789         cpl_msg_info(func, "Curvature model: %d slits are entirely or "
01790                      "partially contained in CCD", nslits / 2);
01791     else
01792         cpl_msg_info(func, "Curvature model: %d slit is entirely or "
01793                      "partially contained in CCD", nslits / 2);
01794 
01795     return polytraces;
01796 }
01797 
01798 
01840 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits)
01841 {
01842     const char *func = "mos_build_disp_coeff";
01843 
01844     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01845 
01846     cpl_polynomial *ids[6];
01847     cpl_vector     *point;
01848     cpl_table      *idscoeff;
01849     double         *dpoint;
01850     double         *xtop;
01851     double         *ytop;
01852     double         *xbottom;
01853     double         *ybottom;
01854     int            *position;
01855     int            *length;
01856     int             nslits;
01857     int             nrows;
01858     int             order;
01859     int             ylow, yhig;
01860     int             i, j, k;
01861 
01862 
01863     if (global == NULL || slits == NULL) {
01864         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
01865         return NULL;
01866     }
01867     
01868     nslits   = cpl_table_get_nrow(slits);
01869     position = cpl_table_get_data_int(slits, "position");
01870     length   = cpl_table_get_data_int(slits, "length");
01871     xtop     = cpl_table_get_data_double(slits, "xtop");
01872     ytop     = cpl_table_get_data_double(slits, "ytop");
01873     xbottom  = cpl_table_get_data_double(slits, "xbottom");
01874     ybottom  = cpl_table_get_data_double(slits, "ybottom");
01875 
01876     for (i = 0; i < 6; i++)
01877         ids[i] = read_global_distortion(global, i);
01878 
01879     for (i = 0; i < 6; i++)
01880         if (ids[i] == NULL)
01881             break;
01882 
01883     order = i - 1;
01884 
01885     if (order < 1) {
01886         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
01887         return NULL;
01888     }
01889 
01890     nrows = 0;
01891     for (i = 0; i < nslits; i++)
01892         nrows += length[i]; 
01893 
01894     idscoeff = cpl_table_new(nrows);
01895 
01896     for (j = 0; j <= order; j++)
01897         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
01898 
01899     cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE);
01900     cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0);
01901     cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT);
01902     cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0);
01903 
01904     point = cpl_vector_new(2);
01905     dpoint = cpl_vector_get_data(point);
01906 
01907     for (i = 0; i < nslits; i++) {
01908 
01909         if (length[i] == 0)
01910             continue;
01911 
01912         ylow = position[i];
01913         yhig = ylow + length[i];
01914 
01915         for (j = 0; j <= order; j++) {
01916             if (j) {
01917                 for (k = 0; k < length[i]; k++) {
01918                     dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i];
01919                     dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i];
01920                     cpl_table_set_double(idscoeff, clab[j], ylow + k,
01921                                          cpl_polynomial_eval(ids[j], point));
01922                 }
01923             }
01924             else {
01925                 for (k = 0; k < length[i]; k++) {
01926                     cpl_table_set_double(idscoeff, clab[0], ylow + k,
01927                                 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]);
01928                 }
01929             }
01930         }
01931     }
01932 
01933     cpl_vector_delete(point);
01934     for (j = 0; j < 6; j++)
01935         cpl_polynomial_delete(ids[j]);
01936 
01937     return idscoeff;
01938 
01939 }
01940 
01941 
01964 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 
01965                             cpl_table *polytraces, double reference, 
01966                             double blue, double red, double dispersion)
01967 {
01968     const char     *func = "mos_subtract_sky";
01969 
01970     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
01971                                                  /* Max order is 5 */
01972 
01973     cpl_image      *sky;
01974     cpl_bivector   *list;
01975     cpl_vector     *listx;
01976     cpl_vector     *listy;
01977     cpl_polynomial *polytop;
01978     cpl_polynomial *polybot;
01979     cpl_polynomial *trend;
01980 
01981     int            *slit_id;
01982     double         *dlistx;
01983     double         *dlisty;
01984     float          *sdata;
01985     float          *kdata;
01986     double          top, bot;
01987     int             itop, ibot;
01988     double          coeff;
01989     double          ytop, ybot;
01990     double          m, q, err;
01991     int             npix;
01992 
01993     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
01994     int             nx, ny;
01995     int             nslits;
01996     int            *length;
01997     int             missing_top, missing_bot;
01998     int             order;
01999     int             null;
02000     int             window = 50;  /* Longer slits have polynomial sky model */
02001     int             count;
02002     int             i, j, k;
02003 
02004 
02005     if (science == NULL || slits == NULL || polytraces == NULL) {
02006         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02007         return NULL;
02008     }
02009  
02010     if (dispersion <= 0.0) {
02011         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02012         return NULL;
02013     }
02014 
02015     if (red - blue < dispersion) {
02016         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02017         return NULL;
02018     }
02019 
02020     nx = cpl_image_get_size_x(science);
02021     ny = cpl_image_get_size_y(science);
02022 
02023     sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
02024 
02025     sdata = cpl_image_get_data(science);
02026     kdata = cpl_image_get_data(sky);
02027 
02028     nslits   = cpl_table_get_nrow(slits);
02029     order    = cpl_table_get_ncol(polytraces) - 2;
02030     length   = cpl_table_get_data_int(slits, "length");
02031     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02032 
02033     /*
02034      * The spatial resampling is performed for a certain number of
02035      * pixels above and below the position of the reference wavelength:
02036      */
02037     
02038     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02039     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02040 
02041     for (i = 0; i < nslits; i++) {
02042 
02043         if (length[i] == 0)
02044             continue;
02045 
02046         
02047         /*
02048          * Recover from the table of spectral curvature coefficients
02049          * the curvature polynomials.
02050          */
02051 
02052         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02053 
02054         start_pixel = refpixel - pixel_below;
02055         if (start_pixel < 0)
02056             start_pixel = 0;
02057 
02058         end_pixel = refpixel + pixel_above;
02059         if (end_pixel > nx)
02060             end_pixel = nx;
02061 
02062         missing_top = 0;
02063         polytop = cpl_polynomial_new(1);
02064         for (k = 0; k <= order; k++) {
02065             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02066             if (null) {
02067                 cpl_polynomial_delete(polytop);
02068                 missing_top = 1;
02069                 break;
02070             }
02071             cpl_polynomial_set_coeff(polytop, &k, coeff);
02072         }
02073 
02074         missing_bot = 0;
02075         polybot = cpl_polynomial_new(1);
02076         for (k = 0; k <= order; k++) {
02077             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02078             if (null) {
02079                 cpl_polynomial_delete(polybot);
02080                 missing_bot = 1;
02081                 break;
02082             }
02083             cpl_polynomial_set_coeff(polybot, &k, coeff);
02084         }
02085 
02086         if (missing_top && missing_bot) {
02087             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02088                           slit_id[i]);
02089             continue;
02090         }
02091 
02092         /*
02093          * In case just one of the two edges was not traced, the other
02094          * edge curvature model is duplicated and shifted to the other
02095          * end of the slit: better than nothing!
02096          */
02097 
02098         if (missing_top) {
02099             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02100                           "the spectral curvature of the lower edge "
02101                           "is used instead.", slit_id[i]);
02102             polytop = cpl_polynomial_duplicate(polybot);
02103             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02104             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02105             k = 0;
02106             coeff = cpl_polynomial_get_coeff(polybot, &k);
02107             coeff += ytop - ybot;
02108             cpl_polynomial_set_coeff(polytop, &k, coeff);
02109         }
02110 
02111         if (missing_bot) {
02112             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02113                           "the spectral curvature of the upper edge "
02114                           "is used instead.", slit_id[i]);
02115             polybot = cpl_polynomial_duplicate(polytop);
02116             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02117             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02118             k = 0;
02119             coeff = cpl_polynomial_get_coeff(polytop, &k);
02120             coeff -= ytop - ybot;
02121             cpl_polynomial_set_coeff(polybot, &k, coeff);
02122         }
02123 
02124 
02125         /*
02126          * Now read pixel values along spatial direction, and fit them.
02127          */
02128 
02129         for (j = start_pixel; j < end_pixel; j++) {
02130             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02131             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02132             itop = floor(top + 0.5) + 1;
02133             ibot = floor(bot + 0.5);
02134             if (itop > ny)
02135                 itop = ny;
02136             if (ibot < 0)
02137                 ibot = 0;
02138             npix = itop - ibot;
02139             if (npix < 5)
02140                 break;
02141 
02142             list = cpl_bivector_new(npix);
02143             listx = cpl_bivector_get_x(list);
02144             listy = cpl_bivector_get_y(list);
02145             dlistx = cpl_vector_get_data(listx);
02146             dlisty = cpl_vector_get_data(listy);
02147 
02148             for (k = 0; k < npix; k++) {
02149                 dlistx[k] = k;
02150                 dlisty[k] = sdata[j + (ibot + k)*nx];
02151             }
02152 
02153             if (robustLinearFit(list, &q, &m, &err)) {
02154                 cpl_bivector_delete(list);
02155                 continue;
02156             }
02157 
02158             cpl_bivector_delete(list);
02159 
02160             for (k = 0; k < npix; k++) {
02161                 kdata[j + (ibot + k)*nx] = m*k + q;
02162             }
02163 
02164             if (npix > window) {
02165 
02166                 /*
02167                  * Polynomial iteration
02168                  */
02169 
02170                 err = 3*sqrt(err);
02171 
02172                 count = 0;
02173                 for (k = 0; k < npix; k++)
02174                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err)
02175                         count++;
02176 
02177                 if (count < 10)
02178                     continue;
02179 
02180                 list = cpl_bivector_new(count);
02181                 listx = cpl_bivector_get_x(list);
02182                 listy = cpl_bivector_get_y(list);
02183                 dlistx = cpl_vector_get_data(listx);
02184                 dlisty = cpl_vector_get_data(listy);
02185 
02186                 count = 0;
02187                 for (k = 0; k < npix; k++) {
02188                     if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) {
02189                         dlistx[count] = k;
02190                         dlisty[count] = sdata[j + (ibot + k)*nx];
02191                         count++;
02192                     }
02193                 }
02194 
02195                 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err);
02196  
02197                 cpl_bivector_delete(list);
02198 
02199                 err = 3*sqrt(err);
02200 
02201                 count = 0;
02202                 for (k = 0; k < npix; k++)
02203                     if (fabs(sdata[j + (ibot + k)*nx] 
02204                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err)
02205                         count++;
02206 
02207                 if (count < 10) {
02208                     cpl_polynomial_delete(trend);
02209                     continue;
02210                 }
02211 
02212                 list = cpl_bivector_new(count);
02213                 listx = cpl_bivector_get_x(list);
02214                 listy = cpl_bivector_get_y(list);
02215                 dlistx = cpl_vector_get_data(listx);
02216                 dlisty = cpl_vector_get_data(listy);
02217 
02218                 count = 0;
02219                 for (k = 0; k < npix; k++) {
02220                     if (fabs(sdata[j + (ibot + k)*nx] 
02221                              - cpl_polynomial_eval_1d(trend, k, NULL)) < err) {
02222                         dlistx[count] = k;
02223                         dlisty[count] = sdata[j + (ibot + k)*nx];
02224                         count++;
02225                     }
02226                 }
02227 
02228                 cpl_polynomial_delete(trend);
02229 
02230                 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err);
02231 
02232                 cpl_bivector_delete(list);
02233  
02234                 for (k = 0; k < npix; k++) {
02235                     kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 
02236                                                k, NULL);
02237                 }
02238 
02239                 cpl_polynomial_delete(trend);
02240             }
02241         }
02242         cpl_polynomial_delete(polytop);
02243         cpl_polynomial_delete(polybot);
02244     }
02245 
02246     cpl_image_subtract(science, sky);
02247 
02248     return sky;
02249 }
02250 
02251 
02284 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 
02285                               cpl_table *slits, cpl_table *polytraces, 
02286                               double reference, double blue, double red, 
02287                               double dispersion, int sradius, int polyorder)
02288 {
02289     const char     *func = "mos_normalise_flat";
02290 
02291     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02292                                                  /* Max order is 5 */
02293 
02294     cpl_image      *rectified;
02295     cpl_image      *smo_flat;
02296     cpl_image      *exslit;
02297     cpl_vector     *positions;
02298     cpl_vector     *flux;
02299     cpl_vector     *smo_flux;
02300     cpl_polynomial *trend;
02301     cpl_polynomial *polytop;
02302     cpl_polynomial *polybot;
02303 
02304     int            *slit_id;
02305     float          *p;
02306     float          *data;
02307     double         *fdata;
02308     double         *pdata;
02309     float          *sdata;
02310     float          *xdata;
02311     float          *wdata;
02312     double          vtop, vbot, value;
02313     double          top, bot;
02314     double          coeff;
02315     double          ytop, ybot;
02316     double          ypos;
02317     double          fvalue;
02318     int             ivalue;
02319     int             yint, yprev;
02320     int             npseudo;
02321 
02322     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
02323     int             nx, ny, nsubx, nsuby;
02324     int             xlow, ylow, xhig, yhig;
02325     int             nslits;
02326     int            *position;
02327     int            *length;
02328     int             missing_top, missing_bot;
02329     int             order;
02330     int             npoints;
02331     int             uradius;
02332     int             null;
02333     int             i, j, k;
02334 
02335 /*    int             exclude = 5;     Number of excluded pixels at edges */
02336 
02337     /* For debug puposes only: cpl_image      *smo_rectified; */
02338 
02339 
02340     if (flat == NULL || slits == NULL || polytraces == NULL) {
02341         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02342         return NULL;
02343     }
02344  
02345     if (dispersion <= 0.0) {
02346         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02347         return NULL;
02348     }
02349 
02350     if (red - blue < dispersion) {
02351         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02352         return NULL;
02353     }
02354 
02355     rectified = mos_spatial_calibration(flat, slits, polytraces, reference,
02356                                         blue, red, dispersion, 0, NULL);
02357 
02358     nx = cpl_image_get_size_x(rectified);
02359     ny = cpl_image_get_size_y(rectified);
02360 
02361     smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 
02362                              cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT);
02363     wdata = cpl_image_get_data(smo_flat);
02364 
02365     nslits   = cpl_table_get_nrow(slits);
02366     order    = cpl_table_get_ncol(polytraces) - 2;
02367     position = cpl_table_get_data_int(slits, "position");
02368     length   = cpl_table_get_data_int(slits, "length");
02369     slit_id  = cpl_table_get_data_int(slits, "slit_id");
02370 
02371     /*
02372      * The spatial resampling is performed for a certain number of
02373      * pixels above and below the position of the reference wavelength:
02374      */
02375     
02376     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
02377     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
02378 
02379     xlow = 1;
02380     xhig = nx;
02381     for (i = 0; i < nslits; i++) {
02382 
02383         if (length[i] == 0)
02384             continue;
02385 
02386         /*
02387          * We DON'T write:
02388          *
02389          * ylow = position[i];
02390          * yhig = ylow + length[i];
02391          *
02392          * because the cpl_image pixels are counted from 1, and because in 
02393          * cpl_image_extract() the coordinates of the last pixel are inclusive.
02394          */
02395 
02396         ylow = position[i] + 1;
02397         yhig = ylow + length[i] - 1;
02398 
02399         exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig);
02400 
02401         if (polyorder < 0) {
02402 
02403             cpl_image_turn(exslit, -1);   /* For faster memory access */
02404     
02405             nsubx = cpl_image_get_size_x(exslit);
02406             nsuby = cpl_image_get_size_y(exslit);
02407             data = cpl_image_get_data(exslit);
02408             flux = cpl_vector_new(nsubx);
02409 
02410             uradius = nsubx / 2;
02411             if (uradius > sradius)
02412                 uradius = sradius;
02413 
02414             for (j = 0; j < nsuby; j++) {
02415                 fdata = cpl_vector_get_data(flux);
02416                 p = data;
02417                 for (k = 0; k < nsubx; k++)
02418                     *fdata++ = *p++;
02419                 smo_flux = cpl_vector_filter_median_create(flux, uradius);
02420                 fdata = cpl_vector_get_data(smo_flux);
02421                 p = data;
02422                 for (k = 0; k < nsubx; k++)
02423                     *p++ = *fdata++;
02424                 cpl_vector_delete(smo_flux);
02425                 data += nsubx;
02426             }
02427 
02428             cpl_vector_delete(flux);
02429 
02430 
02431             /*
02432              * First fit fluxes along the spatial direction with a low-degree
02433              * polynomial (excluding the first and the last pixels, close to
02434              * the edges)
02435              */
02436 /*
02437             if (nsubx-2*exclude > 10) {
02438                 flux = cpl_vector_new(nsubx-2*exclude);
02439                 fdata = cpl_vector_get_data(flux);
02440                 positions = cpl_vector_new(nsubx-2*exclude);
02441                 for (j = 0; j < nsubx-2*exclude; j++)
02442                     cpl_vector_set(positions, j, j+exclude);
02443         
02444                 for (k = 0; k < nsuby; k++) {
02445                     for (j = 0; j < nsubx-2*exclude; j++)
02446                         fdata[j] = data[j+exclude];
02447                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02448                                                          1, NULL);
02449                     for (j = 0; j < nsubx; j++)
02450                         data[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02451                     cpl_polynomial_delete(trend);
02452                     data += nsubx;
02453                 }
02454 
02455                 cpl_vector_delete(flux);
02456                 cpl_vector_delete(positions);
02457             }
02458 */
02459 
02460             /*
02461              * Now smooth along the dispersion direction 
02462              */
02463 
02464             cpl_image_turn(exslit, 1);   /* For faster memory access */
02465             nsubx = cpl_image_get_size_x(exslit);
02466             nsuby = cpl_image_get_size_y(exslit);
02467             data = cpl_image_get_data(exslit);
02468 
02469             for (j = 0; j < nsuby; j++) {
02470                 flux = cpl_vector_new(nsubx);
02471                 fdata = cpl_vector_get_data(flux);
02472                 p = data;
02473                 for (k = 0; k < nsubx; k++)
02474                     *fdata++ = *p++;
02475                 smo_flux = cpl_vector_filter_median_create(flux, sradius);
02476                 cpl_vector_delete(flux);
02477                 fdata = cpl_vector_get_data(smo_flux);
02478                 p = data;
02479                 for (k = 0; k < nsubx; k++)
02480                     *p++ = *fdata++;
02481                 cpl_vector_delete(smo_flux);
02482                 data += nsubx;
02483             }
02484         }
02485         else {
02486 
02487             /*
02488              * Fit with a polynomial the flat field trend row by row.
02489              */
02490 
02491             nsubx = cpl_image_get_size_x(exslit);
02492             nsuby = cpl_image_get_size_y(exslit);
02493             data = cpl_image_get_data(exslit);
02494 
02495             for (j = 0; j < nsuby; j++) {
02496 
02497                 /*
02498                  * First get the size of the vectors to allocate:
02499                  */
02500 
02501                 npoints = 0;
02502                 p = data + j*nsubx;
02503                 for (k = 0; k < nsubx; k++)
02504                     if (p[k] > 1.0)
02505                         npoints++;
02506 
02507                 if (npoints > polyorder + 1) {
02508 
02509                     /*
02510                      * Fill the vectors for the fitting
02511                      */
02512 
02513                     flux = cpl_vector_new(npoints);
02514                     fdata = cpl_vector_get_data(flux);
02515                     positions = cpl_vector_new(npoints);
02516                     pdata = cpl_vector_get_data(positions);
02517 
02518                     npoints = 0;
02519                     p = data + j*nsubx;
02520                     for (k = 0; k < nsubx; k++) {
02521                         if (p[k] > 1.0) {
02522                             fdata[npoints] = p[k];
02523                             pdata[npoints] = k;
02524                             npoints++;
02525                         }
02526                     }
02527     
02528                     trend = cpl_polynomial_fit_1d_create(positions, flux, 
02529                                                          polyorder, NULL);
02530 
02531                     cpl_vector_delete(flux);
02532                     cpl_vector_delete(positions);
02533 
02534                     if (trend) {
02535                         p = data + j*nsubx;
02536                         for (k = 0; k < nsubx; k++)
02537                             if (p[k] > 1.0)
02538                                 p[k] = cpl_polynomial_eval_1d(trend, k, NULL);
02539                         cpl_polynomial_delete(trend);
02540                     }
02541                     else {
02542                         cpl_msg_warning(func, "Invalid flat field flux fit "
02543                                         "(ignored)");
02544                     }
02545                 }
02546             }
02547         }
02548 
02549         
02550         /*
02551          * Recover from the table of spectral curvature coefficients
02552          * the curvature polynomials.
02553          */
02554 
02555         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
02556 
02557         start_pixel = refpixel - pixel_below;
02558         if (start_pixel < 0)
02559             start_pixel = 0;
02560 
02561         end_pixel = refpixel + pixel_above;
02562         if (end_pixel > nx)
02563             end_pixel = nx;
02564 
02565         missing_top = 0;
02566         polytop = cpl_polynomial_new(1);
02567         for (k = 0; k <= order; k++) {
02568             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
02569             if (null) {
02570                 cpl_polynomial_delete(polytop);
02571                 missing_top = 1;
02572                 break;
02573             }
02574             cpl_polynomial_set_coeff(polytop, &k, coeff);
02575         }
02576 
02577         missing_bot = 0;
02578         polybot = cpl_polynomial_new(1);
02579         for (k = 0; k <= order; k++) {
02580             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
02581             if (null) {
02582                 cpl_polynomial_delete(polybot);
02583                 missing_bot = 1;
02584                 break;
02585             }
02586             cpl_polynomial_set_coeff(polybot, &k, coeff);
02587         }
02588 
02589         if (missing_top && missing_bot) {
02590             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
02591                           slit_id[i]);
02592             continue;
02593         }
02594 
02595         /*
02596          * In case just one of the two edges was not traced, the other
02597          * edge curvature model is duplicated and shifted to the other
02598          * end of the slit: better than nothing!
02599          */
02600 
02601         if (missing_top) {
02602             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
02603                           "the spectral curvature of the lower edge "
02604                           "is used instead.", slit_id[i]);
02605             polytop = cpl_polynomial_duplicate(polybot);
02606             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02607             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02608             k = 0;
02609             coeff = cpl_polynomial_get_coeff(polybot, &k);
02610             coeff += ytop - ybot;
02611             cpl_polynomial_set_coeff(polytop, &k, coeff);
02612         }
02613 
02614         if (missing_bot) {
02615             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
02616                           "the spectral curvature of the upper edge "
02617                           "is used instead.", slit_id[i]);
02618             polybot = cpl_polynomial_duplicate(polytop);
02619             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
02620             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
02621             k = 0;
02622             coeff = cpl_polynomial_get_coeff(polytop, &k);
02623             coeff -= ytop - ybot;
02624             cpl_polynomial_set_coeff(polybot, &k, coeff);
02625         }
02626 
02627 
02628         /*
02629          * Now map smoothed image to CCD.
02630          * Note that the npseudo value related to this slit is equal
02631          * to the number of spatial pseudo-pixels decreased by 1
02632          * (compare with function mos_spatial_calibration()).
02633          */
02634 
02635         nx = cpl_image_get_size_x(flat);
02636         ny = cpl_image_get_size_y(flat);
02637 
02638         sdata = cpl_image_get_data(spatial);
02639         xdata = cpl_image_get_data(exslit);
02640         npseudo = cpl_image_get_size_y(exslit) - 1;
02641 
02642         /*
02643          * Write interpolated smoothed values to CCD image
02644          */
02645 
02646         for (j = start_pixel; j < end_pixel; j++) {
02647             top = cpl_polynomial_eval_1d(polytop, j, NULL);
02648             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
02649             for (k = 0; k <= npseudo; k++) {
02650                 ypos = top - k*(top-bot)/npseudo;
02651                 yint = ypos;
02652 
02653                 /*
02654                  * The line:
02655                  *     value = sdata[j + nx*yint];
02656                  * should be equivalent to:
02657                  *     value = npseudo*(top-yint)/(top-bot);
02658                  */
02659 
02660                 if (yint < 0 || yint >= ny-1) {
02661                     yprev = yint;
02662                     continue;
02663                 }
02664 
02665                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
02666                 ivalue = value;               /* Nearest spatial pixels:   */
02667                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
02668                 if (ivalue < npseudo && ivalue >= 0) {
02669                     vtop = xdata[j + nx*(npseudo-ivalue)];
02670                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
02671                     wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue;
02672 
02673                     if (k) {
02674 
02675                         /*
02676                          * This is added to recover lost pixels on
02677                          * the CCD image (pixels are lost because
02678                          * the CCD pixels are less than npseudo+1).
02679                          */
02680 
02681                         if (yprev - yint > 1) {
02682                             value = sdata[j + nx*(yint+1)];
02683                             ivalue = value;
02684                             fvalue = value - ivalue;
02685                             if (ivalue < npseudo && ivalue >= 0) {
02686                                 vtop = xdata[j + nx*(npseudo-ivalue)];
02687                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
02688                                 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 
02689                                                        + vbot*fvalue;
02690                             }
02691                         }
02692                     }
02693                 }
02694                 yprev = yint;
02695             }
02696         }
02697         cpl_polynomial_delete(polytop);
02698         cpl_polynomial_delete(polybot);
02699         cpl_image_delete(exslit);
02700     }
02701 
02702     cpl_image_delete(rectified);
02703 
02704     cpl_image_divide(flat, smo_flat);
02705 
02706     return smo_flat;
02707 }
02708 
02709 
02734 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 
02735                                   int polyorder)
02736 {
02737     const char     *func = "mos_normalise_longflat";
02738 
02739     cpl_image      *smo_flat;
02740     cpl_image      *profile;
02741     cpl_vector     *flux;
02742     cpl_vector     *smo_flux;
02743     cpl_vector     *positions;
02744     cpl_polynomial *trend;
02745 
02746     float          *level;
02747     float          *p;
02748     float          *data;
02749     double         *fdata;
02750     double         *pdata;
02751 
02752     int             nx, ny;
02753     int             npoints;
02754     int             i, j;
02755 
02756 
02757     if (flat == NULL) {
02758         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
02759         return NULL;
02760     }
02761  
02762     if (sradius < 1 || dradius < 1) {
02763         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
02764         return NULL;
02765     }
02766 
02767     smo_flat = cpl_image_duplicate(flat);
02768 
02769     if (polyorder < 0) {
02770 
02771         /*
02772          * First smooth along the spatial direction
02773          */
02774 
02775         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02776 
02777         nx = cpl_image_get_size_x(smo_flat);
02778         ny = cpl_image_get_size_y(smo_flat);
02779         data = cpl_image_get_data(smo_flat);
02780     
02781         for (i = 0; i < ny; i++) {
02782             flux = cpl_vector_new(nx);
02783             fdata = cpl_vector_get_data(flux);
02784             p = data;
02785             for (j = 0; j < nx; j++)
02786                 *fdata++ = *p++;
02787             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02788             cpl_vector_delete(flux);
02789             fdata = cpl_vector_get_data(smo_flux);
02790             p = data;
02791             for (j = 0; j < nx; j++)
02792                 *p++ = *fdata++;
02793             cpl_vector_delete(smo_flux);
02794             data += nx;
02795         }
02796 
02797         /*
02798          * Second smooth along the dispersion direction
02799          */
02800 
02801         cpl_image_turn(smo_flat, 1);   /* For faster memory access */
02802 
02803         nx = cpl_image_get_size_x(smo_flat);
02804         ny = cpl_image_get_size_y(smo_flat);
02805         data = cpl_image_get_data(smo_flat);
02806 
02807         for (i = 0; i < ny; i++) {
02808             flux = cpl_vector_new(nx);
02809             fdata = cpl_vector_get_data(flux);
02810             p = data;
02811             for (j = 0; j < nx; j++)
02812                 *fdata++ = *p++;
02813             smo_flux = cpl_vector_filter_median_create(flux, sradius);
02814             cpl_vector_delete(flux);
02815             fdata = cpl_vector_get_data(smo_flux);
02816             p = data;
02817             for (j = 0; j < nx; j++)
02818                 *p++ = *fdata++;
02819             cpl_vector_delete(smo_flux);
02820             data += nx;
02821         }
02822     }
02823     else {
02824 
02825         /*
02826          * Fit with a polynomial the flat field trend column by column.
02827          */
02828 
02829         cpl_image_turn(smo_flat, -1);   /* For faster memory access */
02830 
02831         nx = cpl_image_get_size_x(smo_flat);
02832         ny = cpl_image_get_size_y(smo_flat);
02833         data = cpl_image_get_data(smo_flat);
02834 
02835         profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0);
02836         level = cpl_image_get_data(profile);
02837 
02838         for (i = 0; i < ny; i++) {
02839 
02840             /*
02841              * First get the size of the vectors to allocate:
02842              * eliminate from fit any value more than 20% away
02843              * from median level in current column.
02844              */
02845 
02846             npoints = 0;
02847             p = data + i*nx;
02848             for (j = 0; j < nx; j++)
02849                 if (fabs(p[j]/level[i] - 1) < 0.20)
02850                     npoints++;
02851 
02852             if (npoints > polyorder + 1) {
02853 
02854                 /*
02855                  * Fill the vectors for the fitting
02856                  */
02857 
02858                 flux = cpl_vector_new(npoints);
02859                 fdata = cpl_vector_get_data(flux);
02860                 positions = cpl_vector_new(npoints);
02861                 pdata = cpl_vector_get_data(positions);
02862 
02863                 npoints = 0;
02864                 p = data + i*nx;
02865                 for (j = 0; j < nx; j++) {
02866                     if (fabs(p[j]/level[i] - 1) < 0.20) {
02867                         fdata[npoints] = p[j];
02868                         pdata[npoints] = j;
02869                         npoints++;
02870                     }
02871                 }
02872     
02873                 trend = cpl_polynomial_fit_1d_create(positions, flux, 
02874                                                      polyorder, NULL);
02875 
02876                 cpl_vector_delete(flux);
02877                 cpl_vector_delete(positions);
02878 
02879                 if (trend) {
02880                     p = data + i*nx;
02881                     for (j = 0; j < nx; j++)
02882                         p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
02883                     cpl_polynomial_delete(trend);
02884                 }
02885                 else {
02886                     cpl_msg_warning(func, 
02887                                     "Invalid flat field flux fit (ignored)");
02888                 }
02889             }
02890         }
02891 
02892         cpl_image_delete(profile);
02893         cpl_image_turn(smo_flat, 1);
02894 
02895     }
02896 
02897     cpl_image_divide(flat, smo_flat);
02898 
02899     return smo_flat;
02900 }
02901 
02902 
02925 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff,
02926                                               cpl_table *slits, 
02927                                               int order, int global)
02928 {
02929     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
02930                                                  /* Max order is 5 */
02931     int nrow = cpl_table_get_nrow(slits);
02932     int i, j;
02933 
02934     
02935     if (order < 0)
02936         return CPL_ERROR_NONE;
02937 
02938     cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE);
02939     cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE);
02940 
02941     for (i = 0; i < nrow; i++) {
02942         int        position = cpl_table_get_int   (slits, "position", i, NULL);
02943         int        length   = cpl_table_get_int   (slits, "length",   i, NULL);
02944         double     xtop     = cpl_table_get_double(slits, "xtop",     i, NULL);
02945         double     xbot     = cpl_table_get_double(slits, "xbottom",  i, NULL);
02946         double     ytop     = cpl_table_get_double(slits, "ytop",     i, NULL);
02947         double     ybot     = cpl_table_get_double(slits, "ybottom",  i, NULL);
02948         double     dx       = xtop - xbot;
02949         double     dy       = ytop - ybot;
02950         cpl_table *table    = cpl_table_extract(idscoeff, position, length);
02951 
02952         if (mos_interpolate_wavecalib(table, NULL, 2, order))
02953             continue;
02954 
02955         cpl_table_erase_window(idscoeff, position, length);
02956         cpl_table_insert(idscoeff, table, position);
02957 
02958         cpl_table_delete(table);
02959 
02960         for (j = 0; j < length; j++) {
02961             cpl_table_set_double(idscoeff, "x", j + position,
02962                                  xbot + j*(dx/length));
02963             cpl_table_set_double(idscoeff, "y", j + position,
02964                                  ybot + j*(dy/length));
02965         }
02966     }
02967 
02968     if (global) {
02969 
02970         /*
02971          * Now fit a global solution
02972          */
02973 
02974         nrow = cpl_table_get_nrow(idscoeff);
02975 
02976         for (i = 0; i < 6; i++) {
02977             cpl_table      *dummy;
02978             cpl_vector     *x;
02979             cpl_vector     *y;
02980             cpl_bivector   *z;
02981             cpl_vector     *c;
02982             cpl_polynomial *p;
02983             cpl_vector     *point;
02984             double         *dpoint;
02985             int             npoints;
02986 
02987             if (!cpl_table_has_column(idscoeff, clab[i]))
02988                 break;
02989 
02990             npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]);
02991             if (npoints < 18)
02992                 break;
02993 
02994             dummy = cpl_table_new(nrow);
02995             cpl_table_duplicate_column(dummy, "x", idscoeff, "x");
02996             cpl_table_duplicate_column(dummy, "y", idscoeff, "y");
02997             cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]);
02998             cpl_table_erase_invalid(dummy);
02999 
03000             x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x"));
03001             y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y"));
03002             z = cpl_bivector_wrap_vectors(x, y);
03003             c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 
03004                                                                    clab[i]));
03005             p = cpl_polynomial_fit_2d_create(z, c, 2, NULL);
03006             cpl_bivector_unwrap_vectors(z);
03007             cpl_vector_unwrap(x);
03008             cpl_vector_unwrap(y);
03009             cpl_vector_unwrap(c);
03010             cpl_table_delete(dummy);
03011 
03012             point  = cpl_vector_new(2);
03013             dpoint = cpl_vector_get_data(point);
03014             for (j = 0; j < nrow; j++) {
03015                 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL);
03016                 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL);
03017                 cpl_table_set_double(idscoeff, clab[i], j,
03018                                      cpl_polynomial_eval(p, point));
03019             }
03020             cpl_vector_delete(point);
03021             cpl_polynomial_delete(p);
03022         }
03023     }
03024 
03025     return CPL_ERROR_NONE;
03026 }
03027 
03028 
03054 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 
03055                                          cpl_image *wavemap, int mode,
03056                                          int degree)
03057 {
03058     const char *func = "mos_interpolate_wavecalib";
03059 
03060     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
03061                                                  /* Max order is 5 */
03062 
03063     cpl_vector     *wave;
03064     cpl_vector     *positions;
03065     cpl_polynomial *trend;
03066 
03067     float          *p;
03068     float          *data;
03069     double         *wdata;
03070     double         *pdata;
03071 
03072     double          c;
03073     double          mse, ksigma;
03074 
03075     int             order;
03076     int             nrows, first_row, last_row;
03077     int             nx, ny;
03078     int             npoints, rpoints;
03079     int             null;
03080     int             i, j, k;
03081 
03082     int             polyorder = 4;  /* Candidate input argument */
03083 
03084 
03085     if (idscoeff == NULL)
03086         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03087 
03088     if (mode < 0 || mode > 2)
03089         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03090 
03091     if (mode == 0 || degree < 0)
03092         return CPL_ERROR_NONE;
03093 
03094     if (wavemap) {
03095 
03096         /*
03097          * Fit with a polynomial the wavelength trend column by column.
03098          */
03099 
03100         cpl_image_turn(wavemap, -1);   /* For faster memory access */
03101 
03102         nx = cpl_image_get_size_x(wavemap);
03103         ny = cpl_image_get_size_y(wavemap);
03104         data = cpl_image_get_data(wavemap);
03105 
03106         for (i = 0; i < ny; i++) {
03107 
03108             /*
03109              * First get the size of the vectors to allocate:
03110              * eliminate from fit any value with "impossible" wavelength.
03111              */
03112 
03113             npoints = 0;
03114             p = data + i*nx;
03115             for (j = 0; j < nx; j++)
03116                 if (p[j] > 1.0)
03117                     npoints++;
03118 
03119             if (npoints > polyorder + 1) {
03120 
03121                 /*
03122                  * Fill the vectors for the fitting
03123                  */
03124 
03125                 wave = cpl_vector_new(npoints);
03126                 wdata = cpl_vector_get_data(wave);
03127                 positions = cpl_vector_new(npoints);
03128                 pdata = cpl_vector_get_data(positions);
03129 
03130                 npoints = 0;
03131                 p = data + i*nx;
03132                 for (j = 0; j < nx; j++) {
03133                     if (p[j] > 1.0) {
03134                         wdata[npoints] = p[j];
03135                         pdata[npoints] = j;
03136                         npoints++;
03137                     }
03138                 }
03139     
03140                 trend = cpl_polynomial_fit_1d_create(positions, wave, 
03141                                                      polyorder, &mse);
03142 
03143                 ksigma = 3*sqrt(mse);
03144 
03145                 cpl_vector_delete(wave);
03146                 cpl_vector_delete(positions);
03147 
03148                 if (trend) {
03149 
03150                     /*
03151                      * Apply 3-sigma rejection
03152                      */
03153 
03154                     rpoints = 0;
03155                     p = data + i*nx;
03156                     for (j = 0; j < nx; j++)
03157                         if (p[j] > 1.0)
03158                             if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 
03159                                                     - p[j]) < ksigma)
03160                                 rpoints++;
03161 
03162                     if (rpoints < npoints && rpoints > polyorder + 1) {
03163 
03164                         wave = cpl_vector_new(rpoints);
03165                         wdata = cpl_vector_get_data(wave);
03166                         positions = cpl_vector_new(rpoints);
03167                         pdata = cpl_vector_get_data(positions);
03168 
03169                         npoints = 0;
03170                         p = data + i*nx;
03171                         for (j = 0; j < nx; j++) {
03172                             if (p[j] > 1.0) {
03173                                 if (fabs(cpl_polynomial_eval_1d(trend, 
03174                                                                 j, NULL) - p[j])
03175                                                                 < ksigma) {
03176                                     wdata[npoints] = p[j];
03177                                     pdata[npoints] = j;
03178                                     npoints++;
03179                                 }
03180                             }
03181                         }
03182         
03183                         cpl_polynomial_delete(trend);
03184                         trend = cpl_polynomial_fit_1d_create(positions, wave,
03185                                                              polyorder, NULL);
03186 
03187                         cpl_vector_delete(wave);
03188                         cpl_vector_delete(positions);
03189                     }
03190                 }
03191 
03192                 if (trend) {
03193                     p = data + i*nx;
03194                     if (mode == 1) {
03195                         for (j = 0; j < nx; j++)
03196                             if (p[j] < 1.0)
03197                                 p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03198                     }
03199                     else if (mode == 2) {
03200                         for (j = 0; j < nx; j++)
03201                             p[j] = cpl_polynomial_eval_1d(trend, j, NULL);
03202                     }
03203                     cpl_polynomial_delete(trend);
03204                 }
03205                 else {
03206                     cpl_msg_warning(func, 
03207                                     "Invalid wavelength field fit (ignored)");
03208                 }
03209             }
03210     
03211         }
03212 
03213         cpl_image_turn(wavemap, 1);
03214 
03215     }
03216 
03217 
03218     /*
03219      * Interpolating the IDS coefficients
03220      */
03221 
03222     nrows = cpl_table_get_nrow(idscoeff);
03223 
03224     order = 0;
03225     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
03226         ++order;
03227     --order;
03228 
03229     if (degree == 0) {
03230         for (k = 0; k <= order; k++) {
03231             double m;
03232             if (cpl_table_has_column(idscoeff, clab[k])) {
03233                 m = cpl_table_get_column_median(idscoeff, clab[k]);
03234                 cpl_table_fill_column_window_double(idscoeff, clab[k], 
03235                                                     0, nrows, m);
03236             }
03237         }
03238 
03239         return CPL_ERROR_NONE;
03240     }
03241 
03242     first_row = 0;
03243     while (!cpl_table_is_valid(idscoeff, clab[0], first_row))
03244         first_row++;
03245 
03246     last_row = nrows - 1;
03247     while (!cpl_table_is_valid(idscoeff, clab[0], last_row))
03248         last_row--;
03249 
03250     for (k = 0; k <= order; k++) {
03251 
03252         npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]);
03253         wave = cpl_vector_new(npoints);
03254         wdata = cpl_vector_get_data(wave);
03255         positions = cpl_vector_new(npoints);
03256         pdata = cpl_vector_get_data(positions);
03257 
03258         npoints = 0;
03259         for (i = first_row; i <= last_row; i++) {
03260             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03261             if (null == 0) {
03262                 wdata[npoints] = c;
03263                 pdata[npoints] = i;
03264                 npoints++;
03265             }
03266         }
03267 
03268 // This doesn't seem to provide good results, I have not understood why.
03269 // Restore for robust linear fitting.
03270 //
03271 //        if (degree == 1) {
03272 //            cpl_vector   *p;
03273 //            cpl_vector   *w;
03274 //            cpl_bivector *list;
03275 //            double        q, m;
03276 //
03277 //            if (npoints > 4) {
03278 //                p = cpl_vector_extract(positions, 2, npoints - 2, 1);
03279 //                w = cpl_vector_extract(wave, 2, npoints - 2, 1);
03280 //            }
03281 //            else {
03282 //                p = positions;
03283 //                w = wave;
03284 //            }
03285 //
03286 //            list = cpl_bivector_wrap_vectors(p, w);
03287 //
03288 //            robustLinearFit(list, &q, &m, &mse);
03289 //            cpl_bivector_unwrap_vectors(list);
03290 //            for (i = first_row; i <= last_row; i++)
03291 //                 cpl_table_set_double(idscoeff, clab[k], i, q + m*i);
03292 //
03293 //            if (npoints > 4) {
03294 //                cpl_vector_delete(p);
03295 //                cpl_vector_delete(w);
03296 //            }
03297 //
03298 //            continue;
03299 //        }
03300 
03301         trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse);
03302 
03303         ksigma = 3*sqrt(mse);
03304 
03305         cpl_vector_delete(wave);
03306         cpl_vector_delete(positions);
03307 
03308         /*
03309          * Iteration
03310          */
03311 
03312         if (trend) {
03313             rpoints = 0;
03314             for (i = first_row; i <= last_row; i++) {
03315                 c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03316                 if (null == 0) {
03317                     if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 
03318                                                                  < ksigma) {
03319                         rpoints++;
03320                     }
03321                 }
03322             }
03323 
03324             if (rpoints > 0 && rpoints < npoints) {
03325                 cpl_msg_debug(func, "%d points rejected from "
03326                               "wavelength calibration fit", 
03327                               npoints - rpoints);
03328 
03329                 wave = cpl_vector_new(rpoints);
03330                 wdata = cpl_vector_get_data(wave);
03331                 positions = cpl_vector_new(rpoints);
03332                 pdata = cpl_vector_get_data(positions);
03333 
03334                 npoints = 0;
03335                 for (i = first_row; i <= last_row; i++) {
03336                     c = cpl_table_get_double(idscoeff, clab[k], i, &null);
03337                     if (null == 0) {
03338                         if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c)
03339                                                                  < ksigma) {
03340                             wdata[npoints] = c;
03341                             pdata[npoints] = i;
03342                             npoints++;
03343                         }
03344                     }
03345                 }
03346 
03347                 if (npoints) {
03348                     cpl_polynomial_delete(trend);
03349                     trend = cpl_polynomial_fit_1d_create(positions, 
03350                                                          wave, degree, NULL);
03351                 }
03352 
03353                 cpl_vector_delete(wave);
03354                 cpl_vector_delete(positions);
03355 
03356             }
03357         }
03358 
03359         if (trend) {
03360             for (i = first_row; i <= last_row; i++) {
03361                 if (mode == 1) {
03362                     if (!cpl_table_is_valid(idscoeff, clab[k], i)) {
03363                         cpl_table_set_double(idscoeff, clab[k], i, 
03364                                              cpl_polynomial_eval_1d(trend, i,
03365                                                                     NULL));
03366                     }
03367                 }
03368                 else if (mode == 2) {
03369                     cpl_table_set_double(idscoeff, clab[k], i, 
03370                                      cpl_polynomial_eval_1d(trend, i, NULL));
03371                 }
03372             }
03373             cpl_polynomial_delete(trend);
03374         }
03375         else {
03376             cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)");
03377         }
03378 
03379     }
03380 
03381     return CPL_ERROR_NONE;
03382 }
03383 
03384 
03385 
03411 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 
03412                            cpl_table *overscans)
03413 {
03414     const char *func = "mos_remove_bias";
03415 
03416     cpl_image *unbiased;
03417     cpl_image *overscan;
03418     double     mean_bias_level;
03419     double     mean_overscans_level;
03420     int        count;
03421     int        nrows;
03422     int        xlow, ylow, xhig, yhig;
03423     int        i;
03424 
03425 
03426     if (image == NULL || overscans == NULL) {
03427         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03428         return NULL;
03429     }
03430 
03431     nrows = cpl_table_get_nrow(overscans);
03432 
03433     if (nrows == 0) {
03434         cpl_msg_error(func, "Empty overscan table");
03435         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03436         return NULL;
03437     }
03438 
03439     if (bias) {
03440         if (nrows == 1) {
03441             unbiased = cpl_image_subtract_create(image, bias);
03442             if (unbiased == NULL) {
03443                 cpl_msg_error(func, "Incompatible master bias");
03444                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03445             }
03446             return unbiased;
03447         }
03448         mean_bias_level = cpl_image_get_mean(bias);
03449     }
03450     else {
03451         if (nrows == 1) {
03452             cpl_msg_error(func, "No master bias in input, and no overscan "
03453                           "regions in input image: bias subtraction "
03454                           "cannot be performed!");
03455             cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
03456             return NULL;
03457         }
03458         mean_bias_level = 0.0;
03459     }
03460 
03461     mean_overscans_level = 0.0;
03462     count = 0;
03463     for (i = 0; i < nrows; i++) {
03464         xlow = cpl_table_get_int(overscans, "xlow", i, NULL);
03465         ylow = cpl_table_get_int(overscans, "ylow", i, NULL);
03466         xhig = cpl_table_get_int(overscans, "xhig", i, NULL);
03467         yhig = cpl_table_get_int(overscans, "yhig", i, NULL);
03468 
03469         if (i == 0) {
03470             unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03471             if (unbiased == NULL) {
03472                 cpl_msg_error(func, "Incompatible overscan table");
03473                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03474                 return NULL;
03475             }
03476             if (bias) {
03477                 if (cpl_image_subtract(unbiased, bias)) {
03478                     cpl_msg_error(func, "Incompatible master bias");
03479                     cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03480                     cpl_image_delete(unbiased);
03481                     return NULL;
03482                 }
03483             }
03484         }
03485         else {
03486             overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig);
03487             if (overscan == NULL) {
03488                 cpl_msg_error(func, "Incompatible overscan table");
03489                 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
03490                 cpl_image_delete(unbiased);
03491                 return NULL;
03492             }
03493 
03494             mean_overscans_level += cpl_image_get_median(overscan);
03495             count++;
03496 
03497 /***
03498  * Here the mean level was used: not very robust...
03499 
03500             mean_overscans_level += cpl_image_get_flux(overscan);
03501             count += cpl_image_get_size_x(overscan)
03502                    * cpl_image_get_size_y(overscan);
03503 ***/
03504             cpl_image_delete(overscan);
03505         }
03506     }
03507 
03508     /*
03509      * Overscan correction
03510      */
03511 
03512     mean_overscans_level /= count;
03513 
03514     cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level);
03515 
03516     cpl_msg_info(cpl_func, 
03517                  "Ratio between mean overscans level and mean bias level: %.2f",
03518                   mean_overscans_level / mean_bias_level);
03519 
03520     return unbiased;
03521 
03522 }
03523 
03524 
03583 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 
03584                                      int length, int msize, int fsize) 
03585 {
03586     const char *func = "mos_arc_background_1D";
03587 
03588     float  *minf;
03589     float  *maxf;
03590     float  *smof;
03591     int     i;
03592 
03593 
03594     if (spectrum == NULL || back == NULL)
03595         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03596 
03597     if (msize % 2 == 0)
03598         msize++;
03599 
03600     if (fsize % 2 == 0)
03601         fsize++;
03602 
03603     if (msize < 3 || fsize < msize || length < 2*fsize)
03604         return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
03605 
03606 
03607     minf = min_filter(spectrum, length, msize);
03608     smof = smo_filter(minf, length, fsize);
03609     cpl_free(minf);
03610     maxf = max_filter(smof, length, 2*msize+1);
03611     cpl_free(smof);
03612     smof = smo_filter(maxf, length, 2*fsize+1);
03613     cpl_free(maxf);
03614     minf = min_filter(smof, length, 2*msize+1);
03615     cpl_free(smof);
03616     smof = smo_filter(minf, length, 2*fsize+1);
03617     cpl_free(minf);
03618 
03619     for (i = 0; i < length; i++)
03620         back[i] = smof[i];
03621 
03622     cpl_free(smof);
03623 
03624     return CPL_ERROR_NONE;
03625 
03626 }
03627 
03628 
03685 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 
03686 {
03687     const char *func = "mos_arc_background";
03688 
03689     cpl_image  *fimage;
03690     cpl_image  *bimage;
03691     cpl_matrix *kernel;
03692     float      *data;
03693     float      *bdata;
03694     float      *row;
03695     float      *brow;
03696     int         nx, ny;
03697     int         i;
03698 
03699 
03700     if (image == NULL) {
03701         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03702         return NULL;
03703     }
03704 
03705     if (msize % 2 == 0)
03706         msize++;
03707 
03708     if (fsize % 2 == 0)
03709         fsize++;
03710 
03711     nx = cpl_image_get_size_x(image);
03712     ny = cpl_image_get_size_y(image);
03713 
03714     bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
03715 
03716     fimage = mos_image_filter_median(image, 3, 3);
03717 
03718     data = cpl_image_get_data_float(fimage);
03719     bdata = cpl_image_get_data_float(bimage);
03720 
03721     for (i = 0; i < ny; i++) {
03722         row = data + i * nx;
03723         brow = bdata + i * nx;
03724         if (mos_arc_background_1D(row, brow, nx, msize, fsize)) {
03725             cpl_error_set_where(func); 
03726             cpl_image_delete(fimage);
03727             cpl_image_delete(bimage);
03728             return NULL;
03729         }
03730     }
03731 
03732     cpl_image_delete(fimage);
03733 
03734     return bimage;
03735 }
03736 
03737 
03758 int mos_lines_width(const float *spectrum, int length)
03759 {
03760 
03761   const char *func = "mos_lines_width";
03762 
03763   double *profile1 = cpl_calloc(length - 1, sizeof(double));
03764   double *profile2 = cpl_calloc(length - 1, sizeof(double));
03765 
03766   double  norm, value, max;
03767   int     radius = 20;
03768   int     short_length = length - 2*radius - 1;
03769   int     width;
03770   int     i, j, k;
03771 
03772 
03773   /*
03774    * Derivative, and separation of positive and negative derivatives
03775    */
03776 
03777   for (j = 0, i = 1; i < length; j++, i++) {
03778       profile1[j] = profile2[j] = spectrum[i] - spectrum[j];
03779       if (profile1[j] < 0)
03780           profile1[j] = 0;
03781       if (profile2[j] > 0)
03782           profile2[j] = 0;
03783       else
03784           profile2[j] = -profile2[j];
03785   }
03786 
03787 
03788   /*
03789    * Profiles normalisation
03790    */
03791 
03792   length--;
03793 
03794   norm = 0;
03795   for (i = 0; i < length; i++)
03796       if (norm < profile1[i])
03797           norm = profile1[i];
03798 
03799   for (i = 0; i < length; i++) {
03800       profile1[i] /= norm;
03801       profile2[i] /= norm;
03802   }
03803 
03804 
03805   /*
03806    * Cross-correlation
03807    */
03808 
03809   max = -1;
03810   for (i = 0; i <= radius; i++) {
03811       value = 0;
03812       for (j = 0; j < short_length; j++) {
03813           k = radius+j;
03814           value += profile1[k] * profile2[k+i];
03815       }
03816       if (max < value) {
03817           max = value;
03818           width = i;
03819       }
03820   }
03821 
03822   cpl_free(profile1);
03823   cpl_free(profile2);
03824 
03825   if (max < 0.0) {
03826       cpl_msg_debug(func, "Cannot estimate line width");
03827       width = 1;
03828   }
03829 
03830   return width;
03831 
03832 }
03833 
03834 
03861 cpl_vector *mos_peak_candidates(const float *spectrum, 
03862                                 int length, float level, 
03863                                 float exp_width)
03864 { 
03865 
03866   const char *func = "mos_peak_candidates";
03867 
03868   int     i, j;
03869   int     nint   = length - 1;
03870   int     n      = 0;
03871   int     width  = 2 * ceil(exp_width / 2) + 1;
03872   int     start  = width / 2;
03873   int     end    = length - width / 2;
03874   int     step;
03875   float  *smo;
03876   double *data   = cpl_calloc(length/2, sizeof(double));
03877 
03878 
03879   if (spectrum == NULL) {
03880       cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03881       return NULL;
03882   }
03883 
03884 
03885   /*
03886    * If lines have a flat top (as in the case of broad slit), smooth
03887    * before determining the max.
03888    */
03889 
03890   if (width > 7) {
03891     smo = cpl_calloc(length, sizeof(float));
03892     start = width / 2;
03893     end = length - width / 2;
03894     for (i = 0; i < start; i++)
03895       smo[i] = spectrum[i];
03896     for (i = start; i < end; i++) {
03897       for (j = i - start; j <= i + start; j++)
03898         smo[i] += spectrum[j];
03899       smo[i] /= width;
03900     }
03901     for (i = end; i < length; i++)
03902       smo[i] = spectrum[i];
03903   }
03904   else {
03905       smo = (float *)spectrum;
03906   }
03907 
03908   /*
03909    * Collect all relative maxima along spectrum, that are higher than the
03910    * specified level.
03911    */
03912 
03913   if (width > 20)
03914     step = width / 2;
03915   else
03916     step = 1;
03917 
03918   for (i = step; i < nint - step + 1; i += step) {
03919     if (smo[i] > level) {
03920       if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) {
03921         if (smo[i-step] != 0.0 && smo[i+step] != 0.0) {
03922           data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]);
03923           ++n;
03924         }
03925       }
03926     }
03927   }
03928 
03929   if (width > 7) {
03930     cpl_free(smo);
03931   }
03932 
03933   if (n == 0) {
03934     cpl_free(data);
03935     return NULL;
03936   }
03937 
03938   return cpl_vector_wrap(n, data);
03939 
03940 }
03941 
03942 
03964 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 
03965                              cpl_vector *peaks, int sradius)
03966 {
03967 
03968     const char *func = "mos_refine_peaks";
03969 
03970     double *data;
03971     float   pos;
03972     int     npeaks;
03973     int     startPos, endPos;
03974     int     window = 2*sradius+1;
03975     int     i, j;
03976 
03977 
03978     if (peaks == NULL || spectrum == NULL) {
03979         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
03980         return NULL;
03981     }
03982 
03983     npeaks = cpl_vector_get_size(peaks);
03984     data = cpl_vector_unwrap(peaks);
03985 
03986     for (i = 0; i < npeaks; i++) {
03987         startPos = data[i] - window/2;
03988         endPos   = startPos + window;
03989         if (startPos < 0 || endPos >= length)
03990             continue;
03991 
03992         if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) {
03993             pos += startPos;
03994             data[i] = pos;
03995         }
03996     }
03997 
03998     for (i = 1; i < npeaks; i++)
03999         if (data[i] - data[i-1] < 0.5)
04000             data[i-1] = -1.0;
04001 
04002     for (i = 0, j = 0; i < npeaks; i++) {
04003         if (data[i] > 0.0) {
04004             if (i != j)
04005                 data[j] = data[i];
04006             j++;
04007         }
04008     }
04009 
04010     return cpl_vector_wrap(j, data);
04011 
04012 }
04013 
04014 
04015 void mos_set_multiplex(int multiplex)
04016 {
04017     mos_multiplex = multiplex;
04018 }
04019 
04073 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines,
04074                                  double min_disp, double max_disp,
04075                                  double tolerance)
04076 {
04077 
04078   int      i, j, k, l;
04079   int      nlint, npint;
04080   int      minpos;
04081   float    min;
04082   double   lratio, pratio;
04083   double   lo_start, lo_end, hi_start, hi_end, denom;
04084   double   disp, variation, prev_variation;
04085   int      max, maxpos, minl, mink;
04086   int      ambiguous;
04087   int      npeaks_lo, npeaks_hi;
04088   int     *peak_lo;
04089   int     *peak_hi;
04090   int    **ident;
04091   int     *nident;
04092   int     *lident;
04093 
04094   double  *peak;
04095   double  *line;
04096   int      npeaks, nlines;
04097 
04098   double  *xpos;
04099   double  *lambda;
04100   int     *ilambda;
04101   double  *tmp_xpos;
04102   double  *tmp_lambda;
04103   int     *tmp_ilambda;
04104   int     *flag;
04105   int      n = 0;
04106   int      nn;
04107   int      nseq = 0;
04108   int      gap;
04109   int     *seq_length;
04110   int      found;
04111 
04112   peak        = cpl_vector_get_data(peaks);
04113   npeaks      = cpl_vector_get_size(peaks);
04114   line        = cpl_vector_get_data(lines);
04115   nlines      = cpl_vector_get_size(lines);
04116 
04117   if (npeaks < 4)
04118       return NULL;
04119 
04120   peak_lo     = cpl_malloc(npeaks * sizeof(int));
04121   peak_hi     = cpl_malloc(npeaks * sizeof(int));
04122   nident      = cpl_calloc(npeaks, sizeof(int));
04123   lident      = cpl_calloc(nlines, sizeof(int));
04124   xpos        = cpl_calloc(npeaks, sizeof(double));
04125   lambda      = cpl_calloc(npeaks, sizeof(double));
04126   ilambda     = cpl_calloc(npeaks, sizeof(int));
04127   tmp_xpos    = cpl_calloc(npeaks, sizeof(double));
04128   tmp_lambda  = cpl_calloc(npeaks, sizeof(double));
04129   tmp_ilambda = cpl_calloc(npeaks, sizeof(int));
04130   flag        = cpl_calloc(npeaks, sizeof(int));
04131   seq_length  = cpl_calloc(npeaks, sizeof(int));
04132   ident       = cpl_malloc(npeaks * sizeof(int *));
04133   for (i = 0; i < npeaks; i++)
04134     ident[i]  = cpl_malloc(3 * npeaks * sizeof(int));
04135 
04136   /*
04137    * This is just the number of intervals - one less than the number
04138    * of points (catalog wavelengths, or detected peaks).
04139    */
04140 
04141   nlint = nlines - 1;
04142   npint = npeaks - 1;
04143 
04144 
04145   /*
04146    * Here the big loops on catalog lines begins.
04147    */
04148 
04149   for (i = 1; i < nlint; i++) {
04150 
04151 
04152     /*
04153      * For each catalog wavelength I take the previous and the next one, 
04154      * and compute the ratio of the corresponding wavelength intervals.
04155      * This ratio will be compared to all the ratios obtained doing the
04156      * same with all the detected peaks positions.
04157      */
04158 
04159     lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]);
04160 
04161 
04162     /*
04163      * Here the loop on detected peaks positions begins.
04164      */
04165 
04166     for (j = 1; j < npint; j++) {
04167 
04168       /*
04169        * Not all peaks are used for computing ratios: just the ones
04170        * that are compatible with the expected spectral dispersion
04171        * are taken into consideration. Therefore, I define the pixel
04172        * intervals before and after any peak that are compatible with
04173        * the specified dispersion interval, and select just the peaks
04174        * within such intervals. If either of the two intervals doesn't
04175        * contain any peak, then I skip the current peak and continue
04176        * with the next.
04177        */
04178 
04179       lo_start = peak[j] - (line[i] - line[i-1]) / min_disp;
04180       lo_end   = peak[j] - (line[i] - line[i-1]) / max_disp;
04181       hi_start = peak[j] + (line[i+1] - line[i]) / max_disp;
04182       hi_end   = peak[j] + (line[i+1] - line[i]) / min_disp;
04183 
04184       for (npeaks_lo = 0, k = 0; k < npeaks; k++) {
04185         if (peak[k] > lo_end)
04186           break;
04187         if (peak[k] > lo_start) {
04188           peak_lo[npeaks_lo] = k;
04189           ++npeaks_lo;
04190         }
04191       }
04192 
04193       if (npeaks_lo == 0)
04194         continue;
04195 
04196       for (npeaks_hi = 0, k = 0; k < npeaks; k++) {
04197         if (peak[k] > hi_end)
04198           break;
04199         if (peak[k] > hi_start) {
04200           peak_hi[npeaks_hi] = k;
04201           ++npeaks_hi;
04202         }
04203       }
04204 
04205       if (npeaks_hi == 0)
04206         continue;
04207 
04208 
04209       /*
04210        * Now I have all peaks that may help for a local identification.
04211        * peak_lo[k] is the sequence number of the k-th peak of the lower
04212        * interval; peak_hi[l] is the sequence number of the l-th peak of
04213        * the higher interval. j is, of course, the sequence number of the
04214        * current peak (second big loop).
04215        */
04216 
04217       prev_variation = 1000.0;
04218       minl = mink = 0;
04219 
04220       for (k = 0; k < npeaks_lo; k++) {
04221         denom = peak[j] - peak[peak_lo[k]];
04222         for (l = 0; l < npeaks_hi; l++) {
04223 
04224           /*
04225            * For any pair of peaks - one from the lower and the other
04226            * from the higher interval - I compute the same ratio that
04227            * was computed with the current line catalog wavelength.
04228            */
04229 
04230           pratio = (peak[peak_hi[l]] - peak[j]) / denom;
04231 
04232           /*
04233            * If the two ratios are compatible within the specified
04234            * tolerance, we have a preliminary identification. This
04235            * will be marked in the matrix ident[][], where the first
04236            * index corresponds to a peak sequence number, and the second
04237            * index is the counter of the identifications made during
04238            * this whole process. The array of counters is nident[].
04239            * If more than one interval pair fulfills the specified
04240            * tolerance, the closest to the expected ratio is selected.
04241            */
04242 
04243           variation = fabs(lratio-pratio) / pratio;
04244 
04245           if (variation < tolerance) {
04246             if (variation < prev_variation) {
04247               prev_variation = variation;
04248               minl = l;
04249               mink = k;
04250             }
04251           }
04252         }
04253       }
04254       if (prev_variation < tolerance) {
04255         ident[j][nident[j]]                         = i;
04256         ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1;
04257         ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1;
04258         ++nident[j];
04259         ++nident[peak_hi[minl]];
04260         ++nident[peak_lo[mink]];
04261       }
04262     }   /* End loop on positions */
04263   }    /* End loop on lines     */
04264 
04265 
04266   /*
04267    * At this point I have filled the ident matrix with all my preliminary
04268    * identifications. Ambiguous identifications must be eliminated.
04269    */
04270 
04271 
04272   for (i = 0; i < npeaks; i++) {
04273 
04274 
04275     /*
04276      * I don't take into consideration peaks that were never identified.
04277      * They are likely contaminations, or emission lines that were not
04278      * listed in the input wavelength catalog.
04279      */
04280 
04281     if (nident[i] > 1) {
04282 
04283 
04284       /*
04285        * Initialise the histogram of wavelengths assigned to the i-th peak.
04286        */
04287 
04288       for (j = 0; j < nlines; j++)
04289         lident[j] = 0;
04290 
04291 
04292       /*
04293        * Count how many times each catalog wavelength was assigned
04294        * to the i-th peak.
04295        */
04296 
04297       for (j = 0; j < nident[i]; j++)
04298         ++lident[ident[i][j]];
04299 
04300 
04301       /*
04302        * What wavelength was most frequently assigned to the i-th peak?
04303        */
04304 
04305       max = 0;
04306       maxpos = 0;
04307       for (j = 0; j < nlines; j++) {
04308         if (max < lident[j]) {
04309           max = lident[j];
04310           maxpos = j;
04311         }
04312       }
04313 
04314 
04315       /*
04316        * Were there other wavelengths assigned with the same frequency?
04317        * This would be the case of an ambiguous identification. It is
04318        * safer to reject this peak...
04319        */
04320 
04321       ambiguous = 0;
04322 
04323       for (k = maxpos + 1; k < nlines; k++) {
04324         if (lident[k] == max) {
04325           ambiguous = 1;
04326           break;
04327         }
04328       }
04329 
04330       if (ambiguous)
04331         continue;
04332 
04333 
04334       /*
04335        * Otherwise, I assign to the i-th peak the wavelength that was
04336        * most often assigned to it.
04337        */
04338 
04339       tmp_xpos[n]   = peak[i];
04340       tmp_lambda[n] = line[maxpos];
04341       tmp_ilambda[n] = maxpos;
04342 
04343       ++n;
04344 
04345     }
04346 
04347   }
04348 
04349 
04350   /*
04351    * Check on identified peaks. Contaminations from other spectra might 
04352    * be present and should be excluded: this type of contamination 
04353    * consists of peaks that have been _correctly_ identified! The non-
04354    * spectral type of light contamination should have been almost all 
04355    * removed already in the previous steps, but it may still be present.
04356    * Here, the self-consistent sequences of identified peaks are
04357    * separated one from the other. At the moment, just the longest of
04358    * such sequences is selected (in other words, spectral multiplexing
04359    * is ignored).
04360    */
04361 
04362   if (n > 1) {
04363     nn = 0;                  /* Number of peaks in the list of sequences */
04364     nseq = 0;                /* Current sequence */
04365     for (k = 0; k < n; k++) {
04366       if (flag[k] == 0) {    /* Was peak k already assigned to a sequence? */
04367         flag[k] = 1;
04368         xpos[nn] = tmp_xpos[k];       /* Begin the nseq-th sequence */
04369         lambda[nn] = tmp_lambda[k];
04370         ilambda[nn] = tmp_ilambda[k];
04371         ++seq_length[nseq];
04372         ++nn;
04373 
04374         /*
04375          * Now look for all the following peaks that are compatible
04376          * with the expected spectral dispersion, and add them in 
04377          * sequence to xpos. Note that missing peaks are not a problem...
04378          */
04379          
04380         i = k;
04381         while (i < n - 1) {
04382           found = 0;
04383           for (j = i + 1; j < n; j++) {
04384             if (flag[j] == 0) {
04385               disp = (tmp_lambda[j] - tmp_lambda[i])
04386                    / (tmp_xpos[j] - tmp_xpos[i]);
04387               if (disp >= min_disp && disp <= max_disp) {
04388                 flag[j] = 1;
04389                 xpos[nn] = tmp_xpos[j];
04390                 lambda[nn] = tmp_lambda[j];
04391                 ilambda[nn] = tmp_ilambda[j];
04392                 ++seq_length[nseq];
04393                 ++nn;
04394                 i = j;
04395                 found = 1;
04396                 break;
04397               }
04398             }
04399           }
04400           if (!found)
04401             break;
04402         }
04403 
04404         /*
04405          * Current sequence is completed: begin new sequence on the
04406          * excluded peaks, starting the loop on peaks again.
04407          */
04408 
04409         ++nseq;
04410         k = 0;
04411       }
04412     }
04413 
04414 
04415     /*
04416      * Find the longest sequence of self-consistent peaks.
04417      */
04418 
04419     maxpos = max = 0;
04420 
04421     if (mos_multiplex < 0) {
04422       for (i = 0; i < nseq; i++) {
04423         if (seq_length[i] > max) {
04424           max = seq_length[i];
04425           maxpos = i;
04426         }
04427       }
04428     }
04429     else {
04430 
04431       /*
04432        * Now consider the sequence which lays in the specified 
04433        * CCD region (indicated by mos_multiplex): that is, _most_ 
04434        * of its lines (more than half) must be in that region...
04435        */
04436 
04437       nn = 0;
04438       found = 0;
04439 
04440       for (i = 0; i < nseq; i++) {
04441         n = seq_length[i];
04442         if (n > 5) {
04443           cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT);
04444           int        region;
04445 
04446           for (j = 0; j < n; j++)
04447             cpl_array_set_int(regions, j, 
04448                               ((int)floor(xpos[nn + j])) / mos_region_size);
04449 
04450           region = (int)cpl_array_get_median(regions);
04451           cpl_array_delete(regions);
04452 
04453           if (mos_multiplex == region) {
04454             if (found) {
04455               cpl_msg_debug(cpl_func, "More than one spectrum found in "
04456                             "region %d (only the first one is extracted)", 
04457                             mos_multiplex);
04458               break;
04459             }
04460             found = 1;
04461             max = seq_length[i];
04462             maxpos = i;
04463           }
04464         }
04465         nn += seq_length[i];
04466       }
04467     }
04468 
04469     /*
04470      * Find where this sequence starts in the whole peak position
04471      * storage.
04472      */
04473 
04474     nn = 0;
04475     for (i = 0; i < maxpos; i++)
04476       nn += seq_length[i];
04477 
04478     /*
04479      * Move the longest sequence at the beginning of the returned lists
04480      */
04481 
04482     n = max;
04483     for (i = 0; i < n; i++, nn++) {
04484       xpos[i] = xpos[nn];
04485       lambda[i] = lambda[nn];
04486       ilambda[i] = ilambda[nn];
04487     }
04488 
04489 
04490     /*
04491      * Are some wavelengths missing? Recover them.
04492      */
04493 
04494     for (i = 1; i < n; i++) {
04495       gap = ilambda[i] - ilambda[i-1];
04496       for (j = 1; j < gap; j++) {
04497 
04498         if (j == 1) {
04499 
04500           /*
04501            * Determine the local dispersion from the current pair of peaks
04502            */
04503   
04504           disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]);
04505         }
04506 
04507         /*
04508          * With this, find the expected position of the missing
04509          * peak by linear interpolation.
04510          */
04511 
04512         hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp;
04513 
04514         /*
04515          * Is there a peak at that position? Here a peak from the
04516          * original list is searched, that is closer than 2 pixels
04517          * to the expected position. If it is found, insert it at
04518          * the current position on the list of identified peaks,
04519          * and leave immediately the loop (taking the new position
04520          * for the following linear interpolation, in case more
04521          * than one peak is missing in the current interval).
04522          * If it is not found, stay in the loop, looking for 
04523          * the following missing peaks in this interval.
04524          */
04525 
04526         found = 0;
04527         for (k = 0; k < npeaks; k++) {
04528           if (fabs(peak[k] - hi_start) < 2) {
04529             for (l = n; l > i; l--) {
04530               xpos[l] = xpos[l-1];
04531               lambda[l] = lambda[l-1];
04532               ilambda[l] = ilambda[l-1];
04533             }
04534             xpos[i] = peak[k];
04535             lambda[i] = line[ilambda[i-1] + j];
04536             ilambda[i] = ilambda[i-1] + j;
04537             ++n;
04538             found = 1;
04539             break;
04540           }
04541         }
04542         if (found)
04543           break;
04544       }
04545     }
04546 
04547 
04548     /*
04549      * Try to extrapolate forward
04550      */
04551 
04552     found = 1;
04553     while (ilambda[n-1] < nlines - 1 && found) {
04554 
04555       /*
04556        * Determine the local dispersion from the last pair of 
04557        * identified peaks
04558        */
04559 
04560       if (n > 1)
04561           disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]);
04562       else
04563           disp = 0.0;
04564 
04565       if (disp > max_disp || disp < min_disp)
04566         break;
04567 
04568 
04569       /*
04570        * With this, find the expected position of the missing
04571        * peak by linear interpolation.
04572        */
04573 
04574       hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp;
04575 
04576       /*
04577        * Is there a peak at that position? Here a peak from the
04578        * original list is searched, that is closer than 6 pixels
04579        * to the expected position. If it is found, insert it at
04580        * the end of the list of identified peaks. If it is not
04581        * found, leave the loop.
04582        */
04583 
04584       found = 0;
04585       min = fabs(peak[0] - hi_start);
04586       minpos = 0;
04587       for (k = 1; k < npeaks; k++) {
04588         if (min > fabs(peak[k] - hi_start)) {
04589             min = fabs(peak[k] - hi_start);
04590             minpos = k;
04591         }
04592       }
04593       if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) {
04594         xpos[n] = peak[minpos];
04595         lambda[n] = line[ilambda[n-1] + 1];
04596         ilambda[n] = ilambda[n-1] + 1;
04597         ++n;
04598         found = 1;
04599       }
04600     }
04601 
04602 
04603     /*
04604      * Try to extrapolate backward
04605      */
04606 
04607     found = 1;
04608     while (ilambda[0] > 0 && found) {
04609 
04610       /*
04611        * Determine the local dispersion from the first pair of
04612        * identified peaks
04613        */
04614 
04615       disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]);
04616 
04617       if (disp > max_disp || disp < min_disp)
04618         break;
04619 
04620 
04621       /*
04622        * With this, find the expected position of the missing
04623        * peak by linear interpolation.
04624        */
04625 
04626       hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp;
04627 
04628 
04629       /*
04630        * Is there a peak at that position? Here a peak from the
04631        * original list is searched, that is closer than 6 pixels
04632        * to the expected position. If it is found, insert it at
04633        * the beginning of the list of identified peaks. If it is not
04634        * found, leave the loop.
04635        */
04636 
04637       found = 0;
04638       min = fabs(peak[0] - hi_start);
04639       minpos = 0;
04640       for (k = 1; k < npeaks; k++) {
04641         if (min > fabs(peak[k] - hi_start)) {
04642             min = fabs(peak[k] - hi_start);
04643             minpos = k;
04644         }
04645       }
04646       if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) {
04647         for (j = n; j > 0; j--) {
04648           xpos[j] = xpos[j-1];
04649           lambda[j] = lambda[j-1];
04650           ilambda[j] = ilambda[j-1];
04651         }
04652         xpos[0] = peak[minpos];
04653         lambda[0] = line[ilambda[0] - 1];
04654         ilambda[0] = ilambda[0] - 1;
04655         ++n;
04656         found = 1;
04657       }
04658     }
04659   }
04660 
04661 
04662   /*
04663    * At this point all peaks are processed. Free memory, and return
04664    * the result.
04665    */
04666 
04667 /************************************************+
04668   for (i = 0; i < npeaks; i++) {
04669     printf("Peak %d:\n   ", i);
04670     for (j = 0; j < nident[i]; j++)
04671       printf("%.2f, ", line[ident[i][j]]);
04672     printf("\n");
04673   }
04674 
04675   printf("\n");
04676 
04677   for (i = 0; i < n; i++)
04678     printf("%.2f, %.2f\n", xpos[i], lambda[i]);
04679 +************************************************/
04680   for (i = 0; i < npeaks; i++)
04681     cpl_free(ident[i]);
04682   cpl_free(ident);
04683   cpl_free(nident);
04684   cpl_free(lident);
04685   cpl_free(ilambda);
04686   cpl_free(tmp_xpos);
04687   cpl_free(tmp_lambda);
04688   cpl_free(tmp_ilambda);
04689   cpl_free(peak_lo);
04690   cpl_free(flag);
04691   cpl_free(seq_length);
04692   cpl_free(peak_hi);
04693 
04694   if (n == 0) {
04695     cpl_free(xpos);
04696     cpl_free(lambda);
04697     return NULL;
04698   }
04699 
04700   return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 
04701                                    cpl_vector_wrap(n, lambda));
04702 }
04703 
04704 
04722 /*
04723 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 
04724                     double refwave, double pixel)
04725 {
04726     double yellow;
04727     double cpixel;
04728     double tolerance = 0.02;
04729     int    max_iter = 20;
04730     int    iter = 0;
04731 
04732     
04733     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04734         return 0.0;
04735     
04736     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04737         return 0.0;
04738 
04739     yellow = (blue + red) / 2;
04740 
04741     cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04742 
04743     while (fabs(cpixel - pixel) > tolerance && iter < max_iter) {
04744 
04745         if (cpixel > pixel)
04746             red = yellow;
04747         else
04748             blue = yellow;
04749 
04750         yellow = (blue + red) / 2;
04751         cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL);
04752 
04753         iter++;
04754 
04755     }
04756 
04757     return yellow;
04758 
04759 }
04760 */
04761 
04762 double mos_eval_dds(cpl_polynomial *ids, double blue, double red,
04763                     double refwave, double pixel)
04764 {
04765     double yellow;
04766     double coeff;
04767     int    zero = 0;
04768 
04769     if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel)
04770         return 0.0;
04771 
04772     if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel)
04773         return 0.0;
04774 
04775     yellow = (blue + red) / 2 - refwave;
04776 
04777     coeff = cpl_polynomial_get_coeff(ids, &zero);
04778     cpl_polynomial_set_coeff(ids, &zero, coeff - pixel);
04779 
04780     cpl_polynomial_solve_1d(ids, yellow, &yellow, 1);
04781     if (cpl_error_get_code() != CPL_ERROR_NONE) {
04782         cpl_error_reset();
04783         return 0.0;
04784     }
04785 
04786     cpl_polynomial_set_coeff(ids, &zero, coeff);
04787 
04788     return yellow + refwave;
04789 
04790 }
04791 
04817 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 
04818                                  double reject, int minlines, 
04819                                  int *nlines, double *err)
04820 {
04821     const char   *func = "mos_poly_wav2pix";
04822 
04823     cpl_bivector *pixwav2;
04824     cpl_vector   *wavel;
04825     cpl_vector   *pixel;
04826     double       *d_wavel;
04827     double       *d_pixel;
04828     double        pixpos;
04829     int           fitlines;
04830     int           rejection = 0;
04831     int           i, j;
04832 
04833     cpl_polynomial *ids;
04834 
04835 
04836     *nlines = 0;
04837     *err = 0;
04838 
04839     if (pixwav == NULL) {
04840         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
04841         return NULL;
04842     }
04843 
04844     fitlines = cpl_bivector_get_size(pixwav);
04845 
04846     if (fitlines < minlines) {
04847         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
04848         return NULL;
04849     }
04850 
04851 
04852     /*
04853      * If outliers rejection was requested, allocate a working
04854      * vector (that can be modified as soon as outliers are removed)
04855      */
04856 
04857     if (reject > 0.0)
04858         rejection = 1;
04859 
04860     if (rejection)
04861         pixwav2 = cpl_bivector_duplicate(pixwav);
04862     else
04863         pixwav2 = pixwav;
04864 
04865 
04866     /*
04867      * The single vectors are extracted just because the fitting routine
04868      * requires it
04869      */
04870 
04871     pixel = cpl_bivector_get_x(pixwav2);
04872     wavel = cpl_bivector_get_y(pixwav2);
04873 
04874 
04875     /*
04876      * Get rid of the wrapper, in case of duplication
04877      */
04878 
04879     if (rejection)
04880         cpl_bivector_unwrap_vectors(pixwav2);
04881 
04882 
04883     /*
04884      * Here begins the iterative fit of identified lines
04885      */
04886 
04887     while (fitlines >= minlines) {
04888 
04889         ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err);
04890         *err = sqrt(*err);
04891     
04892         if (ids == NULL) {
04893             cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message());
04894             cpl_msg_debug(func, "Fitting IDS");
04895             cpl_error_set_where(func);
04896             if (rejection) {
04897                 cpl_vector_delete(wavel);
04898                 cpl_vector_delete(pixel);
04899             }
04900             return NULL;
04901         }
04902 
04903         if (rejection) {
04904 
04905 
04906             /*
04907              * Now work directly with the vector data buffers...
04908              */
04909 
04910             d_pixel = cpl_vector_unwrap(pixel);
04911             d_wavel = cpl_vector_unwrap(wavel);
04912 
04913             for (i = 0, j = 0; i < fitlines; i++) {
04914                 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL);
04915                 if (fabs(pixpos - d_pixel[i]) < reject) {
04916                     d_pixel[j] = d_pixel[i];
04917                     d_wavel[j] = d_wavel[i];
04918                     j++;
04919                 }
04920             }
04921     
04922             if (j == fitlines) {       /* No rejection in last iteration */
04923                 cpl_free(d_wavel);
04924                 cpl_free(d_pixel);
04925                 *nlines = fitlines;
04926                 return ids;
04927             }
04928             else {                     /* Some lines were rejected       */
04929                 fitlines = j;
04930                 cpl_polynomial_delete(ids);
04931                 if (fitlines >= minlines) {
04932                     pixel = cpl_vector_wrap(fitlines, d_pixel);
04933                     wavel = cpl_vector_wrap(fitlines, d_wavel);
04934                 }
04935                 else {                 /* Too few lines: failure         */
04936                     cpl_free(d_wavel);
04937                     cpl_free(d_pixel);
04938                     cpl_error_set(func, CPL_ERROR_CONTINUE);
04939                     return NULL;
04940                 }
04941             }
04942         }
04943         else {
04944             *nlines = fitlines;
04945             return ids;       /* Exit at first iteration if no rejection */
04946         }
04947     }
04948 
04949     return ids;               /* To avoid compiler warnings */
04950 }
04951 
04952 
04977 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order,
04978                                  double reject, int minlines, 
04979                                  int *nlines, double *err)
04980 {
04981 
04982     cpl_bivector *wavpix;
04983     cpl_vector   *wavel;
04984     cpl_vector   *pixel;
04985 
04986     cpl_polynomial *dds;
04987 
04988 
04989     /*
04990      * Swap vectors in bivector, in order to reuse mos_poly_wav2pix()
04991      */
04992 
04993     pixel = cpl_bivector_get_x(pixwav);
04994     wavel = cpl_bivector_get_y(pixwav);
04995 
04996     wavpix = cpl_bivector_wrap_vectors(wavel, pixel);
04997 
04998     dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err);
04999 
05000     cpl_bivector_unwrap_vectors(wavpix);
05001 
05002     return dds;
05003 
05004 }
05005 
05006 
05029 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 
05030                              cpl_vector *lines, cpl_polynomial *ids, 
05031                              double refwave, int sradius)
05032 {
05033     const char   *func = "mos_find_peaks";
05034 
05035     double       *data;
05036     double       *d_pixel;
05037     double       *d_wavel;
05038     float         pos;
05039     int           nlines;
05040     int           pixel;
05041     int           i, j;
05042 
05043 
05044     if (spectrum == NULL || lines == NULL || ids == NULL) {
05045         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05046         return NULL;
05047     }
05048 
05049     nlines = cpl_vector_get_size(lines);
05050 
05051     if (sradius < 1 || length < 2*sradius+1 || nlines < 1) {
05052         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05053         return NULL;
05054     }
05055 
05056     d_wavel = cpl_malloc(nlines * sizeof(double));
05057     d_pixel = cpl_malloc(nlines * sizeof(double));
05058 
05059     data = cpl_vector_get_data(lines);
05060 
05061     for (i = 0, j = 0; i < nlines; i++) {
05062         pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5;
05063         if (pixel - sradius < 0 || pixel + sradius >= length)
05064             continue;
05065         if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) {
05066             pos += pixel - sradius;
05067             d_pixel[j] = pos;
05068             d_wavel[j] = data[i];
05069             j++;
05070         }
05071     }
05072 
05073     if (j > 0) {
05074         return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel),
05075                                          cpl_vector_wrap(j, d_wavel));
05076     }
05077     else {
05078         cpl_free(d_wavel);
05079         cpl_free(d_pixel);
05080         cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT);
05081         return NULL;
05082     }
05083 }
05084 
05085 
05203 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image,
05204                                           cpl_vector *lines,
05205                                           double dispersion, float level,
05206                                           int sradius, int order,
05207                                           double reject, double refwave, 
05208                                           double *wavestart, double *waveend,
05209                                           int *nlines, double *error, 
05210                                           cpl_table *idscoeff,
05211                                           cpl_image *calibration,
05212                                           cpl_image *residuals, 
05213                                           cpl_table *restable,
05214                                           cpl_mask *refmask)
05215 {
05216 
05217     const char *func = "mos_wavelength_calibration_raw";
05218 
05219     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
05220                                                  /* Max order is 5 */
05221 
05222     double  tolerance = 20.0;     /* Probably forever...                */
05223     int     step      = 10;       /* Compute restable every "step" rows */
05224 
05225     char            name[MAX_COLNAME];
05226     cpl_image      *resampled;
05227     cpl_bivector   *output;
05228     cpl_bivector   *new_output;
05229     cpl_vector     *peaks;
05230     cpl_vector     *wavel;
05231     cpl_polynomial *ids;
05232     cpl_polynomial *lin;
05233     cpl_matrix     *kernel;
05234     double          ids_err;
05235     double          max_disp, min_disp;
05236     double         *line;
05237     double          firstLambda, lastLambda, lambda;
05238     double          value, wave, pixe;
05239     cpl_binary     *mdata;
05240     const float    *sdata;
05241     float          *rdata;
05242     float          *idata;
05243     float          *ddata;
05244     float           v1, v2, vi;
05245     float           fpixel;
05246     int            *have_it;
05247     int             pixstart, pixend;
05248     int             extrapolation;
05249     int             nref;
05250     int             nl, nx, ny, pixel;
05251     int             countLines, usedLines;
05252     int             uorder;
05253     int             in, first, last;
05254     int             width, uradius;
05255     int             i, j, k;
05256 
05257 
05258     if (dispersion == 0.0) {
05259         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
05260         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05261         return NULL;
05262     }
05263 
05264     if (dispersion < 0.0) {
05265         cpl_msg_error(func, "The expected dispersion must be positive");
05266         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05267         return NULL;
05268     }
05269 
05270     max_disp = dispersion + dispersion * tolerance / 100;
05271     min_disp = dispersion - dispersion * tolerance / 100;
05272 
05273     if (order < 1) {
05274         cpl_msg_error(func, "The order of the fitting polynomial "
05275                       "must be at least 1");
05276         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
05277         return NULL;
05278     }
05279 
05280     if (image == NULL || lines == NULL) {
05281         cpl_msg_error(func, "Both spectral exposure and reference line "
05282                       "catalog are required in input");
05283         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05284         return NULL;
05285     }
05286 
05287     nx = cpl_image_get_size_x(image);
05288     ny = cpl_image_get_size_y(image);
05289     sdata = cpl_image_get_data_float_const(image);
05290 
05291     nref = cpl_vector_get_size(lines);
05292     line = cpl_vector_get_data(lines);
05293 
05294     if (*wavestart < 1.0 && *waveend < 1.0) {
05295         firstLambda = line[0];
05296         lastLambda = line[nref-1];
05297         extrapolation = (lastLambda - firstLambda) / 10;
05298         firstLambda -= extrapolation;
05299         lastLambda += extrapolation;
05300         *wavestart = firstLambda;
05301         *waveend = lastLambda;
05302     }
05303     else {
05304         firstLambda = *wavestart;
05305         lastLambda = *waveend;
05306     }
05307 
05308     nl = (lastLambda - firstLambda) / dispersion;
05309     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
05310     rdata = cpl_image_get_data_float(resampled);
05311 
05312     if (calibration)
05313         idata = cpl_image_get_data_float(calibration);
05314 
05315     if (residuals)
05316         ddata = cpl_image_get_data_float(residuals);
05317 
05318     if (idscoeff)
05319         for (j = 0; j <= order; j++)
05320             cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
05321 
05322     if (restable) {
05323         cpl_table_set_size(restable, nref);
05324         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
05325         cpl_table_copy_data_double(restable, "wavelength", line);
05326         for (i = 0; i < ny; i += step) {
05327              snprintf(name, MAX_COLNAME, "r%d", i);
05328              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05329              snprintf(name, MAX_COLNAME, "d%d", i);
05330              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05331              snprintf(name, MAX_COLNAME, "p%d", i);
05332              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
05333         }
05334     }
05335 
05336     /*
05337      * Here is the real thing: detecting and identifying peaks,
05338      * and then fit the transformation from wavelength to pixel
05339      * and from pixel to wavelength.
05340      */
05341 
05342     for (i = 0; i < ny; i++) {
05343         width = mos_lines_width(sdata + i*nx, nx);
05344         if (sradius > 0) {
05345             if (width > sradius) {
05346                 uradius = width;
05347             }
05348             else {
05349                 uradius = sradius;
05350             }
05351         }
05352         if (width < 5)
05353             width = 5;
05354         peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
05355         if (peaks) {
05356             peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
05357         }
05358         if (peaks) {
05359             output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05);
05360             if (output) {
05361                 countLines = cpl_bivector_get_size(output);
05362                 if (countLines < 4) {
05363                     cpl_bivector_delete(output);
05364                     cpl_vector_delete(peaks);
05365                     if (nlines)
05366                         nlines[i] = 0;
05367                     if (error)
05368                         error[i] = 0.0;
05369                     continue;
05370                 }
05371 
05372                 /*
05373                  * Set reference wavelength as zero point
05374                  */
05375 
05376                 wavel = cpl_bivector_get_y(output);
05377                 cpl_vector_subtract_scalar(wavel, refwave);
05378 
05379                 uorder = countLines / 2 - 1;
05380                 if (uorder > order)
05381                     uorder = order;
05382 
05383 /* This part is now commented out. In case the first-guess iteration
05384  * was requested, the first fit was made with a lower polynomial degree:
05385  * more robust, and still accurate enough to be used as a first-guess.
05386 
05387                 if (sradius > 0 && uorder > 2)
05388                     --uorder;
05389 
05390  * End of commented part */
05391 
05392                 ids = mos_poly_wav2pix(output, uorder, reject,
05393                                        2 * (uorder + 1), &usedLines,
05394                                        &ids_err);
05395 
05396                 if (ids == NULL) {
05397                     cpl_bivector_delete(output);
05398                     cpl_vector_delete(peaks);
05399                     if (nlines)
05400                         nlines[i] = 0;
05401                     if (error)
05402                         error[i] = 0.0;
05403                     cpl_error_reset();
05404                     continue;
05405                 }
05406 
05407                 if (idscoeff) {
05408 
05409                     /*
05410                      * Write it anyway, even in case a first-guess based
05411                      * solution will be searched afterwards: in case of
05412                      * failure, the "blind" solution is kept.
05413                      */
05414 
05415                     for (k = 0; k <= order; k++) {
05416                         if (k > uorder) {
05417                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05418                         }
05419                         else {
05420                             cpl_table_set_double(idscoeff, clab[k], i,
05421                                       cpl_polynomial_get_coeff(ids, &k));
05422                         }
05423                     }
05424                 }
05425 
05426                 if (sradius > 0) {
05427 
05428                     /*
05429                      * Use ids as a first-guess
05430                      */
05431 
05432                     new_output = mos_find_peaks(sdata + i*nx, nx, lines, 
05433                                                 ids, refwave, uradius);
05434 
05435                     if (new_output) {
05436                         cpl_bivector_delete(output);
05437                         output = new_output;
05438                     }
05439                     else
05440                         cpl_error_reset();
05441 
05442 
05443                     cpl_polynomial_delete(ids);
05444 
05445                     countLines = cpl_bivector_get_size(output);
05446 
05447                     if (countLines < 4) {
05448                         cpl_bivector_delete(output);
05449                         cpl_vector_delete(peaks);
05450 
05451                         /* 
05452                          * With the following code a decision is taken:
05453                          * if using the first-guess gives no results,
05454                          * then also the "blind" solution is rejected.
05455                          */
05456 
05457                         if (nlines)
05458                             nlines[i] = 0;
05459                         if (error)
05460                             error[i] = 0.0;
05461                         if (idscoeff)
05462                             for (k = 0; k <= order; k++)
05463                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05464                         continue;
05465                     }
05466 
05467                     wavel = cpl_bivector_get_y(output);
05468                     cpl_vector_subtract_scalar(wavel, refwave);
05469 
05470                     uorder = countLines / 2 - 1;
05471                     if (uorder > order)
05472                         uorder = order;
05473 
05474                     ids = mos_poly_wav2pix(output, uorder, reject,
05475                                            2 * (uorder + 1), &usedLines,
05476                                            &ids_err);
05477 
05478                     if (ids == NULL) {
05479                         cpl_bivector_delete(output);
05480                         cpl_vector_delete(peaks);
05481 
05482                         /* 
05483                          * With the following code a decision is taken:
05484                          * if using the first-guess gives no results,
05485                          * then also the "blind" solution is rejected.
05486                          */
05487 
05488                         if (nlines)
05489                             nlines[i] = 0;
05490                         if (error)
05491                             error[i] = 0.0;
05492                         if (idscoeff)
05493                             for (k = 0; k <= order; k++)
05494                                 cpl_table_set_invalid(idscoeff, clab[k], i);
05495                         cpl_error_reset();
05496                         continue;
05497                     }
05498 
05499                     if (idscoeff) {
05500                         for (k = 0; k <= order; k++) {
05501                             if (k > uorder) {
05502                                 cpl_table_set_double(idscoeff, clab[k], i, 0.0);
05503                             }
05504                             else {
05505                                 cpl_table_set_double(idscoeff, clab[k], i,
05506                                             cpl_polynomial_get_coeff(ids, &k));
05507                             }
05508                         }
05509                     }
05510 
05511                 } /* End of "use ids as a first-guess" */
05512 
05513                 if (nlines)
05514                     nlines[i] = usedLines;
05515                 if (error)
05516                     error[i] = ids_err / sqrt(usedLines/(uorder + 1));
05517 
05518                 pixstart = cpl_polynomial_eval_1d(ids, 
05519                     cpl_bivector_get_y_data(output)[0], NULL);
05520                 pixend = cpl_polynomial_eval_1d(ids,
05521                     cpl_bivector_get_y_data(output)[countLines-1], NULL);
05522                 extrapolation = (pixend - pixstart) / 5;
05523                 pixstart -= extrapolation;
05524                 pixend += extrapolation;
05525                 if (pixstart < 0)
05526                     pixstart = 0;
05527                 if (pixend > nx)
05528                     pixend = nx;
05529 
05530                 /*
05531                  * Wavelength calibrated image (if requested):
05532                  */
05533 
05534                 if (calibration) {
05535                     for (j = pixstart; j < pixend; j++) {
05536                         (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 
05537                                                          lastLambda, refwave, 
05538                                                          j);
05539                     }
05540                 }
05541 
05542                 /*
05543                  * Resampled image:
05544                  */
05545 
05546                 for (j = 0; j < nl; j++) {
05547                     lambda = firstLambda + j * dispersion;
05548                     fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
05549                                                     NULL);
05550                     pixel = fpixel;
05551                     if (pixel >= 0 && pixel < nx-1) {
05552                         v1 = (sdata + i*nx)[pixel];
05553                         v2 = (sdata + i*nx)[pixel+1];
05554                         vi = v1 + (v2-v1)*(fpixel-pixel);
05555                         (rdata + i*nl)[j] = vi;
05556                     }
05557                 }
05558 
05559                 /*
05560                  * Residuals image
05561                  */
05562 
05563                 if (residuals || (restable && !(i%step))) {
05564                     if (restable && !(i%step)) {
05565                         lin = cpl_polynomial_new(1);
05566                         for (k = 0; k < 2; k++)
05567                             cpl_polynomial_set_coeff(lin, &k, 
05568                                           cpl_polynomial_get_coeff(ids, &k));
05569                     }
05570                     for (j = 0; j < countLines; j++) {
05571                         pixe = cpl_bivector_get_x_data(output)[j];
05572                         wave = cpl_bivector_get_y_data(output)[j];
05573                         value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL);
05574                         if (residuals) {
05575                             pixel = pixe + 0.5;
05576                             (ddata + i*nx)[pixel] = value;
05577                         }
05578                         if (restable && !(i%step)) {
05579                             for (k = 0; k < nref; k++) {
05580                                 if (fabs(line[k] - refwave - wave) < 0.1) {
05581                                     snprintf(name, MAX_COLNAME, "r%d", i);
05582                                     cpl_table_set_double(restable, name, 
05583                                                          k, value);
05584                                     value = pixe
05585                                           - cpl_polynomial_eval_1d(lin, wave,
05586                                                                    NULL);
05587                                     snprintf(name, MAX_COLNAME, "d%d", i);
05588                                     cpl_table_set_double(restable, name, 
05589                                                          k, value);
05590                                     snprintf(name, MAX_COLNAME, "p%d", i);
05591                                     cpl_table_set_double(restable, name,
05592                                                          k, pixe);
05593                                     break;
05594                                 }
05595                             }
05596                         }
05597                     }
05598                     if (restable && !(i%step)) {
05599                         cpl_polynomial_delete(lin);
05600                     }
05601                 }
05602 
05603                 /*
05604                  * Mask at reference wavelength
05605                  */
05606 
05607                 if (refmask) {
05608                     mdata = cpl_mask_get_data(refmask);
05609                     pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5;
05610                     if (pixel - 1 >= 0 && pixel + 1 < nx) {
05611                         mdata[pixel-1 + i*nx] = CPL_BINARY_1;
05612                         mdata[pixel + i*nx] = CPL_BINARY_1;
05613                         mdata[pixel+1 + i*nx] = CPL_BINARY_1;
05614                     }
05615                 }
05616 
05617                 cpl_polynomial_delete(ids);
05618                 cpl_bivector_delete(output);
05619             }
05620             cpl_vector_delete(peaks);
05621         }
05622     }
05623 
05624     if (refmask) {
05625         kernel = cpl_matrix_new(3, 3);
05626         cpl_matrix_set(kernel, 0, 1, 1.0);
05627         cpl_matrix_set(kernel, 1, 1, 1.0);
05628         cpl_matrix_set(kernel, 2, 1, 1.0);
05629 
05630         cpl_mask_dilation(refmask, kernel);
05631         cpl_mask_erosion(refmask, kernel);
05632         cpl_mask_erosion(refmask, kernel);
05633         cpl_mask_dilation(refmask, kernel);
05634 
05635         cpl_matrix_delete(kernel);
05636 
05637         /*
05638          *  Fill possible gaps
05639          */
05640 
05641         mdata = cpl_mask_get_data(refmask);
05642         have_it = cpl_calloc(ny, sizeof(int));
05643 
05644         for (i = 0; i < ny; i++, mdata += nx) {
05645             for (j = 0; j < nx; j++) {
05646                 if (mdata[j] == CPL_BINARY_1) {
05647                     have_it[i] = j;
05648                     break;
05649                 }
05650             }
05651         }
05652 
05653         mdata = cpl_mask_get_data(refmask);
05654         in = 0;
05655         first = last = 0;
05656 
05657         for (i = 0; i < ny; i++) {
05658             if (have_it[i]) {
05659                 if (!in) {
05660                     in = 1;
05661                     if (first) {
05662                         last = i;
05663                         if (abs(have_it[first] - have_it[last]) < 3) {
05664                             for (j = first; j < last; j++) {
05665                                 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1;
05666                                 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1;
05667                                 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1;
05668                             }
05669                         }
05670                     }
05671                 }
05672             }
05673             else {
05674                 if (in) {
05675                     in = 0;
05676                     first = i - 1;
05677                 }
05678             }
05679         }
05680 
05681         cpl_free(have_it);
05682 
05683     }
05684 
05685 /*
05686     for (i = 0; i < ny; i++) {
05687         if (nlines[i] == 0) {
05688             for (k = 0; k <= order; k++) {
05689                 cpl_table_set_invalid(idscoeff, clab[k], i);
05690             }
05691         }
05692     }
05693 */
05694 
05695     return resampled;
05696 }
05697 
05698 
05720 /*
05721 cpl_table *mos_locate_spectra_bis(cpl_mask *mask)
05722 {
05723     const char *func = "mos_locate_spectra_bis";
05724 
05725     cpl_apertures    *slits;
05726     cpl_image        *labimage;
05727     cpl_image        *refimage;
05728     cpl_binary       *mdata;
05729     cpl_table        *slitpos;
05730     cpl_propertylist *sort_col;
05731     int               nslits;
05732     int              *have_it;
05733     int               in, first, last;
05734     int               i, j;
05735 
05736 
05737     if (mask == NULL) {
05738         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05739         return NULL;
05740     }
05741 
05742     nx = cpl_mask_get_size_x(mask);
05743     ny = cpl_mask_get_size_y(mask);
05744 
05745     mdata = cpl_mask_get_data(refmask);
05746     have_it = cpl_calloc(ny, sizeof(int));
05747 
05748     for (i = 0; i < ny; i++, mdata += nx) {
05749         for (j = 0; j < nx; j++) {
05750             if (mdata[j] == CPL_BINARY_1) {
05751                 have_it[i] = j + 1;
05752                 break;
05753             }
05754         }
05755     }
05756 
05757     mdata = cpl_mask_get_data(refmask);
05758     in = 0;
05759     first = last = 0;
05760     nslits = 0;
05761 
05762     for (i = 0; i < ny; i++) {
05763         if (have_it[i]) {
05764             if (in) {
05765                 if (i) {
05766                     if (abs(have_it[i] - have_it[i-1]) > 3) {
05767                         nslits++;
05768                     }
05769                 }
05770             }
05771             else {
05772                 in = 1;
05773                 nslits++;
05774             }
05775         }
05776         else {
05777             if (in) {
05778                 in = 0;
05779             }
05780         }
05781     }
05782 }
05783 */
05784 
05785 
05807 cpl_table *mos_locate_spectra(cpl_mask *mask)
05808 {
05809     const char *func = "mos_locate_spectra";
05810 
05811     cpl_apertures    *slits;
05812     cpl_image        *labimage;
05813     cpl_image        *refimage;
05814     cpl_table        *slitpos;
05815     cpl_propertylist *sort_col;
05816     int               nslits;
05817     int               i;
05818 
05819 
05820     if (mask == NULL) {
05821         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05822         return NULL;
05823     }
05824 
05825     labimage = cpl_image_labelise_mask_create(mask, &nslits);
05826 
05827     if (nslits < 1) {
05828         cpl_image_delete(labimage);
05829         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05830         return NULL;
05831     }
05832 
05833     refimage = cpl_image_new_from_mask(mask);
05834 
05835     slits = cpl_apertures_new_from_image(refimage, labimage);
05836 
05837     cpl_image_delete(labimage);
05838     cpl_image_delete(refimage);
05839 
05840     nslits = cpl_apertures_get_size(slits);  /* Overwriting nslits - safer! */
05841     if (nslits < 1) {
05842         cpl_apertures_delete(slits);
05843         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05844         return NULL;
05845     }
05846 
05847     slitpos = cpl_table_new(nslits);
05848     cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE);
05849     cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE);
05850     cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE);
05851     cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE);
05852     cpl_table_set_column_unit(slitpos, "xtop", "pixel");
05853     cpl_table_set_column_unit(slitpos, "ytop", "pixel");
05854     cpl_table_set_column_unit(slitpos, "xbottom", "pixel");
05855     cpl_table_set_column_unit(slitpos, "ybottom", "pixel");
05856 
05857     for (i = 0; i < nslits; i++) {
05858         cpl_table_set_double(slitpos, "xtop", i, 
05859                              cpl_apertures_get_top_x(slits, i+1) - 1);
05860         cpl_table_set_double(slitpos, "ytop", i, 
05861                              cpl_apertures_get_top(slits, i+1));
05862         cpl_table_set_double(slitpos, "xbottom", i, 
05863                              cpl_apertures_get_bottom_x(slits, i+1) - 1);
05864         cpl_table_set_double(slitpos, "ybottom", i, 
05865                              cpl_apertures_get_bottom(slits, i+1));
05866     }
05867 
05868     cpl_apertures_delete(slits);
05869 
05870     sort_col = cpl_propertylist_new();
05871     cpl_propertylist_append_bool(sort_col, "ytop", 1);
05872     cpl_table_sort(slitpos, sort_col);
05873     cpl_propertylist_delete(sort_col);
05874 
05875     return slitpos;
05876 
05877 }
05878 
05879 
05895 cpl_error_code mos_validate_slits(cpl_table *slits) 
05896 {
05897     const char *func = "mos_validate_slits";
05898 
05899 
05900     if (slits == NULL)
05901         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
05902 
05903     if (1 != cpl_table_has_column(slits, "xtop"))
05904         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05905 
05906     if (1 != cpl_table_has_column(slits, "ytop"))
05907         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05908 
05909     if (1 != cpl_table_has_column(slits, "xbottom"))
05910         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05911 
05912     if (1 != cpl_table_has_column(slits, "ybottom"))
05913         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
05914 
05915     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop"))
05916         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05917 
05918     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop"))
05919         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05920 
05921     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom"))
05922         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05923 
05924     if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom"))
05925         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
05926 
05927     return CPL_ERROR_NONE;
05928 }
05929 
05930 
05959 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny)
05960 {
05961     const char *func = "mos_rotate_slits";
05962 
05963     cpl_error_code error;
05964     char aux_name[] = "_0";
05965     int i;
05966 
05967 
05968     rotation %= 4;
05969     if (rotation < 0)
05970         rotation += 4;
05971 
05972     if (rotation == 0)
05973         return CPL_ERROR_NONE;
05974 
05975     error = mos_validate_slits(slits);
05976     if (error)
05977         return cpl_error_set(func, error);
05978 
05979     if (rotation == 1 || rotation == 3) {
05980 
05981         /*
05982          * Swap x and y column names
05983          */
05984 
05985         for (i = 0; i < 77; i++)
05986             if (1 == cpl_table_has_column(slits, aux_name))
05987                 aux_name[1]++;
05988         if (1 == cpl_table_has_column(slits, aux_name))
05989             return cpl_error_set(func, CPL_ERROR_CONTINUE);
05990         cpl_table_name_column(slits, "xtop", aux_name);
05991         cpl_table_name_column(slits, "ytop", "xtop");
05992         cpl_table_name_column(slits, aux_name, "ytop");
05993         cpl_table_name_column(slits, "xbottom", aux_name);
05994         cpl_table_name_column(slits, "ybottom", "xbottom");
05995         cpl_table_name_column(slits, aux_name, "ybottom");
05996     }
05997 
05998     if (rotation == 1 || rotation == 2) {
05999         cpl_table_multiply_scalar(slits, "xtop", -1.0);
06000         cpl_table_multiply_scalar(slits, "xbottom", -1.0);
06001         cpl_table_add_scalar(slits, "xtop", nx);
06002         cpl_table_add_scalar(slits, "xbottom", nx);
06003     }
06004 
06005     if (rotation == 3 || rotation == 2) {
06006         cpl_table_multiply_scalar(slits, "ytop", -1.0);
06007         cpl_table_multiply_scalar(slits, "ybottom", -1.0);
06008         cpl_table_add_scalar(slits, "ytop", ny);
06009         cpl_table_add_scalar(slits, "ybottom", ny);
06010     }
06011 
06012     return CPL_ERROR_NONE;
06013 }
06014 
06015 
06073 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits,
06074                               cpl_table *global)
06075 {
06076     cpl_array        *top_ident = NULL;;
06077     cpl_array        *bot_ident = NULL;;
06078     cpl_matrix       *mdata;
06079     cpl_matrix       *mpattern;
06080     cpl_matrix       *top_data;
06081     cpl_matrix       *top_pattern;
06082     cpl_matrix       *top_mdata;
06083     cpl_matrix       *top_mpattern;
06084     cpl_matrix       *bot_data;
06085     cpl_matrix       *bot_pattern;
06086     cpl_matrix       *bot_mdata;
06087     cpl_matrix       *bot_mpattern;
06088     cpl_propertylist *sort_col;
06089     double           *xtop;
06090     double           *ytop;
06091     double           *xmtop;
06092     double           *ymtop;
06093     double           *xbot;
06094     double           *ybot;
06095     double           *xmbot;
06096     double           *ymbot;
06097     double            top_scale, bot_scale;
06098     double            angle, top_angle, bot_angle;
06099     double            xmse, ymse;
06100     double            xrms, top_xrms, bot_xrms;
06101     double            yrms, top_yrms, bot_yrms;
06102     int               nslits;
06103     int               nmaskslits, use_pattern;
06104     int               found_slits, found_slits_top, found_slits_bot;
06105     int               degree;
06106     int               i;
06107     cpl_table        *positions;
06108     cpl_error_code    error;
06109 
06110     cpl_vector       *point;
06111     double           *dpoint;
06112     cpl_vector       *xpos;
06113     cpl_vector       *ypos;
06114     cpl_vector       *xmpos;
06115     cpl_vector       *ympos;
06116     cpl_bivector     *mpos;
06117     cpl_polynomial   *xpoly     = NULL;
06118     cpl_polynomial   *ypoly     = NULL;
06119     cpl_polynomial   *top_xpoly = NULL;
06120     cpl_polynomial   *top_ypoly = NULL;
06121     cpl_polynomial   *bot_xpoly = NULL;
06122     cpl_polynomial   *bot_ypoly = NULL;
06123 
06124     char *msg_multiplex = " ";
06125 
06126 
06127     error = mos_validate_slits(slits);
06128     if (error) {
06129         cpl_msg_error(cpl_func, "CCD slits table validation: %s",
06130                       cpl_error_get_message());
06131         cpl_error_set(cpl_func, error);
06132         return NULL;
06133     }
06134 
06135     error = mos_validate_slits(maskslits);
06136     if (error) {
06137         cpl_msg_error(cpl_func, "Mask slits table validation: %s",
06138                       cpl_error_get_message());
06139         cpl_error_set(cpl_func, error);
06140         return NULL;
06141     }
06142 
06143     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06144         cpl_msg_error(cpl_func, "Missing slits identifiers");
06145         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
06146         return NULL;
06147     }
06148 
06149     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06150         cpl_msg_error(cpl_func, "Wrong type used for slits identifiers");
06151         cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE);
06152         return NULL;
06153     }
06154 
06155     nslits = cpl_table_get_nrow(slits);
06156     nmaskslits = cpl_table_get_nrow(maskslits);
06157 
06158     if (nslits == 0 || nmaskslits == 0) {
06159         cpl_msg_error(cpl_func, "Empty slits table");
06160         cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
06161         return NULL;
06162     }
06163 
06164     if (nslits > 25 && mos_multiplex < 0) {
06165         cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching...");
06166         positions = mos_identify_slits_fast(slits, maskslits, global);
06167         if (positions == NULL)
06168             cpl_error_set_where(cpl_func);
06169         return positions;
06170     }
06171 
06172     /*
06173      * Guarantee that both input tables are sorted in the same way
06174      */
06175 
06176     sort_col = cpl_propertylist_new();
06177     cpl_propertylist_append_bool(sort_col, "ytop", 1);
06178     cpl_table_sort(slits, sort_col);
06179     cpl_table_sort(maskslits, sort_col);
06180     cpl_propertylist_delete(sort_col);
06181 
06182     /*
06183      * First we handle all the special cases (too few slits...)
06184      */
06185 
06186     if (nslits < 3 && nmaskslits > nslits) {
06187 
06188         /*
06189          * If there are just 1 or 2 slits on the CCD, and more on the
06190          * mask, the ambiguity cannot be solved, and an error is returned.
06191          * This is a case that must be solved with a first-guess relation
06192          * between mask and CCD.
06193          */
06194 
06195         if (nslits > 1)
06196             cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits "
06197                             "with the %d mask slits: process will continue "
06198                             "using the detected CCD slits positions", nslits,
06199                             nmaskslits);
06200         else
06201             cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with "
06202                             "the %d mask slits: process will continue using "
06203                             "the detected CCD slit position", nmaskslits);
06204         return NULL;
06205     }
06206 
06207     if (nmaskslits < 3 && nslits > nmaskslits) {
06208 
06209         /*
06210          * If there are less than 3 slits on the mask the ambiguity cannot
06211          * be solved, and an error is returned. This is a case that must
06212          * be solved with a first-guess relation between mask and CCD.
06213          */
06214 
06215         cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with "
06216                         "the %d mask slits: process will continue using "
06217                         "the detected CCD slits positions", nslits,
06218                         nmaskslits);
06219         return NULL;
06220     }
06221 
06222     /*
06223      * Pattern matching related operations begin here. Two pattern
06224      * matching will be run, one based on the "top" and another one
06225      * based on the "bottom" slit coordinates. The one with the
06226      * smallest rms in the Y coordinate will be chosen.
06227      */
06228 
06229     xtop  = cpl_table_get_data_double(slits, "xtop");
06230     ytop  = cpl_table_get_data_double(slits, "ytop");
06231     xmtop = cpl_table_get_data_double(maskslits, "xtop");
06232     ymtop = cpl_table_get_data_double(maskslits, "ytop");
06233 
06234     xbot  = cpl_table_get_data_double(slits, "xbottom");
06235     ybot  = cpl_table_get_data_double(slits, "ybottom");
06236     xmbot = cpl_table_get_data_double(maskslits, "xbottom");
06237     ymbot = cpl_table_get_data_double(maskslits, "ybottom");
06238 
06239     top_data    = cpl_matrix_new(2, nslits);
06240     top_pattern = cpl_matrix_new(2, nmaskslits);
06241     bot_data    = cpl_matrix_new(2, nslits);
06242     bot_pattern = cpl_matrix_new(2, nmaskslits);
06243 
06244     for (i = 0; i < nslits; i++)
06245         cpl_matrix_set(top_data, 0, i, xtop[i]);
06246 
06247     for (i = 0; i < nslits; i++)
06248         cpl_matrix_set(top_data, 1, i, ytop[i]);
06249 
06250     for (i = 0; i < nmaskslits; i++)
06251         cpl_matrix_set(top_pattern, 0, i, xmtop[i]);
06252 
06253     for (i = 0; i < nmaskslits; i++)
06254         cpl_matrix_set(top_pattern, 1, i, ymtop[i]);
06255 
06256     for (i = 0; i < nslits; i++)
06257         cpl_matrix_set(bot_data, 0, i, xbot[i]);
06258 
06259     for (i = 0; i < nslits; i++)
06260         cpl_matrix_set(bot_data, 1, i, ybot[i]);
06261 
06262     for (i = 0; i < nmaskslits; i++)
06263         cpl_matrix_set(bot_pattern, 0, i, xmbot[i]);
06264 
06265     for (i = 0; i < nmaskslits; i++)
06266         cpl_matrix_set(bot_pattern, 1, i, ymbot[i]);
06267 
06268     if (nmaskslits > nslits)
06269         use_pattern = nslits;
06270     else
06271         use_pattern = nmaskslits;
06272 
06273     top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern,
06274                                      use_pattern, 0.0, 0.1, 5, &top_mdata,
06275                                      &top_mpattern, &top_scale, &top_angle);
06276 
06277     bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern,
06278                                      use_pattern, 0.0, 0.1, 5, &bot_mdata,
06279                                      &bot_mpattern, &bot_scale, &bot_angle);
06280 
06281     if (top_ident == NULL && bot_ident == NULL) {
06282         cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match "
06283                         "the %d found CCD slits with the %d mask slits: "
06284                         "process will continue using the detected CCD "
06285                         "slits positions", nslits, nmaskslits);
06286         return NULL;
06287     }
06288 
06289     found_slits_top = 0;
06290     found_slits_bot = 0;
06291     if (top_ident && bot_ident) {
06292         cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm",
06293                      (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale));
06294         cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees",
06295                      (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle));
06296         if (fabs(top_angle) < fabs(bot_angle))
06297             angle = fabs(top_angle);
06298         else
06299             angle = fabs(bot_angle);
06300         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06301         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06302     }
06303     else if (top_ident) {
06304         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale);
06305         cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle);
06306         angle = fabs(top_angle);
06307         found_slits_top = cpl_matrix_get_ncol(top_mdata);
06308     }
06309     else {
06310         cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale);
06311         cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle);
06312         angle = fabs(bot_angle);
06313         found_slits_bot = cpl_matrix_get_ncol(bot_mdata);
06314     }
06315 
06316     cpl_array_delete(top_ident);
06317     cpl_array_delete(bot_ident);
06318 
06319     if (angle > 4.0) {
06320         cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation "
06321                         "angle is expected to be around zero. This match is "
06322                         "rejected: the process will continue using the %d "
06323                         "detected CCD slits positions", nslits);
06324         return NULL;
06325     }
06326 
06327     found_slits = found_slits_top;
06328     if (found_slits < found_slits_bot)
06329         found_slits = found_slits_bot;     /* Max value */
06330 
06331     if (found_slits < 4) {
06332         cpl_msg_warning(cpl_func,
06333                         "Too few safely identified slits: %d out of %d "
06334                         "candidates (%d expected). Process will continue "
06335                         "using the detected CCD slits positions", found_slits,
06336                         nslits, nmaskslits);
06337         return NULL;
06338     }
06339 
06340     cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d "
06341                  "candidates\n(%d expected)", found_slits, nslits,
06342                  nmaskslits);
06343 
06344     if (found_slits_top < 4)
06345         found_slits_top = 0;
06346 
06347     if (found_slits_bot < 4)
06348         found_slits_bot = 0;
06349 
06350     /*
06351      * Now for each set select the points of the identified slits, and fit
06352      * two bivariate polynomials to determine a first approximate relation
06353      * between positions on the mask and positions on the CCD.
06354      */
06355 
06356     for (i = 0; i < 2; i++) {
06357         if (i) {
06358             found_slits = found_slits_top;
06359             mdata = top_mdata;
06360             mpattern = top_mpattern;
06361         }
06362         else {
06363             found_slits = found_slits_bot;
06364             mdata = bot_mdata;
06365             mpattern = bot_mpattern;
06366         }
06367 
06368         if (found_slits == 0)
06369             continue;
06370         else if (found_slits < 10)
06371             degree = 1;
06372         else
06373             degree = 2;
06374 
06375         xpos  = cpl_vector_wrap(found_slits,
06376                                 cpl_matrix_get_data(mdata)              );
06377         ypos  = cpl_vector_wrap(found_slits,
06378                                 cpl_matrix_get_data(mdata) + found_slits);
06379         xmpos = cpl_vector_wrap(found_slits,
06380                                 cpl_matrix_get_data(mpattern)              );
06381         ympos = cpl_vector_wrap(found_slits,
06382                                 cpl_matrix_get_data(mpattern) + found_slits);
06383         mpos  = cpl_bivector_wrap_vectors(xmpos, ympos);
06384         xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
06385         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
06386 
06387         cpl_bivector_unwrap_vectors(mpos);
06388         cpl_vector_unwrap(xpos);
06389         cpl_vector_unwrap(ypos);
06390         cpl_vector_unwrap(xmpos);
06391         cpl_vector_unwrap(ympos);
06392         cpl_matrix_delete(mdata);
06393         cpl_matrix_delete(mpattern);
06394 
06395         if (i) {
06396             top_xpoly = xpoly;
06397             top_ypoly = ypoly;
06398             top_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06399             top_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06400         }
06401         else {
06402             bot_xpoly = xpoly;
06403             bot_ypoly = ypoly;
06404             bot_xrms = sqrt(xmse*2*degree/(found_slits - 1));
06405             bot_yrms = sqrt(ymse*2*degree/(found_slits - 1));
06406         }
06407     }
06408 
06409     if (top_xpoly && bot_xpoly) {
06410         if (top_xrms < bot_xrms) {  /* top X solution wins... */
06411             xrms = top_xrms;
06412             xpoly = top_xpoly;
06413             cpl_polynomial_delete(bot_xpoly);
06414         }
06415         else {                      /* bottom X solution wins... */
06416             xrms = bot_xrms;
06417             xpoly = bot_xpoly;
06418             cpl_polynomial_delete(top_xpoly);
06419         }
06420     }
06421     else if (top_xpoly) {
06422         xrms = top_xrms;
06423         xpoly = top_xpoly;
06424     }
06425     else {
06426         xrms = bot_xrms;
06427         xpoly = bot_xpoly;
06428     }
06429 
06430     if (top_ypoly && bot_ypoly) {
06431         if (top_yrms < bot_yrms) {  /* top Y solution wins... */
06432             yrms = top_yrms;
06433             ypoly = top_ypoly;
06434             cpl_polynomial_delete(bot_ypoly);
06435         }
06436         else {                      /* bottom Y solution wins... */
06437             yrms = bot_yrms;
06438             ypoly = bot_ypoly;
06439             cpl_polynomial_delete(top_ypoly);
06440         }
06441     }
06442     else if (top_ypoly) {
06443         yrms = top_yrms;
06444         ypoly = top_ypoly;
06445     }
06446     else {
06447         yrms = bot_yrms;
06448         ypoly = bot_ypoly;
06449     }
06450 
06451     if (xpoly == NULL || ypoly == NULL) {
06452         cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the "
06453                         "identified slits positions cannot be improved.");
06454         cpl_polynomial_delete(xpoly);
06455         cpl_polynomial_delete(ypoly);
06456         cpl_error_reset();
06457         return NULL;
06458     }
06459 
06460     cpl_msg_info(cpl_func,
06461                  "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)",
06462                  xrms, yrms);
06463 
06464     if (global) {
06465         write_global_distortion(global, 0, xpoly);
06466         write_global_distortion(global, 7, ypoly);
06467     }
06468 
06469     /*
06470      * The fit was successful: use the polynomials to obtain a new
06471      * position table with the improved positions of the slits
06472      */
06473 
06474     positions = cpl_table_duplicate(maskslits);
06475     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
06476     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
06477     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
06478     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
06479 
06480     point = cpl_vector_new(2);
06481     dpoint = cpl_vector_get_data(point);
06482 
06483     for (i = 0; i < nmaskslits; i++) {
06484         double position;
06485 
06486         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
06487         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
06488         position  = cpl_polynomial_eval(xpoly, point);
06489 //        if (mos_multiplex >= 0) {
06490 //            if (mos_multiplex != ((int)floor(position)) / mos_region_size) {
06491 //                cpl_table_unselect_row(positions, i);
06492 //                continue;
06493 //            }
06494 //        }
06495         cpl_table_set_double(positions, "xtop", i, position);
06496         position  = cpl_polynomial_eval(ypoly, point);
06497         cpl_table_set_double(positions, "ytop", i, position);
06498         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
06499         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
06500         position  = cpl_polynomial_eval(xpoly, point);
06501         cpl_table_set_double(positions, "xbottom", i, position);
06502         position  = cpl_polynomial_eval(ypoly, point);
06503         cpl_table_set_double(positions, "ybottom", i, position);
06504     }
06505 
06506 //    if (mos_multiplex >= 0) {
06507 //        cpl_table_not_selected(positions);
06508 //        cpl_table_erase_selected(positions);
06509 //        nmaskslits = cpl_table_get_nrow(positions);
06510 //    }
06511 
06512     cpl_vector_delete(point);
06513     cpl_polynomial_delete(xpoly);
06514     cpl_polynomial_delete(ypoly);
06515 
06516     cpl_table_erase_column(positions, "xmtop");
06517     cpl_table_erase_column(positions, "ymtop");
06518     cpl_table_erase_column(positions, "xmbottom");
06519     cpl_table_erase_column(positions, "ymbottom");
06520 
06521     if (mos_multiplex >= 0) {
06522         msg_multiplex = 
06523         cpl_sprintf("in the CCD section between %d and %d pixel", 
06524                     mos_multiplex * mos_region_size, 
06525                     (mos_multiplex + 1) * mos_region_size);
06526     }
06527 
06528     if (nmaskslits > nslits)
06529         cpl_msg_info(cpl_func,
06530                      "Finally identified slits: %d out of %d expected %s\n"
06531                      "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex,
06532                      nmaskslits - nslits);
06533     else if (nmaskslits < nslits)
06534         cpl_msg_info(cpl_func,
06535                      "Finally identified slits: %d out of %d expected %s\n"
06536                      "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex,
06537                      nslits - nmaskslits);
06538     else
06539         cpl_msg_info(cpl_func,
06540                      "Finally identified slits: %d out of %d expected %s",
06541                      nmaskslits, nmaskslits, msg_multiplex);
06542 
06543     if (mos_multiplex >= 0) {
06544         cpl_free(msg_multiplex);
06545     }
06546 
06547     return positions;
06548 
06549 }
06550 
06551 
06552 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits,
06553                                    cpl_table *global)
06554 {
06555     const char *func = "mos_identify_slits_fast";
06556 
06557     cpl_propertylist *sort_col;
06558     cpl_table        *positions;
06559     cpl_vector       *scales;
06560     cpl_vector       *angles;
06561     cpl_vector       *point;
06562     cpl_vector       *xpos;
06563     cpl_vector       *ypos;
06564     cpl_vector       *xmpos;
06565     cpl_vector       *ympos;
06566     cpl_bivector     *mpos;
06567     cpl_polynomial   *xpoly = NULL;
06568     cpl_polynomial   *ypoly = NULL;
06569     cpl_error_code    error;
06570     int nslits;
06571     int nmaskslits;
06572     int found_slits;
06573     int i, j, k;
06574 
06575     double  dist1, dist2, dist3, dist, mindist;
06576     double  scale, minscale, maxscale;
06577     double  angle, minangle, maxangle;
06578     double *dscale;
06579     double *dangle;
06580     double *dpoint;
06581     double *xtop;
06582     double *ytop;
06583     double *xbottom;
06584     double *ybottom;
06585     double *xcenter;
06586     double *ycenter;
06587     double *xpseudo;
06588     double *ypseudo;
06589     int    *slit_id;
06590     double *xmtop;
06591     double *ymtop;
06592     double *xmbottom;
06593     double *ymbottom;
06594     double *xmcenter;
06595     double *ymcenter;
06596     double *xmpseudo;
06597     double *ympseudo;
06598     double  xmse, ymse;
06599     int    *mslit_id;
06600     int    *good;
06601     int     minpos;
06602     int     degree;
06603 
06604     double  sradius = 0.01;   /* Candidate input argument... */
06605     int     in_sradius;
06606 
06607     double pi = 3.14159265358979323846;
06608 
06609 
06610     error = mos_validate_slits(slits);
06611     if (error) {
06612         cpl_msg_error(func, "CCD slits table validation: %s", 
06613                       cpl_error_get_message());
06614         cpl_error_set(func, error);
06615         return NULL;
06616     }
06617 
06618     error = mos_validate_slits(maskslits);
06619     if (error) {
06620         cpl_msg_error(func, "Mask slits table validation: %s", 
06621                       cpl_error_get_message());
06622         cpl_error_set(func, error);
06623         return NULL;
06624     }
06625 
06626     if (1 != cpl_table_has_column(maskslits, "slit_id")) {
06627         cpl_msg_error(func, "Missing slits identifiers");
06628         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
06629         return NULL;
06630     }
06631 
06632     if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) {
06633         cpl_msg_error(func, "Wrong type used for slits identifiers");
06634         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
06635         return NULL;
06636     }
06637 
06638     nslits = cpl_table_get_nrow(slits);
06639     nmaskslits = cpl_table_get_nrow(maskslits);
06640 
06641     if (nslits == 0 || nmaskslits == 0) {
06642         cpl_msg_error(func, "Empty slits table");
06643         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
06644         return NULL;
06645     }
06646 
06647 
06648     /*
06649      * Compute middle point coordinates for each slit listed in both
06650      * input tables.
06651      */
06652 
06653     if (cpl_table_has_column(slits, "xcenter"))
06654         cpl_table_erase_column(slits, "xcenter");
06655 
06656     if (cpl_table_has_column(slits, "ycenter"))
06657         cpl_table_erase_column(slits, "ycenter");
06658 
06659     if (cpl_table_has_column(maskslits, "xcenter"))
06660         cpl_table_erase_column(maskslits, "xcenter");
06661 
06662     if (cpl_table_has_column(maskslits, "ycenter"))
06663         cpl_table_erase_column(maskslits, "ycenter");
06664 
06665     cpl_table_duplicate_column(slits, "xcenter", slits, "xtop");
06666     cpl_table_add_columns(slits, "xcenter", "xbottom");
06667     cpl_table_divide_scalar(slits, "xcenter", 2.0);
06668     cpl_table_duplicate_column(slits, "ycenter", slits, "ytop");
06669     cpl_table_add_columns(slits, "ycenter", "ybottom");
06670     cpl_table_divide_scalar(slits, "ycenter", 2.0);
06671 
06672     cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop");
06673     cpl_table_add_columns(maskslits, "xcenter", "xbottom");
06674     cpl_table_divide_scalar(maskslits, "xcenter", 2.0);
06675     cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop");
06676     cpl_table_add_columns(maskslits, "ycenter", "ybottom");
06677     cpl_table_divide_scalar(maskslits, "ycenter", 2.0);
06678 
06679 
06680     /*
06681      * Guarantee that both input tables are sorted in the same way
06682      */
06683 
06684     sort_col = cpl_propertylist_new();
06685     cpl_propertylist_append_bool(sort_col, "ycenter", 1);
06686     cpl_table_sort(slits, sort_col);
06687     cpl_table_sort(maskslits, sort_col);
06688     cpl_propertylist_delete(sort_col);
06689 
06690 
06691     /*
06692      * First we handle all the special cases (too few slits...)
06693      */
06694 
06695     if (nslits < 3 && nmaskslits > nslits) {
06696 
06697         /*
06698          * If there are just 1 or 2 slits on the CCD, and more on the
06699          * mask, the ambiguity cannot be solved, and an error is returned.
06700          * This is a case that must be solved with a first-guess relation
06701          * between mask and CCD.
06702          */
06703 
06704         if (nslits > 1)
06705             cpl_msg_warning(func, "Cannot match the found CCD slit with the "
06706                             "%d mask slits: process will continue using the "
06707                             "detected CCD slit position", nmaskslits);
06708         else
06709             cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06710                             "the %d mask slits: process will continue using "
06711                             "the detected CCD slits positions", nslits, 
06712                             nmaskslits);
06713         return NULL;
06714     }
06715 
06716     if (nslits <= 3 && nslits == nmaskslits) {
06717 
06718         cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits);
06719         cpl_msg_warning(func, "Their detected positions are left unchanged");
06720 
06721         /*
06722          * If there are just up to 3 slits, both on the mask and on the CCD,
06723          * we can reasonably hope that those slits were found, and accept 
06724          * that their positions on the CCD cannot be improved. We prepare
06725          * therefore an output position table containing the slits with
06726          * their original positions. We can however give an estimate of
06727          * the platescale if there is more than one slit.
06728          */
06729 
06730         positions = cpl_table_duplicate(slits);
06731         cpl_table_erase_column(slits, "xcenter");
06732         cpl_table_erase_column(slits, "ycenter");
06733         cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop");
06734         cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop");
06735         cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom");
06736         cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom");
06737         cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter");
06738         cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter");
06739         cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id");
06740         cpl_table_erase_column(maskslits, "xcenter");
06741         cpl_table_erase_column(maskslits, "ycenter");
06742 
06743         if (nslits > 1) {
06744             xcenter = cpl_table_get_data_double(positions, "xcenter");
06745             ycenter = cpl_table_get_data_double(positions, "ycenter");
06746             xmcenter = cpl_table_get_data_double(positions, "xmcenter");
06747             ymcenter = cpl_table_get_data_double(positions, "ymcenter");
06748 
06749             dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1])
06750                   + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]);
06751             dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1])
06752                   + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]);
06753             scale = sqrt(dist1/dist2);
06754 
06755             if (nslits == 3) {
06756                 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2])
06757                       + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]);
06758                 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2])
06759                       + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]);
06760                 scale += sqrt(dist1/dist2);
06761                 scale /= 2;
06762             }
06763 
06764             cpl_msg_info(func, "Platescale: %f pixel/mm", scale);
06765         }
06766 
06767         return positions;
06768     }
06769 
06770     if (nmaskslits < 3 && nslits > nmaskslits) {
06771 
06772         /*
06773          * If there are less than 3 slits on the mask the ambiguity cannot 
06774          * be solved, and an error is returned. This is a case that must 
06775          * be solved with a first-guess relation between mask and CCD.
06776          */
06777 
06778         cpl_msg_warning(func, "Cannot match the %d found CCD slits with "
06779                         "the %d mask slits: process will continue using "
06780                         "the detected CCD slits positions", nslits, 
06781                         nmaskslits);
06782         return NULL;
06783     }
06784 
06785 
06786     /*
06787      * At this point of the program all the region of the plane
06788      * (nslits, nmaskslits) where either or both mask and CCD display
06789      * less than 3 slits are handled in some way. It would be better
06790      * to add in this place a special handling for identifying slits
06791      * in case of a very reduced number of slits (say, below 6).
06792      * It is also clear that if there are many more slits on the
06793      * mask than on the CCD, or many more on the CCD than on the
06794      * mask, something went deeply wrong with the preliminary 
06795      * wavelength calibration. Such cases should be handled with
06796      * a _complete_ pattern-recognition algorithm based on the
06797      * construction of all possible triangles. For the moment, 
06798      * we go directly to the limited pattern-recognition applied
06799      * below, based on triangles build only for consecutive slits.
06800      * This is reasonably safe, since the preliminary wavelength
06801      * calibration performed by mos_identify_peaks() is generally
06802      * robust.
06803      */
06804 
06805 
06806     /*
06807      * Compute (X, Y) coordinates on pseudo-plane describing the
06808      * different position ratios of successive slits, in both
06809      * input tables.
06810      */
06811 
06812     if (cpl_table_has_column(slits, "xpseudo"))
06813         cpl_table_erase_column(slits, "xpseudo");
06814 
06815     if (cpl_table_has_column(slits, "ypseudo"))
06816         cpl_table_erase_column(slits, "ypseudo");
06817 
06818     if (cpl_table_has_column(maskslits, "xpseudo"))
06819         cpl_table_erase_column(maskslits, "xpseudo");
06820 
06821     if (cpl_table_has_column(maskslits, "ypseudo"))
06822         cpl_table_erase_column(maskslits, "ypseudo");
06823 
06824     cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter");
06825     cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter");
06826 
06827     xcenter = cpl_table_get_data_double(slits, "xcenter");
06828     ycenter = cpl_table_get_data_double(slits, "ycenter");
06829     xpseudo = cpl_table_get_data_double(slits, "xpseudo");
06830     ypseudo = cpl_table_get_data_double(slits, "ypseudo");
06831 
06832     for (i = 1; i < nslits - 1; i++) {
06833         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
06834               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
06835         dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1])
06836               + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]);
06837         dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1])
06838               + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]);
06839         xpseudo[i] = sqrt(dist1/dist2);
06840         ypseudo[i] = sqrt(dist3/dist2);
06841     }
06842 
06843     cpl_table_set_invalid(slits, "xpseudo", 0);
06844     cpl_table_set_invalid(slits, "xpseudo", nslits-1);
06845     cpl_table_set_invalid(slits, "ypseudo", 0);
06846     cpl_table_set_invalid(slits, "ypseudo", nslits-1);
06847 
06848     cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter");
06849     cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter");
06850 
06851     xcenter = cpl_table_get_data_double(maskslits, "xcenter");
06852     ycenter = cpl_table_get_data_double(maskslits, "ycenter");
06853     xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo");
06854     ympseudo = cpl_table_get_data_double(maskslits, "ypseudo");
06855 
06856     for (i = 1; i < nmaskslits - 1; i++) {
06857         dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i])
06858               + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]);
06859         dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1])
06860               + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]);
06861         dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1])
06862               + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]);
06863         xmpseudo[i] = sqrt(dist1/dist2);
06864         ympseudo[i] = sqrt(dist3/dist2);
06865     }
06866     
06867     cpl_table_set_invalid(maskslits, "xpseudo", 0);
06868     cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1);
06869     cpl_table_set_invalid(maskslits, "ypseudo", 0);
06870     cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1);
06871 
06872 
06873     /*
06874      * For each (X, Y) on the pseudo-plane related to the mask positions,
06875      * find the closest (X, Y) on the pseudo-plane related to the CCD
06876      * positions. If the closest point is closer than a given search
06877      * radius, a triangle has been found, and 3 slits are identified.
06878      * However, if more than one point is found within the search
06879      * radius, we have an ambiguity and this is rejected.
06880      */
06881 
06882     if (cpl_table_has_column(slits, "slit_id"))
06883         cpl_table_erase_column(slits, "slit_id");
06884     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
06885     slit_id = cpl_table_get_data_int(maskslits, "slit_id");
06886 
06887     for (i = 1; i < nmaskslits - 1; i++) {
06888         in_sradius = 0;
06889         mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1])
06890                 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]);
06891         minpos = 1;
06892         if (mindist < sradius*sradius)
06893             in_sradius++;
06894         for (j = 2; j < nslits - 1; j++) {
06895             dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j])
06896                  + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]);
06897             if (dist < sradius*sradius)
06898                 in_sradius++;
06899             if (in_sradius > 1)    /* More than one triangle within radius */
06900                 break;
06901             if (mindist > dist) {
06902                 mindist = dist;
06903                 minpos = j;
06904             }
06905         }
06906 
06907         mindist = sqrt(mindist);
06908 
06909         if (mindist < sradius && in_sradius == 1) {
06910             cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]);
06911             cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]);
06912             cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]);
06913         }
06914     }
06915 
06916 
06917     /*
06918      * At this point, the slit_id column contains invalid elements 
06919      * corresponding to unidentified slits.
06920      */
06921 
06922     found_slits = nslits - cpl_table_count_invalid(slits, "slit_id");
06923 
06924     if (found_slits < 3) {
06925         cpl_msg_warning(func, "Too few preliminarily identified slits: "
06926                         "%d out of %d", found_slits, nslits);
06927         if (nslits == nmaskslits) {
06928             cpl_msg_warning(func, "(this is not an error, it could be caused "
06929                             "by a mask with regularly located slits)");
06930             cpl_msg_warning(func, "The detected slits positions are left "
06931                             "unchanged");
06932 
06933             /*
06934              * If there are less than 3 identified slits, this is probably 
06935              * a mask with regularly spaced slits (leading to an ambiguous
06936              * pattern). Only in the case all expected slits appear to have 
06937              * been found on the CCD we can proceed...
06938              */
06939 
06940             cpl_table_erase_column(slits, "slit_id");
06941             cpl_table_erase_column(slits, "xpseudo");
06942             cpl_table_erase_column(slits, "ypseudo");
06943             positions = cpl_table_duplicate(slits);
06944             cpl_table_erase_column(slits, "xcenter");
06945             cpl_table_erase_column(slits, "ycenter");
06946 
06947             cpl_table_erase_column(maskslits, "xpseudo");
06948             cpl_table_erase_column(maskslits, "ypseudo");
06949             cpl_table_duplicate_column(positions, "xmtop", 
06950                                        maskslits, "xtop");
06951             cpl_table_duplicate_column(positions, "ymtop", 
06952                                        maskslits, "ytop");
06953             cpl_table_duplicate_column(positions, "xmbottom", 
06954                                        maskslits, "xbottom");
06955             cpl_table_duplicate_column(positions, "ymbottom", 
06956                                        maskslits, "ybottom");
06957             cpl_table_duplicate_column(positions, "xmcenter", 
06958                                        maskslits, "xcenter");
06959             cpl_table_duplicate_column(positions, "ymcenter", 
06960                                        maskslits, "ycenter");
06961             cpl_table_duplicate_column(positions, "slit_id", 
06962                                        maskslits, "slit_id");
06963             cpl_table_erase_column(maskslits, "xcenter");
06964             cpl_table_erase_column(maskslits, "ycenter");
06965             return positions;
06966         }
06967         else {
06968             cpl_table_erase_column(slits, "slit_id");
06969             cpl_table_erase_column(slits, "xpseudo");
06970             cpl_table_erase_column(slits, "ypseudo");
06971             positions = cpl_table_duplicate(slits);
06972             cpl_table_erase_column(slits, "xcenter");
06973             cpl_table_erase_column(slits, "ycenter");
06974             cpl_msg_warning(func, "(the failure could be caused "
06975                             "by a mask with regularly located slits)");
06976             return NULL;
06977         }
06978     }
06979     else {
06980         cpl_msg_info(func, "Preliminarily identified slits: %d out of %d "
06981                      "candidates (%d expected)", found_slits, nslits, 
06982                      nmaskslits);
06983     }
06984 
06985 
06986     /*
06987      * Create a table with the coordinates of the preliminarily identified 
06988      * slits, both on CCD and mask. The original order of the slits positions 
06989      * is preserved.
06990      */
06991 
06992     positions = cpl_table_new(found_slits);
06993     cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT);
06994     cpl_table_new_column(positions, "xtop",    CPL_TYPE_DOUBLE);
06995     cpl_table_new_column(positions, "ytop",    CPL_TYPE_DOUBLE);
06996     cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE);
06997     cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE);
06998     cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE);
06999     cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE);
07000     cpl_table_new_column(positions, "xmtop",    CPL_TYPE_DOUBLE);
07001     cpl_table_new_column(positions, "ymtop",    CPL_TYPE_DOUBLE);
07002     cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE);
07003     cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE);
07004     cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE);
07005     cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE);
07006     cpl_table_new_column(positions, "good", CPL_TYPE_INT);
07007     cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0);
07008 
07009     slit_id = cpl_table_get_data_int   (slits, "slit_id");
07010     xtop    = cpl_table_get_data_double(slits, "xtop");
07011     ytop    = cpl_table_get_data_double(slits, "ytop");
07012     xbottom = cpl_table_get_data_double(slits, "xbottom");
07013     ybottom = cpl_table_get_data_double(slits, "ybottom");
07014     xcenter = cpl_table_get_data_double(slits, "xcenter");
07015     ycenter = cpl_table_get_data_double(slits, "ycenter");
07016 
07017     mslit_id = cpl_table_get_data_int   (maskslits, "slit_id");
07018     xmtop    = cpl_table_get_data_double(maskslits, "xtop");
07019     ymtop    = cpl_table_get_data_double(maskslits, "ytop");
07020     xmbottom = cpl_table_get_data_double(maskslits, "xbottom");
07021     ymbottom = cpl_table_get_data_double(maskslits, "ybottom");
07022     xmcenter = cpl_table_get_data_double(maskslits, "xcenter");
07023     ymcenter = cpl_table_get_data_double(maskslits, "ycenter");
07024 
07025 
07026     /*
07027      * Transferring the valid slits information to the new table.
07028      * Note that invalid elements are coded as 0 in the internal
07029      * buffer, and this is the way they are recognised and excluded.
07030      */
07031 
07032     k = 0;
07033     cpl_table_fill_invalid_int(slits, "slit_id", 0);
07034     for (i = 0; i < nmaskslits; i++) {
07035         for (j = 0; j < nslits; j++) {
07036             if (slit_id[j] == 0)
07037                 continue; /* Skip invalid slit */
07038             if (mslit_id[i] == slit_id[j]) {
07039                 cpl_table_set_int   (positions, "slit_id",  k, slit_id[j]);
07040 
07041                 cpl_table_set_double(positions, "xtop",     k, xtop[j]);
07042                 cpl_table_set_double(positions, "ytop",     k, ytop[j]);
07043                 cpl_table_set_double(positions, "xbottom",  k, xbottom[j]);
07044                 cpl_table_set_double(positions, "ybottom",  k, ybottom[j]);
07045                 cpl_table_set_double(positions, "xcenter",  k, xcenter[j]);
07046                 cpl_table_set_double(positions, "ycenter",  k, ycenter[j]);
07047 
07048                 cpl_table_set_double(positions, "xmtop",    k, xmtop[i]);
07049                 cpl_table_set_double(positions, "ymtop",    k, ymtop[i]);
07050                 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]);
07051                 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]);
07052                 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]);
07053                 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]);
07054 
07055                 k++;
07056 
07057                 break;
07058             }
07059         }
07060     }
07061 
07062     found_slits = k;
07063 
07064     cpl_table_erase_column(slits, "slit_id");
07065     cpl_table_erase_column(slits, "xpseudo");
07066     cpl_table_erase_column(slits, "ypseudo");
07067     cpl_table_erase_column(slits, "xcenter");
07068     cpl_table_erase_column(slits, "ycenter");
07069     cpl_table_erase_column(maskslits, "xpseudo");
07070     cpl_table_erase_column(maskslits, "ypseudo");
07071     cpl_table_erase_column(maskslits, "xcenter");
07072     cpl_table_erase_column(maskslits, "ycenter");
07073 
07074 
07075     /*
07076      * Find the median platescale and rotation angle from the identified 
07077      * slits, and then exclude slits outlaying more than 10% from the 
07078      * median platescale, and more than 2 degrees from the median
07079      * rotation angle.
07080      */
07081 
07082     ytop    = cpl_table_get_data_double(positions, "ytop");
07083     ybottom = cpl_table_get_data_double(positions, "ybottom");
07084     xcenter = cpl_table_get_data_double(positions, "xcenter");
07085     ycenter = cpl_table_get_data_double(positions, "ycenter");
07086     xmcenter = cpl_table_get_data_double(positions, "xmcenter");
07087     ymcenter = cpl_table_get_data_double(positions, "ymcenter");
07088 
07089     scales = cpl_vector_new(found_slits - 1);
07090     dscale = cpl_vector_get_data(scales);
07091     angles = cpl_vector_new(found_slits - 1);
07092     dangle = cpl_vector_get_data(angles);
07093 
07094     for (i = 1; i < found_slits; i++) {
07095         dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i])
07096               + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]);
07097         dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i])
07098               + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]);
07099         dscale[i-1] = sqrt(dist1/dist2);
07100         dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 
07101                             xcenter[i-1] - xcenter[i])
07102                     - atan2(ymcenter[i-1] - ymcenter[i], 
07103                             xmcenter[i-1] - xmcenter[i]);
07104         dangle[i-1] *= 180;
07105         dangle[i-1] /= pi;
07106     }
07107 
07108     minscale = cpl_vector_get_min(scales);
07109     scale = cpl_vector_get_median_const(scales);
07110     maxscale = cpl_vector_get_max(scales);
07111 
07112     minangle = cpl_vector_get_min(angles);
07113     angle = cpl_vector_get_median_const(angles);
07114     maxangle = cpl_vector_get_max(angles);
07115 
07116     cpl_msg_info(func, "Median platescale: %f pixel/mm", scale);
07117     cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 
07118                  minscale, maxscale);
07119 
07120     cpl_msg_info(func, "Median rotation: %f degrees", angle);
07121     cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 
07122                  minangle, maxangle);
07123 
07124     good = cpl_table_get_data_int(positions, "good");
07125 
07126     good[0] = good[found_slits - 1] = 1;
07127     for (i = 1; i < found_slits; i++) {
07128         if (fabs((dscale[i-1] - scale)/scale) < 0.10
07129          && fabs(dangle[i-1] - angle) < 2) {
07130             good[i-1]++;
07131             good[i]++;
07132         }
07133     }
07134 
07135     for (i = 0; i < found_slits; i++) {
07136         if (good[i] < 2)
07137             good[i] = 0;
07138         else
07139             good[i] = 1;
07140     }
07141 
07142 /*
07143     for (i = 1; i < found_slits; i++)
07144         if (fabs((dscale[i-1] - scale)/scale) < 0.10)
07145             good[i-1] = good[i] = 1;
07146 */
07147 
07148 /* DEBUG ************+
07149     for (i = 0; i < found_slits; i++) {
07150         if (good[i]) {
07151             if (i == found_slits - 1)
07152                 printf("include slit %d, prev = %f, %f\n", 
07153                        i, dscale[i-1], dangle[i-1]);
07154             else if (i == 0)
07155                 printf("include slit %d, next %f, %f\n", 
07156                        i, dscale[i], dangle[i]);
07157             else
07158                 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 
07159                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07160         }
07161         else {
07162             if (i == found_slits - 1)
07163                 printf("EXclude slit %d, prev = %f, %f\n", 
07164                        i, dscale[i-1], dangle[i-1]);
07165             else if (i == 0)
07166                 printf("EXclude slit %d, next %f, %f\n", 
07167                        i, dscale[i], dangle[i]);
07168             else
07169                 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i,    
07170                        dscale[i-1], dangle[i-1], dscale[i], dangle[i]);
07171         }
07172     }
07173 +*********** DEBUG */
07174 
07175     cpl_vector_delete(scales);
07176     cpl_vector_delete(angles);
07177 
07178     cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0);
07179     cpl_table_erase_selected(positions);
07180     cpl_table_erase_column(positions, "good");
07181     found_slits = cpl_table_get_nrow(positions);
07182 
07183     if (found_slits < 4) {
07184 
07185         /*
07186          * If the self-consistency check gives such a poor result,
07187          * something must have gone really wrong in the preliminary
07188          * wavelength calibration... Nothing can be done.
07189          */
07190 
07191         cpl_msg_warning(func, "Too few safely identified slits: %d out of %d "
07192                         "candidates (%d expected). Process will continue "
07193                         "using the detected CCD slits positions", found_slits, 
07194                         nslits, nmaskslits);
07195         cpl_table_delete(positions);
07196         return NULL;
07197     }
07198     else {
07199         cpl_msg_info(func, "Safely identified slits: %d out of %d "
07200                      "candidates\n(%d expected)", found_slits, nslits,
07201                      nmaskslits);
07202     }
07203 
07204 
07205     /*
07206      * Now select the central points of the identified slits, and
07207      * fit two bivariate polynomials to determine a first approximate
07208      * relation between positions on the mask and positions on the CCD.
07209      */
07210 
07211     xpos = cpl_vector_wrap(found_slits, 
07212                            cpl_table_get_data_double(positions, "xcenter"));
07213     ypos = cpl_vector_wrap(found_slits, 
07214                            cpl_table_get_data_double(positions, "ycenter"));
07215     xmpos = cpl_vector_wrap(found_slits, 
07216                             cpl_table_get_data_double(positions, "xmcenter"));
07217     ympos = cpl_vector_wrap(found_slits, 
07218                             cpl_table_get_data_double(positions, "ymcenter"));
07219     mpos = cpl_bivector_wrap_vectors(xmpos, ympos);
07220 
07221     if (found_slits < 10)
07222         degree = 1;
07223     else
07224         degree = 2;
07225 
07226     xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse);
07227     if (xpoly != NULL)
07228         ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse);
07229     cpl_bivector_unwrap_vectors(mpos);
07230     cpl_vector_unwrap(xpos);
07231     cpl_vector_unwrap(ypos);
07232     cpl_vector_unwrap(xmpos);
07233     cpl_vector_unwrap(ympos);
07234     if (ypoly == NULL) {
07235         if (found_slits == nmaskslits) {
07236             cpl_msg_warning(func, "Fit failure: the accuracy of the "
07237                             "identified slits positions is not improved.");
07238 
07239             /*
07240              * The determination of the transformation from mask to CCD
07241              * failed, but since all slits have been already identified
07242              * this is not a problem: data can be reduced also without
07243              * such transformation. Simply the accuracy of the slits 
07244              * positions cannot be improved.
07245              */ 
07246 
07247         } else {
07248             cpl_msg_info(func, "Fit failure: not all slits have been "
07249                          "identified. Process will continue using "
07250                          "the detected CCD slits positions");
07251         }
07252 
07253         cpl_polynomial_delete(xpoly);
07254         return positions;
07255     }
07256 
07257     cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 
07258                  sqrt(xmse), sqrt(ymse));
07259 
07260     if (global) {
07261         write_global_distortion(global, 0, xpoly);
07262         write_global_distortion(global, 7, ypoly);
07263     }
07264 
07265     /*
07266      * The fit was successful: use the polynomials to obtain a new 
07267      * position table with the improved positions of the slits
07268      */
07269 
07270     cpl_table_delete(positions);
07271 
07272     positions = cpl_table_duplicate(maskslits);
07273     cpl_table_duplicate_column(positions, "xmtop", positions, "xtop");
07274     cpl_table_duplicate_column(positions, "ymtop", positions, "ytop");
07275     cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom");
07276     cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom");
07277 
07278     point = cpl_vector_new(2);
07279     dpoint = cpl_vector_get_data(point);
07280 
07281     for (i = 0; i < nmaskslits; i++) {
07282         dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL);
07283         dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL);
07284         cpl_table_set_double(positions, "xtop", i, 
07285                              cpl_polynomial_eval(xpoly, point));
07286         cpl_table_set_double(positions, "ytop", i, 
07287                              cpl_polynomial_eval(ypoly, point));
07288         dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL);
07289         dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL);
07290         cpl_table_set_double(positions, "xbottom", i, 
07291                              cpl_polynomial_eval(xpoly, point));
07292         cpl_table_set_double(positions, "ybottom", i, 
07293                              cpl_polynomial_eval(ypoly, point));
07294     }
07295 
07296     cpl_vector_delete(point);
07297     cpl_polynomial_delete(xpoly);
07298     cpl_polynomial_delete(ypoly);
07299 
07300     cpl_table_erase_column(positions, "xmtop");
07301     cpl_table_erase_column(positions, "ymtop");
07302     cpl_table_erase_column(positions, "xmbottom");
07303     cpl_table_erase_column(positions, "ymbottom");
07304 
07305     if (nmaskslits > nslits)
07306         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07307                  "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits);
07308     else if (nmaskslits < nslits)
07309         cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n"
07310                  "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits);
07311     else
07312         cpl_msg_info(func, "Finally identified slits: %d out of %d expected",
07313                  nmaskslits, nmaskslits);
07314 
07315     return positions;
07316 }
07317 
07318 
07360 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference,
07361                           double blue, double red, double dispersion)
07362 {
07363 
07364     const char  *func = "mos_trace_flat";
07365 
07366     cpl_image   *gradient;
07367     cpl_image   *sgradient;
07368     float       *dgradient;
07369     float        level = 500;   /* It was 250... */
07370     cpl_vector  *row;
07371     cpl_vector  *srow;
07372     cpl_vector **peaks;
07373     double      *peak;
07374     int         *slit_id;
07375     float       *g;
07376     double      *r;
07377     double      *xtop;
07378     double      *ytop;
07379     double      *xbottom;
07380     double      *ybottom;
07381     double       min, dist;
07382     double       sradius;
07383     double       tolerance;
07384     double       start_y, prev_y;
07385     int          minpos;
07386     int          nslits;
07387     int          nrows;
07388     int          step = 10;
07389     int          filtbox = 15;
07390     int          nx, ny, npix;
07391     int          pos, ypos;
07392     int          npeaks;
07393     int          pixel_above, pixel_below;
07394     int          i, j, k, l;
07395     char         trace_id[MAX_COLNAME];
07396 
07397     cpl_table   *traces;
07398 
07399 
07400     if (flat == NULL || slits == NULL) {
07401         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07402         return NULL;
07403     }
07404 
07405     if (dispersion <= 0.0) {
07406         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07407         return NULL;
07408     }
07409 
07410     if (red - blue < dispersion) {
07411         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07412         return NULL;
07413     }
07414 
07415     /*
07416      * Create a dummy slit_id column if it is missing in the
07417      * input slits table
07418      */
07419 
07420     nslits  = cpl_table_get_nrow(slits);
07421     if (1 != cpl_table_has_column(slits, "slit_id")) {
07422         cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
07423         for (i = 0; i < nslits; i++)
07424             cpl_table_set_int(slits, "slit_id", i, -(i+1));  /* it was (i+1) */
07425     }
07426 
07427     slit_id = cpl_table_get_data_int(slits, "slit_id");
07428 
07429     nx = cpl_image_get_size_x(flat);
07430     ny = cpl_image_get_size_y(flat);
07431     npix = nx * ny;
07432 
07433     gradient = cpl_image_duplicate(flat);
07434     dgradient = cpl_image_get_data_float(gradient);
07435 
07436     for (i = 0; i < ny - 1; i++) {
07437         k = i * nx;
07438         for (j = 0; j < nx; j++) {
07439             l = k + j;
07440             dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]);
07441         }
07442     }
07443 
07444     npix--;
07445     for (j = 0; j < nx; j++)
07446         dgradient[npix - j] = 0.0;
07447 
07448     cpl_image_turn(gradient, -1);
07449     nx = cpl_image_get_size_x(gradient);
07450     ny = cpl_image_get_size_y(gradient);
07451     sgradient = mos_image_vertical_median_filter(gradient, 
07452                                                  filtbox, 0, ny, 0, step);
07453     cpl_image_delete(gradient);
07454 
07455 
07456     /*
07457      * Remove background from processed image rows
07458      */
07459 
07460     dgradient = cpl_image_get_data_float(sgradient);
07461 
07462     for (i = 1; i <= ny; i += step) {
07463         row = cpl_vector_new_from_image_row(sgradient, i);
07464         srow = cpl_vector_filter_median_create(row, filtbox);
07465         cpl_vector_subtract(row, srow);
07466         cpl_vector_delete(srow);
07467         g = dgradient + (i-1)*nx;
07468         r = cpl_vector_get_data(row);
07469         for (j = 0; j < nx; j++)
07470             g[j] = r[j];
07471         cpl_vector_delete(row);
07472     }
07473 
07474 
07475     /*
07476      * Rotate (temporarily) the input slits table, to get coordinates
07477      * compatible with the rotated gradient image.
07478      */
07479 
07480     mos_rotate_slits(slits, 1, nx, ny);
07481     xtop    = cpl_table_get_data_double(slits, "xtop");
07482     ytop    = cpl_table_get_data_double(slits, "ytop");
07483     xbottom = cpl_table_get_data_double(slits, "xbottom");
07484     ybottom = cpl_table_get_data_double(slits, "ybottom");
07485 
07486 
07487     /*
07488      * Get positions of peaks candidates for each processed gradient
07489      * image row
07490      */
07491 
07492     peaks = cpl_calloc(ny, sizeof(cpl_vector *));
07493 
07494     for (i = 0; i < ny; i += step) {
07495         g = dgradient + i*nx;
07496         peaks[i] = mos_peak_candidates(g, nx, level, 1.0);
07497 
07498         /* I thought this would be required, but apparently I was wrong.
07499          * Check twice... */
07500         if (peaks[i])
07501             cpl_vector_subtract_scalar(peaks[i], 0.5);
07502          
07503     }
07504 
07505     cpl_image_delete(sgradient);
07506 
07507 
07508     /*
07509      * Tracing the flat field spectra edges, starting from the
07510      * slits positions obtained at reference wavelength. The 
07511      * gradient maximum closest to each slits ends is found and
07512      * accepted only within a given search radius:
07513      */
07514 
07515     sradius = 5.0;  /* Pixel */
07516 
07517     /*
07518      * The tracing proceeds along the processed gradient image rows,
07519      * above and below the start position, finding the closest peak
07520      * to the previous obtained position, accepting the new position
07521      * only if it is closer than a given tolerance:
07522      */
07523 
07524     tolerance = 0.9;  /* Pixel */
07525 
07526     /*
07527      * The trace is attempted for a certain number of pixels above
07528      * and below the position of the reference wavelength:
07529      */
07530 
07531     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
07532     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
07533 
07534 
07535     /*
07536      * Prepare the structure of the output table:
07537      */
07538 
07539     nrows = (ny-1)/step + 1;
07540     traces = cpl_table_new(nrows);
07541     cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE);
07542     cpl_table_set_column_unit(traces, "x", "pixel");
07543     for (i = 0, j = 0; i < ny; i += step, j++)
07544         cpl_table_set(traces, "x", j, i);
07545 
07546     for (i = 0; i < nslits; i++) {
07547 
07548         /*
07549          * Find the closest processed gradient image row
07550          */
07551 
07552         ypos = ytop[i];
07553 
07554         if (ypos < 0)
07555             ypos = 0;
07556         if (ypos >= ny)
07557             ypos = ny - 1;
07558 
07559         pos = ypos / step;
07560         pos *= step;
07561 
07562         /*
07563          * Find the peak in that row that is closest to xtop[i]
07564          */
07565 
07566         if (peaks[pos]) {
07567             peak = cpl_vector_get_data(peaks[pos]);
07568             npeaks = cpl_vector_get_size(peaks[pos]);
07569 
07570             min = fabs(peak[0] - xtop[i]);
07571             minpos = 0;
07572             for (j = 1; j < npeaks; j++) {
07573                 dist = fabs(peak[j] - xtop[i]);
07574                 if (min > dist) {
07575                     min = dist;
07576                     minpos = j;
07577                 }
07578             }
07579         }
07580         else {
07581             npeaks = 0;
07582         }
07583 
07584         snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07585         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07586 
07587         if (min > sradius || npeaks == 0) {
07588             cpl_msg_warning(func, "Cannot find spectrum edge for "
07589                             "top (or left) end of slit %d", slit_id[i]);
07590         }
07591         else {
07592 
07593             /*
07594              * Add to output table the start y position. Note that
07595              * y positions are written in coordinates of the unrotated
07596              * image, i.e., with horizontal dispersion. Currently nx
07597              * is the x size of the temporarily rotated image (for
07598              * faster memory access), but it has the sense of a y size.
07599              */
07600 
07601             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07602             start_y = peak[minpos];
07603 
07604             /*
07605              * Perform the tracing of current edge. Above:
07606              */
07607 
07608             prev_y = start_y;
07609 
07610             for (j = pos + step; j < ny; j += step) {
07611                 if (j - pos > pixel_above)
07612                     break;
07613                 if (peaks[j]) {
07614                     peak = cpl_vector_get_data(peaks[j]);
07615                     npeaks = cpl_vector_get_size(peaks[j]);
07616                     min = fabs(peak[0] - prev_y);
07617                     minpos = 0;
07618                     for (k = 1; k < npeaks; k++) {
07619                         dist = fabs(peak[k] - prev_y);
07620                         if (min > dist) {
07621                             min = dist;
07622                             minpos = k;
07623                         }
07624                     }
07625                     if (min < tolerance) {
07626                         cpl_table_set(traces, trace_id, j/step, 
07627                                       nx - peak[minpos]);
07628                         prev_y = peak[minpos];
07629                     }
07630                 }
07631             }
07632 
07633             /*
07634              * Perform the tracing of current edge. Below:
07635              */
07636 
07637             prev_y = start_y;
07638 
07639             for (j = pos - step; j >= 0; j -= step) {
07640                 if (pos - j > pixel_below)
07641                     break;
07642                 if (peaks[j]) {
07643                     peak = cpl_vector_get_data(peaks[j]);
07644                     npeaks = cpl_vector_get_size(peaks[j]);
07645                     min = fabs(peak[0] - prev_y);
07646                     minpos = 0;
07647                     for (k = 1; k < npeaks; k++) {
07648                         dist = fabs(peak[k] - prev_y);
07649                         if (min > dist) {
07650                             min = dist;
07651                             minpos = k;
07652                         }
07653                     }
07654                     if (min < tolerance) {
07655                         cpl_table_set(traces, trace_id, j/step, 
07656                                       nx - peak[minpos]);
07657                         prev_y = peak[minpos];
07658                     }
07659                 }
07660             }
07661         }
07662 
07663 
07664         /*
07665          * Find the peak in that row that is closest to xbottom[i]
07666          */
07667 
07668         if (peaks[pos]) {
07669             peak = cpl_vector_get_data(peaks[pos]);
07670             npeaks = cpl_vector_get_size(peaks[pos]);
07671     
07672             min = fabs(peak[0] - xbottom[i]);
07673             minpos = 0;
07674             for (j = 1; j < npeaks; j++) {
07675                 dist = fabs(peak[j] - xbottom[i]);
07676                 if (min > dist) {
07677                     min = dist;
07678                     minpos = j;
07679                 }
07680             }
07681         }
07682         else {
07683             npeaks = 0;
07684         }
07685 
07686         snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07687         cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE);
07688 
07689         if (min > sradius || npeaks == 0) {
07690             cpl_msg_warning(func, "Cannot find spectrum edge for "
07691                             "bottom (or right) end of slit %d", slit_id[i]);
07692         }
07693         else {
07694 
07695             cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]);
07696             start_y = peak[minpos]; 
07697 
07698             /*
07699              * Perform the tracing of current edge. Above:
07700              */
07701 
07702             prev_y = start_y;
07703 
07704             for (j = pos + step; j < ny; j += step) {
07705                 if (j - pos > pixel_above)
07706                     break;
07707                 if (peaks[j]) {
07708                     peak = cpl_vector_get_data(peaks[j]);
07709                     npeaks = cpl_vector_get_size(peaks[j]);
07710                     min = fabs(peak[0] - prev_y);
07711                     minpos = 0;
07712                     for (k = 1; k < npeaks; k++) {
07713                         dist = fabs(peak[k] - prev_y);
07714                         if (min > dist) {
07715                             min = dist;
07716                             minpos = k;
07717                         }
07718                     }
07719                     if (min < tolerance) {
07720                         cpl_table_set(traces, trace_id, j/step, 
07721                                       nx - peak[minpos]);
07722                         prev_y = peak[minpos];
07723                     }
07724                 }
07725             }
07726 
07727             /*
07728              * Perform the tracing of current edge. Below:
07729              */
07730 
07731             prev_y = start_y;
07732 
07733             for (j = pos - step; j >= 0; j -= step) {
07734                 if (pos - j > pixel_below)
07735                     break;
07736                 if (peaks[j]) {
07737                     peak = cpl_vector_get_data(peaks[j]);
07738                     npeaks = cpl_vector_get_size(peaks[j]);
07739                     min = fabs(peak[0] - prev_y);
07740                     minpos = 0;
07741                     for (k = 1; k < npeaks; k++) {
07742                         dist = fabs(peak[k] - prev_y);
07743                         if (min > dist) {
07744                             min = dist;
07745                             minpos = k;
07746                         }
07747                     }
07748                     if (min < tolerance) {
07749                         cpl_table_set(traces, trace_id, j/step, 
07750                                       nx - peak[minpos]);
07751                         prev_y = peak[minpos];
07752                     }
07753                 }
07754             }
07755         }
07756 
07757     }   /* End of loop on slits */
07758 
07759     for (i = 0; i < ny; i += step)
07760         cpl_vector_delete(peaks[i]);
07761     cpl_free(peaks);
07762 
07763     /*
07764      * Restore original orientation of slits positions table
07765      */
07766 
07767     mos_rotate_slits(slits, -1, ny, nx);
07768 
07769     return traces;
07770 
07771 }
07772 
07773 
07794 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order)
07795 {
07796     const char *func = "mos_poly_trace";
07797 
07798     cpl_table      *polytraces;
07799     cpl_table      *dummy;
07800     cpl_vector     *x;
07801     cpl_vector     *trace;
07802     cpl_polynomial *polytrace;
07803     char            trace_id[MAX_COLNAME];
07804     char            trace_res[MAX_COLNAME];
07805     char            trace_mod[MAX_COLNAME];
07806     const char     *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07807                                                  /* Max order is 5 */
07808     double         *xdata;
07809     int            *slit_id;
07810     int             nslits;
07811     int             nrows;
07812     int             npoints;
07813     int             i, j, k;
07814 
07815 
07816     if (traces == NULL || slits == NULL) {
07817         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07818         return NULL;
07819     }
07820 
07821     if (order > 5) {
07822         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
07823         return NULL;
07824     }
07825 
07826     nrows   = cpl_table_get_nrow(traces);
07827     xdata   = cpl_table_get_data_double(traces, "x");
07828     nslits  = cpl_table_get_nrow(slits);
07829     slit_id = cpl_table_get_data_int(slits, "slit_id");
07830 
07831     polytraces = cpl_table_new(2*nslits);
07832     cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT);
07833     for (i = 0; i <= order; i++)
07834         cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE);
07835 
07836     for (i = 0; i < nslits; i++) {
07837         for (j = 0; j < 2; j++) {  /* For top and bottom trace of each slit */
07838 
07839             if (j) {
07840                 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]);
07841                 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]);
07842                 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]);
07843             }
07844             else {
07845                 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]);
07846                 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]);
07847                 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]);
07848             }
07849 
07850             cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]);
07851 
07852             /*
07853              * The "dummy" table is just a tool for eliminating invalid
07854              * points from the vectors to be fitted.
07855              */
07856 
07857             dummy = cpl_table_new(nrows);
07858             cpl_table_duplicate_column(dummy, "x", traces, "x");
07859             cpl_table_duplicate_column(dummy, trace_id, traces, trace_id);
07860             npoints = nrows - cpl_table_count_invalid(dummy, trace_id);
07861             if (npoints < 2 * order) {
07862                 cpl_table_delete(dummy);
07863                 continue;
07864             }
07865             cpl_table_erase_invalid(dummy);
07866             x     = cpl_vector_wrap(npoints, 
07867                                     cpl_table_get_data_double(dummy, "x"));
07868             trace = cpl_vector_wrap(npoints, 
07869                                     cpl_table_get_data_double(dummy, trace_id));
07870             polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL);
07871             cpl_vector_unwrap(x);
07872             cpl_vector_unwrap(trace);
07873             cpl_table_delete(dummy);
07874 
07875             /*
07876              * Screen bad solutions. At the moment, a primitive screening
07877              * consists in excluding solutions displaying excessive
07878              * curvature (larger than 1E-5 / pixel)
07879              */
07880 
07881             k = 2;
07882             if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) >  1.E-4) {
07883                 cpl_polynomial_delete(polytrace);
07884                 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07885                 cpl_table_duplicate_column(traces, trace_res, traces, 
07886                                            trace_mod);
07887                 if (j) 
07888                     cpl_msg_warning(func, "Exclude bad curvature solution "
07889                            "for bottom (right) edge of slit %d", slit_id[i]);
07890                 else
07891                     cpl_msg_warning(func, "Exclude bad curvature solution "
07892                                 "for top (left) edge of slit %d", slit_id[i]);
07893                 continue;
07894             }
07895 
07896             /*
07897              * Write polynomial coefficients to the output table,
07898              * tagged with the appropriate slit_id
07899              */
07900 
07901             for (k = 0; k <= order; k++)
07902                 cpl_table_set_double(polytraces, clab[k], 2*i+j, 
07903                                      cpl_polynomial_get_coeff(polytrace, &k));
07904 
07905             /*
07906              * Add column of residuals to input traces table
07907              */
07908 
07909             cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE);
07910             cpl_table_set_column_unit(traces, trace_mod, "pixel");
07911 
07912             for (k = 0; k < nrows; k++) {
07913                 cpl_table_set_double(traces, trace_mod, k,
07914                           cpl_polynomial_eval_1d(polytrace, xdata[k], NULL));
07915             }
07916 
07917             cpl_polynomial_delete(polytrace);
07918 
07919             cpl_table_duplicate_column(traces, trace_res, traces, trace_mod);
07920             cpl_table_subtract_columns(traces, trace_res, trace_id);
07921             cpl_table_multiply_scalar(traces, trace_res, -1.0);
07922 
07923         }
07924     }
07925 
07926     return polytraces;
07927 
07928 }
07929 
07930 
07954 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces,
07955                                 int mode)
07956 {
07957     const char *func = "mos_global_trace";
07958 
07959     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
07960                                                  /* Max order is 5 */
07961     cpl_table      *table;
07962     cpl_vector     *c0;
07963     cpl_vector     *cn;
07964     cpl_bivector   *list;
07965 /* alternative (not robust)
07966     cpl_polynomial *poly;
07967 */
07968 
07969     double *offset;
07970     double  rms, q, m;
07971 
07972     int order, nrows, nslits;
07973     int i, j;
07974 
07975 
07976     if (polytraces == NULL) {
07977         cpl_msg_error(func, "Missing spectral curvature table");
07978         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07979     }
07980 
07981     if (slits == NULL) {
07982         cpl_msg_error(func, "Missing slits positions table");
07983         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
07984     }
07985 
07986     nslits = cpl_table_get_nrow(slits);
07987 
07988     table = cpl_table_duplicate(polytraces);
07989     cpl_table_erase_invalid(table);
07990 
07991     nrows = cpl_table_get_nrow(table);
07992 
07993     if (nrows < 4) {
07994         cpl_msg_warning(func, "Too few successful spectral curvature tracings "
07995                       "(%d): the determination of a global curvature model "
07996                       "failed", nrows);
07997         return CPL_ERROR_NONE;
07998     }
07999     
08000     order = cpl_table_get_ncol(polytraces) - 2;
08001 
08002     for (i = 0; i <= order; i++) {
08003         if (!cpl_table_has_column(table, clab[i])) {
08004             cpl_msg_error(func, "Wrong spectral curvature table");
08005             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08006         }
08007     }
08008 
08009 
08010     /*
08011      * Fill in advance the missing offset terms
08012      */
08013 
08014     for (i = 0; i < nslits; i++) {
08015         if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) {
08016             cpl_table_set_double(polytraces, clab[0], 2*i, 
08017                                 cpl_table_get_double(slits, "ytop", i, NULL));
08018         }
08019         if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) {
08020             cpl_table_set_double(polytraces, clab[0], 2*i+1,
08021                              cpl_table_get_double(slits, "ybottom", i, NULL));
08022         }
08023     }
08024 
08025     offset = cpl_table_get_data_double(polytraces, clab[0]);
08026 
08027 
08028     /*
08029      * Fit the global model and modify polytraces table accordingly
08030      */
08031 
08032     c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0]));
08033 
08034     for (i = 1; i <= order; i++) {
08035         cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i]));
08036         list = cpl_bivector_wrap_vectors(c0, cn);
08037         robustLinearFit(list, &q, &m, &rms);
08038 /* alternative (not robust)
08039         poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL);
08040 */
08041         for (j = 0; j < 2*nslits; j++) {
08042             if (mode == 1)
08043                 if (cpl_table_is_valid(polytraces, clab[i], j))
08044                     continue;
08045             cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q);
08046 /* alternative (not robust)
08047             cpl_table_set_double(polytraces, clab[i], j, 
08048                                  cpl_polynomial_eval_1d(poly, offset[j], NULL));
08049 */
08050         }
08051         cpl_bivector_unwrap_vectors(list);
08052 /* alternative (not robust)
08053         cpl_polynomial_delete(poly);
08054 */
08055         cpl_vector_unwrap(cn);
08056     }
08057 
08058     cpl_vector_unwrap(c0);
08059     cpl_table_delete(table);
08060 
08061     return CPL_ERROR_NONE;
08062 
08063 }
08064 
08065 
08135 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits,
08136                                    cpl_table *polytraces, double reference,
08137                                    double blue, double red, double dispersion,
08138                                    int flux, cpl_image *calibration)
08139 {
08140     const char *func = "mos_spatial_calibration";
08141 
08142     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08143                                                  /* Max order is 5 */
08144     cpl_polynomial *polytop;
08145     cpl_polynomial *polybot;
08146     cpl_image     **exslit;
08147     cpl_image      *resampled;
08148     float          *data;
08149     float          *sdata;
08150     float          *xdata;
08151     double          vtop, vbot, value;
08152     double          top, bot;
08153     double          coeff;
08154     double          ytop, ybot;
08155     double          ypos, yfra;
08156     double          factor;
08157     int             yint, ysize, yprev;
08158     int             nslits;
08159     int             npseudo;
08160     int            *slit_id;
08161     int            *length;
08162     int             nx, ny;
08163     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
08164     int             missing_top, missing_bot;
08165     int             null;
08166     int             order;
08167     int             i, j, k;
08168 
08169     int             create_position = 1;
08170 
08171 
08172     if (spectra == NULL || slits == NULL || polytraces == NULL) {
08173         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08174         return NULL;
08175     }
08176 
08177     if (dispersion <= 0.0) {
08178         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08179         return NULL;
08180     }
08181 
08182     if (red - blue < dispersion) {
08183         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08184         return NULL;
08185     }
08186 
08187     nx = cpl_image_get_size_x(spectra);
08188     ny = cpl_image_get_size_y(spectra);
08189     sdata = cpl_image_get_data(spectra);
08190     if (calibration)
08191         data = cpl_image_get_data(calibration);
08192 
08193     if (cpl_table_has_column(slits, "position"))
08194         create_position = 0;
08195 
08196     if (create_position) {
08197         cpl_table_new_column(slits, "position", CPL_TYPE_INT);
08198         cpl_table_new_column(slits, "length", CPL_TYPE_INT);
08199         cpl_table_set_column_unit(slits, "position", "pixel");
08200         cpl_table_set_column_unit(slits, "length", "pixel");
08201     }
08202     else
08203         length = cpl_table_get_data_int(slits, "length");
08204 
08205     nslits   = cpl_table_get_nrow(slits);
08206     slit_id  = cpl_table_get_data_int(slits, "slit_id");
08207     order    = cpl_table_get_ncol(polytraces) - 2;
08208 
08209     /*
08210      * The spatial resampling is performed for a certain number of 
08211      * pixels above and below the position of the reference wavelength:
08212      */
08213 
08214     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
08215     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
08216 
08217     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
08218 
08219     for (i = 0; i < nslits; i++) {
08220         
08221         if (create_position == 0)
08222             if (length[i] == 0)
08223                 continue;
08224 
08225         /*
08226          * Note that the x coordinate of the reference pixels on the CCD
08227          * is taken arbitrarily at the top end of each slit. This wouldn't
08228          * be entirely correct in case of curved slits, or in presence of
08229          * heavy distortions: in such cases the spatial resampling is
08230          * really performed across a wide range of wavelengths. But
08231          * the lag between top and bottom spectral curvature models 
08232          * would introduce even in such cases negligible effects on
08233          * the spectral spatial resampling.
08234          */
08235 
08236         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
08237 
08238         start_pixel = refpixel - pixel_below;
08239         if (start_pixel < 0)
08240             start_pixel = 0;
08241 
08242         end_pixel = refpixel + pixel_above;
08243         if (end_pixel > nx)
08244             end_pixel = nx;
08245 
08246         /*
08247          * Recover from the table of spectral curvature coefficients
08248          * the curvature polynomials.
08249          */
08250 
08251         missing_top = 0;
08252         polytop = cpl_polynomial_new(1);
08253         for (k = 0; k <= order; k++) {
08254             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
08255             if (null) {
08256                 cpl_polynomial_delete(polytop);
08257                 missing_top = 1;
08258                 break;
08259             }
08260             cpl_polynomial_set_coeff(polytop, &k, coeff);
08261         }
08262 
08263         missing_bot = 0;
08264         polybot = cpl_polynomial_new(1);
08265         for (k = 0; k <= order; k++) {
08266             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
08267             if (null) {
08268                 cpl_polynomial_delete(polybot);
08269                 missing_bot = 1;
08270                 break;
08271             }
08272             cpl_polynomial_set_coeff(polybot, &k, coeff);
08273         }
08274 
08275         if (missing_top && missing_bot) {
08276             cpl_msg_warning(func, "Spatial calibration, slit %d was not "
08277                             "traced: no extraction!", 
08278                             slit_id[i]);
08279             continue;
08280         }
08281 
08282         /*
08283          * In case just one of the two edges was not traced, the other
08284          * edge curvature model is duplicated and shifted to the other
08285          * end of the slit: better than nothing!
08286          */
08287 
08288         if (missing_top) {
08289             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
08290                             "the spectral curvature of the lower edge "
08291                             "is used instead.", slit_id[i]);
08292             polytop = cpl_polynomial_duplicate(polybot);
08293             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08294             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08295             k = 0;
08296             coeff = cpl_polynomial_get_coeff(polybot, &k);
08297             coeff += ytop - ybot;
08298             cpl_polynomial_set_coeff(polytop, &k, coeff);
08299         }
08300 
08301         if (missing_bot) {
08302             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
08303                             "the spectral curvature of the upper edge "
08304                             "is used instead.", slit_id[i]);
08305             polybot = cpl_polynomial_duplicate(polytop);
08306             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
08307             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
08308             k = 0;
08309             coeff = cpl_polynomial_get_coeff(polytop, &k);
08310             coeff -= ytop - ybot;
08311             cpl_polynomial_set_coeff(polybot, &k, coeff);
08312         }
08313 
08314         /*
08315          * Allocate image for current extracted slit
08316          */
08317 
08318         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
08319         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
08320         npseudo = ceil(top-bot) + 1;
08321 
08322         if (npseudo < 1) {
08323             cpl_polynomial_delete(polytop);
08324             cpl_polynomial_delete(polybot);
08325             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
08326                             slit_id[i]);
08327             continue;
08328         }
08329 
08330         exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT);
08331         xdata = cpl_image_get_data(exslit[i]);
08332 
08333         /*
08334          * Write interpolated values to slit image.
08335          */
08336 
08337         for (j = start_pixel; j < end_pixel; j++) {
08338             top = cpl_polynomial_eval_1d(polytop, j, NULL);
08339             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
08340             factor = (top-bot)/npseudo;
08341             for (k = 0; k <= npseudo; k++) {
08342                 ypos = top - k*factor;
08343                 yint = ypos;
08344                 yfra = ypos - yint;
08345                 if (yint >= 0 && yint < ny-1) {
08346                     vtop = sdata[j + nx*yint];
08347                     vbot = sdata[j + nx*(yint+1)];
08348                     value = vtop*(1-yfra) + vbot*yfra;
08349                     if (flux)
08350                         value *= factor;
08351                     xdata[j + nx*(npseudo-k)] = value;
08352                     if (calibration) {
08353                         data[j + nx*yint] = (top-yint)/factor;
08354                         if (k) {
08355 
08356                             /*
08357                              * This is added to recover lost pixels on
08358                              * the CCD image (pixels are lost because
08359                              * the CCD pixels are less than npseudo+1).
08360                              */
08361 
08362                             if (yprev - yint > 1) {
08363                                 data[j + nx*(yint+1)] = (top-yint-1)/factor;
08364                             }
08365                         }
08366                     }
08367                 }
08368                 yprev = yint;
08369             }
08370         }
08371         cpl_polynomial_delete(polytop);
08372         cpl_polynomial_delete(polybot);
08373     }
08374 
08375     /*
08376      * Now all the slits images are copied to a single image
08377      */
08378 
08379     ysize = 0;
08380     for (i = 0; i < nslits; i++)
08381         if (exslit[i])
08382             ysize += cpl_image_get_size_y(exslit[i]);
08383 
08384     resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT);
08385 
08386     yint = -1;
08387     for (i = 0; i < nslits; i++) {
08388         if (exslit[i]) {
08389             yint += cpl_image_get_size_y(exslit[i]);
08390             cpl_image_copy(resampled, exslit[i], 1, ysize - yint);
08391             if (create_position) {
08392                 cpl_table_set_int(slits, "position", i, ysize - yint - 1);
08393                 cpl_table_set_int(slits, "length", i, 
08394                                   cpl_image_get_size_y(exslit[i]));
08395             }
08396             cpl_image_delete(exslit[i]);
08397         }
08398         else if (create_position) {
08399             cpl_table_set_int(slits, "position", i, -1);
08400             cpl_table_set_int(slits, "length", i, 0);
08401         }
08402     }
08403 
08404 
08405     /*
08406      * Elimination of non-traced slits from slit position table: we cannot do
08407      * it because we would lose sync with polytraces and other slit-oriented
08408      * tables. COMMENTED OUT.
08409      * 
08410 
08411     if (create_position) {
08412 
08413         if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1))
08414             cpl_table_erase_selected(slits);
08415 
08416     }
08417 
08418     */
08419 
08420     cpl_free(exslit);
08421 
08422     return resampled;
08423 
08424 }
08425 
08426 
08533 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits,
08534                                             cpl_vector *lines,
08535                                             double dispersion, float level,
08536                                             int sradius, int order,
08537                                             double reject, double refwave,
08538                                             double *wavestart, double *waveend,
08539                                             int *nlines, double *error, 
08540                                             cpl_table *idscoeff,
08541                                             cpl_image *calibration,
08542                                             cpl_image *residuals,
08543                                             cpl_table *restable)
08544 {
08545 
08546     const char *func = "mos_wavelength_calibration_final";
08547 
08548     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
08549                                                  /* Max order is 5 */
08550 
08551     double  tolerance = 20.0;    /* Probably forever...                */
08552     int     step = 10;           /* Compute restable every "step" rows */
08553 
08554     char            name[MAX_COLNAME];
08555 
08556     cpl_image      *resampled;
08557     cpl_bivector   *output;
08558     cpl_vector     *wavel;
08559     cpl_vector     *peaks;
08560     cpl_polynomial *ids;
08561     cpl_polynomial *lin;
08562     cpl_polynomial *fguess;
08563     cpl_table      *coeff;
08564     double          ids_err;
08565     double          max_disp, min_disp;
08566     double         *line;
08567     double          firstLambda, lastLambda, lambda;
08568     double          wave, pixe, value;
08569     double          c;
08570     float          *sdata;
08571     float          *rdata;
08572     float          *idata;
08573     float          *ddata;
08574     float           v1, v2, vi;
08575     float           fpixel;
08576     int            *length;
08577     int             pixstart, pixend;
08578     int             row_top, row_bot;
08579     int             extrapolation;
08580     int             nref;
08581     int             nslits;
08582     int             nfits;
08583     int             nl, nx, ny, pixel;
08584     int             countLines, usedLines;
08585     int             uorder;
08586     int             missing;
08587     int             null;
08588     int             width, uradius;
08589     int             i, j, k, s;
08590 
08591 
08592     if (dispersion == 0.0) {
08593         cpl_msg_error(func, "The expected dispersion (A/pixel) must be given");
08594         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08595         return NULL;
08596     }
08597 
08598     if (dispersion < 0.0) {
08599         cpl_msg_error(func, "The expected dispersion must be positive");
08600         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08601         return NULL;
08602     }
08603 
08604     if (idscoeff == NULL) {
08605         cpl_msg_error(func, "A preallocated IDS coeff table must be given");
08606         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08607         return NULL;
08608     }
08609 
08610     max_disp = dispersion + dispersion * tolerance / 100;
08611     min_disp = dispersion - dispersion * tolerance / 100;
08612 
08613     if (order < 1) {
08614         cpl_msg_error(func, "The order of the fitting polynomial "
08615                       "must be at least 1");
08616         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
08617         return NULL;
08618     }
08619 
08620     if (image == NULL || lines == NULL) {
08621         cpl_msg_error(func, "Both spectral exposure and reference line "
08622                       "catalog are required in input");
08623         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
08624         return NULL;
08625     }
08626 
08627     nx = cpl_image_get_size_x(image);
08628     ny = cpl_image_get_size_y(image);
08629     sdata = cpl_image_get_data_float(image);
08630 
08631     nref = cpl_vector_get_size(lines);
08632     line = cpl_vector_get_data(lines);
08633 
08634     if (*wavestart < 1.0 && *waveend < 1.0) {
08635         firstLambda = line[0];
08636         lastLambda = line[nref-1];
08637         extrapolation = (lastLambda - firstLambda) / 10;
08638         firstLambda -= extrapolation;
08639         lastLambda += extrapolation;
08640         *wavestart = firstLambda;
08641         *waveend = lastLambda;
08642     }
08643     else {
08644         firstLambda = *wavestart;
08645         lastLambda = *waveend;
08646     }
08647 
08648     nl = (lastLambda - firstLambda) / dispersion;
08649     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
08650     rdata = cpl_image_get_data_float(resampled);
08651 
08652     /*
08653      * Allocate total output table of IDS coefficients
08654      */
08655 
08656     for (j = 0; j <= order; j++)
08657         cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE);
08658 
08659     if (calibration)
08660         idata = cpl_image_get_data_float(calibration);
08661 
08662     if (residuals)
08663         ddata = cpl_image_get_data_float(residuals);
08664 
08665     if (restable) {
08666         cpl_table_set_size(restable, nref);
08667         cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE);
08668         cpl_table_copy_data_double(restable, "wavelength", line);
08669         for (i = 0; i < ny; i += step) {
08670              snprintf(name, MAX_COLNAME, "r%d", i);
08671              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08672              snprintf(name, MAX_COLNAME, "d%d", i);
08673              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08674              snprintf(name, MAX_COLNAME, "p%d", i);
08675              cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE);
08676         }
08677     }
08678 
08679 
08680     /*
08681      * Process all slits separately.
08682      */
08683 
08684     nslits   = cpl_table_get_nrow(slits);
08685     length   = cpl_table_get_data_int(slits, "length");
08686 
08687     row_top = ny;
08688     for (s = 0; s < nslits; s++) {
08689 
08690         if (length[s] == 0)
08691             continue;
08692 
08693         /*
08694          * row_top and row_bot define the boundaries of the current slit.
08695          * Here we begin (arbitrarily...) from the top slit.
08696          */
08697 
08698         row_bot = cpl_table_get_int(slits, "position", s, NULL);
08699 
08700         if (sradius > 0) {
08701 
08702             /*
08703              * If a search radius was defined, allocate the table of
08704              * the fitting polynomials coefficients. This table is
08705              * just used to generate the first-guess polynomial made
08706              * of the median coefficients of all polynomials found
08707              * for this slit.
08708              */
08709 
08710             coeff = cpl_table_new(row_top - row_bot);
08711             for (j = 0; j <= order; j++)
08712                 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE);
08713         }
08714 
08715         /*
08716          * Here is the loop on all rows of the current slit. They are
08717          * wavelength calibrated one by one.
08718          */
08719 
08720         for (i = row_bot; i < row_top; i++) {
08721             width = mos_lines_width(sdata + i*nx, nx);
08722             if (width < 5)
08723                 width = 5;
08724             peaks = mos_peak_candidates(sdata + i*nx, nx, level, width);
08725             if (peaks) {
08726                 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width);
08727             }
08728             if (peaks) {
08729                 int keep_multiplex = mos_multiplex;
08730                 mos_multiplex = -1;
08731                 output = mos_identify_peaks(peaks, lines, 
08732                                             min_disp, max_disp, 0.05);
08733                 mos_multiplex = keep_multiplex;
08734                 if (output) {
08735                     countLines = cpl_bivector_get_size(output);
08736                     if (countLines < 4) {
08737                         cpl_bivector_delete(output);
08738                         cpl_vector_delete(peaks);
08739                         if (nlines)
08740                             nlines[i] = 0;
08741                         if (error)
08742                             error[i] = 0.0;
08743                         continue;
08744                     }
08745 
08746                     /*
08747                      * Set reference wavelength as zero point
08748                      */
08749 
08750                     wavel = cpl_bivector_get_y(output);
08751                     cpl_vector_subtract_scalar(wavel, refwave);
08752 
08753                     uorder = countLines / 2 - 1;
08754                     if (uorder > order)
08755                         uorder = order;
08756 
08757                     ids = mos_poly_wav2pix(output, uorder, reject,
08758                                            2 * (uorder + 1), &usedLines,
08759                                            &ids_err);
08760 
08761                     if (ids == NULL) {
08762                         cpl_bivector_delete(output);
08763                         cpl_vector_delete(peaks);
08764                         if (nlines)
08765                             nlines[i] = 0;
08766                         if (error)
08767                             error[i] = 0.0;
08768                         cpl_error_reset();
08769                         continue;
08770                     }
08771 
08772                     if (sradius > 0) {
08773                         for (k = 0; k <= order; k++) {
08774                             if (k > uorder) {
08775                                 cpl_table_set_double(coeff, clab[k], 
08776                                 i - row_bot, 0.0);
08777                             }
08778                             else {
08779                                 cpl_table_set_double(coeff, clab[k], 
08780                                 i - row_bot, cpl_polynomial_get_coeff(ids, &k));
08781                             }
08782                         }
08783                     }
08784                /*   else {   */
08785                         if (calibration) {
08786                             pixstart = cpl_polynomial_eval_1d(ids,
08787                               cpl_bivector_get_y_data(output)[0], 
08788                               NULL);
08789                             pixend = cpl_polynomial_eval_1d(ids,
08790                               cpl_bivector_get_y_data(output)[countLines-1],
08791                               NULL);
08792                             extrapolation = (pixend - pixstart) / 5;
08793                             pixstart -= extrapolation;
08794                             pixend += extrapolation;
08795                             if (pixstart < 0)
08796                                 pixstart = 0;
08797                             if (pixend > nx)
08798                                 pixend = nx;
08799    
08800                             for (j = pixstart; j < pixend; j++) {
08801                                 (idata + i*nx)[j] = mos_eval_dds(ids, 
08802                                      firstLambda, lastLambda, refwave, j);
08803                             }
08804                         }
08805 
08806                         /*
08807                          * Residuals image
08808                          */
08809         
08810                         if (residuals || (restable && !(i%step))) {
08811                             if (restable && !(i%step)) {
08812                                 lin = cpl_polynomial_new(1);
08813                                 for (k = 0; k < 2; k++)
08814                                     cpl_polynomial_set_coeff(lin, &k,
08815                                           cpl_polynomial_get_coeff(ids, &k));
08816                             }
08817                             for (j = 0; j < countLines; j++) {
08818                                 pixe = cpl_bivector_get_x_data(output)[j];
08819                                 wave = cpl_bivector_get_y_data(output)[j];
08820                                 value = pixe 
08821                                       - cpl_polynomial_eval_1d(ids, wave, NULL);
08822                                 if (residuals) {
08823                                     pixel = pixe + 0.5;
08824                                     (ddata + i*nx)[pixel] = value;
08825                                 }
08826                                 if (restable && !(i%step)) {
08827                                     for (k = 0; k < nref; k++) {
08828                                         if (fabs(line[k]-refwave-wave) < 0.1) {
08829                                             snprintf(name, MAX_COLNAME, 
08830                                                      "r%d", i);
08831                                             cpl_table_set_double(restable, name,
08832                                                                  k, value);
08833                                             value = pixe
08834                                                   - cpl_polynomial_eval_1d(lin,
08835                                                               wave, NULL);
08836                                             snprintf(name, MAX_COLNAME, 
08837                                                      "d%d", i);
08838                                             cpl_table_set_double(restable, name,
08839                                                                  k, value);
08840                                             snprintf(name, MAX_COLNAME,
08841                                                      "p%d", i);
08842                                             cpl_table_set_double(restable, name,
08843                                                                  k, pixe);
08844                                             break;
08845                                         }
08846                                     }
08847                                 }
08848                             }
08849                             if (restable && !(i%step)) {
08850                                 cpl_polynomial_delete(lin);
08851                             }
08852 /***
08853                             for (j = 0; j < countLines; j++) {
08854                                 pixel = cpl_bivector_get_x_data(output)[j] 
08855                                       + 0.5;
08856                                 (ddata + i*nx)[pixel] =
08857                                 cpl_bivector_get_x_data(output)[j]
08858                               - cpl_polynomial_eval_1d(ids,
08859                                 cpl_bivector_get_y_data(output)[j], 
08860                                 NULL);
08861                             }
08862 ***/
08863                         }
08864                 /*  }   */
08865 
08866                     /*
08867                      * Write it anyway, even in case a first-guess based
08868                      * solution will be searched afterwards: in case of
08869                      * failure, the "blind" solution is kept.
08870                      */
08871 
08872                     if (nlines)
08873                         nlines[i] = usedLines;
08874                     if (error)
08875                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08876 
08877                     for (k = 0; k <= order; k++) {
08878                         if (k > uorder) {
08879                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
08880                         }
08881                         else {
08882                             cpl_table_set_double(idscoeff, clab[k], i,
08883                                       cpl_polynomial_get_coeff(ids, &k));
08884                         }
08885                     }
08886 
08887                     cpl_polynomial_delete(ids);
08888                     cpl_bivector_delete(output);
08889                 }
08890                 cpl_vector_delete(peaks);
08891             }
08892         }       /* End of loop on current slit's rows */
08893 
08894 
08895         if (sradius > 0) {
08896 
08897             /*
08898              * See whether there are valid fits at all...
08899              */
08900     
08901             nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]);
08902     
08903             if (nfits) {
08904                 int slope = 0;
08905 
08906                 fguess = cpl_polynomial_new(1);
08907 
08908                 if (mos_interpolate_wavecalib(coeff, NULL, 2, 1)) {
08909 
08910                     slope = 0;
08911 
08912                     /*
08913                      * Compute a median IDS polynomial for the current slit
08914                      */
08915 
08916                     for (k = 0; k <= order; k++) {
08917                         c = cpl_table_get_column_median(coeff, clab[k]);
08918                         cpl_polynomial_set_coeff(fguess, &k, c);
08919                     }
08920                 }
08921                 else {
08922                     slope = 1;
08923                 }
08924 
08925                 for (i = row_bot; i < row_top; i++) {
08926 
08927                     /*
08928                      * Use first-guess to find the reference lines again
08929                      */
08930 
08931                     width = mos_lines_width(sdata + i*nx, nx);
08932                     if (width > sradius) {
08933                         uradius = width; 
08934                     }
08935                     else {
08936                         uradius = sradius;
08937                     }
08938 
08939                     if (slope) {
08940                         for (k = 0; k <= order; k++) {
08941                             c = cpl_table_get_double(coeff, clab[k], 
08942                                                      i - row_bot, NULL);
08943                             cpl_polynomial_set_coeff(fguess, &k, c);
08944                         }
08945                     }
08946 
08947                     output = mos_find_peaks(sdata + i*nx, nx, lines, 
08948                                             fguess, refwave, uradius);
08949 
08950                     if (output == NULL) {
08951                         cpl_error_reset();
08952                         continue;
08953                     }
08954 
08955                     countLines = cpl_bivector_get_size(output);
08956 
08957                     if (countLines < 4) {
08958                         cpl_bivector_delete(output);
08959                         continue;
08960                     }
08961 
08962                     /*
08963                      * Set reference wavelength as zero point
08964                      */
08965 
08966                     wavel = cpl_bivector_get_y(output);
08967                     cpl_vector_subtract_scalar(wavel, refwave);
08968 
08969                     uorder = countLines / 2 - 1;
08970                     if (uorder > order)
08971                         uorder = order;
08972 
08973                     ids = mos_poly_wav2pix(output, uorder, reject,
08974                                            2 * (uorder + 1), &usedLines,
08975                                            &ids_err);
08976 
08977                     if (ids == NULL) {
08978                         cpl_error_reset();
08979                         cpl_bivector_delete(output);
08980                         continue;
08981                     }
08982 
08983                     if (nlines)
08984                         nlines[i] = usedLines;
08985                     if (error)
08986                         error[i] = ids_err / sqrt(usedLines/(uorder + 1));
08987 
08988                     if (calibration) {
08989                         pixstart = cpl_polynomial_eval_1d(ids,
08990                            cpl_bivector_get_y_data(output)[0], 
08991                            NULL);
08992                         pixend = cpl_polynomial_eval_1d(ids,
08993                            cpl_bivector_get_y_data(output)[countLines-1], 
08994                            NULL);
08995                         extrapolation = (pixend - pixstart) / 5;
08996                         pixstart -= extrapolation;
08997                         pixend += extrapolation;
08998                         if (pixstart < 0)
08999                             pixstart = 0;
09000                         if (pixend > nx)
09001                             pixend = nx;
09002 
09003                         for (j = pixstart; j < pixend; j++) {
09004                             (idata + i*nx)[j] = mos_eval_dds(ids,
09005                                      firstLambda, lastLambda, refwave, j);
09006                         }
09007                     }
09008 
09009                     /*
09010                      * Residuals image
09011                      */
09012 
09013                     if (residuals || (restable && !(i%step))) {
09014                         if (restable && !(i%step)) {
09015                             lin = cpl_polynomial_new(1);
09016                             for (k = 0; k < 2; k++)
09017                                 cpl_polynomial_set_coeff(lin, &k,
09018                                       cpl_polynomial_get_coeff(ids, &k));
09019                         }
09020                         for (j = 0; j < countLines; j++) {
09021                             pixe = cpl_bivector_get_x_data(output)[j];
09022                             wave = cpl_bivector_get_y_data(output)[j];
09023                             value = pixe
09024                                   - cpl_polynomial_eval_1d(ids, wave, NULL);
09025                             if (residuals) {
09026                                 pixel = pixe + 0.5;
09027                                 (ddata + i*nx)[pixel] = value;
09028                             }
09029                             if (restable && !(i%step)) {
09030                                 for (k = 0; k < nref; k++) {
09031                                     if (fabs(line[k]-refwave-wave) < 0.1) {
09032                                         snprintf(name, MAX_COLNAME,
09033                                                  "r%d", i);
09034                                         cpl_table_set_double(restable, name,
09035                                                              k, value);
09036                                         value = pixe
09037                                               - cpl_polynomial_eval_1d(lin,
09038                                                           wave, NULL);
09039                                         snprintf(name, MAX_COLNAME,
09040                                                  "d%d", i);
09041                                         cpl_table_set_double(restable, name,
09042                                                              k, value);
09043                                         snprintf(name, MAX_COLNAME,
09044                                                  "p%d", i);
09045                                         cpl_table_set_double(restable, name,
09046                                                              k, pixe);
09047                                         break;
09048                                     }
09049                                 }
09050                             }
09051                         }
09052                         if (restable && !(i%step)) {
09053                             cpl_polynomial_delete(lin);
09054                         }
09055 /***
09056                         for (j = 0; j < countLines; j++) {
09057                             pixel = cpl_bivector_get_x_data(output)[j]
09058                                   + 0.5; 
09059                             (ddata + i*nx)[pixel] =
09060                             cpl_bivector_get_x_data(output)[j]
09061                           - cpl_polynomial_eval_1d(ids,
09062                             cpl_bivector_get_y_data(output)[j], 
09063                             NULL);
09064                         }
09065 ***/
09066                     }
09067 
09068                     for (k = 0; k <= order; k++) {
09069                         if (k > uorder) {
09070                             cpl_table_set_double(idscoeff, clab[k], i, 0.0);
09071                         }
09072                         else {
09073                             cpl_table_set_double(idscoeff, clab[k], i,
09074                                         cpl_polynomial_get_coeff(ids, &k));
09075                         }
09076                     }
09077 
09078                     cpl_bivector_delete(output);
09079                     cpl_polynomial_delete(ids);
09080 
09081                 } /* End of loop "use ids as a first-guess" */
09082 
09083                 cpl_polynomial_delete(fguess);
09084             }
09085 
09086             cpl_table_delete(coeff);
09087 
09088         }
09089 
09090         row_top = row_bot;
09091 
09092     } /* End of loop on slits */
09093 
09094 
09095     /*
09096      * At this point the idscoeff table has been filled with all the 
09097      * fits coefficients obtained for all the rows of the input image.
09098      * Now we apply these coefficients to resample the input image
09099      * at constant wavelength step.
09100      */
09101 
09102     for (i = 0; i < ny; i++) {
09103 
09104         missing = 0;
09105         ids = cpl_polynomial_new(1);
09106         for (k = 0; k <= order; k++) {
09107             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09108             if (null) {
09109                 cpl_polynomial_delete(ids);
09110                 missing = 1;
09111                 break;
09112             }
09113             cpl_polynomial_set_coeff(ids, &k, c);
09114         }
09115         if (missing)
09116             continue;
09117 
09118         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09119         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09120         if (pixstart < 0)
09121             pixstart = 0;
09122         if (pixend > nx)
09123             pixend = nx;
09124 
09125         /*
09126          * Resampled image:
09127          */
09128 
09129         for (j = 0; j < nl; j++) {
09130             lambda = firstLambda + j * dispersion;
09131             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL);
09132             pixel = fpixel;
09133             if (pixel >= 0 && pixel < nx-1) {
09134                 v1 = (sdata + i*nx)[pixel];
09135                 v2 = (sdata + i*nx)[pixel+1];
09136                 vi = v1 + (v2-v1)*(fpixel-pixel);
09137                 (rdata + i*nl)[j] = vi;
09138             }
09139         }
09140 
09141         cpl_polynomial_delete(ids);
09142     }
09143 
09144     return resampled;
09145 }
09146 
09147 
09174 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 
09175                                       double firstLambda, double lastLambda, 
09176                                       double dispersion, cpl_table *idscoeff, 
09177                                       int flux)
09178 {
09179 
09180     const char *func = "mos_wavelength_calibration";
09181 
09182     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09183                                                  /* Max order is 5 */
09184 
09185     cpl_image      *resampled;
09186     cpl_polynomial *ids;
09187     double          pixel_per_lambda;
09188     double          lambda;
09189     double          c;
09190     float          *sdata;
09191     float          *rdata;
09192     float           v0, v1, v2, v3, vi;
09193     float           fpixel;
09194     int             order;
09195     int             pixstart, pixend;
09196     int             nl, nx, ny, pixel;
09197     int             missing;
09198     int             null;
09199     int             i, j, k;
09200 
09201 
09202     if (dispersion <= 0.0) {
09203         cpl_msg_error(func, "The resampling step must be positive");
09204         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09205         return NULL;
09206     }
09207 
09208     if (lastLambda - firstLambda < dispersion) {
09209         cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 
09210                       firstLambda, lastLambda);
09211         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
09212         return NULL;
09213     }
09214 
09215     if (idscoeff == NULL) {
09216         cpl_msg_error(func, "An IDS coeff table must be given");
09217         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09218         return NULL;
09219     }
09220 
09221     if (image == NULL) {
09222         cpl_msg_error(func, "A scientific spectral image must be given");
09223         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09224         return NULL;
09225     }
09226 
09227     nx = cpl_image_get_size_x(image);
09228     ny = cpl_image_get_size_y(image);
09229     sdata = cpl_image_get_data_float(image);
09230 
09231     nl = (lastLambda - firstLambda) / dispersion;
09232     resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT);
09233     rdata = cpl_image_get_data_float(resampled);
09234 
09235     order = 0;
09236     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
09237         ++order;
09238     --order;
09239 
09240     for (i = 0; i < ny; i++) {
09241 
09242         missing = 0;
09243         ids = cpl_polynomial_new(1);
09244         for (k = 0; k <= order; k++) {
09245             c = cpl_table_get_double(idscoeff, clab[k], i, &null);
09246             if (null) {
09247                 cpl_polynomial_delete(ids);
09248                 missing = 1;
09249                 break;
09250             }
09251             cpl_polynomial_set_coeff(ids, &k, c);
09252         }
09253         if (missing)
09254             continue;
09255 
09256         pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL);
09257         pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL);
09258         if (pixstart < 0)
09259             pixstart = 0;
09260         if (pixend > nx)
09261             pixend = nx;
09262 
09263         /*
09264          * Resampled image:
09265          */
09266 
09267         for (j = 0; j < nl; j++) {
09268             lambda = firstLambda + j * dispersion;
09269             fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 
09270                                             &pixel_per_lambda);
09271 
09272             /*
09273              * The local dispersion is 1 / pixel_per_lambda
09274              * and this factor is used for applying the flux
09275              * conservation correction (if requested).
09276              */
09277 
09278             pixel = fpixel;
09279 
09280          // if (dispersion * pixel_per_lambda < 2.0) {
09281             if (1) {  /* Old behaviour: this is safe. */
09282 
09283                 /*
09284                  * In this case we just sample interpolating the
09285                  * signal of nearby pixels.
09286                  */
09287 
09288                if (pixel >= 1 && pixel < nx-2) {
09289                    v0 = (sdata + i*nx)[pixel-1];
09290                    v1 = (sdata + i*nx)[pixel];
09291                    v2 = (sdata + i*nx)[pixel+1];
09292                    v3 = (sdata + i*nx)[pixel+2];
09293                    vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0)
09294                       + (fpixel-pixel)*(3*v2 - v3 - v1 - v0)
09295                       + 2*v1;
09296                    vi /= 2;
09297                    if (v1 > v2) {
09298                        if (vi > v1) { 
09299                            vi = v1;
09300                        }
09301                        else if (vi < v2) {
09302                            vi = v2;
09303                        }
09304                    }
09305                    else {
09306                        if (vi > v2) { 
09307                            vi = v2;
09308                        }
09309                        else if (vi < v1) {
09310                            vi = v1;
09311                        }
09312                    }
09313                    if (flux)
09314                        vi *= dispersion * pixel_per_lambda;
09315                    (rdata + i*nl)[j] = vi;
09316                }
09317                else if (pixel >= 0 && pixel < nx-1) {
09318                    v1 = (sdata + i*nx)[pixel];
09319                    v2 = (sdata + i*nx)[pixel+1];
09320                    vi = v1 + (v2-v1)*(fpixel-pixel);
09321                    if (flux)
09322                        vi *= dispersion * pixel_per_lambda;
09323                    (rdata + i*nl)[j] = vi;
09324                }
09325            }
09326            else {
09327 
09328                /*
09329                 * Here instead we integrate the pixel values in
09330                 * the interval centered at the interpolation point.
09331                 * This interval is long dispersion * pixel_per_lambda
09332                 * of the original pixels, and is centered at fpixel.
09333                 * So it starts at fpixel - dispersion * pixel_per_lambda / 2,
09334                 * and it ends at fpixel + dispersion * pixel_per_lambda / 2.
09335                 */
09336 
09337                double spos = fpixel - dispersion * pixel_per_lambda / 2;
09338                double epos = fpixel + dispersion * pixel_per_lambda / 2;
09339 
09340                /*
09341                 * Brutal sum over all involved pixels
09342                 */
09343 
09344                int spix = spos;
09345                int epix = epos + 1;
09346 
09347                if (spix < 0)
09348                    spix = 0;
09349 
09350                if (epix > nx)
09351                    epix = nx;
09352 
09353                vi = 0.0;
09354                for (k = spix; k < epix; k++) {
09355                    if (pixel >= 0 && pixel < nx) {
09356                        vi += (sdata + i*nx)[k];
09357                    }
09358                }
09359 
09360                /*
09361                 * Correct integrated flux by true length
09362                 * of interval. This is clearly an approximation,
09363                 * but it's good enough if the PSF is much larger
09364                 * than the original pix.
09365                 */
09366 
09367                vi *= dispersion * pixel_per_lambda / (epix - spix);
09368 
09369                 /*
09370                  * Flux conservation is a geometric factor that is applied
09371                  * always in the same way...
09372                  */
09373 
09374                if (flux)
09375                    vi *= dispersion * pixel_per_lambda;
09376 
09377                (rdata + i*nl)[j] = vi;
09378             }
09379         }
09380 
09381         cpl_polynomial_delete(ids);
09382     }
09383 
09384     return resampled;
09385 }
09386 
09387 
09454 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 
09455                                 double refwave, double firstLambda, 
09456                                 double lastLambda, cpl_table *idscoeff,
09457                                 cpl_vector *skylines, int highres, int order,
09458                                 cpl_image *calibration, int sradius)
09459 {
09460     const char *func = "mos_wavelength_align";
09461 
09462     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
09463                                                  /* Max order is 5 */
09464     double         *line;
09465     double         *data;
09466     double          expPos, offset;
09467     double          c;
09468     double          lambda1, lambda2;
09469     double          rms;
09470     float           pos;
09471     float          *sdata;
09472     float          *cdata;
09473     int            *idata;
09474     int             startPos, endPos;
09475     int             window = 2*sradius + 1;
09476     int             nlines;
09477     int             nslits;
09478     int             npoints;
09479     int             nrows;
09480     int             nx, ny;
09481     int             xlow, ylow, xhig, yhig;
09482     int             idsorder, uorder;
09483     int            *slit_id;
09484     int            *position;
09485     int            *length;
09486     int             missing;
09487     int             null;
09488     int             i, j, k;
09489 
09490     char            offname[MAX_COLNAME];
09491     char            name[MAX_COLNAME];
09492 
09493     cpl_polynomial *ids;
09494     cpl_polynomial *polycorr;
09495     cpl_image      *exslit;
09496     cpl_image      *sky;
09497     cpl_table      *offsets;
09498     cpl_table      *dummy;
09499     cpl_vector     *wave;
09500     cpl_vector     *offs;
09501     
09502 
09503     if (idscoeff == NULL) {
09504         cpl_msg_error(func, "An IDS coeff table must be given");
09505         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09506         return NULL;
09507     }
09508 
09509     if (image == NULL) {
09510         cpl_msg_error(func, "A scientific spectral image must be given");
09511         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09512         return NULL;
09513     }
09514 
09515     if (slits == NULL) {
09516         cpl_msg_error(func, "A slit position table must be given");
09517         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
09518         return NULL;
09519     }
09520 
09521     if (skylines) {
09522         line = cpl_vector_get_data(skylines);
09523         nlines = cpl_vector_get_size(skylines);
09524     }
09525     else {
09526         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
09527                         "given: using internal list of reference sky lines");
09528         if (highres) {
09529            line = default_lines_hi;
09530            nlines = sizeof(default_lines_hi) / sizeof(double);
09531         }
09532         else {
09533            line = default_lines_lo;
09534            nlines = sizeof(default_lines_lo) / sizeof(double);
09535         }
09536     }
09537 
09538     if (calibration)
09539         cdata = cpl_image_get_data(calibration);
09540 
09541     nx = cpl_image_get_size_x(image);
09542     ny = cpl_image_get_size_y(image);
09543 
09544     nslits   = cpl_table_get_nrow(slits);
09545     slit_id  = cpl_table_get_data_int(slits, "slit_id");
09546     position = cpl_table_get_data_int(slits, "position");
09547     length   = cpl_table_get_data_int(slits, "length");
09548 
09549 
09550     /*
09551      * Define the output table of offsets
09552      */
09553 
09554     nrows = 0;
09555     for (i = 0; i < nlines; i++)
09556         if (line[i] > firstLambda && line[i] < lastLambda)
09557             nrows++;
09558 
09559     offsets = cpl_table_new(nrows);
09560     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
09561     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
09562 
09563     nrows = 0;
09564     for (i = 0; i < nlines; i++) {
09565         if (line[i] > firstLambda && line[i] < lastLambda) {
09566             cpl_table_set_double(offsets, "wave", nrows, line[i]);
09567             nrows++;
09568         }
09569     }
09570 
09571     /*
09572      * Here "line" is made to point to the new list of selected wavelengths
09573      */
09574 
09575     line = cpl_table_get_data_double(offsets, "wave");
09576     nlines = nrows;
09577 
09578     idsorder = 0;
09579     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
09580         ++idsorder;
09581     --idsorder;
09582 
09583     xlow = 1;
09584     xhig = nx;
09585     for (i = 0; i < nslits; i++) {
09586 
09587         if (length[i] == 0)
09588             continue;
09589 
09590         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09591         cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE);
09592 
09593         /* 
09594          * Define the extraction boundaries. We DON'T write:
09595          *
09596          * ylow = position[i];
09597          * yhig = ylow + length[i];
09598          *
09599          * because the cpl_image pixels are counted from 1, and because in
09600          * cpl_image_extract() the coordinates of the last pixel are inclusive.
09601          */
09602 
09603         ylow = position[i] + 1;
09604         yhig = ylow + length[i] - 1;
09605 
09606         exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig);
09607         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
09608         sdata  = cpl_image_get_data(sky);
09609 
09610         cpl_image_delete(exslit);
09611 
09612         /* 
09613          * Return here to a decent way of counting pixels (i.e., starting
09614          * from 0)
09615          */
09616          
09617         ylow--;
09618 
09619         /*
09620          * Allocate a dummy table for collecting all the offsets
09621          * for all the lines: this is only needed for the computation
09622          * of the median offset for each sky line
09623          */
09624 
09625         dummy = cpl_table_new(yhig - ylow);
09626         for (j = 0; j < nlines; j++) {
09627             snprintf(name, MAX_COLNAME, "%d", j);
09628             cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
09629         }
09630 
09631         for (j = ylow; j < yhig; j++) {
09632 
09633             /*
09634              * Get the IDS polynomial for the current slit row
09635              */
09636 
09637             missing = 0;
09638             ids = cpl_polynomial_new(1);
09639             for (k = 0; k <= idsorder; k++) {
09640                 c = cpl_table_get_double(idscoeff, clab[k], j, &null);
09641                 if (null) {
09642                     cpl_polynomial_delete(ids);
09643                     missing = 1;
09644                     break;
09645                 }
09646                 cpl_polynomial_set_coeff(ids, &k, c);
09647             }
09648             if (missing)
09649                 continue;
09650 
09651             for (k = 0; k < nlines; k++) {
09652                 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
09653                 startPos = expPos - sradius;
09654                 endPos   = startPos + window;
09655                 if (startPos < 0 || endPos >= nx)
09656                     continue;
09657            
09658                 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
09659                     pos += startPos;
09660                     offset = pos - expPos;
09661                     snprintf(name, MAX_COLNAME, "%d", k);
09662                     cpl_table_set_double(dummy, name, j - ylow, offset);
09663                 }
09664             }
09665 
09666             cpl_polynomial_delete(ids);
09667         }
09668 
09669         cpl_image_delete(sky);
09670 
09671         for (j = 0; j < nlines; j++) {
09672             snprintf(name, MAX_COLNAME, "%d", j);
09673             if (cpl_table_has_valid(dummy, name)) {
09674                 offset = cpl_table_get_column_median(dummy, name);
09675                 cpl_table_set_double(offsets, offname, j, offset);
09676             }
09677         }
09678 
09679         cpl_table_delete(dummy);
09680 
09681     }
09682 
09683 
09684     /*
09685      * In the following the input idscoeff table is modified by simply
09686      * adding the coefficients of the polynomial used to fit the sky
09687      * line residuals to the coefficients of the IDS polynomials.
09688      */
09689 
09690     for (i = 0; i < nslits; i++) {
09691 
09692         if (length[i] == 0)
09693             continue;
09694 
09695         snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]);
09696 
09697         /*
09698          * In the following, the "dummy" table is just a tool for
09699          * eliminating invalid points from the vectors to be fitted.
09700          */
09701 
09702         dummy = cpl_table_new(nlines);
09703         cpl_table_duplicate_column(dummy, "wave", offsets, "wave");
09704         cpl_table_duplicate_column(dummy, "offset", offsets, offname);
09705 
09706         npoints = nlines - cpl_table_count_invalid(dummy, "offset");
09707         if (npoints == 0) {
09708             cpl_msg_warning(func, "No sky lines alignment was possible "
09709                             "for slit ID=%d: no sky line found", slit_id[i]);
09710             cpl_table_delete(dummy);
09711             continue;
09712         }
09713 
09714         uorder = order;
09715         if (npoints <= uorder) {
09716             uorder = npoints - 1;
09717             if (uorder) {
09718                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09719                                 "ID=%d, while a polynomial order %d was "
09720                                 "requested. Using polynomial order %d for "
09721                                 "this slit!", npoints, slit_id[i], order, 
09722                                 uorder);
09723             }
09724             else {
09725                 cpl_msg_warning(func, "Just %d sky lines detected for slit "
09726                                 "ID=%d, while a polynomial order %d was "
09727                                 "requested. Computing a median offset for "
09728                                 "this slit!", npoints, slit_id[i], order);
09729             }
09730         }
09731 
09732         cpl_table_erase_invalid(dummy);
09733 
09734         if (uorder > 1) {
09735 
09736             /*
09737              * Model offsets with polynomial fitting
09738              */
09739 
09740             wave = cpl_vector_wrap(npoints,
09741                                    cpl_table_get_data_double(dummy, "wave"));
09742             offs = cpl_vector_wrap(npoints,
09743                                    cpl_table_get_data_double(dummy, "offset"));
09744 
09745             /*
09746              * Set reference wavelength as zero point
09747              */
09748 
09749             cpl_vector_subtract_scalar(wave, refwave);
09750 
09751             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
09752 
09753             rms = sqrt(rms * (uorder + 1) / npoints);
09754 
09755             cpl_vector_unwrap(wave);
09756             cpl_vector_unwrap(offs);
09757             cpl_table_delete(dummy);
09758 
09759             /*
09760              * Now correct the coefficients of the corresponding IDS
09761              * polynomials related to this slit:
09762              */
09763 
09764             ylow = position[i];
09765             yhig = ylow + length[i];
09766 
09767             for (j = 0; j <= uorder; j++) {
09768                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09769                 c = cpl_polynomial_get_coeff(polycorr, &j);
09770                 for (k = ylow; k < yhig; k++)
09771                     data[k] += c;
09772             }
09773 
09774             data = cpl_table_get_data_double(idscoeff, "error");
09775             for (k = ylow; k < yhig; k++)
09776                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09777 
09778             idata = cpl_table_get_data_int(idscoeff, "nlines");
09779             for (k = ylow; k < yhig; k++)
09780                  idata[k] = npoints;
09781 
09782             /*
09783              * If a wavelengths map was provided, correct it to keep
09784              * into account the alignment to skylines:
09785              */
09786 
09787             if (calibration) {
09788                 for (j = ylow; j < yhig; j++) {
09789                     for (k = 1; k < nx; k++) {
09790                         lambda1 = cdata[k - 1 + j*nx];
09791                         lambda2 = cdata[k + j*nx];
09792                         if (lambda1 < 1.0 || lambda2 < 1.0)
09793                             continue;
09794                         offset = cpl_polynomial_eval_1d(polycorr, 
09795                                                         lambda1-refwave, NULL);
09796                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09797                     }
09798                 }
09799             }
09800     
09801             cpl_polynomial_delete(polycorr);
09802         }
09803         else if (uorder == 1) {
09804 
09805             /*
09806              * Model offsets with robust linear fitting
09807              */
09808 
09809             double        q, m;
09810             cpl_bivector *list;
09811 
09812 
09813             wave = cpl_vector_wrap(npoints,
09814                                    cpl_table_get_data_double(dummy, "wave"));
09815             offs = cpl_vector_wrap(npoints,
09816                                    cpl_table_get_data_double(dummy, "offset"));
09817 
09818             list = cpl_bivector_wrap_vectors(wave, offs);
09819 
09820             /*
09821              * Set reference wavelength as zero point
09822              */
09823 
09824             cpl_vector_subtract_scalar(wave, refwave);
09825 
09826             robustLinearFit(list, &q, &m, &rms);
09827 
09828             rms = sqrt(rms * (uorder + 1) / npoints);
09829 
09830             cpl_bivector_unwrap_vectors(list);
09831             cpl_vector_unwrap(wave);
09832             cpl_vector_unwrap(offs);
09833             cpl_table_delete(dummy);
09834 
09835             /*
09836              * Now correct the coefficients of the corresponding IDS
09837              * polynomials related to this slit:
09838              */
09839 
09840             ylow = position[i];
09841             yhig = ylow + length[i];
09842 
09843             for (j = 0; j <= uorder; j++) {
09844                 data = cpl_table_get_data_double(idscoeff, clab[j]);
09845                 if (j)
09846                     c = m;
09847                 else
09848                     c = q;
09849                 for (k = ylow; k < yhig; k++)
09850                     data[k] += c;
09851             }
09852 
09853             data = cpl_table_get_data_double(idscoeff, "error");
09854             for (k = ylow; k < yhig; k++)
09855                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09856 
09857             idata = cpl_table_get_data_int(idscoeff, "nlines");
09858             for (k = ylow; k < yhig; k++)
09859                  idata[k] = npoints;
09860 
09861             /*
09862              * If a wavelengths map was provided, correct it to keep
09863              * into account the alignment to skylines:
09864              */
09865 
09866             if (calibration) {
09867                 for (j = ylow; j < yhig; j++) {
09868                     for (k = 1; k < nx; k++) {
09869                         lambda1 = cdata[k - 1 + j*nx];
09870                         lambda2 = cdata[k + j*nx];
09871                         if (lambda1 < 1.0 || lambda2 < 1.0)
09872                             continue;
09873                         offset = q + m*(lambda1-refwave);
09874                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09875                     }
09876                 }
09877             }
09878         }
09879         else {
09880 
09881             /*
09882              * Just compute median offset
09883              */
09884 
09885             offs = cpl_vector_wrap(npoints,
09886                                    cpl_table_get_data_double(dummy, "offset"));
09887 
09888             offset = cpl_vector_get_median_const(offs);
09889 
09890             if (npoints > 1)
09891                 rms = cpl_table_get_column_stdev(dummy, "offset");
09892             else
09893                 rms = 0.0;
09894 
09895             rms /= sqrt(npoints);
09896 
09897             cpl_vector_unwrap(offs);
09898             cpl_table_delete(dummy);
09899 
09900             /*
09901              * Now correct the constant term of the corresponding IDS
09902              * polynomials related to this slit:
09903              */
09904 
09905             ylow = position[i];
09906             yhig = ylow + length[i];
09907 
09908             data = cpl_table_get_data_double(idscoeff, clab[0]);
09909             for (k = ylow; k < yhig; k++)
09910                 data[k] += offset;
09911 
09912             data = cpl_table_get_data_double(idscoeff, "error");
09913             for (k = ylow; k < yhig; k++)
09914                  data[k] = sqrt(data[k]*data[k] + rms*rms);
09915 
09916             idata = cpl_table_get_data_int(idscoeff, "nlines");
09917             for (k = ylow; k < yhig; k++)
09918                  idata[k] = npoints;
09919 
09920             /*
09921              * If a wavelengths map was provided, correct it to keep
09922              * into account the alignment to skylines. Note that 
09923              * the offset must be converted from pixels to wavelengths.
09924              */
09925 
09926             if (calibration) {
09927                 for (j = ylow; j < yhig; j++) {
09928                     for (k = 1; k < nx; k++) {
09929                         lambda1 = cdata[k - 1 + j*nx];
09930                         lambda2 = cdata[k + j*nx];
09931                         if (lambda1 < 1.0 || lambda2 < 1.0)
09932                             continue; 
09933                         cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1);
09934                     }
09935                 }
09936             }
09937         }
09938     }
09939 
09940     return offsets;
09941 
09942 }
09943 
09944 
10006 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 
10007                                     double firstLambda, double lastLambda, 
10008                                     cpl_table *idscoeff, cpl_vector *skylines, 
10009                                     int highres, int order, 
10010                                     cpl_image *calibration, int sradius)
10011 {
10012     const char *func = "mos_wavelength_align_lss";
10013 
10014     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10015                                                  /* Max order is 5 */
10016     double         *line;
10017     double         *data;
10018     double         *wdata;
10019     double         *odata;
10020     double          expPos, offset;
10021     double          c;
10022     double          lambda1, lambda2;
10023     double          rms;
10024     float           pos;
10025     float          *sdata;
10026     float          *cdata;
10027     int            *idata;
10028     int             startPos, endPos;
10029     int             window = 2*sradius + 1;
10030     int             nlines;
10031     int             npoints;
10032     int             nrows;
10033     int             nx, ny;
10034     int             idsorder, uorder;
10035     int             missing;
10036     int             i, j, k;
10037 
10038     char            name[MAX_COLNAME];
10039     char            fname[MAX_COLNAME];
10040 
10041     cpl_polynomial *ids;
10042     cpl_polynomial *polycorr;
10043     cpl_table      *offsets;
10044     cpl_table      *fittable;
10045     cpl_table      *dummy;
10046     cpl_vector     *wave;
10047     cpl_vector     *offs;
10048     cpl_vector     *row;
10049     
10050 
10051     if (idscoeff == NULL) {
10052         cpl_msg_error(func, "An IDS coeff table must be given");
10053         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10054         return NULL;
10055     }
10056 
10057     if (image == NULL) {
10058         cpl_msg_error(func, "A scientific spectral image must be given");
10059         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10060         return NULL;
10061     }
10062 
10063     if (skylines) {
10064         line = cpl_vector_get_data(skylines);
10065         nlines = cpl_vector_get_size(skylines);
10066     }
10067     else {
10068         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10069                         "given: using internal list of reference sky lines");
10070         if (highres) {
10071            line = default_lines_hi;
10072            nlines = sizeof(default_lines_hi) / sizeof(double);
10073         }
10074         else {
10075            line = default_lines_lo;
10076            nlines = sizeof(default_lines_lo) / sizeof(double);
10077         }
10078     }
10079 
10080     if (calibration)
10081         cdata = cpl_image_get_data(calibration);
10082 
10083     nx = cpl_image_get_size_x(image);
10084     ny = cpl_image_get_size_y(image);
10085 
10086     sdata = cpl_image_get_data(image);
10087 
10088     if (ny != cpl_table_get_nrow(idscoeff)) {
10089         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
10090         return NULL;
10091     }
10092     
10093 
10094     /*FIXME: This is a remnant of the adaptation of the function
10095      * mos_wavelength_align(), where an offset table was created.
10096      * I leave it here because I am in a hurry, it is just used to
10097      * hold the list of selected sky lines.
10098      *
10099      * Define table of wavelengths
10100      */
10101 
10102     nrows = 0;
10103     for (i = 0; i < nlines; i++)
10104         if (line[i] > firstLambda && line[i] < lastLambda)
10105             nrows++;
10106 
10107     offsets = cpl_table_new(nrows);
10108     cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE);
10109     cpl_table_set_column_unit(offsets, "wave", "Angstrom");
10110 
10111     nrows = 0;
10112     for (i = 0; i < nlines; i++) {
10113         if (line[i] > firstLambda && line[i] < lastLambda) {
10114             cpl_table_set_double(offsets, "wave", nrows, line[i]);
10115             nrows++;
10116         }
10117     }
10118 
10119     /*
10120      * Here "line" is made to point to the new list of selected wavelengths
10121      */
10122 
10123     line = cpl_table_get_data_double(offsets, "wave");
10124     nlines = nrows;
10125 
10126     idsorder = 0;
10127     while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder]))
10128         ++idsorder;
10129     --idsorder;
10130 
10131 
10132     /*
10133      * Allocate a dummy table for collecting all the offsets
10134      * for all the lines
10135      */
10136 
10137     dummy = cpl_table_new(ny);
10138     for (j = 0; j < nlines; j++) {
10139         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10140         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10141         cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE);
10142         cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE);
10143     }
10144 
10145     for (j = 0; j < ny; j++, sdata += nx) {
10146 
10147         /*
10148          * Get the IDS polynomial for the current slit row
10149          */
10150 
10151         missing = 0;
10152         ids = cpl_polynomial_new(1);
10153         for (k = 0; k <= idsorder; k++) {
10154             c = cpl_table_get_double(idscoeff, clab[k], j, &missing);
10155             if (missing) {
10156                 cpl_polynomial_delete(ids);
10157                 break;
10158             }
10159             cpl_polynomial_set_coeff(ids, &k, c);
10160         }
10161         if (missing)
10162             continue;
10163 
10164         for (k = 0; k < nlines; k++) {
10165             expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL);
10166             startPos = expPos - sradius;
10167             endPos   = startPos + window;
10168             if (startPos < 0 || endPos >= nx)
10169                 continue;
10170            
10171             if (0 == peakPosition(sdata + startPos, window, &pos, 1)) {
10172                 pos += startPos;
10173                 offset = pos - expPos;
10174                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]);
10175                 cpl_table_set_double(dummy, name, j, offset);
10176             }
10177         }
10178 
10179         cpl_polynomial_delete(ids);
10180     }
10181 
10182 
10183     /*
10184      * At this point for each sky line we model its offset along
10185      * the image rows using a robust linear fitting
10186      */
10187 
10188     for (j = 0; j < nlines; j++) {
10189         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10190         snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]);
10191         if (cpl_table_has_valid(dummy, name)) {
10192 
10193             /*
10194              * In the following, the "fittable" is just a tool for
10195              * eliminating invalid points from the vectors to be fitted.
10196              */
10197 
10198             double        q, m;
10199             cpl_bivector *list;
10200 
10201             fittable = cpl_table_new(ny);
10202             cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE);
10203             cpl_table_set_column_unit(fittable, "row", "pixel");
10204             for (k = 0; k < ny; k++)
10205                  cpl_table_set_double(fittable, "row", k, k);
10206             cpl_table_duplicate_column(fittable, "offset", dummy, name);
10207             npoints = ny - cpl_table_count_invalid(fittable, "offset");
10208             cpl_table_erase_invalid(fittable);
10209             row = cpl_vector_wrap(npoints,
10210                                cpl_table_get_data_double(fittable, "row"));
10211             offs = cpl_vector_wrap(npoints,
10212                                cpl_table_get_data_double(fittable, "offset"));
10213             list = cpl_bivector_wrap_vectors(row, offs);
10214             robustLinearFit(list, &q, &m, &rms);
10215             cpl_bivector_unwrap_vectors(list);
10216             cpl_vector_unwrap(row);
10217             cpl_vector_unwrap(offs);
10218             cpl_table_delete(fittable);
10219             for (k = 0; k < ny; k++)
10220                  cpl_table_set_double(dummy, fname, k, q + m*k);
10221         }
10222     }
10223 
10224 
10225     /*
10226      * Now each dummy table row consists of a sequence of offsets,
10227      * one for each wavelength. A table row corresponds to an image row.
10228      * We must fit a polynomial to each one of these rows, in order to
10229      * express the offsets as a function of wavelength. The obtained 
10230      * polynomial coefficients are used to correct the IDS coefficients.
10231      */
10232 
10233     for (i = 0; i < ny; i++) {
10234 
10235         if (!cpl_table_is_valid(idscoeff, clab[0], i))
10236             continue;
10237 
10238         npoints = 0;
10239         for (j = 0; j < nlines; j++) {
10240             snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10241             if (cpl_table_is_valid(dummy, name, i))
10242                 npoints++;
10243         }
10244 
10245         if (npoints == 0)
10246             continue;
10247 
10248         uorder = order;
10249         if (npoints <= uorder)
10250             uorder = npoints - 1;
10251 
10252         if (uorder > 1) {
10253 
10254             /*
10255              * Model offsets with polynomial fitting
10256              */
10257 
10258             wave = cpl_vector_new(npoints);
10259             wdata = cpl_vector_get_data(wave);
10260             offs = cpl_vector_new(npoints);
10261             odata = cpl_vector_get_data(offs);
10262 
10263             npoints = 0;
10264             for (j = 0; j < nlines; j++) {
10265                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10266                 if (cpl_table_is_valid(dummy, name, i)) {
10267                     wdata[npoints] = line[j] - refwave;
10268                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10269                     npoints++;
10270                 }
10271             }
10272 
10273             polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms);
10274 
10275             rms = sqrt(rms * (uorder + 1) / npoints);
10276 
10277             cpl_vector_delete(wave);
10278             cpl_vector_delete(offs);
10279 
10280             /*
10281              * Now correct the coefficients of the corresponding IDS
10282              * polynomials related to this slit:
10283              */
10284 
10285             for (j = 0; j <= uorder; j++) {
10286                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10287                 c = cpl_polynomial_get_coeff(polycorr, &j);
10288                 data[i] += c;
10289             }
10290 
10291             data = cpl_table_get_data_double(idscoeff, "error");
10292             data[i] = sqrt(data[i]*data[i] + rms*rms);
10293 
10294             idata = cpl_table_get_data_int(idscoeff, "nlines");
10295             idata[i] = npoints;
10296 
10297             /*
10298              * If a wavelengths map was provided, correct it to keep
10299              * into account the alignment to skylines:
10300              */
10301 
10302             if (calibration) {
10303                 for (k = 1; k < nx; k++) {
10304                     lambda1 = cdata[k - 1 + i*nx];
10305                     lambda2 = cdata[k + i*nx];
10306                     if (lambda1 < 1.0 || lambda2 < 1.0)
10307                         continue;
10308                     offset = cpl_polynomial_eval_1d(polycorr,
10309                                                     lambda1-refwave, NULL);
10310                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10311                 }
10312             }
10313 
10314             cpl_polynomial_delete(polycorr);
10315 
10316         }
10317         else if (uorder == 1) {
10318 
10319             /*
10320              * Model offsets with robust linear fitting
10321              */
10322 
10323             cpl_bivector *list;
10324             double        q, m;
10325 
10326             wave = cpl_vector_new(npoints);
10327             wdata = cpl_vector_get_data(wave);
10328             offs = cpl_vector_new(npoints);
10329             odata = cpl_vector_get_data(offs);
10330 
10331             npoints = 0;
10332             for (j = 0; j < nlines; j++) {
10333                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10334                 if (cpl_table_is_valid(dummy, name, i)) {
10335                     wdata[npoints] = line[j] - refwave;
10336                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10337                     npoints++;
10338                 }
10339             }
10340 
10341             list = cpl_bivector_wrap_vectors(wave, offs);
10342             robustLinearFit(list, &q, &m, &rms);
10343 
10344             rms = sqrt(rms * (uorder + 1) / npoints);
10345 
10346             cpl_bivector_unwrap_vectors(list);
10347             cpl_vector_delete(wave);
10348             cpl_vector_delete(offs);
10349 
10350             /*
10351              * Now correct the coefficients of the corresponding IDS
10352              * polynomials related to this row:
10353              */
10354 
10355             for (j = 0; j <= uorder; j++) {
10356                 data = cpl_table_get_data_double(idscoeff, clab[j]);
10357                 if (j)
10358                     c = m;
10359                 else
10360                     c = q;
10361                 data[i] += c;
10362             }
10363 
10364             data = cpl_table_get_data_double(idscoeff, "error");
10365             data[i] = sqrt(data[i]*data[i] + rms*rms);
10366 
10367             idata = cpl_table_get_data_int(idscoeff, "nlines");
10368             idata[i] = npoints;
10369 
10370             /*
10371              * If a wavelengths map was provided, correct it to keep
10372              * into account the alignment to skylines:
10373              */
10374 
10375             if (calibration) {
10376                 for (k = 1; k < nx; k++) {
10377                     lambda1 = cdata[k - 1 + i*nx];
10378                     lambda2 = cdata[k + i*nx];
10379                     if (lambda1 < 1.0 || lambda2 < 1.0)
10380                         continue;
10381                     offset = q + m*(lambda1-refwave);
10382                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10383                 }
10384             }
10385         }
10386         else {
10387 
10388             /*
10389              * Just compute median offset
10390              */
10391 
10392             offs = cpl_vector_new(npoints);
10393             odata = cpl_vector_get_data(offs);
10394 
10395             npoints = 0;
10396             for (j = 0; j < nlines; j++) {
10397                 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]);
10398                 if (cpl_table_is_valid(dummy, name, i)) {
10399                     odata[npoints] = cpl_table_get_double(dummy, name, i, NULL);
10400                     npoints++;
10401                 }
10402             }
10403 
10404             offset = cpl_vector_get_median_const(offs);
10405 
10406             if (npoints > 1) {
10407                 rms = cpl_vector_get_stdev(offs);
10408             }
10409             else if (npoints == 1) {
10410                 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]);
10411                 if (cpl_table_has_valid(dummy, name)) {
10412                     rms = cpl_table_get_column_stdev(dummy, name);
10413                     rms /= sqrt(ny - cpl_table_count_invalid(dummy, name));
10414                 }
10415                 else {
10416                     rms = 0.0;
10417                 }
10418             }
10419             else {
10420                 rms = 0.0;
10421             }
10422 
10423             rms /= sqrt(npoints);
10424 
10425             cpl_vector_delete(offs);
10426 
10427             /*
10428              * Now correct the constant term of the corresponding IDS
10429              * polynomials related to this slit:
10430              */
10431 
10432             data = cpl_table_get_data_double(idscoeff, clab[0]);
10433             data[i] += offset;
10434 
10435             data = cpl_table_get_data_double(idscoeff, "error");
10436             data[i] = sqrt(data[i]*data[i] + rms*rms);
10437 
10438             idata = cpl_table_get_data_int(idscoeff, "nlines");
10439             idata[i] = npoints;
10440 
10441             /*
10442              * If a wavelengths map was provided, correct it to keep
10443              * into account the alignment to skylines. Note that
10444              * the offset must be converted from pixels to wavelengths.
10445              */
10446 
10447             if (calibration) {
10448                 for (k = 1; k < nx; k++) {
10449                     lambda1 = cdata[k - 1 + i*nx];
10450                     lambda2 = cdata[k + i*nx];
10451                     if (lambda1 < 1.0 || lambda2 < 1.0)
10452                         continue;
10453                     cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1);
10454                 }
10455             }
10456         }
10457     }
10458 
10459     missing = 1;
10460     for (j = 0; j < nlines; j++) {
10461         snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]);
10462         if (cpl_table_has_valid(dummy, name)) {
10463             missing = 0;
10464             offset = cpl_table_get_column_median(dummy, name);
10465             cpl_msg_info(func, "Median offset for %.3f: %.3f pixel",
10466                          line[j], offset);
10467         }
10468         else {
10469             cpl_msg_info(func, 
10470                          "Median offset for %.2f: not available", line[j]);
10471         }
10472     }
10473 
10474     cpl_table_delete(offsets);
10475 
10476     if (missing) {
10477         cpl_table_delete(dummy);
10478         dummy = NULL;
10479     }
10480 
10481     return dummy;
10482 
10483 }
10484 
10485 
10513 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 
10514                            double wavestart, double dispersion, int radius,
10515                            int highres)
10516 {
10517 
10518     const char *func = "mos_distortions_rms";
10519 
10520     int xlen;
10521     int ylen;
10522     int numLines;
10523     int cpix, npix, nzero;
10524     int sp, ep;
10525     int i, j, k;
10526     int npeaks, allPeaks;
10527 
10528     float *profile;
10529     float  peak, expectPeak, offset;
10530     double lambda;
10531 
10532     double  average;
10533     double  rms, oneRms;
10534 
10535     float  *sdata;
10536     double *wdata;
10537 
10538   
10539     xlen = cpl_image_get_size_x(rectified);
10540     ylen = cpl_image_get_size_y(rectified);
10541     sdata = cpl_image_get_data(rectified);
10542 
10543     if (lines) {
10544         wdata = cpl_vector_get_data(lines);
10545         numLines = cpl_vector_get_size(lines);
10546     }
10547     else {
10548         cpl_msg_warning(func, "A catalog of sky lines wavelengths was not "
10549                         "given: using internal list of reference sky lines");
10550         if (highres) {
10551            wdata = default_lines_hi;
10552            numLines = sizeof(default_lines_hi) / sizeof(double);
10553         }
10554         else {
10555            wdata = default_lines_lo;
10556            numLines = sizeof(default_lines_lo) / sizeof(double);
10557         }
10558     }
10559 
10560     npix = 2 * radius + 1;
10561     profile = cpl_calloc(npix, sizeof(float));
10562 
10563     rms = 0.0;
10564     allPeaks = 0;
10565 
10566     for (i = 0; i < numLines; i++) {
10567 
10568         /*
10569          *  Expected peak and closest pixel to specified wavelength.
10570          */
10571 
10572         lambda = wdata[i];
10573         expectPeak = (lambda - wavestart) / dispersion;
10574         cpix = floor(expectPeak + 0.5);
10575 
10576         /*
10577          *  Search interval for peak. Abort if too close to image border.
10578          */
10579 
10580         sp = cpix - radius;
10581         ep = cpix + radius;
10582 
10583         if (sp < 0 || ep > xlen)
10584             continue;
10585 
10586         average = 0.0;
10587         npeaks = 0;
10588         oneRms = 0.0;
10589 
10590         for (j = 0; j < ylen; j++) {    /*  For each row of each slit  */
10591             nzero = 0;
10592             for (k = 0; k < npix; k++) {
10593                 profile[k] = sdata[sp + k + j * xlen];
10594                 if (fabs(profile[k]) < 0.0001)
10595                     nzero++; /* Count number of 0 pixels (spectrum truncated) */
10596             }
10597             if (nzero > 0)
10598                 continue;
10599 
10600             if (peakPosition(profile, npix, &peak, 1) == 0) {
10601                 offset = (sp + peak) - expectPeak;
10602                 average += offset;
10603                 rms += fabs(offset);
10604                 oneRms += fabs(offset);
10605                 npeaks++;
10606                 allPeaks++;
10607             }
10608         }
10609 
10610         if (npeaks)
10611             cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)",
10612                          lambda, oneRms / npeaks * 1.25, npeaks);
10613         else
10614             cpl_msg_info(func, "RMS for %.2f: line not available", lambda);
10615     }
10616 
10617     cpl_free(profile);
10618 
10619     if (allPeaks < 10)
10620         return 0.0;
10621 
10622     rms /= allPeaks;
10623     rms *= 1.25;       /* Factor to convert average deviation to sigma */
10624 
10625     return rms;
10626 
10627 }
10628 
10629 
10650 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference,
10651                          double blue, double red, double dispersion, int trend)
10652 {
10653     const char *func = "mos_map_pixel";
10654 
10655     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10656                                                  /* Max order is 5 */
10657 
10658     cpl_polynomial *ids;
10659     cpl_image      *map;
10660     float          *mdata;
10661     double          lambda;
10662     double          c;
10663     int             order;
10664     int             xsize, ysize;
10665     int             missing;
10666     int             i, j, k;
10667 
10668 
10669     if (idscoeff == NULL) {
10670         cpl_msg_error(func, "An IDS coeff table must be given");
10671         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10672         return NULL;
10673     }
10674 
10675     xsize = (red - blue) / dispersion;
10676     ysize = cpl_table_get_nrow(idscoeff);
10677     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10678     mdata = cpl_image_get_data(map);
10679 
10680     order = 0;
10681     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10682         ++order;
10683     --order;
10684 
10685     for (i = 0; i < ysize; i++, mdata += xsize) {
10686 
10687         missing = 0;
10688         ids = cpl_polynomial_new(1);
10689         for (k = trend; k <= order; k++) {
10690             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10691             if (missing) {
10692                 cpl_polynomial_delete(ids);
10693                 break;
10694             }
10695             cpl_polynomial_set_coeff(ids, &k, c);
10696         }
10697         if (missing)
10698             continue;
10699 
10700         for (j = 0; j < xsize; j++) {
10701             lambda = blue + j*dispersion;
10702             mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL);
10703         }
10704 
10705         cpl_polynomial_delete(ids);
10706     }
10707 
10708     return map;
10709 
10710 }
10711 
10712 
10734 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference,
10735                             double blue, double red)
10736 {
10737     const char *func = "mos_map_idscoeff";
10738 
10739     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10740                                                  /* Max order is 5 */
10741 
10742     cpl_polynomial *ids;
10743     cpl_image      *map;
10744     float          *mdata;
10745     double          lambda;
10746     double          c;
10747     int             order;
10748     int             ysize;
10749     int             missing;
10750     int             i, j, k;
10751 
10752 
10753     if (idscoeff == NULL) {
10754         cpl_msg_error(func, "An IDS coeff table must be given");
10755         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10756         return NULL;
10757     }
10758 
10759     if (xsize < 1) {
10760         cpl_msg_error(func, "Invalid image size");
10761         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10762         return NULL;
10763     }
10764 
10765     if (xsize < 20 || xsize > 5000) {
10766         cpl_msg_warning(func, "Do you really have a detector %d pixels long?",
10767                         xsize);
10768     }
10769 
10770     ysize = cpl_table_get_nrow(idscoeff);
10771     map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT);
10772     mdata = cpl_image_get_data(map);
10773 
10774     order = 0;
10775     while (order < 6 && cpl_table_has_column(idscoeff, clab[order]))
10776         ++order;
10777     --order;
10778 
10779     for (i = 0; i < ysize; i++, mdata += xsize) {
10780 
10781         missing = 0;
10782         ids = cpl_polynomial_new(1);
10783         for (k = 0; k <= order; k++) {
10784             c = cpl_table_get_double(idscoeff, clab[k], i, &missing);
10785             if (missing) {
10786                 cpl_polynomial_delete(ids);
10787                 break;
10788             }
10789             cpl_polynomial_set_coeff(ids, &k, c);
10790         }
10791         if (missing)
10792             continue;
10793 
10794         for (j = 0; j < xsize; j++) {
10795             lambda = mos_eval_dds(ids, blue, red, reference, j);
10796 
10797             if (lambda >= blue && lambda <= red) {
10798                 mdata[j] = lambda;
10799             }
10800         }
10801 
10802         cpl_polynomial_delete(ids);
10803     }
10804 
10805     return map;
10806 
10807 }
10808 
10809 
10844 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration,
10845                                cpl_table *slits, cpl_table *polytraces, 
10846                                double reference, double blue, double red, 
10847                                double dispersion)
10848 {
10849     const char *func = "mos_map_wavelengths";
10850 
10851     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
10852                                                  /* Max order is 5 */
10853     cpl_polynomial *polytop;
10854     cpl_polynomial *polybot;
10855     cpl_image      *remapped;
10856     float          *data;
10857     float          *wdata;
10858     float          *sdata;
10859     float          *xdata;
10860     double          vtop, vbot, value;
10861     double          top, bot;
10862     double          coeff;
10863     double          ytop, ybot;
10864     double          ypos;
10865     double          fvalue;
10866     int             ivalue;
10867     int             yint, ysize, yprev;
10868     int             nslits;
10869     int             npseudo;
10870     int            *slit_id;
10871     int            *position;
10872     int            *length;
10873     int             nx, ny;
10874     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
10875     int             missing_top, missing_bot;
10876     int             null;
10877     int             order;
10878     int             i, j, k;
10879 
10880 
10881     if (spatial == NULL || calibration == NULL || 
10882         slits == NULL || polytraces == NULL) {
10883         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
10884         return NULL;
10885     }
10886 
10887     if (dispersion <= 0.0) {
10888         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10889         return NULL;
10890     }
10891 
10892     if (red - blue < dispersion) {
10893         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
10894         return NULL;
10895     }
10896 
10897     nx = cpl_image_get_size_x(spatial);
10898     ny = cpl_image_get_size_y(spatial);
10899     ysize = cpl_image_get_size_y(calibration);
10900     remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
10901     data  = cpl_image_get_data(remapped);
10902     sdata = cpl_image_get_data(spatial);
10903     wdata = cpl_image_get_data(calibration);
10904 
10905     nslits   = cpl_table_get_nrow(slits);
10906     slit_id  = cpl_table_get_data_int(slits, "slit_id");
10907     order    = cpl_table_get_ncol(polytraces) - 2;
10908     position = cpl_table_get_data_int(slits, "position");
10909     length   = cpl_table_get_data_int(slits, "length");
10910 
10911     /*
10912      * The spatial resampling is performed for a certain number of 
10913      * pixels above and below the position of the reference wavelength:
10914      */
10915 
10916     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
10917     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
10918 
10919     for (i = 0; i < nslits; i++) {
10920 
10921         if (length[i] == 0)
10922             continue;
10923 
10924         /*
10925          * Note that the x coordinate of the reference pixels on the CCD
10926          * is taken arbitrarily at the top end of each slit. This wouldn't
10927          * be entirely correct in case of curved slits, or in presence of
10928          * heavy distortions: in such cases the spatial resampling is
10929          * really performed across a wide range of wavelengths. But
10930          * the lag between top and bottom spectral curvature models 
10931          * would introduce even in such cases negligible effects on
10932          * the spectral spatial resampling.
10933          */
10934 
10935         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
10936 
10937         start_pixel = refpixel - pixel_below;
10938         if (start_pixel < 0)
10939             start_pixel = 0;
10940 
10941         end_pixel = refpixel + pixel_above;
10942         if (end_pixel > nx)
10943             end_pixel = nx;
10944 
10945         /*
10946          * Recover from the table of spectral curvature coefficients
10947          * the curvature polynomials.
10948          */
10949 
10950         missing_top = 0;
10951         polytop = cpl_polynomial_new(1);
10952         for (k = 0; k <= order; k++) {
10953             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
10954             if (null) {
10955                 cpl_polynomial_delete(polytop);
10956                 missing_top = 1;
10957                 break;
10958             }
10959             cpl_polynomial_set_coeff(polytop, &k, coeff);
10960         }
10961 
10962         missing_bot = 0;
10963         polybot = cpl_polynomial_new(1);
10964         for (k = 0; k <= order; k++) {
10965             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
10966             if (null) {
10967                 cpl_polynomial_delete(polybot);
10968                 missing_bot = 1;
10969                 break;
10970             }
10971             cpl_polynomial_set_coeff(polybot, &k, coeff);
10972         }
10973 
10974         if (missing_top && missing_bot) {
10975             cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 
10976                           slit_id[i]);
10977             continue;
10978         }
10979 
10980         /*
10981          * In case just one of the two edges was not traced, the other
10982          * edge curvature model is duplicated and shifted to the other
10983          * end of the slit: better than nothing!
10984          */
10985 
10986         if (missing_top) {
10987             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
10988                           "the spectral curvature of the lower edge "
10989                           "is used instead.", slit_id[i]);
10990             polytop = cpl_polynomial_duplicate(polybot);
10991             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
10992             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
10993             k = 0;
10994             coeff = cpl_polynomial_get_coeff(polybot, &k);
10995             coeff += ytop - ybot;
10996             cpl_polynomial_set_coeff(polytop, &k, coeff);
10997         }
10998 
10999         if (missing_bot) {
11000             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11001                           "the spectral curvature of the upper edge "
11002                           "is used instead.", slit_id[i]);
11003             polybot = cpl_polynomial_duplicate(polytop);
11004             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11005             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11006             k = 0;
11007             coeff = cpl_polynomial_get_coeff(polytop, &k);
11008             coeff -= ytop - ybot;
11009             cpl_polynomial_set_coeff(polybot, &k, coeff);
11010         }
11011 
11012         /*
11013          * Point to current slit on wavelength calibration image.
11014          * Note that the npseudo value related to this slit is equal 
11015          * to the number of spatial pseudo-pixels decreased by 1 
11016          * (compare with function mos_spatial_calibration()).
11017          */
11018 
11019         xdata = wdata + nx*position[i];
11020         npseudo = length[i] - 1;
11021 
11022         /*
11023          * Write interpolated wavelengths to CCD image
11024          */
11025 
11026         for (j = start_pixel; j < end_pixel; j++) {
11027             top = cpl_polynomial_eval_1d(polytop, j, NULL);
11028             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
11029             for (k = 0; k <= npseudo; k++) {
11030                 ypos = top - k*(top-bot)/npseudo;
11031                 yint = ypos;
11032 
11033                 /* 
11034                  * The line:
11035                  *     value = sdata[j + nx*yint];
11036                  * should be equivalent to:
11037                  *     value = npseudo*(top-yint)/(top-bot);
11038                  */
11039 
11040                 if (yint < 0 || yint >= ny-1) {
11041                     yprev = yint;
11042                     continue;
11043                 }
11044 
11045                 value = sdata[j + nx*yint];   /* Spatial coordinate on CCD */
11046                 ivalue = value;               /* Nearest spatial pixels:   */
11047                 fvalue = value - ivalue;      /* ivalue and ivalue+1       */
11048                 if (ivalue < npseudo && ivalue >= 0) {
11049                     vtop = xdata[j + nx*(npseudo-ivalue)];
11050                     vbot = xdata[j + nx*(npseudo-ivalue-1)];
11051                     if (vtop < 1.0) {  /* Impossible wavelength */
11052                         if (vbot < 1.0) {
11053                             value = 0.0;
11054                         }
11055                         else {
11056                             value = vbot;
11057                         }
11058                     }
11059                     else if (vbot < 1.0) {
11060                         if (k)
11061                             value = vtop;
11062                         else
11063                             value = 0.0;
11064                     }
11065                     else if (fabs(vbot-vtop) > 10*dispersion) {
11066                         value = 0.0;
11067                     }
11068                     else {
11069                         value = vtop*(1-fvalue) + vbot*fvalue;
11070                     }
11071                     data[j + nx*yint] = value;
11072 
11073                     if (k) {
11074 
11075                         /*
11076                          * This is added to recover lost pixels on
11077                          * the CCD image (pixels are lost because
11078                          * the CCD pixels are less than npseudo+1).
11079                          */
11080 
11081                         if (yprev - yint > 1) {
11082                             value = sdata[j + nx*(yint+1)];
11083                             ivalue = value;
11084                             fvalue = value - ivalue;
11085                             if (ivalue < npseudo && ivalue >= 0) {
11086                                 vtop = xdata[j + nx*(npseudo-ivalue)];
11087                                 vbot = xdata[j + nx*(npseudo-ivalue-1)];
11088                                 if (vtop < 1.0) {
11089                                     if (vbot < 1.0) {
11090                                         value = data[j + nx*(yint+1)];
11091                                     }
11092                                     else {
11093                                         value = vbot;
11094                                     }
11095                                 }
11096                                 else if (vbot < 1.0) {
11097                                     value = vtop;
11098                                 }
11099                                 else if (fabs(vbot-vtop) > 2*dispersion) {
11100                                     value = vtop;
11101                                 }
11102                                 else {
11103                                     value = vtop*(1-fvalue) + vbot*fvalue;
11104                                 }
11105                                 data[j + nx*(yint+1)] = value;
11106                             }
11107                         }
11108                     }
11109                 }
11110                 yprev = yint;
11111             }
11112         }
11113         cpl_polynomial_delete(polytop);
11114         cpl_polynomial_delete(polybot);
11115     }
11116 
11117     return remapped;
11118 }
11119 
11193 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 
11194                             cpl_image *spatial, cpl_table *slits,
11195                             cpl_table *polytraces, double reference,
11196                             double blue, double red, double dispersion,
11197                             int flux)
11198 {
11199     const char *func = "mos_map_spectrum";
11200     
11201     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
11202                                                  /* Max order is 5 */
11203     cpl_polynomial *polytop;
11204     cpl_polynomial *polybot;
11205     cpl_image      *remapped;
11206     cpl_image     **exslit;
11207     float          *data;
11208     float          *wdata;
11209     float          *sdata;
11210     float          *xdata;
11211     double          lambda00, lambda01, lambda10, lambda11, lambda;
11212     double          space00, space01, space10, space11, space;
11213     double          value00, value01, value10, value11, value0, value1, value;
11214     double          dL, dS;
11215     double          top, bot;
11216     double          coeff;
11217     double          ytop, ybot;
11218     double          xfrac, yfrac;
11219     int             yint, ysize;
11220     int             itop, ibot;
11221     int             shift;
11222     int             L, S;
11223     int             nslits;
11224     int             npseudo;
11225     int            *slit_id;
11226     int            *position;
11227     int            *length;
11228     int             nx, ny;
11229     int             x, y;
11230     int             nlambda;
11231     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
11232     int             missing_top, missing_bot; 
11233     int             null;
11234     int             order;
11235     int             i, k;
11236     
11237 
11238     flux += flux;
11239 
11240     if (spectra == NULL || spatial == NULL || wavecalib == NULL ||
11241         slits == NULL || polytraces == NULL) { 
11242         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11243         return NULL;
11244     }
11245 
11246     if (dispersion <= 0.0) {
11247         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11248         return NULL;
11249     }
11250 
11251     if (red - blue < dispersion) {
11252         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11253         return NULL;
11254     }
11255     
11256     nx = cpl_image_get_size_x(spectra);
11257     ny = cpl_image_get_size_y(spectra);
11258 
11259     if (nx != cpl_image_get_size_x(spatial) ||
11260         ny != cpl_image_get_size_y(spatial) ||
11261         nx != cpl_image_get_size_x(wavecalib) ||
11262         ny != cpl_image_get_size_y(wavecalib)) {
11263         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11264         return NULL;
11265     }
11266 
11267     nlambda     = STRETCH_FACTOR * (red - blue) / dispersion;
11268     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
11269     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
11270 
11271     data  = cpl_image_get_data(spectra);
11272     sdata = cpl_image_get_data(spatial);
11273     wdata = cpl_image_get_data(wavecalib);
11274     
11275     nslits   = cpl_table_get_nrow(slits);
11276     slit_id  = cpl_table_get_data_int(slits, "slit_id");
11277     order    = cpl_table_get_ncol(polytraces) - 2;
11278     position = cpl_table_get_data_int(slits, "position");
11279     length   = cpl_table_get_data_int(slits, "length");
11280     
11281     exslit = cpl_calloc(nslits, sizeof(cpl_image *));
11282 
11283     for (i = 0; i < nslits; i++) {
11284 
11285          if (length == 0)
11286              continue;
11287 
11288         /*
11289          * Note that the x coordinate of the reference pixels on the CCD
11290          * is taken arbitrarily at the top end of each slit. This wouldn't
11291          * be entirely correct in case of curved slits, or in presence of
11292          * heavy distortions: in such cases the spatial resampling is
11293          * really performed across a wide range of wavelengths. But
11294          * the lag between top and bottom spectral curvature models
11295          * would introduce even in such cases negligible effects on
11296          * the spectral spatial resampling.
11297          */
11298 
11299         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
11300 
11301         start_pixel = refpixel - pixel_below;
11302         if (start_pixel < 1)
11303             start_pixel = 1;
11304 
11305         end_pixel = refpixel + pixel_above;
11306         if (end_pixel > nx)
11307             end_pixel = nx;
11308 
11309         /*
11310          * Recover from the table of spectral curvature coefficients
11311          * the curvature polynomials.
11312          */
11313 
11314         missing_top = 0;
11315         polytop = cpl_polynomial_new(1);
11316         for (k = 0; k <= order; k++) {
11317             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
11318             if (null) {
11319                 cpl_polynomial_delete(polytop);
11320                 missing_top = 1;
11321                 break;
11322             }
11323             cpl_polynomial_set_coeff(polytop, &k, coeff);
11324         }
11325 
11326         missing_bot = 0;
11327         polybot = cpl_polynomial_new(1);
11328         for (k = 0; k <= order; k++) {
11329             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
11330             if (null) {
11331                 cpl_polynomial_delete(polybot);
11332                 missing_bot = 1;
11333                 break;
11334             }
11335             cpl_polynomial_set_coeff(polybot, &k, coeff);
11336         }
11337 
11338         if (missing_top && missing_bot) {
11339             cpl_msg_debug(func, "Slit %d was not traced: no extraction!",
11340                           slit_id[i]);
11341             continue;
11342         }
11343 
11344         /*
11345          * In case just one of the two edges was not traced, the other
11346          * edge curvature model is duplicated and shifted to the other
11347          * end of the slit: better than nothing!
11348          */
11349 
11350         if (missing_top) {
11351             cpl_msg_debug(func, "Upper edge of slit %d was not traced: "
11352                           "the spectral curvature of the lower edge "
11353                           "is used instead.", slit_id[i]);
11354             polytop = cpl_polynomial_duplicate(polybot);
11355             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11356             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11357             k = 0;
11358             coeff = cpl_polynomial_get_coeff(polybot, &k);
11359             coeff += ytop - ybot;
11360             cpl_polynomial_set_coeff(polytop, &k, coeff);
11361         }
11362 
11363         if (missing_bot) {
11364             cpl_msg_debug(func, "Lower edge of slit %d was not traced: "
11365                           "the spectral curvature of the upper edge "
11366                           "is used instead.", slit_id[i]);
11367             polybot = cpl_polynomial_duplicate(polytop);
11368             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
11369             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
11370             k = 0;
11371             coeff = cpl_polynomial_get_coeff(polytop, &k);
11372             coeff -= ytop - ybot;
11373             cpl_polynomial_set_coeff(polybot, &k, coeff);
11374         }
11375 
11376         /*
11377          * Allocate image for current extracted slit
11378          */
11379 
11380         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
11381         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
11382         npseudo = ceil(top-bot) + 1;
11383 
11384         if (npseudo < 1) {
11385             cpl_polynomial_delete(polytop);
11386             cpl_polynomial_delete(polybot);
11387             cpl_msg_debug(func, "Slit %d was badly traced: no extraction!",
11388                           slit_id[i]);
11389             continue;
11390         }
11391 
11392         exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT);
11393         xdata = cpl_image_get_data(exslit[i]);
11394 
11395         /*
11396          * Write interpolated spectral values to remapped slit spectrum.
11397          */
11398 
11399         for (x = start_pixel; x < end_pixel; x++) {
11400             top = cpl_polynomial_eval_1d(polytop, x, NULL);
11401             bot = cpl_polynomial_eval_1d(polybot, x, NULL);
11402             itop = top + 1;
11403             ibot = bot;
11404             if (itop < 0)
11405                 itop = 0;
11406             if (itop > ny - 1)
11407                 itop = ny - 1;
11408             if (ibot < 0)
11409                 ibot = 0;
11410             if (ibot > ny - 1)
11411                 ibot = ny - 1;
11412             for (y = ibot; y < itop; y++) {
11413                  lambda11 = wdata[x + y*nx];
11414                  if (lambda11 < 1.0)        /* Impossible wavelength */
11415                      continue;
11416                  space11 = sdata[x + y*nx];
11417                  if (space11 < 0.0)         /* Impossible spatial coordinate */
11418                      continue;
11419                  lambda01 = wdata[x - 1 + y*nx];
11420                  if (lambda01 < 1.0)        /* Impossible wavelength */
11421                      continue;
11422                  space01 = sdata[x - 1 + y*nx];
11423                  if (space01 < 0.0)         /* Impossible spatial coordinate */
11424                      continue;
11425 
11426                  shift = 0;
11427 
11428 /****+
11429                  if (wdata[x + (y+1)*nx] > 1.0) {
11430                      if (wdata[x + (y+1)*nx] - lambda11 > 0) {
11431                          shift = -1;
11432                          while (wdata[x + shift + (y+1)*nx] - lambda11 > 0)
11433                              shift--;
11434                          if (lambda11 - wdata[x + shift + (y+1)*nx] > 
11435                              wdata[x + shift + 1 + (y+1)*nx] - lambda11) {
11436                              shift++;
11437                          }
11438                      }
11439                      else {
11440                          shift = 1;
11441                          while (wdata[x + shift + (y+1)*nx] - lambda11 < 0)
11442                              shift++;
11443                          if (wdata[x + shift + (y+1)*nx] - lambda11 >
11444                              lambda11 - wdata[x + shift + 1 + (y+1)*nx]) {
11445                              shift--;
11446                          }
11447                      }
11448                  }
11449 ****/
11450 
11451 /****
11452 printf("y = %d, shift = %d\n", y, shift);
11453 ****/
11454 
11455                  lambda10 = wdata[x + shift + (y+1)*nx];
11456                  if (lambda10 < 1.0)        /* Impossible wavelength */
11457                      continue;
11458                  space10 = sdata[x + shift + (y+1)*nx];
11459                  if (space10 < 0.0)         /* Impossible spatial coordinate */
11460                      continue;
11461                  lambda00 = wdata[x - 1 + shift + (y+1)*nx];
11462                  if (lambda00 < 1.0)        /* Impossible wavelength */
11463                      continue;
11464                  space00 = sdata[x - 1 + shift + (y+1)*nx];
11465                  if (space00 < 0.0)         /* Impossible spatial coordinate */
11466                      continue;
11467                  
11468                  /*
11469                   * Find the variation in lambda and space in this
11470                   * position for each CCD pixel (both quantities are 
11471                   * expected to be positive).
11472                   */
11473 
11474                  dL = lambda11 - lambda01;
11475                  dS = space11 - space10;
11476 
11477                  /*
11478                   * Find the position (L,S) of the output pixel 
11479                   * (by integer truncation).
11480                   */
11481 
11482                  L = (lambda11 - blue)/dispersion + 0.5;
11483                  S = space11 + 0.5;                   /* Counted from top! */
11484 
11485                  if (L < 0 || L >= nlambda)
11486                      continue;
11487                  if (S < 0 || S > npseudo)
11488                      continue;
11489 
11490                  /*
11491                   * Find the coordinate of pixel (L,S)
11492                   */
11493 
11494                  lambda = blue + L*dispersion;
11495                  space  = S;
11496 
11497                  /*
11498                   * Find the interpolation point on the CCD: it is
11499                   * defined as the (positive) distance from current
11500                   * CCD pixel (x,y) of the interpolation point (x',y'),
11501                   * measured in CCD pixels. The interpolation point
11502                   * is located between the four CCD pixels selected
11503                   * above.
11504                   */
11505 
11506                  xfrac = (lambda11-lambda)/dL;
11507                  yfrac = (space11-space)/dS;
11508 
11509 /*
11510 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0)
11511 printf("xyfrac = %f, %f\n", xfrac, yfrac);
11512 */
11513 
11514                  /*
11515                   * Get the four values to interpolate
11516                   */
11517 
11518                  value11 = data[x + y*nx];
11519                  value01 = data[x - 1 + y*nx];
11520                  value10 = data[x + shift + (y+1)*nx];
11521                  value00 = data[x + shift - 1 + (y+1)*nx];
11522 
11523                  /*
11524                   * Interpolation
11525                   */
11526 
11527                  value1 = (1-xfrac)*value11 + xfrac*value01;
11528                  value0 = (1-xfrac)*value10 + xfrac*value00;
11529                  value  = (1-yfrac)*value1  + yfrac*value0;
11530 
11531                  /*
11532                   * Write this value to the appropriate (L,S) coordinate
11533                   * on output slit
11534                   */
11535 
11536                  xdata[L + nlambda*(npseudo-S)] = value;
11537                  
11538             }
11539         }
11540         cpl_polynomial_delete(polytop);
11541         cpl_polynomial_delete(polybot);
11542     }
11543 
11544     /*
11545      * Now all the slits images are copied to a single image
11546      */
11547 
11548     ysize = 0;
11549     for (i = 0; i < nslits; i++)
11550         if (exslit[i])
11551             ysize += cpl_image_get_size_y(exslit[i]);
11552 
11553     remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT);
11554 
11555     yint = -1;
11556     for (i = 0; i < nslits; i++) {
11557         if (exslit[i]) {
11558             yint += cpl_image_get_size_y(exslit[i]);
11559             cpl_image_copy(remapped, exslit[i], 1, ysize - yint);
11560             cpl_image_delete(exslit[i]);
11561             cpl_table_set_int(slits, "position", i, ysize - yint - 1);
11562         }
11563     }
11564 
11565     cpl_free(exslit);
11566 
11567     return remapped;
11568 
11569 }
11570 
11571 
11604 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap,
11605                              double dispersion, double factor, int minpoints,
11606                              cpl_image *skymap)
11607 {
11608     const char *func = "mos_sky_map_super";
11609 
11610     cpl_vector **vector;
11611     cpl_vector **wvector;
11612     double       firstLambda, lastLambda;
11613     double       lambda, lambda1, lambda2;
11614     double       value, value1, value2;
11615     double       frac;
11616     float        min, max;
11617     int         *count;
11618     int          nbin, bin;
11619     int          nx, ny, npix;
11620     int          first_valid, valid_bins;
11621     int          i, j;
11622 
11623     cpl_table   *sky;
11624     double      *sky_spectrum;
11625     double      *sky_wave;
11626     float       *data;
11627     float       *sdata;
11628     float       *kdata;
11629 
11630 
11631     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11632         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11633         return NULL;
11634     }
11635     
11636     if (dispersion <= 0.0) {
11637         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11638         cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message());
11639         return NULL;
11640     }
11641 
11642     nx = cpl_image_get_size_x(spectra);
11643     ny = cpl_image_get_size_y(spectra);
11644     npix = nx * ny;
11645 
11646     if (nx != cpl_image_get_size_x(wavemap) ||
11647         ny != cpl_image_get_size_y(wavemap) ||
11648         nx != cpl_image_get_size_x(skymap) ||
11649         ny != cpl_image_get_size_y(skymap)) {
11650         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11651         cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message());
11652         return NULL;
11653     }
11654 
11655     if (factor < 1.0) {
11656         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11657         cpl_msg_error(func, "Undersampling (%f): %s", factor, 
11658                       cpl_error_get_message());
11659         return NULL;
11660     }
11661 
11662     if (minpoints < 0) {
11663         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11664         cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message());
11665         return NULL;
11666     }
11667 
11668     dispersion /= factor;
11669 
11670 
11671     /*
11672      * Find bluest and reddest wavelengths in the whole image
11673      */
11674 
11675     data = cpl_image_get_data(wavemap);
11676 
11677     for (i = 0; i < npix; i++) {
11678         if (data[i] > 1.0) {
11679             min = max = data[i];
11680             j = i+1;
11681             break;
11682         }
11683     }
11684 
11685     for (i = j; i < npix; i++) {
11686         if (data[i] < 1.0)      /* Impossible wavelength */
11687             continue;
11688         if (min > data[i])
11689             min = data[i];
11690         if (max < data[i])
11691             max = data[i];
11692     }
11693 
11694     firstLambda = min;
11695     lastLambda = max;
11696 
11697 
11698     /*
11699      * Determine length of median spectrum
11700      */
11701 
11702     nbin = (lastLambda - firstLambda) / dispersion;
11703 
11704     /*
11705      * Count how many values will be found for each spectral bin.
11706      * The ith bin has a wavelength range from firstLambda + i*dispersion
11707      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
11708      * it is assigned to its central wavelength.
11709      */
11710 
11711     count = cpl_calloc(nbin, sizeof(int));
11712 
11713     data = cpl_image_get_data(wavemap);
11714 
11715     for (i = 0; i < npix; i++) {
11716         if (data[i] < 1.0)
11717             continue;
11718         bin = (data[i] - firstLambda) / dispersion;
11719         if (bin < nbin)                               /* Safer */
11720             count[bin]++;
11721     }
11722 
11723     valid_bins = 0;
11724     for (i = 0; i < nbin; i++)
11725         if (count[i] >= minpoints)
11726             valid_bins++;
11727 
11728     if (valid_bins < nbin/3) {
11729         cpl_msg_warning(func, "Cannot determine a good global sky "
11730                         "spectrum from input data");
11731         return NULL;
11732     }
11733 
11734 
11735     /*
11736      * Allocate an array of vectors with the appropriate size, to
11737      * contain a list of all the spectral pixels values. At the same
11738      * time, reset the array of counters (because we will have to
11739      * count again...).
11740      */
11741 
11742     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
11743     wvector = cpl_calloc(nbin, sizeof(cpl_vector *));
11744     for (i = 0; i < nbin; i++) {
11745         if (count[i] >= minpoints) {
11746             vector[i] = cpl_vector_new(count[i]);
11747             wvector[i] = cpl_vector_new(count[i]);
11748         }
11749         count[i] = 0;
11750     }
11751 
11752 
11753     /*
11754      * Read the wavemap and the spectral images, and add the data values
11755      * to the appropriate wavelength bins
11756      */
11757 
11758     data  = cpl_image_get_data(wavemap);
11759     sdata = cpl_image_get_data(spectra);
11760 
11761     for (i = 0; i < npix; i++) {
11762         if (data[i] < 1.0)
11763             continue;
11764         bin = (data[i] - firstLambda) / dispersion;
11765         if (bin < nbin) {                             /* Safer */
11766             if (vector[bin]) {
11767                 cpl_vector_set(vector[bin], count[bin], sdata[i]);
11768                 cpl_vector_set(wvector[bin], count[bin], data[i]);
11769             }
11770             count[bin]++;
11771         }
11772     }
11773 
11774 
11775     /*
11776      * Compute the median flux for each wavelength bin, and destroy
11777      * at the same time the used vectors
11778      */
11779 
11780     sky_spectrum = cpl_calloc(nbin, sizeof(double));
11781     sky_wave = cpl_calloc(nbin, sizeof(double));
11782     for (i = 0; i < nbin; i++) {
11783         if (vector[i]) {
11784             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
11785             sky_wave[i] = cpl_vector_get_median_const(wvector[i]);
11786             cpl_vector_delete(vector[i]);
11787             cpl_vector_delete(wvector[i]);
11788         }
11789     }
11790 
11791     cpl_free(vector);
11792     cpl_free(wvector);
11793 
11794 
11795     /*
11796      * Here possible gaps in the final spectrum are filled by interpolation
11797      */
11798 
11799     for (i = 0; i < nbin; i++) {
11800         if (count[i] >= minpoints) {
11801             first_valid = i;
11802             break;
11803         }
11804     }
11805     
11806     for (i = first_valid; i < nbin; i++) {
11807         if (count[i] < minpoints) {
11808             sky_wave[i] = firstLambda + (i+0.5)*dispersion;
11809             for (j = i+1; j < nbin; j++) {
11810                 if (count[j] >= minpoints) {
11811                     if (sky_wave[j] - sky_wave[i-1] < 0.1) {
11812                         sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1])
11813                                         / 2;
11814                     }
11815                     else {
11816                         frac = (sky_wave[i] - sky_wave[i-1]) 
11817                              / (sky_wave[j] - sky_wave[i-1]);
11818                         sky_spectrum[i] = frac * sky_spectrum[j]
11819                                         + (1 - frac) * sky_spectrum[i-1];
11820                     }
11821                 }
11822             }
11823         }
11824     }
11825 
11826 
11827     /*
11828      * Create the output table
11829      */
11830 
11831     sky = cpl_table_new(nbin);
11832     cpl_table_wrap_double(sky, sky_wave, "wavelength");
11833     cpl_table_wrap_double(sky, sky_spectrum, "sky");
11834     cpl_table_wrap_int(sky, count, "npoints");
11835 
11836 
11837     /*
11838      * Fill the sky map
11839      */
11840 
11841     data  = cpl_image_get_data(wavemap);
11842     sdata = cpl_image_get_data(spectra);
11843     kdata = cpl_image_get_data(skymap);
11844 
11845     for (i = 0; i < npix; i++) {
11846 
11847         /*
11848          * Currently based on linear interpolation
11849          */
11850 
11851         lambda = data[i];
11852         if (lambda < 1.0)
11853             continue;
11854         bin = (lambda - firstLambda) / dispersion;
11855         lambda1 = sky_wave[bin];
11856         value1 = sky_spectrum[bin];
11857         if (lambda1 < lambda) {
11858             bin++;
11859             if (bin < nbin) {
11860                 lambda2 = sky_wave[bin];
11861                 value2  = sky_spectrum[bin];
11862                 if (lambda2 - lambda1 < 0.1) {
11863                     value = (value1 + value2) / 2;
11864                 }
11865                 else {
11866                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11867                     value = frac * value2 + (1 - frac) * value1;
11868                 }
11869             }
11870             else {
11871                 value = value1;
11872             }
11873         }
11874         else {
11875             if (bin > 0) {
11876                 bin--;
11877                 lambda2 = lambda1;
11878                 value2  = value1;
11879                 lambda1 = sky_wave[bin];
11880                 value1  = sky_spectrum[bin];
11881                 if (lambda2 - lambda1 < 0.1) {
11882                     value = (value1 + value2) / 2;
11883                 }
11884                 else {
11885                     frac = (lambda - lambda1) / (lambda2 - lambda1);
11886                     value = frac * value2 + (1 - frac) * value1;
11887                 }
11888             }
11889             else {
11890                 value = value1;
11891             }
11892         }
11893         kdata[i] = value;
11894     }
11895 
11896     if (first_valid)
11897         cpl_table_erase_window(sky, 0, first_valid);
11898 
11899     return sky;
11900 
11901 }
11902 
11903 
11937 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap,
11938                        double dispersion, cpl_image *skymap)
11939 {
11940     const char *func = "mos_sky_map";
11941 
11942     cpl_vector **vector;
11943     double       firstLambda, lastLambda;
11944     double       lambda, lambda1, lambda2;
11945     double       value, value1, value2;
11946     float        min, max;
11947     int         *count;
11948     int          nbin, bin;
11949     int          nx, ny, npix;
11950     int          i, j;
11951 
11952     cpl_table   *sky;
11953     double      *sky_spectrum;
11954     float       *data;
11955     float       *sdata;
11956     float       *kdata;
11957     double      *wdata;
11958 
11959 
11960     if (spectra == NULL || wavemap == NULL || skymap == NULL) {
11961         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
11962         return NULL;
11963     }
11964     
11965     if (dispersion <= 0.0) {
11966         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
11967         return NULL;
11968     }
11969 
11970     nx = cpl_image_get_size_x(spectra);
11971     ny = cpl_image_get_size_y(spectra);
11972     npix = nx * ny;
11973 
11974     if (nx != cpl_image_get_size_x(wavemap) ||
11975         ny != cpl_image_get_size_y(wavemap) ||
11976         nx != cpl_image_get_size_x(skymap) ||
11977         ny != cpl_image_get_size_y(skymap)) {
11978         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
11979         return NULL;
11980     }
11981 
11982 
11983     /*
11984      * Find bluest and reddest wavelengths in the whole image
11985      */
11986 
11987     data = cpl_image_get_data(wavemap);
11988 
11989     for (i = 0; i < npix; i++) {
11990         if (data[i] > 1.0) {
11991             min = max = data[i];
11992             j = i+1;
11993             break;
11994         }
11995     }
11996 
11997     for (i = j; i < npix; i++) {
11998         if (data[i] < 1.0)      /* Impossible wavelength */
11999             continue;
12000         if (min > data[i])
12001             min = data[i];
12002         if (max < data[i])
12003             max = data[i];
12004     }
12005 
12006     firstLambda = min;
12007     lastLambda = max;
12008 
12009 
12010     /*
12011      * Determine length of median spectrum
12012      */
12013 
12014     nbin = (lastLambda - firstLambda) / dispersion;
12015 
12016     /*
12017      * Count how many values will be found for each spectral bin.
12018      * The ith bin has a wavelength range from firstLambda + i*dispersion
12019      * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and
12020      * it is assigned to its central wavelength.
12021      */
12022 
12023     count = cpl_calloc(nbin, sizeof(int));
12024 
12025     data = cpl_image_get_data(wavemap);
12026 
12027     for (i = 0; i < npix; i++) {
12028         if (data[i] < 1.0)
12029             continue;
12030         bin = (data[i] - firstLambda) / dispersion;
12031         if (bin < nbin)                               /* Safer */
12032             count[bin]++;
12033     }
12034 
12035 
12036     /*
12037      * Allocate an array of vectors with the appropriate size, to
12038      * contain a list of all the spectral pixels values. At the same
12039      * time, reset the array of counters (because we will have to
12040      * count again...).
12041      */
12042 
12043     vector = cpl_calloc(nbin, sizeof(cpl_vector *));
12044     for (i = 0; i < nbin; i++) {
12045         if (count[i])
12046             vector[i] = cpl_vector_new(count[i]);
12047         else
12048             vector[i] = NULL;
12049         count[i] = 0;
12050     }
12051 
12052 
12053     /*
12054      * Read the wavemap and the spectral images, and add the data values
12055      * to the appropriate wavelength bins
12056      */
12057 
12058     data  = cpl_image_get_data(wavemap);
12059     sdata = cpl_image_get_data(spectra);
12060 
12061     for (i = 0; i < npix; i++) {
12062         if (data[i] < 1.0)
12063             continue;
12064         bin = (data[i] - firstLambda) / dispersion;
12065         if (bin < nbin) {                             /* Safer */
12066             cpl_vector_set(vector[bin], count[bin], sdata[i]);
12067             count[bin]++;
12068         }
12069     }
12070 
12071 
12072     /*
12073      * Compute the median flux for each wavelength bin, and destroy
12074      * at the same time the used vectors
12075      */
12076 
12077     sky_spectrum = cpl_calloc(nbin, sizeof(double));
12078     for (i = 0; i < nbin; i++) {
12079         if (vector[i]) {
12080             sky_spectrum[i] = cpl_vector_get_median_const(vector[i]);
12081             cpl_vector_delete(vector[i]);
12082         }
12083     }
12084 
12085     cpl_free(vector);
12086 
12087 
12088     /*
12089      * Here possible gaps in the final spectrum should be filled
12090      * by interpolation
12091      */
12092 
12093     /* ... */
12094 
12095     /*
12096      * Create the output table
12097      */
12098 
12099     sky = cpl_table_new(nbin);
12100     cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE);
12101     cpl_table_set_column_unit(sky, "wavelength", "pixel");
12102     cpl_table_wrap_double(sky, sky_spectrum, "sky");
12103     cpl_table_wrap_int(sky, count, "npoints");
12104     for (i = 0; i < nbin; i++)
12105         cpl_table_set_double(sky, "wavelength", i, 
12106                              firstLambda + (i+0.5)*dispersion);
12107 
12108 
12109     /*
12110      * Fill the sky map
12111      */
12112 
12113     data  = cpl_image_get_data(wavemap);
12114     sdata = cpl_image_get_data(spectra);
12115     kdata = cpl_image_get_data(skymap);
12116     wdata = cpl_table_get_data_double(sky, "wavelength");
12117 
12118     for (i = 0; i < npix; i++) {
12119 
12120         /*
12121          * Currently based on linear interpolation
12122          */
12123 
12124         lambda = data[i];
12125         if (lambda < 1.0)
12126             continue;
12127         bin = (lambda - firstLambda) / dispersion;
12128         lambda1 = wdata[bin];
12129         value1 = sky_spectrum[bin];
12130         if (lambda1 < lambda) {
12131             bin++;
12132             if (bin < nbin) {
12133                 lambda2 = wdata[bin];
12134                 value2  = sky_spectrum[bin];
12135                 value   = ((lambda2 - lambda)*value1 
12136                         +  (lambda - lambda1)*value2) / dispersion;
12137             }
12138             else {
12139                 value = value1;
12140             }
12141         }
12142         else {
12143             if (bin > 0) {
12144                 bin--;
12145                 lambda2 = lambda1;
12146                 value2  = value1;
12147                 lambda1 = wdata[bin];
12148                 value1  = sky_spectrum[bin];
12149                 value   = ((lambda2 - lambda)*value1 
12150                         +  (lambda - lambda1)*value2)/dispersion;
12151             }
12152             else {
12153                 value = value1;
12154             }
12155         }
12156         kdata[i] = value;
12157     }
12158 
12159     return sky;
12160 
12161 }
12162 
12163 
12179 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits)
12180 {
12181     const char *func = "mos_sky_local_old";
12182 
12183     cpl_image *exslit;
12184     cpl_image *sky;
12185     cpl_image *skymap;
12186     float     *data;
12187     float     *sdata;
12188     int        nx, ny;
12189     int        xlow, ylow, xhig, yhig;
12190     int        nslits;
12191     int       *slit_id;
12192     int       *position;
12193     int       *length;
12194     int        i, j, k;
12195 
12196 
12197     if (spectra == NULL) {
12198         cpl_msg_error(func, 
12199                       "A scientific rectified spectral image must be given");
12200         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12201         return NULL;
12202     }
12203 
12204     if (slits == NULL) {
12205         cpl_msg_error(func, "A slits position table must be given");
12206         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12207         return NULL;
12208     }
12209 
12210     nslits   = cpl_table_get_nrow(slits);
12211     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12212     position = cpl_table_get_data_int(slits, "position");
12213     length   = cpl_table_get_data_int(slits, "length");
12214 
12215     nx = cpl_image_get_size_x(spectra);
12216     ny = cpl_image_get_size_y(spectra);
12217 
12218     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12219 
12220     xlow = 1;
12221     xhig = nx;
12222     for (i = 0; i < nslits; i++) {
12223 
12224         if (length[i] == 0)
12225             continue;
12226 
12227         /*
12228          * Define the extraction boundaries. We DON'T write:
12229          *
12230          * ylow = position[i];
12231          * yhig = ylow + length[i];
12232          *
12233          * because the cpl_image pixels are counted from 1, and because in
12234          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12235          */
12236 
12237         ylow = position[i] + 1;
12238         yhig = ylow + length[i] - 1;
12239 
12240         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12241         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12242         cpl_image_delete(exslit);
12243 
12244         data   = cpl_image_get_data(skymap);
12245         data  += nx * position[i];
12246 
12247         for (j = 0; j < length[i]; j++) {
12248             sdata  = cpl_image_get_data(sky);
12249             for (k = 0; k < nx; k++) {
12250                 *data++ = *sdata++;
12251             }
12252         }
12253 
12254         cpl_image_delete(sky);
12255     }
12256 
12257     return skymap;
12258 
12259 }
12260 
12261 
12281 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order)
12282 {
12283     const char *func = "mos_sky_local";
12284 
12285     char        name[MAX_COLNAME];
12286 
12287     cpl_polynomial *fit;
12288     cpl_vector     *points;
12289     cpl_vector     *values;
12290     cpl_vector     *keep_points;
12291     cpl_vector     *keep_values;
12292     cpl_image      *exslit;
12293     cpl_image      *sky;
12294     cpl_image      *subtracted;
12295     cpl_image      *profile;
12296     cpl_image      *skymap;
12297     cpl_table      *objects;
12298     float          *data;
12299     float          *sdata;
12300     float          *xdata;
12301     double         *vdata;
12302     double         *pdata;
12303     double          median;
12304     int             nx, ny;
12305     int             xlow, ylow, xhig, yhig;
12306     int             nslits;
12307     int            *slit_id;
12308     int            *position;
12309     int            *length;
12310     int            *is_sky;
12311     int             nsky, nbad;
12312     int             maxobjects;
12313     int             margin = 3;
12314     int             radius = 6;
12315     int             i, j, k;
12316 
12317 
12318     if (spectra == NULL) {
12319         cpl_msg_error(func, 
12320                       "A scientific rectified spectral image must be given");
12321         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12322         return NULL;
12323     }
12324 
12325     if (slits == NULL) {
12326         cpl_msg_error(func, "A slits position table must be given");
12327         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12328         return NULL;
12329     }
12330 
12331     if (order < 0) {
12332         cpl_msg_error(func, "Invalid fit order");
12333         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12334         return NULL;
12335     }
12336 
12337     nslits   = cpl_table_get_nrow(slits);
12338     slit_id  = cpl_table_get_data_int(slits, "slit_id");
12339     position = cpl_table_get_data_int(slits, "position");
12340     length   = cpl_table_get_data_int(slits, "length");
12341 
12342     nx = cpl_image_get_size_x(spectra);
12343     ny = cpl_image_get_size_y(spectra);
12344 
12345     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12346 
12347     xlow = 1;
12348     xhig = nx;
12349     for (i = 0; i < nslits; i++) {
12350 
12351         if (length[i] == 0)
12352             continue;
12353 
12354         /*
12355          * Define the extraction boundaries. We DON'T write:
12356          *
12357          * ylow = position[i];
12358          * yhig = ylow + length[i];
12359          *
12360          * because the cpl_image pixels are counted from 1, and because in
12361          * cpl_image_extract() the coordinates of the last pixel are inclusive.
12362          */
12363 
12364         ylow = position[i] + 1;
12365         yhig = ylow + length[i] - 1;
12366 
12367         exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig);
12368         sky    = cpl_image_collapse_median_create(exslit, 0, 0, 1);
12369         cpl_image_delete(exslit);
12370 
12371         data   = cpl_image_get_data(skymap);
12372         data  += nx * position[i];
12373 
12374         for (j = 0; j < length[i]; j++) {
12375             sdata  = cpl_image_get_data(sky);
12376             for (k = 0; k < nx; k++) {
12377                 *data++ = *sdata++;
12378             }
12379         }
12380 
12381         cpl_image_delete(sky);
12382     }
12383 
12384 
12385     /*
12386      * Preliminary sky-subtracted image
12387      */
12388 
12389     subtracted = cpl_image_duplicate(spectra);
12390     cpl_image_subtract(subtracted, skymap);
12391     cpl_image_delete(skymap);
12392 
12393 
12394     /*
12395      * Detect objects positions in all slits
12396      */
12397 
12398     objects = cpl_table_duplicate(slits);
12399     profile = mos_detect_objects(subtracted, objects, margin, radius, 0);
12400     cpl_image_delete(profile);
12401     cpl_image_delete(subtracted);
12402 
12403 
12404     /*
12405      * Flag the sky pixels. Note that maxobjects is intentionally 
12406      * the max number of objects increased by one.
12407      */
12408 
12409     maxobjects = 1;
12410     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12411     while (cpl_table_has_column(objects, name)) {
12412         maxobjects++;
12413         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
12414     }
12415 
12416     is_sky = cpl_calloc(ny, sizeof(int));
12417 
12418     for (i = 0; i < nslits; i++) {
12419 
12420         if (length[i] == 0)
12421             continue;
12422 
12423         ylow = position[i] + margin;
12424         yhig = position[i] + length[i] - margin;
12425 
12426         for (j = ylow; j < yhig; j++)
12427             is_sky[j] = 1;
12428 
12429         for (j = 1; j < maxobjects; j++) {
12430             snprintf(name, MAX_COLNAME, "object_%d", j);
12431             if (cpl_table_is_valid(objects, name, i)) {
12432                 snprintf(name, MAX_COLNAME, "start_%d", j);
12433                 ylow = cpl_table_get_int(objects, name, i, NULL);
12434                 snprintf(name, MAX_COLNAME, "end_%d", j);
12435                 yhig = cpl_table_get_int(objects, name, i, NULL);
12436                 for (k = ylow; k <= yhig; k++)
12437                     is_sky[k] = 0;
12438             }
12439         }
12440 
12441 
12442         /*
12443          * Eliminate isolated sky points
12444          */
12445 
12446         ylow = position[i] + margin + 1;
12447         yhig = position[i] + length[i] - margin - 1;
12448 
12449         for (j = ylow; j < yhig; j++)
12450             if (is_sky[j])
12451                 if (is_sky[j-1] == 0 && is_sky[j+1] == 0)
12452                     is_sky[j] = 0;
12453 
12454     }
12455 
12456 
12457     /*
12458      * Determination of the sky map
12459      */
12460 
12461     skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
12462 
12463     for (i = 0; i < nslits; i++) {
12464 
12465         if (length[i] == 0)
12466             continue;
12467 
12468         ylow = position[i];
12469         yhig = ylow + length[i];
12470 
12471         nsky = 0;
12472         for (j = ylow; j < yhig; j++)
12473             if (is_sky[j])
12474                 nsky++;
12475 
12476         if (nsky > order + 1) {
12477             if (order) {
12478                 points = cpl_vector_new(nsky);
12479                 nsky = 0;
12480                 for (j = ylow; j < yhig; j++) {
12481                     if (is_sky[j]) {
12482                         cpl_vector_set(points, nsky, j);
12483                         nsky++;
12484                     }
12485                 }
12486 
12487                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12488                 xdata = cpl_image_get_data(exslit);
12489                 values = cpl_vector_new(nsky);
12490 
12491                 for (j = 0; j < nx; j++) {
12492                     nsky = 0;
12493                     for (k = ylow; k < yhig; k++) {
12494                         if (is_sky[k]) {
12495                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12496                             nsky++;
12497                         }
12498                     }
12499 
12500                     /*
12501                      * Eliminate obvious outliers
12502                      */
12503 
12504                     median = cpl_vector_get_median_const(values);
12505                     vdata = cpl_vector_get_data(values);
12506                     pdata = cpl_vector_get_data(points);
12507                     nbad = 0;
12508                     for (k = 0; k < nsky; k++) {
12509                         if (fabs(vdata[k] - median) < 100) {
12510                             if (nbad) {
12511                                 vdata[k-nbad] = vdata[k];
12512                                 pdata[k-nbad] = pdata[k];
12513                             }
12514                         }
12515                         else
12516                             nbad++;
12517                     }
12518 
12519                     if (nsky == nbad)
12520                         continue;
12521 
12522                     if (nbad && nsky - nbad > order + 1) {
12523                         keep_values = values;
12524                         keep_points = points;
12525                         values = cpl_vector_wrap(nsky-nbad, vdata);
12526                         points = cpl_vector_wrap(nsky-nbad, pdata);
12527                     }
12528 
12529                     if (nsky - nbad > order + 1) {
12530 
12531                         fit = cpl_polynomial_fit_1d_create(points, values, 
12532                                                            order, NULL);
12533 
12534                         if (fit) {
12535                             for (k = ylow; k < yhig; k++) {
12536                                 xdata[j+(k-ylow)*nx] = 
12537                                          cpl_polynomial_eval_1d(fit, k, NULL);
12538                             }
12539 
12540                             cpl_polynomial_delete(fit);
12541                         }
12542                         else
12543                             cpl_error_reset();
12544                     }
12545                     else {
12546                         for (k = 0; k < nsky; k++) {
12547                             xdata[j+k*nx] = median;
12548                         }
12549                     }
12550 
12551                     if (nbad && nsky - nbad > order + 1) {
12552                         cpl_vector_unwrap(values);
12553                         cpl_vector_unwrap(points);
12554                         values = keep_values;
12555                         points = keep_points;
12556                     }
12557 
12558                     if (nbad) {
12559                         nsky = 0;
12560                         for (k = ylow; k < yhig; k++) {
12561                             if (is_sky[k]) {
12562                                 cpl_vector_set(points, nsky, k);
12563                                 nsky++;
12564                             }
12565                         }
12566                     }
12567 
12568                 }
12569 
12570                 cpl_vector_delete(values);
12571                 cpl_vector_delete(points);
12572 
12573                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12574                 cpl_image_delete(exslit);
12575 
12576             }
12577             else {
12578                 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig);
12579                 xdata = cpl_image_get_data(exslit);
12580                 values = cpl_vector_new(nsky);
12581 
12582                 for (j = 0; j < nx; j++) {
12583                     nsky = 0;
12584                     for (k = ylow; k < yhig; k++) {
12585                         if (is_sky[k]) {
12586                             cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]);
12587                             nsky++;
12588                         }
12589                     }
12590 
12591                     median = cpl_vector_get_median_const(values);
12592 
12593                     for (k = ylow; k < yhig; k++)
12594                         xdata[j+(k-ylow)*nx] = median;
12595 
12596                 }
12597 
12598                 cpl_vector_delete(values);
12599 
12600                 cpl_image_copy(skymap, exslit, 1, ylow+1);
12601                 cpl_image_delete(exslit);
12602             }
12603         }
12604         else
12605             cpl_msg_warning(func, "Too few sky points in slit %d", i + 1);
12606     }
12607 
12608     cpl_free(is_sky);
12609 
12610     return skymap;
12611 
12612 }
12613 
12614 
12636 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain,
12637                                  float threshold, float ratio)
12638 {
12639     const char *func = "mos_clean_cosmics";
12640 
12641     cpl_image  *smoothImage;
12642     cpl_table  *table;
12643     cpl_matrix *kernel;
12644     int        *xdata;
12645     int        *ydata;
12646     float      *idata;
12647     float      *sdata;
12648     float       sigma, sum, value, smoothValue;
12649     double      noise;
12650     int         count;
12651     float       fMax;
12652     int         iMin, iMax, jMin, jMax, iPosMax, jPosMax;
12653     int         xLen;
12654     int         yLen;
12655     int         nPix;
12656     int         first = 1;  /* position of first cosmic ray candidate
12657                                encountered while scanning the image */
12658     int         pos, i, j, k, l, ii, jj, iii = 0, jjj = 0;
12659     int         numCosmic = 0;
12660     int         found, foundContiguousCandidate;
12661     int        *cosmic;
12662   
12663 
12664     if (image == NULL)
12665         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12666 
12667 
12668     /*
12669      *  "cosmic" is a flags holder (initialized to zero):
12670      *
12671      *           -1 = candidate for cosmic ray
12672      *            0 = not a cosmic
12673      *            1 = a cosmic ray
12674      *            2 = member of current group of contiguous candidates
12675      *            3 = examined member of current group
12676      */
12677 
12678     xLen = cpl_image_get_size_x(image);
12679     yLen = cpl_image_get_size_y(image);
12680 
12681     if (xLen < 4 || yLen < 4)
12682         return CPL_ERROR_NONE;
12683 
12684     nPix = xLen * yLen;
12685 
12686     /*
12687      * Noise estimation from negative offsets in image. Note that this
12688      * assumes that the background level (skyLevel) has already been 
12689      * subtracted from the data. In this way we estimate the noise due 
12690      * to detector readout and to the background signal level (before 
12691      * it were removed). Theoretically this is given by 
12692      *
12693      *        noise = sqrt(ron^2 + skyLevel/gain)
12694      *
12695      * where ron is the read-out-noise. To this we will sum the noise 
12696      * contribution due to any increase of the signal above the background
12697      * by an amount scienceLevel. Theoretically the total noise is given by
12698      *
12699      *        totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain)
12700      *
12701      * that is
12702      *
12703      *        totalNoise = sqrt(noise^2 + scienceLevel/gain)
12704      *
12705      */
12706 
12707     idata = cpl_image_get_data(image);
12708     noise = 0.0;
12709     count = 0;
12710 
12711     for (i = 0; i < nPix; i++) {
12712         if (idata[i] < -0.00001) {
12713             noise -= idata[i];
12714             count++;
12715         }
12716     }
12717 
12718     noise /= count;
12719     noise *= 1.25;       /* Factor to convert average deviation to sigma */
12720 
12721     cosmic = cpl_calloc(nPix, sizeof(int));
12722 
12723     if (threshold < 0.)
12724         threshold = 4.0;
12725     if (ratio < 0.)
12726         ratio = 2.0;
12727 
12728     kernel = cpl_matrix_new(3, 3);
12729     cpl_matrix_fill(kernel, 1.0);
12730     cpl_matrix_set(kernel, 1, 1, 0.0);
12731     smoothImage = cpl_image_filter_median(image, kernel);
12732     cpl_matrix_delete(kernel);
12733     
12734     /*
12735      *  Loop on images pixels, searching for cosmic rays candidates.
12736      *  Border pixels are currently excluded (they cannot contain
12737      *  candidates), to avoid that the search for groups of contiguous
12738      *  pixels would ever go out of image boundaries. In future we may
12739      *  overcome this limit, adding an appropriate check when contiguous
12740      *  pixels are searched.
12741      */
12742 
12743     sdata = cpl_image_get_data(smoothImage);
12744 
12745     for (j = 1; j < yLen - 1; j++) {
12746         for (i = 1; i < xLen - 1; i++) {
12747             value = idata[i + j * xLen];
12748             smoothValue = sdata[i + j * xLen];
12749             if (smoothValue < 1.0)
12750                 smoothValue = 1.0;
12751             sigma = sqrt(noise * noise + smoothValue / gain);
12752             if (value - smoothValue >= threshold * sigma) 
12753                 cosmic[i + j * xLen] = -1;
12754         }
12755     }
12756 
12757     cpl_image_delete(smoothImage);
12758 
12759 
12760     /*
12761      *  Search for groups of contiguous cosmic rays candidates.
12762      */
12763 
12764     do {
12765         found = 0;
12766         for (pos = first; pos < nPix; pos++) {
12767             if (cosmic[pos] == -1) {
12768                 cosmic[pos] = 2;         /*  Candidate found.  */
12769                 i = pos % xLen;          /*  Its coordinates.  */
12770                 j = pos / xLen;
12771                 first = pos;
12772                 first++;      /* ???  really necessary? */
12773                 found = 1;
12774                 break;
12775             }
12776         }
12777 
12778         if (found) {
12779 
12780             /*
12781              *  Determine new group of contiguous cosmic rays candidates.
12782              *  Initialize the working box boundaries, iMin, iMax, jMin, jMax, 
12783              *  and the value of the max pixel and its position, fMax, iPosMax,
12784              *  jPosMax.
12785              */
12786 
12787             iMin = iMax = iPosMax = i;
12788             jMin = jMax = jPosMax = j;
12789             fMax = idata[i + j * xLen];
12790 
12791             do {
12792                 foundContiguousCandidate = 0;
12793                 for (l = 0; l <= 1; l++) {
12794                     for (k = 0; k <= 1; k++) {
12795 
12796                         /*
12797                          *  Looping on 4 pixels to North, East, South and West
12798                          */
12799 
12800                         ii = i + k - l;
12801                         jj = j + k + l - 1;
12802                         if (cosmic[ii + jj * xLen] == -1) {
12803                             foundContiguousCandidate = 1;
12804                             cosmic[ii + jj * xLen] = 2;
12805                                         /* Candidate belongs to current group */
12806                             iii = ii;   /* Keep its position */
12807                             jjj = jj;
12808 
12809                             /*
12810                              * Upgrade search box
12811                              */
12812 
12813                             if (ii < iMin)
12814                                 iMin = ii;
12815                             if (ii > iMax)
12816                                 iMax = ii;
12817                             if (jj < jMin)
12818                                 jMin = jj;
12819                             if (jj > jMax)
12820                                 jMax = jj;
12821 
12822                             if (idata[ii + jj * xLen] > fMax) {
12823                                 fMax = idata[ii + jj * xLen];
12824                                 iPosMax = ii;
12825                                 jPosMax = jj;
12826                             }
12827                         }
12828                     }
12829                 }
12830 
12831                 /*
12832                  *  We are done exploring the "cross". Now mark as "examined"
12833                  *  the current candidate (at the center of the cross):
12834                  */
12835 
12836                 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */
12837 
12838                 if (foundContiguousCandidate) {
12839 
12840                     /*
12841                      * Pass (arbitrarily) the coordinates of the LAST found 
12842                      * candidate
12843                      */
12844 
12845                     i = iii;
12846                     j = jjj;
12847 
12848                     /* 
12849                      * Skip the rest, continue loop on new candidate 
12850                      */
12851 
12852                     continue; 
12853                 }
12854 
12855 
12856                 /*
12857                  *  Look for leftovers in the (growing!) search box
12858                  */
12859 
12860                 for (l = jMin; l <= jMax; l++) {
12861                     for (k = iMin; k <= iMax; k++) {
12862                         if (cosmic[k + l * xLen] == 2) {
12863                             i = k;
12864                             j = l;
12865                             foundContiguousCandidate = 1;
12866                             break;
12867                         }
12868                     }
12869                     if (foundContiguousCandidate) 
12870                         break;
12871                 }
12872             } while (foundContiguousCandidate);
12873 
12874 
12875             /*
12876              *  No more contiguous candidates are found. Decide now
12877              *  whether the current group is a cosmic ray or not.
12878              */
12879 
12880             sum = 0.;                /* Sum of 8 pixels around max position */
12881             for (l = -1; l <= 1; l++) {
12882                 for (k = -1; k <= 1; k++) {
12883                     if (l != 0 || k != 0) {
12884                         sum += idata[iPosMax + k + (jPosMax + l) * xLen];
12885                     }
12886                 }
12887             }
12888 
12889             sum /= 8.;
12890             if (fMax > ratio * sum) {
12891                 for (l = jMin - 1; l <= jMax + 1; l++) {
12892                     for (k = iMin - 1; k <= iMax + 1; k++) {
12893                         if (cosmic[k + l * xLen] == 3) {
12894                             cosmic[k + l * xLen] = 1;
12895                             numCosmic++;
12896                         }
12897                     }
12898                 }
12899             }
12900             else {
12901                 for (l = jMin - 1; l <= jMax + 1; l++) {
12902                     for (k = iMin - 1; k <= iMax + 1; k++) {
12903                         if (cosmic[k + l * xLen] != -1) {
12904                             if (cosmic[k + l * xLen] == 1) 
12905                                 numCosmic--;
12906                             cosmic[k + l * xLen] = 0;
12907                         }
12908                     }
12909                 }
12910             }
12911         }
12912     } while (found);
12913 
12914 
12915     /*
12916      *  Prepare table containing cosmic rays coordinates. 
12917      */
12918 
12919     table = cpl_table_new(numCosmic);
12920     cpl_table_new_column(table, "x", CPL_TYPE_INT);
12921     cpl_table_new_column(table, "y", CPL_TYPE_INT);
12922     cpl_table_set_column_unit(table, "x", "pixel");
12923     cpl_table_set_column_unit(table, "y", "pixel");
12924     xdata = cpl_table_get_data_int(table, "x");
12925     ydata = cpl_table_get_data_int(table, "y");
12926 
12927     for (pos = 0, i = 0; pos < nPix; pos++) {
12928         if (cosmic[pos] == 1) {
12929             xdata[i] = (pos % xLen);
12930             ydata[i] = (pos / xLen);
12931             i++;
12932         }
12933     }
12934 
12935     mos_clean_bad_pixels(image, table, 1);
12936 
12937     cpl_free(cosmic);
12938     cpl_table_delete(table);
12939 
12940     return CPL_ERROR_NONE;
12941 
12942 }
12943 
12944 
12945 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table,
12946                                     int spectral)
12947 {
12948     const char *func = "mos_clean_cosmics";
12949  
12950     float       *idata;
12951     int         *isBadPix;
12952     int          i, j, k, d;
12953     int          xlen, ylen, totPix;
12954     int          nBadPixels = 0;
12955     int          sign, foundFirst;
12956     int         *xValue = NULL;
12957     int         *yValue = NULL;
12958     float        save = 0.;
12959     double       sumd;
12960     int          cx, cy;
12961     int          nPairs;
12962     float        estimate[4];
12963     int          sx[] = {0, 1, 1, 1};
12964     int          sy[] = {1,-1, 0, 1};
12965     int          searchHorizon = 100;
12966     int          percent = 15;
12967 
12968 
12969     if (image == NULL || table == NULL)
12970         return cpl_error_set(func, CPL_ERROR_NULL_INPUT);
12971 
12972     if (1 != cpl_table_has_column(table, "x"))
12973         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12974 
12975     if (1 != cpl_table_has_column(table, "y"))
12976         return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
12977 
12978     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x"))
12979         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12980 
12981     if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y"))
12982         return cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
12983 
12984     nBadPixels = cpl_table_get_nrow(table);
12985 
12986     if (nBadPixels) {
12987         xlen = cpl_image_get_size_x(image);
12988         ylen = cpl_image_get_size_y(image);
12989         idata = cpl_image_get_data(image);
12990         totPix = xlen * ylen;
12991         if (((float) nBadPixels) / ((float) totPix) < percent/100.) {
12992             isBadPix = cpl_calloc(totPix, sizeof(int));
12993         }
12994         else {
12995             cpl_msg_warning(func, "Too many bad pixels (> %d%%): "
12996                             "skip bad pixel correction", percent);
12997             return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
12998         }
12999     }
13000     else {
13001         cpl_msg_debug(func, "No pixel values to interpolate");
13002         return CPL_ERROR_NONE;
13003     }
13004 
13005     xValue = cpl_table_get_data_int(table, "x");
13006     yValue = cpl_table_get_data_int(table, "y");
13007 
13008     for (i = 0; i < nBadPixels; i++)
13009         isBadPix[xValue[i] + yValue[i] * xlen] = 1;
13010 
13011     for (i = 0; i < nBadPixels; i++) {
13012 
13013         /*
13014          *  Search for the closest good pixel along the 4 fundamental 
13015          *  directions (in both senses):
13016          *                            \ | /
13017          *                             \|/
13018          *                           --- ---
13019          *                             /|\
13020          *                            / | \
13021          *
13022          *  Then collect pairs of values to interpolate linearly.
13023          */
13024 
13025         nPairs = 0;
13026         for (j = 0; j < 4; j++) {
13027 
13028             if (spectral) /* Just horizontal interpolation for spectral data */
13029                 if (j != 2)
13030                     continue;
13031 
13032             estimate[nPairs] = 0.;  /* Pairs interpolations are stored here */
13033             sumd = 0.;
13034             foundFirst = 0;
13035             for (k = 0; k < 2; k++) {
13036                 sign = 2 * k - 1;
13037                 d = 0;
13038                 cx = xValue[i];
13039                 cy = yValue[i];
13040                 do {
13041                     cx += sign * sx[j];
13042                     cy += sign * sy[j];
13043                     if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 
13044                         break;
13045                     d++;
13046                 } while (isBadPix[cx + cy * xlen] && d < searchHorizon);
13047 
13048                 if (cx >= 0 && cx < xlen && 
13049                     cy >= 0 && cy < ylen && d < searchHorizon) {
13050 
13051                     /*
13052                      *  In this block is cripted the linear interpolation...
13053                      */
13054 
13055                     save = idata[cx + cy * xlen];
13056                     estimate[nPairs] += save / d;
13057                     sumd += 1. / (double) d;
13058                     if (k) {
13059                         estimate[nPairs] /= sumd;
13060                         nPairs++;
13061                     }
13062                     else {
13063                         foundFirst = 1;
13064                     }
13065                 }
13066                 else {
13067 
13068                     /*
13069                      * Image borders were crossed, incomplete pair of values
13070                      */
13071 
13072                     if (k) {
13073                         if (foundFirst) {
13074                             estimate[nPairs] = save;
13075                             nPairs++;
13076                         }
13077                     }
13078                 }
13079             }
13080         }
13081 
13082         /*
13083          * Replace pixel value of the input image, corresponding to
13084          * the current bad pixel, with the median of the estimates
13085          * resulted from the 4 linear interpolations.
13086          */
13087 
13088         if (nPairs > 2) {
13089             idata[xValue[i] + yValue[i] * xlen] = 
13090                                cpl_tools_get_median_float(estimate, nPairs);
13091         }
13092         else if (nPairs == 2) {
13093             idata[xValue[i] + yValue[i] * xlen] =
13094                                             (estimate[0] + estimate[1]) / 2.;
13095         }
13096         else if (nPairs == 1) {
13097             idata[xValue[i] + yValue[i] * xlen] = estimate[0];
13098         }
13099         else {
13100             cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n",
13101                           xValue[i], yValue[i]);
13102         }
13103     }
13104 
13105     cpl_free(isBadPix);
13106 
13107     return CPL_ERROR_NONE;
13108 }
13109 
13110 
13140 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits,
13141                            cpl_table *polytraces, double reference,
13142                            double blue, double red, double dispersion)
13143 {
13144     const char *func = "mos_spatial_map";
13145 
13146     const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"};
13147                                                  /* Max order is 5 */
13148     cpl_polynomial *polytop;
13149     cpl_polynomial *polybot;
13150     cpl_image      *calibration;
13151     float          *data;
13152     double          top, bot;
13153     double          coeff;
13154     double          ytop, ybot;
13155     double          ypos, yfra;
13156     double          factor;
13157     int             yint, yprev;
13158     int             nslits;
13159     int             npseudo;
13160     int            *slit_id;
13161     int            *length;
13162     int             nx, ny;
13163     int             pixel_above, pixel_below, refpixel, start_pixel, end_pixel;
13164     int             missing_top, missing_bot;
13165     int             null;
13166     int             order;
13167     int             i, j, k;
13168 
13169 
13170     if (spectra == NULL || slits == NULL || polytraces == NULL) {
13171         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13172         return NULL;
13173     }
13174 
13175     if (dispersion <= 0.0) {
13176         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13177         return NULL;
13178     }
13179 
13180     if (red - blue < dispersion) {
13181         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13182         return NULL;
13183     }
13184 
13185     nx = cpl_image_get_size_x(spectra);
13186     ny = cpl_image_get_size_y(spectra);
13187 
13188     calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
13189     data = cpl_image_get_data(calibration);
13190 
13191     length  = cpl_table_get_data_int(slits, "length");
13192     nslits  = cpl_table_get_nrow(slits);
13193     slit_id = cpl_table_get_data_int(slits, "slit_id");
13194     order   = cpl_table_get_ncol(polytraces) - 2;
13195 
13196     /*
13197      * The spatial resampling is performed for a certain number of 
13198      * pixels above and below the position of the reference wavelength:
13199      */
13200 
13201     pixel_above = STRETCH_FACTOR * (red - reference) / dispersion;
13202     pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion;
13203 
13204     for (i = 0; i < nslits; i++) {
13205         
13206         if (length[i] == 0)
13207             continue;
13208 
13209         /*
13210          * Note that the x coordinate of the reference pixels on the CCD
13211          * is taken arbitrarily at the top end of each slit. This wouldn't
13212          * be entirely correct in case of curved slits, or in presence of
13213          * heavy distortions: in such cases the spatial resampling is
13214          * really performed across a wide range of wavelengths. But
13215          * the lag between top and bottom spectral curvature models 
13216          * would introduce even in such cases negligible effects on
13217          * the spectral spatial resampling.
13218          */
13219 
13220         refpixel = cpl_table_get_double(slits, "xtop", i, NULL);
13221 
13222         start_pixel = refpixel - pixel_below;
13223         if (start_pixel < 0)
13224             start_pixel = 0;
13225 
13226         end_pixel = refpixel + pixel_above;
13227         if (end_pixel > nx)
13228             end_pixel = nx;
13229 
13230         /*
13231          * Recover from the table of spectral curvature coefficients
13232          * the curvature polynomials.
13233          */
13234 
13235         missing_top = 0;
13236         polytop = cpl_polynomial_new(1);
13237         for (k = 0; k <= order; k++) {
13238             coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null);
13239             if (null) {
13240                 cpl_polynomial_delete(polytop);
13241                 missing_top = 1;
13242                 break;
13243             }
13244             cpl_polynomial_set_coeff(polytop, &k, coeff);
13245         }
13246 
13247         missing_bot = 0;
13248         polybot = cpl_polynomial_new(1);
13249         for (k = 0; k <= order; k++) {
13250             coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null);
13251             if (null) {
13252                 cpl_polynomial_delete(polybot);
13253                 missing_bot = 1;
13254                 break;
13255             }
13256             cpl_polynomial_set_coeff(polybot, &k, coeff);
13257         }
13258 
13259         if (missing_top && missing_bot) {
13260             cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 
13261                             slit_id[i]);
13262             continue;
13263         }
13264 
13265         /*
13266          * In case just one of the two edges was not traced, the other
13267          * edge curvature model is duplicated and shifted to the other
13268          * end of the slit: better than nothing!
13269          */
13270 
13271         if (missing_top) {
13272             cpl_msg_warning(func, "Upper edge of slit %d was not traced: "
13273                             "the spectral curvature of the lower edge "
13274                             "is used instead.", slit_id[i]);
13275             polytop = cpl_polynomial_duplicate(polybot);
13276             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13277             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13278             k = 0;
13279             coeff = cpl_polynomial_get_coeff(polybot, &k);
13280             coeff += ytop - ybot;
13281             cpl_polynomial_set_coeff(polytop, &k, coeff);
13282         }
13283 
13284         if (missing_bot) {
13285             cpl_msg_warning(func, "Lower edge of slit %d was not traced: "
13286                             "the spectral curvature of the upper edge "
13287                             "is used instead.", slit_id[i]);
13288             polybot = cpl_polynomial_duplicate(polytop);
13289             ytop = cpl_table_get_double(slits, "ytop", i, NULL);
13290             ybot = cpl_table_get_double(slits, "ybottom", i, NULL);
13291             k = 0;
13292             coeff = cpl_polynomial_get_coeff(polytop, &k);
13293             coeff -= ytop - ybot;
13294             cpl_polynomial_set_coeff(polybot, &k, coeff);
13295         }
13296 
13297         top = cpl_polynomial_eval_1d(polytop, refpixel, NULL);
13298         bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL);
13299         npseudo = ceil(top-bot) + 1;
13300 
13301         if (npseudo < 1) {
13302             cpl_polynomial_delete(polytop);
13303             cpl_polynomial_delete(polybot);
13304             cpl_msg_warning(func, "Slit %d was badly traced: no extraction!",
13305                             slit_id[i]);
13306             continue;
13307         }
13308 
13309         for (j = start_pixel; j < end_pixel; j++) {
13310             top = cpl_polynomial_eval_1d(polytop, j, NULL);
13311             bot = cpl_polynomial_eval_1d(polybot, j, NULL);
13312             factor = (top-bot)/npseudo;
13313             for (k = 0; k <= npseudo; k++) {
13314                 ypos = top - k*factor;
13315                 yint = ypos;
13316                 yfra = ypos - yint;
13317                 if (yint >= 0 && yint < ny-1) {
13318                     data[j + nx*yint] = (top-yint)/factor;
13319                     if (k) {
13320 
13321                         /*
13322                          * This is added to recover lost pixels on
13323                          * the CCD image (pixels are lost because
13324                          * the CCD pixels are less than npseudo+1).
13325                          */
13326 
13327                         if (yprev - yint > 1) {
13328                             data[j + nx*(yint+1)] = (top-yint-1)/factor;
13329                         }
13330                     }
13331                 }
13332                 yprev = yint;
13333             }
13334         }
13335         cpl_polynomial_delete(polytop);
13336         cpl_polynomial_delete(polybot);
13337     }
13338 
13339     return calibration;
13340 }
13341 
13342 
13405 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin,
13406                               int maxradius, int conradius)
13407 {
13408     const char *func = "mos_detect_objects";
13409 
13410     cpl_image  *profile;
13411     float      *pdata;
13412     float      *p;
13413 
13414     char        name[MAX_COLNAME];
13415 
13416     int         nslits;
13417     int         npeaks;
13418     int         nobjects, objpos, totobj;
13419     int         maxobjects;
13420     int        *position;
13421     int        *length;
13422     int        *reject;
13423     double     *place;
13424     double     *bright;
13425     double      mindistance;
13426     int         pos, count;
13427     int         up;
13428     int         low, hig;
13429     int         row;
13430     int         i, j, k;
13431 
13432     const int   min_pixels = 10;
13433 
13434     
13435     if (cpl_error_get_code() != CPL_ERROR_NONE)
13436         return NULL;
13437  
13438     if (image == NULL || slits == NULL) { 
13439         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13440         return NULL;
13441     }
13442 
13443     if (margin < 0)
13444         margin = 0;
13445 
13446     if (maxradius < 0) {
13447         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13448         return NULL;
13449     }
13450 
13451     if (conradius < 0) {
13452         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13453         return NULL;
13454     }
13455 
13456     nslits   = cpl_table_get_nrow(slits);
13457     position = cpl_table_get_data_int(slits, "position");
13458     length   = cpl_table_get_data_int(slits, "length");
13459 
13460     profile = cpl_image_collapse_create(image, 1);
13461     cpl_image_divide_scalar(profile, cpl_image_get_size_x(image));
13462     pdata = cpl_image_get_data(profile);
13463 
13464     row = 1;
13465     maxobjects = 0;
13466     totobj = 0;
13467     for (i = 0; i < nslits; i++) {
13468 
13469         if (length[i] == 0)
13470             continue;
13471 
13472         pos = position[i] + margin;
13473         count = length[i] - 2*margin;
13474 
13475         if (count < min_pixels)
13476             continue;
13477 
13478         p = pdata + pos;
13479 
13480 
13481         /*
13482          * Count peaks candidates
13483          */
13484 
13485         npeaks = 0;
13486         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13487             npeaks++;
13488         }
13489 
13490         up = 0;
13491         for (j = 0; j < count - 3; j++) {
13492             if (p[j] > 0) {
13493                 if (p[j+1] > p[j]) {
13494                     up++;
13495                 }
13496                 else {
13497                     if (up > 2) {
13498                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13499                             if (p[j] > 5)
13500                                 npeaks++;
13501                         }
13502                     }
13503                     else if (up > 1) {
13504                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13505                             if (p[j] > 5)
13506                                 npeaks++;
13507                         }
13508                     }
13509                     up = 0;
13510                 }
13511             }
13512             else {
13513                 up = 0;
13514             }
13515         }
13516 
13517         if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 
13518             && p[count-3] > p[count-4] && p[count-4] > 0) {
13519             npeaks++;
13520         }
13521 
13522         if (npeaks == 0)
13523             continue;
13524 
13525 
13526         /*
13527          * Get candidates parameters
13528          */
13529 
13530         reject = cpl_calloc(npeaks, sizeof(int));
13531         bright = cpl_calloc(npeaks, sizeof(double));
13532         place  = cpl_calloc(npeaks, sizeof(double));
13533 
13534         npeaks = 0;
13535         if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) {
13536             bright[0] = p[0];
13537             place[0] = position[i] + margin;
13538             npeaks++;
13539         }
13540 
13541         up = 0;
13542         for (j = 0; j < count - 3; j++) {
13543             if (p[j] > 0) {
13544                 if (p[j+1] > p[j]) {
13545                     up++;
13546                 }
13547                 else {
13548                     if (up > 2) {
13549                         if (p[j+1] > p[j+2] && p[j+2] > 0) {
13550                             if (p[j] > 5) {
13551                                 bright[npeaks] = p[j];
13552                                 place[npeaks] = position[i] + margin + j + 1
13553                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13554                                 npeaks++;
13555                             }
13556                         }
13557                     }
13558                     else if (up > 1) {
13559                         if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) {
13560                             if (p[j] > 5) {
13561                                 bright[npeaks] = p[j];
13562                                 place[npeaks] = position[i] + margin + j + 1
13563                                        + values_to_dx(p[j-1], p[j], p[j+1]);
13564                                 npeaks++;
13565                             }
13566                         }
13567                     }
13568                     up = 0;
13569                 }
13570             }
13571             else {
13572                 up = 0;
13573             }
13574         }
13575 
13576         if (p[count-1] > p[count-2] && p[count-2] > p[count-3]
13577             && p[count-3] > p[count-4] && p[count-4] > 0) {
13578             bright[npeaks] = p[count-1];
13579             place[npeaks] = position[i] + count;
13580             npeaks++;
13581         }
13582 
13583 
13584         /*
13585          * Now select the uncontaminated peaks
13586          */
13587 
13588         if (fabs(place[0] - pos) < 1.0)
13589             reject[0] = 1;
13590         if (fabs(place[npeaks-1] - pos - count) < 1.0)
13591             reject[npeaks-1] = 1;
13592         for (j = 0; j < npeaks; j++) {
13593             for (k = 0; k < npeaks; k++) {
13594                 if (k == j)
13595                     continue;
13596                 mindistance = conradius * bright[k] / bright[j] 
13597                                         * bright[k] / bright[j];
13598                 if (fabs(place[j] - place[k]) < mindistance)
13599                     reject[j] = 1;
13600             }
13601         }
13602 
13603 /* new part */
13604         for (j = 0; j < npeaks; j++) {
13605             if (reject[j])
13606                 continue;
13607             if (j) {
13608                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13609                     / (bright[j-1] + bright[j]) + 1;
13610             }
13611             else {
13612                 low = pos;
13613             }
13614             if (j < npeaks - 1) {
13615                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13616                     / (bright[j+1] + bright[j]) + 1;
13617             }
13618             else {
13619                 hig = pos + count;
13620             }
13621 
13622             if (low < pos)
13623                 low = pos;
13624             if (hig > pos + count)
13625                 hig = pos + count;
13626             if (place[j] - low > maxradius)
13627                 low = place[j] - maxradius;
13628             if (hig - place[j] > maxradius)
13629                 hig = place[j] + maxradius;
13630             if (hig == low)
13631                 reject[j] = 1;
13632         }
13633 /* end new part */
13634 
13635         nobjects = npeaks;
13636         for (j = 0; j < npeaks; j++)
13637             if (reject[j])
13638                 nobjects--;
13639 
13640         for (j = 0; j < nobjects; j++) {
13641             snprintf(name, MAX_COLNAME, "object_%d", j+1);
13642             if (cpl_table_has_column(slits, name))
13643                 continue;
13644             cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE);
13645             snprintf(name, MAX_COLNAME, "start_%d", j+1);
13646             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13647             cpl_table_set_column_unit(slits, name, "pixel");
13648             snprintf(name, MAX_COLNAME, "end_%d", j+1);
13649             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13650             cpl_table_set_column_unit(slits, name, "pixel");
13651             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13652             cpl_table_new_column(slits, name, CPL_TYPE_INT);
13653             cpl_table_set_column_unit(slits, name, "pixel");
13654         }
13655 
13656         objpos = nobjects;
13657         for (j = 0; j < npeaks; j++) {
13658             if (reject[j])
13659                 continue;
13660             if (j) {
13661                 low = (place[j-1]*bright[j] + place[j]*bright[j-1])
13662                     / (bright[j-1] + bright[j]) + 1;
13663             }
13664             else {
13665                low = pos;
13666             }
13667             if (j < npeaks - 1) {
13668                 hig = (place[j+1]*bright[j] + place[j]*bright[j+1])
13669                     / (bright[j+1] + bright[j]) + 1;
13670             }
13671             else {
13672                 hig = pos + count;
13673             }
13674 
13675             if (low < pos)
13676                 low = pos;
13677             if (hig > pos + count)
13678                 hig = pos + count;
13679             if (place[j] - low > maxradius)
13680                 low = place[j] - maxradius;
13681             if (hig - place[j] > maxradius)
13682                 hig = place[j] + maxradius;
13683 
13684             snprintf(name, MAX_COLNAME, "object_%d", objpos);
13685             cpl_table_set_double(slits, name, i, place[j]);
13686             snprintf(name, MAX_COLNAME, "start_%d", objpos);
13687             cpl_table_set_int(slits, name, i, low);
13688             snprintf(name, MAX_COLNAME, "end_%d", objpos);
13689             cpl_table_set_int(slits, name, i, hig);
13690             snprintf(name, MAX_COLNAME, "row_%d", objpos);
13691             cpl_table_set_int(slits, name, i, row + objpos - 1);
13692             totobj++;
13693             objpos--;
13694         }
13695 
13696         row += nobjects;
13697 
13698         if (maxobjects < nobjects)
13699             maxobjects = nobjects;
13700 
13701         cpl_free(reject);
13702         cpl_free(bright);
13703         cpl_free(place);
13704 
13705     }
13706 
13707 /*    nobjects = row - nobjects;     A bug, I think... */
13708     row = cpl_table_get_nrow(slits);
13709 
13710     for (i = 0; i < row; i++) {
13711         for (j = 0; j < maxobjects; j++) {
13712             snprintf(name, MAX_COLNAME, "row_%d", j+1);
13713             if (cpl_table_is_valid(slits, name, i))
13714                 cpl_table_set_int(slits, name, i, totobj -
13715                                   cpl_table_get_int(slits, name, i, NULL));
13716         }
13717     }
13718 
13719     for (i = 0; i < maxobjects; i++) {
13720         snprintf(name, MAX_COLNAME, "start_%d", i+1);
13721         cpl_table_fill_invalid_int(slits, name, -1);
13722         snprintf(name, MAX_COLNAME, "end_%d", i+1);
13723         cpl_table_fill_invalid_int(slits, name, -1);
13724         snprintf(name, MAX_COLNAME, "row_%d", i+1);
13725         cpl_table_fill_invalid_int(slits, name, -1);
13726     }
13727 
13728     return profile;
13729 }
13730 
13731 
13756 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky,
13757                                 cpl_table *objects, int extraction, double ron,
13758                                 double gain, int ncombined)
13759 {
13760     const char *func = "mos_extract_objects";
13761 
13762     char        name[MAX_COLNAME];
13763 
13764     cpl_image **output;
13765     cpl_image  *extracted;
13766     cpl_image  *extr_sky;
13767     cpl_image  *error;
13768     cpl_image  *sciwin;
13769     cpl_image  *skywin;
13770     int         nslits;
13771     int         nobjects;
13772     int         maxobjects;
13773     int         nx, ny;
13774     int         ylow, yhig;
13775     int         i, j;
13776 
13777 
13778     if (science == NULL || sky == NULL) {
13779         cpl_msg_error(func, "Both scientific exposures are required in input");
13780         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13781         return NULL;
13782     }
13783 
13784     if (objects == NULL) {
13785         cpl_msg_error(func, "An object table is required in input");
13786         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
13787         return NULL;
13788     }
13789 
13790     if (extraction < 0 || extraction > 1) {
13791         cpl_msg_error(func, "Invalid extraction mode (%d): it should be "
13792                       "either 0 or 1", extraction); 
13793         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13794         return NULL;
13795     }
13796 
13797     if (ron < 0.0) {
13798         cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron);
13799         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13800         return NULL;
13801     }
13802 
13803     if (gain < 0.1) {
13804         cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain);
13805         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13806         return NULL;
13807     }
13808 
13809     if (ncombined < 1) {
13810         cpl_msg_error(func, "Invalid number of combined frames (%d): "
13811                       "it should be at least 1", ncombined);
13812         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
13813         return NULL;
13814     }
13815 
13816 
13817     /*
13818      * Count the max number of objects per slit. Note that maxobjects 
13819      * is intentionally the max number of objects increased by one.
13820      */
13821 
13822     maxobjects = 1;
13823     snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13824     while (cpl_table_has_column(objects, name)) {
13825         maxobjects++;
13826         snprintf(name, MAX_COLNAME, "object_%d", maxobjects);
13827     }
13828 
13829 
13830     /*
13831      * Count objects to extract
13832      */
13833 
13834     nobjects = 0;
13835     nslits = cpl_table_get_nrow(objects);
13836 
13837     for (i = 0; i < nslits; i++) {
13838         for (j = 1; j < maxobjects; j++) {
13839             snprintf(name, MAX_COLNAME, "object_%d", j);
13840             if (cpl_table_is_valid(objects, name, i))
13841                 nobjects++;
13842         }
13843     }
13844 
13845     if (nobjects == 0)
13846         return NULL;
13847 
13848     nx = cpl_image_get_size_x(science);
13849     ny = cpl_image_get_size_x(science);
13850 
13851     output = cpl_calloc(3, sizeof(cpl_image *));
13852     extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13853     extr_sky  = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13854     error     = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT);
13855 
13856 
13857     /*
13858      * Extract objects
13859      */
13860 
13861     nobjects = 0;
13862     for (i = 0; i < nslits; i++) {
13863         for (j = 1; j < maxobjects; j++) {
13864             snprintf(name, MAX_COLNAME, "object_%d", j);
13865             if (cpl_table_is_valid(objects, name, i)) {
13866                 snprintf(name, MAX_COLNAME, "start_%d", j);
13867                 ylow = cpl_table_get_int(objects, name, i, NULL);
13868                 snprintf(name, MAX_COLNAME, "end_%d", j);
13869                 yhig = cpl_table_get_int(objects, name, i, NULL);
13870                 snprintf(name, MAX_COLNAME, "row_%d", j);
13871                 nobjects = cpl_table_get_int(objects, name, i, NULL);
13872                 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig);
13873                 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig);
13874 /*
13875  * Cleaning the cosmics locally was really NOT a good idea...
13876  * I leave it here, commented out, to never forget this mistake!
13877 
13878                 if (extraction) {
13879                     mos_clean_cosmics(sciwin, gain, -1., -1.);
13880                 }
13881  */
13882                 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 
13883                                nobjects, extraction, ron, gain, ncombined);
13884 
13885                 /*
13886                  * Hidden check whether the spectrum was saturated or not
13887                  */
13888 
13889                 {
13890                     cpl_image *total = cpl_image_add_create(sciwin, skywin);
13891                     float     *data  = cpl_image_get_data_float(total);
13892                     int        size  = cpl_image_get_size_x(total)
13893                                      * cpl_image_get_size_y(total);
13894                     int        k;
13895                     char      *saturation_level = getenv("SATURATION_LEVEL");
13896                     float      saturation = 62000.0;
13897                     char      *max_saturated = getenv("MAX_SATURATED");
13898                     int        max_satur = 10;
13899                     int        saturated;
13900 
13901                     if (saturation_level)
13902                         saturation = atof(saturation_level);
13903 
13904                     if (max_saturated)
13905                         max_satur = atoi(max_saturated);
13906 
13907                     saturated = 0;
13908                     for (k = 0; k < size; k++) {
13909                         if (data[k] > saturation) {
13910                             saturated++;
13911                             if (saturated > max_satur) {
13912                                 break;
13913                             }
13914                         }
13915                     }
13916 
13917                     if (saturated > max_satur)
13918                         saturated = 1;
13919                     else
13920                         saturated = 0;
13921 
13922                     data = cpl_image_get_data(extracted);
13923                     data[nobjects * nx] = saturated;
13924                 }
13925 
13926                 cpl_image_delete(sciwin);
13927                 cpl_image_delete(skywin);
13928                 nobjects++;
13929             }
13930         }
13931     }
13932 
13933     return output;
13934 
13935 }
13936 
13937 
13960 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 
13961                             double dispersion, int saturation, 
13962                             double *mfwhm, double *rmsfwhm,
13963                             double *resolution, double *rmsres, int *nlines)
13964 {
13965     cpl_vector *vector;
13966 
13967     int     i, j, n, m;
13968     int     position, maxpos;
13969     int     xlen, ylen;
13970     int     sp, ep;
13971     int     radius;
13972     int     sradius = 40;
13973     int     threshold = 250;    /* Peak must be so many ADUs above min */
13974 
13975     int     ifwhm;
13976     double  fwhm;
13977     double *buffer;
13978     double  min, max, halfmax;
13979     double  cut = 1.5;         /* To cut outliers from FWHM values (pixel) */
13980     double  value, rms;
13981 
13982     float  *data;
13983 
13984 
13985     *resolution = 0.0;
13986     *rmsres = 0.0;
13987     *nlines = 0;
13988 
13989     xlen = cpl_image_get_size_x(image);
13990     ylen = cpl_image_get_size_y(image);
13991     data = cpl_image_get_data(image);
13992 
13993     buffer = cpl_malloc(ylen * sizeof(double));
13994 
13995     /*
13996      *  Closest pixel to specified wavelength.
13997      */
13998 
13999     position = floor((lambda - startwave) / dispersion + 0.5);
14000 
14001     sp = position - sradius;
14002     ep = position + sradius;
14003 
14004     if (sp < 0 || ep > xlen) {
14005         cpl_free(buffer);
14006         return 0;
14007     }
14008 
14009     for (i = 0, n = 0; i < ylen; i++) {    /*  For each row of each slit  */
14010 
14011         /*
14012          *  Search interval for peak. Abort if too close to image border.
14013          */
14014 
14015         radius = mos_lines_width(data + i*xlen + position - sradius, 
14016                                  2*sradius + 1);
14017         if (radius < 5)
14018             radius = 5;
14019 
14020         sp = position - radius;
14021         ep = position + radius;
14022 
14023         if (sp < 0 || ep > xlen) {
14024             cpl_free(buffer);
14025             return 0;
14026         }
14027 
14028 
14029         /*
14030          *  Determine min-max value and position.
14031          */
14032 
14033         maxpos = sp;
14034         min = max = data[sp + i * xlen];
14035         for (j = sp; j < ep; j++) {
14036             if (data[j + i * xlen] > max) {
14037                 max = data[j + i * xlen];
14038                 maxpos = j;
14039             }
14040             if (data[j + i * xlen] < min) {
14041                 min = data[j + i * xlen];
14042             }
14043         }
14044 
14045         if (fabs(min) < 0.0000001)        /* Truncated spectrum */
14046             continue;
14047 
14048         if (max - min < threshold)        /* Low signal... */
14049             continue;
14050 
14051         if (max > saturation)             /* Saturation */
14052             continue;
14053 
14054         /*
14055          *  Determine FWHM counting pixels with value greater than
14056          *  half of the max value, to the right and to the left of
14057          *  the max. Linear interpolation between the pixels where
14058          *  the transition happens.
14059          */
14060 
14061         halfmax = (max + min)/ 2.0;
14062 
14063         fwhm = 0.0;
14064         ifwhm = 0;
14065         for (j = maxpos; j < maxpos + radius; j++) {
14066             if (j < xlen) {
14067                 if (data[j + i * xlen] < halfmax) {
14068                     fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax)
14069                          / (data[j - 1 + i * xlen] - data[j + i * xlen]);
14070                     break;
14071                 }
14072                 ifwhm++;
14073             }
14074         }
14075 
14076         ifwhm = 0;
14077         for (j = maxpos; j > maxpos - radius; j--) {
14078             if (j >= 0) {
14079                 if (data[j + i * xlen] < halfmax) {
14080                     fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax)
14081                           / (data[j + 1 + i * xlen] - data[j + i * xlen]);
14082                     break;
14083                 }
14084                 ifwhm++;
14085             }
14086         }
14087 
14088         if (fwhm > 3.0) {
14089             buffer[n] = fwhm - 2.0;
14090             n++;
14091         }
14092 
14093     }
14094 
14095     if (n == 0) {
14096         cpl_free(buffer);
14097         return 0;
14098     }
14099 
14100     vector = cpl_vector_wrap(n, buffer);
14101     value = cpl_vector_get_median_const(vector);
14102     cpl_vector_unwrap(vector);
14103 
14104     rms = 0.0;
14105     for (i = 0, m = 0; i < n; i++) {
14106         if (fabs(buffer[i] - value) < cut) {
14107             rms += fabs(buffer[i] - value);
14108             m++;
14109         }
14110     }
14111 
14112     cpl_free(buffer);
14113 
14114     if (m < 3)
14115         return 0;
14116 
14117     rms /= m;
14118     rms *= 1.25;       /* Factor to convert average deviation to sigma */
14119 
14120     value *= dispersion;
14121     rms *= dispersion;
14122 
14123     *mfwhm = value;
14124     *rmsfwhm = rms;
14125 
14126     *resolution = lambda / value;
14127     *rmsres = *resolution * rms / value;
14128 
14129     *nlines = m;
14130 
14131     return 1;
14132 }
14133 
14134 
14156 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 
14157                                 double dispersion, int saturation, 
14158                                 cpl_vector *lines)
14159 {
14160 
14161     cpl_table *table;
14162     double    *line;
14163     double     fwhm;
14164     double     rmsfwhm;
14165     double     resolution;
14166     double     rmsres;
14167     int        nref;
14168     int        nlines;
14169     int        i;
14170 
14171 
14172     nref = cpl_vector_get_size(lines);
14173     line = cpl_vector_get_data(lines);
14174 
14175     table = cpl_table_new(nref);
14176     cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE);
14177     cpl_table_set_column_unit(table, "wavelength", "Angstrom");
14178     cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE);
14179     cpl_table_set_column_unit(table, "fwhm", "Angstrom");
14180     cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE);
14181     cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom");
14182     cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE);
14183     cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE);
14184     cpl_table_new_column(table, "nlines", CPL_TYPE_INT);
14185 
14186     for (i = 0; i < nref; i++) {
14187         if (mos_spectral_resolution(image, line[i], startwave, dispersion, 
14188                                     saturation, &fwhm, &rmsfwhm, 
14189                                     &resolution, &rmsres, &nlines)) {
14190             cpl_table_set_double(table, "wavelength", i, line[i]);
14191             cpl_table_set_double(table, "fwhm", i, fwhm);
14192             cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm);
14193             cpl_table_set_double(table, "resolution", i, resolution);
14194             cpl_table_set_double(table, "resolution_rms", i, rmsres);
14195             cpl_table_set_int(table, "nlines", i, nlines);
14196         }
14197         else
14198             cpl_table_set_int(table, "nlines", i, 0);
14199     }
14200 
14201     if (cpl_table_has_valid(table, "wavelength"))
14202         return table;
14203 
14204     cpl_table_delete(table);
14205 
14206     return NULL;
14207     
14208 }
14209 
14210 
14228 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap,
14229                             int ystart, int yend, double wstart, double wend)
14230 {
14231     const char *func = "mos_integrate_signal";
14232 
14233     double sum;
14234     float *sdata;
14235     float *wdata;
14236     int    nx, ny;
14237     int    x, y;
14238     
14239 
14240     if (image == NULL || wavemap == NULL) { 
14241         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14242         return 0.0;
14243     }
14244 
14245     if (ystart > yend || wstart >= wend) {
14246         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14247         return 0.0;
14248     }
14249 
14250     nx = cpl_image_get_size_x(image);
14251     ny = cpl_image_get_size_y(image);
14252 
14253     if (!(nx == cpl_image_get_size_x(wavemap) 
14254         && ny == cpl_image_get_size_y(wavemap))) {
14255         cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT);
14256         return 0.0;
14257     }
14258 
14259     if (ystart < 0 || yend > ny) {
14260         cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE);
14261         return 0.0;
14262     }
14263 
14264     sdata = cpl_image_get_data(image);
14265     wdata = cpl_image_get_data(wavemap);
14266 
14267     sdata += ystart*nx;
14268     wdata += ystart*nx;
14269 
14270     sum = 0.0;
14271     for (y = ystart; y < yend; y++) {
14272         for (x = 0; x < nx; x++) {
14273             if (wdata[x] < wstart || wdata[x] > wend)
14274                 continue;
14275             sum += sdata[x];
14276         }
14277         sdata += nx;
14278         wdata += nx;
14279     }
14280 
14281     return sum;
14282 
14283 }
14284 
14285 /****************************************************************************
14286  * From this point on, the instrument dependent functions are added:
14287  * they are functions that retrieve information that is stored in
14288  * the data headers in some instrument specific way, such as the
14289  * location of overscans, the slits positions on the telescope
14290  * focal plane, the gain factor, etc.
14291  */
14292 
14293 
14316 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header)
14317 {
14318     const char *func = "mos_load_slits_fors_mxu";
14319 
14320     cpl_table  *slits;
14321     char        keyname[MAX_COLNAME];
14322     const char *instrume;
14323     const char *target_name;
14324     float       slit_x;
14325     float       slit_y;
14326     float       length;
14327 /*    double      arc2mm = 0.53316;         */
14328     double      arc2mm = 0.528;
14329     int         nslits;
14330     int         slit_id;
14331     int         fors;
14332     int         chip;
14333     int         found;
14334 
14335     /*
14336      * The limits below are used to exclude from the loaded slit list
14337      * any slit that surely doesn't belong to the used chip. This is
14338      * a way to reduce the chance of ambiguous slit identification.
14339      */
14340 
14341     float      low_limit1 = 10.0;
14342     float      hig_limit2 = 30.0;
14343 
14344 
14345     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14346         return NULL;
14347     }
14348 
14349     if (header == NULL) {
14350         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14351         return NULL;
14352     }
14353 
14354 
14355     /*
14356      * See if this is FORS1 or FORS2;
14357      */
14358 
14359     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14360 
14361     fors = 0;
14362     if (instrume[4] == '1')
14363         fors = 1;
14364     if (instrume[4] == '2')
14365         fors = 2;
14366 
14367     if (fors != 2) {
14368         cpl_msg_error(func, "Wrong instrument: %s\n"
14369                       "FORS2 is expected for MXU data", instrume);
14370         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14371         return NULL;
14372     }
14373 
14374 
14375     /*
14376      * The master and slave chips can be identified by their positions
14377      * in the chip array in the case of FORS2 data (with fors1 the chip
14378      * is always 1). chip = 2 is the master, chip = 1 is the slave.
14379      */
14380 
14381     chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14382 
14383     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14384         cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14385                       "in FITS header");
14386         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14387         return NULL;
14388     }
14389 
14390     if (chip != 1 && chip != 2) {
14391         cpl_msg_error(func, "Unexpected chip position in keyword "
14392                       "ESO DET CHIP1 Y: %d", chip);
14393         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14394         return NULL;
14395     }
14396 
14397 
14398     /*
14399      * Count slits in header (excluding reference slits, and the slits
14400      * that _surely_ belong to the other chip)
14401      */
14402 
14403     nslits = 0;
14404     slit_id = 0;
14405     found = 1;
14406 
14407     while (found) {
14408         slit_id++;
14409         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14410         if (cpl_propertylist_has(header, keyname)) {
14411             slit_y = cpl_propertylist_get_double(header, keyname);
14412 
14413             if (chip == 1)
14414                 if (slit_y < low_limit1)
14415                     continue;
14416             if (chip == 2)
14417                 if (slit_y > hig_limit2)
14418                     continue;
14419                 
14420             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14421                      slit_id + 100);
14422             if (cpl_propertylist_has(header, keyname)) {
14423                 target_name = cpl_propertylist_get_string(header, keyname);
14424                 if (strncmp(target_name, "refslit", 7))
14425                     nslits++;
14426             }
14427             else
14428                 nslits++;
14429         }
14430         else
14431             found = 0;
14432     }
14433 
14434     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14435         cpl_msg_error(func, "%s while loading slits coordinates from "
14436                       "FITS header", cpl_error_get_message());
14437         cpl_error_set_where(func);
14438         return NULL;
14439     }
14440 
14441     if (nslits == 0)  {
14442         cpl_msg_error(func, "No slits coordinates found in header");
14443         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14444         return NULL;
14445     }
14446 
14447     slits = cpl_table_new(nslits);
14448     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14449     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14450     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14451     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14452     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14453     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14454     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14455     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14456     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14457 
14458     nslits = 0;
14459     slit_id = 0; 
14460     found = 1;
14461     while (found) {
14462         slit_id++;
14463         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100);
14464         if (cpl_propertylist_has(header, keyname)) {
14465             slit_y = cpl_propertylist_get_double(header, keyname);
14466 
14467             if (chip == 1) 
14468                 if (slit_y < low_limit1)
14469                     continue;
14470             if (chip == 2)
14471                 if (slit_y > hig_limit2)
14472                     continue;
14473 
14474             /*
14475              * Y-flip the slit position, to match CCD pixel coordinate
14476              * convention
14477              */
14478 
14479             slit_y = -slit_y;
14480 
14481             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100);
14482             slit_x = cpl_propertylist_get_double(header, keyname);
14483             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14484                 cpl_table_delete(slits);
14485                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14486                               keyname);
14487                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14488                 return NULL;
14489             }
14490 
14491             snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100);
14492             length = cpl_propertylist_get_double(header, keyname);
14493             if (cpl_error_get_code() != CPL_ERROR_NONE) {
14494                 cpl_table_delete(slits);
14495                 cpl_msg_error(func, "Missing keyword %s in FITS header", 
14496                               keyname);
14497                 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14498                 return NULL;
14499             }
14500 
14501             length *= arc2mm;
14502 
14503             snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 
14504                      slit_id + 100);
14505             if (cpl_propertylist_has(header, keyname)) {
14506                 target_name = cpl_propertylist_get_string(header, keyname);
14507                 if (strncmp(target_name, "refslit", 7)) {
14508                     cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14509                     cpl_table_set(slits, "xtop", nslits, slit_x);
14510                     cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14511                     cpl_table_set(slits, "xbottom", nslits, slit_x);
14512                     cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14513                     nslits++;
14514                 }
14515             }
14516             else {
14517                 cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14518                 cpl_table_set(slits, "xtop", nslits, slit_x);
14519                 cpl_table_set(slits, "ytop", nslits, slit_y + length/2);
14520                 cpl_table_set(slits, "xbottom", nslits, slit_x);
14521                 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2);
14522                 nslits++;
14523             }
14524         }
14525         else
14526             found = 0;
14527     }
14528 
14529     return slits;
14530 }
14531 
14532 
14555 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header)
14556 {
14557     const char *func = "mos_load_slits_fors_mos";
14558 
14559     cpl_table  *slits;
14560     char        keyname[MAX_COLNAME];
14561     const char *instrume;
14562     const char *chipname;
14563     float       slit_x;
14564     int         first_slit, last_slit;
14565     int         nslits;
14566     int         slit_id;
14567     int         fors;
14568     int         chip;
14569     int         fors_is_old;
14570 
14571     /*
14572      * The Y coordinates of the slits are fixed
14573      */
14574 
14575     float       ytop[19]    = { 113.9, 101.3,  89.9,  77.3,  65.9,  53.3, 
14576                                  41.9,  29.3,  17.9,   5.3,  -6.1, -18.7, 
14577                                 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 
14578                                -102.1 };
14579     float       ybottom[19] = { 102.1,  90.7,  78.1,  66.7,  54.1,  42.7,
14580                                  30.1,  18.7,   6.1,  -5.3, -17.9, -29.3,
14581                                 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3,
14582                                -113.9 };
14583 
14584 
14585     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14586         return NULL;
14587     }
14588 
14589     if (header == NULL) {
14590         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14591         return NULL;
14592     }
14593 
14594 
14595     /*
14596      * See if this is FORS1 or FORS2;
14597      */
14598 
14599     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14600 
14601     fors = 0;
14602     if (instrume[4] == '1')
14603         fors = 1;
14604     if (instrume[4] == '2')
14605         fors = 2;
14606 
14607     if (fors == 0) {
14608         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14609                       instrume);
14610         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14611         return NULL;
14612     }
14613 
14614     /* FIXME:
14615      * This is the way FORS1 data belong to the upgraded chips,
14616      * named "Marlene" and "Norma III". It's a quick solution,
14617      * there are hardcoded values here!!!
14618      */
14619 
14620     chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID");
14621 
14622     if (chipname[0] == 'M' || chipname[0] == 'N')
14623         fors_is_old = 0;
14624     else
14625         fors_is_old = 1;
14626 
14627     if (fors == 1 && fors_is_old) {
14628         first_slit = 1;
14629         last_slit = 19;
14630     }
14631     else {
14632 
14633         /*
14634          * The master and slave chips can be identified by their positions
14635          * in the chip array in the case of FORS2 data: chip = 2 is the 
14636          * master, chip = 1 is the slave.
14637          */
14638 
14639         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14640 
14641         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14642             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14643                           "in FITS header");
14644             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14645             return NULL;
14646         }
14647 
14648         if (chip != 1 && chip != 2) {
14649             cpl_msg_error(func, "Unexpected chip position in keyword "
14650                           "ESO DET CHIP1 Y: %d", chip);
14651             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14652             return NULL;
14653         }
14654 
14655         if (chip == 1) {
14656             first_slit = 12;
14657             last_slit = 19;
14658         }
14659         else {
14660             first_slit = 1;
14661             last_slit = 11;
14662         }
14663     }
14664 
14665 
14666     /*
14667      * Count slits in header (excluding closed slits - i.e. those with
14668      * offsets greater than 115 mm - and the slits that do not belong 
14669      * to this chip)
14670      */
14671 
14672     nslits = 0;
14673     slit_id = 0;
14674     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14675         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14676         if (cpl_propertylist_has(header, keyname)) {
14677             slit_x = cpl_propertylist_get_double(header, keyname);
14678             if (fabs(slit_x) < 115.0)
14679                 nslits++;
14680         }
14681         else {
14682             cpl_msg_error(func, "Missing keyword %s in FITS header", keyname);
14683             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14684             return NULL;
14685         }
14686     }
14687 
14688     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14689         cpl_msg_error(func, "%s while loading slits coordinates from "
14690                       "FITS header", cpl_error_get_message());
14691         cpl_error_set_where(func);
14692         return NULL;
14693     }
14694 
14695     if (nslits == 0)  {
14696         cpl_msg_error(func, "No slits coordinates found in header");
14697         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14698         return NULL;
14699     }
14700 
14701     slits = cpl_table_new(nslits);
14702     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14703     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14704     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14705     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14706     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14707     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14708     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14709     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14710     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14711 
14712     nslits = 0;
14713     slit_id = 0;
14714     for (slit_id = first_slit; slit_id <= last_slit; slit_id++) {
14715         snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id);
14716         slit_x = cpl_propertylist_get_double(header, keyname);
14717         if (fabs(slit_x) < 115.0) {
14718             cpl_table_set_int(slits, "slit_id", nslits, slit_id);
14719             cpl_table_set(slits, "xtop", nslits, slit_x);
14720             cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]);
14721             cpl_table_set(slits, "xbottom", nslits, slit_x);
14722             cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]);
14723             nslits++;
14724         }
14725     }
14726 
14727     return slits;
14728 }
14729 
14730 
14754 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header)
14755 {
14756     const char *func = "mos_load_slits_fors_lss";
14757 
14758     cpl_table  *slits;
14759     char       *slit_name;
14760     const char *instrume;
14761     int         fors;
14762     int         chip;
14763     float       ytop;
14764     float       ybottom;
14765 
14766     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14767         return NULL;
14768     }
14769 
14770     if (header == NULL) {
14771         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14772         return NULL;
14773     }
14774 
14775 
14776     /*
14777      * See if this is FORS1 or FORS2;
14778      */
14779 
14780     instrume = cpl_propertylist_get_string(header, "INSTRUME");
14781 
14782     fors = 0;
14783     if (instrume[4] == '1')
14784         fors = 1;
14785     if (instrume[4] == '2')
14786         fors = 2;
14787 
14788     if (fors == 0) {
14789         cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 
14790                       instrume);
14791         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14792         return NULL;
14793     }
14794 
14795     if (fors == 1) {
14796         ytop = 109.94;
14797         ybottom = -109.94;
14798     }
14799     else {
14800 
14801         /*
14802          * The master and slave chips can be identified by their positions
14803          * in the chip array in the case of FORS2 data: chip = 2 is the 
14804          * master, chip = 1 is the slave.
14805          */
14806 
14807         chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y");
14808 
14809         if (cpl_error_get_code() != CPL_ERROR_NONE) {
14810             cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y "
14811                           "in FITS header");
14812             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14813             return NULL;
14814         }
14815 
14816         if (chip != 1 && chip != 2) {
14817             cpl_msg_error(func, "Unexpected chip position in keyword "
14818                           "ESO DET CHIP1 Y: %d", chip);
14819             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
14820             return NULL;
14821         }
14822 
14823         if (chip == 1) {
14824             ytop = 30.0;
14825             ybottom = -109.94;
14826         }
14827         else {
14828             ytop = 109.94;
14829             ybottom = -20.0;
14830         }
14831     }
14832 
14833 
14834     slits = cpl_table_new(1);
14835     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
14836     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
14837     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
14838     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
14839     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
14840     cpl_table_set_column_unit(slits, "xtop",    "pixel");
14841     cpl_table_set_column_unit(slits, "ytop",    "pixel");
14842     cpl_table_set_column_unit(slits, "xbottom", "pixel");
14843     cpl_table_set_column_unit(slits, "ybottom", "pixel");
14844 
14845     slit_name = (char *)cpl_propertylist_get_string(header, 
14846                                                     "ESO INS SLIT NAME");
14847 
14848     cpl_table_set(slits, "ytop", 0, ytop);
14849     cpl_table_set(slits, "ybottom", 0, ybottom);
14850 
14851     if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) {
14852         cpl_table_set_int(slits, "slit_id", 0, 1);
14853         cpl_table_set(slits, "xbottom", 0, -0.075);
14854         cpl_table_set(slits, "xtop", 0, 0.075);
14855     }
14856     else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) {
14857         cpl_table_set_int(slits, "slit_id", 0, 2);
14858         cpl_table_set(slits, "xbottom", 0, 5.895);
14859         cpl_table_set(slits, "xtop", 0, 6.105);
14860     }
14861     else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) {
14862         cpl_table_set_int(slits, "slit_id", 0, 3);
14863         cpl_table_set(slits, "xbottom", 0, -6.135);
14864         cpl_table_set(slits, "xtop", 0, -5.865);
14865     }
14866     else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) {
14867         cpl_table_set_int(slits, "slit_id", 0, 4);
14868         cpl_table_set(slits, "xbottom", 0, 11.815);
14869         cpl_table_set(slits, "xtop", 0, 12.185);
14870     }
14871     else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) {
14872         cpl_table_set_int(slits, "slit_id", 0, 5);
14873         cpl_table_set(slits, "xbottom", 0, -12.265);
14874         cpl_table_set(slits, "xtop", 0, -11.735);
14875     }
14876     else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) {
14877         cpl_table_set_int(slits, "slit_id", 0, 6);
14878         cpl_table_set(slits, "xbottom", 0, 17.655);
14879         cpl_table_set(slits, "xtop", 0, 18.345);
14880     }
14881     else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) {
14882         cpl_table_set_int(slits, "slit_id", 0, 7);
14883         cpl_table_set(slits, "xbottom", 0, -18.425);
14884         cpl_table_set(slits, "xtop", 0, -17.575);
14885     }
14886     else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) {
14887         cpl_table_set_int(slits, "slit_id", 0, 8);
14888         cpl_table_set(slits, "xbottom", 0, 23.475);
14889         cpl_table_set(slits, "xtop", 0, 24.525);
14890     }
14891     else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) {
14892         cpl_table_set_int(slits, "slit_id", 0, 9);
14893         cpl_table_set(slits, "xbottom", 0, -24.66);
14894         cpl_table_set(slits, "xtop", 0, -23.34);
14895     }
14896     else {
14897         cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME",
14898                       slit_name);
14899         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
14900         cpl_table_delete(slits);
14901         return NULL;
14902     }
14903 
14904     return slits;
14905 }
14906 
14907 
14922 double mos_get_gain_vimos(cpl_propertylist *header)
14923 {
14924     const char *func = "mos_get_gain_vimos";
14925 
14926     double gain = -1.0;
14927 
14928 
14929     if (cpl_error_get_code() != CPL_ERROR_NONE)
14930         return gain;
14931 
14932     if (header == NULL) {
14933         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14934         return gain;
14935     }
14936 
14937     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
14938     if (cpl_error_get_code()) {
14939         cpl_error_set_where(func);
14940         gain = -1.0;
14941     }
14942 
14943     return gain;
14944 
14945 }
14946 
14947 
14967 cpl_table *mos_load_slits_vimos(cpl_propertylist *header)
14968 {
14969     const char *func = "mos_load_slits_vimos";
14970 
14971     cpl_table *slits;
14972     char       keyname[MAX_COLNAME];
14973     float      slit_x;
14974     float      slit_y;
14975     float      dim_x;
14976     float      dim_y;
14977     int        nslits;
14978     int        slit_id;
14979     int        curved;
14980     int        i;
14981 
14982 
14983     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14984         return NULL;
14985     }
14986 
14987     if (header == NULL) {
14988         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
14989         return NULL;
14990     }
14991 
14992     nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO");
14993 
14994     if (cpl_error_get_code() != CPL_ERROR_NONE) {
14995         cpl_error_set_where(func);
14996         return NULL;
14997     }
14998 
14999     slits = cpl_table_new(nslits);
15000     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
15001     cpl_table_new_column(slits, "xtop",    CPL_TYPE_DOUBLE);
15002     cpl_table_new_column(slits, "ytop",    CPL_TYPE_DOUBLE);
15003     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
15004     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
15005     cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE);
15006     cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE);
15007     cpl_table_new_column(slits, "curved", CPL_TYPE_INT);
15008     cpl_table_set_column_unit(slits, "xtop",    "pixel");
15009     cpl_table_set_column_unit(slits, "ytop",    "pixel");
15010     cpl_table_set_column_unit(slits, "xbottom", "pixel");
15011     cpl_table_set_column_unit(slits, "ybottom", "pixel");
15012     cpl_table_set_column_unit(slits, "xwidth", "mm");
15013     cpl_table_set_column_unit(slits, "ywidth", "mm");
15014 
15015     for (i = 0; i < nslits; i++) {
15016         sprintf(keyname, "ESO INS SLIT%d ID", i+1);
15017         slit_id = cpl_propertylist_get_int(header, keyname);
15018         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15019             cpl_error_set_where(func);
15020             return NULL;
15021         }
15022         sprintf(keyname, "ESO INS SLIT%d X", i+1);
15023         slit_x = cpl_propertylist_get_double(header, keyname);
15024         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15025             cpl_error_set_where(func);
15026             return NULL;
15027         }
15028         sprintf(keyname, "ESO INS SLIT%d Y", i+1);
15029         slit_y = cpl_propertylist_get_double(header, keyname);
15030         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15031             cpl_error_set_where(func);
15032             return NULL;
15033         }
15034         sprintf(keyname, "ESO INS SLIT%d DIMX", i+1);
15035         dim_x = cpl_propertylist_get_double(header, keyname);
15036         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15037             cpl_error_set_where(func);
15038             return NULL;
15039         }
15040 
15041         sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1);
15042         if (cpl_propertylist_has(header, keyname)) {
15043             curved = 1;
15044         }
15045         else {
15046             sprintf(keyname, "ESO INS SLIT%d DIMY", i+1);
15047             curved = 0;
15048         }
15049         dim_y = cpl_propertylist_get_double(header, keyname);
15050         if (cpl_error_get_code() != CPL_ERROR_NONE) {
15051             cpl_error_set_where(func);
15052             return NULL;
15053         }
15054 
15055         cpl_table_set_int(slits, "slit_id", i, slit_id);
15056         cpl_table_set(slits, "xtop", i, slit_x - dim_x/2);
15057         cpl_table_set(slits, "ytop", i, slit_y);
15058         cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2);
15059         cpl_table_set(slits, "ybottom", i, slit_y);
15060         cpl_table_set(slits, "xwidth", i, dim_x);
15061         cpl_table_set(slits, "ywidth", i, dim_y);
15062         cpl_table_set_int(slits, "curved", i, curved);
15063     }
15064 
15065     return slits;
15066 }
15067 
15068 
15078 int mos_check_multiplex(cpl_table *slits)
15079 {
15080     cpl_propertylist *sort;
15081     int               nrow;
15082     int               i, multiplex, xprev, xcur;
15083     double            prev, cur;
15084     double            tolerance = 1.0; // About spatially aligned slits (mm)
15085 
15086 
15087     /*
15088      * Create an auxiliary column containing a sort of integer
15089      * x coordinate of the slit, to guarantee that slits at the
15090      * same spatial offset are recognised immediately as in spectral 
15091      * multiplexing.
15092      */
15093 
15094     sort = cpl_propertylist_new();
15095     cpl_propertylist_append_bool(sort, "xtop", 0);
15096     cpl_table_sort(slits, sort);
15097     cpl_propertylist_delete(sort);
15098 
15099     prev = cpl_table_get_double(slits, "xtop", 0, NULL);
15100     cpl_table_new_column(slits, "xind", CPL_TYPE_INT);
15101     cpl_table_set_int(slits, "xind", 0, prev);   // cast to int is intentional
15102     nrow = cpl_table_get_nrow(slits);
15103     for (i = 1; i < nrow; i++) {
15104         cur = cpl_table_get_double(slits, "xtop", i, NULL);
15105         if (fabs(prev - cur) > tolerance)
15106             prev = cur;
15107         cpl_table_set_int(slits, "xind", i, prev);
15108     }
15109 
15110     /*
15111      * Now sort according to increasing (integer) x positions, and when
15112      * those are equal (multiplexed) according to the increasing y position.
15113      */
15114 
15115     sort = cpl_propertylist_new();
15116     cpl_propertylist_append_bool(sort, "xind", 0);
15117     cpl_propertylist_append_bool(sort, "ytop", 0);
15118     cpl_table_sort(slits, sort);
15119     cpl_propertylist_delete(sort);
15120 
15121     /*
15122      * Now assign to each slit its multiplex order.
15123      */
15124 
15125     multiplex = 0;
15126     cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT);
15127     xprev = cpl_table_get_int(slits, "xind", 0, NULL);
15128     cpl_table_set_int(slits, "multiplex", 0, multiplex);
15129     nrow = cpl_table_get_nrow(slits);
15130     for (i = 1; i < nrow; i++) {
15131         xcur = cpl_table_get_int(slits, "xind", i, NULL);
15132         if (xcur == xprev) {
15133             multiplex++;
15134         }
15135         else {
15136             xprev = xcur;
15137             multiplex = 0;
15138         }
15139         cpl_table_set_int(slits, "multiplex", i, multiplex);
15140     }
15141 
15142     cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT);
15143 
15144     cpl_table_erase_column(slits, "xind");
15145 
15146     return 1 + cpl_table_get_column_max(slits, "multiplex");
15147 
15148 }
15149 
15150 
15177 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 
15178                                     int check_consistency)
15179 {
15180     const char *func = "mos_load_overscans_vimos";
15181 
15182     int        nx = 0;
15183     int        ny = 0;
15184     int        px = 0;
15185     int        py = 0;
15186     int        ox = 0;
15187     int        oy = 0;
15188     int        vx = 0;
15189     int        vy = 0;
15190     int        nrows;
15191     cpl_table *overscans;
15192 
15193 
15194     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15195         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15196         return NULL;
15197     }
15198 
15199     if (header == NULL) {
15200         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15201         return NULL;
15202     }
15203 
15204     if (cpl_propertylist_has(header, "NAXIS1"))
15205         nx = cpl_propertylist_get_int(header, "NAXIS1");
15206     if (cpl_propertylist_has(header, "NAXIS2"))
15207         ny = cpl_propertylist_get_int(header, "NAXIS2");
15208     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX"))
15209         px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX");
15210     if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY"))
15211         py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY");
15212     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX"))
15213         ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX");
15214     if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY"))
15215         oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY");
15216     if (cpl_propertylist_has(header, "ESO DET OUT1 NX"))
15217         vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX");
15218     if (cpl_propertylist_has(header, "ESO DET OUT1 NY"))
15219         vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY");
15220 
15221     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15222         cpl_msg_error(func, "Missing overscan keywords in header");
15223         cpl_error_set_where(func);
15224         return NULL;
15225     }
15226 
15227     if (px < 0 || py < 0 || ox < 0 || oy < 0) {
15228         cpl_msg_error(func, "Missing overscan keywords in header");
15229         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15230         return NULL;
15231     }
15232 
15233     if ((px + vx + ox != nx) || (py + vy + oy != ny)) {
15234         if (check_consistency) {
15235             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15236             return NULL;
15237         }
15238         else {
15239             cpl_msg_debug(func, "Overscans description conflicts with "
15240                           "reported image sizes, "
15241                           "%d + %d + %d != %d or "
15242                           "%d + %d + %d != %d",
15243                           px, vx, ox, nx,
15244                           py, vy, oy, ny);
15245         }
15246     }
15247 
15248     nrows = 0;
15249     if (px > 0)
15250         nrows++;
15251     if (ox > 0)
15252         nrows++;
15253     if (py > 0)
15254         nrows++;
15255     if (oy > 0)
15256         nrows++;
15257 
15258     if (nrows > 2) {
15259         cpl_msg_error(func, "Unexpected overscan regions "
15260                       "(both in X and Y direction)");
15261         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15262         return NULL;
15263     }
15264 
15265 
15266     /*
15267      * A row is added for the description of the valid region of the
15268      * exposure the input header belongs to.
15269      */
15270 
15271     nrows++;
15272 
15273     overscans = cpl_table_new(nrows);
15274     cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15275     cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15276     cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15277     cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15278 
15279     nrows = 0;
15280 
15281     cpl_table_set_int(overscans, "xlow", nrows, px);
15282     cpl_table_set_int(overscans, "ylow", nrows, py);
15283     cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15284     cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15285     nrows++;
15286 
15287     if (px > 0) {
15288         cpl_table_set_int(overscans, "xlow", nrows, 0);
15289         cpl_table_set_int(overscans, "ylow", nrows, 0);
15290         cpl_table_set_int(overscans, "xhig", nrows, px);
15291         cpl_table_set_int(overscans, "yhig", nrows, ny);
15292         nrows++;
15293     }
15294 
15295     if (ox > 0) {
15296         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15297         cpl_table_set_int(overscans, "ylow", nrows, 0);
15298         cpl_table_set_int(overscans, "xhig", nrows, nx);
15299         cpl_table_set_int(overscans, "yhig", nrows, ny);
15300         nrows++;
15301     }
15302 
15303     if (py > 0) {
15304         cpl_table_set_int(overscans, "xlow", nrows, 0);
15305         cpl_table_set_int(overscans, "ylow", nrows, 0);
15306         cpl_table_set_int(overscans, "xhig", nrows, nx);
15307         cpl_table_set_int(overscans, "yhig", nrows, py);
15308         nrows++;
15309     }
15310 
15311     if (oy > 0) {
15312         cpl_table_set_int(overscans, "xlow", nrows, 0);
15313         cpl_table_set_int(overscans, "ylow", nrows, ny - oy);
15314         cpl_table_set_int(overscans, "xhig", nrows, nx);
15315         cpl_table_set_int(overscans, "yhig", nrows, ny);
15316         nrows++;
15317     }
15318 
15319     return overscans;
15320 
15321 }
15322 
15323 
15324 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header)
15325 {
15326     const char *func = "mos_load_overscans_fors";
15327 
15328     int        nports;
15329     int        nx = 0;
15330     int        ny = 0;
15331     int        px = 0;
15332     int        py = 0;
15333     int        ox = 0;
15334     int        oy = 0;
15335     int        rebin;
15336     int        nrows;
15337     cpl_table *overscans;
15338 
15339 
15340     if (cpl_error_get_code() != CPL_ERROR_NONE) {
15341         cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message());
15342         return NULL;
15343     }
15344 
15345     if (header == NULL) {
15346         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15347         return NULL;
15348     }
15349 
15350     if (cpl_propertylist_has(header, "ESO DET OUTPUTS"))
15351         nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS");
15352 
15353     if (nports == 4                                        && 
15354         cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") &&
15355         cpl_propertylist_has(header, "ESO DET WIN1 BINX")) {
15356 
15357         rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
15358 
15359         overscans = cpl_table_new(3);
15360         cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT);
15361         cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT);
15362         cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT);
15363         cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT);
15364 
15365         px = 16 / rebin;
15366         ox = 16 / rebin;
15367         nx = 2080 / rebin;
15368         ny = 2048 / rebin;
15369         nrows = 0;
15370 
15371         cpl_table_set_int(overscans, "xlow", nrows, px);
15372         cpl_table_set_int(overscans, "ylow", nrows, py);
15373         cpl_table_set_int(overscans, "xhig", nrows, nx - ox);
15374         cpl_table_set_int(overscans, "yhig", nrows, ny - oy);
15375         nrows++;
15376 
15377         cpl_table_set_int(overscans, "xlow", nrows, 0);
15378         cpl_table_set_int(overscans, "ylow", nrows, 0);
15379         cpl_table_set_int(overscans, "xhig", nrows, px);
15380         cpl_table_set_int(overscans, "yhig", nrows, ny);
15381         nrows++;
15382 
15383         cpl_table_set_int(overscans, "xlow", nrows, nx - ox);
15384         cpl_table_set_int(overscans, "ylow", nrows, 0);
15385         cpl_table_set_int(overscans, "xhig", nrows, nx);
15386         cpl_table_set_int(overscans, "yhig", nrows, ny);
15387         nrows++;
15388     }
15389     else {
15390         overscans = mos_load_overscans_vimos(header, 0);
15391     }
15392 
15393     return overscans;
15394 
15395 }
15396 
15428 #define READY 1
15429 #ifdef READY
15430 
15431 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 
15432                                        int samples, int order)
15433 {
15434 
15435     const char *func = "mos_montecarlo_polyfit";
15436 
15437     cpl_polynomial *p;
15438     cpl_polynomial *q;
15439     cpl_vector     *listx;
15440     cpl_vector     *listy;
15441     double          err;
15442     double         *x;
15443     double         *px;
15444     double         *x_eval;
15445     double         *px_eval;
15446     double         *sigma;
15447     double         *vy;
15448     double         *dy;
15449     int             npoints, nevaluate;
15450     int             i, j;
15451 
15452 
15453     if (points == NULL || evaluate == NULL) {
15454         cpl_error_set(func, CPL_ERROR_NULL_INPUT);
15455         return NULL;
15456     }
15457 
15458     if (!cpl_table_has_column(points, "x")) {
15459         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15460         return NULL;
15461     }
15462 
15463     if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) {
15464         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15465         return NULL;
15466     }
15467 
15468     if (cpl_table_has_invalid(points, "x")) {
15469         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15470         return NULL;
15471     }
15472 
15473     if (!cpl_table_has_column(points, "y")) {
15474         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15475         return NULL;
15476     }
15477 
15478     if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) {
15479         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15480         return NULL;
15481     }
15482 
15483     if (cpl_table_has_invalid(points, "y")) {
15484         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15485         return NULL;
15486     }
15487 
15488     if (cpl_table_has_column(points, "y_err")) {
15489 
15490         if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) {
15491             cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15492             return NULL;
15493         }
15494     
15495         if (cpl_table_has_invalid(points, "y_err")) {
15496             cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15497             return NULL;
15498         }
15499     }
15500 
15501     if (!cpl_table_has_column(evaluate, "x")) {
15502         cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND);
15503         return NULL;
15504     }
15505 
15506     if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) {
15507         cpl_error_set(func, CPL_ERROR_INVALID_TYPE);
15508         return NULL;
15509     }
15510 
15511     if (cpl_table_has_invalid(evaluate, "x")) {
15512         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15513         return NULL;
15514     }
15515 
15516     if (samples < 2 || order < 0) {
15517         cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT);
15518         return NULL;
15519     }
15520 
15521     npoints = cpl_table_get_nrow(points);
15522     listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x"));
15523     listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y"));
15524 
15525     p = cpl_polynomial_fit_1d_create(listx, listy, order, &err);
15526 
15527     if (!cpl_table_has_column(points, "y_err")) {
15528         err = sqrt(err);
15529         cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE);
15530         cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err);
15531         cpl_msg_info(func, "Error column not found - set to %f\n", err);
15532     }
15533 
15534     /*
15535      * Create columns containing modeled values at each x
15536      */
15537 
15538     if (cpl_table_has_column(points, "px"))
15539         cpl_table_erase_column(points, "px");
15540     cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE);
15541     cpl_table_fill_column_window_double(points, "px", 0, npoints, 0);
15542     x = cpl_table_get_data_double(points, "x");
15543     px = cpl_table_get_data_double(points, "px");
15544     for (i = 0; i < npoints; i++)
15545         px[i] = cpl_polynomial_eval_1d(p, x[i], NULL);
15546 
15547     nevaluate = cpl_table_get_nrow(evaluate);
15548 
15549     if (cpl_table_has_column(evaluate, "px"))
15550         cpl_table_erase_column(evaluate, "px");
15551     cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE);
15552     cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0);
15553     x_eval = cpl_table_get_data_double(evaluate, "x");
15554     px_eval = cpl_table_get_data_double(evaluate, "px");
15555     for (i = 0; i < nevaluate; i++)
15556         px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL);
15557 
15558     /*
15559      * Initialise column with sigma
15560      */
15561 
15562     if (cpl_table_has_column(evaluate, "sigma"))
15563         cpl_table_erase_column(evaluate, "sigma");
15564     cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE);
15565     cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0);
15566     sigma = cpl_table_get_data_double(evaluate, "sigma");
15567 
15568     /*
15569      * Compute varied y cordinates to fit
15570      */
15571 
15572     if (cpl_table_has_column(points, "vy"))
15573         cpl_table_erase_column(points, "vy");
15574     cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE);
15575     cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0);
15576     vy = cpl_table_get_data_double(points, "vy");
15577     dy = cpl_table_get_data_double(points, "y_err");
15578     cpl_vector_unwrap(listy);
15579     listy = cpl_vector_wrap(npoints, vy);
15580 
15581     for (i = 0; i < samples; i++) {
15582         for (j = 0; j < npoints; j++)
15583             vy[j] = px[j] + dy[j] * mos_randg(1);
15584         q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL);
15585         for (j = 0; j < nevaluate; j++)
15586             sigma[j] += fabs(px_eval[j] 
15587                       - cpl_polynomial_eval_1d(q, x_eval[j], NULL));
15588         cpl_polynomial_delete(q);
15589     }
15590 
15591     /* 
15592      * Factor 1.25 to convert average deviation to sigma 
15593      */
15594 
15595     cpl_table_multiply_scalar(evaluate, "sigma", 1.25);
15596     cpl_table_divide_scalar(evaluate, "sigma", samples);
15597 
15598     cpl_vector_unwrap(listx);
15599     cpl_vector_unwrap(listy);
15600 
15601     return p;
15602 }
15603 
15604 #endif
15605 
15628 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 
15629                                    double gain, double bias)
15630 {
15631     float *data;
15632     int    npix, i;
15633 
15634 
15635     if (image == NULL)
15636         return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
15637 
15638     if (ron < 0.0 || gain <= FLT_EPSILON)
15639         return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
15640 
15641     data = cpl_image_get_data_float(image);
15642     npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image);
15643     ron *= ron;
15644 
15645     for (i = 0; i < npix; i++) {
15646         if (data[i] < bias) {
15647             data[i] += sqrt(ron) * mos_randg(1);
15648         }
15649         else {
15650             data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1);
15651         }
15652     }
15653 
15654     return CPL_ERROR_NONE;
15655 }
15656 
15657 
15672 cpl_error_code mos_refmask_find_gaps(cpl_mask  *refmask,
15673                                      cpl_image *master_flat,
15674                                      double     level)
15675 {
15676     int          nx     = cpl_mask_get_size_x(refmask);
15677     int          ny     = cpl_mask_get_size_y(refmask);
15678 
15679     int        * xpos   = cpl_calloc(sizeof(int), ny);
15680 
15681     cpl_image  * filtered = cpl_image_duplicate(master_flat);
15682     cpl_mask   * kernel = cpl_mask_new(9, 9);
15683     cpl_vector * v      = cpl_vector_new(ny);
15684     cpl_vector * truev;
15685     int          nvalid = 0;
15686     double     * flats  = cpl_vector_get_data(v);
15687 
15688     double       median, stdev, delta;
15689 
15690     int          i, kill;
15691 
15692 
15693     cpl_mask_not(kernel);
15694     cpl_image_filter_mask(filtered, master_flat, kernel, 
15695                           CPL_FILTER_MEDIAN, CPL_BORDER_COPY);
15696     cpl_mask_delete(kernel);
15697 
15698     for (i = 1; i <= ny; i++) {
15699         int j = 0;
15700 
15701         do j++;
15702         while (!cpl_mask_get(refmask, j, i) && j < nx);
15703 
15704         if (j < nx) {
15705             int rejected;
15706 
15707             xpos[i - 1] = j;
15708             flats[nvalid] = cpl_image_get(filtered, j, i, &rejected);
15709             nvalid++;
15710         }
15711         else {
15712             xpos[i - 1] = -1;
15713         }
15714     }
15715 
15716     if (nvalid == 0)
15717         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15718 
15719     truev = cpl_vector_wrap(nvalid, flats);
15720 
15721     median = cpl_vector_get_median(truev);
15722 
15723     if (level < 0.0)
15724        stdev = cpl_vector_get_stdev(truev);
15725 
15726     cpl_vector_unwrap(truev);
15727     cpl_vector_delete(v);
15728 
15729     for (i = 1; i <= ny; i++) {
15730         if (xpos[i - 1] > 0) {
15731             int    rejected;
15732             double kappa = 1.0;
15733 
15734             delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median;
15735 
15736             if (level < 0.0)
15737                 kill = fabs(delta) > stdev * kappa;
15738             else
15739                 kill = delta < level;
15740 
15741             if (kill) {
15742                 int j = 0;
15743             
15744                 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) {
15745                     cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0);
15746                     j++;
15747                 }
15748             }
15749         }
15750     }
15751 
15752     cpl_image_delete(filtered);
15753     cpl_free(xpos);
15754 
15755     return cpl_error_get_code();
15756 }
15757 
15765 cpl_error_code mos_saturation_process(cpl_image * image)
15766 {
15767     int     nx    = cpl_image_get_size_x(image);
15768     int     ny    = cpl_image_get_size_y(image);
15769     int     npix  = nx * ny;
15770     float * sdata = cpl_image_get_data_float(image);
15771 
15772     int count, i, j, k;
15773 
15774     /*
15775      * This is used to avoid saturation level coded with pixel value zero
15776      * To make it more robust against random 0.0 values, check that also
15777      * next pixel along the spatial direction is 0.0.
15778      */
15779 
15780     for (i = 0; i < npix - nx; i++)
15781         if (sdata[i] == 0.0 && sdata[i + nx] == 0.0)
15782             sdata[i] = 65535.0;
15783 
15784     for (i = npix - nx; i < npix; i++)
15785         if (sdata[i] == 0.0) 
15786             sdata[i] = 65535.0;
15787 
15788     /*
15789      * This is a dirty trick to overcome saturations (making up a false
15790      * tip on their flat tops). This should be useless with a better
15791      * peak detection algorithm.
15792      */
15793 
15794     for (i = 0; i < npix; i++) {
15795         if (sdata[i] >= 65535.0) {
15796             count = 0;
15797             for (j = i; j < npix; j++) {
15798                 if (sdata[j] < 65535.0) {
15799                     break;
15800                 }
15801                 else {
15802                     count++;
15803                 }
15804             }
15805             if (count < 30 && count > 2) {
15806                 for (j = i; j < i + count/2; j++)
15807                     sdata[j] = sdata[i] + 1000.0 * (j - i);
15808                 if (count % 2 != 0) {
15809                     sdata[j] = sdata[j-1] + 1000.0;
15810                     j++;
15811                 }
15812                 for (k = j; k <= i + count; k++)
15813                     sdata[k] = sdata[i] - 1000.0 * (k - i - count);
15814                 i = k;
15815             }
15816         }
15817     }
15818 
15819     return cpl_error_get_code();
15820 }
15821 
15822 
15831 cpl_error_code mos_subtract_background(cpl_image * image)
15832 {
15833     /*
15834      * Create and subtract background
15835      */
15836 
15837     cpl_image * bimage = mos_arc_background(image, 15, 15);
15838     cpl_image_subtract(image, bimage);
15839     cpl_image_delete(bimage);
15840 
15841     return cpl_error_get_code();
15842 }
15843 
15844 
15861 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 
15862                                     int nscience, float tolerance)
15863 {
15864     int i, j;
15865 
15866     cpl_table *summary;
15867     int summary_nobjs = 0;
15868  
15869     int nobjs;
15870 
15871     int nmatches;
15872     int nslits = cpl_table_get_nrow(slitss[0]);
15873 
15874     int maxobjs;
15875     int k, m;
15876     int nstokes, sstokes;
15877 
15878     cpl_table **work;
15879 
15880     work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience);
15881 
15882 
15883     /* 
15884      * First we build a table listing the offset of each detected
15885      * object at each angle and each beam, from the bottom of each 
15886      * slit spectrum, and the pair that slit spectrum belongs to.
15887      * This summary table will have as many rows as objects found 
15888      * in total at all angles.
15889      */
15890 
15891     for (j = 0; j < nscience; j++) {
15892         int c_nobjs = mos_get_nobjects(slitss[j]);
15893         if (!c_nobjs) 
15894             return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
15895         summary_nobjs += c_nobjs;
15896     }
15897 
15898     summary = cpl_table_new(summary_nobjs);
15899 
15900     cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE);
15901     cpl_table_new_column(summary, "pair",   CPL_TYPE_INT);
15902     cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE);
15903     cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE);
15904 
15905     /*
15906      * Fill the summary table with data from all objects:
15907      */
15908 
15909     nobjs = 0;
15910 
15911     /* Loop on all object tables (one for each angle) */
15912     for (j = 0; j < nscience; j++) {
15913         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]);
15914 
15915         /* Loop on all slits found on first - i.e., ALL - object table */
15916         for (k = 0; k < nslits; k++) {
15917 
15918             /* Loop on all objects found on each object table */
15919             for (m = 0; m < c_maxobjs; m++) {
15920                 int null;
15921                 char *name = cpl_sprintf("object_%d", m + 1);
15922                 double obj = cpl_table_get_double(slitss[j], name, k, &null);
15923                 int pos;
15924                 int pair;
15925 
15926                 cpl_free(name);
15927 
15928                 if (null) 
15929                     break;  /* No object #m+1 in this slit - go to next slit */
15930 
15931                 /*
15932                  * Copy necessary object data to summary table. Note 
15933                  * that the absolute object position (row) in the
15934                  * rectified image is made relative to the bottom
15935                  * position (row) of the current slit.
15936                  */ 
15937         
15938                 pos  = cpl_table_get_int(slitss[j], "position", k, &null);
15939                 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null);
15940                 cpl_table_set(summary, "absolute", nobjs, obj);
15941                 cpl_table_set(summary, "pos", nobjs, pos);
15942                 cpl_table_set(summary, "offset", nobjs, obj - pos);
15943                 cpl_table_set(summary, "pair", nobjs, pair);
15944 
15945                 nobjs++;
15946             }
15947         }
15948     }
15949 
15950 //    cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT);
15951 
15952     /* 
15953      * Perform the intersection: what are the objects belonging
15954      * to the same slit (same pair ordinary + extraordinary) which 
15955      * are observed at the same offset at all angles? Those are
15956      * the polarimetric objects.
15957      */
15958 
15959     nmatches = 0;
15960     maxobjs = mos_get_maxobjs_per_slit(slitss[0]);
15961 
15962     /*
15963      * We loop on the objects of the first-angle object table as 
15964      * reference, and check whether those objects are present also
15965      * at *all* other angles. Note that the loop advances by pairs.
15966      * If the top (k = 0) slit spectrum is not an ordinary beam,
15967      * it is ignored. The loop advances by pairs, starting at the
15968      * first complete pair. It is implicitely assumed that the 
15969      * slit spectrum on top is always from the ordinary beam, and 
15970      * the spectrum below (k+1) its extraordinary match.
15971      */
15972 
15973     for (k = 0; k < nslits; k+=2) {
15974         int slitmatches = 0;
15975 
15976         if (k == 0) {
15977             if (cpl_table_get_int(slitss[0], "pair_id",  0, NULL) !=
15978                 cpl_table_get_int(slitss[0], "pair_id",  1, NULL)) {
15979 
15980                 /*
15981                  * This is not an ordinary beam - advance to next slit.
15982                  */
15983 
15984                 k++;
15985                 continue;
15986             }
15987         }
15988 
15989         for (m = 0; m < maxobjs; m++) {
15990             int null;
15991             char *name = cpl_sprintf("object_%d", m + 1);
15992             double obj = cpl_table_get_double(slitss[0], name, k, &null);
15993             double pos;
15994             int pair;
15995 
15996             char *name_obj = NULL;
15997             char *name_start = NULL;
15998             char *name_end = NULL;
15999             char *name_row = NULL;
16000             char *name_row_s = NULL;
16001 
16002             char *name_start_o = NULL;
16003             char *name_end_o = NULL;
16004             char *name_row_o = NULL;
16005             char *name_start_v = NULL;
16006             char *name_end_v = NULL;
16007             char *name_obj_v = NULL;
16008 
16009             int start, end;
16010             int length;
16011  
16012             int selected;
16013             int v, start_v, end_v;
16014             double min_v, obj_v;
16015 
16016 
16017             cpl_free(name);
16018 
16019             if (null) 
16020                 break;
16021 
16022             /*
16023              * Each object of the first object table belongs to a
16024              * slit spectrum (k). This slit spectrum has a position
16025              * in the rectified image, and it belongs to a given 
16026              * ordinary + extraordinary pair.
16027              */
16028      
16029             pos  = cpl_table_get_int(slitss[0], "position", k, &null);
16030             pair = cpl_table_get_int(slitss[0], "pair_id",  k, &null);
16031 
16032             /*
16033              * Now from the summary table we can select all objects
16034              * which have the same offset (obj - pos) within all slit
16035              * spectra belonging to the same ordinary + extraordinary 
16036              * pair (at all angles).
16037              */
16038 
16039             cpl_table_select_all(summary);  /* Reset selection */
16040 
16041             cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair);
16042             cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN,
16043                                           obj - pos + tolerance);
16044             selected = 
16045             cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN,
16046                                           obj - pos - tolerance);
16047 
16048 
16049             /*
16050              * If this object were observed at all angles (nscience) and 
16051              * at all beams (2), we should have selected exactly 2*nscience
16052              * objects. If not, this is not a polarimetric object, and it
16053              * is discarded from the intersection.
16054              */
16055             
16056             if (selected != nscience * 2) 
16057                 continue;
16058 
16059             /*
16060              * If we reach this point we have found one valid polarimetric
16061              * object, that must be inserted in the intersection object
16062              * table.
16063              */
16064  
16065             slitmatches++;
16066 
16067             /*
16068              * Names of the columns of the output table where the
16069              * object information needs to be copied. Note that a
16070              * new column is created, the "row_stokes_#", where the
16071              * row number of the extracted polarimetric signal is
16072              * also computed. For the moment this column will be 
16073              * left empty - it will be filled only when all matches 
16074              * are collected.
16075              */
16076 
16077             name_obj   = cpl_sprintf("object_%d",     slitmatches);
16078             name_start = cpl_sprintf("start_%d",      slitmatches);
16079             name_end   = cpl_sprintf("end_%d",        slitmatches);
16080             name_row   = cpl_sprintf("row_%d",        slitmatches);
16081             name_row_s = cpl_sprintf("row_stokes_%d", slitmatches);
16082 
16083             /*
16084              * Names of the columns of the input table where the
16085              * object information is available.
16086              */
16087 
16088             name_start_o = cpl_sprintf("start_%d",  m + 1);
16089             name_end_o   = cpl_sprintf("end_%d",    m + 1);
16090             name_row_o   = cpl_sprintf("row_%d",    m + 1);
16091 
16092             /*
16093              * If the output columns do not exist yet, create them.
16094              */
16095  
16096             if (!cpl_table_has_column(origslits, name_obj)) {
16097                 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE);
16098                 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT);
16099                 cpl_table_new_column(origslits, name_end,   CPL_TYPE_INT);
16100                 cpl_table_new_column(origslits, name_row,   CPL_TYPE_INT);
16101                 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT);
16102             }
16103 
16104             /*
16105              * The current slit spectrum is k. The slit spectrum immediately
16106              * below (in the rectified image) is k+1. We need the length of
16107              * the spectrum below for computing the _absolute_ coordinates
16108              * of the objects in the rectified image in both beams.
16109              */
16110  
16111             length = cpl_table_get_int(origslits, "length", k + 1, &null);
16112 
16113             /* NEW:
16114              * Names of the columns of the input table where
16115              * the information of the corresponding object of 
16116              * the next beam is available.
16117              */
16118 
16119             for (v = 0; v < maxobjs; v++) {
16120                 char *name_v = cpl_sprintf("object_%d", v + 1);
16121                 double obj_v = cpl_table_get_double(slitss[0], name_v, 
16122                                                     k + 1, &null);
16123 
16124                 cpl_free(name_v);
16125 
16126                 if (null) 
16127                     break;
16128 
16129                 if (v) {
16130                     if (fabs(obj - length - obj_v) < min_v) {
16131                         min_v = fabs(obj - length - obj_v);
16132                         cpl_free(name_start_v);
16133                         cpl_free(name_end_v);
16134                         cpl_free(name_obj_v);
16135                         name_start_v = cpl_sprintf("start_%d", v + 1);
16136                         name_end_v   = cpl_sprintf("end_%d",   v + 1);
16137                         name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16138                     }
16139                 }
16140                 else {
16141                     min_v = fabs(obj - length - obj_v);
16142                     name_start_v = cpl_sprintf("start_%d", v + 1);
16143                     name_end_v   = cpl_sprintf("end_%d",   v + 1);
16144                     name_obj_v   = cpl_sprintf("object_%d",   v + 1);
16145                 }
16146             }
16147 
16148             /*
16149              * Read from the first input object table (first angle)
16150              * the spatial window enclosing the object.
16151              */
16152 
16153             start = cpl_table_get_int(slitss[0], name_start_o, k, &null);
16154             end   = cpl_table_get_int(slitss[0], name_end_o,   k, &null);
16155 
16156             /* NEW:
16157              * Spatial window of the matching object in the next beam.
16158              */
16159 
16160             start_v = cpl_table_get_int(slitss[0], name_start_v,  k + 1, &null);
16161             end_v   = cpl_table_get_int(slitss[0], name_end_v,    k + 1, &null);
16162             obj_v   = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null);
16163 
16164             /*
16165              * Write the object coordinates in the same slit, and in the
16166              * slit below. Note that here we assume that all slits were
16167              * traced perfectly, and we compute the theoretical coords
16168              * (obj - length) within the next slit spectrum (k + 1). In
16169              * principle we should read them as well from the input
16170              * table!
16171              */
16172 
16173             cpl_table_set_double(origslits, name_obj,   k,     obj);
16174             cpl_table_set_double(origslits, name_obj,   k + 1, obj_v);
16175          // cpl_table_set_double(origslits, name_obj,   k + 1, obj - length);
16176 
16177             cpl_table_set_int(origslits,    name_start, k,     start);
16178             cpl_table_set_int(origslits,    name_start, k + 1, start_v);
16179          // cpl_table_set_int(origslits,    name_start, k + 1, start - length);
16180 
16181             cpl_table_set_int(origslits,    name_end,   k,     end);
16182             cpl_table_set_int(origslits,    name_end,   k + 1, end_v);
16183          // cpl_table_set_int(origslits,    name_end,   k + 1, end - length);
16184 
16185             /*
16186              * "nmatches" is counting at what "reduced" image row the
16187              * extracted spectra are. Note that this is s preliminary
16188              * numbering - which is wrong: other objects may be found
16189              * in the same slit, and then the indeces would not be in
16190              * sequence. What is important is that at the end of this
16191              * loop "nmatches" would be the total number of matching 
16192              * objects. The two cpl_table_set_int() calls made here
16193              * cannot be removed - they "validate" those table elements
16194              * (see ahead). 
16195              */
16196 
16197             cpl_table_set_int(origslits,    name_row,   k,     nmatches);
16198             nmatches++;
16199             cpl_table_set_int(origslits,    name_row,   k + 1, nmatches);
16200             nmatches++;
16201 
16202             cpl_free(name_obj);
16203             cpl_free(name_start);
16204             cpl_free(name_end);
16205             cpl_free(name_row);
16206             cpl_free(name_row_s);
16207 
16208             cpl_free(name_start_o);
16209             cpl_free(name_end_o);
16210             cpl_free(name_row_o);
16211 
16212             cpl_free(name_start_v); name_start_v = NULL;
16213             cpl_free(name_end_v); name_end_v = NULL;
16214             cpl_free(name_obj_v); name_obj_v = NULL;
16215         }
16216     }
16217 
16218     /*
16219      * The summary table has fulfilled its function. If no matching 
16220      * objects are found, the function returns with an error.
16221      */
16222 
16223     cpl_table_delete(summary);
16224 
16225     if (!nmatches)
16226         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 
16227 
16228     /*
16229      * Now we consider the resulting intersection object table,
16230      * listing all matches. As seen, the image row number reported
16231      * in the columns "row_#" was not really performed sequentially.
16232      * We need to renumber sequentially...
16233      * We need also to fill the "row_stokes_#" column the way the
16234      * extracted polarimetric signal will be stored in the 
16235      * reduced_pol_images...
16236      */
16237  
16238     maxobjs = mos_get_maxobjs_per_slit(origslits);
16239     nstokes = nmatches / 2;         /* nmatches is always an even number     */
16240 
16241     for (k = 0; k < nslits; k++) {
16242         if (k % 2) { /* Extraordinary beam */
16243             nstokes = sstokes;      /* Use same start value as for ordinary  */
16244         }
16245         else {       /* Ordinary beam      */
16246             sstokes = nstokes;      /* Memorise start value at ordinary beam */
16247         }
16248 
16249         for (m = 0; m < maxobjs; m++) {
16250             char *name       = cpl_sprintf("row_%d",        m + 1);
16251             char *namestokes = cpl_sprintf("row_stokes_%d", m + 1);
16252 
16253             if (!cpl_table_is_valid(origslits, name, k)) {
16254                 cpl_free(name);
16255                 cpl_free(namestokes);
16256                 break;
16257             }
16258             else { 
16259                 nmatches--;
16260                 nstokes--;
16261                 cpl_table_set_int(origslits, name, k, nmatches);
16262                 cpl_table_set_int(origslits, namestokes, k, nstokes);
16263             }
16264 
16265             cpl_free(name);
16266             cpl_free(namestokes);
16267         }
16268     }
16269 
16270 
16271     /*
16272      * This is done to avoid the NULL value is zero (it would invalidate
16273      * also the row_# = 0 or start_# = 0 for an object), and to enable 
16274      * working directly with the column data buffers, when using this 
16275      * table afterwards.
16276      */
16277 
16278     for (j = 0; j < maxobjs; j++) {
16279         char *name = cpl_sprintf("object_%d", j + 1);
16280         cpl_table_fill_invalid_double(origslits, name, -1);
16281         cpl_free(name);
16282 
16283         name       = cpl_sprintf("start_%d", j + 1);
16284         cpl_table_fill_invalid_int(origslits, name, -1);
16285         cpl_free(name);
16286 
16287         name       = cpl_sprintf("end_%d", j + 1);
16288         cpl_table_fill_invalid_int(origslits, name, -1);
16289         cpl_free(name);
16290 
16291         name       = cpl_sprintf("row_%d", j + 1);
16292         cpl_table_fill_invalid_int(origslits, name, -1);
16293         cpl_free(name);
16294 
16295         name       = cpl_sprintf("row_stokes_%d", j + 1);
16296         cpl_table_fill_invalid_int(origslits, name, -1);
16297         cpl_free(name);
16298     }
16299 
16300     /*********************************************************************
16301      * This tail has been added to propagate the selection of valid
16302      * objects also to the input slitss[] tables. Just eliminate all
16303      * this final part to suppress this behaviour.
16304      */
16305 
16306     /*
16307      * First of all, make a working copy and remove all columns related 
16308      * to objects from the input object tables. 
16309      */
16310 
16311     for (i = 0; i < nscience; i++) {
16312         int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]);
16313 
16314         work[i] = cpl_table_duplicate(slitss[i]);
16315 
16316         for (m = 0; m < c_maxobjs; m++) {
16317             char *object_o = cpl_sprintf("object_%d", m + 1);
16318             char *start_o  = cpl_sprintf("start_%d",  m + 1);
16319             char *end_o    = cpl_sprintf("end_%d",    m + 1);
16320             char *row_o    = cpl_sprintf("row_%d",    m + 1);
16321 
16322             cpl_table_erase_column(slitss[i], object_o);
16323             cpl_table_erase_column(slitss[i], start_o);
16324             cpl_table_erase_column(slitss[i], end_o);
16325             cpl_table_erase_column(slitss[i], row_o);
16326         }
16327     }
16328 
16329     /* 
16330      * Now just consider all the objects in the intersection table.
16331      */
16332 
16333     for (k = 0; k < nslits; k++) {
16334         for (j = 0; j < maxobjs; j++) {
16335             double object_w, object_r;
16336             int    start_w, start_r;
16337             int    end_w, end_r;
16338             int    row_w, row_r;
16339 
16340             char  *object_i = cpl_sprintf("object_%d", j + 1);
16341             char  *start_i  = cpl_sprintf("start_%d",  j + 1);
16342             char  *end_i    = cpl_sprintf("end_%d",    j + 1);
16343             char  *row_i    = cpl_sprintf("row_%d",    j + 1);
16344 
16345 
16346             if (!cpl_table_is_valid(origslits, object_i, k))
16347                 break;
16348 
16349             /* 
16350              * We have found a valid object (valid because it belongs
16351              * to the intersection). Now we look for this object in each
16352              * one of the original tables, we get its parameters, and
16353              * copy them at the right position (i.e., same position as
16354              * in intersection table). The object will be the one closest
16355              * to the object position (column object_i) in the intersection
16356              * table. Note that we examine the same row, k, in all tables.
16357              */
16358 
16359             object_w = cpl_table_get_double(origslits, object_i, k, NULL);
16360             start_w  = cpl_table_get_int   (origslits, start_i,  k, NULL);
16361             end_w    = cpl_table_get_int   (origslits, end_i,    k, NULL);
16362             row_w    = cpl_table_get_int   (origslits, row_i,    k, NULL);
16363 
16364             for (i = 0; i < nscience; i++) {
16365                 int        c_maxobjs = mos_get_maxobjs_per_slit(work[i]);
16366                 int        minpos;
16367                 double     mindiff, diff;
16368                 char      *object_o;
16369                 char      *start_o;
16370                 char      *end_o;
16371                 char      *row_o;
16372 
16373                 for (m = 0; m < c_maxobjs; m++) {
16374                     object_o = cpl_sprintf("object_%d", m + 1);
16375                     start_o  = cpl_sprintf("start_%d",  m + 1);
16376                     end_o    = cpl_sprintf("end_%d",    m + 1);
16377                     row_o    = cpl_sprintf("row_%d",    m + 1);
16378 
16379                     if (!cpl_table_is_valid(work[i], object_o, k))
16380                         break;
16381 
16382                     object_r = cpl_table_get_double(work[i], object_o, k, NULL);
16383                     start_r  = cpl_table_get_int   (work[i], start_o,  k, NULL);
16384                     end_r    = cpl_table_get_int   (work[i], end_o,    k, NULL);
16385                     row_r    = cpl_table_get_int   (work[i], row_o,    k, NULL);
16386 
16387                     diff = fabs(object_w - object_r);
16388                     if (m) {
16389                         if (mindiff > diff) {
16390                             mindiff = diff;
16391                             minpos = m;
16392                         }
16393                     }
16394                     else {
16395                         mindiff = diff;
16396                         minpos = 0;
16397                     }
16398 
16399                     cpl_free(object_o);
16400                     cpl_free(start_o);
16401                     cpl_free(end_o);
16402                     cpl_free(row_o);
16403                 }
16404 
16405                 object_o = cpl_sprintf("object_%d", minpos + 1);
16406                 start_o  = cpl_sprintf("start_%d",  minpos + 1);
16407                 end_o    = cpl_sprintf("end_%d",    minpos + 1);
16408                 row_o    = cpl_sprintf("row_%d",    minpos + 1);
16409 
16410                 if (!cpl_table_has_column(slitss[i], object_i)) {
16411                     cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE);
16412                     cpl_table_new_column(slitss[i], start_i,  CPL_TYPE_INT);
16413                     cpl_table_new_column(slitss[i], end_i,    CPL_TYPE_INT);
16414                     cpl_table_new_column(slitss[i], row_i,    CPL_TYPE_INT);
16415                     cpl_table_fill_invalid_double(slitss[i], object_i, -1);
16416                     cpl_table_fill_invalid_int   (slitss[i], start_i,  -1);
16417                     cpl_table_fill_invalid_int   (slitss[i], end_i,    -1);
16418                     cpl_table_fill_invalid_int   (slitss[i], row_i,    -1);
16419                 }
16420 
16421                 cpl_table_set_double(slitss[i], object_i, k,
16422                                      cpl_table_get_double(work[i], object_o, 
16423                                                           k, NULL));
16424                 cpl_table_set_int(slitss[i], start_i , k,
16425                                   cpl_table_get_int(work[i], start_o, k, NULL));
16426                 cpl_table_set_int(slitss[i], end_i , k,
16427                                   cpl_table_get_int(work[i], end_o, k, NULL));
16428                 cpl_table_set_int(slitss[i], row_i , k, row_w);
16429 
16430                 cpl_free(object_o);
16431                 cpl_free(start_o);
16432                 cpl_free(end_o);
16433                 cpl_free(row_o);
16434             }
16435 
16436             cpl_free(object_i);
16437             cpl_free(start_i);
16438             cpl_free(end_i);
16439             cpl_free(row_i);
16440         }
16441     }
16442 
16443     for (i = 0; i < nscience; i++)
16444         cpl_table_delete(work[i]);
16445 
16446     cpl_free(work);
16447 
16448 
16449     return cpl_error_get_code();
16450 }
16451 
16452 
16460 int mos_get_maxobjs_per_slit(cpl_table * slits)
16461 {
16462     int maxobjs = 1;
16463 
16464     char * colname = cpl_sprintf("object_%d", maxobjs);
16465     
16466     while (cpl_table_has_column(slits, colname)) {
16467         maxobjs++;
16468         cpl_free(colname);
16469         colname = cpl_sprintf("object_%d", maxobjs);
16470     }
16471     
16472     cpl_free(colname);
16473 
16474     maxobjs--;
16475 
16476     return maxobjs;
16477 }
16478 
16486 int mos_get_nobjects(cpl_table * slits)
16487 {
16488     int nobjs = 0;
16489 
16490     int nslits  = cpl_table_get_nrow(slits);
16491     int maxobjs = mos_get_maxobjs_per_slit(slits);
16492 
16493     int k, m;
16494 
16495     for (k = 0; k < nslits; k++) {
16496         for (m = 0; m < maxobjs; m++) {
16497             char * name = cpl_sprintf("object_%d", m + 1);
16498             int    null = !cpl_table_is_valid(slits, name, k);
16499 
16500             cpl_free(name);
16501 
16502             if (null)  break;
16503             else nobjs++;
16504         }
16505     }
16506 
16507     return nobjs;
16508 }
16509 
16517 int mos_check_slits(cpl_table *slits, float rescale)
16518 {
16519 
16520     cpl_propertylist *sort;
16521 
16522     int nslits  = cpl_table_get_nrow(slits);
16523 
16524     int k, null;
16525 
16526     const float interval = 90.0 * rescale;
16527     const float offset   = (90.0 - 5) * rescale;
16528 
16529 
16530     for (k = 0; k < nslits; k++) {
16531         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16532         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16533 
16534         double xtop    = cpl_table_get_double(slits, "xtop",    k, &null);
16535         double xbottom = cpl_table_get_double(slits, "xbottom", k, &null);
16536 
16537         int nmiss = (int)((ytop - ybottom) / interval + 0.5);
16538 
16539         if (nmiss > 1) {
16540             cpl_msg_warning(cpl_func, 
16541                             "Some slits could not be properly detected. "
16542                             "There might be accountable inaccuracies.");
16543             while (nmiss > 1) {
16544                 cpl_table_set_size(slits, nslits + 1);
16545 
16546                 /* Fill in new slit 'cut' */
16547 
16548                 /* x coordinates be the same (acceptable approximation) */
16549                 cpl_table_set_double(slits, "xtop",    nslits, xtop);
16550                 cpl_table_set_double(slits, "xbottom", nslits, xbottom);
16551 
16552                 /* Cut */
16553                 if (k == 0) {
16554                     cpl_table_set_double(slits, "ybottom", nslits, ybottom); 
16555                     cpl_table_set_double(slits, "ytop",    nslits, ybottom
16556                                                                    + offset);
16557                     ybottom += interval;
16558                     cpl_table_set_double(slits, "ybottom", k,      ybottom);
16559                 } else {
16560                     cpl_table_set_double(slits, "ytop",    nslits, ytop);
16561                     cpl_table_set_double(slits, "ybottom", nslits, ytop 
16562                                                                    - offset);
16563                     ytop -= interval;
16564                     cpl_table_set_double(slits, "ytop",     k,     ytop);
16565                 }
16566 
16567                 nslits++; nmiss--;
16568             }
16569         }
16570     }
16571 
16572     sort = cpl_propertylist_new();
16573     cpl_propertylist_append_bool(sort, "ytop", 1);
16574     cpl_table_sort(slits, sort);
16575     cpl_propertylist_delete(sort);
16576 
16577     /*
16578      * Add here an ad hoc check on the last slit: is it too long 
16579      * (by more than 10%)? Then shorten it...
16580      */
16581 
16582     k = cpl_table_get_nrow(slits) - 1;
16583 
16584     {
16585         double ytop    = cpl_table_get_double(slits, "ytop",    k, &null);
16586         double ybottom = cpl_table_get_double(slits, "ybottom", k, &null);
16587         double length  = (ytop - ybottom) / interval;
16588 
16589         if (length > 1.1) {
16590             cpl_table_set_double(slits, "ybottom", k, ytop - offset);
16591         }
16592   
16593     }
16594 
16595     return 0;
16596 }
16597 
16620 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header)
16621 {
16622     int m, null;
16623     int halfsize;
16624 
16625     cpl_propertylist * sort;
16626     cpl_table        * slits; 
16627 
16628     slits    = mos_load_slits_fors_mos(header);
16629     halfsize = cpl_table_get_nrow(slits);
16630 
16631     cpl_table_set_size(slits, 2 * halfsize);
16632 
16633     for (m = 0; m < halfsize; m++) {
16634 
16635         double gap = 1.4;
16636 
16637         double length = 
16638             cpl_table_get(slits, "ytop",    m, &null) -
16639             cpl_table_get(slits, "ybottom", m, &null);
16640 
16641         if (m) {
16642             double interval = 
16643                 cpl_table_get(slits, "ybottom", m - 1, &null) -
16644                 cpl_table_get(slits, "ytop",    m,     &null);
16645 
16646             gap = (interval - length) / 2;
16647         }
16648 
16649         cpl_table_set(slits, "slit_id", m + halfsize,
16650                       cpl_table_get(slits, "slit_id", m, &null) - 1);
16651 
16652         cpl_table_set(slits, "xtop",    m + halfsize,
16653                       cpl_table_get(slits, "xtop",    m, &null));
16654 
16655         cpl_table_set(slits, "xbottom", m + halfsize,
16656                       cpl_table_get(slits, "xbottom", m, &null));
16657 
16658         cpl_table_set(slits, "ytop",    m + halfsize, 
16659                       cpl_table_get(slits, "ytop", m, &null) + gap + length);
16660 
16661         cpl_table_set(slits, "ybottom", m + halfsize,
16662                       cpl_table_get(slits, "ytop", m, &null) + gap);
16663     }
16664 
16665     for (m = 0; m < 2 * halfsize; m++) {
16666         cpl_table_set(slits, "ytop",    m, 
16667                       cpl_table_get(slits, "ytop",    m, &null) - 5.3);
16668 
16669         cpl_table_set(slits, "ybottom", m,
16670                       cpl_table_get(slits, "ybottom", m, &null) - 5.3);
16671 
16672     }
16673 
16674     sort = cpl_propertylist_new();
16675     cpl_propertylist_append_bool(sort, "ytop", 1);
16676     cpl_table_sort(slits, sort);
16677 
16678     cpl_propertylist_delete(sort);
16679 
16680     return slits;
16681 }
16682 
16683 int * fors_get_nobjs_perslit(cpl_table * slits)
16684 {
16685     int nslits  = cpl_table_get_nrow(slits);
16686     int maxobjs = mos_get_maxobjs_per_slit(slits);
16687 
16688     int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits);
16689 
16690     int k, m;
16691 
16692     for (k = 0; k < nslits; k++) {
16693         int nobjs = 0;
16694         for (m = 0; m < maxobjs; m++) {
16695             char * name = cpl_sprintf("object_%d", m + 1);
16696             int    null = !cpl_table_is_valid(slits, name, k);
16697 
16698             cpl_free(name);
16699 
16700             if (null)  break;
16701             else nobjs++;
16702         }
16703         
16704         nobjs_per_slit[k] = nobjs;
16705     }
16706 
16707     return nobjs_per_slit;
16708 }
16709 
16710 double fors_get_object_position(cpl_table *slits, int slit, int object)
16711 {
16712     char   *name = cpl_sprintf("object_%d", object);
16713     double  position;
16714 
16715     position = cpl_table_get_double(slits, name, slit, NULL)
16716              - cpl_table_get_int(slits, "position", slit, NULL);
16717 
16718     cpl_free(name);
16719 
16720     return position;
16721 }
16722 
16723 int mos_rebin_signal(cpl_image **image, int rebin)
16724 {
16725     cpl_image *rebinned;
16726 
16727 
16728     if (*image == NULL)
16729         return 1;
16730 
16731     if (rebin == 1)
16732         return 0;
16733 
16734     rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1);
16735 
16736     cpl_image_delete(*image);
16737 
16738     *image = rebinned;
16739 
16740     return 0;
16741 }
16742 
16743 int mos_rebin_error(cpl_image **image, int rebin)
16744 {
16745     if (*image == NULL)
16746         return 1;
16747 
16748     if (rebin == 1)
16749         return 0;
16750 
16751     cpl_image_power(*image, 2);
16752     mos_rebin_signal(image, rebin);
16753     cpl_image_power(*image, 0.5);
16754 
16755     return 0;
16756 }
16757 
16758 /*
16759  * @brief
16760  *   Map table values into a 1D image
16761  *  
16762  * @param image       Target image
16763  * @param start       Coordinate of first pixel in image
16764  * @param step        Coordinate step for one pixel in image
16765  * @param table       Source table
16766  * @param xname       Name of coordinate column
16767  * @param yname       Name of values column
16768  *
16769  * @return 0 on success
16770  *
16771  * The values in @em yname are linearly interpolated at the @em image 
16772  * pixel coordinates. The @em image must have Nx1 size.
16773  */
16774 
16775 int map_table(cpl_image *image, double start, double step,
16776               cpl_table *table, char *xname, char *yname)
16777 {
16778     int      length = cpl_image_get_size_x(image);
16779     int      nrows  = cpl_table_get_nrow(table);
16780     float   *data   = cpl_image_get_data_float(image);
16781     float   *fdata  = NULL;
16782     double  *xdata  = NULL;
16783     double  *ydata  = NULL;
16784     cpl_type xtype  = cpl_table_get_column_type(table, xname);
16785     cpl_type ytype  = cpl_table_get_column_type(table, yname);
16786     double   xzero, pos;
16787     int      i, j, n;
16788 
16789 
16790     /*
16791      * Initialization of output image at 0.0 - this value is left 
16792      * on non-overlapping portions.
16793      */
16794 
16795     for (i = 0; i < length; i++)
16796         data[i] = 0.0;
16797 
16798 
16799     /*
16800      * Do everything in double precision
16801      */
16802 
16803     if (xtype == CPL_TYPE_FLOAT) {
16804         fdata = cpl_table_get_data_float(table, xname);
16805         xdata = cpl_malloc(nrows * sizeof(double));
16806         for (i = 0; i < nrows; i++) {
16807            xdata[i] = fdata[i];
16808         }
16809     }
16810     else {
16811         xdata = cpl_table_get_data_double(table, xname);
16812     }
16813 
16814     if (ytype == CPL_TYPE_FLOAT) {
16815         fdata = cpl_table_get_data_float(table, yname);
16816         ydata = cpl_malloc(nrows * sizeof(double));
16817         for (i = 0; i < nrows; i++) {
16818            ydata[i] = fdata[i];
16819         }
16820     }
16821     else {
16822         ydata = cpl_table_get_data_double(table, yname);
16823     }
16824 
16825     /*
16826      * Mapping
16827      */
16828 
16829     n = 0;
16830     xzero = xdata[n];
16831 
16832     for (i = 0; i < length; i++) {
16833         pos = start + step * i;
16834         if (pos < xzero)
16835             continue;
16836         for (j = n; j < nrows; j++) {
16837             if (xdata[j] > pos) {
16838                 n = j;
16839                 data[i] = ydata[j-1]
16840                         + (ydata[j] - ydata[j-1])
16841                         * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]);
16842                 break;
16843             }
16844         }
16845     }
16846 
16847     if (xtype == CPL_TYPE_FLOAT)
16848         cpl_free(xdata);
16849 
16850     if (ytype == CPL_TYPE_FLOAT)
16851         cpl_free(ydata);
16852 
16853     return 0;
16854 }
16855 
16856 
16857 /*
16858  * @brief
16859  *   Fit overall trend of a Nx1 image
16860  *  
16861  * @param image       Values to smooth
16862  * @param order       Order of fitting polynomial
16863  * @param hw          Half width of smoothing window
16864  *
16865  * @return Smoothed image, or NULL on failure.
16866  *
16867  * Heavily smooth and fit data in the input Nx1 size @em image.
16868  */
16869 
16870 static cpl_image *polysmooth(cpl_image *image, int order, int hw)
16871 {
16872     int             npoints;
16873     cpl_vector     *x;
16874     cpl_vector     *y;
16875     double         *xdata;
16876     double         *ydata;
16877     cpl_polynomial *poly;
16878     cpl_vector     *ysmooth;
16879     cpl_image      *smoothed;
16880     float          *sdata;
16881     int             i;
16882 
16883 
16884     npoints = cpl_image_get_size_x(image);
16885 
16886     if (2 * hw + 1 > npoints)
16887         return NULL;
16888 
16889     x       = cpl_vector_new(npoints);
16890     y       = cpl_vector_new(npoints);
16891     xdata   = cpl_vector_get_data(x);
16892     ydata   = cpl_vector_get_data(y);
16893 
16894     smoothed = cpl_image_duplicate(image);
16895     sdata = cpl_image_get_data_float(smoothed);
16896 
16897     for (i = 0; i < npoints; i++) {
16898         xdata[i] = i;
16899         ydata[i] = sdata[i];
16900     }
16901 
16902     ysmooth = cpl_vector_filter_median_create(y, hw);
16903     cpl_vector_delete(y);
16904 
16905     poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL);
16906     cpl_vector_delete(x);
16907     cpl_vector_delete(ysmooth);
16908 
16909     if (poly) {
16910         for (i = 0; i < npoints; i++)
16911             sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL);
16912     
16913         cpl_polynomial_delete(poly);
16914     }
16915     else {
16916         cpl_image_delete(smoothed);
16917         return NULL;
16918     }
16919 
16920     return smoothed;
16921 }
16922 
16923 #undef cleanup
16924 #define cleanup                       \
16925 do {                                  \
16926     cpl_image_delete(spectrum);       \
16927     cpl_image_delete(flux);           \
16928     cpl_image_delete(efficiency);     \
16929     cpl_image_delete(smo_efficiency); \
16930     cpl_image_delete(extinction);     \
16931     cpl_image_delete(response);       \
16932     cpl_image_delete(smo_response);   \
16933     cpl_image_delete(physical);       \
16934 } while (0)
16935 
16959 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 
16960                                  double dispersion, double gain,
16961                                  double exptime, cpl_table *ext_table,
16962                                  double airmass, cpl_table *flux_table,
16963                                  int order)
16964 {
16965 
16966     cpl_image *spectrum       = NULL; // Extracted standard star spectrum
16967     float     *data;
16968     cpl_image *extinction     = NULL; // Extinction binned as "spectrum"
16969     float     *ext_data;
16970     cpl_image *flux           = NULL; // Standard star flux binned as "spectrum"
16971     float     *flux_data;
16972     cpl_image *physical       = NULL; // Physical units of above
16973     float     *phys_data;
16974     cpl_image *efficiency     = NULL; // Raw efficiency curve
16975     float     *eff_data;
16976     cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve
16977     float     *smo_eff_data;
16978     cpl_image *response       = NULL; // Raw response curve
16979     float     *res_data;
16980     cpl_image *smo_response   = NULL; // Smoothed response curve
16981     float     *smo_res_data;
16982     cpl_image *image;
16983     cpl_image *smo_image;
16984     cpl_table *table;
16985     float      lambda;
16986     int        nx, ny;
16987     int        ext_count, ext_pos;
16988     int        eff_count, eff_pos;
16989     int        flux_count, flux_pos;
16990     int        start, end;
16991     int        i;
16992 
16993 
16994     if (spectra == NULL || ext_table == NULL || flux_table == NULL) {
16995         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
16996         return NULL;
16997     }
16998 
16999     if (!cpl_table_has_column(ext_table, "WAVE")) {
17000         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17001                               "Column WAVE in atmospheric extinction table");
17002         return NULL;
17003     }
17004 
17005     if (!cpl_table_has_column(ext_table, "EXTINCTION")) {
17006         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17007                         "Column EXTINCTION in atmospheric extinction table");
17008         return NULL;
17009     }
17010 
17011     if (!cpl_table_has_column(flux_table, "WAVE")) {
17012         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17013                               "Column WAVE in standard star flux table");
17014         return NULL;
17015     }
17016 
17017     if (!cpl_table_has_column(flux_table, "FLUX")) {
17018         cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
17019                               "Column FLUX in standard star flux table");
17020         return NULL;
17021     }
17022 
17023     if (gain < 0.1) {
17024         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17025                               "Invalid gain factor (%.2f)", gain);
17026         return NULL;
17027     }
17028 
17029     if (exptime < 0.001) {
17030         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17031                               "Invalid exposure time (%.2f)", exptime);
17032         return NULL;
17033     }
17034 
17035     if (dispersion < 0.001) {
17036         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17037                               "Invalid dispersion (%.2f)", dispersion);
17038         return NULL;
17039     }
17040 
17041     if (order < 2) {
17042         cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
17043                               "Order of the polynomial fitting the "
17044                               "instrument response must be at least 2");
17045         return NULL;
17046     }
17047 
17048     nx = cpl_image_get_size_x(spectra);
17049     ny = cpl_image_get_size_y(spectra);
17050 
17051 
17052     /*
17053      * Find brightest spectrum and duplicate it.
17054      */
17055 
17056     if (ny == 1) {
17057         spectrum = cpl_image_duplicate(spectra);
17058     }
17059     else {
17060         int        x, y;
17061         cpl_image *brights = cpl_image_collapse_create(spectra, 1);
17062 
17063         cpl_image_get_maxpos(brights, &x, &y);
17064         cpl_image_delete(brights);
17065         spectrum = cpl_image_extract(spectra, 1, y, nx, y);
17066     }
17067 
17068 
17069     /*
17070      * Convert standard star spectrum in electrons per second per Angstrom.
17071      */
17072 
17073     cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion);
17074 
17075 
17076     /*
17077      * Map the atmospheric extinction factors to the same lambda sampling
17078      * of the extracted spectrum.
17079      */
17080 
17081     extinction = cpl_image_duplicate(spectrum);
17082     map_table(extinction, startwave + dispersion/2, dispersion, 
17083               ext_table, "WAVE", "EXTINCTION");
17084 
17085 
17086     /*
17087      * Convert from magnitudes to actual flux loss.
17088      */
17089 
17090     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17091     cpl_image_exponential(extinction, 10.);
17092 
17093 
17094     /*
17095      * Correct the scientific spectrum to airmass 0
17096      */
17097 
17098     cpl_image_multiply(spectrum, extinction);
17099 
17100 
17101     /*
17102      * Find in what pixel interval (start at "ext_pos", for "ext_count" 
17103      * pixels) the atmospheric extinction is available.
17104      */
17105     
17106     ext_data = cpl_image_get_data_float(extinction);
17107 
17108     ext_count = 0;
17109     ext_pos = 0;
17110     for (i = 0; i < nx; i++) {
17111         if (ext_data[i] > 0.0) {
17112             if (ext_count == 0) {
17113                 ext_pos = i;
17114             }
17115             ext_count++;
17116         }
17117         else {
17118             if (ext_count) {
17119                 break;
17120             }
17121         }
17122     }
17123 
17124     cpl_image_delete(extinction); extinction = NULL;
17125 
17126 
17127     /*
17128      * Map the standard star catalog flux to the same lambda sampling
17129      * of the extracted spectrum.
17130      */
17131 
17132     flux = cpl_image_duplicate(spectrum);
17133     map_table(flux, startwave + dispersion/2, dispersion, 
17134               flux_table, "WAVE", "FLUX");
17135 
17136 
17137     /*
17138      * Find in what pixel interval (start at "flux_pos", for "flux_count" 
17139      * pixels) the standard star flux is available.
17140      */
17141     
17142     flux_data = cpl_image_get_data_float(flux);
17143 
17144     flux_count = 0;
17145     flux_pos = 0;
17146     for (i = 0; i < nx; i++) {
17147         if (flux_data[i] > 0.0) {
17148             if (flux_count == 0) {
17149                 flux_pos = i;
17150             }
17151             flux_count++;
17152         }
17153         else {
17154             if (flux_count) {
17155                 break;
17156             }
17157         }
17158     }
17159 
17160 
17161     /*
17162      * Intersection with previous selection
17163      */
17164 
17165     start      = ext_pos > flux_pos ? ext_pos : flux_pos;
17166     end        = (ext_pos + ext_count) < (flux_pos + flux_count) ?
17167                  (ext_pos + ext_count) : (flux_pos + flux_count);
17168     flux_pos   = start;
17169     flux_count = end - start;
17170 
17171 
17172     /*
17173      * Convert the flux to photons (per second per Angstrom).
17174      * std_flux is in units of erg / cm^2 / s / Angstrom. This
17175      * must be multiplied by the efficient area of the telescope,
17176      * 5.18E+5 cm^2, and divided by hv (v = frequency). With 
17177      * hc = 1.98E-8 erg*Angstrom one obtains the following:
17178      */
17179 
17180     physical = cpl_image_duplicate(spectrum);
17181     phys_data = cpl_image_get_data_float(physical);
17182 
17183     for (i = 0; i < nx; i++) {
17184         lambda = startwave + dispersion * (i + 0.5);
17185         phys_data[i] = 0.0026 * lambda * flux_data[i];
17186     }
17187 
17188     efficiency = cpl_image_duplicate(spectrum);
17189     eff_data = cpl_image_get_data_float(efficiency);
17190     data = cpl_image_get_data_float(spectrum);
17191 
17192     for (i = 0; i < nx; i++) {
17193         if (phys_data[i] > 0.0)
17194             eff_data[i] = data[i] / phys_data[i];
17195         else
17196             eff_data[i] = 0.0;
17197     }
17198 
17199     cpl_image_delete(physical); physical = NULL;
17200 
17201 
17202     /*
17203      * Find interval (longer than 300 pixels) where efficiency is 
17204      * greater than 1%
17205      */
17206 
17207     eff_count = 0;
17208     eff_pos = 0;
17209     for (i = 0; i < nx; i++) {
17210         if (eff_data[i] > 0.01) {
17211             if (eff_count == 0) {
17212                 eff_pos = i; 
17213             }
17214             eff_count++;
17215         }
17216         else {
17217             if (eff_count > 300) {
17218                 break;
17219             }
17220         }
17221     }
17222 
17223 
17224     /*
17225      * Intersection with previous selection
17226      */
17227 
17228     start      = eff_pos > flux_pos ? eff_pos : flux_pos;
17229     end        = (eff_pos + eff_count) < (flux_pos + flux_count) ?
17230                  (eff_pos + eff_count) : (flux_pos + flux_count);
17231     eff_pos    = start;
17232     eff_count  = end - start;
17233 
17234     if (eff_count < 1) {
17235         cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
17236                               "No overlap between catalog and spectrum");
17237         cleanup;
17238         return NULL;
17239     }
17240 
17241 
17242     /*
17243      * Extract only data to fit, i.e., where the efficiency is available.
17244      */
17245 
17246     image = cpl_image_extract(efficiency, eff_pos + 1, 1, 
17247                               eff_pos + eff_count, 1);
17248 
17249     smo_image = polysmooth(image, order, 50);
17250     cpl_image_delete(image);
17251 
17252     smo_efficiency = cpl_image_duplicate(efficiency);
17253     smo_eff_data = cpl_image_get_data_float(smo_efficiency);
17254     cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1);
17255 
17256     cpl_image_delete(smo_image);
17257 
17258 
17259     /*
17260      * Compute instrument response as the ratio between the catalog
17261      * spectrum and the observed spectrum (converted in physical units).
17262      * The polynomial smoothing, however, is performed on the inverse
17263      * of this ration, for obvious reasons (i.e., no divergence at zero
17264      * efficiency).
17265      */
17266 
17267     response = cpl_image_duplicate(spectrum);
17268     res_data = cpl_image_get_data_float(response);
17269 
17270     for (i = 0; i < nx; i++) {
17271         if (eff_data[i] > 0.01 && flux_data[i] > 0.0)
17272             res_data[i] = data[i] / flux_data[i];
17273         else
17274             res_data[i] = 0.0;
17275     }
17276 
17277 
17278     /*
17279      * Extract only data to fit, i.e., where the response is available.
17280      */
17281 
17282     image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1);
17283 
17284     smo_image = polysmooth(image, order, 50);
17285     cpl_image_delete(image);
17286 
17287     smo_response = cpl_image_duplicate(response);
17288     smo_res_data = cpl_image_get_data_float(smo_response);
17289     cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1);
17290 
17291     cpl_image_delete(smo_image);
17292 
17293     for (i = 0; i < nx; i++) {
17294         if (eff_data[i] > 0.01) {
17295             res_data[i] = 1 / res_data[i];
17296             smo_res_data[i] = 1 / smo_res_data[i];
17297         }
17298         else {
17299             res_data[i] = 0.0;
17300             smo_res_data[i] = 0.0;
17301         }
17302     }
17303 
17304 
17305     /*
17306      * Assemble the product spectrophotometric table.
17307      */
17308 
17309     table = cpl_table_new(nx);
17310 
17311     cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT);
17312     cpl_table_set_column_unit(table, "WAVE", "Angstrom");
17313 
17314     for (i = 0; i < nx; i++)
17315         cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5));
17316 
17317     cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT);
17318     cpl_table_set_column_unit(table, "STD_FLUX", 
17319                               "10^(-16) erg/(cm^2 s Angstrom)");
17320     cpl_table_copy_data_float(table, "STD_FLUX", flux_data);
17321     cpl_image_delete(flux); flux = NULL;
17322 
17323     cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT);
17324     cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)");
17325     cpl_table_copy_data_float(table, "OBS_FLUX", data);
17326     cpl_image_delete(spectrum); spectrum = NULL;
17327 
17328     cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT);
17329     cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon");
17330     cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data);
17331     cpl_image_delete(efficiency); efficiency = NULL;
17332 
17333     cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT);
17334     cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon");
17335     cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data);
17336     cpl_image_delete(smo_efficiency); smo_efficiency = NULL;
17337 
17338     cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT);
17339     cpl_table_set_column_unit(table, "RAW_RESPONSE", 
17340                               "10^(-16) erg/(cm^2 electron)");
17341     cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data);
17342     cpl_image_delete(response); response = NULL;
17343 
17344     cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT);
17345     cpl_table_set_column_unit(table, 
17346                               "RESPONSE", "10^(-16) erg/(cm^2 electron)");
17347     cpl_table_copy_data_float(table, "RESPONSE", smo_res_data);
17348     cpl_image_delete(smo_response); smo_response = NULL;
17349 
17350     cleanup;
17351 
17352     return table;
17353 }
17354 
17355 static double ksigma_vector(cpl_vector *values, 
17356                             double klow, double khigh, int kiter, int *good)
17357 {
17358     cpl_vector *accepted;
17359     double  mean  = 0.0;
17360     double  sigma = 0.0;
17361     double *data  = cpl_vector_get_data(values);
17362     int     n     = cpl_vector_get_size(values);
17363     int     ngood = n;
17364     int     count = 0;
17365     int     i;
17366 
17367 
17368     /*
17369      * At first iteration the mean is taken as the median, and the
17370      * standard deviation relative to this value is computed.
17371      */
17372 
17373     mean = cpl_vector_get_median(values);
17374 
17375     for (i = 0; i < n; i++) 
17376         sigma += (mean - data[i]) * (mean - data[i]);
17377 
17378     sigma = sqrt(sigma / (n - 1));
17379 
17380     while (kiter) {
17381         count = 0;
17382         for (i = 0; i < ngood; i++) {
17383             if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) {
17384                 data[count] = data[i];
17385                 ++count;
17386             }
17387         }
17388 
17389         if (count == 0) // This cannot happen at first iteration.
17390             break;      // So we can break: we have already computed a mean.
17391 
17392         /*
17393          * The mean must be computed even if no element was rejected
17394          * (count == ngood), because at first iteration median instead 
17395          * of mean was computed.
17396          */
17397 
17398         accepted = cpl_vector_wrap(count, data);
17399         mean = cpl_vector_get_mean(accepted);
17400         if (count > 1)
17401             sigma = cpl_vector_get_stdev(accepted);
17402         cpl_vector_unwrap(accepted);
17403 
17404         if (count == ngood || count == 1)
17405             break;
17406 
17407         ngood = count;
17408         --kiter;
17409     }
17410 
17411     if (good)
17412         *good = ngood;
17413 
17414     return mean;
17415 }
17416 
17417 
17436 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 
17437                             double klow, double khigh, int kiter,
17438                             cpl_image **good)
17439 {
17440     int         ni, nx, ny, npix;
17441     cpl_image  *out_ima;
17442     float      *pout_ima;
17443     float      *good_ima;
17444     cpl_image  *image;
17445     float     **data;
17446     cpl_vector *time_line;
17447     double     *ptime_line;
17448     int         ngood;
17449     int         i, j;
17450 
17451 
17452     ni         = cpl_imagelist_get_size(imlist);
17453 
17454     image      = cpl_imagelist_get(imlist, 0);
17455     nx         = cpl_image_get_size_x(image);
17456     ny         = cpl_image_get_size_y(image);
17457     npix       = nx * ny;
17458     
17459     out_ima    = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17460     pout_ima   = cpl_image_get_data_float(out_ima);
17461 
17462     if (good) {
17463         *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
17464         good_ima = cpl_image_get_data_float(*good);
17465     }
17466 
17467     time_line  = cpl_vector_new(ni);
17468     ptime_line = cpl_vector_get_data(time_line);
17469 
17470     data = cpl_calloc(sizeof(float *), ni);
17471     
17472     for (i = 0; i < ni; i++) {
17473         image = cpl_imagelist_get(imlist, i);
17474         data[i] = cpl_image_get_data_float(image);
17475     }
17476 
17477     for (i = 0; i < npix; i++) {
17478         for (j = 0; j < ni; j++) {
17479             ptime_line[j] = data[j][i];
17480         }
17481         pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood);
17482         if (good) {
17483             good_ima[i] = ngood;
17484         }
17485     }
17486 
17487     cpl_free(data);
17488     cpl_vector_delete(time_line);
17489 
17490     return out_ima;
17491 
17492 }
17493 
17494 
17511 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response,
17512                                 cpl_table *ext_table, double startwave,
17513                                 double dispersion, double gain,
17514                                 double exptime, double airmass)
17515 {
17516     cpl_image *extinction;
17517     cpl_image *outspectra;
17518     cpl_image *mapresponse;
17519     float     *res_data;
17520     float     *out_data;
17521     float     *ext_data;
17522     int        tlength, xlength, ylength;
17523     int        i, j, k;
17524 
17525 
17526     if (spectra == NULL || ext_table == NULL || response == NULL) {
17527         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17528         return NULL;
17529     }
17530 
17531     res_data = cpl_table_get_data_float(response, "RESPONSE");
17532 
17533     if (res_data == NULL) {
17534         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17535         return NULL;
17536     }
17537 
17538     tlength = cpl_table_get_nrow(response);
17539     xlength = cpl_image_get_size_x(spectra);
17540     ylength = cpl_image_get_size_y(spectra);
17541 
17542     if (xlength != tlength) {
17543         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17544         map_table(mapresponse, startwave + dispersion/2, dispersion,
17545                   response, "WAVE", "RESPONSE");
17546         res_data = cpl_image_get_data_float(mapresponse);
17547     }
17548 
17549     /*
17550      * Map the atmospheric extinction factors to the same lambda sampling
17551      * of the extracted spectrum.
17552      */
17553 
17554     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17555     map_table(extinction, startwave + dispersion/2, dispersion,
17556               ext_table, "WAVE", "EXTINCTION");
17557 
17558 
17559     /*
17560      * Convert from magnitudes to actual flux loss.
17561      */
17562 
17563     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17564     cpl_image_exponential(extinction, 10.);
17565 
17566     outspectra = cpl_image_duplicate(spectra);
17567 
17568     ext_data = cpl_image_get_data_float(extinction);
17569     out_data = cpl_image_get_data_float(outspectra);
17570 
17571     for (k = 0, i = 0; i < ylength; i++) {
17572         for (j = 0; j < xlength; j++, k++) {
17573             out_data[k] *= ext_data[j] * res_data[j];
17574         }
17575     }
17576 
17577     cpl_image_delete(extinction);
17578     if (xlength != tlength) {
17579         cpl_image_delete(mapresponse);
17580     }
17581 
17582     cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion);
17583 
17584     return outspectra;
17585 }
17586 
17587 
17604 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 
17605                                           cpl_image *errors, 
17606                                           cpl_table *response,
17607                                           cpl_table *ext_table, 
17608                                           double startwave,
17609                                           double dispersion, double gain,
17610                                           double exptime, double airmass)
17611 {
17612     cpl_image *extinction;
17613     cpl_image *outerrors;
17614     cpl_image *mapresponse;
17615     cpl_image *maperror;
17616     float     *err_data;
17617     float     *out_data;
17618     float     *ext_data;
17619     float     *res_data;
17620     float     *spe_data;
17621     int        tlength, xlength, ylength;
17622     int        i, j, k;
17623 
17624 
17625     if (errors == NULL || ext_table == NULL || response == NULL) {
17626         cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT);
17627         return NULL;
17628     }
17629 
17630     if (!cpl_table_has_column(response, "ERROR")) {
17631         return mos_apply_photometry(errors, response, ext_table, startwave,
17632                                     dispersion, gain, exptime, airmass);
17633     }
17634 
17635     res_data = cpl_table_get_data_float(response, "RESPONSE");
17636 
17637     if (res_data == NULL) {
17638         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17639         return NULL;
17640     }
17641 
17642     err_data = cpl_table_get_data_float(response, "ERROR");
17643 
17644     if (err_data == NULL) {
17645         cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
17646         return NULL;
17647     }
17648 
17649     tlength = cpl_table_get_nrow(response);
17650     xlength = cpl_image_get_size_x(errors);
17651     ylength = cpl_image_get_size_y(errors);
17652 
17653     if (xlength != tlength) {
17654         mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17655         map_table(mapresponse, startwave + dispersion/2, dispersion,
17656                   response, "WAVE", "RESPONSE");
17657         res_data = cpl_image_get_data_float(mapresponse);
17658 
17659         maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17660         map_table(maperror, startwave + dispersion/2, dispersion,
17661                   response, "WAVE", "ERROR");
17662         err_data = cpl_image_get_data_float(maperror);
17663     }
17664 
17665     /*
17666      * Map the atmospheric extinction factors to the same lambda sampling
17667      * of the extracted spectrum.
17668      */
17669 
17670     extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT);
17671     map_table(extinction, startwave + dispersion/2, dispersion,
17672               ext_table, "WAVE", "EXTINCTION");
17673 
17674 
17675     /*
17676      * Convert from magnitudes to actual flux loss.
17677      */
17678 
17679     cpl_image_multiply_scalar(extinction, 0.4 * airmass);
17680     cpl_image_exponential(extinction, 10.);
17681 
17682     outerrors = cpl_image_duplicate(errors);
17683 
17684     ext_data = cpl_image_get_data_float(extinction);
17685     out_data = cpl_image_get_data_float(outerrors);
17686     spe_data = cpl_image_get_data_float(spectra);
17687 
17688     for (k = 0, i = 0; i < ylength; i++) {
17689         for (j = 0; j < xlength; j++, k++) {
17690             out_data[k] = ext_data[j] * 
17691               sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] +
17692                    res_data[j] * res_data[j] * out_data[k] * out_data[k]);
17693         }
17694     }
17695 
17696     cpl_image_delete(extinction);
17697     if (xlength != tlength) {
17698         cpl_image_delete(maperror);
17699     }
17700 
17701     cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion);
17702 
17703     return outerrors;
17704 }
17705 
17706 
17782 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error,
17783                            cpl_image *u_image, cpl_image *u_error,
17784                            double startwave, double dispersion,
17785                            double band, cpl_table *pol_sta,
17786                            double ra, double dec, char *filter, 
17787                            int *polarisation,
17788                            double *p_offset, double *p_error,
17789                            double *a_offset, double *a_error)
17790 {
17791     cpl_table *standard;
17792     cpl_image *q_noise;
17793     cpl_image *q_signal;
17794     cpl_image *u_noise;
17795     cpl_image *u_signal;
17796     cpl_image *noise;
17797     double    *q_ndata;
17798     double    *q_sdata;
17799     double    *u_ndata;
17800     double    *u_sdata;
17801     double     arctol = 0.5;  /* Arc tolerance in degrees */
17802     double     mindist;
17803     double     cwave;
17804     double     bwave[] = {3650., 4450., 5510., 6580., 8060};
17805     char      *bands = "UBVRI";
17806     char       p_label[] = {' ', 'p', '\0'};
17807     char       dp_label[] = {' ', 'd', 'p', '\0'};
17808     char       a_label[] = {' ', 'a', '\0'};
17809     char       da_label[] = {' ', 'd', 'a', '\0'};
17810     int        nbands = strlen(bands);
17811     int        selected;
17812     int        first, last, count, center;
17813     int        nx, col, row;
17814     int        i, found, closest;
17815     int        pband;
17816     int        polarised;
17817     double     q_obs;
17818     double     q_err;
17819     double     u_obs;
17820     double     u_err;
17821     double     p_obs;
17822     double     p_err;
17823     double     p_ref;
17824     double     dp_ref;
17825     double     a_obs;
17826     double     a_err;
17827     double     a_ref;
17828     double     da_ref;
17829 
17830 
17831     *filter       = '\0';
17832     *polarisation = 0;
17833     *p_offset     = 0.0;
17834     *p_error      = 0.0;
17835     *a_offset     = 0.0;
17836     *a_error      = 0.0;
17837 
17838     /*
17839      * Select reference standard star
17840      */
17841 
17842     cpl_table_select_all(pol_sta);
17843     cpl_table_and_selected_double(pol_sta, "RA",  CPL_GREATER_THAN, ra-arctol);
17844     cpl_table_and_selected_double(pol_sta, "RA",  CPL_LESS_THAN,    ra+arctol);
17845     cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol);
17846     selected =
17847     cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN,    dec+arctol);
17848 
17849     if (selected == 0) {
17850         cpl_msg_warning(cpl_func, "No standard star found in FOV");
17851         return 1;
17852     }
17853 
17854     if (selected > 1) {
17855         cpl_msg_warning(cpl_func, 
17856                         "Ambiguity: %d standard stars found in FOV", selected);
17857         return 1;
17858     }
17859 
17860     standard = cpl_table_extract_selected(pol_sta);
17861 
17862     cpl_msg_info(cpl_func, "Standard star: %s", 
17863                  cpl_table_get_string(standard, "name", 0));
17864 
17865     /*
17866      * Check whether the star is polarised or not
17867      */
17868 
17869     polarised = cpl_table_get_int(standard,  "polarised", 0, NULL);
17870 
17871     cpl_msg_info(cpl_func, "This star is%sexpected to be polarised",
17872                  polarised ? " " : " not ");
17873 
17874 
17875     /*
17876      * Determine the image row with the smallest median noise: this 
17877      * row is assumed to refer to the standard star.
17878      * (note: the higher the S/N ratio of the original spectra, the 
17879      * smaller the noise of the Stokes parameters Q and U).
17880      */
17881 
17882     nx = cpl_image_get_size_x(q_error);
17883 
17884     noise = cpl_image_collapse_median_create(q_error, 1, 0, 0);
17885     cpl_image_get_minpos(noise, &col, &row);
17886 
17887     cpl_image_delete(noise);
17888 
17889     if (col != 1) {
17890         cpl_table_delete(standard);
17891         cpl_msg_error(cpl_func, 
17892                       "Assertion failure!!! col = %d (it should be 1)", col);
17893         return 1;
17894     }
17895 
17896     q_signal = cpl_image_extract(q_image, 1, row, nx, row);
17897     q_noise  = cpl_image_extract(q_error, 1, row, nx, row);
17898     u_signal = cpl_image_extract(u_image, 1, row, nx, row);
17899     u_noise  = cpl_image_extract(u_error, 1, row, nx, row);
17900 
17901     q_sdata = cpl_image_get_data_double(q_signal);
17902     q_ndata = cpl_image_get_data_double(q_noise);
17903     u_sdata = cpl_image_get_data_double(u_signal);
17904     u_ndata = cpl_image_get_data_double(u_noise);
17905 
17906 
17907     /*
17908      * Determine valid interval in input images (where error is positive).
17909      */
17910 
17911     first = -1;
17912     last = nx = cpl_image_get_size_x(q_signal);
17913     for (i = 0; i < nx; i++) {
17914         if (first < 0) {
17915             if (q_ndata[i] > 0.0) {
17916                 first = i;
17917             }
17918         }
17919         else {
17920             if (q_ndata[i] <= 0.0) {
17921                 last = i - 1;
17922                 break;
17923             }
17924         }
17925     }
17926 
17927     count = last - first + 1;
17928 
17929     if (first < 0 || count < band) {
17930         cpl_table_delete(standard);
17931         cpl_image_delete(q_signal);
17932         cpl_image_delete(q_noise);
17933         cpl_image_delete(u_signal);
17934         cpl_image_delete(u_noise);
17935         cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count);
17936         return 1;
17937     }
17938 
17939     center = (first + last) / 2;              // Center of valid spectrum
17940     cwave = startwave + dispersion * center;  // Corresponding wavelength
17941 
17942 
17943     /*
17944      * Find the band UBVRI closest to the central wavelength.
17945      */
17946 
17947     found = 0;
17948     for (i = 0; i < nbands; i++) {
17949         p_label[0] = bands[i];
17950         if (cpl_table_is_valid(standard, p_label, 0)) {
17951             if (found == 0) {
17952                 found = 1;
17953                 mindist = fabs(bwave[i] - cwave);
17954                 closest = i;
17955             }
17956             else if (mindist > fabs(bwave[i] - cwave)) {
17957                 mindist = fabs(bwave[i] - cwave);
17958                 closest = i;
17959             }
17960         }
17961     }
17962 
17963     if (!found) {
17964         cpl_table_delete(standard);
17965         cpl_image_delete(q_signal);
17966         cpl_image_delete(q_noise);
17967         cpl_image_delete(u_signal);
17968         cpl_image_delete(u_noise);
17969         cpl_msg_warning(cpl_func, "No reference value available");
17970         return 1;
17971     }
17972 
17973     center = (bwave[closest] - startwave) / dispersion; // Center of band (pix)
17974     cwave  =  bwave[closest];                           // Wavelength of band
17975 
17976 
17977     /*
17978      * Check that the integration interval is entirely contained
17979      * in the valid interval, or give it up.
17980      */
17981 
17982     pband = floor(band / dispersion);  // Band width in pixels
17983 
17984     if (center - pband/2 < first || center + pband/2 > last) {
17985         cpl_table_delete(standard);
17986         cpl_image_delete(q_signal);
17987         cpl_image_delete(q_noise);
17988         cpl_image_delete(u_signal);
17989         cpl_image_delete(u_noise);
17990         cpl_msg_warning(cpl_func, "No reference value available");
17991         return 1;
17992     }
17993 
17994     first = center - pband/2;
17995     last  = center + pband/2;
17996 
17997     /*
17998      * Collect reference values. Note that if angle info is not available,
17999      * angle stuff is set automaticaly to zero.
18000      */
18001 
18002      p_label[0] = bands[closest];
18003     dp_label[0] = bands[closest];
18004      a_label[0] = bands[closest];
18005     da_label[0] = bands[closest];
18006 
18007      p_ref = cpl_table_get(standard,  p_label, 0, NULL);
18008     dp_ref = cpl_table_get(standard, dp_label, 0, NULL);
18009      a_ref = cpl_table_get(standard,  a_label, 0, NULL);
18010     da_ref = cpl_table_get(standard, da_label, 0, NULL);
18011 
18012     cpl_msg_info(cpl_func, 
18013                  "The expected polarisation is %.2f +- %.2f %%", 
18014                  p_ref, dp_ref);
18015 
18016     if (polarised) {
18017         cpl_msg_info(cpl_func, 
18018                      "The expected polarisation angle is %.2f +- %.2f degrees", 
18019                      a_ref, da_ref);
18020     }
18021 
18022     /*
18023      * Find median signal and median error.
18024      */
18025 
18026     q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1);
18027     q_err = cpl_image_get_median_window(q_error, first, 1, last, 1);
18028     u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1);
18029     u_err = cpl_image_get_median_window(u_error, first, 1, last, 1);
18030 
18031     /*
18032      * Measured linear polarisation and its error
18033      */
18034 
18035     p_obs = sqrt(q_obs * q_obs + u_obs * u_obs);
18036     p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err);
18037 
18038     /*
18039      * Measured polarisation angle
18040      */
18041 
18042     a_obs = 0.0;
18043     if (polarised) {
18044         if (fabs(q_obs) < 0.00001) {
18045             if (u_obs > 0.0) {
18046                 a_obs = 45.0;
18047             }
18048             else {
18049                 a_obs = 135.0;
18050             }
18051         }
18052         else {
18053             a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI;
18054             if (q_obs > 0.0) {
18055                 if (u_obs < 0.0) {
18056                     a_obs += 180.;
18057                 }
18058             }
18059             else {
18060                 a_obs += 90.;
18061             }
18062         }
18063     }
18064 
18065     /*
18066      * Error on polarisation angle
18067      */
18068 
18069     a_err = 0.0;
18070     if (polarised) {
18071         a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err)
18072               / (p_obs * p_obs) 
18073               * 90 / CPL_MATH_PI;
18074     }
18075 
18076     p_obs *= 100;
18077     p_err *= 100;
18078     cpl_msg_info(cpl_func, 
18079                  "The measured polarisation is %.2f +- %.2f %%", 
18080                  p_obs, p_err);
18081 
18082     if (polarised) {
18083         cpl_msg_info(cpl_func, 
18084                      "The measured polarisation angle is %.2f +- %.2f degrees", 
18085                      a_obs, a_err);
18086     }
18087 
18088     *filter       = bands[closest];
18089     *polarisation = polarised;
18090 
18091     if (polarised) {
18092         *p_offset = (p_obs - p_ref) / p_ref;
18093         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref;
18094     }
18095     else {
18096         *p_offset = p_obs - p_ref;
18097         *p_error  = sqrt(p_err * p_err + dp_ref * dp_ref);
18098     }
18099 
18100     *a_offset     = a_obs - a_ref;
18101     *a_error      = sqrt(a_err*a_err + da_ref*da_ref);
18102 
18103     return 0;
18104 
18105 }
18106 
18107 
18139 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset)
18140 {
18141     cpl_array *offsets;
18142     int        noffset;
18143     int        nslits = cpl_table_get_nrow(reference);
18144     int       *nref;
18145     int       *nobj;
18146     int        corr, maxcorr;
18147     int        shift, best_shift;
18148     int        i, j, k;
18149 
18150     cpl_error_code status = CPL_ERROR_NONE;
18151 
18152 
18153     *offset = 0.0;
18154 
18155     if (nslits != cpl_table_get_nrow(objects))
18156         return CPL_ERROR_INCOMPATIBLE_INPUT;
18157 
18158     nref = fors_get_nobjs_perslit(reference);
18159     nobj = fors_get_nobjs_perslit(objects);
18160 
18161     noffset = 0;
18162     for (i = 0; i < nslits; i++)
18163         noffset += nobj[i];
18164 
18165     if (noffset == 0) {
18166         cpl_free(nref);
18167         cpl_free(nobj);
18168         return CPL_ERROR_DATA_NOT_FOUND;
18169     }
18170 
18171     noffset = 0;
18172     for (i = 0; i < nslits; i++)
18173         noffset += nref[i];
18174 
18175     if (noffset == 0) {
18176         cpl_free(nref);
18177         cpl_free(nobj);
18178         return CPL_ERROR_DATA_NOT_FOUND;
18179     }
18180 
18181     offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE);
18182 
18183     noffset = 0;    // The real number of offsets found will be counted.
18184 
18185     for (i = 0; i < nslits; i++) {
18186         if (nref[i] > 0 && nobj[i] > 0) {
18187             double shift;
18188             int    length  = cpl_table_get_int(objects, "length", i, NULL);
18189             double ytop    = cpl_table_get_double(objects, "xtop", i, NULL);
18190             double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL);
18191             int   *aref    = cpl_calloc(length, sizeof(int));
18192             int   *aobj    = cpl_calloc(length, sizeof(int));
18193             float *pref    = cpl_calloc(nref[i], sizeof(float));
18194             float *pobj    = cpl_calloc(nobj[i], sizeof(float));
18195             
18196             for (j = 0; j < nref[i]; j++) {
18197                 pref[j] = fors_get_object_position(reference, i, j + 1);
18198                 aref[(int)pref[j]] = 1;
18199             }
18200 
18201             for (j = 0; j < nobj[i]; j++) {
18202                 pobj[j] = fors_get_object_position(objects, i, j + 1);
18203                 aobj[(int)pobj[j]] = 1;
18204             }
18205 
18206             /*
18207              * Do not consider objects at border
18208              */
18209 
18210             aref[0] = 0;
18211             aref[length - 1] = 0;
18212             aobj[0] = 0;
18213             aobj[length - 1] = 0;
18214 
18215 //for (j = 0; j < nref[i]; j++)
18216 //printf("references: %f, ", pref[j]);
18217 //printf("\n");
18218 //for (j = 0; j < nref[i]; j++)
18219 //printf("objects   : %f, ", pobj[j]);
18220 //printf("\n");
18221 //for (j = 0; j < length; j++)
18222 //printf("%d", aref[j]);
18223 //printf("\n");
18224 //for (j = 0; j < length; j++)
18225 //printf("%d", aobj[j]);
18226 //printf("\n");
18227 
18228             /*
18229              * Object matching by correlation
18230              */
18231 
18232             maxcorr = 0;
18233             best_shift = length;
18234 
18235             for (shift = length/2, j = 0; j <= length; shift--, j++) {
18236                 int rstart, ostart, count;
18237 
18238                 if (shift > 0) {
18239                    rstart = shift;
18240                    ostart = 0;
18241                    count  = length - shift;
18242                 }
18243                 else {
18244                    rstart = 0;
18245                    ostart = -shift;
18246                    count  = length + shift;
18247                 }
18248 
18249                 corr = 0;
18250                 for (k = 0; k < count; k++) {
18251                     corr += aref[rstart + k] * aobj[ostart + k];
18252                 }
18253 
18254                 if (maxcorr < corr) {
18255                     maxcorr = corr;
18256                     best_shift = shift;
18257                 }
18258             }
18259 
18260             if (best_shift == length) { // No shift found
18261 //printf("%d: No shift found\n", i);
18262                 cpl_free(aref);
18263                 cpl_free(aobj);
18264                 cpl_free(pref);
18265                 cpl_free(pobj);
18266                 continue;
18267             }
18268 //printf("%d: Integer shift found = %d\n", i, best_shift);
18269 
18270             for (j = 0; j < nref[i]; j++) {
18271                 for (k = 0; k < nobj[i]; k++) {
18272                     if (fabs(pref[j] - pobj[k] - best_shift) < 2) {
18273                        double ccd_offset = (pref[j] - pobj[k]) 
18274                                          * (ytop - ybottom)
18275                                          / length;
18276 
18277 //printf("%d: Match found: %f\n", i, ccd_offset);
18278                        /* 
18279                         * The matching object is found, store the
18280                         * corresponding offset
18281                         */
18282 
18283                        cpl_array_set(offsets, noffset, ccd_offset);
18284                        noffset++;
18285                        break;
18286                     }
18287                 }
18288             }
18289 
18290             cpl_free(aref);
18291             cpl_free(aobj);
18292             cpl_free(pref);
18293             cpl_free(pobj);
18294         }
18295 //else
18296 //printf("%d: No object found\n", i);
18297     }
18298 
18299     cpl_free(nref);
18300     cpl_free(nobj);
18301 
18302 //printf("%d offsets found in total\n", noffset);
18303     if (noffset > 0) {
18304         if (noffset % 2) {
18305             *offset = cpl_array_get_median(offsets);
18306         }
18307         else {
18308             double *a = cpl_malloc(sizeof(double) * noffset);
18309             for (i = 0; i < noffset; i++) {
18310                 a[i] = cpl_array_get_double(offsets, i, NULL);
18311             }
18312             *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) +
18313                        fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0;
18314             cpl_free(a);
18315         }
18316     }
18317     else
18318         status = CPL_ERROR_DATA_NOT_FOUND;
18319 //printf("Median offset: %f\n", *offset);
18320 
18321     cpl_array_delete(offsets);
18322 
18323     return status;
18324 
18325 }
18326 
18327 
18339 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy)
18340 {
18341     cpl_image *source;
18342     int        nx = cpl_image_get_size_x(image);
18343     int        ny = cpl_image_get_size_y(image);
18344     float     *idata;
18345     float     *sdata;
18346     int        i, j, pos;
18347     double     xpos, ypos, xfrac, yfrac;
18348     int        xint, yint;
18349 
18350 
18351     if (fabs(dx) >= nx || fabs(dy) >= ny)
18352         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18353 
18354     source = cpl_image_duplicate(image);
18355     idata = cpl_image_get_data_float(image);
18356     sdata = cpl_image_get_data_float(source);
18357 
18358     /*
18359      * Shift in y
18360      */
18361 
18362     yfrac = - dy - floor(- dy);
18363     xfrac = - dx - floor(- dx);
18364 
18365     for (pos = 0, j = 0; j < ny; j++) {
18366         ypos = j - dy;
18367         yint = floor(ypos);
18368         for (i = 0; i < nx; i++) {
18369             xpos = i - dx;
18370             xint = floor(xpos);
18371             if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) {
18372                 idata[pos] = 0.0;
18373             }
18374             else {
18375                 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac)
18376                            + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac)
18377                            + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac
18378                            + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac;
18379             }
18380             pos++;
18381         }
18382     }
18383 
18384     cpl_image_delete(source);
18385 
18386     return CPL_ERROR_NONE;
18387 }
18388 
18400 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny)
18401 {
18402 #ifdef CPL_SIZE_FORMAT
18403     cpl_size row;
18404 #else
18405     int row;
18406 #endif
18407 
18408     cpl_table_duplicate_column(slits, "x", slits, "xtop");
18409     cpl_table_add_columns(slits, "x", "xbottom");
18410     cpl_table_divide_scalar(slits, "x", 2);         // Mean x position
18411     cpl_table_subtract_scalar(slits, "x", nx/2);    // Relative to CCD center
18412     cpl_table_multiply_columns(slits, "x", "x");    // Squared
18413 
18414     cpl_table_duplicate_column(slits, "y", slits, "ytop");
18415     cpl_table_add_columns(slits, "y", "ybottom");
18416     cpl_table_divide_scalar(slits, "y", 2);         // Mean y position
18417     cpl_table_subtract_scalar(slits, "y", ny/2);    // Relative to CCD center
18418     cpl_table_multiply_columns(slits, "y", "y");    // Squared
18419 
18420     cpl_table_add_columns(slits, "x", "y");         // Distance from center
18421     cpl_table_get_column_minpos(slits, "x", &row);  // Min distance from center
18422 
18423     cpl_table_erase_column(slits, "x");
18424     cpl_table_erase_column(slits, "y");
18425 
18426     return row;
18427 }
18428 
18448 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 
18449                      double xwidth, double ywidth,
18450                      int dx, double gain, double *o_flux, double *o_err)
18451 {
18452     int    nx      = cpl_image_get_size_x(image);
18453     int    ny      = cpl_image_get_size_y(image);
18454     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18455     int    ytop    = (int)cpl_table_get(slits, "ytop", slit, NULL);
18456     int    ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL);
18457     int    dy      = ytop - ybottom;
18458     int    xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) +
18459                             cpl_table_get(slits, "xbottom", slit, NULL)) / 2);
18460     int    xleft   = xcenter - dx;
18461     int    xright  = xcenter + dx + 1;
18462     double area    = xwidth * ywidth; // squared mm
18463     int    npix    = (2*dx + 1) * dy;
18464     int    count   = 0;
18465     float *data    = cpl_image_get_data_float(image);
18466     double flux    = 0.0;
18467     double error   = 0.0;
18468     int    satur   = 60000;
18469     int    x, y;
18470 
18471 
18472     if (cpl_table_has_column(slits, "ywidth")) {
18473         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18474                 * cpl_table_get(slits, "ywidth", slit, NULL);
18475     }
18476 
18477     *o_flux = 0.0;
18478     *o_err = 0.0;
18479 
18480     if (xleft < 0)
18481         xleft = 0;
18482 
18483     if (xleft > nx)
18484         xleft = nx;
18485 
18486     if (xright < 0)
18487         xright = 0;
18488 
18489     if (xright > nx)
18490         xright = nx;
18491 
18492     if (ytop < 0)
18493         ytop = 0;
18494 
18495     if (ytop > ny)
18496         ytop = ny;
18497 
18498     if (ybottom < 0)
18499         ybottom = 0;
18500 
18501     if (ybottom > ny)
18502         ybottom = ny;
18503 
18504     count = (xright - xleft) * (ytop - ybottom);
18505 
18506     if (count == 0)
18507         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18508 
18509     count = 0;
18510 
18511     for (y = ybottom; y < ytop; y++) {
18512         for (x = xleft; x < xright; x++) {
18513             double value = data[x + y * nx];
18514             if (value < satur) {
18515                 flux += value;
18516                 count++;
18517             }
18518         }
18519     }
18520 
18521     if (count == 0)
18522         return CPL_ERROR_DIVISION_BY_ZERO;
18523 
18524     error = sqrt(flux/gain);
18525 
18526     /*
18527      * Flux correction for lost pixels
18528      */
18529 
18530     flux *= (float)npix / count;
18531     error *= (float)npix / count;
18532 
18533     flux /= area;
18534     error /= area;
18535 
18536     *o_flux = flux;
18537     *o_err = error;
18538 
18539     return CPL_ERROR_NONE;
18540 }
18541 
18542 
18565 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits,
18566                                        double xwidth, double ywidth,
18567                                        double lambda, double startwave, 
18568                                        double dispersion, int dx, double gain, 
18569                                        double *o_flux, double *o_err)
18570 {
18571     int    nx      = cpl_image_get_size_x(image);
18572     int    ny      = cpl_image_get_size_y(image);
18573     int    slit    = mos_slit_closest_to_center(slits, nx, ny);
18574     int    dy      = (int)cpl_table_get(slits, "length", slit, NULL);
18575     int    ybottom = (int)cpl_table_get(slits, "position", slit, NULL);
18576     int    ytop    = ybottom + dy;
18577     int    xcenter = (int)floor((lambda - startwave) / dispersion + 0.5);
18578     int    xleft   = xcenter - dx;
18579     int    xright  = xcenter + dx + 1;
18580     double area    = xwidth * ywidth;
18581     int    npix    = (2*dx + 1) * dy;
18582     int    count   = 0;
18583     float *data    = cpl_image_get_data_float(image);
18584     double flux    = 0.0;
18585     double error   = 0.0;
18586     int    satur   = 60000;
18587     int    x, y;
18588 
18589 
18590     if (cpl_table_has_column(slits, "ywidth")) {
18591         area    = cpl_table_get(slits, "xwidth", slit, NULL)
18592                 * cpl_table_get(slits, "ywidth", slit, NULL);
18593     }
18594 
18595     *o_flux = 0.0;
18596     *o_err = 0.0;
18597 
18598     if (xleft < 0)
18599         xleft = 0;
18600 
18601     if (xleft > nx)
18602         xleft = nx;
18603 
18604     if (xright < 0)
18605         xright = 0;
18606 
18607     if (xright > nx)
18608         xright = nx;
18609 
18610     if (ytop < 0)
18611         ytop = 0;
18612 
18613     if (ytop > ny)
18614         ytop = ny;
18615 
18616     if (ybottom < 0)
18617         ybottom = 0;
18618 
18619     if (ybottom > ny)
18620         ybottom = ny;
18621 
18622     count = (xright - xleft) * (ytop - ybottom);
18623 
18624     if (count == 0)
18625         return CPL_ERROR_ACCESS_OUT_OF_RANGE;
18626 
18627     count = 0;
18628 
18629     for (y = ybottom; y < ytop; y++) {
18630         for (x = xleft; x < xright; x++) {
18631             double value = data[x + y * nx];
18632             if (value < satur) {
18633                 flux += value;
18634                 count++;
18635             }
18636         }
18637     }
18638 
18639     if (count == 0)
18640         return CPL_ERROR_DIVISION_BY_ZERO;
18641 
18642     error = sqrt(flux/gain);
18643 
18644     /*
18645      * Flux correction for lost pixels
18646      */
18647 
18648     flux *= (float)npix / count;
18649     error *= (float)npix / count;
18650     
18651     flux /= area;  
18652     error /= area; 
18653     
18654     *o_flux = flux;
18655     *o_err = error;
18656 
18657     return CPL_ERROR_NONE;
18658 
18659 }
18660 
18661 
18675 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 
18676                        char *label, double *mvalue)
18677 {
18678     int        position   = cpl_table_get_int(slits, "position", slit, NULL);
18679     int        length     = cpl_table_get_int(slits, "length", slit, NULL);
18680     cpl_table *tmp        = cpl_table_extract(table, position, length);
18681 
18682     *mvalue = cpl_table_get_column_median(tmp, label);
18683     cpl_table_delete(tmp);
18684 
18685     if (cpl_error_get_code() != CPL_ERROR_NONE)
18686         return 1;
18687 
18688     return 0;
18689 }
18690 
18691 
18703 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny)
18704 {
18705       cpl_mask  *kernel   = cpl_mask_new(nx, ny);
18706       cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image),
18707                                           cpl_image_get_size_y(image),
18708                                           cpl_image_get_type(image));
18709 
18710       cpl_mask_not(kernel);
18711       cpl_image_filter_mask(filtered, image, kernel,
18712                             CPL_FILTER_MEDIAN, CPL_BORDER_FILTER);
18713       cpl_mask_delete(kernel);
18714 
18715       return filtered;
18716 }

Generated on Fri Mar 4 09:46:01 2011 for FORS Pipeline Reference Manual by  doxygen 1.4.7