FORS Pipeline Reference Manual 4.9.9
|
00001 /* $Id: moses.c,v 1.97 2012/02/06 16:17:06 cgarcia Exp $ 00002 * 00003 * This file is part of the MOSES library 00004 * Copyright (C) 2002-2010 European Southern Observatory 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00019 */ 00020 00021 /* 00022 * $Author: cgarcia $ 00023 * $Date: 2012/02/06 16:17:06 $ 00024 * $Revision: 1.97 $ 00025 * $Name: fors-4_9_9 $ 00026 */ 00027 00028 #ifdef HAVE_CONFIG_H 00029 #include <config.h> 00030 #endif 00031 00032 #include <stdio.h> 00033 #include <stdlib.h> 00034 #include <string.h> 00035 #include <unistd.h> 00036 #include <math.h> 00037 #include <time.h> 00038 00039 #include <fors_utils.h> 00040 #include <moses.h> 00041 00042 /* Prototypes */ 00043 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row); 00044 00045 00046 /* Cheating: moving here the cpl_tools_get_median_float() prototype, 00047 * even if cpl_tool.h is not public. It should be removed as soon as 00048 * an image median filtering with generic kernel will be implemented 00049 * in the CPL, or as soon as this module will be moved into the CPL. */ 00050 00051 float cpl_tools_get_median_float(float *, cpl_size); 00052 00053 #define MAX_COLNAME (80) 00054 #define STRETCH_FACTOR (1.20) 00055 00056 // Related to mos_identify_peaks(), used in multiplex mode 00057 00058 static int mos_multiplex = -1; 00059 static int mos_region_size = 800; 00060 00061 static double default_lines_hi[] = { /* Default sky line catalog */ 00062 5577.338, /* for high res data */ 00063 5889.953, 00064 5895.923, 00065 5915.301, 00066 5932.862, 00067 5953.420, 00068 6257.961, 00069 6287.434, 00070 6300.304, 00071 6306.869, 00072 6363.780, 00073 6498.729, 00074 6533.044, 00075 6553.617, 00076 6841.945, 00077 6863.955, 00078 6870.994, 00079 6889.288, 00080 6900.833, 00081 6912.623, 00082 6923.220, 00083 6939.521, 00084 6969.930, 00085 7003.858, 00086 7244.907, 00087 7276.405, 00088 7284.439, 00089 7316.282, 00090 7329.148, 00091 7340.885, 00092 7358.659, 00093 7571.746, 00094 7750.640, 00095 7759.996, 00096 7794.112, 00097 7808.467, 00098 7821.503, 00099 7841.266, 00100 7913.708, 00101 7949.204, 00102 7964.650, 00103 7993.332, 00104 8014.059, 00105 8310.719, 00106 8344.602, 00107 8382.392, 00108 8399.170, 00109 8415.231, 00110 8430.174, 00111 8452.250, 00112 8493.389, 00113 8791.186, 00114 8827.096, 00115 8885.850, 00116 8903.114, 00117 8943.395, 00118 8988.366 00119 }; 00120 00121 static double default_lines_lo[] = { /* Default sky line catalog */ 00122 5577.338, /* for low res data */ 00123 6300.304, 00124 6863.955, 00125 7571.746, 00126 7964.650, 00127 7993.332 00128 }; 00129 00130 00140 /* 00141 * The following macros and function for finding the k-th smallest 00142 * value on a float array will be accessible from cpl_tools once 00143 * this module will be moved into the CPL. 00144 */ 00145 00146 /**** 00147 00148 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; } 00149 00150 static float kthSmallest(float a[], int n, int k) 00151 { 00152 register int i,j,l,m; 00153 register float x; 00154 00155 l = 0; 00156 m = n-1; 00157 while (l < m) { 00158 x = a[k]; 00159 i = l; 00160 j = m; 00161 do { 00162 while (a[i] < x) { 00163 i++; 00164 } 00165 while (x < a[j]) { 00166 j--; 00167 } 00168 if (i <= j) { 00169 PIX_SWAP(a[i],a[j]); 00170 i++; 00171 j--; 00172 } 00173 } while (i <= j); 00174 00175 if (j < k) { 00176 l = i; 00177 } 00178 if (k < i) { 00179 m = j; 00180 } 00181 00182 } 00183 return(a[k]); 00184 } 00185 00186 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1))) 00187 00188 ****/ 00189 00190 /* 00191 * Return random number with gaussian distribution (mean = 0, variance = 1) 00192 * (Box-Mueller method). The mos_randg() argument is either true or false, 00193 * indicating whether to "seed" or not the sequence of generated random 00194 * numbers. The "seeding" is performed just at the first mos_randg(1) call, 00195 * and at further calls the input argument is ignored. This function 00196 * generates two random numbers at each call, returning the first one 00197 * at odd calls, and the second one at even calls. 00198 */ 00199 00200 static void mos_seed(void) 00201 { 00202 srand((unsigned int)time((time_t *)0)); 00203 } 00204 00205 static double mos_randg(int seme) 00206 { 00207 static int doit = 1; 00208 static int gotit = 1; 00209 double x1, x2, w, y1; 00210 static double y2; 00211 00212 if (gotit && seme) { 00213 mos_seed(); 00214 gotit = 0; 00215 } 00216 00217 if (doit) { 00218 doit = 0; 00219 do { 00220 x1 = 2.0 * (double)rand() / RAND_MAX - 1.0; 00221 x2 = 2.0 * (double)rand() / RAND_MAX - 1.0; 00222 w = x1 * x1 + x2 * x2; 00223 } while (w >= 1.0 || w == 0.0); 00224 00225 w = sqrt( (-2.0 * log(w)) / w); 00226 00227 y1 = x1 * w; 00228 y2 = x2 * w; 00229 return y1; 00230 } 00231 00232 doit = 1; 00233 return y2; 00234 } 00235 00236 /* 00237 * This function contained a dependency on the VIMOS library 00238 * (function medianPixelvalue()): it should be removed as soon as an 00239 * image median filtering with generic kernel will be implemented 00240 * in the CPL. Currently it has been solved by a direct call to 00241 * a cpl_tool function. 00242 */ 00243 00244 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in, 00245 int filtsizey, int refrow, 00246 int above, int below, int step) 00247 { 00248 00249 const char *func = "mos_image_general_median_filter"; 00250 00251 cpl_image *filt_img = NULL; 00252 int col, row; 00253 float *buf = NULL; 00254 float *data; 00255 float *fdata; 00256 int upright_y, loleft_y; 00257 int j; 00258 int yIsEven = !(filtsizey - (filtsizey/2)*2); 00259 int f2y; 00260 int nx = cpl_image_get_size_x(ima_in); 00261 int ny = cpl_image_get_size_y(ima_in); 00262 int firstRow; 00263 00264 00265 if (yIsEven) filtsizey++; 00266 00267 if (ny <= filtsizey) { 00268 cpl_msg_error(func, 00269 "Median filter size: %d, image size: %d", filtsizey, ny); 00270 return NULL; 00271 } 00272 00273 f2y = filtsizey / 2; 00274 00275 filt_img = cpl_image_duplicate(ima_in); 00276 buf = cpl_malloc(filtsizey * sizeof(float)); 00277 data = cpl_image_get_data(ima_in); 00278 fdata = cpl_image_get_data(filt_img); 00279 00280 firstRow = refrow - step * (below / step); 00281 if (firstRow < f2y) 00282 firstRow += step; 00283 00284 for (col = 0; col < nx; col++) { 00285 for (row = firstRow; row < refrow + above; row += step) { 00286 if (row >= ny - f2y) 00287 break; 00288 loleft_y = row - f2y; 00289 upright_y = row + f2y + 1; 00290 for (j = loleft_y; j < upright_y; j++) 00291 buf[j - loleft_y] = data[col + j * nx]; 00292 00293 fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey); 00294 } 00295 } 00296 00297 cpl_free(buf); 00298 00299 return filt_img; 00300 00301 } 00302 00303 00304 /* 00305 * The following static function is used to find an accurate position 00306 * of a peak within a short interval (however at least 5 pixels long). 00307 * The basic idea is to find the baricenter of all the pixel values 00308 * that pass a threshold level between the median value and the maximum 00309 * value within the examined interval (in case such levels are equal, 00310 * the input is considered flat and no position is returned). At least 00311 * minPoints must pass this threshold, or no position is computed. To 00312 * evaluate the significance of the computed baricenter, the variance 00313 * of the contributing positions (relative to the found baricenter) is 00314 * also evaluated, and compared with the expected variance for a uniform 00315 * distribution of positions. If the observed variance is greater than 00316 * 80% of the variance of the uniform distribution, the found position 00317 * is rejected. 00318 */ 00319 00320 static int peakPosition(const float *data, int size, float *position, 00321 int minPoints) 00322 { 00323 int i; 00324 int count = 0; 00325 float *copy; 00326 float max, median, level, pos, variance, uniformVariance; 00327 double sum, weights; 00328 00329 00330 if (data == NULL) 00331 return 1; 00332 00333 if (size < 5) /* Hardcoded, I know... */ 00334 return 1; 00335 00336 00337 /* 00338 * Find median level 00339 */ 00340 00341 copy = (float *) cpl_malloc(size*sizeof(float)); 00342 for (i = 0; i < size; i++) 00343 copy[i] = data[i]; 00344 median = cpl_tools_get_median_float(copy, size); 00345 cpl_free(copy); 00346 00347 00348 /* 00349 * Find max 00350 */ 00351 00352 max = data[0]; 00353 for (i = 1; i < size; i++) 00354 if (data[i] > max) 00355 max = data[i]; 00356 00357 00358 /* 00359 * If the max equals the median we have a flat input, therefore 00360 * no peak is found. 00361 */ 00362 00363 if (max-median < 0.00001) 00364 return 1; 00365 00366 00367 /* 00368 * Discrimination level: only pixels with values above this 00369 * level are considered in baricenter calculation. 00370 */ 00371 00372 level = (max + median) / 2; 00373 00374 00375 /* 00376 * Of the values above this level compute the baricenter and 00377 * then the variance of the positions used. Note that the weights 00378 * are taken as the difference between the pixels values and 00379 * the median level (supposedly the background). 00380 */ 00381 00382 count = 0; 00383 for (i = 0, sum = 0., weights = 0.; i < size; i++) { 00384 if (data[i] > level) { 00385 count++; 00386 weights += (data[i] - median); 00387 sum += i * (data[i] - median); 00388 } 00389 } 00390 00391 00392 /* 00393 * If too few values are above threshold, refuse the position 00394 * as insignificant 00395 */ 00396 00397 if (count < minPoints) 00398 return 1; 00399 00400 pos = sum / weights; 00401 for (i = 0, sum = 0., weights = 0.; i < size; i++) { 00402 if (data[i] > level) { 00403 weights++; 00404 sum += (i - pos) * (i - pos); 00405 } 00406 } 00407 variance = sqrt(sum / weights); 00408 00409 00410 /* 00411 * The "uniform variance" is the variance that should be obtained 00412 * in the case of uniform distribution of the points positions in 00413 * the selected interval. If the real variance is comparable with 00414 * this value, the peak is considered not found. 00415 */ 00416 00417 uniformVariance = sqrt(size*size/3 - pos*size + pos*pos); 00418 00419 if (variance > 0.8 * uniformVariance) 00420 return 1; 00421 00422 *position = pos + 0.5; 00423 00424 return 0; 00425 } 00426 00427 00428 /* 00429 * The following static function determines the quantity dx to be 00430 * added to the position of the highest pixel of a fiber profile, 00431 * to get the true position of the profile maximum. All is needed 00432 * is the maximum observed value v2 in the profile, and the observed 00433 * values v1 and v3 of the previous and the next pixels in the profile. 00434 * 00435 * The following ratio is defined: 00436 * 00437 * R = 0.5 (v3 - v1) / (2*v2 - v3 - v1) 00438 * 00439 * This is a conventional ratio that wouldn't diverge for any set of 00440 * pixel values, and that would not depend on the presence of background 00441 * (with the assumption that the background level is the same for the 00442 * three pixels). R has also been chosen in such a way that its value 00443 * is already quite close to the real dx. It should be noted that the 00444 * following condition should be fulfilled: 00445 * 00446 * v1 <= v2 and v3 < v2 00447 * or 00448 * v1 < v2 and v3 <= v2 00449 * 00450 * This implies that dx varies between -0.5 and 0.5 pixels. In such 00451 * boundary cases, one has: 00452 * 00453 * v2 = v1 and R = dx = -0.5 00454 * v2 = v3 and R = dx = 0.5 00455 * 00456 * Another special case is when the observed pixel values are perfectly 00457 * symmetrical: 00458 * 00459 * v1 = v3 and R = dx = 0.0 00460 * 00461 * In all the intermediate cases the relation between R and dx depends 00462 * on the shape of the fiber profile, that has been determined elsewhere. 00463 * Using the accurate reconstruction of the fiber profile obtained by 00464 * the * functions ifuProfile() and rebinProfile(), it can be shown 00465 * that R differs from dx always less than 0.01 pixels. If the condition 00466 * 00467 * v1 <= v2 and v3 < v2 00468 * or 00469 * v1 < v2 and v3 <= v2 00470 * 00471 * is not fulfilled, then this function returns the value 2.0. 00472 */ 00473 00474 static double values_to_dx(double v1, double v2, double v3) 00475 { 00476 00477 static double epsilon = 0.00000001; 00478 double r = 2.0; 00479 00480 00481 if (v1 > v2 || v3 > v2) 00482 return r; 00483 00484 if (2 * v2 - v1 - v3 < epsilon) 00485 return r; 00486 00487 r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1); 00488 00489 return r; 00490 00491 } 00492 00493 00494 /* 00495 * The following static function passes a min filter of given box 00496 * size on the data buffer. The box size must be a positive odd integer. 00497 */ 00498 00499 static float *min_filter(float *buffer, int length, int size) 00500 { 00501 float *minf = cpl_calloc(length, sizeof(float)); 00502 float min; 00503 int start = size / 2; 00504 int end = length - size / 2; 00505 int i, j; 00506 00507 00508 for (i = start; i < end; i++) { 00509 min = buffer[i-start]; 00510 for (j = i - start + 1; j <= i + start; j++) 00511 if (min > buffer[j]) 00512 min = buffer[j]; 00513 minf[i] = min; 00514 } 00515 00516 for (i = 0; i < start; i++) 00517 minf[i] = minf[start]; 00518 00519 for (i = end; i < length; i++) 00520 minf[i] = minf[end-1]; 00521 00522 return minf; 00523 } 00524 00525 00526 /* 00527 * The following static function passes a max filter of given box 00528 * size on the data buffer. The box size must be a positive odd integer. 00529 */ 00530 00531 static float *max_filter(float *buffer, int length, int size) 00532 { 00533 float *maxf = cpl_calloc(length, sizeof(float)); 00534 float max; 00535 int start = size / 2; 00536 int end = length - size / 2; 00537 int i, j; 00538 00539 00540 for (i = start; i < end; i++) { 00541 max = buffer[i-start]; 00542 for (j = i - start + 1; j <= i + start; j++) 00543 if (max < buffer[j]) 00544 max = buffer[j]; 00545 maxf[i] = max; 00546 } 00547 00548 for (i = 0; i < start; i++) 00549 maxf[i] = maxf[start]; 00550 00551 for (i = end; i < length; i++) 00552 maxf[i] = maxf[end-1]; 00553 00554 return maxf; 00555 } 00556 00557 00558 /* 00559 * The following static function passes a running average of given box 00560 * size on the data buffer. The box size must be a positive odd integer. 00561 */ 00562 00563 static float *smo_filter(float *buffer, int length, int size) 00564 { 00565 float *smof = cpl_calloc(length, sizeof(float)); 00566 double sum; 00567 int start = size / 2; 00568 int end = length - size / 2; 00569 int i, j; 00570 00571 00572 for (i = start; i < end; i++) { 00573 sum = 0.0; 00574 for (j = i - start; j <= i + start; j++) 00575 sum += buffer[j]; 00576 smof[i] = sum / size; 00577 } 00578 00579 for (i = 0; i < start; i++) 00580 smof[i] = smof[start]; 00581 00582 for (i = end; i < length; i++) 00583 smof[i] = smof[end-1]; 00584 00585 return smof; 00586 } 00587 00588 /* 00589 * The following two static functions are used to read and write from the 00590 * global distortion table the different model components. Conventionally 00591 * the table consists of 6 columns and 10 rows. Each row is just ordered 00592 * storage for model coefficients, and these functions guarantee that the 00593 * coefficients are read in and written out correctly, independent on their 00594 * physical meaning. The first 6 table rows are a description of the IDS 00595 * coefficients, followed by a row containing only the used reference 00596 * wavelength. The remaining 3 are a description of the spectral curvature. 00597 * The first row is a description of coefficient c0, the second of coefficient 00598 * c1, etc., of the IDS. The 8th row is a description of coefficient c0, 00599 * the 9th of coefficient c1, etc., of the spectral curvature. All are 00600 * bivariate polynomialx on x,y mask coordinates. If the input table 00601 * to the write routine is NULL, it is allocated and initialised. Also 00602 * the input polynomial could be NULL, and nothing would be written to 00603 * the table. If both pointers are NULL the function is basically a 00604 * constructor of the global distortion table. 00605 */ 00606 00607 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row) 00608 { 00609 cpl_polynomial *poly = NULL; 00610 cpl_size p[2]; 00611 cpl_size degree = 2; 00612 int null; 00613 double coeff; 00614 00615 char name[MAX_COLNAME]; 00616 00617 00618 for (p[0] = 0; p[0] <= degree; p[0]++) { 00619 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00620 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00621 coeff = cpl_table_get_double(global, name, row, &null); 00622 if (null) 00623 continue; 00624 if (poly == NULL) 00625 poly = cpl_polynomial_new(2); 00626 cpl_polynomial_set_coeff(poly, p, coeff); 00627 } 00628 } 00629 00630 return poly; 00631 } 00632 00633 static cpl_table *write_global_distortion(cpl_table *global, int row, 00634 cpl_polynomial *poly) 00635 { 00636 cpl_table *table; 00637 cpl_size p[2]; 00638 cpl_size degree = 2; 00639 int nrow = 10; 00640 00641 char name[MAX_COLNAME]; 00642 00643 00644 if (global) { 00645 table = global; 00646 } 00647 else { 00648 table = cpl_table_new(nrow); 00649 for (p[0] = 0; p[0] <= degree; p[0]++) { 00650 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00651 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00652 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE); 00653 } 00654 } 00655 } 00656 00657 if (poly) { 00658 for (p[0] = 0; p[0] <= degree; p[0]++) { 00659 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00660 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00661 cpl_table_set_double(table, name, row, 00662 cpl_polynomial_get_coeff(poly, p)); 00663 } 00664 } 00665 } 00666 00667 return table; 00668 } 00669 00670 00671 /* 00672 * The following static function is performing a robust linear fit 00673 * (drawn from the VIMOS library, and originally from ESO-Eclipse). 00674 * 00675 * ----> y = a + b * x 00676 * 00677 * This function return 0 on success. 00678 */ 00679 00680 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) 00681 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 00682 double *abdev) 00683 { 00684 cpl_vector *vx; 00685 cpl_vector *vy; 00686 cpl_vector *va; 00687 00688 double aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum; 00689 double sx, sy, sxy, sxx, chisq; 00690 double *arr; 00691 double aa_ls, bb_ls; 00692 double *x; 00693 double *y; 00694 int np; 00695 int iter; 00696 int max_iterate = 30; 00697 int i; 00698 00699 00700 np = cpl_bivector_get_size(list); 00701 vx = cpl_bivector_get_x(list); 00702 vy = cpl_bivector_get_y(list); 00703 x = cpl_vector_get_data(vx); 00704 y = cpl_vector_get_data(vy); 00705 00706 sx = sy = sxx = sxy = 0.00; 00707 for (i = 0; i < np; i++) { 00708 sx += x[i]; 00709 sy += y[i]; 00710 sxy += x[i] * y[i]; 00711 sxx += x[i] * x[i]; 00712 } 00713 00714 del = np * sxx - sx * sx; 00715 aa_ls = aa = (sxx * sy - sx * sxy) / del; 00716 bb_ls = bb = (np * sxy - sx * sy) / del; 00717 00718 chisq = 0.00; 00719 for (i = 0; i < np; i++) { 00720 temp = y[i] - (aa+bb*x[i]); 00721 temp *= temp; 00722 chisq += temp; 00723 } 00724 00725 va = cpl_vector_new(np); 00726 arr = cpl_vector_get_data(va); 00727 sigb = sqrt(chisq/del); 00728 b1 = bb; 00729 00730 bcomp = b1; 00731 sum = 0.00; 00732 for (i = 0; i < np; i++) { 00733 arr[i] = y[i] - bcomp * x[i]; 00734 } 00735 aa = cpl_vector_get_median_const(va); 00736 abdevt = 0.0; 00737 for (i = 0; i < np; i++) { 00738 d = y[i] - (bcomp * x[i] + aa); 00739 abdevt += fabs(d); 00740 if (y[i] != 0.0) 00741 d /= fabs(y[i]); 00742 if (fabs(d) > 1e-7) 00743 sum += (d >= 0.0 ? x[i] : -x[i]); 00744 } 00745 f1 = sum; 00746 00747 b2 = bb + SEGNO(3.0 * sigb, f1); 00748 00749 bcomp = b2; 00750 sum = 0.00; 00751 for (i = 0; i < np; i++) { 00752 arr[i] = y[i] - bcomp * x[i]; 00753 } 00754 aa = cpl_vector_get_median_const(va); 00755 abdevt = 0.0; 00756 for (i = 0; i < np; i++) { 00757 d = y[i] - (bcomp * x[i] + aa); 00758 abdevt += fabs(d); 00759 if (y[i] != 0.0) 00760 d /= fabs(y[i]); 00761 if (fabs(d) > 1e-7) 00762 sum += (d >= 0.0 ? x[i] : -x[i]); 00763 } 00764 f2 = sum; 00765 00766 if (fabs(b2-b1)<1e-7) { 00767 *a = aa; 00768 *b = bb; 00769 *abdev = abdevt / (double)np; 00770 cpl_vector_delete(va); 00771 return 0; 00772 } 00773 00774 iter = 0; 00775 while (f1*f2 > 0.0) { 00776 bb = 2.0*b2-b1; 00777 b1 = b2; 00778 f1 = f2; 00779 b2 = bb; 00780 00781 bcomp = b2; 00782 sum = 0.00; 00783 for (i = 0; i < np; i++) { 00784 arr[i] = y[i] - bcomp * x[i]; 00785 } 00786 aa = cpl_vector_get_median_const(va); 00787 abdevt = 0.0; 00788 for (i = 0; i < np; i++) { 00789 d = y[i] - (bcomp * x[i] + aa); 00790 abdevt += fabs(d); 00791 if (y[i] != 0.0) 00792 d /= fabs(y[i]); 00793 if (fabs(d) > 1e-7) 00794 sum += (d >= 0.0 ? x[i] : -x[i]); 00795 } 00796 f2 = sum; 00797 iter++; 00798 if (iter >= max_iterate) 00799 break; 00800 } 00801 if (iter >= max_iterate) { 00802 *a = aa_ls; 00803 *b = bb_ls; 00804 *abdev = -1.0; 00805 cpl_vector_delete(va); 00806 return 1; 00807 } 00808 00809 sigb = 0.01 * sigb; 00810 while (fabs(b2-b1) > sigb) { 00811 bb = 0.5 * (b1 + b2); 00812 if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 00813 break; 00814 bcomp = bb; 00815 sum = 0.0; 00816 for (i = 0; i < np; i++) { 00817 arr[i] = y[i] - bcomp * x[i]; 00818 } 00819 aa = cpl_vector_get_median_const(va); 00820 abdevt = 0.0; 00821 for (i = 0; i < np; i++) { 00822 d = y[i] - (bcomp * x[i] + aa); 00823 abdevt += fabs(d); 00824 if (y[i] != 0.0) 00825 d /= fabs(y[i]); 00826 if (fabs(d) > 1e-7) 00827 sum += (d >= 0.0 ? x[i] : -x[i]); 00828 } 00829 f = sum; 00830 00831 if (f*f1 >= 0.0) { 00832 f1=f; 00833 b1=bb; 00834 } 00835 else { 00836 f2=f; 00837 b2=bb; 00838 } 00839 } 00840 cpl_vector_delete(va); 00841 *a = aa; 00842 *b = bb; 00843 *abdev = abdevt / np; 00844 return 0; 00845 } 00846 #undef SEGNO 00847 00848 /* 00849 * The following static function applies the Hough transform from a table 00850 * of points to another table of points. Given the points p_i = (x_i,y_i) 00851 * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j) 00852 * and Y = y_i - X*x_i is computed and added to the output table for each 00853 * p_i, p_j pair. This means that if the input table has N points, the 00854 * output table has N*(N-1)/2 points. 00855 */ 00856 00857 /* static */ 00858 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y) 00859 { 00860 cpl_table *output; 00861 double *xdata; 00862 double *ydata; 00863 double *xodata; 00864 double *yodata; 00865 int npoints; 00866 int opoints; 00867 int i, j, k; 00868 00869 00870 npoints = cpl_table_get_nrow(table); 00871 opoints = npoints*(npoints-1)/2; 00872 00873 output = cpl_table_new(opoints); 00874 cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE); 00875 cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE); 00876 cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0); 00877 cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0); 00878 00879 xodata = cpl_table_get_data_double(output, "m"); 00880 yodata = cpl_table_get_data_double(output, "q"); 00881 00882 cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE); 00883 cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE); 00884 00885 xdata = cpl_table_get_data_double(table, "x"); 00886 ydata = cpl_table_get_data_double(table, "y"); 00887 00888 k = 0; 00889 for (i = 0; i < npoints; i++) { 00890 for (j = i+1; j < npoints; j++) { 00891 xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]); 00892 yodata[k] = ydata[i] - xodata[k] * xdata[i]; 00893 k++; 00894 } 00895 } 00896 00897 if (k != opoints) 00898 printf("Assert k = %d, expected %d\n", k, opoints); 00899 00900 cpl_table_erase_column(table, "x"); 00901 cpl_table_erase_column(table, "y"); 00902 00903 return output; 00904 } 00905 00906 00907 /* 00908 * The following static function is performing the spectral 00909 * extraction for the function mos_extract_objects() 00910 */ 00911 00912 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 00913 cpl_image *extracted, cpl_image *sky, 00914 cpl_image *error, int nobjects, int extraction, 00915 double ron, double conad, int ncomb) 00916 { 00917 00918 cpl_vector *vprofile; 00919 cpl_matrix *kernel; 00920 cpl_image *smowin; 00921 00922 int i, j; 00923 int specLen; 00924 int numRows; 00925 int index; 00926 int iter; 00927 int maxIter = 2; /* Not less than 2 !!! */ 00928 int smoothBox = 31; /* Not less than 5 !!! */ 00929 double nsigma = 5.0; 00930 00931 double sumWeight, sum, sumSky, sumProf, variance, weight; 00932 double *profile; 00933 double *buffer; 00934 float *edata; 00935 float *ekdata; 00936 float *endata; 00937 float *sdata; 00938 float *kdata; 00939 float *fdata; 00940 00941 double value; 00942 00943 00944 specLen = cpl_image_get_size_x(sciwin); 00945 numRows = cpl_image_get_size_y(sciwin); 00946 00947 edata = cpl_image_get_data(extracted); 00948 edata += nobjects * specLen; 00949 00950 ekdata = cpl_image_get_data(sky); 00951 ekdata += nobjects * specLen; 00952 00953 endata = cpl_image_get_data(error); 00954 endata += nobjects * specLen; 00955 00956 sdata = cpl_image_get_data(sciwin); 00957 kdata = cpl_image_get_data(skywin); 00958 00959 00960 /* 00961 * Initial spectrum estimate 00962 if (sdata[i + j * specLen] > 0.0) 00963 */ 00964 00965 if (extraction && numRows > 5) { 00966 smowin = mos_image_filter_median(sciwin, 3, 3); 00967 fdata = cpl_image_get_data(smowin); 00968 for (i = 0; i < specLen; i++) 00969 for (j = 0, edata[i] = 0.0; j < numRows; j++) 00970 edata[i] += fdata[i + j * specLen]; 00971 cpl_image_delete(smowin); 00972 } 00973 else { 00974 for (i = 0; i < specLen; i++) 00975 for (j = 0, edata[i] = 0.0; j < numRows; j++) 00976 edata[i] += sdata[i + j * specLen]; 00977 } 00978 00979 if (extraction) { 00980 00981 profile = cpl_calloc(specLen * numRows, sizeof(double)); 00982 buffer = cpl_calloc(specLen, sizeof(double)); 00983 00984 for (iter = 0; iter < maxIter; iter++) { 00985 00986 /* 00987 * Normalised spatial profile 00988 */ 00989 00990 for (i = 0; i < specLen; i++) { 00991 for (j = 0; j < numRows; j++) { 00992 index = i + j * specLen; 00993 /* if (sdata[index] > 0.0 && edata[i] > 0.00001) */ 00994 if (fabs(edata[i]) > 0.00001) 00995 profile[index] = sdata[index] / edata[i]; 00996 else 00997 profile[index] = 0.0; 00998 } 00999 } 01000 01001 for (j = 0; j < numRows; j++) { 01002 01003 /* 01004 * Smooth each row in the dispersion direction, and enforce positivity 01005 */ 01006 01007 for (i = 0; i < specLen - smoothBox; i++) { 01008 vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen); 01009 value = cpl_vector_get_median_const(vprofile); 01010 cpl_vector_unwrap(vprofile); 01011 if (value < 0) 01012 value = 0.0; 01013 buffer[i + smoothBox / 2] = value; 01014 } 01015 01016 /* 01017 * Replace the end portions (i.e., not median filtered) with a mean 01018 */ 01019 01020 vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen); 01021 value = cpl_vector_get_mean(vprofile); 01022 cpl_vector_unwrap(vprofile); 01023 01024 if (value < 0) 01025 value = 0.0; 01026 01027 for (i = 0; i < smoothBox / 2; i++) 01028 buffer[i] = value; 01029 01030 vprofile = cpl_vector_wrap(smoothBox / 2, 01031 profile + specLen - smoothBox/2 + j*specLen); 01032 value = cpl_vector_get_mean(vprofile); 01033 cpl_vector_unwrap(vprofile); 01034 01035 if (value < 0) 01036 value = 0.0; 01037 01038 for (i = 0; i < smoothBox / 2; i++) 01039 buffer[i + specLen - smoothBox / 2] = value; 01040 01041 for (i = 0; i < specLen; i++) 01042 profile[i + j * specLen] = buffer[i]; 01043 01044 } 01045 01046 /* 01047 * Enforce normalization of spatial profile after smoothing 01048 */ 01049 01050 for (i = 0; i < specLen; i++) { 01051 for (j = 0, value = 0.0; j < numRows; j++) 01052 value += profile[i + j * specLen]; 01053 if (value > 0.00001) 01054 for (j = 0; j < numRows; j++) 01055 profile[i + j * specLen] /= value; 01056 else 01057 for (j = 0; j < numRows; j++) 01058 profile[i + j * specLen] = 0.0; 01059 } 01060 01061 01062 /* 01063 * Optimal extraction 01064 */ 01065 01066 for (i = 0; i < specLen; i++) { 01067 sum = 0.0; 01068 sumSky = 0.0; 01069 sumWeight = 0.0; 01070 sumProf = 0.0; 01071 for (j = 0; j < numRows; j++) { 01072 index = i + j * specLen; 01073 /* 01074 if (sdata[index] > 0.0) { 01075 */ 01076 variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index]) 01077 / conad; 01078 variance /= ncomb; /* If input dataset is sum of ncomb images */ 01079 value = sdata[index] - edata[i] * profile[index]; 01080 if (fabs(value) / sqrt(variance) < nsigma) { 01081 weight = 1000000 * profile[index] / variance; 01082 sum += weight * sdata[index]; 01083 sumSky += weight * kdata[index]; 01084 sumWeight += weight * profile[index]; 01085 sumProf += profile[index]; 01086 } 01087 /* 01088 } 01089 */ 01090 } 01091 01092 if (sumWeight > 0.00001) { 01093 edata[i] = sum / sumWeight; 01094 ekdata[i] = sumSky / sumWeight; 01095 endata[i] = 1000 * sqrt(sumProf / sumWeight); 01096 } 01097 else { 01098 /* 01099 edata[i] = 0.0; 01100 ekdata[i] = 0.0; 01101 endata[i] = 0.0; 01102 */ 01103 endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad); 01104 } 01105 } 01106 } 01107 cpl_free(profile); 01108 cpl_free(buffer); 01109 } 01110 else { 01111 01112 /* 01113 * Add sky estimation for the simple aperture extraction. 01114 if (kdata[i + j * specLen] > 0.0) 01115 */ 01116 01117 for (i = 0; i < specLen; i++) 01118 for (j = 0, ekdata[i] = 0.0; j < numRows; j++) 01119 ekdata[i] += kdata[i + j * specLen]; 01120 01121 /* 01122 * Add error estimation for the simple aperture extraction. 01123 */ 01124 01125 for (i = 0; i < specLen; i++) 01126 endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad); 01127 01128 } 01129 01130 } 01131 01132 01179 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits, 01180 cpl_table *ids, cpl_table *crv, 01181 double reference) 01182 { 01183 const char *func = "mos_global_distortion"; 01184 01185 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01186 01187 cpl_table *global = NULL; 01188 cpl_table *coeff; 01189 cpl_table *dummy; 01190 cpl_vector *ci; 01191 cpl_vector *xmask; 01192 cpl_vector *ymask; 01193 cpl_bivector *mask; 01194 cpl_vector *xccd; 01195 cpl_vector *yccd; 01196 cpl_bivector *ccd; 01197 cpl_polynomial *poly; 01198 double *xtop; 01199 double *ytop; 01200 double *xbottom; 01201 double *ybottom; 01202 double *mxtop; 01203 double *mytop; 01204 double *mxbottom; 01205 double *mybottom; 01206 int *position; 01207 int *length; 01208 int *slit_id; 01209 int *mslit_id; 01210 int nslits, nmaskslits, npoints; 01211 int order; 01212 int i, j; 01213 int minslit = 6; // 12; 01214 01215 01216 /* *+ 01217 printf("error1: %s\n", cpl_error_get_message()); 01218 +* */ 01219 if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) { 01220 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01221 return NULL; 01222 } 01223 /* *+ 01224 printf("error1a: %s\n", cpl_error_get_message()); 01225 +* */ 01226 01227 nslits = cpl_table_get_nrow(slits); 01228 /* *+ 01229 printf("error1b: %s\n", cpl_error_get_message()); 01230 +* */ 01231 01232 if (nslits < minslit) { 01233 cpl_msg_warning(func, "Too few slits (%d < %d) for global " 01234 "distortion model determination", nslits, minslit); 01235 return NULL; 01236 } 01237 /* *+ 01238 printf("error1c: %s\n", cpl_error_get_message()); 01239 +* */ 01240 01241 nmaskslits = cpl_table_get_nrow(maskslits); 01242 01243 length = cpl_table_get_data_int(slits, "length"); 01244 position = cpl_table_get_data_int(slits, "position"); 01245 slit_id = cpl_table_get_data_int(slits, "slit_id"); 01246 mslit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01247 xtop = cpl_table_get_data_double(slits, "xtop"); 01248 ytop = cpl_table_get_data_double(slits, "ytop"); 01249 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01250 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01251 mxtop = cpl_table_get_data_double(maskslits, "xtop"); 01252 mytop = cpl_table_get_data_double(maskslits, "ytop"); 01253 mxbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01254 mybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01255 01256 01257 /* 01258 * Global IDS 01259 */ 01260 01261 coeff = cpl_table_new(nslits); 01262 cpl_table_copy_structure(coeff, ids); 01263 cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE); 01264 cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE); 01265 cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE); 01266 cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE); 01267 01268 /* *+ 01269 printf("error2: %s\n", cpl_error_get_message()); 01270 +* */ 01271 for (i = 0; i < nslits; i++) { 01272 for (j = 0; j < nmaskslits; j++) { 01273 if (slit_id[i] == mslit_id[j]) { 01274 cpl_table_set_double(coeff, "xmask", i, 01275 (mxtop[j] + mxbottom[j]) / 2); 01276 cpl_table_set_double(coeff, "ymask", i, 01277 (mytop[j] + mybottom[j]) / 2); 01278 } 01279 } 01280 } 01281 01282 if (cpl_table_has_invalid(coeff, "xmask")) { 01283 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 01284 cpl_table_delete(coeff); 01285 return NULL; 01286 } 01287 01288 for (i = 0; i < nslits; i++) { 01289 cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2); 01290 cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2); 01291 } 01292 01293 /* *+ 01294 printf("error3: %s\n", cpl_error_get_message()); 01295 +* */ 01296 for (i = 0; i < nslits; i++) { 01297 01298 if (length[i] == 0) 01299 continue; 01300 01301 cpl_table_and_selected_window(ids, position[i], length[i]); 01302 dummy = cpl_table_extract_selected(ids); 01303 for (j = 0; j < 6; j++) { 01304 if (cpl_table_has_column(dummy, clab[j])) { 01305 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) { 01306 cpl_table_set_double(coeff, clab[j], i, 01307 cpl_table_get_column_median(dummy, clab[j])); 01308 } 01309 } 01310 } 01311 01312 cpl_table_delete(dummy); 01313 cpl_table_select_all(ids); 01314 01315 } 01316 01317 /* *+ 01318 printf("error4: %s\n", cpl_error_get_message()); 01319 +* */ 01320 for (j = 0; j < 6; j++) { 01321 if (cpl_table_has_column(coeff, clab[j])) { 01322 cpl_table_and_selected_invalid(coeff, clab[j]); 01323 01324 if (cpl_table_not_selected(coeff)) 01325 dummy = cpl_table_extract_selected(coeff); 01326 else 01327 break; 01328 01329 npoints = cpl_table_get_nrow(dummy); 01330 01331 if (npoints >= 6) { 01332 01333 if (npoints >= 12) 01334 order = 2; 01335 else 01336 order = 1; 01337 01338 ci = cpl_vector_wrap(npoints, 01339 cpl_table_get_data_double(dummy, clab[j])); 01340 if (j) { 01341 xccd = cpl_vector_wrap(npoints, 01342 cpl_table_get_data_double(dummy, "xccd")); 01343 yccd = cpl_vector_wrap(npoints, 01344 cpl_table_get_data_double(dummy, "yccd")); 01345 ccd = cpl_bivector_wrap_vectors(xccd, yccd); 01346 01347 /* %%% */ 01348 poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL); 01349 01350 cpl_bivector_unwrap_vectors(ccd); 01351 cpl_vector_unwrap(xccd); 01352 cpl_vector_unwrap(yccd); 01353 cpl_vector_unwrap(ci); 01354 } 01355 else { 01356 xmask = cpl_vector_wrap(npoints, 01357 cpl_table_get_data_double(dummy, "xmask")); 01358 ymask = cpl_vector_wrap(npoints, 01359 cpl_table_get_data_double(dummy, "ymask")); 01360 mask = cpl_bivector_wrap_vectors(xmask, ymask); 01361 01362 /* %%% */ 01363 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL); 01364 01365 cpl_bivector_unwrap_vectors(mask); 01366 cpl_vector_unwrap(xmask); 01367 cpl_vector_unwrap(ymask); 01368 cpl_vector_unwrap(ci); 01369 } 01370 } 01371 else { 01372 cpl_size p[2] = {0, 0}; 01373 poly = cpl_polynomial_new(2); 01374 cpl_polynomial_set_coeff(poly, p, 01375 cpl_table_get_column_median(dummy, clab[j])); 01376 } 01377 01378 cpl_table_delete(dummy); 01379 01380 global = write_global_distortion(global, j, poly); 01381 01382 cpl_polynomial_delete(poly); 01383 01384 cpl_table_select_all(coeff); 01385 } 01386 } 01387 01388 /* *+ 01389 printf("error5: %s\n", cpl_error_get_message()); 01390 +* */ 01391 cpl_table_delete(coeff); 01392 /* *+ 01393 printf("error6: %s\n", cpl_error_get_message()); 01394 +* */ 01395 01396 01397 /* 01398 * Add model's reference wavelength 01399 */ 01400 01401 cpl_table_set_double(global, "a00", 6, reference); 01402 01403 01404 /* 01405 * Global curvature model 01406 */ 01407 01408 coeff = cpl_table_duplicate(crv); 01409 cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE); 01410 cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE); 01411 slit_id = cpl_table_get_data_int(coeff, "slit_id"); 01412 npoints = cpl_table_get_nrow(coeff); 01413 01414 /* *+ 01415 printf("error7: %s\n", cpl_error_get_message()); 01416 +* */ 01417 for (i = 0; i < npoints; i++) { 01418 for (j = 0; j < nmaskslits; j++) { 01419 if (slit_id[i] == mslit_id[j]) { 01420 if (i%2) { 01421 cpl_table_set_double(coeff, "xmask", i, mxbottom[j]); 01422 cpl_table_set_double(coeff, "ymask", i, mybottom[j]); 01423 } 01424 else { 01425 cpl_table_set_double(coeff, "xmask", i, mxtop[j]); 01426 cpl_table_set_double(coeff, "ymask", i, mytop[j]); 01427 } 01428 } 01429 } 01430 } 01431 01432 /* *+ 01433 printf("error8: %s\n", cpl_error_get_message()); 01434 +* */ 01435 if (cpl_table_has_invalid(coeff, "xmask")) { 01436 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 01437 cpl_table_delete(coeff); 01438 return NULL; 01439 } 01440 01441 /* *+ 01442 printf("error9: %s\n", cpl_error_get_message()); 01443 +* */ 01444 for (j = 0; j < 3; j++) { 01445 if (cpl_table_has_column(coeff, clab[j])) { 01446 cpl_table_and_selected_invalid(coeff, clab[j]); 01447 01448 if (cpl_table_not_selected(coeff)) 01449 dummy = cpl_table_extract_selected(coeff); 01450 else 01451 break; 01452 01453 npoints = cpl_table_get_nrow(dummy); 01454 01455 if (npoints >= 6) { 01456 01457 if (npoints >= 12) 01458 order = 2; 01459 else 01460 order = 1; 01461 01462 ci = cpl_vector_wrap(npoints, 01463 cpl_table_get_data_double(dummy, clab[j])); 01464 xmask = cpl_vector_wrap(npoints, 01465 cpl_table_get_data_double(dummy, "xmask")); 01466 ymask = cpl_vector_wrap(npoints, 01467 cpl_table_get_data_double(dummy, "ymask")); 01468 mask = cpl_bivector_wrap_vectors(xmask, ymask); 01469 01470 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL); 01471 01472 cpl_bivector_unwrap_vectors(mask); 01473 cpl_vector_unwrap(ci); 01474 cpl_vector_unwrap(xmask); 01475 cpl_vector_unwrap(ymask); 01476 } 01477 else { 01478 cpl_size p[2] = {0, 0}; 01479 poly = cpl_polynomial_new(2); 01480 cpl_polynomial_set_coeff(poly, p, 01481 cpl_table_get_column_median(dummy, clab[j])); 01482 } 01483 01484 cpl_table_delete(dummy); 01485 01486 global = write_global_distortion(global, j + 7, poly); 01487 01488 cpl_polynomial_delete(poly); 01489 cpl_table_select_all(coeff); 01490 } 01491 } 01492 01493 /* *+ 01494 printf("error10: %s\n", cpl_error_get_message()); 01495 +* */ 01496 cpl_table_delete(coeff); 01497 /* *+ 01498 printf("error11: %s\n", cpl_error_get_message()); 01499 +* */ 01500 01501 return global; 01502 01503 } 01504 01505 01543 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits, 01544 int ysize) 01545 { 01546 const char *func = "mos_build_slit_location"; 01547 01548 cpl_propertylist *sort_col; 01549 cpl_polynomial *ids0; 01550 cpl_polynomial *crv[3]; 01551 cpl_polynomial *loc_crv; 01552 cpl_vector *point; 01553 cpl_table *slits; 01554 cpl_size nslits; 01555 int *slit_id; 01556 double *dpoint; 01557 double *xtop; 01558 double *ytop; 01559 double *xbottom; 01560 double *ybottom; 01561 double *mxtop; 01562 double *mytop; 01563 double *mxbottom; 01564 double *mybottom; 01565 cpl_size i; 01566 cpl_size j; 01567 01568 01569 if (global == NULL || maskslits == NULL) { 01570 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01571 return NULL; 01572 } 01573 01574 nslits = cpl_table_get_nrow(maskslits); 01575 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01576 mxtop = cpl_table_get_data_double(maskslits, "xtop"); 01577 mytop = cpl_table_get_data_double(maskslits, "ytop"); 01578 mxbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01579 mybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01580 01581 slits = cpl_table_duplicate(maskslits); 01582 01583 xtop = cpl_table_get_data_double(slits, "xtop"); 01584 ytop = cpl_table_get_data_double(slits, "ytop"); 01585 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01586 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01587 01588 ids0 = read_global_distortion(global, 0); 01589 crv[0] = read_global_distortion(global, 7); 01590 crv[1] = read_global_distortion(global, 8); 01591 crv[2] = read_global_distortion(global, 9); 01592 01593 loc_crv = cpl_polynomial_new(1); 01594 01595 point = cpl_vector_new(2); 01596 dpoint = cpl_vector_get_data(point); 01597 01598 for (i = 0; i < nslits; i++) { 01599 dpoint[0] = mxtop[i]; 01600 dpoint[1] = mytop[i]; 01601 01602 xtop[i] = cpl_polynomial_eval(ids0, point); 01603 01604 for (j = 0; j < 3; j++) 01605 if (crv[j]) 01606 cpl_polynomial_set_coeff(loc_crv, &j, 01607 cpl_polynomial_eval(crv[j], point)); 01608 01609 ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL); 01610 01611 dpoint[0] = mxbottom[i]; 01612 dpoint[1] = mybottom[i]; 01613 xbottom[i] = cpl_polynomial_eval(ids0, point); 01614 01615 for (j = 0; j < 3; j++) 01616 if (crv[j]) 01617 cpl_polynomial_set_coeff(loc_crv, &j, 01618 cpl_polynomial_eval(crv[j], point)); 01619 01620 ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL); 01621 } 01622 01623 cpl_vector_delete(point); 01624 cpl_polynomial_delete(ids0); 01625 cpl_polynomial_delete(loc_crv); 01626 for (j = 0; j < 3; j++) 01627 cpl_polynomial_delete(crv[j]); 01628 01629 sort_col = cpl_propertylist_new(); 01630 cpl_propertylist_append_bool(sort_col, "ytop", 1); 01631 cpl_table_sort(slits, sort_col); 01632 cpl_table_sort(maskslits, sort_col); 01633 cpl_propertylist_delete(sort_col); 01634 01635 /* 01636 * Eliminate slits which are _entirely_ outside the CCD 01637 */ 01638 01639 cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1); 01640 cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0); 01641 cpl_table_erase_selected(slits); 01642 01643 nslits = cpl_table_get_nrow(slits); 01644 01645 if (nslits == 0) { 01646 cpl_msg_warning(func, "No slits found on the CCD"); 01647 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01648 cpl_table_delete(slits); 01649 return NULL; 01650 } 01651 01652 if (nslits > 1) 01653 cpl_msg_info(func, "Slit location: %d slits are entirely or partially " 01654 "contained in CCD", nslits); 01655 else 01656 cpl_msg_info(func, "Slit location: %d slit is entirely or partially " 01657 "contained in CCD", nslits); 01658 01659 return slits; 01660 01661 } 01662 01663 01690 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits, 01691 cpl_table *slits) 01692 { 01693 const char *func = "mos_build_curv_coeff"; 01694 01695 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01696 /* Max order is 5 */ 01697 01698 cpl_polynomial *crv[3]; 01699 cpl_vector *point; 01700 cpl_table *polytraces; 01701 double *dpoint; 01702 double *xtop; 01703 double *ytop; 01704 double *xbottom; 01705 double *ybottom; 01706 int *slit_id; 01707 int *valid_id; 01708 int nslits, nvalid; 01709 int found; 01710 int i, j, k; 01711 01712 01713 if (global == NULL || slits == NULL || maskslits == NULL) { 01714 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01715 return NULL; 01716 } 01717 01718 nslits = cpl_table_get_nrow(maskslits); 01719 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01720 xtop = cpl_table_get_data_double(maskslits, "xtop"); 01721 ytop = cpl_table_get_data_double(maskslits, "ytop"); 01722 xbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01723 ybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01724 01725 polytraces = cpl_table_new(2*nslits); 01726 cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT); 01727 for (i = 0; i < 3; i++) 01728 cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE); 01729 01730 crv[0] = read_global_distortion(global, 7); 01731 crv[1] = read_global_distortion(global, 8); 01732 crv[2] = read_global_distortion(global, 9); 01733 01734 point = cpl_vector_new(2); 01735 dpoint = cpl_vector_get_data(point); 01736 01737 for (i = 0; i < nslits; i++) { 01738 for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */ 01739 01740 cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]); 01741 01742 if (j) { 01743 dpoint[0] = xbottom[i]; 01744 dpoint[1] = ybottom[i]; 01745 } 01746 else { 01747 dpoint[0] = xtop[i]; 01748 dpoint[1] = ytop[i]; 01749 } 01750 01751 for (k = 0; k < 3; k++) 01752 if (crv[j]) 01753 cpl_table_set_double(polytraces, clab[k], 2*i+j, 01754 cpl_polynomial_eval(crv[k], point)); 01755 } 01756 } 01757 01758 cpl_vector_delete(point); 01759 for (j = 0; j < 3; j++) 01760 cpl_polynomial_delete(crv[j]); 01761 01762 /* 01763 * Eliminate slits which are _entirely_ outside the CCD 01764 */ 01765 01766 nvalid = cpl_table_get_nrow(slits); 01767 valid_id = cpl_table_get_data_int(slits, "slit_id"); 01768 cpl_table_unselect_all(polytraces); 01769 for (i = 0; i < nslits; i++) { 01770 found = 0; 01771 for (j = 0; j < nvalid; j++) { 01772 if (slit_id[i] == valid_id[j]) { 01773 found = 1; 01774 break; 01775 } 01776 } 01777 if (!found) { 01778 cpl_table_select_row(polytraces, 2*i); 01779 cpl_table_select_row(polytraces, 2*i + 1); 01780 } 01781 } 01782 cpl_table_erase_selected(polytraces); 01783 01784 nslits = cpl_table_get_nrow(polytraces); 01785 01786 if (nslits == 0) { 01787 cpl_msg_warning(func, "No slits found on the CCD"); 01788 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01789 cpl_table_delete(polytraces); 01790 return NULL; 01791 } 01792 01793 if (nslits > 2) 01794 cpl_msg_info(func, "Curvature model: %d slits are entirely or " 01795 "partially contained in CCD", nslits / 2); 01796 else 01797 cpl_msg_info(func, "Curvature model: %d slit is entirely or " 01798 "partially contained in CCD", nslits / 2); 01799 01800 return polytraces; 01801 } 01802 01803 01845 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits) 01846 { 01847 const char *func = "mos_build_disp_coeff"; 01848 01849 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01850 01851 cpl_polynomial *ids[6]; 01852 cpl_vector *point; 01853 cpl_table *idscoeff; 01854 double *dpoint; 01855 double *xtop; 01856 double *ytop; 01857 double *xbottom; 01858 double *ybottom; 01859 int *position; 01860 int *length; 01861 int nslits; 01862 int nrows; 01863 int order; 01864 int ylow, yhig; 01865 int i, j, k; 01866 01867 01868 if (global == NULL || slits == NULL) { 01869 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01870 return NULL; 01871 } 01872 01873 nslits = cpl_table_get_nrow(slits); 01874 position = cpl_table_get_data_int(slits, "position"); 01875 length = cpl_table_get_data_int(slits, "length"); 01876 xtop = cpl_table_get_data_double(slits, "xtop"); 01877 ytop = cpl_table_get_data_double(slits, "ytop"); 01878 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01879 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01880 01881 for (i = 0; i < 6; i++) 01882 ids[i] = read_global_distortion(global, i); 01883 01884 for (i = 0; i < 6; i++) 01885 if (ids[i] == NULL) 01886 break; 01887 01888 order = i - 1; 01889 01890 if (order < 1) { 01891 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01892 return NULL; 01893 } 01894 01895 nrows = 0; 01896 for (i = 0; i < nslits; i++) 01897 nrows += length[i]; 01898 01899 idscoeff = cpl_table_new(nrows); 01900 01901 for (j = 0; j <= order; j++) 01902 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 01903 01904 cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE); 01905 cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0); 01906 cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT); 01907 cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0); 01908 01909 point = cpl_vector_new(2); 01910 dpoint = cpl_vector_get_data(point); 01911 01912 for (i = 0; i < nslits; i++) { 01913 01914 if (length[i] == 0) 01915 continue; 01916 01917 ylow = position[i]; 01918 yhig = ylow + length[i]; 01919 01920 for (j = 0; j <= order; j++) { 01921 if (j) { 01922 for (k = 0; k < length[i]; k++) { 01923 dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]; 01924 dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i]; 01925 cpl_table_set_double(idscoeff, clab[j], ylow + k, 01926 cpl_polynomial_eval(ids[j], point)); 01927 } 01928 } 01929 else { 01930 for (k = 0; k < length[i]; k++) { 01931 cpl_table_set_double(idscoeff, clab[0], ylow + k, 01932 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]); 01933 } 01934 } 01935 } 01936 } 01937 01938 cpl_vector_delete(point); 01939 for (j = 0; j < 6; j++) 01940 cpl_polynomial_delete(ids[j]); 01941 01942 return idscoeff; 01943 01944 } 01945 01946 01969 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 01970 cpl_table *polytraces, double reference, 01971 double blue, double red, double dispersion) 01972 { 01973 const char *func = "mos_subtract_sky"; 01974 01975 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01976 /* Max order is 5 */ 01977 01978 cpl_image *sky; 01979 cpl_bivector *list; 01980 cpl_vector *listx; 01981 cpl_vector *listy; 01982 cpl_polynomial *polytop; 01983 cpl_polynomial *polybot; 01984 cpl_polynomial *trend; 01985 01986 int *slit_id; 01987 double *dlistx; 01988 double *dlisty; 01989 float *sdata; 01990 float *kdata; 01991 double top, bot; 01992 int itop, ibot; 01993 double coeff; 01994 double ytop, ybot; 01995 double m, q, err; 01996 int npix; 01997 01998 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 01999 int nx, ny; 02000 int nslits; 02001 int *length; 02002 int missing_top, missing_bot; 02003 int order; 02004 int null; 02005 int window = 50; /* Longer slits have polynomial sky model */ 02006 int count; 02007 int i, j; 02008 cpl_size k; 02009 02010 02011 if (science == NULL || slits == NULL || polytraces == NULL) { 02012 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02013 return NULL; 02014 } 02015 02016 if (dispersion <= 0.0) { 02017 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02018 return NULL; 02019 } 02020 02021 if (red - blue < dispersion) { 02022 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02023 return NULL; 02024 } 02025 02026 nx = cpl_image_get_size_x(science); 02027 ny = cpl_image_get_size_y(science); 02028 02029 sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 02030 02031 sdata = cpl_image_get_data(science); 02032 kdata = cpl_image_get_data(sky); 02033 02034 nslits = cpl_table_get_nrow(slits); 02035 order = cpl_table_get_ncol(polytraces) - 2; 02036 length = cpl_table_get_data_int(slits, "length"); 02037 slit_id = cpl_table_get_data_int(slits, "slit_id"); 02038 02039 /* 02040 * The spatial resampling is performed for a certain number of 02041 * pixels above and below the position of the reference wavelength: 02042 */ 02043 02044 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 02045 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 02046 02047 for (i = 0; i < nslits; i++) { 02048 02049 if (length[i] == 0) 02050 continue; 02051 02052 02053 /* 02054 * Recover from the table of spectral curvature coefficients 02055 * the curvature polynomials. 02056 */ 02057 02058 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 02059 02060 start_pixel = refpixel - pixel_below; 02061 if (start_pixel < 0) 02062 start_pixel = 0; 02063 02064 end_pixel = refpixel + pixel_above; 02065 if (end_pixel > nx) 02066 end_pixel = nx; 02067 02068 missing_top = 0; 02069 polytop = cpl_polynomial_new(1); 02070 for (k = 0; k <= order; k++) { 02071 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 02072 if (null) { 02073 cpl_polynomial_delete(polytop); 02074 missing_top = 1; 02075 break; 02076 } 02077 cpl_polynomial_set_coeff(polytop, &k, coeff); 02078 } 02079 02080 missing_bot = 0; 02081 polybot = cpl_polynomial_new(1); 02082 for (k = 0; k <= order; k++) { 02083 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 02084 if (null) { 02085 cpl_polynomial_delete(polybot); 02086 missing_bot = 1; 02087 break; 02088 } 02089 cpl_polynomial_set_coeff(polybot, &k, coeff); 02090 } 02091 02092 if (missing_top && missing_bot) { 02093 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 02094 slit_id[i]); 02095 continue; 02096 } 02097 02098 /* 02099 * In case just one of the two edges was not traced, the other 02100 * edge curvature model is duplicated and shifted to the other 02101 * end of the slit: better than nothing! 02102 */ 02103 02104 if (missing_top) { 02105 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 02106 "the spectral curvature of the lower edge " 02107 "is used instead.", slit_id[i]); 02108 polytop = cpl_polynomial_duplicate(polybot); 02109 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02110 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02111 k = 0; 02112 coeff = cpl_polynomial_get_coeff(polybot, &k); 02113 coeff += ytop - ybot; 02114 cpl_polynomial_set_coeff(polytop, &k, coeff); 02115 } 02116 02117 if (missing_bot) { 02118 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 02119 "the spectral curvature of the upper edge " 02120 "is used instead.", slit_id[i]); 02121 polybot = cpl_polynomial_duplicate(polytop); 02122 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02123 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02124 k = 0; 02125 coeff = cpl_polynomial_get_coeff(polytop, &k); 02126 coeff -= ytop - ybot; 02127 cpl_polynomial_set_coeff(polybot, &k, coeff); 02128 } 02129 02130 02131 /* 02132 * Now read pixel values along spatial direction, and fit them. 02133 */ 02134 02135 for (j = start_pixel; j < end_pixel; j++) { 02136 top = cpl_polynomial_eval_1d(polytop, j, NULL); 02137 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 02138 itop = floor(top + 0.5) + 1; 02139 ibot = floor(bot + 0.5); 02140 if (itop > ny) 02141 itop = ny; 02142 if (ibot < 0) 02143 ibot = 0; 02144 npix = itop - ibot; 02145 if (npix < 5) 02146 break; 02147 02148 list = cpl_bivector_new(npix); 02149 listx = cpl_bivector_get_x(list); 02150 listy = cpl_bivector_get_y(list); 02151 dlistx = cpl_vector_get_data(listx); 02152 dlisty = cpl_vector_get_data(listy); 02153 02154 for (k = 0; k < npix; k++) { 02155 dlistx[k] = k; 02156 dlisty[k] = sdata[j + (ibot + k)*nx]; 02157 } 02158 02159 if (robustLinearFit(list, &q, &m, &err)) { 02160 cpl_bivector_delete(list); 02161 continue; 02162 } 02163 02164 cpl_bivector_delete(list); 02165 02166 for (k = 0; k < npix; k++) { 02167 kdata[j + (ibot + k)*nx] = m*k + q; 02168 } 02169 02170 if (npix > window) { 02171 02172 /* 02173 * Polynomial iteration 02174 */ 02175 02176 err = 3*sqrt(err); 02177 02178 count = 0; 02179 for (k = 0; k < npix; k++) 02180 if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) 02181 count++; 02182 02183 if (count < 10) 02184 continue; 02185 02186 list = cpl_bivector_new(count); 02187 listx = cpl_bivector_get_x(list); 02188 listy = cpl_bivector_get_y(list); 02189 dlistx = cpl_vector_get_data(listx); 02190 dlisty = cpl_vector_get_data(listy); 02191 02192 count = 0; 02193 for (k = 0; k < npix; k++) { 02194 if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) { 02195 dlistx[count] = k; 02196 dlisty[count] = sdata[j + (ibot + k)*nx]; 02197 count++; 02198 } 02199 } 02200 02201 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err); 02202 02203 cpl_bivector_delete(list); 02204 02205 err = 3*sqrt(err); 02206 02207 count = 0; 02208 for (k = 0; k < npix; k++) 02209 if (fabs(sdata[j + (ibot + k)*nx] 02210 - cpl_polynomial_eval_1d(trend, k, NULL)) < err) 02211 count++; 02212 02213 if (count < 10) { 02214 cpl_polynomial_delete(trend); 02215 continue; 02216 } 02217 02218 list = cpl_bivector_new(count); 02219 listx = cpl_bivector_get_x(list); 02220 listy = cpl_bivector_get_y(list); 02221 dlistx = cpl_vector_get_data(listx); 02222 dlisty = cpl_vector_get_data(listy); 02223 02224 count = 0; 02225 for (k = 0; k < npix; k++) { 02226 if (fabs(sdata[j + (ibot + k)*nx] 02227 - cpl_polynomial_eval_1d(trend, k, NULL)) < err) { 02228 dlistx[count] = k; 02229 dlisty[count] = sdata[j + (ibot + k)*nx]; 02230 count++; 02231 } 02232 } 02233 02234 cpl_polynomial_delete(trend); 02235 02236 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err); 02237 02238 cpl_bivector_delete(list); 02239 02240 for (k = 0; k < npix; k++) { 02241 kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 02242 k, NULL); 02243 } 02244 02245 cpl_polynomial_delete(trend); 02246 } 02247 } 02248 cpl_polynomial_delete(polytop); 02249 cpl_polynomial_delete(polybot); 02250 } 02251 02252 cpl_image_subtract(science, sky); 02253 02254 return sky; 02255 } 02256 02257 02290 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 02291 cpl_table *slits, cpl_table *polytraces, 02292 double reference, double blue, double red, 02293 double dispersion, int sradius, int polyorder) 02294 { 02295 const char *func = "mos_normalise_flat"; 02296 02297 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 02298 /* Max order is 5 */ 02299 02300 cpl_image *rectified; 02301 cpl_image *smo_flat; 02302 cpl_image *exslit; 02303 cpl_vector *positions; 02304 cpl_vector *flux; 02305 cpl_vector *smo_flux; 02306 cpl_polynomial *trend; 02307 cpl_polynomial *polytop; 02308 cpl_polynomial *polybot; 02309 02310 int *slit_id; 02311 float *p; 02312 float *data; 02313 double *fdata; 02314 double *pdata; 02315 float *sdata; 02316 float *xdata; 02317 float *wdata; 02318 double vtop, vbot, value; 02319 double top, bot; 02320 double coeff; 02321 double ytop, ybot; 02322 double ypos; 02323 double fvalue; 02324 int ivalue; 02325 int yint, yprev; 02326 int npseudo; 02327 02328 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 02329 int nx, ny, nsubx, nsuby; 02330 int xlow, ylow, xhig, yhig; 02331 int nslits; 02332 int *position; 02333 int *length; 02334 int missing_top, missing_bot; 02335 int order; 02336 int npoints; 02337 int uradius; 02338 int null; 02339 int i, j; 02340 cpl_size k; 02341 02342 /* int exclude = 5; Number of excluded pixels at edges */ 02343 02344 /* For debug puposes only: cpl_image *smo_rectified; */ 02345 02346 02347 if (flat == NULL || slits == NULL || polytraces == NULL) { 02348 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02349 return NULL; 02350 } 02351 02352 if (dispersion <= 0.0) { 02353 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02354 return NULL; 02355 } 02356 02357 if (red - blue < dispersion) { 02358 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02359 return NULL; 02360 } 02361 02362 rectified = mos_spatial_calibration(flat, slits, polytraces, reference, 02363 blue, red, dispersion, 0, NULL); 02364 02365 nx = cpl_image_get_size_x(rectified); 02366 ny = cpl_image_get_size_y(rectified); 02367 02368 smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 02369 cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT); 02370 wdata = cpl_image_get_data(smo_flat); 02371 02372 nslits = cpl_table_get_nrow(slits); 02373 order = cpl_table_get_ncol(polytraces) - 2; 02374 position = cpl_table_get_data_int(slits, "position"); 02375 length = cpl_table_get_data_int(slits, "length"); 02376 slit_id = cpl_table_get_data_int(slits, "slit_id"); 02377 02378 /* 02379 * The spatial resampling is performed for a certain number of 02380 * pixels above and below the position of the reference wavelength: 02381 */ 02382 02383 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 02384 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 02385 02386 xlow = 1; 02387 xhig = nx; 02388 for (i = 0; i < nslits; i++) { 02389 02390 if (length[i] == 0) 02391 continue; 02392 02393 /* 02394 * We DON'T write: 02395 * 02396 * ylow = position[i]; 02397 * yhig = ylow + length[i]; 02398 * 02399 * because the cpl_image pixels are counted from 1, and because in 02400 * cpl_image_extract() the coordinates of the last pixel are inclusive. 02401 */ 02402 02403 ylow = position[i] + 1; 02404 yhig = ylow + length[i] - 1; 02405 02406 exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig); 02407 02408 if (polyorder < 0) { 02409 02410 cpl_image_turn(exslit, -1); /* For faster memory access */ 02411 02412 nsubx = cpl_image_get_size_x(exslit); 02413 nsuby = cpl_image_get_size_y(exslit); 02414 data = cpl_image_get_data(exslit); 02415 flux = cpl_vector_new(nsubx); 02416 02417 uradius = nsubx / 2; 02418 if (uradius > sradius) 02419 uradius = sradius; 02420 02421 for (j = 0; j < nsuby; j++) { 02422 fdata = cpl_vector_get_data(flux); 02423 p = data; 02424 for (k = 0; k < nsubx; k++) 02425 *fdata++ = *p++; 02426 smo_flux = cpl_vector_filter_median_create(flux, uradius); 02427 fdata = cpl_vector_get_data(smo_flux); 02428 p = data; 02429 for (k = 0; k < nsubx; k++) 02430 *p++ = *fdata++; 02431 cpl_vector_delete(smo_flux); 02432 data += nsubx; 02433 } 02434 02435 cpl_vector_delete(flux); 02436 02437 02438 /* 02439 * First fit fluxes along the spatial direction with a low-degree 02440 * polynomial (excluding the first and the last pixels, close to 02441 * the edges) 02442 */ 02443 /* 02444 if (nsubx-2*exclude > 10) { 02445 flux = cpl_vector_new(nsubx-2*exclude); 02446 fdata = cpl_vector_get_data(flux); 02447 positions = cpl_vector_new(nsubx-2*exclude); 02448 for (j = 0; j < nsubx-2*exclude; j++) 02449 cpl_vector_set(positions, j, j+exclude); 02450 02451 for (k = 0; k < nsuby; k++) { 02452 for (j = 0; j < nsubx-2*exclude; j++) 02453 fdata[j] = data[j+exclude]; 02454 trend = cpl_polynomial_fit_1d_create(positions, flux, 02455 1, NULL); 02456 for (j = 0; j < nsubx; j++) 02457 data[j] = cpl_polynomial_eval_1d(trend, j, NULL); 02458 cpl_polynomial_delete(trend); 02459 data += nsubx; 02460 } 02461 02462 cpl_vector_delete(flux); 02463 cpl_vector_delete(positions); 02464 } 02465 */ 02466 02467 /* 02468 * Now smooth along the dispersion direction 02469 */ 02470 02471 cpl_image_turn(exslit, 1); /* For faster memory access */ 02472 nsubx = cpl_image_get_size_x(exslit); 02473 nsuby = cpl_image_get_size_y(exslit); 02474 data = cpl_image_get_data(exslit); 02475 02476 for (j = 0; j < nsuby; j++) { 02477 flux = cpl_vector_new(nsubx); 02478 fdata = cpl_vector_get_data(flux); 02479 p = data; 02480 for (k = 0; k < nsubx; k++) 02481 *fdata++ = *p++; 02482 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02483 cpl_vector_delete(flux); 02484 fdata = cpl_vector_get_data(smo_flux); 02485 p = data; 02486 for (k = 0; k < nsubx; k++) 02487 *p++ = *fdata++; 02488 cpl_vector_delete(smo_flux); 02489 data += nsubx; 02490 } 02491 } 02492 else { 02493 02494 /* 02495 * Fit with a polynomial the flat field trend row by row. 02496 */ 02497 02498 nsubx = cpl_image_get_size_x(exslit); 02499 nsuby = cpl_image_get_size_y(exslit); 02500 data = cpl_image_get_data(exslit); 02501 02502 for (j = 0; j < nsuby; j++) { 02503 02504 /* 02505 * First get the size of the vectors to allocate: 02506 */ 02507 02508 npoints = 0; 02509 p = data + j*nsubx; 02510 for (k = 0; k < nsubx; k++) 02511 if (p[k] > 1.0) 02512 npoints++; 02513 02514 if (npoints > polyorder + 1) { 02515 02516 /* 02517 * Fill the vectors for the fitting 02518 */ 02519 02520 flux = cpl_vector_new(npoints); 02521 fdata = cpl_vector_get_data(flux); 02522 positions = cpl_vector_new(npoints); 02523 pdata = cpl_vector_get_data(positions); 02524 02525 npoints = 0; 02526 p = data + j*nsubx; 02527 for (k = 0; k < nsubx; k++) { 02528 if (p[k] > 1.0) { 02529 fdata[npoints] = p[k]; 02530 pdata[npoints] = k; 02531 npoints++; 02532 } 02533 } 02534 02535 trend = cpl_polynomial_fit_1d_create(positions, flux, 02536 polyorder, NULL); 02537 02538 cpl_vector_delete(flux); 02539 cpl_vector_delete(positions); 02540 02541 if (trend) { 02542 p = data + j*nsubx; 02543 for (k = 0; k < nsubx; k++) 02544 if (p[k] > 1.0) 02545 p[k] = cpl_polynomial_eval_1d(trend, k, NULL); 02546 cpl_polynomial_delete(trend); 02547 } 02548 else { 02549 cpl_msg_warning(func, "Invalid flat field flux fit " 02550 "(ignored)"); 02551 } 02552 } 02553 } 02554 } 02555 02556 02557 /* 02558 * Recover from the table of spectral curvature coefficients 02559 * the curvature polynomials. 02560 */ 02561 02562 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 02563 02564 start_pixel = refpixel - pixel_below; 02565 if (start_pixel < 0) 02566 start_pixel = 0; 02567 02568 end_pixel = refpixel + pixel_above; 02569 if (end_pixel > nx) 02570 end_pixel = nx; 02571 02572 missing_top = 0; 02573 polytop = cpl_polynomial_new(1); 02574 for (k = 0; k <= order; k++) { 02575 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 02576 if (null) { 02577 cpl_polynomial_delete(polytop); 02578 missing_top = 1; 02579 break; 02580 } 02581 cpl_polynomial_set_coeff(polytop, &k, coeff); 02582 } 02583 02584 missing_bot = 0; 02585 polybot = cpl_polynomial_new(1); 02586 for (k = 0; k <= order; k++) { 02587 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 02588 if (null) { 02589 cpl_polynomial_delete(polybot); 02590 missing_bot = 1; 02591 break; 02592 } 02593 cpl_polynomial_set_coeff(polybot, &k, coeff); 02594 } 02595 02596 if (missing_top && missing_bot) { 02597 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 02598 slit_id[i]); 02599 continue; 02600 } 02601 02602 /* 02603 * In case just one of the two edges was not traced, the other 02604 * edge curvature model is duplicated and shifted to the other 02605 * end of the slit: better than nothing! 02606 */ 02607 02608 if (missing_top) { 02609 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 02610 "the spectral curvature of the lower edge " 02611 "is used instead.", slit_id[i]); 02612 polytop = cpl_polynomial_duplicate(polybot); 02613 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02614 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02615 k = 0; 02616 coeff = cpl_polynomial_get_coeff(polybot, &k); 02617 coeff += ytop - ybot; 02618 cpl_polynomial_set_coeff(polytop, &k, coeff); 02619 } 02620 02621 if (missing_bot) { 02622 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 02623 "the spectral curvature of the upper edge " 02624 "is used instead.", slit_id[i]); 02625 polybot = cpl_polynomial_duplicate(polytop); 02626 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02627 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02628 k = 0; 02629 coeff = cpl_polynomial_get_coeff(polytop, &k); 02630 coeff -= ytop - ybot; 02631 cpl_polynomial_set_coeff(polybot, &k, coeff); 02632 } 02633 02634 02635 /* 02636 * Now map smoothed image to CCD. 02637 * Note that the npseudo value related to this slit is equal 02638 * to the number of spatial pseudo-pixels decreased by 1 02639 * (compare with function mos_spatial_calibration()). 02640 */ 02641 02642 nx = cpl_image_get_size_x(flat); 02643 ny = cpl_image_get_size_y(flat); 02644 02645 sdata = cpl_image_get_data(spatial); 02646 xdata = cpl_image_get_data(exslit); 02647 npseudo = cpl_image_get_size_y(exslit) - 1; 02648 02649 /* 02650 * Write interpolated smoothed values to CCD image 02651 */ 02652 02653 for (j = start_pixel; j < end_pixel; j++) { 02654 top = cpl_polynomial_eval_1d(polytop, j, NULL); 02655 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 02656 for (k = 0; k <= npseudo; k++) { 02657 ypos = top - k*(top-bot)/npseudo; 02658 yint = ypos; 02659 02660 /* 02661 * The line: 02662 * value = sdata[j + nx*yint]; 02663 * should be equivalent to: 02664 * value = npseudo*(top-yint)/(top-bot); 02665 */ 02666 02667 if (yint < 0 || yint >= ny-1) { 02668 yprev = yint; 02669 continue; 02670 } 02671 02672 value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */ 02673 ivalue = value; /* Nearest spatial pixels: */ 02674 fvalue = value - ivalue; /* ivalue and ivalue+1 */ 02675 if (ivalue < npseudo && ivalue >= 0) { 02676 vtop = xdata[j + nx*(npseudo-ivalue)]; 02677 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 02678 wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue; 02679 02680 if (k) { 02681 02682 /* 02683 * This is added to recover lost pixels on 02684 * the CCD image (pixels are lost because 02685 * the CCD pixels are less than npseudo+1). 02686 */ 02687 02688 if (yprev - yint > 1) { 02689 value = sdata[j + nx*(yint+1)]; 02690 ivalue = value; 02691 fvalue = value - ivalue; 02692 if (ivalue < npseudo && ivalue >= 0) { 02693 vtop = xdata[j + nx*(npseudo-ivalue)]; 02694 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 02695 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 02696 + vbot*fvalue; 02697 } 02698 } 02699 } 02700 } 02701 yprev = yint; 02702 } 02703 } 02704 cpl_polynomial_delete(polytop); 02705 cpl_polynomial_delete(polybot); 02706 cpl_image_delete(exslit); 02707 } 02708 02709 cpl_image_delete(rectified); 02710 02711 cpl_image_divide(flat, smo_flat); 02712 02713 return smo_flat; 02714 } 02715 02716 02741 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 02742 int polyorder) 02743 { 02744 const char *func = "mos_normalise_longflat"; 02745 02746 cpl_image *smo_flat; 02747 cpl_image *profile; 02748 cpl_vector *flux; 02749 cpl_vector *smo_flux; 02750 cpl_vector *positions; 02751 cpl_polynomial *trend; 02752 02753 float *level; 02754 float *p; 02755 float *data; 02756 double *fdata; 02757 double *pdata; 02758 02759 int nx, ny; 02760 int npoints; 02761 int i, j; 02762 02763 02764 if (flat == NULL) { 02765 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02766 return NULL; 02767 } 02768 02769 if (sradius < 1 || dradius < 1) { 02770 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02771 return NULL; 02772 } 02773 02774 smo_flat = cpl_image_duplicate(flat); 02775 02776 if (polyorder < 0) { 02777 02778 /* 02779 * First smooth along the spatial direction 02780 */ 02781 02782 cpl_image_turn(smo_flat, -1); /* For faster memory access */ 02783 02784 nx = cpl_image_get_size_x(smo_flat); 02785 ny = cpl_image_get_size_y(smo_flat); 02786 data = cpl_image_get_data(smo_flat); 02787 02788 for (i = 0; i < ny; i++) { 02789 flux = cpl_vector_new(nx); 02790 fdata = cpl_vector_get_data(flux); 02791 p = data; 02792 for (j = 0; j < nx; j++) 02793 *fdata++ = *p++; 02794 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02795 cpl_vector_delete(flux); 02796 fdata = cpl_vector_get_data(smo_flux); 02797 p = data; 02798 for (j = 0; j < nx; j++) 02799 *p++ = *fdata++; 02800 cpl_vector_delete(smo_flux); 02801 data += nx; 02802 } 02803 02804 /* 02805 * Second smooth along the dispersion direction 02806 */ 02807 02808 cpl_image_turn(smo_flat, 1); /* For faster memory access */ 02809 02810 nx = cpl_image_get_size_x(smo_flat); 02811 ny = cpl_image_get_size_y(smo_flat); 02812 data = cpl_image_get_data(smo_flat); 02813 02814 for (i = 0; i < ny; i++) { 02815 flux = cpl_vector_new(nx); 02816 fdata = cpl_vector_get_data(flux); 02817 p = data; 02818 for (j = 0; j < nx; j++) 02819 *fdata++ = *p++; 02820 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02821 cpl_vector_delete(flux); 02822 fdata = cpl_vector_get_data(smo_flux); 02823 p = data; 02824 for (j = 0; j < nx; j++) 02825 *p++ = *fdata++; 02826 cpl_vector_delete(smo_flux); 02827 data += nx; 02828 } 02829 } 02830 else { 02831 02832 /* 02833 * Fit with a polynomial the flat field trend column by column. 02834 */ 02835 02836 cpl_image_turn(smo_flat, -1); /* For faster memory access */ 02837 02838 nx = cpl_image_get_size_x(smo_flat); 02839 ny = cpl_image_get_size_y(smo_flat); 02840 data = cpl_image_get_data(smo_flat); 02841 02842 profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0); 02843 level = cpl_image_get_data(profile); 02844 02845 for (i = 0; i < ny; i++) { 02846 02847 /* 02848 * First get the size of the vectors to allocate: 02849 * eliminate from fit any value more than 20% away 02850 * from median level in current column. 02851 */ 02852 02853 npoints = 0; 02854 p = data + i*nx; 02855 for (j = 0; j < nx; j++) 02856 if (fabs(p[j]/level[i] - 1) < 0.20) 02857 npoints++; 02858 02859 if (npoints > polyorder + 1) { 02860 02861 /* 02862 * Fill the vectors for the fitting 02863 */ 02864 02865 flux = cpl_vector_new(npoints); 02866 fdata = cpl_vector_get_data(flux); 02867 positions = cpl_vector_new(npoints); 02868 pdata = cpl_vector_get_data(positions); 02869 02870 npoints = 0; 02871 p = data + i*nx; 02872 for (j = 0; j < nx; j++) { 02873 if (fabs(p[j]/level[i] - 1) < 0.20) { 02874 fdata[npoints] = p[j]; 02875 pdata[npoints] = j; 02876 npoints++; 02877 } 02878 } 02879 02880 trend = cpl_polynomial_fit_1d_create(positions, flux, 02881 polyorder, NULL); 02882 02883 cpl_vector_delete(flux); 02884 cpl_vector_delete(positions); 02885 02886 if (trend) { 02887 p = data + i*nx; 02888 for (j = 0; j < nx; j++) 02889 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 02890 cpl_polynomial_delete(trend); 02891 } 02892 else { 02893 cpl_msg_warning(func, 02894 "Invalid flat field flux fit (ignored)"); 02895 } 02896 } 02897 } 02898 02899 cpl_image_delete(profile); 02900 cpl_image_turn(smo_flat, 1); 02901 02902 } 02903 02904 cpl_image_divide(flat, smo_flat); 02905 02906 return smo_flat; 02907 } 02908 02909 02932 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff, 02933 cpl_table *slits, 02934 int order, int global) 02935 { 02936 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 02937 /* Max order is 5 */ 02938 int nrow = cpl_table_get_nrow(slits); 02939 int i, j; 02940 02941 02942 if (order < 0) 02943 return CPL_ERROR_NONE; 02944 02945 cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE); 02946 cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE); 02947 02948 for (i = 0; i < nrow; i++) { 02949 int position = cpl_table_get_int (slits, "position", i, NULL); 02950 int length = cpl_table_get_int (slits, "length", i, NULL); 02951 double xtop = cpl_table_get_double(slits, "xtop", i, NULL); 02952 double xbot = cpl_table_get_double(slits, "xbottom", i, NULL); 02953 double ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02954 double ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02955 double dx = xtop - xbot; 02956 double dy = ytop - ybot; 02957 cpl_table *table = cpl_table_extract(idscoeff, position, length); 02958 02959 if (mos_interpolate_wavecalib(table, NULL, 2, order)) 02960 continue; 02961 02962 cpl_table_erase_window(idscoeff, position, length); 02963 cpl_table_insert(idscoeff, table, position); 02964 02965 cpl_table_delete(table); 02966 02967 for (j = 0; j < length; j++) { 02968 cpl_table_set_double(idscoeff, "x", j + position, 02969 xbot + j*(dx/length)); 02970 cpl_table_set_double(idscoeff, "y", j + position, 02971 ybot + j*(dy/length)); 02972 } 02973 } 02974 02975 if (global) { 02976 02977 /* 02978 * Now fit a global solution 02979 */ 02980 02981 nrow = cpl_table_get_nrow(idscoeff); 02982 02983 for (i = 0; i < 6; i++) { 02984 cpl_table *dummy; 02985 cpl_vector *x; 02986 cpl_vector *y; 02987 cpl_bivector *z; 02988 cpl_vector *c; 02989 cpl_polynomial *p; 02990 cpl_vector *point; 02991 double *dpoint; 02992 int npoints; 02993 02994 if (!cpl_table_has_column(idscoeff, clab[i])) 02995 break; 02996 02997 npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]); 02998 if (npoints < 18) 02999 break; 03000 03001 dummy = cpl_table_new(nrow); 03002 cpl_table_duplicate_column(dummy, "x", idscoeff, "x"); 03003 cpl_table_duplicate_column(dummy, "y", idscoeff, "y"); 03004 cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]); 03005 cpl_table_erase_invalid(dummy); 03006 03007 x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x")); 03008 y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y")); 03009 z = cpl_bivector_wrap_vectors(x, y); 03010 c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 03011 clab[i])); 03012 p = cpl_polynomial_fit_2d_create(z, c, 2, NULL); 03013 cpl_bivector_unwrap_vectors(z); 03014 cpl_vector_unwrap(x); 03015 cpl_vector_unwrap(y); 03016 cpl_vector_unwrap(c); 03017 cpl_table_delete(dummy); 03018 03019 point = cpl_vector_new(2); 03020 dpoint = cpl_vector_get_data(point); 03021 for (j = 0; j < nrow; j++) { 03022 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL); 03023 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL); 03024 cpl_table_set_double(idscoeff, clab[i], j, 03025 cpl_polynomial_eval(p, point)); 03026 } 03027 cpl_vector_delete(point); 03028 cpl_polynomial_delete(p); 03029 } 03030 } 03031 03032 return CPL_ERROR_NONE; 03033 } 03034 03035 03061 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 03062 cpl_image *wavemap, int mode, 03063 int degree) 03064 { 03065 const char *func = "mos_interpolate_wavecalib"; 03066 03067 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 03068 /* Max order is 5 */ 03069 03070 cpl_vector *wave; 03071 cpl_vector *positions; 03072 cpl_polynomial *trend; 03073 03074 float *p; 03075 float *data; 03076 double *wdata; 03077 double *pdata; 03078 03079 double c; 03080 double mse, ksigma; 03081 03082 int order; 03083 int nrows, first_row, last_row; 03084 int nx, ny; 03085 int npoints, rpoints; 03086 int null; 03087 int i, j, k; 03088 03089 int polyorder = 4; /* Candidate input argument */ 03090 03091 03092 if (idscoeff == NULL) 03093 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03094 03095 if (mode < 0 || mode > 2) 03096 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 03097 03098 if (mode == 0 || degree < 0) 03099 return CPL_ERROR_NONE; 03100 03101 if (wavemap) { 03102 03103 /* 03104 * Fit with a polynomial the wavelength trend column by column. 03105 */ 03106 03107 cpl_image_turn(wavemap, -1); /* For faster memory access */ 03108 03109 nx = cpl_image_get_size_x(wavemap); 03110 ny = cpl_image_get_size_y(wavemap); 03111 data = cpl_image_get_data(wavemap); 03112 03113 for (i = 0; i < ny; i++) { 03114 03115 /* 03116 * First get the size of the vectors to allocate: 03117 * eliminate from fit any value with "impossible" wavelength. 03118 */ 03119 03120 npoints = 0; 03121 p = data + i*nx; 03122 for (j = 0; j < nx; j++) 03123 if (p[j] > 1.0) 03124 npoints++; 03125 03126 if (npoints > polyorder + 1) { 03127 03128 /* 03129 * Fill the vectors for the fitting 03130 */ 03131 03132 wave = cpl_vector_new(npoints); 03133 wdata = cpl_vector_get_data(wave); 03134 positions = cpl_vector_new(npoints); 03135 pdata = cpl_vector_get_data(positions); 03136 03137 npoints = 0; 03138 p = data + i*nx; 03139 for (j = 0; j < nx; j++) { 03140 if (p[j] > 1.0) { 03141 wdata[npoints] = p[j]; 03142 pdata[npoints] = j; 03143 npoints++; 03144 } 03145 } 03146 03147 trend = cpl_polynomial_fit_1d_create(positions, wave, 03148 polyorder, &mse); 03149 03150 ksigma = 3*sqrt(mse); 03151 03152 cpl_vector_delete(wave); 03153 cpl_vector_delete(positions); 03154 03155 if (trend) { 03156 03157 /* 03158 * Apply 3-sigma rejection 03159 */ 03160 03161 rpoints = 0; 03162 p = data + i*nx; 03163 for (j = 0; j < nx; j++) 03164 if (p[j] > 1.0) 03165 if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 03166 - p[j]) < ksigma) 03167 rpoints++; 03168 03169 if (rpoints < npoints && rpoints > polyorder + 1) { 03170 03171 wave = cpl_vector_new(rpoints); 03172 wdata = cpl_vector_get_data(wave); 03173 positions = cpl_vector_new(rpoints); 03174 pdata = cpl_vector_get_data(positions); 03175 03176 npoints = 0; 03177 p = data + i*nx; 03178 for (j = 0; j < nx; j++) { 03179 if (p[j] > 1.0) { 03180 if (fabs(cpl_polynomial_eval_1d(trend, 03181 j, NULL) - p[j]) 03182 < ksigma) { 03183 wdata[npoints] = p[j]; 03184 pdata[npoints] = j; 03185 npoints++; 03186 } 03187 } 03188 } 03189 03190 cpl_polynomial_delete(trend); 03191 trend = cpl_polynomial_fit_1d_create(positions, wave, 03192 polyorder, NULL); 03193 03194 cpl_vector_delete(wave); 03195 cpl_vector_delete(positions); 03196 } 03197 } 03198 03199 if (trend) { 03200 p = data + i*nx; 03201 if (mode == 1) { 03202 for (j = 0; j < nx; j++) 03203 if (p[j] < 1.0) 03204 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 03205 } 03206 else if (mode == 2) { 03207 for (j = 0; j < nx; j++) 03208 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 03209 } 03210 cpl_polynomial_delete(trend); 03211 } 03212 else { 03213 cpl_msg_warning(func, 03214 "Invalid wavelength field fit (ignored)"); 03215 } 03216 } 03217 03218 } 03219 03220 cpl_image_turn(wavemap, 1); 03221 03222 } 03223 03224 03225 /* 03226 * Interpolating the IDS coefficients 03227 */ 03228 03229 nrows = cpl_table_get_nrow(idscoeff); 03230 03231 order = 0; 03232 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 03233 ++order; 03234 --order; 03235 03236 if (degree == 0) { 03237 for (k = 0; k <= order; k++) { 03238 double m; 03239 if (cpl_table_has_column(idscoeff, clab[k])) { 03240 m = cpl_table_get_column_median(idscoeff, clab[k]); 03241 cpl_table_fill_column_window_double(idscoeff, clab[k], 03242 0, nrows, m); 03243 } 03244 } 03245 03246 return CPL_ERROR_NONE; 03247 } 03248 03249 first_row = 0; 03250 while (!cpl_table_is_valid(idscoeff, clab[0], first_row)) 03251 first_row++; 03252 03253 last_row = nrows - 1; 03254 while (!cpl_table_is_valid(idscoeff, clab[0], last_row)) 03255 last_row--; 03256 03257 for (k = 0; k <= order; k++) { 03258 03259 npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]); 03260 wave = cpl_vector_new(npoints); 03261 wdata = cpl_vector_get_data(wave); 03262 positions = cpl_vector_new(npoints); 03263 pdata = cpl_vector_get_data(positions); 03264 03265 npoints = 0; 03266 for (i = first_row; i <= last_row; i++) { 03267 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03268 if (null == 0) { 03269 wdata[npoints] = c; 03270 pdata[npoints] = i; 03271 npoints++; 03272 } 03273 } 03274 03275 // This doesn't seem to provide good results, I have not understood why. 03276 // Restore for robust linear fitting. 03277 // 03278 if (degree == 1) { 03279 cpl_vector *p; 03280 cpl_vector *w; 03281 cpl_bivector *list; 03282 double q, m; 03283 03284 if (npoints > 4) { 03285 p = cpl_vector_extract(positions, 2, npoints - 2, 1); 03286 w = cpl_vector_extract(wave, 2, npoints - 2, 1); 03287 } 03288 else { 03289 p = positions; 03290 w = wave; 03291 } 03292 03293 list = cpl_bivector_wrap_vectors(p, w); 03294 03295 robustLinearFit(list, &q, &m, &mse); 03296 cpl_bivector_unwrap_vectors(list); 03297 for (i = first_row; i <= last_row; i++) 03298 cpl_table_set_double(idscoeff, clab[k], i, q + m*i); 03299 03300 if (npoints > 4) { 03301 cpl_vector_delete(p); 03302 cpl_vector_delete(w); 03303 } 03304 03305 continue; 03306 } 03307 03308 // End robust linear fitting 03309 03310 trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse); 03311 03312 ksigma = 3*sqrt(mse); 03313 03314 cpl_vector_delete(wave); 03315 cpl_vector_delete(positions); 03316 03317 /* 03318 * Iteration 03319 */ 03320 03321 if (trend) { 03322 rpoints = 0; 03323 for (i = first_row; i <= last_row; i++) { 03324 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03325 if (null == 0) { 03326 if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 03327 < ksigma) { 03328 rpoints++; 03329 } 03330 } 03331 } 03332 03333 if (rpoints > 0 && rpoints < npoints) { 03334 cpl_msg_debug(func, "%d points rejected from " 03335 "wavelength calibration fit", 03336 npoints - rpoints); 03337 03338 wave = cpl_vector_new(rpoints); 03339 wdata = cpl_vector_get_data(wave); 03340 positions = cpl_vector_new(rpoints); 03341 pdata = cpl_vector_get_data(positions); 03342 03343 npoints = 0; 03344 for (i = first_row; i <= last_row; i++) { 03345 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03346 if (null == 0) { 03347 if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 03348 < ksigma) { 03349 wdata[npoints] = c; 03350 pdata[npoints] = i; 03351 npoints++; 03352 } 03353 } 03354 } 03355 03356 if (npoints) { 03357 cpl_polynomial_delete(trend); 03358 trend = cpl_polynomial_fit_1d_create(positions, 03359 wave, degree, NULL); 03360 } 03361 03362 cpl_vector_delete(wave); 03363 cpl_vector_delete(positions); 03364 03365 } 03366 } 03367 03368 if (trend) { 03369 for (i = first_row; i <= last_row; i++) { 03370 if (mode == 1) { 03371 if (!cpl_table_is_valid(idscoeff, clab[k], i)) { 03372 cpl_table_set_double(idscoeff, clab[k], i, 03373 cpl_polynomial_eval_1d(trend, i, 03374 NULL)); 03375 } 03376 } 03377 else if (mode == 2) { 03378 cpl_table_set_double(idscoeff, clab[k], i, 03379 cpl_polynomial_eval_1d(trend, i, NULL)); 03380 } 03381 } 03382 cpl_polynomial_delete(trend); 03383 } 03384 else { 03385 cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)"); 03386 } 03387 03388 } 03389 03390 return CPL_ERROR_NONE; 03391 } 03392 03393 03394 03420 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 03421 cpl_table *overscans) 03422 { 03423 const char *func = "mos_remove_bias"; 03424 03425 cpl_image *unbiased; 03426 cpl_image *overscan; 03427 double mean_bias_level; 03428 double mean_overscans_level; 03429 int count; 03430 int nrows; 03431 int xlow, ylow, xhig, yhig; 03432 int i; 03433 03434 03435 if (image == NULL || overscans == NULL) { 03436 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03437 return NULL; 03438 } 03439 03440 nrows = cpl_table_get_nrow(overscans); 03441 03442 if (nrows == 0) { 03443 cpl_msg_error(func, "Empty overscan table"); 03444 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 03445 return NULL; 03446 } 03447 03448 if (bias) { 03449 if (nrows == 1) { 03450 unbiased = cpl_image_subtract_create(image, bias); 03451 if (unbiased == NULL) { 03452 cpl_msg_error(func, "Incompatible master bias"); 03453 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03454 } 03455 return unbiased; 03456 } 03457 mean_bias_level = cpl_image_get_mean(bias); 03458 } 03459 else { 03460 if (nrows == 1) { 03461 cpl_msg_error(func, "No master bias in input, and no overscan " 03462 "regions in input image: bias subtraction " 03463 "cannot be performed!"); 03464 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 03465 return NULL; 03466 } 03467 mean_bias_level = 0.0; 03468 } 03469 03470 mean_overscans_level = 0.0; 03471 count = 0; 03472 for (i = 0; i < nrows; i++) { 03473 xlow = cpl_table_get_int(overscans, "xlow", i, NULL); 03474 ylow = cpl_table_get_int(overscans, "ylow", i, NULL); 03475 xhig = cpl_table_get_int(overscans, "xhig", i, NULL); 03476 yhig = cpl_table_get_int(overscans, "yhig", i, NULL); 03477 03478 if (i == 0) { 03479 unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig); 03480 if (unbiased == NULL) { 03481 cpl_msg_error(func, "Incompatible overscan table"); 03482 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03483 return NULL; 03484 } 03485 if (bias) { 03486 if (cpl_image_subtract(unbiased, bias)) { 03487 cpl_msg_error(func, "Incompatible master bias"); 03488 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03489 cpl_image_delete(unbiased); 03490 return NULL; 03491 } 03492 } 03493 } 03494 else { 03495 overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig); 03496 if (overscan == NULL) { 03497 cpl_msg_error(func, "Incompatible overscan table"); 03498 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03499 cpl_image_delete(unbiased); 03500 return NULL; 03501 } 03502 03503 mean_overscans_level += cpl_image_get_median(overscan); 03504 count++; 03505 03506 /*** 03507 * Here the mean level was used: not very robust... 03508 03509 mean_overscans_level += cpl_image_get_flux(overscan); 03510 count += cpl_image_get_size_x(overscan) 03511 * cpl_image_get_size_y(overscan); 03512 ***/ 03513 cpl_image_delete(overscan); 03514 } 03515 } 03516 03517 /* 03518 * Overscan correction 03519 */ 03520 03521 mean_overscans_level /= count; 03522 03523 cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level); 03524 03525 cpl_msg_info(cpl_func, 03526 "Ratio between mean overscans level and mean bias level: %.2f", 03527 mean_overscans_level / mean_bias_level); 03528 03529 return unbiased; 03530 03531 } 03532 03533 03592 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 03593 int length, int msize, int fsize) 03594 { 03595 const char *func = "mos_arc_background_1D"; 03596 03597 float *minf; 03598 float *maxf; 03599 float *smof; 03600 int i; 03601 03602 03603 if (spectrum == NULL || back == NULL) 03604 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03605 03606 if (msize % 2 == 0) 03607 msize++; 03608 03609 if (fsize % 2 == 0) 03610 fsize++; 03611 03612 if (msize < 3 || fsize < msize || length < 2*fsize) 03613 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 03614 03615 03616 minf = min_filter(spectrum, length, msize); 03617 smof = smo_filter(minf, length, fsize); 03618 cpl_free(minf); 03619 maxf = max_filter(smof, length, 2*msize+1); 03620 cpl_free(smof); 03621 smof = smo_filter(maxf, length, 2*fsize+1); 03622 cpl_free(maxf); 03623 minf = min_filter(smof, length, 2*msize+1); 03624 cpl_free(smof); 03625 smof = smo_filter(minf, length, 2*fsize+1); 03626 cpl_free(minf); 03627 03628 for (i = 0; i < length; i++) 03629 back[i] = smof[i]; 03630 03631 cpl_free(smof); 03632 03633 return CPL_ERROR_NONE; 03634 03635 } 03636 03637 03694 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 03695 { 03696 const char *func = "mos_arc_background"; 03697 03698 cpl_image *fimage; 03699 cpl_image *bimage; 03700 cpl_matrix *kernel; 03701 float *data; 03702 float *bdata; 03703 float *row; 03704 float *brow; 03705 int nx, ny; 03706 int i; 03707 03708 03709 if (image == NULL) { 03710 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03711 return NULL; 03712 } 03713 03714 if (msize % 2 == 0) 03715 msize++; 03716 03717 if (fsize % 2 == 0) 03718 fsize++; 03719 03720 nx = cpl_image_get_size_x(image); 03721 ny = cpl_image_get_size_y(image); 03722 03723 bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 03724 03725 fimage = mos_image_filter_median(image, 3, 3); 03726 03727 data = cpl_image_get_data_float(fimage); 03728 bdata = cpl_image_get_data_float(bimage); 03729 03730 for (i = 0; i < ny; i++) { 03731 row = data + i * nx; 03732 brow = bdata + i * nx; 03733 if (mos_arc_background_1D(row, brow, nx, msize, fsize)) { 03734 cpl_error_set_where(func); 03735 cpl_image_delete(fimage); 03736 cpl_image_delete(bimage); 03737 return NULL; 03738 } 03739 } 03740 03741 cpl_image_delete(fimage); 03742 03743 return bimage; 03744 } 03745 03746 03767 int mos_lines_width(const float *spectrum, int length) 03768 { 03769 03770 const char *func = "mos_lines_width"; 03771 03772 double *profile1 = cpl_calloc(length - 1, sizeof(double)); 03773 double *profile2 = cpl_calloc(length - 1, sizeof(double)); 03774 03775 double norm, value, max; 03776 int radius = 20; 03777 int short_length = length - 2*radius - 1; 03778 int width; 03779 int i, j, k; 03780 03781 03782 /* 03783 * Derivative, and separation of positive and negative derivatives 03784 */ 03785 03786 for (j = 0, i = 1; i < length; j++, i++) { 03787 profile1[j] = profile2[j] = spectrum[i] - spectrum[j]; 03788 if (profile1[j] < 0) 03789 profile1[j] = 0; 03790 if (profile2[j] > 0) 03791 profile2[j] = 0; 03792 else 03793 profile2[j] = -profile2[j]; 03794 } 03795 03796 03797 /* 03798 * Profiles normalisation 03799 */ 03800 03801 length--; 03802 03803 norm = 0; 03804 for (i = 0; i < length; i++) 03805 if (norm < profile1[i]) 03806 norm = profile1[i]; 03807 03808 for (i = 0; i < length; i++) { 03809 profile1[i] /= norm; 03810 profile2[i] /= norm; 03811 } 03812 03813 03814 /* 03815 * Cross-correlation 03816 */ 03817 03818 max = -1; 03819 for (i = 0; i <= radius; i++) { 03820 value = 0; 03821 for (j = 0; j < short_length; j++) { 03822 k = radius+j; 03823 value += profile1[k] * profile2[k+i]; 03824 } 03825 if (max < value) { 03826 max = value; 03827 width = i; 03828 } 03829 } 03830 03831 cpl_free(profile1); 03832 cpl_free(profile2); 03833 03834 if (max < 0.0) { 03835 cpl_msg_debug(func, "Cannot estimate line width"); 03836 width = 1; 03837 } 03838 03839 return width; 03840 03841 } 03842 03843 03870 cpl_vector *mos_peak_candidates(const float *spectrum, 03871 int length, float level, 03872 float exp_width) 03873 { 03874 03875 const char *func = "mos_peak_candidates"; 03876 03877 int i, j; 03878 int nint = length - 1; 03879 int n = 0; 03880 int width = 2 * ceil(exp_width / 2) + 1; 03881 int start = width / 2; 03882 int end = length - width / 2; 03883 int step; 03884 float *smo; 03885 double *data = cpl_calloc(length/2, sizeof(double)); 03886 03887 03888 if (spectrum == NULL) { 03889 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03890 return NULL; 03891 } 03892 03893 03894 /* 03895 * If lines have a flat top (as in the case of broad slit), smooth 03896 * before determining the max. 03897 */ 03898 03899 if (width > 7) { 03900 smo = cpl_calloc(length, sizeof(float)); 03901 start = width / 2; 03902 end = length - width / 2; 03903 for (i = 0; i < start; i++) 03904 smo[i] = spectrum[i]; 03905 for (i = start; i < end; i++) { 03906 for (j = i - start; j <= i + start; j++) 03907 smo[i] += spectrum[j]; 03908 smo[i] /= width; 03909 } 03910 for (i = end; i < length; i++) 03911 smo[i] = spectrum[i]; 03912 } 03913 else { 03914 smo = (float *)spectrum; 03915 } 03916 03917 /* 03918 * Collect all relative maxima along spectrum, that are higher than the 03919 * specified level. 03920 */ 03921 03922 if (width > 20) 03923 step = width / 2; 03924 else 03925 step = 1; 03926 03927 for (i = step; i < nint - step + 1; i += step) { 03928 if (smo[i] > level) { 03929 if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) { 03930 if (smo[i-step] != 0.0 && smo[i+step] != 0.0) { 03931 data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]); 03932 ++n; 03933 } 03934 } 03935 } 03936 } 03937 03938 if (width > 7) { 03939 cpl_free(smo); 03940 } 03941 03942 if (n == 0) { 03943 cpl_free(data); 03944 return NULL; 03945 } 03946 03947 return cpl_vector_wrap(n, data); 03948 03949 } 03950 03951 03973 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 03974 cpl_vector *peaks, int sradius) 03975 { 03976 03977 const char *func = "mos_refine_peaks"; 03978 03979 double *data; 03980 float pos; 03981 int npeaks; 03982 int startPos, endPos; 03983 int window = 2*sradius+1; 03984 int i, j; 03985 03986 03987 if (peaks == NULL || spectrum == NULL) { 03988 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03989 return NULL; 03990 } 03991 03992 npeaks = cpl_vector_get_size(peaks); 03993 data = cpl_vector_unwrap(peaks); 03994 03995 for (i = 0; i < npeaks; i++) { 03996 startPos = data[i] - window/2; 03997 endPos = startPos + window; 03998 if (startPos < 0 || endPos >= length) 03999 continue; 04000 04001 if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) { 04002 pos += startPos; 04003 data[i] = pos; 04004 } 04005 } 04006 04007 for (i = 1; i < npeaks; i++) 04008 if (data[i] - data[i-1] < 0.5) 04009 data[i-1] = -1.0; 04010 04011 for (i = 0, j = 0; i < npeaks; i++) { 04012 if (data[i] > 0.0) { 04013 if (i != j) 04014 data[j] = data[i]; 04015 j++; 04016 } 04017 } 04018 04019 return cpl_vector_wrap(j, data); 04020 04021 } 04022 04023 04024 void mos_set_multiplex(int multiplex) 04025 { 04026 mos_multiplex = multiplex; 04027 } 04028 04082 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines, 04083 double min_disp, double max_disp, 04084 double tolerance) 04085 { 04086 04087 int i, j, k, l; 04088 int nlint, npint; 04089 int minpos; 04090 float min; 04091 double lratio, pratio; 04092 double lo_start, lo_end, hi_start, hi_end, denom; 04093 double disp, variation, prev_variation; 04094 int max, maxpos, minl, mink; 04095 int ambiguous; 04096 int npeaks_lo, npeaks_hi; 04097 int *peak_lo; 04098 int *peak_hi; 04099 int **ident; 04100 int *nident; 04101 int *lident; 04102 04103 double *peak; 04104 double *line; 04105 int npeaks, nlines; 04106 04107 double *xpos; 04108 double *lambda; 04109 int *ilambda; 04110 double *tmp_xpos; 04111 double *tmp_lambda; 04112 int *tmp_ilambda; 04113 int *flag; 04114 int n = 0; 04115 int nn; 04116 int nseq = 0; 04117 int gap; 04118 int *seq_length; 04119 int found; 04120 04121 peak = cpl_vector_get_data(peaks); 04122 npeaks = cpl_vector_get_size(peaks); 04123 line = cpl_vector_get_data(lines); 04124 nlines = cpl_vector_get_size(lines); 04125 04126 if (npeaks < 4) 04127 return NULL; 04128 04129 peak_lo = cpl_malloc(npeaks * sizeof(int)); 04130 peak_hi = cpl_malloc(npeaks * sizeof(int)); 04131 nident = cpl_calloc(npeaks, sizeof(int)); 04132 lident = cpl_calloc(nlines, sizeof(int)); 04133 xpos = cpl_calloc(npeaks, sizeof(double)); 04134 lambda = cpl_calloc(npeaks, sizeof(double)); 04135 ilambda = cpl_calloc(npeaks, sizeof(int)); 04136 tmp_xpos = cpl_calloc(npeaks, sizeof(double)); 04137 tmp_lambda = cpl_calloc(npeaks, sizeof(double)); 04138 tmp_ilambda = cpl_calloc(npeaks, sizeof(int)); 04139 flag = cpl_calloc(npeaks, sizeof(int)); 04140 seq_length = cpl_calloc(npeaks, sizeof(int)); 04141 ident = cpl_malloc(npeaks * sizeof(int *)); 04142 for (i = 0; i < npeaks; i++) 04143 ident[i] = cpl_malloc(3 * npeaks * sizeof(int)); 04144 04145 /* 04146 * This is just the number of intervals - one less than the number 04147 * of points (catalog wavelengths, or detected peaks). 04148 */ 04149 04150 nlint = nlines - 1; 04151 npint = npeaks - 1; 04152 04153 04154 /* 04155 * Here the big loops on catalog lines begins. 04156 */ 04157 04158 for (i = 1; i < nlint; i++) { 04159 04160 04161 /* 04162 * For each catalog wavelength I take the previous and the next one, 04163 * and compute the ratio of the corresponding wavelength intervals. 04164 * This ratio will be compared to all the ratios obtained doing the 04165 * same with all the detected peaks positions. 04166 */ 04167 04168 lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]); 04169 04170 04171 /* 04172 * Here the loop on detected peaks positions begins. 04173 */ 04174 04175 for (j = 1; j < npint; j++) { 04176 04177 /* 04178 * Not all peaks are used for computing ratios: just the ones 04179 * that are compatible with the expected spectral dispersion 04180 * are taken into consideration. Therefore, I define the pixel 04181 * intervals before and after any peak that are compatible with 04182 * the specified dispersion interval, and select just the peaks 04183 * within such intervals. If either of the two intervals doesn't 04184 * contain any peak, then I skip the current peak and continue 04185 * with the next. 04186 */ 04187 04188 lo_start = peak[j] - (line[i] - line[i-1]) / min_disp; 04189 lo_end = peak[j] - (line[i] - line[i-1]) / max_disp; 04190 hi_start = peak[j] + (line[i+1] - line[i]) / max_disp; 04191 hi_end = peak[j] + (line[i+1] - line[i]) / min_disp; 04192 04193 for (npeaks_lo = 0, k = 0; k < npeaks; k++) { 04194 if (peak[k] > lo_end) 04195 break; 04196 if (peak[k] > lo_start) { 04197 peak_lo[npeaks_lo] = k; 04198 ++npeaks_lo; 04199 } 04200 } 04201 04202 if (npeaks_lo == 0) 04203 continue; 04204 04205 for (npeaks_hi = 0, k = 0; k < npeaks; k++) { 04206 if (peak[k] > hi_end) 04207 break; 04208 if (peak[k] > hi_start) { 04209 peak_hi[npeaks_hi] = k; 04210 ++npeaks_hi; 04211 } 04212 } 04213 04214 if (npeaks_hi == 0) 04215 continue; 04216 04217 04218 /* 04219 * Now I have all peaks that may help for a local identification. 04220 * peak_lo[k] is the sequence number of the k-th peak of the lower 04221 * interval; peak_hi[l] is the sequence number of the l-th peak of 04222 * the higher interval. j is, of course, the sequence number of the 04223 * current peak (second big loop). 04224 */ 04225 04226 prev_variation = 1000.0; 04227 minl = mink = 0; 04228 04229 for (k = 0; k < npeaks_lo; k++) { 04230 denom = peak[j] - peak[peak_lo[k]]; 04231 for (l = 0; l < npeaks_hi; l++) { 04232 04233 /* 04234 * For any pair of peaks - one from the lower and the other 04235 * from the higher interval - I compute the same ratio that 04236 * was computed with the current line catalog wavelength. 04237 */ 04238 04239 pratio = (peak[peak_hi[l]] - peak[j]) / denom; 04240 04241 /* 04242 * If the two ratios are compatible within the specified 04243 * tolerance, we have a preliminary identification. This 04244 * will be marked in the matrix ident[][], where the first 04245 * index corresponds to a peak sequence number, and the second 04246 * index is the counter of the identifications made during 04247 * this whole process. The array of counters is nident[]. 04248 * If more than one interval pair fulfills the specified 04249 * tolerance, the closest to the expected ratio is selected. 04250 */ 04251 04252 variation = fabs(lratio-pratio) / pratio; 04253 04254 if (variation < tolerance) { 04255 if (variation < prev_variation) { 04256 prev_variation = variation; 04257 minl = l; 04258 mink = k; 04259 } 04260 } 04261 } 04262 } 04263 if (prev_variation < tolerance) { 04264 ident[j][nident[j]] = i; 04265 ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1; 04266 ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1; 04267 ++nident[j]; 04268 ++nident[peak_hi[minl]]; 04269 ++nident[peak_lo[mink]]; 04270 } 04271 } /* End loop on positions */ 04272 } /* End loop on lines */ 04273 04274 04275 /* 04276 * At this point I have filled the ident matrix with all my preliminary 04277 * identifications. Ambiguous identifications must be eliminated. 04278 */ 04279 04280 04281 for (i = 0; i < npeaks; i++) { 04282 04283 04284 /* 04285 * I don't take into consideration peaks that were never identified. 04286 * They are likely contaminations, or emission lines that were not 04287 * listed in the input wavelength catalog. 04288 */ 04289 04290 if (nident[i] > 1) { 04291 04292 04293 /* 04294 * Initialise the histogram of wavelengths assigned to the i-th peak. 04295 */ 04296 04297 for (j = 0; j < nlines; j++) 04298 lident[j] = 0; 04299 04300 04301 /* 04302 * Count how many times each catalog wavelength was assigned 04303 * to the i-th peak. 04304 */ 04305 04306 for (j = 0; j < nident[i]; j++) 04307 ++lident[ident[i][j]]; 04308 04309 04310 /* 04311 * What wavelength was most frequently assigned to the i-th peak? 04312 */ 04313 04314 max = 0; 04315 maxpos = 0; 04316 for (j = 0; j < nlines; j++) { 04317 if (max < lident[j]) { 04318 max = lident[j]; 04319 maxpos = j; 04320 } 04321 } 04322 04323 04324 /* 04325 * Were there other wavelengths assigned with the same frequency? 04326 * This would be the case of an ambiguous identification. It is 04327 * safer to reject this peak... 04328 */ 04329 04330 ambiguous = 0; 04331 04332 for (k = maxpos + 1; k < nlines; k++) { 04333 if (lident[k] == max) { 04334 ambiguous = 1; 04335 break; 04336 } 04337 } 04338 04339 if (ambiguous) 04340 continue; 04341 04342 04343 /* 04344 * Otherwise, I assign to the i-th peak the wavelength that was 04345 * most often assigned to it. 04346 */ 04347 04348 tmp_xpos[n] = peak[i]; 04349 tmp_lambda[n] = line[maxpos]; 04350 tmp_ilambda[n] = maxpos; 04351 04352 ++n; 04353 04354 } 04355 04356 } 04357 04358 04359 /* 04360 * Check on identified peaks. Contaminations from other spectra might 04361 * be present and should be excluded: this type of contamination 04362 * consists of peaks that have been _correctly_ identified! The non- 04363 * spectral type of light contamination should have been almost all 04364 * removed already in the previous steps, but it may still be present. 04365 * Here, the self-consistent sequences of identified peaks are 04366 * separated one from the other. At the moment, just the longest of 04367 * such sequences is selected (in other words, spectral multiplexing 04368 * is ignored). 04369 */ 04370 04371 if (n > 1) { 04372 nn = 0; /* Number of peaks in the list of sequences */ 04373 nseq = 0; /* Current sequence */ 04374 for (k = 0; k < n; k++) { 04375 if (flag[k] == 0) { /* Was peak k already assigned to a sequence? */ 04376 flag[k] = 1; 04377 xpos[nn] = tmp_xpos[k]; /* Begin the nseq-th sequence */ 04378 lambda[nn] = tmp_lambda[k]; 04379 ilambda[nn] = tmp_ilambda[k]; 04380 ++seq_length[nseq]; 04381 ++nn; 04382 04383 /* 04384 * Now look for all the following peaks that are compatible 04385 * with the expected spectral dispersion, and add them in 04386 * sequence to xpos. Note that missing peaks are not a problem... 04387 */ 04388 04389 i = k; 04390 while (i < n - 1) { 04391 found = 0; 04392 for (j = i + 1; j < n; j++) { 04393 if (flag[j] == 0) { 04394 disp = (tmp_lambda[j] - tmp_lambda[i]) 04395 / (tmp_xpos[j] - tmp_xpos[i]); 04396 if (disp >= min_disp && disp <= max_disp) { 04397 flag[j] = 1; 04398 xpos[nn] = tmp_xpos[j]; 04399 lambda[nn] = tmp_lambda[j]; 04400 ilambda[nn] = tmp_ilambda[j]; 04401 ++seq_length[nseq]; 04402 ++nn; 04403 i = j; 04404 found = 1; 04405 break; 04406 } 04407 } 04408 } 04409 if (!found) 04410 break; 04411 } 04412 04413 /* 04414 * Current sequence is completed: begin new sequence on the 04415 * excluded peaks, starting the loop on peaks again. 04416 */ 04417 04418 ++nseq; 04419 k = 0; 04420 } 04421 } 04422 04423 04424 /* 04425 * Find the longest sequence of self-consistent peaks. 04426 */ 04427 04428 maxpos = max = 0; 04429 04430 if (mos_multiplex < 0) { 04431 for (i = 0; i < nseq; i++) { 04432 if (seq_length[i] > max) { 04433 max = seq_length[i]; 04434 maxpos = i; 04435 } 04436 } 04437 } 04438 else { 04439 04440 /* 04441 * Now consider the sequence which lays in the specified 04442 * CCD region (indicated by mos_multiplex): that is, _most_ 04443 * of its lines (more than half) must be in that region... 04444 */ 04445 04446 nn = 0; 04447 found = 0; 04448 04449 for (i = 0; i < nseq; i++) { 04450 n = seq_length[i]; 04451 if (n > 5) { 04452 cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT); 04453 int region; 04454 04455 for (j = 0; j < n; j++) 04456 cpl_array_set_int(regions, j, 04457 ((int)floor(xpos[nn + j])) / mos_region_size); 04458 04459 region = (int)cpl_array_get_median(regions); 04460 cpl_array_delete(regions); 04461 04462 if (mos_multiplex == region) { 04463 if (found) { 04464 cpl_msg_debug(cpl_func, "More than one spectrum found in " 04465 "region %d (only the first one is extracted)", 04466 mos_multiplex); 04467 break; 04468 } 04469 found = 1; 04470 max = seq_length[i]; 04471 maxpos = i; 04472 } 04473 } 04474 nn += seq_length[i]; 04475 } 04476 } 04477 04478 /* 04479 * Find where this sequence starts in the whole peak position 04480 * storage. 04481 */ 04482 04483 nn = 0; 04484 for (i = 0; i < maxpos; i++) 04485 nn += seq_length[i]; 04486 04487 /* 04488 * Move the longest sequence at the beginning of the returned lists 04489 */ 04490 04491 n = max; 04492 for (i = 0; i < n; i++, nn++) { 04493 xpos[i] = xpos[nn]; 04494 lambda[i] = lambda[nn]; 04495 ilambda[i] = ilambda[nn]; 04496 } 04497 04498 04499 /* 04500 * Are some wavelengths missing? Recover them. 04501 */ 04502 04503 for (i = 1; i < n; i++) { 04504 gap = ilambda[i] - ilambda[i-1]; 04505 for (j = 1; j < gap; j++) { 04506 04507 if (j == 1) { 04508 04509 /* 04510 * Determine the local dispersion from the current pair of peaks 04511 */ 04512 04513 disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]); 04514 } 04515 04516 /* 04517 * With this, find the expected position of the missing 04518 * peak by linear interpolation. 04519 */ 04520 04521 hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp; 04522 04523 /* 04524 * Is there a peak at that position? Here a peak from the 04525 * original list is searched, that is closer than 2 pixels 04526 * to the expected position. If it is found, insert it at 04527 * the current position on the list of identified peaks, 04528 * and leave immediately the loop (taking the new position 04529 * for the following linear interpolation, in case more 04530 * than one peak is missing in the current interval). 04531 * If it is not found, stay in the loop, looking for 04532 * the following missing peaks in this interval. 04533 */ 04534 04535 found = 0; 04536 for (k = 0; k < npeaks; k++) { 04537 if (fabs(peak[k] - hi_start) < 2) { 04538 for (l = n; l > i; l--) { 04539 xpos[l] = xpos[l-1]; 04540 lambda[l] = lambda[l-1]; 04541 ilambda[l] = ilambda[l-1]; 04542 } 04543 xpos[i] = peak[k]; 04544 lambda[i] = line[ilambda[i-1] + j]; 04545 ilambda[i] = ilambda[i-1] + j; 04546 ++n; 04547 found = 1; 04548 break; 04549 } 04550 } 04551 if (found) 04552 break; 04553 } 04554 } 04555 04556 04557 /* 04558 * Try to extrapolate forward 04559 */ 04560 04561 found = 1; 04562 while (ilambda[n-1] < nlines - 1 && found) { 04563 04564 /* 04565 * Determine the local dispersion from the last pair of 04566 * identified peaks 04567 */ 04568 04569 if (n > 1) 04570 disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]); 04571 else 04572 disp = 0.0; 04573 04574 if (disp > max_disp || disp < min_disp) 04575 break; 04576 04577 04578 /* 04579 * With this, find the expected position of the missing 04580 * peak by linear interpolation. 04581 */ 04582 04583 hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp; 04584 04585 /* 04586 * Is there a peak at that position? Here a peak from the 04587 * original list is searched, that is closer than 6 pixels 04588 * to the expected position. If it is found, insert it at 04589 * the end of the list of identified peaks. If it is not 04590 * found, leave the loop. 04591 */ 04592 04593 found = 0; 04594 min = fabs(peak[0] - hi_start); 04595 minpos = 0; 04596 for (k = 1; k < npeaks; k++) { 04597 if (min > fabs(peak[k] - hi_start)) { 04598 min = fabs(peak[k] - hi_start); 04599 minpos = k; 04600 } 04601 } 04602 if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) { 04603 xpos[n] = peak[minpos]; 04604 lambda[n] = line[ilambda[n-1] + 1]; 04605 ilambda[n] = ilambda[n-1] + 1; 04606 ++n; 04607 found = 1; 04608 } 04609 } 04610 04611 04612 /* 04613 * Try to extrapolate backward 04614 */ 04615 04616 found = 1; 04617 while (ilambda[0] > 0 && found) { 04618 04619 /* 04620 * Determine the local dispersion from the first pair of 04621 * identified peaks 04622 */ 04623 04624 disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]); 04625 04626 if (disp > max_disp || disp < min_disp) 04627 break; 04628 04629 04630 /* 04631 * With this, find the expected position of the missing 04632 * peak by linear interpolation. 04633 */ 04634 04635 hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp; 04636 04637 04638 /* 04639 * Is there a peak at that position? Here a peak from the 04640 * original list is searched, that is closer than 6 pixels 04641 * to the expected position. If it is found, insert it at 04642 * the beginning of the list of identified peaks. If it is not 04643 * found, leave the loop. 04644 */ 04645 04646 found = 0; 04647 min = fabs(peak[0] - hi_start); 04648 minpos = 0; 04649 for (k = 1; k < npeaks; k++) { 04650 if (min > fabs(peak[k] - hi_start)) { 04651 min = fabs(peak[k] - hi_start); 04652 minpos = k; 04653 } 04654 } 04655 if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) { 04656 for (j = n; j > 0; j--) { 04657 xpos[j] = xpos[j-1]; 04658 lambda[j] = lambda[j-1]; 04659 ilambda[j] = ilambda[j-1]; 04660 } 04661 xpos[0] = peak[minpos]; 04662 lambda[0] = line[ilambda[0] - 1]; 04663 ilambda[0] = ilambda[0] - 1; 04664 ++n; 04665 found = 1; 04666 } 04667 } 04668 } 04669 04670 04671 /* 04672 * At this point all peaks are processed. Free memory, and return 04673 * the result. 04674 */ 04675 04676 /************************************************+ 04677 for (i = 0; i < npeaks; i++) { 04678 printf("Peak %d:\n ", i); 04679 for (j = 0; j < nident[i]; j++) 04680 printf("%.2f, ", line[ident[i][j]]); 04681 printf("\n"); 04682 } 04683 04684 printf("\n"); 04685 04686 for (i = 0; i < n; i++) 04687 printf("%.2f, %.2f\n", xpos[i], lambda[i]); 04688 +************************************************/ 04689 for (i = 0; i < npeaks; i++) 04690 cpl_free(ident[i]); 04691 cpl_free(ident); 04692 cpl_free(nident); 04693 cpl_free(lident); 04694 cpl_free(ilambda); 04695 cpl_free(tmp_xpos); 04696 cpl_free(tmp_lambda); 04697 cpl_free(tmp_ilambda); 04698 cpl_free(peak_lo); 04699 cpl_free(flag); 04700 cpl_free(seq_length); 04701 cpl_free(peak_hi); 04702 04703 if (n == 0) { 04704 cpl_free(xpos); 04705 cpl_free(lambda); 04706 return NULL; 04707 } 04708 04709 return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 04710 cpl_vector_wrap(n, lambda)); 04711 } 04712 04713 04731 /* 04732 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 04733 double refwave, double pixel) 04734 { 04735 double yellow; 04736 double cpixel; 04737 double tolerance = 0.02; 04738 int max_iter = 20; 04739 int iter = 0; 04740 04741 04742 if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel) 04743 return 0.0; 04744 04745 if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel) 04746 return 0.0; 04747 04748 yellow = (blue + red) / 2; 04749 04750 cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL); 04751 04752 while (fabs(cpixel - pixel) > tolerance && iter < max_iter) { 04753 04754 if (cpixel > pixel) 04755 red = yellow; 04756 else 04757 blue = yellow; 04758 04759 yellow = (blue + red) / 2; 04760 cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL); 04761 04762 iter++; 04763 04764 } 04765 04766 return yellow; 04767 04768 } 04769 */ 04770 04771 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 04772 double refwave, double pixel) 04773 { 04774 double yellow; 04775 double coeff; 04776 cpl_size zero = 0; 04777 04778 if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel) 04779 return 0.0; 04780 04781 if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel) 04782 return 0.0; 04783 04784 yellow = (blue + red) / 2 - refwave; 04785 04786 coeff = cpl_polynomial_get_coeff(ids, &zero); 04787 cpl_polynomial_set_coeff(ids, &zero, coeff - pixel); 04788 04789 cpl_polynomial_solve_1d(ids, yellow, &yellow, 1); 04790 if (cpl_error_get_code() != CPL_ERROR_NONE) { 04791 cpl_error_reset(); 04792 return 0.0; 04793 } 04794 04795 cpl_polynomial_set_coeff(ids, &zero, coeff); 04796 04797 return yellow + refwave; 04798 04799 } 04800 04826 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 04827 double reject, int minlines, 04828 int *nlines, double *err) 04829 { 04830 const char *func = "mos_poly_wav2pix"; 04831 04832 cpl_bivector *pixwav2; 04833 cpl_vector *wavel; 04834 cpl_vector *pixel; 04835 double *d_wavel; 04836 double *d_pixel; 04837 double pixpos; 04838 int fitlines; 04839 int rejection = 0; 04840 int i, j; 04841 04842 cpl_polynomial *ids; 04843 04844 04845 *nlines = 0; 04846 *err = 0; 04847 04848 if (pixwav == NULL) { 04849 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 04850 return NULL; 04851 } 04852 04853 fitlines = cpl_bivector_get_size(pixwav); 04854 04855 if (fitlines < minlines) { 04856 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 04857 return NULL; 04858 } 04859 04860 04861 /* 04862 * If outliers rejection was requested, allocate a working 04863 * vector (that can be modified as soon as outliers are removed) 04864 */ 04865 04866 if (reject > 0.0) 04867 rejection = 1; 04868 04869 if (rejection) 04870 pixwav2 = cpl_bivector_duplicate(pixwav); 04871 else 04872 pixwav2 = pixwav; 04873 04874 04875 /* 04876 * The single vectors are extracted just because the fitting routine 04877 * requires it 04878 */ 04879 04880 pixel = cpl_bivector_get_x(pixwav2); 04881 wavel = cpl_bivector_get_y(pixwav2); 04882 04883 04884 /* 04885 * Get rid of the wrapper, in case of duplication 04886 */ 04887 04888 if (rejection) 04889 cpl_bivector_unwrap_vectors(pixwav2); 04890 04891 04892 /* 04893 * Here begins the iterative fit of identified lines 04894 */ 04895 04896 while (fitlines >= minlines) { 04897 04898 ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err); 04899 *err = sqrt(*err); 04900 04901 if (ids == NULL) { 04902 cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message()); 04903 cpl_msg_debug(func, "Fitting IDS"); 04904 cpl_error_set_where(func); 04905 if (rejection) { 04906 cpl_vector_delete(wavel); 04907 cpl_vector_delete(pixel); 04908 } 04909 return NULL; 04910 } 04911 04912 if (rejection) { 04913 04914 04915 /* 04916 * Now work directly with the vector data buffers... 04917 */ 04918 04919 d_pixel = cpl_vector_unwrap(pixel); 04920 d_wavel = cpl_vector_unwrap(wavel); 04921 04922 for (i = 0, j = 0; i < fitlines; i++) { 04923 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL); 04924 if (fabs(pixpos - d_pixel[i]) < reject) { 04925 d_pixel[j] = d_pixel[i]; 04926 d_wavel[j] = d_wavel[i]; 04927 j++; 04928 } 04929 } 04930 04931 if (j == fitlines) { /* No rejection in last iteration */ 04932 cpl_free(d_wavel); 04933 cpl_free(d_pixel); 04934 *nlines = fitlines; 04935 return ids; 04936 } 04937 else { /* Some lines were rejected */ 04938 fitlines = j; 04939 cpl_polynomial_delete(ids); 04940 if (fitlines >= minlines) { 04941 pixel = cpl_vector_wrap(fitlines, d_pixel); 04942 wavel = cpl_vector_wrap(fitlines, d_wavel); 04943 } 04944 else { /* Too few lines: failure */ 04945 cpl_free(d_wavel); 04946 cpl_free(d_pixel); 04947 cpl_error_set(func, CPL_ERROR_CONTINUE); 04948 return NULL; 04949 } 04950 } 04951 } 04952 else { 04953 *nlines = fitlines; 04954 return ids; /* Exit at first iteration if no rejection */ 04955 } 04956 } 04957 04958 return ids; /* To avoid compiler warnings */ 04959 } 04960 04961 04986 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order, 04987 double reject, int minlines, 04988 int *nlines, double *err) 04989 { 04990 04991 cpl_bivector *wavpix; 04992 cpl_vector *wavel; 04993 cpl_vector *pixel; 04994 04995 cpl_polynomial *dds; 04996 04997 04998 /* 04999 * Swap vectors in bivector, in order to reuse mos_poly_wav2pix() 05000 */ 05001 05002 pixel = cpl_bivector_get_x(pixwav); 05003 wavel = cpl_bivector_get_y(pixwav); 05004 05005 wavpix = cpl_bivector_wrap_vectors(wavel, pixel); 05006 05007 dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err); 05008 05009 cpl_bivector_unwrap_vectors(wavpix); 05010 05011 return dds; 05012 05013 } 05014 05015 05038 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 05039 cpl_vector *lines, cpl_polynomial *ids, 05040 double refwave, int sradius) 05041 { 05042 const char *func = "mos_find_peaks"; 05043 05044 double *data; 05045 double *d_pixel; 05046 double *d_wavel; 05047 float pos; 05048 int nlines; 05049 int pixel; 05050 int i, j; 05051 05052 05053 if (spectrum == NULL || lines == NULL || ids == NULL) { 05054 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05055 return NULL; 05056 } 05057 05058 nlines = cpl_vector_get_size(lines); 05059 05060 if (sradius < 1 || length < 2*sradius+1 || nlines < 1) { 05061 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05062 return NULL; 05063 } 05064 05065 d_wavel = cpl_malloc(nlines * sizeof(double)); 05066 d_pixel = cpl_malloc(nlines * sizeof(double)); 05067 05068 data = cpl_vector_get_data(lines); 05069 05070 for (i = 0, j = 0; i < nlines; i++) { 05071 pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5; 05072 if (pixel - sradius < 0 || pixel + sradius >= length) 05073 continue; 05074 if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) { 05075 pos += pixel - sradius; 05076 d_pixel[j] = pos; 05077 d_wavel[j] = data[i]; 05078 j++; 05079 } 05080 } 05081 05082 if (j > 0) { 05083 return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel), 05084 cpl_vector_wrap(j, d_wavel)); 05085 } 05086 else { 05087 cpl_free(d_wavel); 05088 cpl_free(d_pixel); 05089 cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT); 05090 return NULL; 05091 } 05092 } 05093 05094 05212 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image, 05213 cpl_vector *lines, 05214 double dispersion, float level, 05215 int sradius, int order, 05216 double reject, double refwave, 05217 double *wavestart, double *waveend, 05218 int *nlines, double *error, 05219 cpl_table *idscoeff, 05220 cpl_image *calibration, 05221 cpl_image *residuals, 05222 cpl_table *restable, 05223 cpl_mask *refmask) 05224 { 05225 05226 const char *func = "mos_wavelength_calibration_raw"; 05227 05228 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 05229 /* Max order is 5 */ 05230 05231 double tolerance = 20.0; /* Probably forever... */ 05232 int step = 10; /* Compute restable every "step" rows */ 05233 05234 char name[MAX_COLNAME]; 05235 cpl_image *resampled; 05236 cpl_bivector *output; 05237 cpl_bivector *new_output; 05238 cpl_vector *peaks; 05239 cpl_vector *wavel; 05240 cpl_polynomial *ids; 05241 cpl_polynomial *lin; 05242 cpl_matrix *kernel; 05243 double ids_err; 05244 double max_disp, min_disp; 05245 double *line; 05246 double firstLambda, lastLambda, lambda; 05247 double value, wave, pixe; 05248 cpl_binary *mdata; 05249 const float *sdata; 05250 float *rdata; 05251 float *idata; 05252 float *ddata; 05253 float v1, v2, vi; 05254 float fpixel; 05255 int *have_it; 05256 int pixstart, pixend; 05257 int extrapolation; 05258 int nref; 05259 int nl, nx, ny, pixel; 05260 int countLines, usedLines; 05261 int uorder; 05262 int in, first, last; 05263 int width, uradius; 05264 int i, j; 05265 cpl_size k; 05266 05267 05268 if (dispersion == 0.0) { 05269 cpl_msg_error(func, "The expected dispersion (A/pixel) must be given"); 05270 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05271 return NULL; 05272 } 05273 05274 if (dispersion < 0.0) { 05275 cpl_msg_error(func, "The expected dispersion must be positive"); 05276 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05277 return NULL; 05278 } 05279 05280 max_disp = dispersion + dispersion * tolerance / 100; 05281 min_disp = dispersion - dispersion * tolerance / 100; 05282 05283 if (order < 1) { 05284 cpl_msg_error(func, "The order of the fitting polynomial " 05285 "must be at least 1"); 05286 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05287 return NULL; 05288 } 05289 05290 if (image == NULL || lines == NULL) { 05291 cpl_msg_error(func, "Both spectral exposure and reference line " 05292 "catalog are required in input"); 05293 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05294 return NULL; 05295 } 05296 05297 nx = cpl_image_get_size_x(image); 05298 ny = cpl_image_get_size_y(image); 05299 sdata = cpl_image_get_data_float_const(image); 05300 05301 nref = cpl_vector_get_size(lines); 05302 line = cpl_vector_get_data(lines); 05303 05304 if (*wavestart < 1.0 && *waveend < 1.0) { 05305 firstLambda = line[0]; 05306 lastLambda = line[nref-1]; 05307 extrapolation = (lastLambda - firstLambda) / 10; 05308 firstLambda -= extrapolation; 05309 lastLambda += extrapolation; 05310 *wavestart = firstLambda; 05311 *waveend = lastLambda; 05312 } 05313 else { 05314 firstLambda = *wavestart; 05315 lastLambda = *waveend; 05316 } 05317 05318 nl = (lastLambda - firstLambda) / dispersion; 05319 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 05320 rdata = cpl_image_get_data_float(resampled); 05321 05322 if (calibration) 05323 idata = cpl_image_get_data_float(calibration); 05324 05325 if (residuals) 05326 ddata = cpl_image_get_data_float(residuals); 05327 05328 if (idscoeff) 05329 for (j = 0; j <= order; j++) 05330 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 05331 05332 if (restable) { 05333 cpl_table_set_size(restable, nref); 05334 cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE); 05335 cpl_table_copy_data_double(restable, "wavelength", line); 05336 for (i = 0; i < ny; i += step) { 05337 snprintf(name, MAX_COLNAME, "r%d", i); 05338 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05339 snprintf(name, MAX_COLNAME, "d%d", i); 05340 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05341 snprintf(name, MAX_COLNAME, "p%d", i); 05342 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05343 } 05344 } 05345 05346 /* 05347 * Here is the real thing: detecting and identifying peaks, 05348 * and then fit the transformation from wavelength to pixel 05349 * and from pixel to wavelength. 05350 */ 05351 05352 for (i = 0; i < ny; i++) { 05353 width = mos_lines_width(sdata + i*nx, nx); 05354 if (sradius > 0) { 05355 if (width > sradius) { 05356 uradius = width; 05357 } 05358 else { 05359 uradius = sradius; 05360 } 05361 } 05362 if (width < 5) 05363 width = 5; 05364 peaks = mos_peak_candidates(sdata + i*nx, nx, level, width); 05365 if (peaks) { 05366 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width); 05367 } 05368 if (peaks) { 05369 output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05); 05370 if (output) { 05371 countLines = cpl_bivector_get_size(output); 05372 if (countLines < 4) { 05373 cpl_bivector_delete(output); 05374 cpl_vector_delete(peaks); 05375 if (nlines) 05376 nlines[i] = 0; 05377 if (error) 05378 error[i] = 0.0; 05379 continue; 05380 } 05381 05382 /* 05383 * Set reference wavelength as zero point 05384 */ 05385 05386 wavel = cpl_bivector_get_y(output); 05387 cpl_vector_subtract_scalar(wavel, refwave); 05388 05389 uorder = countLines / 2 - 1; 05390 if (uorder > order) 05391 uorder = order; 05392 05393 /* This part is now commented out. In case the first-guess iteration 05394 * was requested, the first fit was made with a lower polynomial degree: 05395 * more robust, and still accurate enough to be used as a first-guess. 05396 05397 if (sradius > 0 && uorder > 2) 05398 --uorder; 05399 05400 * End of commented part */ 05401 05402 ids = mos_poly_wav2pix(output, uorder, reject, 05403 2 * (uorder + 1), &usedLines, 05404 &ids_err); 05405 05406 if (ids == NULL) { 05407 cpl_bivector_delete(output); 05408 cpl_vector_delete(peaks); 05409 if (nlines) 05410 nlines[i] = 0; 05411 if (error) 05412 error[i] = 0.0; 05413 cpl_error_reset(); 05414 continue; 05415 } 05416 05417 if (idscoeff) { 05418 05419 /* 05420 * Write it anyway, even in case a first-guess based 05421 * solution will be searched afterwards: in case of 05422 * failure, the "blind" solution is kept. 05423 */ 05424 05425 for (k = 0; k <= order; k++) { 05426 if (k > uorder) { 05427 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 05428 } 05429 else { 05430 cpl_table_set_double(idscoeff, clab[k], i, 05431 cpl_polynomial_get_coeff(ids, &k)); 05432 } 05433 } 05434 } 05435 05436 if (sradius > 0) { 05437 05438 /* 05439 * Use ids as a first-guess 05440 */ 05441 05442 new_output = mos_find_peaks(sdata + i*nx, nx, lines, 05443 ids, refwave, uradius); 05444 05445 if (new_output) { 05446 cpl_bivector_delete(output); 05447 output = new_output; 05448 } 05449 else 05450 cpl_error_reset(); 05451 05452 05453 cpl_polynomial_delete(ids); 05454 05455 countLines = cpl_bivector_get_size(output); 05456 05457 if (countLines < 4) { 05458 cpl_bivector_delete(output); 05459 cpl_vector_delete(peaks); 05460 05461 /* 05462 * With the following code a decision is taken: 05463 * if using the first-guess gives no results, 05464 * then also the "blind" solution is rejected. 05465 */ 05466 05467 if (nlines) 05468 nlines[i] = 0; 05469 if (error) 05470 error[i] = 0.0; 05471 if (idscoeff) 05472 for (k = 0; k <= order; k++) 05473 cpl_table_set_invalid(idscoeff, clab[k], i); 05474 continue; 05475 } 05476 05477 wavel = cpl_bivector_get_y(output); 05478 cpl_vector_subtract_scalar(wavel, refwave); 05479 05480 uorder = countLines / 2 - 1; 05481 if (uorder > order) 05482 uorder = order; 05483 05484 ids = mos_poly_wav2pix(output, uorder, reject, 05485 2 * (uorder + 1), &usedLines, 05486 &ids_err); 05487 05488 if (ids == NULL) { 05489 cpl_bivector_delete(output); 05490 cpl_vector_delete(peaks); 05491 05492 /* 05493 * With the following code a decision is taken: 05494 * if using the first-guess gives no results, 05495 * then also the "blind" solution is rejected. 05496 */ 05497 05498 if (nlines) 05499 nlines[i] = 0; 05500 if (error) 05501 error[i] = 0.0; 05502 if (idscoeff) 05503 for (k = 0; k <= order; k++) 05504 cpl_table_set_invalid(idscoeff, clab[k], i); 05505 cpl_error_reset(); 05506 continue; 05507 } 05508 05509 if (idscoeff) { 05510 for (k = 0; k <= order; k++) { 05511 if (k > uorder) { 05512 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 05513 } 05514 else { 05515 cpl_table_set_double(idscoeff, clab[k], i, 05516 cpl_polynomial_get_coeff(ids, &k)); 05517 } 05518 } 05519 } 05520 05521 } /* End of "use ids as a first-guess" */ 05522 05523 if (nlines) 05524 nlines[i] = usedLines; 05525 if (error) 05526 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 05527 05528 pixstart = cpl_polynomial_eval_1d(ids, 05529 cpl_bivector_get_y_data(output)[0], NULL); 05530 pixend = cpl_polynomial_eval_1d(ids, 05531 cpl_bivector_get_y_data(output)[countLines-1], NULL); 05532 extrapolation = (pixend - pixstart) / 5; 05533 pixstart -= extrapolation; 05534 pixend += extrapolation; 05535 if (pixstart < 0) 05536 pixstart = 0; 05537 if (pixend > nx) 05538 pixend = nx; 05539 05540 /* 05541 * Wavelength calibrated image (if requested): 05542 */ 05543 05544 if (calibration) { 05545 for (j = pixstart; j < pixend; j++) { 05546 (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 05547 lastLambda, refwave, 05548 j); 05549 } 05550 } 05551 05552 /* 05553 * Resampled image: 05554 */ 05555 05556 for (j = 0; j < nl; j++) { 05557 lambda = firstLambda + j * dispersion; 05558 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 05559 NULL); 05560 pixel = fpixel; 05561 if (pixel >= 0 && pixel < nx-1) { 05562 v1 = (sdata + i*nx)[pixel]; 05563 v2 = (sdata + i*nx)[pixel+1]; 05564 vi = v1 + (v2-v1)*(fpixel-pixel); 05565 (rdata + i*nl)[j] = vi; 05566 } 05567 } 05568 05569 /* 05570 * Residuals image 05571 */ 05572 05573 if (residuals || (restable && !(i%step))) { 05574 if (restable && !(i%step)) { 05575 lin = cpl_polynomial_new(1); 05576 for (k = 0; k < 2; k++) 05577 cpl_polynomial_set_coeff(lin, &k, 05578 cpl_polynomial_get_coeff(ids, &k)); 05579 } 05580 for (j = 0; j < countLines; j++) { 05581 pixe = cpl_bivector_get_x_data(output)[j]; 05582 wave = cpl_bivector_get_y_data(output)[j]; 05583 value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL); 05584 if (residuals) { 05585 pixel = pixe + 0.5; 05586 (ddata + i*nx)[pixel] = value; 05587 } 05588 if (restable && !(i%step)) { 05589 for (k = 0; k < nref; k++) { 05590 if (fabs(line[k] - refwave - wave) < 0.1) { 05591 snprintf(name, MAX_COLNAME, "r%d", i); 05592 cpl_table_set_double(restable, name, 05593 k, value); 05594 value = pixe 05595 - cpl_polynomial_eval_1d(lin, wave, 05596 NULL); 05597 snprintf(name, MAX_COLNAME, "d%d", i); 05598 cpl_table_set_double(restable, name, 05599 k, value); 05600 snprintf(name, MAX_COLNAME, "p%d", i); 05601 cpl_table_set_double(restable, name, 05602 k, pixe); 05603 break; 05604 } 05605 } 05606 } 05607 } 05608 if (restable && !(i%step)) { 05609 cpl_polynomial_delete(lin); 05610 } 05611 } 05612 05613 /* 05614 * Mask at reference wavelength 05615 */ 05616 05617 if (refmask) { 05618 mdata = cpl_mask_get_data(refmask); 05619 pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5; 05620 if (pixel - 1 >= 0 && pixel + 1 < nx) { 05621 mdata[pixel-1 + i*nx] = CPL_BINARY_1; 05622 mdata[pixel + i*nx] = CPL_BINARY_1; 05623 mdata[pixel+1 + i*nx] = CPL_BINARY_1; 05624 } 05625 } 05626 05627 cpl_polynomial_delete(ids); 05628 cpl_bivector_delete(output); 05629 } 05630 cpl_vector_delete(peaks); 05631 } 05632 } 05633 05634 if (refmask) { 05635 kernel = cpl_matrix_new(3, 3); 05636 cpl_matrix_set(kernel, 0, 1, 1.0); 05637 cpl_matrix_set(kernel, 1, 1, 1.0); 05638 cpl_matrix_set(kernel, 2, 1, 1.0); 05639 05640 cpl_mask_dilation(refmask, kernel); 05641 cpl_mask_erosion(refmask, kernel); 05642 cpl_mask_erosion(refmask, kernel); 05643 cpl_mask_dilation(refmask, kernel); 05644 05645 cpl_matrix_delete(kernel); 05646 05647 /* 05648 * Fill possible gaps 05649 */ 05650 05651 mdata = cpl_mask_get_data(refmask); 05652 have_it = cpl_calloc(ny, sizeof(int)); 05653 05654 for (i = 0; i < ny; i++, mdata += nx) { 05655 for (j = 0; j < nx; j++) { 05656 if (mdata[j] == CPL_BINARY_1) { 05657 have_it[i] = j; 05658 break; 05659 } 05660 } 05661 } 05662 05663 mdata = cpl_mask_get_data(refmask); 05664 in = 0; 05665 first = last = 0; 05666 05667 for (i = 0; i < ny; i++) { 05668 if (have_it[i]) { 05669 if (!in) { 05670 in = 1; 05671 if (first) { 05672 last = i; 05673 if (abs(have_it[first] - have_it[last]) < 3) { 05674 for (j = first; j < last; j++) { 05675 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1; 05676 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1; 05677 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1; 05678 } 05679 } 05680 } 05681 } 05682 } 05683 else { 05684 if (in) { 05685 in = 0; 05686 first = i - 1; 05687 } 05688 } 05689 } 05690 05691 cpl_free(have_it); 05692 05693 } 05694 05695 /* 05696 for (i = 0; i < ny; i++) { 05697 if (nlines[i] == 0) { 05698 for (k = 0; k <= order; k++) { 05699 cpl_table_set_invalid(idscoeff, clab[k], i); 05700 } 05701 } 05702 } 05703 */ 05704 05705 return resampled; 05706 } 05707 05708 05730 /* 05731 cpl_table *mos_locate_spectra_bis(cpl_mask *mask) 05732 { 05733 const char *func = "mos_locate_spectra_bis"; 05734 05735 cpl_apertures *slits; 05736 cpl_image *labimage; 05737 cpl_image *refimage; 05738 cpl_binary *mdata; 05739 cpl_table *slitpos; 05740 cpl_propertylist *sort_col; 05741 int nslits; 05742 int *have_it; 05743 int in, first, last; 05744 int i, j; 05745 05746 05747 if (mask == NULL) { 05748 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05749 return NULL; 05750 } 05751 05752 nx = cpl_mask_get_size_x(mask); 05753 ny = cpl_mask_get_size_y(mask); 05754 05755 mdata = cpl_mask_get_data(refmask); 05756 have_it = cpl_calloc(ny, sizeof(int)); 05757 05758 for (i = 0; i < ny; i++, mdata += nx) { 05759 for (j = 0; j < nx; j++) { 05760 if (mdata[j] == CPL_BINARY_1) { 05761 have_it[i] = j + 1; 05762 break; 05763 } 05764 } 05765 } 05766 05767 mdata = cpl_mask_get_data(refmask); 05768 in = 0; 05769 first = last = 0; 05770 nslits = 0; 05771 05772 for (i = 0; i < ny; i++) { 05773 if (have_it[i]) { 05774 if (in) { 05775 if (i) { 05776 if (abs(have_it[i] - have_it[i-1]) > 3) { 05777 nslits++; 05778 } 05779 } 05780 } 05781 else { 05782 in = 1; 05783 nslits++; 05784 } 05785 } 05786 else { 05787 if (in) { 05788 in = 0; 05789 } 05790 } 05791 } 05792 } 05793 */ 05794 05795 05817 cpl_table *mos_locate_spectra(cpl_mask *mask) 05818 { 05819 const char *func = "mos_locate_spectra"; 05820 05821 cpl_apertures *slits; 05822 cpl_image *labimage; 05823 cpl_image *refimage; 05824 cpl_table *slitpos; 05825 cpl_propertylist *sort_col; 05826 cpl_size nslits; 05827 int i; 05828 05829 05830 if (mask == NULL) { 05831 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05832 return NULL; 05833 } 05834 05835 labimage = cpl_image_labelise_mask_create(mask, &nslits); 05836 05837 if (nslits < 1) { 05838 cpl_image_delete(labimage); 05839 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05840 return NULL; 05841 } 05842 05843 refimage = cpl_image_new_from_mask(mask); 05844 05845 slits = cpl_apertures_new_from_image(refimage, labimage); 05846 05847 cpl_image_delete(labimage); 05848 cpl_image_delete(refimage); 05849 05850 nslits = cpl_apertures_get_size(slits); /* Overwriting nslits - safer! */ 05851 if (nslits < 1) { 05852 cpl_apertures_delete(slits); 05853 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05854 return NULL; 05855 } 05856 05857 slitpos = cpl_table_new(nslits); 05858 cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE); 05859 cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE); 05860 cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE); 05861 cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE); 05862 cpl_table_set_column_unit(slitpos, "xtop", "pixel"); 05863 cpl_table_set_column_unit(slitpos, "ytop", "pixel"); 05864 cpl_table_set_column_unit(slitpos, "xbottom", "pixel"); 05865 cpl_table_set_column_unit(slitpos, "ybottom", "pixel"); 05866 05867 for (i = 0; i < nslits; i++) { 05868 cpl_table_set_double(slitpos, "xtop", i, 05869 cpl_apertures_get_top_x(slits, i+1) - 1); 05870 cpl_table_set_double(slitpos, "ytop", i, 05871 cpl_apertures_get_top(slits, i+1)); 05872 cpl_table_set_double(slitpos, "xbottom", i, 05873 cpl_apertures_get_bottom_x(slits, i+1) - 1); 05874 cpl_table_set_double(slitpos, "ybottom", i, 05875 cpl_apertures_get_bottom(slits, i+1)); 05876 } 05877 05878 cpl_apertures_delete(slits); 05879 05880 sort_col = cpl_propertylist_new(); 05881 cpl_propertylist_append_bool(sort_col, "ytop", 1); 05882 cpl_table_sort(slitpos, sort_col); 05883 cpl_propertylist_delete(sort_col); 05884 05885 return slitpos; 05886 05887 } 05888 05889 05905 cpl_error_code mos_validate_slits(cpl_table *slits) 05906 { 05907 const char *func = "mos_validate_slits"; 05908 05909 05910 if (slits == NULL) 05911 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05912 05913 if (1 != cpl_table_has_column(slits, "xtop")) 05914 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05915 05916 if (1 != cpl_table_has_column(slits, "ytop")) 05917 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05918 05919 if (1 != cpl_table_has_column(slits, "xbottom")) 05920 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05921 05922 if (1 != cpl_table_has_column(slits, "ybottom")) 05923 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05924 05925 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop")) 05926 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05927 05928 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop")) 05929 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05930 05931 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom")) 05932 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05933 05934 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom")) 05935 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05936 05937 return CPL_ERROR_NONE; 05938 } 05939 05940 05969 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny) 05970 { 05971 const char *func = "mos_rotate_slits"; 05972 05973 cpl_error_code error; 05974 char aux_name[] = "_0"; 05975 int i; 05976 05977 05978 rotation %= 4; 05979 if (rotation < 0) 05980 rotation += 4; 05981 05982 if (rotation == 0) 05983 return CPL_ERROR_NONE; 05984 05985 error = mos_validate_slits(slits); 05986 if (error) 05987 return cpl_error_set(func, error); 05988 05989 if (rotation == 1 || rotation == 3) { 05990 05991 /* 05992 * Swap x and y column names 05993 */ 05994 05995 for (i = 0; i < 77; i++) 05996 if (1 == cpl_table_has_column(slits, aux_name)) 05997 aux_name[1]++; 05998 if (1 == cpl_table_has_column(slits, aux_name)) 05999 return cpl_error_set(func, CPL_ERROR_CONTINUE); 06000 cpl_table_name_column(slits, "xtop", aux_name); 06001 cpl_table_name_column(slits, "ytop", "xtop"); 06002 cpl_table_name_column(slits, aux_name, "ytop"); 06003 cpl_table_name_column(slits, "xbottom", aux_name); 06004 cpl_table_name_column(slits, "ybottom", "xbottom"); 06005 cpl_table_name_column(slits, aux_name, "ybottom"); 06006 } 06007 06008 if (rotation == 1 || rotation == 2) { 06009 cpl_table_multiply_scalar(slits, "xtop", -1.0); 06010 cpl_table_multiply_scalar(slits, "xbottom", -1.0); 06011 cpl_table_add_scalar(slits, "xtop", nx); 06012 cpl_table_add_scalar(slits, "xbottom", nx); 06013 } 06014 06015 if (rotation == 3 || rotation == 2) { 06016 cpl_table_multiply_scalar(slits, "ytop", -1.0); 06017 cpl_table_multiply_scalar(slits, "ybottom", -1.0); 06018 cpl_table_add_scalar(slits, "ytop", ny); 06019 cpl_table_add_scalar(slits, "ybottom", ny); 06020 } 06021 06022 return CPL_ERROR_NONE; 06023 } 06024 06025 06083 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits, 06084 cpl_table *global) 06085 { 06086 cpl_array *top_ident = NULL;; 06087 cpl_array *bot_ident = NULL;; 06088 cpl_matrix *mdata; 06089 cpl_matrix *mpattern; 06090 cpl_matrix *top_data; 06091 cpl_matrix *top_pattern; 06092 cpl_matrix *top_mdata; 06093 cpl_matrix *top_mpattern; 06094 cpl_matrix *bot_data; 06095 cpl_matrix *bot_pattern; 06096 cpl_matrix *bot_mdata; 06097 cpl_matrix *bot_mpattern; 06098 cpl_propertylist *sort_col; 06099 double *xtop; 06100 double *ytop; 06101 double *xmtop; 06102 double *ymtop; 06103 double *xbot; 06104 double *ybot; 06105 double *xmbot; 06106 double *ymbot; 06107 double top_scale, bot_scale; 06108 double angle, top_angle, bot_angle; 06109 double xmse, ymse; 06110 double xrms, top_xrms, bot_xrms; 06111 double yrms, top_yrms, bot_yrms; 06112 int nslits; 06113 int nmaskslits, use_pattern; 06114 int found_slits, found_slits_top, found_slits_bot; 06115 int degree; 06116 int i; 06117 cpl_table *positions; 06118 cpl_error_code error; 06119 06120 cpl_vector *point; 06121 double *dpoint; 06122 cpl_vector *xpos; 06123 cpl_vector *ypos; 06124 cpl_vector *xmpos; 06125 cpl_vector *ympos; 06126 cpl_bivector *mpos; 06127 cpl_polynomial *xpoly = NULL; 06128 cpl_polynomial *ypoly = NULL; 06129 cpl_polynomial *top_xpoly = NULL; 06130 cpl_polynomial *top_ypoly = NULL; 06131 cpl_polynomial *bot_xpoly = NULL; 06132 cpl_polynomial *bot_ypoly = NULL; 06133 06134 char *msg_multiplex = " "; 06135 06136 06137 error = mos_validate_slits(slits); 06138 if (error) { 06139 cpl_msg_error(cpl_func, "CCD slits table validation: %s", 06140 cpl_error_get_message()); 06141 cpl_error_set(cpl_func, error); 06142 return NULL; 06143 } 06144 06145 error = mos_validate_slits(maskslits); 06146 if (error) { 06147 cpl_msg_error(cpl_func, "Mask slits table validation: %s", 06148 cpl_error_get_message()); 06149 cpl_error_set(cpl_func, error); 06150 return NULL; 06151 } 06152 06153 if (1 != cpl_table_has_column(maskslits, "slit_id")) { 06154 cpl_msg_error(cpl_func, "Missing slits identifiers"); 06155 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 06156 return NULL; 06157 } 06158 06159 if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) { 06160 cpl_msg_error(cpl_func, "Wrong type used for slits identifiers"); 06161 cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE); 06162 return NULL; 06163 } 06164 06165 nslits = cpl_table_get_nrow(slits); 06166 nmaskslits = cpl_table_get_nrow(maskslits); 06167 06168 if (nslits == 0 || nmaskslits == 0) { 06169 cpl_msg_error(cpl_func, "Empty slits table"); 06170 cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT); 06171 return NULL; 06172 } 06173 06174 if (nslits > 25 && mos_multiplex < 0) { 06175 cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching..."); 06176 positions = mos_identify_slits_fast(slits, maskslits, global); 06177 if (positions == NULL) 06178 cpl_error_set_where(cpl_func); 06179 return positions; 06180 } 06181 06182 /* 06183 * Guarantee that both input tables are sorted in the same way 06184 */ 06185 06186 sort_col = cpl_propertylist_new(); 06187 cpl_propertylist_append_bool(sort_col, "ytop", 1); 06188 cpl_table_sort(slits, sort_col); 06189 cpl_table_sort(maskslits, sort_col); 06190 cpl_propertylist_delete(sort_col); 06191 06192 /* 06193 * First we handle all the special cases (too few slits...) 06194 */ 06195 06196 if (nslits < 3 && nmaskslits > nslits) { 06197 06198 /* 06199 * If there are just 1 or 2 slits on the CCD, and more on the 06200 * mask, the ambiguity cannot be solved, and an error is returned. 06201 * This is a case that must be solved with a first-guess relation 06202 * between mask and CCD. 06203 */ 06204 06205 if (nslits > 1) 06206 cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits " 06207 "with the %d mask slits: process will continue " 06208 "using the detected CCD slits positions", nslits, 06209 nmaskslits); 06210 else 06211 cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with " 06212 "the %d mask slits: process will continue using " 06213 "the detected CCD slit position", nmaskslits); 06214 return NULL; 06215 } 06216 06217 if (nmaskslits < 3 && nslits > nmaskslits) { 06218 06219 /* 06220 * If there are less than 3 slits on the mask the ambiguity cannot 06221 * be solved, and an error is returned. This is a case that must 06222 * be solved with a first-guess relation between mask and CCD. 06223 */ 06224 06225 cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with " 06226 "the %d mask slits: process will continue using " 06227 "the detected CCD slits positions", nslits, 06228 nmaskslits); 06229 return NULL; 06230 } 06231 06232 /* 06233 * Pattern matching related operations begin here. Two pattern 06234 * matching will be run, one based on the "top" and another one 06235 * based on the "bottom" slit coordinates. The one with the 06236 * smallest rms in the Y coordinate will be chosen. 06237 */ 06238 06239 xtop = cpl_table_get_data_double(slits, "xtop"); 06240 ytop = cpl_table_get_data_double(slits, "ytop"); 06241 xmtop = cpl_table_get_data_double(maskslits, "xtop"); 06242 ymtop = cpl_table_get_data_double(maskslits, "ytop"); 06243 06244 xbot = cpl_table_get_data_double(slits, "xbottom"); 06245 ybot = cpl_table_get_data_double(slits, "ybottom"); 06246 xmbot = cpl_table_get_data_double(maskslits, "xbottom"); 06247 ymbot = cpl_table_get_data_double(maskslits, "ybottom"); 06248 06249 top_data = cpl_matrix_new(2, nslits); 06250 top_pattern = cpl_matrix_new(2, nmaskslits); 06251 bot_data = cpl_matrix_new(2, nslits); 06252 bot_pattern = cpl_matrix_new(2, nmaskslits); 06253 06254 for (i = 0; i < nslits; i++) 06255 cpl_matrix_set(top_data, 0, i, xtop[i]); 06256 06257 for (i = 0; i < nslits; i++) 06258 cpl_matrix_set(top_data, 1, i, ytop[i]); 06259 06260 for (i = 0; i < nmaskslits; i++) 06261 cpl_matrix_set(top_pattern, 0, i, xmtop[i]); 06262 06263 for (i = 0; i < nmaskslits; i++) 06264 cpl_matrix_set(top_pattern, 1, i, ymtop[i]); 06265 06266 for (i = 0; i < nslits; i++) 06267 cpl_matrix_set(bot_data, 0, i, xbot[i]); 06268 06269 for (i = 0; i < nslits; i++) 06270 cpl_matrix_set(bot_data, 1, i, ybot[i]); 06271 06272 for (i = 0; i < nmaskslits; i++) 06273 cpl_matrix_set(bot_pattern, 0, i, xmbot[i]); 06274 06275 for (i = 0; i < nmaskslits; i++) 06276 cpl_matrix_set(bot_pattern, 1, i, ymbot[i]); 06277 06278 if (nmaskslits > nslits) 06279 use_pattern = nslits; 06280 else 06281 use_pattern = nmaskslits; 06282 06283 top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern, 06284 use_pattern, 0.0, 0.1, 5, &top_mdata, 06285 &top_mpattern, &top_scale, &top_angle); 06286 06287 bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern, 06288 use_pattern, 0.0, 0.1, 5, &bot_mdata, 06289 &bot_mpattern, &bot_scale, &bot_angle); 06290 06291 if (top_ident == NULL && bot_ident == NULL) { 06292 cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match " 06293 "the %d found CCD slits with the %d mask slits: " 06294 "process will continue using the detected CCD " 06295 "slits positions", nslits, nmaskslits); 06296 return NULL; 06297 } 06298 06299 found_slits_top = 0; 06300 found_slits_bot = 0; 06301 if (top_ident && bot_ident) { 06302 cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm", 06303 (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale)); 06304 cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees", 06305 (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle)); 06306 if (fabs(top_angle) < fabs(bot_angle)) 06307 angle = fabs(top_angle); 06308 else 06309 angle = fabs(bot_angle); 06310 found_slits_top = cpl_matrix_get_ncol(top_mdata); 06311 found_slits_bot = cpl_matrix_get_ncol(bot_mdata); 06312 } 06313 else if (top_ident) { 06314 cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale); 06315 cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle); 06316 angle = fabs(top_angle); 06317 found_slits_top = cpl_matrix_get_ncol(top_mdata); 06318 } 06319 else { 06320 cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale); 06321 cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle); 06322 angle = fabs(bot_angle); 06323 found_slits_bot = cpl_matrix_get_ncol(bot_mdata); 06324 } 06325 06326 cpl_array_delete(top_ident); 06327 cpl_array_delete(bot_ident); 06328 06329 if (angle > 4.0) { 06330 cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation " 06331 "angle is expected to be around zero. This match is " 06332 "rejected: the process will continue using the %d " 06333 "detected CCD slits positions", nslits); 06334 return NULL; 06335 } 06336 06337 found_slits = found_slits_top; 06338 if (found_slits < found_slits_bot) 06339 found_slits = found_slits_bot; /* Max value */ 06340 06341 if (found_slits < 4) { 06342 cpl_msg_warning(cpl_func, 06343 "Too few safely identified slits: %d out of %d " 06344 "candidates (%d expected). Process will continue " 06345 "using the detected CCD slits positions", found_slits, 06346 nslits, nmaskslits); 06347 return NULL; 06348 } 06349 06350 cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d " 06351 "candidates\n(%d expected)", found_slits, nslits, 06352 nmaskslits); 06353 06354 if (found_slits_top < 4) 06355 found_slits_top = 0; 06356 06357 if (found_slits_bot < 4) 06358 found_slits_bot = 0; 06359 06360 /* 06361 * Now for each set select the points of the identified slits, and fit 06362 * two bivariate polynomials to determine a first approximate relation 06363 * between positions on the mask and positions on the CCD. 06364 */ 06365 06366 for (i = 0; i < 2; i++) { 06367 if (i) { 06368 found_slits = found_slits_top; 06369 mdata = top_mdata; 06370 mpattern = top_mpattern; 06371 } 06372 else { 06373 found_slits = found_slits_bot; 06374 mdata = bot_mdata; 06375 mpattern = bot_mpattern; 06376 } 06377 06378 if (found_slits == 0) 06379 continue; 06380 else if (found_slits < 10) 06381 degree = 1; 06382 else 06383 degree = 2; 06384 06385 xpos = cpl_vector_wrap(found_slits, 06386 cpl_matrix_get_data(mdata) ); 06387 ypos = cpl_vector_wrap(found_slits, 06388 cpl_matrix_get_data(mdata) + found_slits); 06389 xmpos = cpl_vector_wrap(found_slits, 06390 cpl_matrix_get_data(mpattern) ); 06391 ympos = cpl_vector_wrap(found_slits, 06392 cpl_matrix_get_data(mpattern) + found_slits); 06393 mpos = cpl_bivector_wrap_vectors(xmpos, ympos); 06394 xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse); 06395 ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse); 06396 06397 cpl_bivector_unwrap_vectors(mpos); 06398 cpl_vector_unwrap(xpos); 06399 cpl_vector_unwrap(ypos); 06400 cpl_vector_unwrap(xmpos); 06401 cpl_vector_unwrap(ympos); 06402 cpl_matrix_delete(mdata); 06403 cpl_matrix_delete(mpattern); 06404 06405 if (i) { 06406 top_xpoly = xpoly; 06407 top_ypoly = ypoly; 06408 top_xrms = sqrt(xmse*2*degree/(found_slits - 1)); 06409 top_yrms = sqrt(ymse*2*degree/(found_slits - 1)); 06410 } 06411 else { 06412 bot_xpoly = xpoly; 06413 bot_ypoly = ypoly; 06414 bot_xrms = sqrt(xmse*2*degree/(found_slits - 1)); 06415 bot_yrms = sqrt(ymse*2*degree/(found_slits - 1)); 06416 } 06417 } 06418 06419 if (top_xpoly && bot_xpoly) { 06420 if (top_xrms < bot_xrms) { /* top X solution wins... */ 06421 xrms = top_xrms; 06422 xpoly = top_xpoly; 06423 cpl_polynomial_delete(bot_xpoly); 06424 } 06425 else { /* bottom X solution wins... */ 06426 xrms = bot_xrms; 06427 xpoly = bot_xpoly; 06428 cpl_polynomial_delete(top_xpoly); 06429 } 06430 } 06431 else if (top_xpoly) { 06432 xrms = top_xrms; 06433 xpoly = top_xpoly; 06434 } 06435 else { 06436 xrms = bot_xrms; 06437 xpoly = bot_xpoly; 06438 } 06439 06440 if (top_ypoly && bot_ypoly) { 06441 if (top_yrms < bot_yrms) { /* top Y solution wins... */ 06442 yrms = top_yrms; 06443 ypoly = top_ypoly; 06444 cpl_polynomial_delete(bot_ypoly); 06445 } 06446 else { /* bottom Y solution wins... */ 06447 yrms = bot_yrms; 06448 ypoly = bot_ypoly; 06449 cpl_polynomial_delete(top_ypoly); 06450 } 06451 } 06452 else if (top_ypoly) { 06453 yrms = top_yrms; 06454 ypoly = top_ypoly; 06455 } 06456 else { 06457 yrms = bot_yrms; 06458 ypoly = bot_ypoly; 06459 } 06460 06461 if (xpoly == NULL || ypoly == NULL) { 06462 cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the " 06463 "identified slits positions cannot be improved."); 06464 cpl_polynomial_delete(xpoly); 06465 cpl_polynomial_delete(ypoly); 06466 cpl_error_reset(); 06467 return NULL; 06468 } 06469 06470 cpl_msg_info(cpl_func, 06471 "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 06472 xrms, yrms); 06473 06474 if (global) { 06475 write_global_distortion(global, 0, xpoly); 06476 write_global_distortion(global, 7, ypoly); 06477 } 06478 06479 /* 06480 * The fit was successful: use the polynomials to obtain a new 06481 * position table with the improved positions of the slits 06482 */ 06483 06484 positions = cpl_table_duplicate(maskslits); 06485 cpl_table_duplicate_column(positions, "xmtop", positions, "xtop"); 06486 cpl_table_duplicate_column(positions, "ymtop", positions, "ytop"); 06487 cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom"); 06488 cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom"); 06489 06490 point = cpl_vector_new(2); 06491 dpoint = cpl_vector_get_data(point); 06492 06493 for (i = 0; i < nmaskslits; i++) { 06494 double position; 06495 06496 dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL); 06497 dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL); 06498 position = cpl_polynomial_eval(xpoly, point); 06499 // if (mos_multiplex >= 0) { 06500 // if (mos_multiplex != ((int)floor(position)) / mos_region_size) { 06501 // cpl_table_unselect_row(positions, i); 06502 // continue; 06503 // } 06504 // } 06505 cpl_table_set_double(positions, "xtop", i, position); 06506 position = cpl_polynomial_eval(ypoly, point); 06507 cpl_table_set_double(positions, "ytop", i, position); 06508 dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL); 06509 dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL); 06510 position = cpl_polynomial_eval(xpoly, point); 06511 cpl_table_set_double(positions, "xbottom", i, position); 06512 position = cpl_polynomial_eval(ypoly, point); 06513 cpl_table_set_double(positions, "ybottom", i, position); 06514 } 06515 06516 // if (mos_multiplex >= 0) { 06517 // cpl_table_not_selected(positions); 06518 // cpl_table_erase_selected(positions); 06519 // nmaskslits = cpl_table_get_nrow(positions); 06520 // } 06521 06522 cpl_vector_delete(point); 06523 cpl_polynomial_delete(xpoly); 06524 cpl_polynomial_delete(ypoly); 06525 06526 cpl_table_erase_column(positions, "xmtop"); 06527 cpl_table_erase_column(positions, "ymtop"); 06528 cpl_table_erase_column(positions, "xmbottom"); 06529 cpl_table_erase_column(positions, "ymbottom"); 06530 06531 if (mos_multiplex >= 0) { 06532 msg_multiplex = 06533 cpl_sprintf("in the CCD section between %d and %d pixel", 06534 mos_multiplex * mos_region_size, 06535 (mos_multiplex + 1) * mos_region_size); 06536 } 06537 06538 if (nmaskslits > nslits) 06539 cpl_msg_info(cpl_func, 06540 "Finally identified slits: %d out of %d expected %s\n" 06541 "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex, 06542 nmaskslits - nslits); 06543 else if (nmaskslits < nslits) 06544 cpl_msg_info(cpl_func, 06545 "Finally identified slits: %d out of %d expected %s\n" 06546 "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex, 06547 nslits - nmaskslits); 06548 else 06549 cpl_msg_info(cpl_func, 06550 "Finally identified slits: %d out of %d expected %s", 06551 nmaskslits, nmaskslits, msg_multiplex); 06552 06553 if (mos_multiplex >= 0) { 06554 cpl_free(msg_multiplex); 06555 } 06556 06557 return positions; 06558 06559 } 06560 06561 06562 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits, 06563 cpl_table *global) 06564 { 06565 const char *func = "mos_identify_slits_fast"; 06566 06567 cpl_propertylist *sort_col; 06568 cpl_table *positions; 06569 cpl_vector *scales; 06570 cpl_vector *angles; 06571 cpl_vector *point; 06572 cpl_vector *xpos; 06573 cpl_vector *ypos; 06574 cpl_vector *xmpos; 06575 cpl_vector *ympos; 06576 cpl_bivector *mpos; 06577 cpl_polynomial *xpoly = NULL; 06578 cpl_polynomial *ypoly = NULL; 06579 cpl_error_code error; 06580 int nslits; 06581 int nmaskslits; 06582 int found_slits; 06583 int i, j, k; 06584 06585 double dist1, dist2, dist3, dist, mindist; 06586 double scale, minscale, maxscale; 06587 double angle, minangle, maxangle; 06588 double *dscale; 06589 double *dangle; 06590 double *dpoint; 06591 double *xtop; 06592 double *ytop; 06593 double *xbottom; 06594 double *ybottom; 06595 double *xcenter; 06596 double *ycenter; 06597 double *xpseudo; 06598 double *ypseudo; 06599 int *slit_id; 06600 double *xmtop; 06601 double *ymtop; 06602 double *xmbottom; 06603 double *ymbottom; 06604 double *xmcenter; 06605 double *ymcenter; 06606 double *xmpseudo; 06607 double *ympseudo; 06608 double xmse, ymse; 06609 int *mslit_id; 06610 int *good; 06611 int minpos; 06612 int degree; 06613 06614 double sradius = 0.01; /* Candidate input argument... */ 06615 int in_sradius; 06616 06617 double pi = 3.14159265358979323846; 06618 06619 06620 error = mos_validate_slits(slits); 06621 if (error) { 06622 cpl_msg_error(func, "CCD slits table validation: %s", 06623 cpl_error_get_message()); 06624 cpl_error_set(func, error); 06625 return NULL; 06626 } 06627 06628 error = mos_validate_slits(maskslits); 06629 if (error) { 06630 cpl_msg_error(func, "Mask slits table validation: %s", 06631 cpl_error_get_message()); 06632 cpl_error_set(func, error); 06633 return NULL; 06634 } 06635 06636 if (1 != cpl_table_has_column(maskslits, "slit_id")) { 06637 cpl_msg_error(func, "Missing slits identifiers"); 06638 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 06639 return NULL; 06640 } 06641 06642 if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) { 06643 cpl_msg_error(func, "Wrong type used for slits identifiers"); 06644 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 06645 return NULL; 06646 } 06647 06648 nslits = cpl_table_get_nrow(slits); 06649 nmaskslits = cpl_table_get_nrow(maskslits); 06650 06651 if (nslits == 0 || nmaskslits == 0) { 06652 cpl_msg_error(func, "Empty slits table"); 06653 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 06654 return NULL; 06655 } 06656 06657 06658 /* 06659 * Compute middle point coordinates for each slit listed in both 06660 * input tables. 06661 */ 06662 06663 if (cpl_table_has_column(slits, "xcenter")) 06664 cpl_table_erase_column(slits, "xcenter"); 06665 06666 if (cpl_table_has_column(slits, "ycenter")) 06667 cpl_table_erase_column(slits, "ycenter"); 06668 06669 if (cpl_table_has_column(maskslits, "xcenter")) 06670 cpl_table_erase_column(maskslits, "xcenter"); 06671 06672 if (cpl_table_has_column(maskslits, "ycenter")) 06673 cpl_table_erase_column(maskslits, "ycenter"); 06674 06675 cpl_table_duplicate_column(slits, "xcenter", slits, "xtop"); 06676 cpl_table_add_columns(slits, "xcenter", "xbottom"); 06677 cpl_table_divide_scalar(slits, "xcenter", 2.0); 06678 cpl_table_duplicate_column(slits, "ycenter", slits, "ytop"); 06679 cpl_table_add_columns(slits, "ycenter", "ybottom"); 06680 cpl_table_divide_scalar(slits, "ycenter", 2.0); 06681 06682 cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop"); 06683 cpl_table_add_columns(maskslits, "xcenter", "xbottom"); 06684 cpl_table_divide_scalar(maskslits, "xcenter", 2.0); 06685 cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop"); 06686 cpl_table_add_columns(maskslits, "ycenter", "ybottom"); 06687 cpl_table_divide_scalar(maskslits, "ycenter", 2.0); 06688 06689 06690 /* 06691 * Guarantee that both input tables are sorted in the same way 06692 */ 06693 06694 sort_col = cpl_propertylist_new(); 06695 cpl_propertylist_append_bool(sort_col, "ycenter", 1); 06696 cpl_table_sort(slits, sort_col); 06697 cpl_table_sort(maskslits, sort_col); 06698 cpl_propertylist_delete(sort_col); 06699 06700 06701 /* 06702 * First we handle all the special cases (too few slits...) 06703 */ 06704 06705 if (nslits < 3 && nmaskslits > nslits) { 06706 06707 /* 06708 * If there are just 1 or 2 slits on the CCD, and more on the 06709 * mask, the ambiguity cannot be solved, and an error is returned. 06710 * This is a case that must be solved with a first-guess relation 06711 * between mask and CCD. 06712 */ 06713 06714 if (nslits > 1) 06715 cpl_msg_warning(func, "Cannot match the found CCD slit with the " 06716 "%d mask slits: process will continue using the " 06717 "detected CCD slit position", nmaskslits); 06718 else 06719 cpl_msg_warning(func, "Cannot match the %d found CCD slits with " 06720 "the %d mask slits: process will continue using " 06721 "the detected CCD slits positions", nslits, 06722 nmaskslits); 06723 return NULL; 06724 } 06725 06726 if (nslits <= 3 && nslits == nmaskslits) { 06727 06728 cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits); 06729 cpl_msg_warning(func, "Their detected positions are left unchanged"); 06730 06731 /* 06732 * If there are just up to 3 slits, both on the mask and on the CCD, 06733 * we can reasonably hope that those slits were found, and accept 06734 * that their positions on the CCD cannot be improved. We prepare 06735 * therefore an output position table containing the slits with 06736 * their original positions. We can however give an estimate of 06737 * the platescale if there is more than one slit. 06738 */ 06739 06740 positions = cpl_table_duplicate(slits); 06741 cpl_table_erase_column(slits, "xcenter"); 06742 cpl_table_erase_column(slits, "ycenter"); 06743 cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop"); 06744 cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop"); 06745 cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom"); 06746 cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom"); 06747 cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter"); 06748 cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter"); 06749 cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id"); 06750 cpl_table_erase_column(maskslits, "xcenter"); 06751 cpl_table_erase_column(maskslits, "ycenter"); 06752 06753 if (nslits > 1) { 06754 xcenter = cpl_table_get_data_double(positions, "xcenter"); 06755 ycenter = cpl_table_get_data_double(positions, "ycenter"); 06756 xmcenter = cpl_table_get_data_double(positions, "xmcenter"); 06757 ymcenter = cpl_table_get_data_double(positions, "ymcenter"); 06758 06759 dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1]) 06760 + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]); 06761 dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1]) 06762 + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]); 06763 scale = sqrt(dist1/dist2); 06764 06765 if (nslits == 3) { 06766 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2]) 06767 + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]); 06768 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2]) 06769 + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]); 06770 scale += sqrt(dist1/dist2); 06771 scale /= 2; 06772 } 06773 06774 cpl_msg_info(func, "Platescale: %f pixel/mm", scale); 06775 } 06776 06777 return positions; 06778 } 06779 06780 if (nmaskslits < 3 && nslits > nmaskslits) { 06781 06782 /* 06783 * If there are less than 3 slits on the mask the ambiguity cannot 06784 * be solved, and an error is returned. This is a case that must 06785 * be solved with a first-guess relation between mask and CCD. 06786 */ 06787 06788 cpl_msg_warning(func, "Cannot match the %d found CCD slits with " 06789 "the %d mask slits: process will continue using " 06790 "the detected CCD slits positions", nslits, 06791 nmaskslits); 06792 return NULL; 06793 } 06794 06795 06796 /* 06797 * At this point of the program all the region of the plane 06798 * (nslits, nmaskslits) where either or both mask and CCD display 06799 * less than 3 slits are handled in some way. It would be better 06800 * to add in this place a special handling for identifying slits 06801 * in case of a very reduced number of slits (say, below 6). 06802 * It is also clear that if there are many more slits on the 06803 * mask than on the CCD, or many more on the CCD than on the 06804 * mask, something went deeply wrong with the preliminary 06805 * wavelength calibration. Such cases should be handled with 06806 * a _complete_ pattern-recognition algorithm based on the 06807 * construction of all possible triangles. For the moment, 06808 * we go directly to the limited pattern-recognition applied 06809 * below, based on triangles build only for consecutive slits. 06810 * This is reasonably safe, since the preliminary wavelength 06811 * calibration performed by mos_identify_peaks() is generally 06812 * robust. 06813 */ 06814 06815 06816 /* 06817 * Compute (X, Y) coordinates on pseudo-plane describing the 06818 * different position ratios of successive slits, in both 06819 * input tables. 06820 */ 06821 06822 if (cpl_table_has_column(slits, "xpseudo")) 06823 cpl_table_erase_column(slits, "xpseudo"); 06824 06825 if (cpl_table_has_column(slits, "ypseudo")) 06826 cpl_table_erase_column(slits, "ypseudo"); 06827 06828 if (cpl_table_has_column(maskslits, "xpseudo")) 06829 cpl_table_erase_column(maskslits, "xpseudo"); 06830 06831 if (cpl_table_has_column(maskslits, "ypseudo")) 06832 cpl_table_erase_column(maskslits, "ypseudo"); 06833 06834 cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter"); 06835 cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter"); 06836 06837 xcenter = cpl_table_get_data_double(slits, "xcenter"); 06838 ycenter = cpl_table_get_data_double(slits, "ycenter"); 06839 xpseudo = cpl_table_get_data_double(slits, "xpseudo"); 06840 ypseudo = cpl_table_get_data_double(slits, "ypseudo"); 06841 06842 for (i = 1; i < nslits - 1; i++) { 06843 dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i]) 06844 + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]); 06845 dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1]) 06846 + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]); 06847 dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1]) 06848 + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]); 06849 xpseudo[i] = sqrt(dist1/dist2); 06850 ypseudo[i] = sqrt(dist3/dist2); 06851 } 06852 06853 cpl_table_set_invalid(slits, "xpseudo", 0); 06854 cpl_table_set_invalid(slits, "xpseudo", nslits-1); 06855 cpl_table_set_invalid(slits, "ypseudo", 0); 06856 cpl_table_set_invalid(slits, "ypseudo", nslits-1); 06857 06858 cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter"); 06859 cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter"); 06860 06861 xcenter = cpl_table_get_data_double(maskslits, "xcenter"); 06862 ycenter = cpl_table_get_data_double(maskslits, "ycenter"); 06863 xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo"); 06864 ympseudo = cpl_table_get_data_double(maskslits, "ypseudo"); 06865 06866 for (i = 1; i < nmaskslits - 1; i++) { 06867 dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i]) 06868 + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]); 06869 dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1]) 06870 + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]); 06871 dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1]) 06872 + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]); 06873 xmpseudo[i] = sqrt(dist1/dist2); 06874 ympseudo[i] = sqrt(dist3/dist2); 06875 } 06876 06877 cpl_table_set_invalid(maskslits, "xpseudo", 0); 06878 cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1); 06879 cpl_table_set_invalid(maskslits, "ypseudo", 0); 06880 cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1); 06881 06882 06883 /* 06884 * For each (X, Y) on the pseudo-plane related to the mask positions, 06885 * find the closest (X, Y) on the pseudo-plane related to the CCD 06886 * positions. If the closest point is closer than a given search 06887 * radius, a triangle has been found, and 3 slits are identified. 06888 * However, if more than one point is found within the search 06889 * radius, we have an ambiguity and this is rejected. 06890 */ 06891 06892 if (cpl_table_has_column(slits, "slit_id")) 06893 cpl_table_erase_column(slits, "slit_id"); 06894 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 06895 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 06896 06897 for (i = 1; i < nmaskslits - 1; i++) { 06898 in_sradius = 0; 06899 mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1]) 06900 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]); 06901 minpos = 1; 06902 if (mindist < sradius*sradius) 06903 in_sradius++; 06904 for (j = 2; j < nslits - 1; j++) { 06905 dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j]) 06906 + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]); 06907 if (dist < sradius*sradius) 06908 in_sradius++; 06909 if (in_sradius > 1) /* More than one triangle within radius */ 06910 break; 06911 if (mindist > dist) { 06912 mindist = dist; 06913 minpos = j; 06914 } 06915 } 06916 06917 mindist = sqrt(mindist); 06918 06919 if (mindist < sradius && in_sradius == 1) { 06920 cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]); 06921 cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]); 06922 cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]); 06923 } 06924 } 06925 06926 06927 /* 06928 * At this point, the slit_id column contains invalid elements 06929 * corresponding to unidentified slits. 06930 */ 06931 06932 found_slits = nslits - cpl_table_count_invalid(slits, "slit_id"); 06933 06934 if (found_slits < 3) { 06935 cpl_msg_warning(func, "Too few preliminarily identified slits: " 06936 "%d out of %d", found_slits, nslits); 06937 if (nslits == nmaskslits) { 06938 cpl_msg_warning(func, "(this is not an error, it could be caused " 06939 "by a mask with regularly located slits)"); 06940 cpl_msg_warning(func, "The detected slits positions are left " 06941 "unchanged"); 06942 06943 /* 06944 * If there are less than 3 identified slits, this is probably 06945 * a mask with regularly spaced slits (leading to an ambiguous 06946 * pattern). Only in the case all expected slits appear to have 06947 * been found on the CCD we can proceed... 06948 */ 06949 06950 cpl_table_erase_column(slits, "slit_id"); 06951 cpl_table_erase_column(slits, "xpseudo"); 06952 cpl_table_erase_column(slits, "ypseudo"); 06953 positions = cpl_table_duplicate(slits); 06954 cpl_table_erase_column(slits, "xcenter"); 06955 cpl_table_erase_column(slits, "ycenter"); 06956 06957 cpl_table_erase_column(maskslits, "xpseudo"); 06958 cpl_table_erase_column(maskslits, "ypseudo"); 06959 cpl_table_duplicate_column(positions, "xmtop", 06960 maskslits, "xtop"); 06961 cpl_table_duplicate_column(positions, "ymtop", 06962 maskslits, "ytop"); 06963 cpl_table_duplicate_column(positions, "xmbottom", 06964 maskslits, "xbottom"); 06965 cpl_table_duplicate_column(positions, "ymbottom", 06966 maskslits, "ybottom"); 06967 cpl_table_duplicate_column(positions, "xmcenter", 06968 maskslits, "xcenter"); 06969 cpl_table_duplicate_column(positions, "ymcenter", 06970 maskslits, "ycenter"); 06971 cpl_table_duplicate_column(positions, "slit_id", 06972 maskslits, "slit_id"); 06973 cpl_table_erase_column(maskslits, "xcenter"); 06974 cpl_table_erase_column(maskslits, "ycenter"); 06975 return positions; 06976 } 06977 else { 06978 cpl_table_erase_column(slits, "slit_id"); 06979 cpl_table_erase_column(slits, "xpseudo"); 06980 cpl_table_erase_column(slits, "ypseudo"); 06981 positions = cpl_table_duplicate(slits); 06982 cpl_table_erase_column(slits, "xcenter"); 06983 cpl_table_erase_column(slits, "ycenter"); 06984 cpl_msg_warning(func, "(the failure could be caused " 06985 "by a mask with regularly located slits)"); 06986 return NULL; 06987 } 06988 } 06989 else { 06990 cpl_msg_info(func, "Preliminarily identified slits: %d out of %d " 06991 "candidates (%d expected)", found_slits, nslits, 06992 nmaskslits); 06993 } 06994 06995 06996 /* 06997 * Create a table with the coordinates of the preliminarily identified 06998 * slits, both on CCD and mask. The original order of the slits positions 06999 * is preserved. 07000 */ 07001 07002 positions = cpl_table_new(found_slits); 07003 cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT); 07004 cpl_table_new_column(positions, "xtop", CPL_TYPE_DOUBLE); 07005 cpl_table_new_column(positions, "ytop", CPL_TYPE_DOUBLE); 07006 cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE); 07007 cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE); 07008 cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE); 07009 cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE); 07010 cpl_table_new_column(positions, "xmtop", CPL_TYPE_DOUBLE); 07011 cpl_table_new_column(positions, "ymtop", CPL_TYPE_DOUBLE); 07012 cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE); 07013 cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE); 07014 cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE); 07015 cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE); 07016 cpl_table_new_column(positions, "good", CPL_TYPE_INT); 07017 cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0); 07018 07019 slit_id = cpl_table_get_data_int (slits, "slit_id"); 07020 xtop = cpl_table_get_data_double(slits, "xtop"); 07021 ytop = cpl_table_get_data_double(slits, "ytop"); 07022 xbottom = cpl_table_get_data_double(slits, "xbottom"); 07023 ybottom = cpl_table_get_data_double(slits, "ybottom"); 07024 xcenter = cpl_table_get_data_double(slits, "xcenter"); 07025 ycenter = cpl_table_get_data_double(slits, "ycenter"); 07026 07027 mslit_id = cpl_table_get_data_int (maskslits, "slit_id"); 07028 xmtop = cpl_table_get_data_double(maskslits, "xtop"); 07029 ymtop = cpl_table_get_data_double(maskslits, "ytop"); 07030 xmbottom = cpl_table_get_data_double(maskslits, "xbottom"); 07031 ymbottom = cpl_table_get_data_double(maskslits, "ybottom"); 07032 xmcenter = cpl_table_get_data_double(maskslits, "xcenter"); 07033 ymcenter = cpl_table_get_data_double(maskslits, "ycenter"); 07034 07035 07036 /* 07037 * Transferring the valid slits information to the new table. 07038 * Note that invalid elements are coded as 0 in the internal 07039 * buffer, and this is the way they are recognised and excluded. 07040 */ 07041 07042 k = 0; 07043 cpl_table_fill_invalid_int(slits, "slit_id", 0); 07044 for (i = 0; i < nmaskslits; i++) { 07045 for (j = 0; j < nslits; j++) { 07046 if (slit_id[j] == 0) 07047 continue; /* Skip invalid slit */ 07048 if (mslit_id[i] == slit_id[j]) { 07049 cpl_table_set_int (positions, "slit_id", k, slit_id[j]); 07050 07051 cpl_table_set_double(positions, "xtop", k, xtop[j]); 07052 cpl_table_set_double(positions, "ytop", k, ytop[j]); 07053 cpl_table_set_double(positions, "xbottom", k, xbottom[j]); 07054 cpl_table_set_double(positions, "ybottom", k, ybottom[j]); 07055 cpl_table_set_double(positions, "xcenter", k, xcenter[j]); 07056 cpl_table_set_double(positions, "ycenter", k, ycenter[j]); 07057 07058 cpl_table_set_double(positions, "xmtop", k, xmtop[i]); 07059 cpl_table_set_double(positions, "ymtop", k, ymtop[i]); 07060 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]); 07061 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]); 07062 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]); 07063 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]); 07064 07065 k++; 07066 07067 break; 07068 } 07069 } 07070 } 07071 07072 found_slits = k; 07073 07074 cpl_table_erase_column(slits, "slit_id"); 07075 cpl_table_erase_column(slits, "xpseudo"); 07076 cpl_table_erase_column(slits, "ypseudo"); 07077 cpl_table_erase_column(slits, "xcenter"); 07078 cpl_table_erase_column(slits, "ycenter"); 07079 cpl_table_erase_column(maskslits, "xpseudo"); 07080 cpl_table_erase_column(maskslits, "ypseudo"); 07081 cpl_table_erase_column(maskslits, "xcenter"); 07082 cpl_table_erase_column(maskslits, "ycenter"); 07083 07084 07085 /* 07086 * Find the median platescale and rotation angle from the identified 07087 * slits, and then exclude slits outlaying more than 10% from the 07088 * median platescale, and more than 2 degrees from the median 07089 * rotation angle. 07090 */ 07091 07092 ytop = cpl_table_get_data_double(positions, "ytop"); 07093 ybottom = cpl_table_get_data_double(positions, "ybottom"); 07094 xcenter = cpl_table_get_data_double(positions, "xcenter"); 07095 ycenter = cpl_table_get_data_double(positions, "ycenter"); 07096 xmcenter = cpl_table_get_data_double(positions, "xmcenter"); 07097 ymcenter = cpl_table_get_data_double(positions, "ymcenter"); 07098 07099 scales = cpl_vector_new(found_slits - 1); 07100 dscale = cpl_vector_get_data(scales); 07101 angles = cpl_vector_new(found_slits - 1); 07102 dangle = cpl_vector_get_data(angles); 07103 07104 for (i = 1; i < found_slits; i++) { 07105 dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i]) 07106 + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]); 07107 dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i]) 07108 + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]); 07109 dscale[i-1] = sqrt(dist1/dist2); 07110 dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 07111 xcenter[i-1] - xcenter[i]) 07112 - atan2(ymcenter[i-1] - ymcenter[i], 07113 xmcenter[i-1] - xmcenter[i]); 07114 dangle[i-1] *= 180; 07115 dangle[i-1] /= pi; 07116 } 07117 07118 minscale = cpl_vector_get_min(scales); 07119 scale = cpl_vector_get_median_const(scales); 07120 maxscale = cpl_vector_get_max(scales); 07121 07122 minangle = cpl_vector_get_min(angles); 07123 angle = cpl_vector_get_median_const(angles); 07124 maxangle = cpl_vector_get_max(angles); 07125 07126 cpl_msg_info(func, "Median platescale: %f pixel/mm", scale); 07127 cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 07128 minscale, maxscale); 07129 07130 cpl_msg_info(func, "Median rotation: %f degrees", angle); 07131 cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 07132 minangle, maxangle); 07133 07134 good = cpl_table_get_data_int(positions, "good"); 07135 07136 good[0] = good[found_slits - 1] = 1; 07137 for (i = 1; i < found_slits; i++) { 07138 if (fabs((dscale[i-1] - scale)/scale) < 0.10 07139 && fabs(dangle[i-1] - angle) < 2) { 07140 good[i-1]++; 07141 good[i]++; 07142 } 07143 } 07144 07145 for (i = 0; i < found_slits; i++) { 07146 if (good[i] < 2) 07147 good[i] = 0; 07148 else 07149 good[i] = 1; 07150 } 07151 07152 /* 07153 for (i = 1; i < found_slits; i++) 07154 if (fabs((dscale[i-1] - scale)/scale) < 0.10) 07155 good[i-1] = good[i] = 1; 07156 */ 07157 07158 /* DEBUG ************+ 07159 for (i = 0; i < found_slits; i++) { 07160 if (good[i]) { 07161 if (i == found_slits - 1) 07162 printf("include slit %d, prev = %f, %f\n", 07163 i, dscale[i-1], dangle[i-1]); 07164 else if (i == 0) 07165 printf("include slit %d, next %f, %f\n", 07166 i, dscale[i], dangle[i]); 07167 else 07168 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 07169 dscale[i-1], dangle[i-1], dscale[i], dangle[i]); 07170 } 07171 else { 07172 if (i == found_slits - 1) 07173 printf("EXclude slit %d, prev = %f, %f\n", 07174 i, dscale[i-1], dangle[i-1]); 07175 else if (i == 0) 07176 printf("EXclude slit %d, next %f, %f\n", 07177 i, dscale[i], dangle[i]); 07178 else 07179 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i, 07180 dscale[i-1], dangle[i-1], dscale[i], dangle[i]); 07181 } 07182 } 07183 +*********** DEBUG */ 07184 07185 cpl_vector_delete(scales); 07186 cpl_vector_delete(angles); 07187 07188 cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0); 07189 cpl_table_erase_selected(positions); 07190 cpl_table_erase_column(positions, "good"); 07191 found_slits = cpl_table_get_nrow(positions); 07192 07193 if (found_slits < 4) { 07194 07195 /* 07196 * If the self-consistency check gives such a poor result, 07197 * something must have gone really wrong in the preliminary 07198 * wavelength calibration... Nothing can be done. 07199 */ 07200 07201 cpl_msg_warning(func, "Too few safely identified slits: %d out of %d " 07202 "candidates (%d expected). Process will continue " 07203 "using the detected CCD slits positions", found_slits, 07204 nslits, nmaskslits); 07205 cpl_table_delete(positions); 07206 return NULL; 07207 } 07208 else { 07209 cpl_msg_info(func, "Safely identified slits: %d out of %d " 07210 "candidates\n(%d expected)", found_slits, nslits, 07211 nmaskslits); 07212 } 07213 07214 07215 /* 07216 * Now select the central points of the identified slits, and 07217 * fit two bivariate polynomials to determine a first approximate 07218 * relation between positions on the mask and positions on the CCD. 07219 */ 07220 07221 xpos = cpl_vector_wrap(found_slits, 07222 cpl_table_get_data_double(positions, "xcenter")); 07223 ypos = cpl_vector_wrap(found_slits, 07224 cpl_table_get_data_double(positions, "ycenter")); 07225 xmpos = cpl_vector_wrap(found_slits, 07226 cpl_table_get_data_double(positions, "xmcenter")); 07227 ympos = cpl_vector_wrap(found_slits, 07228 cpl_table_get_data_double(positions, "ymcenter")); 07229 mpos = cpl_bivector_wrap_vectors(xmpos, ympos); 07230 07231 if (found_slits < 10) 07232 degree = 1; 07233 else 07234 degree = 2; 07235 07236 xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse); 07237 if (xpoly != NULL) 07238 ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse); 07239 cpl_bivector_unwrap_vectors(mpos); 07240 cpl_vector_unwrap(xpos); 07241 cpl_vector_unwrap(ypos); 07242 cpl_vector_unwrap(xmpos); 07243 cpl_vector_unwrap(ympos); 07244 if (ypoly == NULL) { 07245 if (found_slits == nmaskslits) { 07246 cpl_msg_warning(func, "Fit failure: the accuracy of the " 07247 "identified slits positions is not improved."); 07248 07249 /* 07250 * The determination of the transformation from mask to CCD 07251 * failed, but since all slits have been already identified 07252 * this is not a problem: data can be reduced also without 07253 * such transformation. Simply the accuracy of the slits 07254 * positions cannot be improved. 07255 */ 07256 07257 } else { 07258 cpl_msg_info(func, "Fit failure: not all slits have been " 07259 "identified. Process will continue using " 07260 "the detected CCD slits positions"); 07261 } 07262 07263 cpl_polynomial_delete(xpoly); 07264 return positions; 07265 } 07266 07267 cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 07268 sqrt(xmse), sqrt(ymse)); 07269 07270 if (global) { 07271 write_global_distortion(global, 0, xpoly); 07272 write_global_distortion(global, 7, ypoly); 07273 } 07274 07275 /* 07276 * The fit was successful: use the polynomials to obtain a new 07277 * position table with the improved positions of the slits 07278 */ 07279 07280 cpl_table_delete(positions); 07281 07282 positions = cpl_table_duplicate(maskslits); 07283 cpl_table_duplicate_column(positions, "xmtop", positions, "xtop"); 07284 cpl_table_duplicate_column(positions, "ymtop", positions, "ytop"); 07285 cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom"); 07286 cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom"); 07287 07288 point = cpl_vector_new(2); 07289 dpoint = cpl_vector_get_data(point); 07290 07291 for (i = 0; i < nmaskslits; i++) { 07292 dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL); 07293 dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL); 07294 cpl_table_set_double(positions, "xtop", i, 07295 cpl_polynomial_eval(xpoly, point)); 07296 cpl_table_set_double(positions, "ytop", i, 07297 cpl_polynomial_eval(ypoly, point)); 07298 dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL); 07299 dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL); 07300 cpl_table_set_double(positions, "xbottom", i, 07301 cpl_polynomial_eval(xpoly, point)); 07302 cpl_table_set_double(positions, "ybottom", i, 07303 cpl_polynomial_eval(ypoly, point)); 07304 } 07305 07306 cpl_vector_delete(point); 07307 cpl_polynomial_delete(xpoly); 07308 cpl_polynomial_delete(ypoly); 07309 07310 cpl_table_erase_column(positions, "xmtop"); 07311 cpl_table_erase_column(positions, "ymtop"); 07312 cpl_table_erase_column(positions, "xmbottom"); 07313 cpl_table_erase_column(positions, "ymbottom"); 07314 07315 if (nmaskslits > nslits) 07316 cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n" 07317 "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits); 07318 else if (nmaskslits < nslits) 07319 cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n" 07320 "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits); 07321 else 07322 cpl_msg_info(func, "Finally identified slits: %d out of %d expected", 07323 nmaskslits, nmaskslits); 07324 07325 return positions; 07326 } 07327 07328 07370 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference, 07371 double blue, double red, double dispersion) 07372 { 07373 07374 const char *func = "mos_trace_flat"; 07375 07376 cpl_image *gradient; 07377 cpl_image *sgradient; 07378 float *dgradient; 07379 float level = 500; /* It was 250... */ 07380 cpl_vector *row; 07381 cpl_vector *srow; 07382 cpl_vector **peaks; 07383 double *peak; 07384 int *slit_id; 07385 float *g; 07386 double *r; 07387 double *xtop; 07388 double *ytop; 07389 double *xbottom; 07390 double *ybottom; 07391 double min, dist; 07392 double sradius; 07393 double tolerance; 07394 double start_y, prev_y; 07395 int minpos; 07396 int nslits; 07397 int nrows; 07398 int step = 10; 07399 int filtbox = 15; 07400 int nx, ny, npix; 07401 int pos, ypos; 07402 int npeaks; 07403 int pixel_above, pixel_below; 07404 int i, j, k, l; 07405 char trace_id[MAX_COLNAME]; 07406 07407 cpl_table *traces; 07408 07409 07410 if (flat == NULL || slits == NULL) { 07411 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07412 return NULL; 07413 } 07414 07415 if (dispersion <= 0.0) { 07416 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07417 return NULL; 07418 } 07419 07420 if (red - blue < dispersion) { 07421 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07422 return NULL; 07423 } 07424 07425 /* 07426 * Create a dummy slit_id column if it is missing in the 07427 * input slits table 07428 */ 07429 07430 nslits = cpl_table_get_nrow(slits); 07431 if (1 != cpl_table_has_column(slits, "slit_id")) { 07432 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 07433 for (i = 0; i < nslits; i++) 07434 cpl_table_set_int(slits, "slit_id", i, -(i+1)); /* it was (i+1) */ 07435 } 07436 07437 slit_id = cpl_table_get_data_int(slits, "slit_id"); 07438 07439 nx = cpl_image_get_size_x(flat); 07440 ny = cpl_image_get_size_y(flat); 07441 npix = nx * ny; 07442 07443 gradient = cpl_image_duplicate(flat); 07444 dgradient = cpl_image_get_data_float(gradient); 07445 07446 for (i = 0; i < ny - 1; i++) { 07447 k = i * nx; 07448 for (j = 0; j < nx; j++) { 07449 l = k + j; 07450 dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]); 07451 } 07452 } 07453 07454 npix--; 07455 for (j = 0; j < nx; j++) 07456 dgradient[npix - j] = 0.0; 07457 07458 cpl_image_turn(gradient, -1); 07459 nx = cpl_image_get_size_x(gradient); 07460 ny = cpl_image_get_size_y(gradient); 07461 sgradient = mos_image_vertical_median_filter(gradient, 07462 filtbox, 0, ny, 0, step); 07463 cpl_image_delete(gradient); 07464 07465 07466 /* 07467 * Remove background from processed image rows 07468 */ 07469 07470 dgradient = cpl_image_get_data_float(sgradient); 07471 07472 for (i = 1; i <= ny; i += step) { 07473 row = cpl_vector_new_from_image_row(sgradient, i); 07474 srow = cpl_vector_filter_median_create(row, filtbox); 07475 cpl_vector_subtract(row, srow); 07476 cpl_vector_delete(srow); 07477 g = dgradient + (i-1)*nx; 07478 r = cpl_vector_get_data(row); 07479 for (j = 0; j < nx; j++) 07480 g[j] = r[j]; 07481 cpl_vector_delete(row); 07482 } 07483 07484 07485 /* 07486 * Rotate (temporarily) the input slits table, to get coordinates 07487 * compatible with the rotated gradient image. 07488 */ 07489 07490 mos_rotate_slits(slits, 1, nx, ny); 07491 xtop = cpl_table_get_data_double(slits, "xtop"); 07492 ytop = cpl_table_get_data_double(slits, "ytop"); 07493 xbottom = cpl_table_get_data_double(slits, "xbottom"); 07494 ybottom = cpl_table_get_data_double(slits, "ybottom"); 07495 07496 07497 /* 07498 * Get positions of peaks candidates for each processed gradient 07499 * image row 07500 */ 07501 07502 peaks = cpl_calloc(ny, sizeof(cpl_vector *)); 07503 07504 for (i = 0; i < ny; i += step) { 07505 g = dgradient + i*nx; 07506 peaks[i] = mos_peak_candidates(g, nx, level, 1.0); 07507 07508 /* I thought this would be required, but apparently I was wrong. 07509 * Check twice... */ 07510 if (peaks[i]) 07511 cpl_vector_subtract_scalar(peaks[i], 0.5); 07512 07513 } 07514 07515 cpl_image_delete(sgradient); 07516 07517 07518 /* 07519 * Tracing the flat field spectra edges, starting from the 07520 * slits positions obtained at reference wavelength. The 07521 * gradient maximum closest to each slits ends is found and 07522 * accepted only within a given search radius: 07523 */ 07524 07525 sradius = 5.0; /* Pixel */ 07526 07527 /* 07528 * The tracing proceeds along the processed gradient image rows, 07529 * above and below the start position, finding the closest peak 07530 * to the previous obtained position, accepting the new position 07531 * only if it is closer than a given tolerance: 07532 */ 07533 07534 tolerance = 0.9; /* Pixel */ 07535 07536 /* 07537 * The trace is attempted for a certain number of pixels above 07538 * and below the position of the reference wavelength: 07539 */ 07540 07541 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 07542 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 07543 07544 07545 /* 07546 * Prepare the structure of the output table: 07547 */ 07548 07549 nrows = (ny-1)/step + 1; 07550 traces = cpl_table_new(nrows); 07551 cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE); 07552 cpl_table_set_column_unit(traces, "x", "pixel"); 07553 for (i = 0, j = 0; i < ny; i += step, j++) 07554 cpl_table_set(traces, "x", j, i); 07555 07556 for (i = 0; i < nslits; i++) { 07557 07558 /* 07559 * Find the closest processed gradient image row 07560 */ 07561 07562 ypos = ytop[i]; 07563 07564 if (ypos < 0) 07565 ypos = 0; 07566 if (ypos >= ny) 07567 ypos = ny - 1; 07568 07569 pos = ypos / step; 07570 pos *= step; 07571 07572 /* 07573 * Find the peak in that row that is closest to xtop[i] 07574 */ 07575 07576 if (peaks[pos]) { 07577 peak = cpl_vector_get_data(peaks[pos]); 07578 npeaks = cpl_vector_get_size(peaks[pos]); 07579 07580 min = fabs(peak[0] - xtop[i]); 07581 minpos = 0; 07582 for (j = 1; j < npeaks; j++) { 07583 dist = fabs(peak[j] - xtop[i]); 07584 if (min > dist) { 07585 min = dist; 07586 minpos = j; 07587 } 07588 } 07589 } 07590 else { 07591 npeaks = 0; 07592 } 07593 07594 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]); 07595 cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE); 07596 07597 if (min > sradius || npeaks == 0) { 07598 cpl_msg_warning(func, "Cannot find spectrum edge for " 07599 "top (or left) end of slit %d", slit_id[i]); 07600 } 07601 else { 07602 07603 /* 07604 * Add to output table the start y position. Note that 07605 * y positions are written in coordinates of the unrotated 07606 * image, i.e., with horizontal dispersion. Currently nx 07607 * is the x size of the temporarily rotated image (for 07608 * faster memory access), but it has the sense of a y size. 07609 */ 07610 07611 cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]); 07612 start_y = peak[minpos]; 07613 07614 /* 07615 * Perform the tracing of current edge. Above: 07616 */ 07617 07618 prev_y = start_y; 07619 07620 for (j = pos + step; j < ny; j += step) { 07621 if (j - pos > pixel_above) 07622 break; 07623 if (peaks[j]) { 07624 peak = cpl_vector_get_data(peaks[j]); 07625 npeaks = cpl_vector_get_size(peaks[j]); 07626 min = fabs(peak[0] - prev_y); 07627 minpos = 0; 07628 for (k = 1; k < npeaks; k++) { 07629 dist = fabs(peak[k] - prev_y); 07630 if (min > dist) { 07631 min = dist; 07632 minpos = k; 07633 } 07634 } 07635 if (min < tolerance) { 07636 cpl_table_set(traces, trace_id, j/step, 07637 nx - peak[minpos]); 07638 prev_y = peak[minpos]; 07639 } 07640 } 07641 } 07642 07643 /* 07644 * Perform the tracing of current edge. Below: 07645 */ 07646 07647 prev_y = start_y; 07648 07649 for (j = pos - step; j >= 0; j -= step) { 07650 if (pos - j > pixel_below) 07651 break; 07652 if (peaks[j]) { 07653 peak = cpl_vector_get_data(peaks[j]); 07654 npeaks = cpl_vector_get_size(peaks[j]); 07655 min = fabs(peak[0] - prev_y); 07656 minpos = 0; 07657 for (k = 1; k < npeaks; k++) { 07658 dist = fabs(peak[k] - prev_y); 07659 if (min > dist) { 07660 min = dist; 07661 minpos = k; 07662 } 07663 } 07664 if (min < tolerance) { 07665 cpl_table_set(traces, trace_id, j/step, 07666 nx - peak[minpos]); 07667 prev_y = peak[minpos]; 07668 } 07669 } 07670 } 07671 } 07672 07673 07674 /* 07675 * Find the peak in that row that is closest to xbottom[i] 07676 */ 07677 07678 if (peaks[pos]) { 07679 peak = cpl_vector_get_data(peaks[pos]); 07680 npeaks = cpl_vector_get_size(peaks[pos]); 07681 07682 min = fabs(peak[0] - xbottom[i]); 07683 minpos = 0; 07684 for (j = 1; j < npeaks; j++) { 07685 dist = fabs(peak[j] - xbottom[i]); 07686 if (min > dist) { 07687 min = dist; 07688 minpos = j; 07689 } 07690 } 07691 } 07692 else { 07693 npeaks = 0; 07694 } 07695 07696 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]); 07697 cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE); 07698 07699 if (min > sradius || npeaks == 0) { 07700 cpl_msg_warning(func, "Cannot find spectrum edge for " 07701 "bottom (or right) end of slit %d", slit_id[i]); 07702 } 07703 else { 07704 07705 cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]); 07706 start_y = peak[minpos]; 07707 07708 /* 07709 * Perform the tracing of current edge. Above: 07710 */ 07711 07712 prev_y = start_y; 07713 07714 for (j = pos + step; j < ny; j += step) { 07715 if (j - pos > pixel_above) 07716 break; 07717 if (peaks[j]) { 07718 peak = cpl_vector_get_data(peaks[j]); 07719 npeaks = cpl_vector_get_size(peaks[j]); 07720 min = fabs(peak[0] - prev_y); 07721 minpos = 0; 07722 for (k = 1; k < npeaks; k++) { 07723 dist = fabs(peak[k] - prev_y); 07724 if (min > dist) { 07725 min = dist; 07726 minpos = k; 07727 } 07728 } 07729 if (min < tolerance) { 07730 cpl_table_set(traces, trace_id, j/step, 07731 nx - peak[minpos]); 07732 prev_y = peak[minpos]; 07733 } 07734 } 07735 } 07736 07737 /* 07738 * Perform the tracing of current edge. Below: 07739 */ 07740 07741 prev_y = start_y; 07742 07743 for (j = pos - step; j >= 0; j -= step) { 07744 if (pos - j > pixel_below) 07745 break; 07746 if (peaks[j]) { 07747 peak = cpl_vector_get_data(peaks[j]); 07748 npeaks = cpl_vector_get_size(peaks[j]); 07749 min = fabs(peak[0] - prev_y); 07750 minpos = 0; 07751 for (k = 1; k < npeaks; k++) { 07752 dist = fabs(peak[k] - prev_y); 07753 if (min > dist) { 07754 min = dist; 07755 minpos = k; 07756 } 07757 } 07758 if (min < tolerance) { 07759 cpl_table_set(traces, trace_id, j/step, 07760 nx - peak[minpos]); 07761 prev_y = peak[minpos]; 07762 } 07763 } 07764 } 07765 } 07766 07767 } /* End of loop on slits */ 07768 07769 for (i = 0; i < ny; i += step) 07770 cpl_vector_delete(peaks[i]); 07771 cpl_free(peaks); 07772 07773 /* 07774 * Restore original orientation of slits positions table 07775 */ 07776 07777 mos_rotate_slits(slits, -1, ny, nx); 07778 07779 return traces; 07780 07781 } 07782 07783 07804 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order) 07805 { 07806 const char *func = "mos_poly_trace"; 07807 07808 cpl_table *polytraces; 07809 cpl_table *dummy; 07810 cpl_vector *x; 07811 cpl_vector *trace; 07812 cpl_polynomial *polytrace; 07813 char trace_id[MAX_COLNAME]; 07814 char trace_res[MAX_COLNAME]; 07815 char trace_mod[MAX_COLNAME]; 07816 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 07817 /* Max order is 5 */ 07818 double *xdata; 07819 int *slit_id; 07820 int nslits; 07821 int nrows; 07822 int npoints; 07823 int i, j; 07824 cpl_size k; 07825 07826 07827 if (traces == NULL || slits == NULL) { 07828 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07829 return NULL; 07830 } 07831 07832 if (order > 5) { 07833 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07834 return NULL; 07835 } 07836 07837 nrows = cpl_table_get_nrow(traces); 07838 xdata = cpl_table_get_data_double(traces, "x"); 07839 nslits = cpl_table_get_nrow(slits); 07840 slit_id = cpl_table_get_data_int(slits, "slit_id"); 07841 07842 polytraces = cpl_table_new(2*nslits); 07843 cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT); 07844 for (i = 0; i <= order; i++) 07845 cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE); 07846 07847 for (i = 0; i < nslits; i++) { 07848 for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */ 07849 07850 if (j) { 07851 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]); 07852 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]); 07853 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]); 07854 } 07855 else { 07856 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]); 07857 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]); 07858 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]); 07859 } 07860 07861 cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]); 07862 07863 /* 07864 * The "dummy" table is just a tool for eliminating invalid 07865 * points from the vectors to be fitted. 07866 */ 07867 07868 dummy = cpl_table_new(nrows); 07869 cpl_table_duplicate_column(dummy, "x", traces, "x"); 07870 cpl_table_duplicate_column(dummy, trace_id, traces, trace_id); 07871 npoints = nrows - cpl_table_count_invalid(dummy, trace_id); 07872 if (npoints < 2 * order) { 07873 cpl_table_delete(dummy); 07874 continue; 07875 } 07876 cpl_table_erase_invalid(dummy); 07877 x = cpl_vector_wrap(npoints, 07878 cpl_table_get_data_double(dummy, "x")); 07879 trace = cpl_vector_wrap(npoints, 07880 cpl_table_get_data_double(dummy, trace_id)); 07881 polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL); 07882 cpl_vector_unwrap(x); 07883 cpl_vector_unwrap(trace); 07884 cpl_table_delete(dummy); 07885 07886 /* 07887 * Screen bad solutions. At the moment, a primitive screening 07888 * consists in excluding solutions displaying excessive 07889 * curvature (larger than 1E-5 / pixel) 07890 */ 07891 07892 k = 2; 07893 if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) > 1.E-4) { 07894 cpl_polynomial_delete(polytrace); 07895 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE); 07896 cpl_table_duplicate_column(traces, trace_res, traces, 07897 trace_mod); 07898 if (j) 07899 cpl_msg_warning(func, "Exclude bad curvature solution " 07900 "for bottom (right) edge of slit %d", slit_id[i]); 07901 else 07902 cpl_msg_warning(func, "Exclude bad curvature solution " 07903 "for top (left) edge of slit %d", slit_id[i]); 07904 continue; 07905 } 07906 07907 /* 07908 * Write polynomial coefficients to the output table, 07909 * tagged with the appropriate slit_id 07910 */ 07911 07912 for (k = 0; k <= order; k++) 07913 cpl_table_set_double(polytraces, clab[k], 2*i+j, 07914 cpl_polynomial_get_coeff(polytrace, &k)); 07915 07916 /* 07917 * Add column of residuals to input traces table 07918 */ 07919 07920 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE); 07921 cpl_table_set_column_unit(traces, trace_mod, "pixel"); 07922 07923 for (k = 0; k < nrows; k++) { 07924 cpl_table_set_double(traces, trace_mod, k, 07925 cpl_polynomial_eval_1d(polytrace, xdata[k], NULL)); 07926 } 07927 07928 cpl_polynomial_delete(polytrace); 07929 07930 cpl_table_duplicate_column(traces, trace_res, traces, trace_mod); 07931 cpl_table_subtract_columns(traces, trace_res, trace_id); 07932 cpl_table_multiply_scalar(traces, trace_res, -1.0); 07933 07934 } 07935 } 07936 07937 return polytraces; 07938 07939 } 07940 07941 07965 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces, 07966 int mode) 07967 { 07968 const char *func = "mos_global_trace"; 07969 07970 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 07971 /* Max order is 5 */ 07972 cpl_table *table; 07973 cpl_vector *c0; 07974 cpl_vector *cn; 07975 cpl_bivector *list; 07976 /* alternative (not robust) 07977 cpl_polynomial *poly; 07978 */ 07979 07980 double *offset; 07981 double rms, q, m; 07982 07983 int order, nrows, nslits; 07984 int i, j; 07985 07986 07987 if (polytraces == NULL) { 07988 cpl_msg_error(func, "Missing spectral curvature table"); 07989 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07990 } 07991 07992 if (slits == NULL) { 07993 cpl_msg_error(func, "Missing slits positions table"); 07994 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07995 } 07996 07997 nslits = cpl_table_get_nrow(slits); 07998 07999 table = cpl_table_duplicate(polytraces); 08000 cpl_table_erase_invalid(table); 08001 08002 nrows = cpl_table_get_nrow(table); 08003 08004 if (nrows < 4) { 08005 cpl_msg_warning(func, "Too few successful spectral curvature tracings " 08006 "(%d): the determination of a global curvature model " 08007 "failed", nrows); 08008 return CPL_ERROR_NONE; 08009 } 08010 08011 order = cpl_table_get_ncol(polytraces) - 2; 08012 08013 for (i = 0; i <= order; i++) { 08014 if (!cpl_table_has_column(table, clab[i])) { 08015 cpl_msg_error(func, "Wrong spectral curvature table"); 08016 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08017 } 08018 } 08019 08020 08021 /* 08022 * Fill in advance the missing offset terms 08023 */ 08024 08025 for (i = 0; i < nslits; i++) { 08026 if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) { 08027 cpl_table_set_double(polytraces, clab[0], 2*i, 08028 cpl_table_get_double(slits, "ytop", i, NULL)); 08029 } 08030 if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) { 08031 cpl_table_set_double(polytraces, clab[0], 2*i+1, 08032 cpl_table_get_double(slits, "ybottom", i, NULL)); 08033 } 08034 } 08035 08036 offset = cpl_table_get_data_double(polytraces, clab[0]); 08037 08038 08039 /* 08040 * Fit the global model and modify polytraces table accordingly 08041 */ 08042 08043 c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0])); 08044 08045 for (i = 1; i <= order; i++) { 08046 cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i])); 08047 list = cpl_bivector_wrap_vectors(c0, cn); 08048 robustLinearFit(list, &q, &m, &rms); 08049 /* alternative (not robust) 08050 poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL); 08051 */ 08052 for (j = 0; j < 2*nslits; j++) { 08053 if (mode == 1) 08054 if (cpl_table_is_valid(polytraces, clab[i], j)) 08055 continue; 08056 cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q); 08057 /* alternative (not robust) 08058 cpl_table_set_double(polytraces, clab[i], j, 08059 cpl_polynomial_eval_1d(poly, offset[j], NULL)); 08060 */ 08061 } 08062 cpl_bivector_unwrap_vectors(list); 08063 /* alternative (not robust) 08064 cpl_polynomial_delete(poly); 08065 */ 08066 cpl_vector_unwrap(cn); 08067 } 08068 08069 cpl_vector_unwrap(c0); 08070 cpl_table_delete(table); 08071 08072 return CPL_ERROR_NONE; 08073 08074 } 08075 08076 08146 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, 08147 cpl_table *polytraces, double reference, 08148 double blue, double red, double dispersion, 08149 int flux, cpl_image *calibration) 08150 { 08151 const char *func = "mos_spatial_calibration"; 08152 08153 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 08154 /* Max order is 5 */ 08155 cpl_polynomial *polytop; 08156 cpl_polynomial *polybot; 08157 cpl_image **exslit; 08158 cpl_image *resampled; 08159 float *data; 08160 float *sdata; 08161 float *xdata; 08162 double vtop, vbot, value; 08163 double top, bot; 08164 double coeff; 08165 double ytop, ybot; 08166 double ypos, yfra; 08167 double factor; 08168 int yint, ysize, yprev; 08169 int nslits; 08170 int npseudo; 08171 int *slit_id; 08172 int *length; 08173 int nx, ny; 08174 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 08175 int missing_top, missing_bot; 08176 int null; 08177 int order; 08178 int i, j; 08179 cpl_size k; 08180 08181 int create_position = 1; 08182 08183 08184 if (spectra == NULL || slits == NULL || polytraces == NULL) { 08185 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08186 return NULL; 08187 } 08188 08189 if (dispersion <= 0.0) { 08190 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08191 return NULL; 08192 } 08193 08194 if (red - blue < dispersion) { 08195 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08196 return NULL; 08197 } 08198 08199 nx = cpl_image_get_size_x(spectra); 08200 ny = cpl_image_get_size_y(spectra); 08201 sdata = cpl_image_get_data(spectra); 08202 if (calibration) 08203 data = cpl_image_get_data(calibration); 08204 08205 if (cpl_table_has_column(slits, "position")) 08206 create_position = 0; 08207 08208 if (create_position) { 08209 cpl_table_new_column(slits, "position", CPL_TYPE_INT); 08210 cpl_table_new_column(slits, "length", CPL_TYPE_INT); 08211 cpl_table_set_column_unit(slits, "position", "pixel"); 08212 cpl_table_set_column_unit(slits, "length", "pixel"); 08213 } 08214 else 08215 length = cpl_table_get_data_int(slits, "length"); 08216 08217 nslits = cpl_table_get_nrow(slits); 08218 slit_id = cpl_table_get_data_int(slits, "slit_id"); 08219 order = cpl_table_get_ncol(polytraces) - 2; 08220 08221 /* 08222 * The spatial resampling is performed for a certain number of 08223 * pixels above and below the position of the reference wavelength: 08224 */ 08225 08226 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 08227 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 08228 08229 exslit = cpl_calloc(nslits, sizeof(cpl_image *)); 08230 08231 for (i = 0; i < nslits; i++) { 08232 08233 if (create_position == 0) 08234 if (length[i] == 0) 08235 continue; 08236 08237 /* 08238 * Note that the x coordinate of the reference pixels on the CCD 08239 * is taken arbitrarily at the top end of each slit. This wouldn't 08240 * be entirely correct in case of curved slits, or in presence of 08241 * heavy distortions: in such cases the spatial resampling is 08242 * really performed across a wide range of wavelengths. But 08243 * the lag between top and bottom spectral curvature models 08244 * would introduce even in such cases negligible effects on 08245 * the spectral spatial resampling. 08246 */ 08247 08248 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 08249 08250 start_pixel = refpixel - pixel_below; 08251 if (start_pixel < 0) 08252 start_pixel = 0; 08253 08254 end_pixel = refpixel + pixel_above; 08255 if (end_pixel > nx) 08256 end_pixel = nx; 08257 08258 /* 08259 * Recover from the table of spectral curvature coefficients 08260 * the curvature polynomials. 08261 */ 08262 08263 missing_top = 0; 08264 polytop = cpl_polynomial_new(1); 08265 for (k = 0; k <= order; k++) { 08266 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 08267 if (null) { 08268 cpl_polynomial_delete(polytop); 08269 missing_top = 1; 08270 break; 08271 } 08272 cpl_polynomial_set_coeff(polytop, &k, coeff); 08273 } 08274 08275 missing_bot = 0; 08276 polybot = cpl_polynomial_new(1); 08277 for (k = 0; k <= order; k++) { 08278 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 08279 if (null) { 08280 cpl_polynomial_delete(polybot); 08281 missing_bot = 1; 08282 break; 08283 } 08284 cpl_polynomial_set_coeff(polybot, &k, coeff); 08285 } 08286 08287 if (missing_top && missing_bot) { 08288 cpl_msg_warning(func, "Spatial calibration, slit %d was not " 08289 "traced: no extraction!", 08290 slit_id[i]); 08291 continue; 08292 } 08293 08294 /* 08295 * In case just one of the two edges was not traced, the other 08296 * edge curvature model is duplicated and shifted to the other 08297 * end of the slit: better than nothing! 08298 */ 08299 08300 if (missing_top) { 08301 cpl_msg_warning(func, "Upper edge of slit %d was not traced: " 08302 "the spectral curvature of the lower edge " 08303 "is used instead.", slit_id[i]); 08304 polytop = cpl_polynomial_duplicate(polybot); 08305 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 08306 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 08307 k = 0; 08308 coeff = cpl_polynomial_get_coeff(polybot, &k); 08309 coeff += ytop - ybot; 08310 cpl_polynomial_set_coeff(polytop, &k, coeff); 08311 } 08312 08313 if (missing_bot) { 08314 cpl_msg_warning(func, "Lower edge of slit %d was not traced: " 08315 "the spectral curvature of the upper edge " 08316 "is used instead.", slit_id[i]); 08317 polybot = cpl_polynomial_duplicate(polytop); 08318 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 08319 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 08320 k = 0; 08321 coeff = cpl_polynomial_get_coeff(polytop, &k); 08322 coeff -= ytop - ybot; 08323 cpl_polynomial_set_coeff(polybot, &k, coeff); 08324 } 08325 08326 /* 08327 * Allocate image for current extracted slit 08328 */ 08329 08330 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 08331 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 08332 npseudo = ceil(top-bot) + 1; 08333 08334 if (npseudo < 1) { 08335 cpl_polynomial_delete(polytop); 08336 cpl_polynomial_delete(polybot); 08337 cpl_msg_warning(func, "Slit %d was badly traced: no extraction!", 08338 slit_id[i]); 08339 continue; 08340 } 08341 08342 exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT); 08343 xdata = cpl_image_get_data(exslit[i]); 08344 08345 /* 08346 * Write interpolated values to slit image. 08347 */ 08348 08349 for (j = start_pixel; j < end_pixel; j++) { 08350 top = cpl_polynomial_eval_1d(polytop, j, NULL); 08351 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 08352 factor = (top-bot)/npseudo; 08353 for (k = 0; k <= npseudo; k++) { 08354 ypos = top - k*factor; 08355 yint = ypos; 08356 yfra = ypos - yint; 08357 if (yint >= 0 && yint < ny-1) { 08358 vtop = sdata[j + nx*yint]; 08359 vbot = sdata[j + nx*(yint+1)]; 08360 value = vtop*(1-yfra) + vbot*yfra; 08361 if (flux) 08362 value *= factor; 08363 xdata[j + nx*(npseudo-k)] = value; 08364 if (calibration) { 08365 data[j + nx*yint] = (top-yint)/factor; 08366 if (k) { 08367 08368 /* 08369 * This is added to recover lost pixels on 08370 * the CCD image (pixels are lost because 08371 * the CCD pixels are less than npseudo+1). 08372 */ 08373 08374 if (yprev - yint > 1) { 08375 data[j + nx*(yint+1)] = (top-yint-1)/factor; 08376 } 08377 } 08378 } 08379 } 08380 yprev = yint; 08381 } 08382 } 08383 cpl_polynomial_delete(polytop); 08384 cpl_polynomial_delete(polybot); 08385 } 08386 08387 /* 08388 * Now all the slits images are copied to a single image 08389 */ 08390 08391 ysize = 0; 08392 for (i = 0; i < nslits; i++) 08393 if (exslit[i]) 08394 ysize += cpl_image_get_size_y(exslit[i]); 08395 08396 resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT); 08397 08398 yint = -1; 08399 for (i = 0; i < nslits; i++) { 08400 if (exslit[i]) { 08401 yint += cpl_image_get_size_y(exslit[i]); 08402 cpl_image_copy(resampled, exslit[i], 1, ysize - yint); 08403 if (create_position) { 08404 cpl_table_set_int(slits, "position", i, ysize - yint - 1); 08405 cpl_table_set_int(slits, "length", i, 08406 cpl_image_get_size_y(exslit[i])); 08407 } 08408 cpl_image_delete(exslit[i]); 08409 } 08410 else if (create_position) { 08411 cpl_table_set_int(slits, "position", i, -1); 08412 cpl_table_set_int(slits, "length", i, 0); 08413 } 08414 } 08415 08416 08417 /* 08418 * Elimination of non-traced slits from slit position table: we cannot do 08419 * it because we would lose sync with polytraces and other slit-oriented 08420 * tables. COMMENTED OUT. 08421 * 08422 08423 if (create_position) { 08424 08425 if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1)) 08426 cpl_table_erase_selected(slits); 08427 08428 } 08429 08430 */ 08431 08432 cpl_free(exslit); 08433 08434 return resampled; 08435 08436 } 08437 08438 08545 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits, 08546 cpl_vector *lines, 08547 double dispersion, float level, 08548 int sradius, int order, 08549 double reject, double refwave, 08550 double *wavestart, double *waveend, 08551 int *nlines, double *error, 08552 cpl_table *idscoeff, 08553 cpl_image *calibration, 08554 cpl_image *residuals, 08555 cpl_table *restable) 08556 { 08557 08558 const char *func = "mos_wavelength_calibration_final"; 08559 08560 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 08561 /* Max order is 5 */ 08562 08563 double tolerance = 20.0; /* Probably forever... */ 08564 int step = 10; /* Compute restable every "step" rows */ 08565 08566 char name[MAX_COLNAME]; 08567 08568 cpl_image *resampled; 08569 cpl_bivector *output; 08570 cpl_vector *wavel; 08571 cpl_vector *peaks; 08572 cpl_polynomial *ids; 08573 cpl_polynomial *lin; 08574 cpl_polynomial *fguess; 08575 cpl_table *coeff; 08576 double ids_err; 08577 double max_disp, min_disp; 08578 double *line; 08579 double firstLambda, lastLambda, lambda; 08580 double wave, pixe, value; 08581 double c; 08582 float *sdata; 08583 float *rdata; 08584 float *idata; 08585 float *ddata; 08586 float v1, v2, vi; 08587 float fpixel; 08588 int *length; 08589 int pixstart, pixend; 08590 int row_top, row_bot; 08591 int extrapolation; 08592 int nref; 08593 int nslits; 08594 int nfits; 08595 int nl, nx, ny, pixel; 08596 int countLines, usedLines; 08597 int uorder; 08598 int missing; 08599 int null; 08600 int width, uradius; 08601 int i, j, s; 08602 cpl_size k; 08603 08604 08605 if (dispersion == 0.0) { 08606 cpl_msg_error(func, "The expected dispersion (A/pixel) must be given"); 08607 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08608 return NULL; 08609 } 08610 08611 if (dispersion < 0.0) { 08612 cpl_msg_error(func, "The expected dispersion must be positive"); 08613 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08614 return NULL; 08615 } 08616 08617 if (idscoeff == NULL) { 08618 cpl_msg_error(func, "A preallocated IDS coeff table must be given"); 08619 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08620 return NULL; 08621 } 08622 08623 max_disp = dispersion + dispersion * tolerance / 100; 08624 min_disp = dispersion - dispersion * tolerance / 100; 08625 08626 if (order < 1) { 08627 cpl_msg_error(func, "The order of the fitting polynomial " 08628 "must be at least 1"); 08629 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08630 return NULL; 08631 } 08632 08633 if (image == NULL || lines == NULL) { 08634 cpl_msg_error(func, "Both spectral exposure and reference line " 08635 "catalog are required in input"); 08636 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08637 return NULL; 08638 } 08639 08640 nx = cpl_image_get_size_x(image); 08641 ny = cpl_image_get_size_y(image); 08642 sdata = cpl_image_get_data_float(image); 08643 08644 nref = cpl_vector_get_size(lines); 08645 line = cpl_vector_get_data(lines); 08646 08647 if (*wavestart < 1.0 && *waveend < 1.0) { 08648 firstLambda = line[0]; 08649 lastLambda = line[nref-1]; 08650 extrapolation = (lastLambda - firstLambda) / 10; 08651 firstLambda -= extrapolation; 08652 lastLambda += extrapolation; 08653 *wavestart = firstLambda; 08654 *waveend = lastLambda; 08655 } 08656 else { 08657 firstLambda = *wavestart; 08658 lastLambda = *waveend; 08659 } 08660 08661 nl = (lastLambda - firstLambda) / dispersion; 08662 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 08663 rdata = cpl_image_get_data_float(resampled); 08664 08665 /* 08666 * Allocate total output table of IDS coefficients 08667 */ 08668 08669 for (j = 0; j <= order; j++) 08670 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 08671 08672 if (calibration) 08673 idata = cpl_image_get_data_float(calibration); 08674 08675 if (residuals) 08676 ddata = cpl_image_get_data_float(residuals); 08677 08678 if (restable) { 08679 cpl_table_set_size(restable, nref); 08680 cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE); 08681 cpl_table_copy_data_double(restable, "wavelength", line); 08682 for (i = 0; i < ny; i += step) { 08683 snprintf(name, MAX_COLNAME, "r%d", i); 08684 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08685 snprintf(name, MAX_COLNAME, "d%d", i); 08686 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08687 snprintf(name, MAX_COLNAME, "p%d", i); 08688 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08689 } 08690 } 08691 08692 08693 /* 08694 * Process all slits separately. 08695 */ 08696 08697 nslits = cpl_table_get_nrow(slits); 08698 length = cpl_table_get_data_int(slits, "length"); 08699 08700 row_top = ny; 08701 for (s = 0; s < nslits; s++) { 08702 08703 if (length[s] == 0) 08704 continue; 08705 08706 /* 08707 * row_top and row_bot define the boundaries of the current slit. 08708 * Here we begin (arbitrarily...) from the top slit. 08709 */ 08710 08711 row_bot = cpl_table_get_int(slits, "position", s, NULL); 08712 08713 if (sradius > 0) { 08714 08715 /* 08716 * If a search radius was defined, allocate the table of 08717 * the fitting polynomials coefficients. This table is 08718 * just used to generate the first-guess polynomial made 08719 * of the median coefficients of all polynomials found 08720 * for this slit. 08721 */ 08722 08723 coeff = cpl_table_new(row_top - row_bot); 08724 for (j = 0; j <= order; j++) 08725 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE); 08726 } 08727 08728 /* 08729 * Here is the loop on all rows of the current slit. They are 08730 * wavelength calibrated one by one. 08731 */ 08732 08733 for (i = row_bot; i < row_top; i++) { 08734 width = mos_lines_width(sdata + i*nx, nx); 08735 if (width < 5) 08736 width = 5; 08737 peaks = mos_peak_candidates(sdata + i*nx, nx, level, width); 08738 if (peaks) { 08739 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width); 08740 } 08741 if (peaks) { 08742 int keep_multiplex = mos_multiplex; 08743 mos_multiplex = -1; 08744 output = mos_identify_peaks(peaks, lines, 08745 min_disp, max_disp, 0.05); 08746 mos_multiplex = keep_multiplex; 08747 if (output) { 08748 countLines = cpl_bivector_get_size(output); 08749 if (countLines < 4) { 08750 cpl_bivector_delete(output); 08751 cpl_vector_delete(peaks); 08752 if (nlines) 08753 nlines[i] = 0; 08754 if (error) 08755 error[i] = 0.0; 08756 continue; 08757 } 08758 08759 /* 08760 * Set reference wavelength as zero point 08761 */ 08762 08763 wavel = cpl_bivector_get_y(output); 08764 cpl_vector_subtract_scalar(wavel, refwave); 08765 08766 uorder = countLines / 2 - 1; 08767 if (uorder > order) 08768 uorder = order; 08769 08770 ids = mos_poly_wav2pix(output, uorder, reject, 08771 2 * (uorder + 1), &usedLines, 08772 &ids_err); 08773 08774 if (ids == NULL) { 08775 cpl_bivector_delete(output); 08776 cpl_vector_delete(peaks); 08777 if (nlines) 08778 nlines[i] = 0; 08779 if (error) 08780 error[i] = 0.0; 08781 cpl_error_reset(); 08782 continue; 08783 } 08784 08785 if (sradius > 0) { 08786 for (k = 0; k <= order; k++) { 08787 if (k > uorder) { 08788 cpl_table_set_double(coeff, clab[k], 08789 i - row_bot, 0.0); 08790 } 08791 else { 08792 cpl_table_set_double(coeff, clab[k], 08793 i - row_bot, cpl_polynomial_get_coeff(ids, &k)); 08794 } 08795 } 08796 } 08797 /* else { */ 08798 if (calibration) { 08799 pixstart = cpl_polynomial_eval_1d(ids, 08800 cpl_bivector_get_y_data(output)[0], 08801 NULL); 08802 pixend = cpl_polynomial_eval_1d(ids, 08803 cpl_bivector_get_y_data(output)[countLines-1], 08804 NULL); 08805 extrapolation = (pixend - pixstart) / 5; 08806 pixstart -= extrapolation; 08807 pixend += extrapolation; 08808 if (pixstart < 0) 08809 pixstart = 0; 08810 if (pixend > nx) 08811 pixend = nx; 08812 08813 for (j = pixstart; j < pixend; j++) { 08814 (idata + i*nx)[j] = mos_eval_dds(ids, 08815 firstLambda, lastLambda, refwave, j); 08816 } 08817 } 08818 08819 /* 08820 * Residuals image 08821 */ 08822 08823 if (residuals || (restable && !(i%step))) { 08824 if (restable && !(i%step)) { 08825 lin = cpl_polynomial_new(1); 08826 for (k = 0; k < 2; k++) 08827 cpl_polynomial_set_coeff(lin, &k, 08828 cpl_polynomial_get_coeff(ids, &k)); 08829 } 08830 for (j = 0; j < countLines; j++) { 08831 pixe = cpl_bivector_get_x_data(output)[j]; 08832 wave = cpl_bivector_get_y_data(output)[j]; 08833 value = pixe 08834 - cpl_polynomial_eval_1d(ids, wave, NULL); 08835 if (residuals) { 08836 pixel = pixe + 0.5; 08837 (ddata + i*nx)[pixel] = value; 08838 } 08839 if (restable && !(i%step)) { 08840 for (k = 0; k < nref; k++) { 08841 if (fabs(line[k]-refwave-wave) < 0.1) { 08842 snprintf(name, MAX_COLNAME, 08843 "r%d", i); 08844 cpl_table_set_double(restable, name, 08845 k, value); 08846 value = pixe 08847 - cpl_polynomial_eval_1d(lin, 08848 wave, NULL); 08849 snprintf(name, MAX_COLNAME, 08850 "d%d", i); 08851 cpl_table_set_double(restable, name, 08852 k, value); 08853 snprintf(name, MAX_COLNAME, 08854 "p%d", i); 08855 cpl_table_set_double(restable, name, 08856 k, pixe); 08857 break; 08858 } 08859 } 08860 } 08861 } 08862 if (restable && !(i%step)) { 08863 cpl_polynomial_delete(lin); 08864 } 08865 /*** 08866 for (j = 0; j < countLines; j++) { 08867 pixel = cpl_bivector_get_x_data(output)[j] 08868 + 0.5; 08869 (ddata + i*nx)[pixel] = 08870 cpl_bivector_get_x_data(output)[j] 08871 - cpl_polynomial_eval_1d(ids, 08872 cpl_bivector_get_y_data(output)[j], 08873 NULL); 08874 } 08875 ***/ 08876 } 08877 /* } */ 08878 08879 /* 08880 * Write it anyway, even in case a first-guess based 08881 * solution will be searched afterwards: in case of 08882 * failure, the "blind" solution is kept. 08883 */ 08884 08885 if (nlines) 08886 nlines[i] = usedLines; 08887 if (error) 08888 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 08889 08890 for (k = 0; k <= order; k++) { 08891 if (k > uorder) { 08892 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 08893 } 08894 else { 08895 cpl_table_set_double(idscoeff, clab[k], i, 08896 cpl_polynomial_get_coeff(ids, &k)); 08897 } 08898 } 08899 08900 cpl_polynomial_delete(ids); 08901 cpl_bivector_delete(output); 08902 } 08903 cpl_vector_delete(peaks); 08904 } 08905 } /* End of loop on current slit's rows */ 08906 08907 08908 if (sradius > 0) { 08909 08910 /* 08911 * See whether there are valid fits at all... 08912 */ 08913 08914 nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]); 08915 08916 if (nfits) { 08917 int slope = 0; 08918 08919 fguess = cpl_polynomial_new(1); 08920 08921 if (mos_interpolate_wavecalib(coeff, NULL, 2, 1)) { 08922 08923 slope = 0; 08924 08925 /* 08926 * Compute a median IDS polynomial for the current slit 08927 */ 08928 08929 for (k = 0; k <= order; k++) { 08930 c = cpl_table_get_column_median(coeff, clab[k]); 08931 cpl_polynomial_set_coeff(fguess, &k, c); 08932 } 08933 } 08934 else { 08935 slope = 1; 08936 } 08937 08938 for (i = row_bot; i < row_top; i++) { 08939 08940 /* 08941 * Use first-guess to find the reference lines again 08942 */ 08943 08944 width = mos_lines_width(sdata + i*nx, nx); 08945 if (width > sradius) { 08946 uradius = width; 08947 } 08948 else { 08949 uradius = sradius; 08950 } 08951 08952 if (slope) { 08953 for (k = 0; k <= order; k++) { 08954 c = cpl_table_get_double(coeff, clab[k], 08955 i - row_bot, NULL); 08956 cpl_polynomial_set_coeff(fguess, &k, c); 08957 } 08958 } 08959 08960 output = mos_find_peaks(sdata + i*nx, nx, lines, 08961 fguess, refwave, uradius); 08962 08963 if (output == NULL) { 08964 cpl_error_reset(); 08965 continue; 08966 } 08967 08968 countLines = cpl_bivector_get_size(output); 08969 08970 if (countLines < 4) { 08971 cpl_bivector_delete(output); 08972 continue; 08973 } 08974 08975 /* 08976 * Set reference wavelength as zero point 08977 */ 08978 08979 wavel = cpl_bivector_get_y(output); 08980 cpl_vector_subtract_scalar(wavel, refwave); 08981 08982 uorder = countLines / 2 - 1; 08983 if (uorder > order) 08984 uorder = order; 08985 08986 ids = mos_poly_wav2pix(output, uorder, reject, 08987 2 * (uorder + 1), &usedLines, 08988 &ids_err); 08989 08990 if (ids == NULL) { 08991 cpl_error_reset(); 08992 cpl_bivector_delete(output); 08993 continue; 08994 } 08995 08996 if (nlines) 08997 nlines[i] = usedLines; 08998 if (error) 08999 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 09000 09001 if (calibration) { 09002 pixstart = cpl_polynomial_eval_1d(ids, 09003 cpl_bivector_get_y_data(output)[0], 09004 NULL); 09005 pixend = cpl_polynomial_eval_1d(ids, 09006 cpl_bivector_get_y_data(output)[countLines-1], 09007 NULL); 09008 extrapolation = (pixend - pixstart) / 5; 09009 pixstart -= extrapolation; 09010 pixend += extrapolation; 09011 if (pixstart < 0) 09012 pixstart = 0; 09013 if (pixend > nx) 09014 pixend = nx; 09015 09016 for (j = pixstart; j < pixend; j++) { 09017 (idata + i*nx)[j] = mos_eval_dds(ids, 09018 firstLambda, lastLambda, refwave, j); 09019 } 09020 } 09021 09022 /* 09023 * Residuals image 09024 */ 09025 09026 if (residuals || (restable && !(i%step))) { 09027 if (restable && !(i%step)) { 09028 lin = cpl_polynomial_new(1); 09029 for (k = 0; k < 2; k++) 09030 cpl_polynomial_set_coeff(lin, &k, 09031 cpl_polynomial_get_coeff(ids, &k)); 09032 } 09033 for (j = 0; j < countLines; j++) { 09034 pixe = cpl_bivector_get_x_data(output)[j]; 09035 wave = cpl_bivector_get_y_data(output)[j]; 09036 value = pixe 09037 - cpl_polynomial_eval_1d(ids, wave, NULL); 09038 if (residuals) { 09039 pixel = pixe + 0.5; 09040 (ddata + i*nx)[pixel] = value; 09041 } 09042 if (restable && !(i%step)) { 09043 for (k = 0; k < nref; k++) { 09044 if (fabs(line[k]-refwave-wave) < 0.1) { 09045 snprintf(name, MAX_COLNAME, 09046 "r%d", i); 09047 cpl_table_set_double(restable, name, 09048 k, value); 09049 value = pixe 09050 - cpl_polynomial_eval_1d(lin, 09051 wave, NULL); 09052 snprintf(name, MAX_COLNAME, 09053 "d%d", i); 09054 cpl_table_set_double(restable, name, 09055 k, value); 09056 snprintf(name, MAX_COLNAME, 09057 "p%d", i); 09058 cpl_table_set_double(restable, name, 09059 k, pixe); 09060 break; 09061 } 09062 } 09063 } 09064 } 09065 if (restable && !(i%step)) { 09066 cpl_polynomial_delete(lin); 09067 } 09068 /*** 09069 for (j = 0; j < countLines; j++) { 09070 pixel = cpl_bivector_get_x_data(output)[j] 09071 + 0.5; 09072 (ddata + i*nx)[pixel] = 09073 cpl_bivector_get_x_data(output)[j] 09074 - cpl_polynomial_eval_1d(ids, 09075 cpl_bivector_get_y_data(output)[j], 09076 NULL); 09077 } 09078 ***/ 09079 } 09080 09081 for (k = 0; k <= order; k++) { 09082 if (k > uorder) { 09083 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 09084 } 09085 else { 09086 cpl_table_set_double(idscoeff, clab[k], i, 09087 cpl_polynomial_get_coeff(ids, &k)); 09088 } 09089 } 09090 09091 cpl_bivector_delete(output); 09092 cpl_polynomial_delete(ids); 09093 09094 } /* End of loop "use ids as a first-guess" */ 09095 09096 cpl_polynomial_delete(fguess); 09097 } 09098 09099 cpl_table_delete(coeff); 09100 09101 } 09102 09103 row_top = row_bot; 09104 09105 } /* End of loop on slits */ 09106 09107 09108 /* 09109 * At this point the idscoeff table has been filled with all the 09110 * fits coefficients obtained for all the rows of the input image. 09111 * Now we apply these coefficients to resample the input image 09112 * at constant wavelength step. 09113 */ 09114 09115 for (i = 0; i < ny; i++) { 09116 09117 missing = 0; 09118 ids = cpl_polynomial_new(1); 09119 for (k = 0; k <= order; k++) { 09120 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 09121 if (null) { 09122 cpl_polynomial_delete(ids); 09123 missing = 1; 09124 break; 09125 } 09126 cpl_polynomial_set_coeff(ids, &k, c); 09127 } 09128 if (missing) 09129 continue; 09130 09131 pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL); 09132 pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL); 09133 if (pixstart < 0) 09134 pixstart = 0; 09135 if (pixend > nx) 09136 pixend = nx; 09137 09138 /* 09139 * Resampled image: 09140 */ 09141 09142 for (j = 0; j < nl; j++) { 09143 lambda = firstLambda + j * dispersion; 09144 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL); 09145 pixel = fpixel; 09146 if (pixel >= 0 && pixel < nx-1) { 09147 v1 = (sdata + i*nx)[pixel]; 09148 v2 = (sdata + i*nx)[pixel+1]; 09149 vi = v1 + (v2-v1)*(fpixel-pixel); 09150 (rdata + i*nl)[j] = vi; 09151 } 09152 } 09153 09154 cpl_polynomial_delete(ids); 09155 } 09156 09157 return resampled; 09158 } 09159 09160 09187 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 09188 double firstLambda, double lastLambda, 09189 double dispersion, cpl_table *idscoeff, 09190 int flux) 09191 { 09192 09193 const char *func = "mos_wavelength_calibration"; 09194 09195 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 09196 /* Max order is 5 */ 09197 09198 cpl_image *resampled; 09199 cpl_polynomial *ids; 09200 double pixel_per_lambda; 09201 double lambda; 09202 double c; 09203 float *sdata; 09204 float *rdata; 09205 float v0, v1, v2, v3, vi; 09206 float fpixel; 09207 int order; 09208 int pixstart, pixend; 09209 int nl, nx, ny, pixel; 09210 int missing; 09211 int null; 09212 int i, j; 09213 cpl_size k; 09214 09215 09216 if (dispersion <= 0.0) { 09217 cpl_msg_error(func, "The resampling step must be positive"); 09218 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 09219 return NULL; 09220 } 09221 09222 if (lastLambda - firstLambda < dispersion) { 09223 cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 09224 firstLambda, lastLambda); 09225 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 09226 return NULL; 09227 } 09228 09229 if (idscoeff == NULL) { 09230 cpl_msg_error(func, "An IDS coeff table must be given"); 09231 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09232 return NULL; 09233 } 09234 09235 if (image == NULL) { 09236 cpl_msg_error(func, "A scientific spectral image must be given"); 09237 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09238 return NULL; 09239 } 09240 09241 nx = cpl_image_get_size_x(image); 09242 ny = cpl_image_get_size_y(image); 09243 sdata = cpl_image_get_data_float(image); 09244 09245 nl = (lastLambda - firstLambda) / dispersion; 09246 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 09247 rdata = cpl_image_get_data_float(resampled); 09248 09249 order = 0; 09250 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 09251 ++order; 09252 --order; 09253 09254 for (i = 0; i < ny; i++) { 09255 09256 missing = 0; 09257 ids = cpl_polynomial_new(1); 09258 for (k = 0; k <= order; k++) { 09259 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 09260 if (null) { 09261 cpl_polynomial_delete(ids); 09262 missing = 1; 09263 break; 09264 } 09265 cpl_polynomial_set_coeff(ids, &k, c); 09266 } 09267 if (missing) 09268 continue; 09269 09270 pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL); 09271 pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL); 09272 if (pixstart < 0) 09273 pixstart = 0; 09274 if (pixend > nx) 09275 pixend = nx; 09276 09277 /* 09278 * Resampled image: 09279 */ 09280 09281 for (j = 0; j < nl; j++) { 09282 lambda = firstLambda + j * dispersion; 09283 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 09284 &pixel_per_lambda); 09285 09286 /* 09287 * The local dispersion is 1 / pixel_per_lambda 09288 * and this factor is used for applying the flux 09289 * conservation correction (if requested). 09290 */ 09291 09292 pixel = fpixel; 09293 09294 // if (dispersion * pixel_per_lambda < 2.0) { 09295 if (1) { /* Old behaviour: this is safe. */ 09296 09297 /* 09298 * In this case we just sample interpolating the 09299 * signal of nearby pixels. 09300 */ 09301 09302 if (pixel >= 1 && pixel < nx-2) { 09303 v0 = (sdata + i*nx)[pixel-1]; 09304 v1 = (sdata + i*nx)[pixel]; 09305 v2 = (sdata + i*nx)[pixel+1]; 09306 v3 = (sdata + i*nx)[pixel+2]; 09307 vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0) 09308 + (fpixel-pixel)*(3*v2 - v3 - v1 - v0) 09309 + 2*v1; 09310 vi /= 2; 09311 if (v1 > v2) { 09312 if (vi > v1) { 09313 vi = v1; 09314 } 09315 else if (vi < v2) { 09316 vi = v2; 09317 } 09318 } 09319 else { 09320 if (vi > v2) { 09321 vi = v2; 09322 } 09323 else if (vi < v1) { 09324 vi = v1; 09325 } 09326 } 09327 if (flux) 09328 vi *= dispersion * pixel_per_lambda; 09329 (rdata + i*nl)[j] = vi; 09330 } 09331 else if (pixel >= 0 && pixel < nx-1) { 09332 v1 = (sdata + i*nx)[pixel]; 09333 v2 = (sdata + i*nx)[pixel+1]; 09334 vi = v1 + (v2-v1)*(fpixel-pixel); 09335 if (flux) 09336 vi *= dispersion * pixel_per_lambda; 09337 (rdata + i*nl)[j] = vi; 09338 } 09339 } 09340 else { 09341 09342 /* 09343 * Here instead we integrate the pixel values in 09344 * the interval centered at the interpolation point. 09345 * This interval is long dispersion * pixel_per_lambda 09346 * of the original pixels, and is centered at fpixel. 09347 * So it starts at fpixel - dispersion * pixel_per_lambda / 2, 09348 * and it ends at fpixel + dispersion * pixel_per_lambda / 2. 09349 */ 09350 09351 double spos = fpixel - dispersion * pixel_per_lambda / 2; 09352 double epos = fpixel + dispersion * pixel_per_lambda / 2; 09353 09354 /* 09355 * Brutal sum over all involved pixels 09356 */ 09357 09358 int spix = spos; 09359 int epix = epos + 1; 09360 09361 if (spix < 0) 09362 spix = 0; 09363 09364 if (epix > nx) 09365 epix = nx; 09366 09367 vi = 0.0; 09368 for (k = spix; k < epix; k++) { 09369 if (pixel >= 0 && pixel < nx) { 09370 vi += (sdata + i*nx)[k]; 09371 } 09372 } 09373 09374 /* 09375 * Correct integrated flux by true length 09376 * of interval. This is clearly an approximation, 09377 * but it's good enough if the PSF is much larger 09378 * than the original pix. 09379 */ 09380 09381 vi *= dispersion * pixel_per_lambda / (epix - spix); 09382 09383 /* 09384 * Flux conservation is a geometric factor that is applied 09385 * always in the same way... 09386 */ 09387 09388 if (flux) 09389 vi *= dispersion * pixel_per_lambda; 09390 09391 (rdata + i*nl)[j] = vi; 09392 } 09393 } 09394 09395 cpl_polynomial_delete(ids); 09396 } 09397 09398 return resampled; 09399 } 09400 09401 09468 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 09469 double refwave, double firstLambda, 09470 double lastLambda, cpl_table *idscoeff, 09471 cpl_vector *skylines, int highres, int order, 09472 cpl_image *calibration, int sradius) 09473 { 09474 const char *func = "mos_wavelength_align"; 09475 09476 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 09477 /* Max order is 5 */ 09478 double *line; 09479 double *data; 09480 double expPos, offset; 09481 double c; 09482 double lambda1, lambda2; 09483 double rms; 09484 float pos; 09485 float *sdata; 09486 float *cdata; 09487 int *idata; 09488 int startPos, endPos; 09489 int window = 2*sradius + 1; 09490 int nlines; 09491 int nslits; 09492 int npoints; 09493 int nrows; 09494 int nx, ny; 09495 int xlow, ylow, xhig, yhig; 09496 int idsorder, uorder; 09497 int *slit_id; 09498 int *position; 09499 int *length; 09500 int missing; 09501 int null; 09502 int i; 09503 cpl_size j, k; 09504 09505 char offname[MAX_COLNAME]; 09506 char name[MAX_COLNAME]; 09507 09508 cpl_polynomial *ids; 09509 cpl_polynomial *polycorr; 09510 cpl_image *exslit; 09511 cpl_image *sky; 09512 cpl_table *offsets; 09513 cpl_table *dummy; 09514 cpl_vector *wave; 09515 cpl_vector *offs; 09516 09517 09518 if (idscoeff == NULL) { 09519 cpl_msg_error(func, "An IDS coeff table must be given"); 09520 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09521 return NULL; 09522 } 09523 09524 if (image == NULL) { 09525 cpl_msg_error(func, "A scientific spectral image must be given"); 09526 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09527 return NULL; 09528 } 09529 09530 if (slits == NULL) { 09531 cpl_msg_error(func, "A slit position table must be given"); 09532 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09533 return NULL; 09534 } 09535 09536 if (skylines) { 09537 line = cpl_vector_get_data(skylines); 09538 nlines = cpl_vector_get_size(skylines); 09539 } 09540 else { 09541 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 09542 "given: using internal list of reference sky lines"); 09543 if (highres) { 09544 line = default_lines_hi; 09545 nlines = sizeof(default_lines_hi) / sizeof(double); 09546 } 09547 else { 09548 line = default_lines_lo; 09549 nlines = sizeof(default_lines_lo) / sizeof(double); 09550 } 09551 } 09552 09553 if (calibration) 09554 cdata = cpl_image_get_data(calibration); 09555 09556 nx = cpl_image_get_size_x(image); 09557 ny = cpl_image_get_size_y(image); 09558 09559 nslits = cpl_table_get_nrow(slits); 09560 slit_id = cpl_table_get_data_int(slits, "slit_id"); 09561 position = cpl_table_get_data_int(slits, "position"); 09562 length = cpl_table_get_data_int(slits, "length"); 09563 09564 09565 /* 09566 * Define the output table of offsets 09567 */ 09568 09569 nrows = 0; 09570 for (i = 0; i < nlines; i++) 09571 if (line[i] > firstLambda && line[i] < lastLambda) 09572 nrows++; 09573 09574 offsets = cpl_table_new(nrows); 09575 cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE); 09576 cpl_table_set_column_unit(offsets, "wave", "Angstrom"); 09577 09578 nrows = 0; 09579 for (i = 0; i < nlines; i++) { 09580 if (line[i] > firstLambda && line[i] < lastLambda) { 09581 cpl_table_set_double(offsets, "wave", nrows, line[i]); 09582 nrows++; 09583 } 09584 } 09585 09586 /* 09587 * Here "line" is made to point to the new list of selected wavelengths 09588 */ 09589 09590 line = cpl_table_get_data_double(offsets, "wave"); 09591 nlines = nrows; 09592 09593 idsorder = 0; 09594 while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder])) 09595 ++idsorder; 09596 --idsorder; 09597 09598 xlow = 1; 09599 xhig = nx; 09600 for (i = 0; i < nslits; i++) { 09601 09602 if (length[i] == 0) 09603 continue; 09604 09605 snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]); 09606 cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE); 09607 09608 /* 09609 * Define the extraction boundaries. We DON'T write: 09610 * 09611 * ylow = position[i]; 09612 * yhig = ylow + length[i]; 09613 * 09614 * because the cpl_image pixels are counted from 1, and because in 09615 * cpl_image_extract() the coordinates of the last pixel are inclusive. 09616 */ 09617 09618 ylow = position[i] + 1; 09619 yhig = ylow + length[i] - 1; 09620 09621 exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig); 09622 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 09623 sdata = cpl_image_get_data(sky); 09624 09625 cpl_image_delete(exslit); 09626 09627 /* 09628 * Return here to a decent way of counting pixels (i.e., starting 09629 * from 0) 09630 */ 09631 09632 ylow--; 09633 09634 /* 09635 * Allocate a dummy table for collecting all the offsets 09636 * for all the lines: this is only needed for the computation 09637 * of the median offset for each sky line 09638 */ 09639 09640 dummy = cpl_table_new(yhig - ylow); 09641 for (j = 0; j < nlines; j++) { 09642 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j); 09643 cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE); 09644 } 09645 09646 for (j = ylow; j < yhig; j++) { 09647 09648 /* 09649 * Get the IDS polynomial for the current slit row 09650 */ 09651 09652 missing = 0; 09653 ids = cpl_polynomial_new(1); 09654 for (k = 0; k <= idsorder; k++) { 09655 c = cpl_table_get_double(idscoeff, clab[k], j, &null); 09656 if (null) { 09657 cpl_polynomial_delete(ids); 09658 missing = 1; 09659 break; 09660 } 09661 cpl_polynomial_set_coeff(ids, &k, c); 09662 } 09663 if (missing) 09664 continue; 09665 09666 for (k = 0; k < nlines; k++) { 09667 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL); 09668 startPos = expPos - sradius; 09669 endPos = startPos + window; 09670 if (startPos < 0 || endPos >= nx) 09671 continue; 09672 09673 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) { 09674 pos += startPos; 09675 offset = pos - expPos; 09676 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k); 09677 cpl_table_set_double(dummy, name, j - ylow, offset); 09678 } 09679 } 09680 09681 cpl_polynomial_delete(ids); 09682 } 09683 09684 cpl_image_delete(sky); 09685 09686 for (j = 0; j < nlines; j++) { 09687 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j); 09688 if (cpl_table_has_valid(dummy, name)) { 09689 offset = cpl_table_get_column_median(dummy, name); 09690 cpl_table_set_double(offsets, offname, j, offset); 09691 } 09692 } 09693 09694 cpl_table_delete(dummy); 09695 09696 } 09697 09698 09699 /* 09700 * In the following the input idscoeff table is modified by simply 09701 * adding the coefficients of the polynomial used to fit the sky 09702 * line residuals to the coefficients of the IDS polynomials. 09703 */ 09704 09705 for (i = 0; i < nslits; i++) { 09706 09707 if (length[i] == 0) 09708 continue; 09709 09710 snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]); 09711 09712 /* 09713 * In the following, the "dummy" table is just a tool for 09714 * eliminating invalid points from the vectors to be fitted. 09715 */ 09716 09717 dummy = cpl_table_new(nlines); 09718 cpl_table_duplicate_column(dummy, "wave", offsets, "wave"); 09719 cpl_table_duplicate_column(dummy, "offset", offsets, offname); 09720 09721 npoints = nlines - cpl_table_count_invalid(dummy, "offset"); 09722 if (npoints == 0) { 09723 cpl_msg_warning(func, "No sky lines alignment was possible " 09724 "for slit ID=%d: no sky line found", slit_id[i]); 09725 cpl_table_delete(dummy); 09726 continue; 09727 } 09728 09729 uorder = order; 09730 if (npoints <= uorder) { 09731 uorder = npoints - 1; 09732 if (uorder) { 09733 cpl_msg_warning(func, "Just %d sky lines detected for slit " 09734 "ID=%d, while a polynomial order %d was " 09735 "requested. Using polynomial order %d for " 09736 "this slit!", npoints, slit_id[i], order, 09737 uorder); 09738 } 09739 else { 09740 cpl_msg_warning(func, "Just %d sky lines detected for slit " 09741 "ID=%d, while a polynomial order %d was " 09742 "requested. Computing a median offset for " 09743 "this slit!", npoints, slit_id[i], order); 09744 } 09745 } 09746 09747 cpl_table_erase_invalid(dummy); 09748 09749 if (uorder > 1) { 09750 09751 /* 09752 * Model offsets with polynomial fitting 09753 */ 09754 09755 wave = cpl_vector_wrap(npoints, 09756 cpl_table_get_data_double(dummy, "wave")); 09757 offs = cpl_vector_wrap(npoints, 09758 cpl_table_get_data_double(dummy, "offset")); 09759 09760 /* 09761 * Set reference wavelength as zero point 09762 */ 09763 09764 cpl_vector_subtract_scalar(wave, refwave); 09765 09766 polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms); 09767 09768 rms = sqrt(rms * (uorder + 1) / npoints); 09769 09770 cpl_vector_unwrap(wave); 09771 cpl_vector_unwrap(offs); 09772 cpl_table_delete(dummy); 09773 09774 /* 09775 * Now correct the coefficients of the corresponding IDS 09776 * polynomials related to this slit: 09777 */ 09778 09779 ylow = position[i]; 09780 yhig = ylow + length[i]; 09781 09782 for (j = 0; j <= uorder; j++) { 09783 data = cpl_table_get_data_double(idscoeff, clab[j]); 09784 c = cpl_polynomial_get_coeff(polycorr, &j); 09785 for (k = ylow; k < yhig; k++) 09786 data[k] += c; 09787 } 09788 09789 data = cpl_table_get_data_double(idscoeff, "error"); 09790 for (k = ylow; k < yhig; k++) 09791 data[k] = sqrt(data[k]*data[k] + rms*rms); 09792 09793 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09794 for (k = ylow; k < yhig; k++) 09795 idata[k] = npoints; 09796 09797 /* 09798 * If a wavelengths map was provided, correct it to keep 09799 * into account the alignment to skylines: 09800 */ 09801 09802 if (calibration) { 09803 for (j = ylow; j < yhig; j++) { 09804 for (k = 1; k < nx; k++) { 09805 lambda1 = cdata[k - 1 + j*nx]; 09806 lambda2 = cdata[k + j*nx]; 09807 if (lambda1 < 1.0 || lambda2 < 1.0) 09808 continue; 09809 offset = cpl_polynomial_eval_1d(polycorr, 09810 lambda1-refwave, NULL); 09811 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09812 } 09813 } 09814 } 09815 09816 cpl_polynomial_delete(polycorr); 09817 } 09818 else if (uorder == 1) { 09819 09820 /* 09821 * Model offsets with robust linear fitting 09822 */ 09823 09824 double q, m; 09825 cpl_bivector *list; 09826 09827 09828 wave = cpl_vector_wrap(npoints, 09829 cpl_table_get_data_double(dummy, "wave")); 09830 offs = cpl_vector_wrap(npoints, 09831 cpl_table_get_data_double(dummy, "offset")); 09832 09833 list = cpl_bivector_wrap_vectors(wave, offs); 09834 09835 /* 09836 * Set reference wavelength as zero point 09837 */ 09838 09839 cpl_vector_subtract_scalar(wave, refwave); 09840 09841 robustLinearFit(list, &q, &m, &rms); 09842 09843 rms = sqrt(rms * (uorder + 1) / npoints); 09844 09845 cpl_bivector_unwrap_vectors(list); 09846 cpl_vector_unwrap(wave); 09847 cpl_vector_unwrap(offs); 09848 cpl_table_delete(dummy); 09849 09850 /* 09851 * Now correct the coefficients of the corresponding IDS 09852 * polynomials related to this slit: 09853 */ 09854 09855 ylow = position[i]; 09856 yhig = ylow + length[i]; 09857 09858 for (j = 0; j <= uorder; j++) { 09859 data = cpl_table_get_data_double(idscoeff, clab[j]); 09860 if (j) 09861 c = m; 09862 else 09863 c = q; 09864 for (k = ylow; k < yhig; k++) 09865 data[k] += c; 09866 } 09867 09868 data = cpl_table_get_data_double(idscoeff, "error"); 09869 for (k = ylow; k < yhig; k++) 09870 data[k] = sqrt(data[k]*data[k] + rms*rms); 09871 09872 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09873 for (k = ylow; k < yhig; k++) 09874 idata[k] = npoints; 09875 09876 /* 09877 * If a wavelengths map was provided, correct it to keep 09878 * into account the alignment to skylines: 09879 */ 09880 09881 if (calibration) { 09882 for (j = ylow; j < yhig; j++) { 09883 for (k = 1; k < nx; k++) { 09884 lambda1 = cdata[k - 1 + j*nx]; 09885 lambda2 = cdata[k + j*nx]; 09886 if (lambda1 < 1.0 || lambda2 < 1.0) 09887 continue; 09888 offset = q + m*(lambda1-refwave); 09889 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09890 } 09891 } 09892 } 09893 } 09894 else { 09895 09896 /* 09897 * Just compute median offset 09898 */ 09899 09900 offs = cpl_vector_wrap(npoints, 09901 cpl_table_get_data_double(dummy, "offset")); 09902 09903 offset = cpl_vector_get_median_const(offs); 09904 09905 if (npoints > 1) 09906 rms = cpl_table_get_column_stdev(dummy, "offset"); 09907 else 09908 rms = 0.0; 09909 09910 rms /= sqrt(npoints); 09911 09912 cpl_vector_unwrap(offs); 09913 cpl_table_delete(dummy); 09914 09915 /* 09916 * Now correct the constant term of the corresponding IDS 09917 * polynomials related to this slit: 09918 */ 09919 09920 ylow = position[i]; 09921 yhig = ylow + length[i]; 09922 09923 data = cpl_table_get_data_double(idscoeff, clab[0]); 09924 for (k = ylow; k < yhig; k++) 09925 data[k] += offset; 09926 09927 data = cpl_table_get_data_double(idscoeff, "error"); 09928 for (k = ylow; k < yhig; k++) 09929 data[k] = sqrt(data[k]*data[k] + rms*rms); 09930 09931 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09932 for (k = ylow; k < yhig; k++) 09933 idata[k] = npoints; 09934 09935 /* 09936 * If a wavelengths map was provided, correct it to keep 09937 * into account the alignment to skylines. Note that 09938 * the offset must be converted from pixels to wavelengths. 09939 */ 09940 09941 if (calibration) { 09942 for (j = ylow; j < yhig; j++) { 09943 for (k = 1; k < nx; k++) { 09944 lambda1 = cdata[k - 1 + j*nx]; 09945 lambda2 = cdata[k + j*nx]; 09946 if (lambda1 < 1.0 || lambda2 < 1.0) 09947 continue; 09948 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09949 } 09950 } 09951 } 09952 } 09953 } 09954 09955 return offsets; 09956 09957 } 09958 09959 10021 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 10022 double firstLambda, double lastLambda, 10023 cpl_table *idscoeff, cpl_vector *skylines, 10024 int highres, int order, 10025 cpl_image *calibration, int sradius) 10026 { 10027 const char *func = "mos_wavelength_align_lss"; 10028 10029 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10030 /* Max order is 5 */ 10031 double *line; 10032 double *data; 10033 double *wdata; 10034 double *odata; 10035 double expPos, offset; 10036 double c; 10037 double lambda1, lambda2; 10038 double rms; 10039 float pos; 10040 float *sdata; 10041 float *cdata; 10042 int *idata; 10043 int startPos, endPos; 10044 int window = 2*sradius + 1; 10045 int nlines; 10046 int npoints; 10047 int nrows; 10048 int nx, ny; 10049 int idsorder, uorder; 10050 int missing; 10051 int i; 10052 cpl_size j, k; 10053 10054 char name[MAX_COLNAME]; 10055 char fname[MAX_COLNAME]; 10056 10057 cpl_polynomial *ids; 10058 cpl_polynomial *polycorr; 10059 cpl_table *offsets; 10060 cpl_table *fittable; 10061 cpl_table *dummy; 10062 cpl_vector *wave; 10063 cpl_vector *offs; 10064 cpl_vector *row; 10065 10066 10067 if (idscoeff == NULL) { 10068 cpl_msg_error(func, "An IDS coeff table must be given"); 10069 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10070 return NULL; 10071 } 10072 10073 if (image == NULL) { 10074 cpl_msg_error(func, "A scientific spectral image must be given"); 10075 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10076 return NULL; 10077 } 10078 10079 if (skylines) { 10080 line = cpl_vector_get_data(skylines); 10081 nlines = cpl_vector_get_size(skylines); 10082 } 10083 else { 10084 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 10085 "given: using internal list of reference sky lines"); 10086 if (highres) { 10087 line = default_lines_hi; 10088 nlines = sizeof(default_lines_hi) / sizeof(double); 10089 } 10090 else { 10091 line = default_lines_lo; 10092 nlines = sizeof(default_lines_lo) / sizeof(double); 10093 } 10094 } 10095 10096 if (calibration) 10097 cdata = cpl_image_get_data(calibration); 10098 10099 nx = cpl_image_get_size_x(image); 10100 ny = cpl_image_get_size_y(image); 10101 10102 sdata = cpl_image_get_data(image); 10103 10104 if (ny != cpl_table_get_nrow(idscoeff)) { 10105 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 10106 return NULL; 10107 } 10108 10109 10110 /*FIXME: This is a remnant of the adaptation of the function 10111 * mos_wavelength_align(), where an offset table was created. 10112 * I leave it here because I am in a hurry, it is just used to 10113 * hold the list of selected sky lines. 10114 * 10115 * Define table of wavelengths 10116 */ 10117 10118 nrows = 0; 10119 for (i = 0; i < nlines; i++) 10120 if (line[i] > firstLambda && line[i] < lastLambda) 10121 nrows++; 10122 10123 offsets = cpl_table_new(nrows); 10124 cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE); 10125 cpl_table_set_column_unit(offsets, "wave", "Angstrom"); 10126 10127 nrows = 0; 10128 for (i = 0; i < nlines; i++) { 10129 if (line[i] > firstLambda && line[i] < lastLambda) { 10130 cpl_table_set_double(offsets, "wave", nrows, line[i]); 10131 nrows++; 10132 } 10133 } 10134 10135 /* 10136 * Here "line" is made to point to the new list of selected wavelengths 10137 */ 10138 10139 line = cpl_table_get_data_double(offsets, "wave"); 10140 nlines = nrows; 10141 10142 idsorder = 0; 10143 while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder])) 10144 ++idsorder; 10145 --idsorder; 10146 10147 10148 /* 10149 * Allocate a dummy table for collecting all the offsets 10150 * for all the lines 10151 */ 10152 10153 dummy = cpl_table_new(ny); 10154 for (j = 0; j < nlines; j++) { 10155 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10156 snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]); 10157 cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE); 10158 cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE); 10159 } 10160 10161 for (j = 0; j < ny; j++, sdata += nx) { 10162 10163 /* 10164 * Get the IDS polynomial for the current slit row 10165 */ 10166 10167 missing = 0; 10168 ids = cpl_polynomial_new(1); 10169 for (k = 0; k <= idsorder; k++) { 10170 c = cpl_table_get_double(idscoeff, clab[k], j, &missing); 10171 if (missing) { 10172 cpl_polynomial_delete(ids); 10173 break; 10174 } 10175 cpl_polynomial_set_coeff(ids, &k, c); 10176 } 10177 if (missing) 10178 continue; 10179 10180 for (k = 0; k < nlines; k++) { 10181 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL); 10182 startPos = expPos - sradius; 10183 endPos = startPos + window; 10184 if (startPos < 0 || endPos >= nx) 10185 continue; 10186 10187 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) { 10188 pos += startPos; 10189 offset = pos - expPos; 10190 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]); 10191 cpl_table_set_double(dummy, name, j, offset); 10192 } 10193 } 10194 10195 cpl_polynomial_delete(ids); 10196 } 10197 10198 10199 /* 10200 * At this point for each sky line we model its offset along 10201 * the image rows using a robust linear fitting 10202 */ 10203 10204 for (j = 0; j < nlines; j++) { 10205 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10206 snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]); 10207 if (cpl_table_has_valid(dummy, name)) { 10208 10209 /* 10210 * In the following, the "fittable" is just a tool for 10211 * eliminating invalid points from the vectors to be fitted. 10212 */ 10213 10214 double q, m; 10215 cpl_bivector *list; 10216 10217 fittable = cpl_table_new(ny); 10218 cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE); 10219 cpl_table_set_column_unit(fittable, "row", "pixel"); 10220 for (k = 0; k < ny; k++) 10221 cpl_table_set_double(fittable, "row", k, k); 10222 cpl_table_duplicate_column(fittable, "offset", dummy, name); 10223 npoints = ny - cpl_table_count_invalid(fittable, "offset"); 10224 cpl_table_erase_invalid(fittable); 10225 row = cpl_vector_wrap(npoints, 10226 cpl_table_get_data_double(fittable, "row")); 10227 offs = cpl_vector_wrap(npoints, 10228 cpl_table_get_data_double(fittable, "offset")); 10229 list = cpl_bivector_wrap_vectors(row, offs); 10230 robustLinearFit(list, &q, &m, &rms); 10231 cpl_bivector_unwrap_vectors(list); 10232 cpl_vector_unwrap(row); 10233 cpl_vector_unwrap(offs); 10234 cpl_table_delete(fittable); 10235 for (k = 0; k < ny; k++) 10236 cpl_table_set_double(dummy, fname, k, q + m*k); 10237 } 10238 } 10239 10240 10241 /* 10242 * Now each dummy table row consists of a sequence of offsets, 10243 * one for each wavelength. A table row corresponds to an image row. 10244 * We must fit a polynomial to each one of these rows, in order to 10245 * express the offsets as a function of wavelength. The obtained 10246 * polynomial coefficients are used to correct the IDS coefficients. 10247 */ 10248 10249 for (i = 0; i < ny; i++) { 10250 10251 if (!cpl_table_is_valid(idscoeff, clab[0], i)) 10252 continue; 10253 10254 npoints = 0; 10255 for (j = 0; j < nlines; j++) { 10256 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10257 if (cpl_table_is_valid(dummy, name, i)) 10258 npoints++; 10259 } 10260 10261 if (npoints == 0) 10262 continue; 10263 10264 uorder = order; 10265 if (npoints <= uorder) 10266 uorder = npoints - 1; 10267 10268 if (uorder > 1) { 10269 10270 /* 10271 * Model offsets with polynomial fitting 10272 */ 10273 10274 wave = cpl_vector_new(npoints); 10275 wdata = cpl_vector_get_data(wave); 10276 offs = cpl_vector_new(npoints); 10277 odata = cpl_vector_get_data(offs); 10278 10279 npoints = 0; 10280 for (j = 0; j < nlines; j++) { 10281 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10282 if (cpl_table_is_valid(dummy, name, i)) { 10283 wdata[npoints] = line[j] - refwave; 10284 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10285 npoints++; 10286 } 10287 } 10288 10289 polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms); 10290 10291 rms = sqrt(rms * (uorder + 1) / npoints); 10292 10293 cpl_vector_delete(wave); 10294 cpl_vector_delete(offs); 10295 10296 /* 10297 * Now correct the coefficients of the corresponding IDS 10298 * polynomials related to this slit: 10299 */ 10300 10301 for (j = 0; j <= uorder; j++) { 10302 data = cpl_table_get_data_double(idscoeff, clab[j]); 10303 c = cpl_polynomial_get_coeff(polycorr, &j); 10304 data[i] += c; 10305 } 10306 10307 data = cpl_table_get_data_double(idscoeff, "error"); 10308 data[i] = sqrt(data[i]*data[i] + rms*rms); 10309 10310 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10311 idata[i] = npoints; 10312 10313 /* 10314 * If a wavelengths map was provided, correct it to keep 10315 * into account the alignment to skylines: 10316 */ 10317 10318 if (calibration) { 10319 for (k = 1; k < nx; k++) { 10320 lambda1 = cdata[k - 1 + i*nx]; 10321 lambda2 = cdata[k + i*nx]; 10322 if (lambda1 < 1.0 || lambda2 < 1.0) 10323 continue; 10324 offset = cpl_polynomial_eval_1d(polycorr, 10325 lambda1-refwave, NULL); 10326 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10327 } 10328 } 10329 10330 cpl_polynomial_delete(polycorr); 10331 10332 } 10333 else if (uorder == 1) { 10334 10335 /* 10336 * Model offsets with robust linear fitting 10337 */ 10338 10339 cpl_bivector *list; 10340 double q, m; 10341 10342 wave = cpl_vector_new(npoints); 10343 wdata = cpl_vector_get_data(wave); 10344 offs = cpl_vector_new(npoints); 10345 odata = cpl_vector_get_data(offs); 10346 10347 npoints = 0; 10348 for (j = 0; j < nlines; j++) { 10349 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10350 if (cpl_table_is_valid(dummy, name, i)) { 10351 wdata[npoints] = line[j] - refwave; 10352 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10353 npoints++; 10354 } 10355 } 10356 10357 list = cpl_bivector_wrap_vectors(wave, offs); 10358 robustLinearFit(list, &q, &m, &rms); 10359 10360 rms = sqrt(rms * (uorder + 1) / npoints); 10361 10362 cpl_bivector_unwrap_vectors(list); 10363 cpl_vector_delete(wave); 10364 cpl_vector_delete(offs); 10365 10366 /* 10367 * Now correct the coefficients of the corresponding IDS 10368 * polynomials related to this row: 10369 */ 10370 10371 for (j = 0; j <= uorder; j++) { 10372 data = cpl_table_get_data_double(idscoeff, clab[j]); 10373 if (j) 10374 c = m; 10375 else 10376 c = q; 10377 data[i] += c; 10378 } 10379 10380 data = cpl_table_get_data_double(idscoeff, "error"); 10381 data[i] = sqrt(data[i]*data[i] + rms*rms); 10382 10383 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10384 idata[i] = npoints; 10385 10386 /* 10387 * If a wavelengths map was provided, correct it to keep 10388 * into account the alignment to skylines: 10389 */ 10390 10391 if (calibration) { 10392 for (k = 1; k < nx; k++) { 10393 lambda1 = cdata[k - 1 + i*nx]; 10394 lambda2 = cdata[k + i*nx]; 10395 if (lambda1 < 1.0 || lambda2 < 1.0) 10396 continue; 10397 offset = q + m*(lambda1-refwave); 10398 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10399 } 10400 } 10401 } 10402 else { 10403 10404 /* 10405 * Just compute median offset 10406 */ 10407 10408 offs = cpl_vector_new(npoints); 10409 odata = cpl_vector_get_data(offs); 10410 10411 npoints = 0; 10412 for (j = 0; j < nlines; j++) { 10413 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10414 if (cpl_table_is_valid(dummy, name, i)) { 10415 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10416 npoints++; 10417 } 10418 } 10419 10420 offset = cpl_vector_get_median_const(offs); 10421 10422 if (npoints > 1) { 10423 rms = cpl_vector_get_stdev(offs); 10424 } 10425 else if (npoints == 1) { 10426 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]); 10427 if (cpl_table_has_valid(dummy, name)) { 10428 rms = cpl_table_get_column_stdev(dummy, name); 10429 rms /= sqrt(ny - cpl_table_count_invalid(dummy, name)); 10430 } 10431 else { 10432 rms = 0.0; 10433 } 10434 } 10435 else { 10436 rms = 0.0; 10437 } 10438 10439 rms /= sqrt(npoints); 10440 10441 cpl_vector_delete(offs); 10442 10443 /* 10444 * Now correct the constant term of the corresponding IDS 10445 * polynomials related to this slit: 10446 */ 10447 10448 data = cpl_table_get_data_double(idscoeff, clab[0]); 10449 data[i] += offset; 10450 10451 data = cpl_table_get_data_double(idscoeff, "error"); 10452 data[i] = sqrt(data[i]*data[i] + rms*rms); 10453 10454 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10455 idata[i] = npoints; 10456 10457 /* 10458 * If a wavelengths map was provided, correct it to keep 10459 * into account the alignment to skylines. Note that 10460 * the offset must be converted from pixels to wavelengths. 10461 */ 10462 10463 if (calibration) { 10464 for (k = 1; k < nx; k++) { 10465 lambda1 = cdata[k - 1 + i*nx]; 10466 lambda2 = cdata[k + i*nx]; 10467 if (lambda1 < 1.0 || lambda2 < 1.0) 10468 continue; 10469 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10470 } 10471 } 10472 } 10473 } 10474 10475 missing = 1; 10476 for (j = 0; j < nlines; j++) { 10477 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10478 if (cpl_table_has_valid(dummy, name)) { 10479 missing = 0; 10480 offset = cpl_table_get_column_median(dummy, name); 10481 cpl_msg_info(func, "Median offset for %.3f: %.3f pixel", 10482 line[j], offset); 10483 } 10484 else { 10485 cpl_msg_info(func, 10486 "Median offset for %.2f: not available", line[j]); 10487 } 10488 } 10489 10490 cpl_table_delete(offsets); 10491 10492 if (missing) { 10493 cpl_table_delete(dummy); 10494 dummy = NULL; 10495 } 10496 10497 return dummy; 10498 10499 } 10500 10501 10529 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 10530 double wavestart, double dispersion, int radius, 10531 int highres) 10532 { 10533 10534 const char *func = "mos_distortions_rms"; 10535 10536 int xlen; 10537 int ylen; 10538 int numLines; 10539 int cpix, npix, nzero; 10540 int sp, ep; 10541 int i, j, k; 10542 int npeaks, allPeaks; 10543 10544 float *profile; 10545 float peak, expectPeak, offset; 10546 double lambda; 10547 10548 double average; 10549 double rms, oneRms; 10550 10551 float *sdata; 10552 double *wdata; 10553 10554 10555 xlen = cpl_image_get_size_x(rectified); 10556 ylen = cpl_image_get_size_y(rectified); 10557 sdata = cpl_image_get_data(rectified); 10558 10559 if (lines) { 10560 wdata = cpl_vector_get_data(lines); 10561 numLines = cpl_vector_get_size(lines); 10562 } 10563 else { 10564 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 10565 "given: using internal list of reference sky lines"); 10566 if (highres) { 10567 wdata = default_lines_hi; 10568 numLines = sizeof(default_lines_hi) / sizeof(double); 10569 } 10570 else { 10571 wdata = default_lines_lo; 10572 numLines = sizeof(default_lines_lo) / sizeof(double); 10573 } 10574 } 10575 10576 npix = 2 * radius + 1; 10577 profile = cpl_calloc(npix, sizeof(float)); 10578 10579 rms = 0.0; 10580 allPeaks = 0; 10581 10582 for (i = 0; i < numLines; i++) { 10583 10584 /* 10585 * Expected peak and closest pixel to specified wavelength. 10586 */ 10587 10588 lambda = wdata[i]; 10589 expectPeak = (lambda - wavestart) / dispersion; 10590 cpix = floor(expectPeak + 0.5); 10591 10592 /* 10593 * Search interval for peak. Abort if too close to image border. 10594 */ 10595 10596 sp = cpix - radius; 10597 ep = cpix + radius; 10598 10599 if (sp < 0 || ep > xlen) 10600 continue; 10601 10602 average = 0.0; 10603 npeaks = 0; 10604 oneRms = 0.0; 10605 10606 for (j = 0; j < ylen; j++) { /* For each row of each slit */ 10607 nzero = 0; 10608 for (k = 0; k < npix; k++) { 10609 profile[k] = sdata[sp + k + j * xlen]; 10610 if (fabs(profile[k]) < 0.0001) 10611 nzero++; /* Count number of 0 pixels (spectrum truncated) */ 10612 } 10613 if (nzero > 0) 10614 continue; 10615 10616 if (peakPosition(profile, npix, &peak, 1) == 0) { 10617 offset = (sp + peak) - expectPeak; 10618 average += offset; 10619 rms += fabs(offset); 10620 oneRms += fabs(offset); 10621 npeaks++; 10622 allPeaks++; 10623 } 10624 } 10625 10626 if (npeaks) 10627 cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)", 10628 lambda, oneRms / npeaks * 1.25, npeaks); 10629 else 10630 cpl_msg_info(func, "RMS for %.2f: line not available", lambda); 10631 } 10632 10633 cpl_free(profile); 10634 10635 if (allPeaks < 10) 10636 return 0.0; 10637 10638 rms /= allPeaks; 10639 rms *= 1.25; /* Factor to convert average deviation to sigma */ 10640 10641 return rms; 10642 10643 } 10644 10645 10666 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference, 10667 double blue, double red, double dispersion, int trend) 10668 { 10669 const char *func = "mos_map_pixel"; 10670 10671 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10672 /* Max order is 5 */ 10673 10674 cpl_polynomial *ids; 10675 cpl_image *map; 10676 float *mdata; 10677 double lambda; 10678 double c; 10679 int order; 10680 int xsize, ysize; 10681 int missing; 10682 int i, j; 10683 cpl_size k; 10684 10685 10686 if (idscoeff == NULL) { 10687 cpl_msg_error(func, "An IDS coeff table must be given"); 10688 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10689 return NULL; 10690 } 10691 10692 xsize = (red - blue) / dispersion; 10693 ysize = cpl_table_get_nrow(idscoeff); 10694 map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT); 10695 mdata = cpl_image_get_data(map); 10696 10697 order = 0; 10698 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 10699 ++order; 10700 --order; 10701 10702 for (i = 0; i < ysize; i++, mdata += xsize) { 10703 10704 missing = 0; 10705 ids = cpl_polynomial_new(1); 10706 for (k = trend; k <= order; k++) { 10707 c = cpl_table_get_double(idscoeff, clab[k], i, &missing); 10708 if (missing) { 10709 cpl_polynomial_delete(ids); 10710 break; 10711 } 10712 cpl_polynomial_set_coeff(ids, &k, c); 10713 } 10714 if (missing) 10715 continue; 10716 10717 for (j = 0; j < xsize; j++) { 10718 lambda = blue + j*dispersion; 10719 mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL); 10720 } 10721 10722 cpl_polynomial_delete(ids); 10723 } 10724 10725 return map; 10726 10727 } 10728 10729 10751 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, 10752 double blue, double red) 10753 { 10754 const char *func = "mos_map_idscoeff"; 10755 10756 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10757 /* Max order is 5 */ 10758 10759 cpl_polynomial *ids; 10760 cpl_image *map; 10761 float *mdata; 10762 double lambda; 10763 double c; 10764 int order; 10765 int ysize; 10766 int missing; 10767 int i, j; 10768 cpl_size k; 10769 10770 10771 if (idscoeff == NULL) { 10772 cpl_msg_error(func, "An IDS coeff table must be given"); 10773 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10774 return NULL; 10775 } 10776 10777 if (xsize < 1) { 10778 cpl_msg_error(func, "Invalid image size"); 10779 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10780 return NULL; 10781 } 10782 10783 if (xsize < 20 || xsize > 5000) { 10784 cpl_msg_warning(func, "Do you really have a detector %d pixels long?", 10785 xsize); 10786 } 10787 10788 ysize = cpl_table_get_nrow(idscoeff); 10789 map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT); 10790 mdata = cpl_image_get_data(map); 10791 10792 order = 0; 10793 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 10794 ++order; 10795 --order; 10796 10797 for (i = 0; i < ysize; i++, mdata += xsize) { 10798 10799 missing = 0; 10800 ids = cpl_polynomial_new(1); 10801 for (k = 0; k <= order; k++) { 10802 c = cpl_table_get_double(idscoeff, clab[k], i, &missing); 10803 if (missing) { 10804 cpl_polynomial_delete(ids); 10805 break; 10806 } 10807 cpl_polynomial_set_coeff(ids, &k, c); 10808 } 10809 if (missing) 10810 continue; 10811 10812 for (j = 0; j < xsize; j++) { 10813 lambda = mos_eval_dds(ids, blue, red, reference, j); 10814 10815 if (lambda >= blue && lambda <= red) { 10816 mdata[j] = lambda; 10817 } 10818 } 10819 10820 cpl_polynomial_delete(ids); 10821 } 10822 10823 return map; 10824 10825 } 10826 10827 10862 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, 10863 cpl_table *slits, cpl_table *polytraces, 10864 double reference, double blue, double red, 10865 double dispersion) 10866 { 10867 const char *func = "mos_map_wavelengths"; 10868 10869 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10870 /* Max order is 5 */ 10871 cpl_polynomial *polytop; 10872 cpl_polynomial *polybot; 10873 cpl_image *remapped; 10874 float *data; 10875 float *wdata; 10876 float *sdata; 10877 float *xdata; 10878 double vtop, vbot, value; 10879 double top, bot; 10880 double coeff; 10881 double ytop, ybot; 10882 double ypos; 10883 double fvalue; 10884 int ivalue; 10885 int yint, ysize, yprev; 10886 int nslits; 10887 int npseudo; 10888 int *slit_id; 10889 int *position; 10890 int *length; 10891 int nx, ny; 10892 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 10893 int missing_top, missing_bot; 10894 int null; 10895 int order; 10896 int i, j; 10897 cpl_size k; 10898 10899 10900 if (spatial == NULL || calibration == NULL || 10901 slits == NULL || polytraces == NULL) { 10902 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10903 return NULL; 10904 } 10905 10906 if (dispersion <= 0.0) { 10907 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10908 return NULL; 10909 } 10910 10911 if (red - blue < dispersion) { 10912 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10913 return NULL; 10914 } 10915 10916 nx = cpl_image_get_size_x(spatial); 10917 ny = cpl_image_get_size_y(spatial); 10918 ysize = cpl_image_get_size_y(calibration); 10919 remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 10920 data = cpl_image_get_data(remapped); 10921 sdata = cpl_image_get_data(spatial); 10922 wdata = cpl_image_get_data(calibration); 10923 10924 nslits = cpl_table_get_nrow(slits); 10925 slit_id = cpl_table_get_data_int(slits, "slit_id"); 10926 order = cpl_table_get_ncol(polytraces) - 2; 10927 position = cpl_table_get_data_int(slits, "position"); 10928 length = cpl_table_get_data_int(slits, "length"); 10929 10930 /* 10931 * The spatial resampling is performed for a certain number of 10932 * pixels above and below the position of the reference wavelength: 10933 */ 10934 10935 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 10936 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 10937 10938 for (i = 0; i < nslits; i++) { 10939 10940 if (length[i] == 0) 10941 continue; 10942 10943 /* 10944 * Note that the x coordinate of the reference pixels on the CCD 10945 * is taken arbitrarily at the top end of each slit. This wouldn't 10946 * be entirely correct in case of curved slits, or in presence of 10947 * heavy distortions: in such cases the spatial resampling is 10948 * really performed across a wide range of wavelengths. But 10949 * the lag between top and bottom spectral curvature models 10950 * would introduce even in such cases negligible effects on 10951 * the spectral spatial resampling. 10952 */ 10953 10954 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 10955 10956 start_pixel = refpixel - pixel_below; 10957 if (start_pixel < 0) 10958 start_pixel = 0; 10959 10960 end_pixel = refpixel + pixel_above; 10961 if (end_pixel > nx) 10962 end_pixel = nx; 10963 10964 /* 10965 * Recover from the table of spectral curvature coefficients 10966 * the curvature polynomials. 10967 */ 10968 10969 missing_top = 0; 10970 polytop = cpl_polynomial_new(1); 10971 for (k = 0; k <= order; k++) { 10972 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 10973 if (null) { 10974 cpl_polynomial_delete(polytop); 10975 missing_top = 1; 10976 break; 10977 } 10978 cpl_polynomial_set_coeff(polytop, &k, coeff); 10979 } 10980 10981 missing_bot = 0; 10982 polybot = cpl_polynomial_new(1); 10983 for (k = 0; k <= order; k++) { 10984 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 10985 if (null) { 10986 cpl_polynomial_delete(polybot); 10987 missing_bot = 1; 10988 break; 10989 } 10990 cpl_polynomial_set_coeff(polybot, &k, coeff); 10991 } 10992 10993 if (missing_top && missing_bot) { 10994 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 10995 slit_id[i]); 10996 continue; 10997 } 10998 10999 /* 11000 * In case just one of the two edges was not traced, the other 11001 * edge curvature model is duplicated and shifted to the other 11002 * end of the slit: better than nothing! 11003 */ 11004 11005 if (missing_top) { 11006 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 11007 "the spectral curvature of the lower edge " 11008 "is used instead.", slit_id[i]); 11009 polytop = cpl_polynomial_duplicate(polybot); 11010 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11011 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11012 k = 0; 11013 coeff = cpl_polynomial_get_coeff(polybot, &k); 11014 coeff += ytop - ybot; 11015 cpl_polynomial_set_coeff(polytop, &k, coeff); 11016 } 11017 11018 if (missing_bot) { 11019 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 11020 "the spectral curvature of the upper edge " 11021 "is used instead.", slit_id[i]); 11022 polybot = cpl_polynomial_duplicate(polytop); 11023 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11024 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11025 k = 0; 11026 coeff = cpl_polynomial_get_coeff(polytop, &k); 11027 coeff -= ytop - ybot; 11028 cpl_polynomial_set_coeff(polybot, &k, coeff); 11029 } 11030 11031 /* 11032 * Point to current slit on wavelength calibration image. 11033 * Note that the npseudo value related to this slit is equal 11034 * to the number of spatial pseudo-pixels decreased by 1 11035 * (compare with function mos_spatial_calibration()). 11036 */ 11037 11038 xdata = wdata + nx*position[i]; 11039 npseudo = length[i] - 1; 11040 11041 /* 11042 * Write interpolated wavelengths to CCD image 11043 */ 11044 11045 for (j = start_pixel; j < end_pixel; j++) { 11046 top = cpl_polynomial_eval_1d(polytop, j, NULL); 11047 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 11048 for (k = 0; k <= npseudo; k++) { 11049 ypos = top - k*(top-bot)/npseudo; 11050 yint = ypos; 11051 11052 /* 11053 * The line: 11054 * value = sdata[j + nx*yint]; 11055 * should be equivalent to: 11056 * value = npseudo*(top-yint)/(top-bot); 11057 */ 11058 11059 if (yint < 0 || yint >= ny-1) { 11060 yprev = yint; 11061 continue; 11062 } 11063 11064 value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */ 11065 ivalue = value; /* Nearest spatial pixels: */ 11066 fvalue = value - ivalue; /* ivalue and ivalue+1 */ 11067 if (ivalue < npseudo && ivalue >= 0) { 11068 vtop = xdata[j + nx*(npseudo-ivalue)]; 11069 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 11070 if (vtop < 1.0) { /* Impossible wavelength */ 11071 if (vbot < 1.0) { 11072 value = 0.0; 11073 } 11074 else { 11075 value = vbot; 11076 } 11077 } 11078 else if (vbot < 1.0) { 11079 if (k) 11080 value = vtop; 11081 else 11082 value = 0.0; 11083 } 11084 else if (fabs(vbot-vtop) > 10*dispersion) { 11085 value = 0.0; 11086 } 11087 else { 11088 value = vtop*(1-fvalue) + vbot*fvalue; 11089 } 11090 data[j + nx*yint] = value; 11091 11092 if (k) { 11093 11094 /* 11095 * This is added to recover lost pixels on 11096 * the CCD image (pixels are lost because 11097 * the CCD pixels are less than npseudo+1). 11098 */ 11099 11100 if (yprev - yint > 1) { 11101 value = sdata[j + nx*(yint+1)]; 11102 ivalue = value; 11103 fvalue = value - ivalue; 11104 if (ivalue < npseudo && ivalue >= 0) { 11105 vtop = xdata[j + nx*(npseudo-ivalue)]; 11106 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 11107 if (vtop < 1.0) { 11108 if (vbot < 1.0) { 11109 value = data[j + nx*(yint+1)]; 11110 } 11111 else { 11112 value = vbot; 11113 } 11114 } 11115 else if (vbot < 1.0) { 11116 value = vtop; 11117 } 11118 else if (fabs(vbot-vtop) > 2*dispersion) { 11119 value = vtop; 11120 } 11121 else { 11122 value = vtop*(1-fvalue) + vbot*fvalue; 11123 } 11124 data[j + nx*(yint+1)] = value; 11125 } 11126 } 11127 } 11128 } 11129 yprev = yint; 11130 } 11131 } 11132 cpl_polynomial_delete(polytop); 11133 cpl_polynomial_delete(polybot); 11134 } 11135 11136 return remapped; 11137 } 11138 11212 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 11213 cpl_image *spatial, cpl_table *slits, 11214 cpl_table *polytraces, double reference, 11215 double blue, double red, double dispersion, 11216 int flux) 11217 { 11218 const char *func = "mos_map_spectrum"; 11219 11220 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 11221 /* Max order is 5 */ 11222 cpl_polynomial *polytop; 11223 cpl_polynomial *polybot; 11224 cpl_image *remapped; 11225 cpl_image **exslit; 11226 float *data; 11227 float *wdata; 11228 float *sdata; 11229 float *xdata; 11230 double lambda00, lambda01, lambda10, lambda11, lambda; 11231 double space00, space01, space10, space11, space; 11232 double value00, value01, value10, value11, value0, value1, value; 11233 double dL, dS; 11234 double top, bot; 11235 double coeff; 11236 double ytop, ybot; 11237 double xfrac, yfrac; 11238 int yint, ysize; 11239 int itop, ibot; 11240 int shift; 11241 int L, S; 11242 int nslits; 11243 int npseudo; 11244 int *slit_id; 11245 int *position; 11246 int *length; 11247 int nx, ny; 11248 int x, y; 11249 int nlambda; 11250 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 11251 int missing_top, missing_bot; 11252 int null; 11253 int order; 11254 int i; 11255 cpl_size k; 11256 11257 11258 flux += flux; 11259 11260 if (spectra == NULL || spatial == NULL || wavecalib == NULL || 11261 slits == NULL || polytraces == NULL) { 11262 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11263 return NULL; 11264 } 11265 11266 if (dispersion <= 0.0) { 11267 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11268 return NULL; 11269 } 11270 11271 if (red - blue < dispersion) { 11272 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11273 return NULL; 11274 } 11275 11276 nx = cpl_image_get_size_x(spectra); 11277 ny = cpl_image_get_size_y(spectra); 11278 11279 if (nx != cpl_image_get_size_x(spatial) || 11280 ny != cpl_image_get_size_y(spatial) || 11281 nx != cpl_image_get_size_x(wavecalib) || 11282 ny != cpl_image_get_size_y(wavecalib)) { 11283 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 11284 return NULL; 11285 } 11286 11287 nlambda = STRETCH_FACTOR * (red - blue) / dispersion; 11288 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 11289 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 11290 11291 data = cpl_image_get_data(spectra); 11292 sdata = cpl_image_get_data(spatial); 11293 wdata = cpl_image_get_data(wavecalib); 11294 11295 nslits = cpl_table_get_nrow(slits); 11296 slit_id = cpl_table_get_data_int(slits, "slit_id"); 11297 order = cpl_table_get_ncol(polytraces) - 2; 11298 position = cpl_table_get_data_int(slits, "position"); 11299 length = cpl_table_get_data_int(slits, "length"); 11300 11301 exslit = cpl_calloc(nslits, sizeof(cpl_image *)); 11302 11303 for (i = 0; i < nslits; i++) { 11304 11305 if (length == 0) 11306 continue; 11307 11308 /* 11309 * Note that the x coordinate of the reference pixels on the CCD 11310 * is taken arbitrarily at the top end of each slit. This wouldn't 11311 * be entirely correct in case of curved slits, or in presence of 11312 * heavy distortions: in such cases the spatial resampling is 11313 * really performed across a wide range of wavelengths. But 11314 * the lag between top and bottom spectral curvature models 11315 * would introduce even in such cases negligible effects on 11316 * the spectral spatial resampling. 11317 */ 11318 11319 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 11320 11321 start_pixel = refpixel - pixel_below; 11322 if (start_pixel < 1) 11323 start_pixel = 1; 11324 11325 end_pixel = refpixel + pixel_above; 11326 if (end_pixel > nx) 11327 end_pixel = nx; 11328 11329 /* 11330 * Recover from the table of spectral curvature coefficients 11331 * the curvature polynomials. 11332 */ 11333 11334 missing_top = 0; 11335 polytop = cpl_polynomial_new(1); 11336 for (k = 0; k <= order; k++) { 11337 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 11338 if (null) { 11339 cpl_polynomial_delete(polytop); 11340 missing_top = 1; 11341 break; 11342 } 11343 cpl_polynomial_set_coeff(polytop, &k, coeff); 11344 } 11345 11346 missing_bot = 0; 11347 polybot = cpl_polynomial_new(1); 11348 for (k = 0; k <= order; k++) { 11349 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 11350 if (null) { 11351 cpl_polynomial_delete(polybot); 11352 missing_bot = 1; 11353 break; 11354 } 11355 cpl_polynomial_set_coeff(polybot, &k, coeff); 11356 } 11357 11358 if (missing_top && missing_bot) { 11359 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 11360 slit_id[i]); 11361 continue; 11362 } 11363 11364 /* 11365 * In case just one of the two edges was not traced, the other 11366 * edge curvature model is duplicated and shifted to the other 11367 * end of the slit: better than nothing! 11368 */ 11369 11370 if (missing_top) { 11371 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 11372 "the spectral curvature of the lower edge " 11373 "is used instead.", slit_id[i]); 11374 polytop = cpl_polynomial_duplicate(polybot); 11375 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11376 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11377 k = 0; 11378 coeff = cpl_polynomial_get_coeff(polybot, &k); 11379 coeff += ytop - ybot; 11380 cpl_polynomial_set_coeff(polytop, &k, coeff); 11381 } 11382 11383 if (missing_bot) { 11384 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 11385 "the spectral curvature of the upper edge " 11386 "is used instead.", slit_id[i]); 11387 polybot = cpl_polynomial_duplicate(polytop); 11388 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11389 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11390 k = 0; 11391 coeff = cpl_polynomial_get_coeff(polytop, &k); 11392 coeff -= ytop - ybot; 11393 cpl_polynomial_set_coeff(polybot, &k, coeff); 11394 } 11395 11396 /* 11397 * Allocate image for current extracted slit 11398 */ 11399 11400 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 11401 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 11402 npseudo = ceil(top-bot) + 1; 11403 11404 if (npseudo < 1) { 11405 cpl_polynomial_delete(polytop); 11406 cpl_polynomial_delete(polybot); 11407 cpl_msg_debug(func, "Slit %d was badly traced: no extraction!", 11408 slit_id[i]); 11409 continue; 11410 } 11411 11412 exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT); 11413 xdata = cpl_image_get_data(exslit[i]); 11414 11415 /* 11416 * Write interpolated spectral values to remapped slit spectrum. 11417 */ 11418 11419 for (x = start_pixel; x < end_pixel; x++) { 11420 top = cpl_polynomial_eval_1d(polytop, x, NULL); 11421 bot = cpl_polynomial_eval_1d(polybot, x, NULL); 11422 itop = top + 1; 11423 ibot = bot; 11424 if (itop < 0) 11425 itop = 0; 11426 if (itop > ny - 1) 11427 itop = ny - 1; 11428 if (ibot < 0) 11429 ibot = 0; 11430 if (ibot > ny - 1) 11431 ibot = ny - 1; 11432 for (y = ibot; y < itop; y++) { 11433 lambda11 = wdata[x + y*nx]; 11434 if (lambda11 < 1.0) /* Impossible wavelength */ 11435 continue; 11436 space11 = sdata[x + y*nx]; 11437 if (space11 < 0.0) /* Impossible spatial coordinate */ 11438 continue; 11439 lambda01 = wdata[x - 1 + y*nx]; 11440 if (lambda01 < 1.0) /* Impossible wavelength */ 11441 continue; 11442 space01 = sdata[x - 1 + y*nx]; 11443 if (space01 < 0.0) /* Impossible spatial coordinate */ 11444 continue; 11445 11446 shift = 0; 11447 11448 /****+ 11449 if (wdata[x + (y+1)*nx] > 1.0) { 11450 if (wdata[x + (y+1)*nx] - lambda11 > 0) { 11451 shift = -1; 11452 while (wdata[x + shift + (y+1)*nx] - lambda11 > 0) 11453 shift--; 11454 if (lambda11 - wdata[x + shift + (y+1)*nx] > 11455 wdata[x + shift + 1 + (y+1)*nx] - lambda11) { 11456 shift++; 11457 } 11458 } 11459 else { 11460 shift = 1; 11461 while (wdata[x + shift + (y+1)*nx] - lambda11 < 0) 11462 shift++; 11463 if (wdata[x + shift + (y+1)*nx] - lambda11 > 11464 lambda11 - wdata[x + shift + 1 + (y+1)*nx]) { 11465 shift--; 11466 } 11467 } 11468 } 11469 ****/ 11470 11471 /**** 11472 printf("y = %d, shift = %d\n", y, shift); 11473 ****/ 11474 11475 lambda10 = wdata[x + shift + (y+1)*nx]; 11476 if (lambda10 < 1.0) /* Impossible wavelength */ 11477 continue; 11478 space10 = sdata[x + shift + (y+1)*nx]; 11479 if (space10 < 0.0) /* Impossible spatial coordinate */ 11480 continue; 11481 lambda00 = wdata[x - 1 + shift + (y+1)*nx]; 11482 if (lambda00 < 1.0) /* Impossible wavelength */ 11483 continue; 11484 space00 = sdata[x - 1 + shift + (y+1)*nx]; 11485 if (space00 < 0.0) /* Impossible spatial coordinate */ 11486 continue; 11487 11488 /* 11489 * Find the variation in lambda and space in this 11490 * position for each CCD pixel (both quantities are 11491 * expected to be positive). 11492 */ 11493 11494 dL = lambda11 - lambda01; 11495 dS = space11 - space10; 11496 11497 /* 11498 * Find the position (L,S) of the output pixel 11499 * (by integer truncation). 11500 */ 11501 11502 L = (lambda11 - blue)/dispersion + 0.5; 11503 S = space11 + 0.5; /* Counted from top! */ 11504 11505 if (L < 0 || L >= nlambda) 11506 continue; 11507 if (S < 0 || S > npseudo) 11508 continue; 11509 11510 /* 11511 * Find the coordinate of pixel (L,S) 11512 */ 11513 11514 lambda = blue + L*dispersion; 11515 space = S; 11516 11517 /* 11518 * Find the interpolation point on the CCD: it is 11519 * defined as the (positive) distance from current 11520 * CCD pixel (x,y) of the interpolation point (x',y'), 11521 * measured in CCD pixels. The interpolation point 11522 * is located between the four CCD pixels selected 11523 * above. 11524 */ 11525 11526 xfrac = (lambda11-lambda)/dL; 11527 yfrac = (space11-space)/dS; 11528 11529 /* 11530 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0) 11531 printf("xyfrac = %f, %f\n", xfrac, yfrac); 11532 */ 11533 11534 /* 11535 * Get the four values to interpolate 11536 */ 11537 11538 value11 = data[x + y*nx]; 11539 value01 = data[x - 1 + y*nx]; 11540 value10 = data[x + shift + (y+1)*nx]; 11541 value00 = data[x + shift - 1 + (y+1)*nx]; 11542 11543 /* 11544 * Interpolation 11545 */ 11546 11547 value1 = (1-xfrac)*value11 + xfrac*value01; 11548 value0 = (1-xfrac)*value10 + xfrac*value00; 11549 value = (1-yfrac)*value1 + yfrac*value0; 11550 11551 /* 11552 * Write this value to the appropriate (L,S) coordinate 11553 * on output slit 11554 */ 11555 11556 xdata[L + nlambda*(npseudo-S)] = value; 11557 11558 } 11559 } 11560 cpl_polynomial_delete(polytop); 11561 cpl_polynomial_delete(polybot); 11562 } 11563 11564 /* 11565 * Now all the slits images are copied to a single image 11566 */ 11567 11568 ysize = 0; 11569 for (i = 0; i < nslits; i++) 11570 if (exslit[i]) 11571 ysize += cpl_image_get_size_y(exslit[i]); 11572 11573 remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT); 11574 11575 yint = -1; 11576 for (i = 0; i < nslits; i++) { 11577 if (exslit[i]) { 11578 yint += cpl_image_get_size_y(exslit[i]); 11579 cpl_image_copy(remapped, exslit[i], 1, ysize - yint); 11580 cpl_image_delete(exslit[i]); 11581 cpl_table_set_int(slits, "position", i, ysize - yint - 1); 11582 } 11583 } 11584 11585 cpl_free(exslit); 11586 11587 return remapped; 11588 11589 } 11590 11591 11624 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap, 11625 double dispersion, double factor, int minpoints, 11626 cpl_image *skymap) 11627 { 11628 const char *func = "mos_sky_map_super"; 11629 11630 cpl_vector **vector; 11631 cpl_vector **wvector; 11632 double firstLambda, lastLambda; 11633 double lambda, lambda1, lambda2; 11634 double value, value1, value2; 11635 double frac; 11636 float min, max; 11637 int *count; 11638 int nbin, bin; 11639 int nx, ny, npix; 11640 int first_valid, valid_bins; 11641 int i, j; 11642 11643 cpl_table *sky; 11644 double *sky_spectrum; 11645 double *sky_wave; 11646 float *data; 11647 float *sdata; 11648 float *kdata; 11649 11650 11651 if (spectra == NULL || wavemap == NULL || skymap == NULL) { 11652 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11653 return NULL; 11654 } 11655 11656 if (dispersion <= 0.0) { 11657 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11658 cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message()); 11659 return NULL; 11660 } 11661 11662 nx = cpl_image_get_size_x(spectra); 11663 ny = cpl_image_get_size_y(spectra); 11664 npix = nx * ny; 11665 11666 if (nx != cpl_image_get_size_x(wavemap) || 11667 ny != cpl_image_get_size_y(wavemap) || 11668 nx != cpl_image_get_size_x(skymap) || 11669 ny != cpl_image_get_size_y(skymap)) { 11670 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 11671 cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message()); 11672 return NULL; 11673 } 11674 11675 if (factor < 1.0) { 11676 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11677 cpl_msg_error(func, "Undersampling (%f): %s", factor, 11678 cpl_error_get_message()); 11679 return NULL; 11680 } 11681 11682 if (minpoints < 0) { 11683 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11684 cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message()); 11685 return NULL; 11686 } 11687 11688 dispersion /= factor; 11689 11690 11691 /* 11692 * Find bluest and reddest wavelengths in the whole image 11693 */ 11694 11695 data = cpl_image_get_data(wavemap); 11696 11697 for (i = 0; i < npix; i++) { 11698 if (data[i] > 1.0) { 11699 min = max = data[i]; 11700 j = i+1; 11701 break; 11702 } 11703 } 11704 11705 for (i = j; i < npix; i++) { 11706 if (data[i] < 1.0) /* Impossible wavelength */ 11707 continue; 11708 if (min > data[i]) 11709 min = data[i]; 11710 if (max < data[i]) 11711 max = data[i]; 11712 } 11713 11714 firstLambda = min; 11715 lastLambda = max; 11716 11717 11718 /* 11719 * Determine length of median spectrum 11720 */ 11721 11722 nbin = (lastLambda - firstLambda) / dispersion; 11723 11724 /* 11725 * Count how many values will be found for each spectral bin. 11726 * The ith bin has a wavelength range from firstLambda + i*dispersion 11727 * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and 11728 * it is assigned to its central wavelength. 11729 */ 11730 11731 count = cpl_calloc(nbin, sizeof(int)); 11732 11733 data = cpl_image_get_data(wavemap); 11734 11735 for (i = 0; i < npix; i++) { 11736 if (data[i] < 1.0) 11737 continue; 11738 bin = (data[i] - firstLambda) / dispersion; 11739 if (bin < nbin) /* Safer */ 11740 count[bin]++; 11741 } 11742 11743 valid_bins = 0; 11744 for (i = 0; i < nbin; i++) 11745 if (count[i] >= minpoints) 11746 valid_bins++; 11747 11748 if (valid_bins < nbin/3) { 11749 cpl_msg_warning(func, "Cannot determine a good global sky " 11750 "spectrum from input data"); 11751 return NULL; 11752 } 11753 11754 11755 /* 11756 * Allocate an array of vectors with the appropriate size, to 11757 * contain a list of all the spectral pixels values. At the same 11758 * time, reset the array of counters (because we will have to 11759 * count again...). 11760 */ 11761 11762 vector = cpl_calloc(nbin, sizeof(cpl_vector *)); 11763 wvector = cpl_calloc(nbin, sizeof(cpl_vector *)); 11764 for (i = 0; i < nbin; i++) { 11765 if (count[i] >= minpoints) { 11766 vector[i] = cpl_vector_new(count[i]); 11767 wvector[i] = cpl_vector_new(count[i]); 11768 } 11769 count[i] = 0; 11770 } 11771 11772 11773 /* 11774 * Read the wavemap and the spectral images, and add the data values 11775 * to the appropriate wavelength bins 11776 */ 11777 11778 data = cpl_image_get_data(wavemap); 11779 sdata = cpl_image_get_data(spectra); 11780 11781 for (i = 0; i < npix; i++) { 11782 if (data[i] < 1.0) 11783 continue; 11784 bin = (data[i] - firstLambda) / dispersion; 11785 if (bin < nbin) { /* Safer */ 11786 if (vector[bin]) { 11787 cpl_vector_set(vector[bin], count[bin], sdata[i]); 11788 cpl_vector_set(wvector[bin], count[bin], data[i]); 11789 } 11790 count[bin]++; 11791 } 11792 } 11793 11794 11795 /* 11796 * Compute the median flux for each wavelength bin, and destroy 11797 * at the same time the used vectors 11798 */ 11799 11800 sky_spectrum = cpl_calloc(nbin, sizeof(double)); 11801 sky_wave = cpl_calloc(nbin, sizeof(double)); 11802 for (i = 0; i < nbin; i++) { 11803 if (vector[i]) { 11804 sky_spectrum[i] = cpl_vector_get_median_const(vector[i]); 11805 sky_wave[i] = cpl_vector_get_median_const(wvector[i]); 11806 cpl_vector_delete(vector[i]); 11807 cpl_vector_delete(wvector[i]); 11808 } 11809 } 11810 11811 cpl_free(vector); 11812 cpl_free(wvector); 11813 11814 11815 /* 11816 * Here possible gaps in the final spectrum are filled by interpolation 11817 */ 11818 11819 for (i = 0; i < nbin; i++) { 11820 if (count[i] >= minpoints) { 11821 first_valid = i; 11822 break; 11823 } 11824 } 11825 11826 for (i = first_valid; i < nbin; i++) { 11827 if (count[i] < minpoints) { 11828 sky_wave[i] = firstLambda + (i+0.5)*dispersion; 11829 for (j = i+1; j < nbin; j++) { 11830 if (count[j] >= minpoints) { 11831 if (sky_wave[j] - sky_wave[i-1] < 0.1) { 11832 sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1]) 11833 / 2; 11834 } 11835 else { 11836 frac = (sky_wave[i] - sky_wave[i-1]) 11837 / (sky_wave[j] - sky_wave[i-1]); 11838 sky_spectrum[i] = frac * sky_spectrum[j] 11839 + (1 - frac) * sky_spectrum[i-1]; 11840 } 11841 } 11842 } 11843 } 11844 } 11845 11846 11847 /* 11848 * Create the output table 11849 */ 11850 11851 sky = cpl_table_new(nbin); 11852 cpl_table_wrap_double(sky, sky_wave, "wavelength"); 11853 cpl_table_wrap_double(sky, sky_spectrum, "sky"); 11854 cpl_table_wrap_int(sky, count, "npoints"); 11855 11856 11857 /* 11858 * Fill the sky map 11859 */ 11860 11861 data = cpl_image_get_data(wavemap); 11862 sdata = cpl_image_get_data(spectra); 11863 kdata = cpl_image_get_data(skymap); 11864 11865 for (i = 0; i < npix; i++) { 11866 11867 /* 11868 * Currently based on linear interpolation 11869 */ 11870 11871 lambda = data[i]; 11872 if (lambda < 1.0) 11873 continue; 11874 bin = (lambda - firstLambda) / dispersion; 11875 lambda1 = sky_wave[bin]; 11876 value1 = sky_spectrum[bin]; 11877 if (lambda1 < lambda) { 11878 bin++; 11879 if (bin < nbin) { 11880 lambda2 = sky_wave[bin]; 11881 value2 = sky_spectrum[bin]; 11882 if (lambda2 - lambda1 < 0.1) { 11883 value = (value1 + value2) / 2; 11884 } 11885 else { 11886 frac = (lambda - lambda1) / (lambda2 - lambda1); 11887 value = frac * value2 + (1 - frac) * value1; 11888 } 11889 } 11890 else { 11891 value = value1; 11892 } 11893 } 11894 else { 11895 if (bin > 0) { 11896 bin--; 11897 lambda2 = lambda1; 11898 value2 = value1; 11899 lambda1 = sky_wave[bin]; 11900 value1 = sky_spectrum[bin]; 11901 if (lambda2 - lambda1 < 0.1) { 11902 value = (value1 + value2) / 2; 11903 } 11904 else { 11905 frac = (lambda - lambda1) / (lambda2 - lambda1); 11906 value = frac * value2 + (1 - frac) * value1; 11907 } 11908 } 11909 else { 11910 value = value1; 11911 } 11912 } 11913 kdata[i] = value; 11914 } 11915 11916 if (first_valid) 11917 cpl_table_erase_window(sky, 0, first_valid); 11918 11919 return sky; 11920 11921 } 11922 11923 11957 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap, 11958 double dispersion, cpl_image *skymap) 11959 { 11960 const char *func = "mos_sky_map"; 11961 11962 cpl_vector **vector; 11963 double firstLambda, lastLambda; 11964 double lambda, lambda1, lambda2; 11965 double value, value1, value2; 11966 float min, max; 11967 int *count; 11968 int nbin, bin; 11969 int nx, ny, npix; 11970 int i, j; 11971 11972 cpl_table *sky; 11973 double *sky_spectrum; 11974 float *data; 11975 float *sdata; 11976 float *kdata; 11977 double *wdata; 11978 11979 11980 if (spectra == NULL || wavemap == NULL || skymap == NULL) { 11981 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11982 return NULL; 11983 } 11984 11985 if (dispersion <= 0.0) { 11986 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11987 return NULL; 11988 } 11989 11990 nx = cpl_image_get_size_x(spectra); 11991 ny = cpl_image_get_size_y(spectra); 11992 npix = nx * ny; 11993 11994 if (nx != cpl_image_get_size_x(wavemap) || 11995 ny != cpl_image_get_size_y(wavemap) || 11996 nx != cpl_image_get_size_x(skymap) || 11997 ny != cpl_image_get_size_y(skymap)) { 11998 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 11999 return NULL; 12000 } 12001 12002 12003 /* 12004 * Find bluest and reddest wavelengths in the whole image 12005 */ 12006 12007 data = cpl_image_get_data(wavemap); 12008 12009 for (i = 0; i < npix; i++) { 12010 if (data[i] > 1.0) { 12011 min = max = data[i]; 12012 j = i+1; 12013 break; 12014 } 12015 } 12016 12017 for (i = j; i < npix; i++) { 12018 if (data[i] < 1.0) /* Impossible wavelength */ 12019 continue; 12020 if (min > data[i]) 12021 min = data[i]; 12022 if (max < data[i]) 12023 max = data[i]; 12024 } 12025 12026 firstLambda = min; 12027 lastLambda = max; 12028 12029 12030 /* 12031 * Determine length of median spectrum 12032 */ 12033 12034 nbin = (lastLambda - firstLambda) / dispersion; 12035 12036 /* 12037 * Count how many values will be found for each spectral bin. 12038 * The ith bin has a wavelength range from firstLambda + i*dispersion 12039 * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and 12040 * it is assigned to its central wavelength. 12041 */ 12042 12043 count = cpl_calloc(nbin, sizeof(int)); 12044 12045 data = cpl_image_get_data(wavemap); 12046 12047 for (i = 0; i < npix; i++) { 12048 if (data[i] < 1.0) 12049 continue; 12050 bin = (data[i] - firstLambda) / dispersion; 12051 if (bin < nbin) /* Safer */ 12052 count[bin]++; 12053 } 12054 12055 12056 /* 12057 * Allocate an array of vectors with the appropriate size, to 12058 * contain a list of all the spectral pixels values. At the same 12059 * time, reset the array of counters (because we will have to 12060 * count again...). 12061 */ 12062 12063 vector = cpl_calloc(nbin, sizeof(cpl_vector *)); 12064 for (i = 0; i < nbin; i++) { 12065 if (count[i]) 12066 vector[i] = cpl_vector_new(count[i]); 12067 else 12068 vector[i] = NULL; 12069 count[i] = 0; 12070 } 12071 12072 12073 /* 12074 * Read the wavemap and the spectral images, and add the data values 12075 * to the appropriate wavelength bins 12076 */ 12077 12078 data = cpl_image_get_data(wavemap); 12079 sdata = cpl_image_get_data(spectra); 12080 12081 for (i = 0; i < npix; i++) { 12082 if (data[i] < 1.0) 12083 continue; 12084 bin = (data[i] - firstLambda) / dispersion; 12085 if (bin < nbin) { /* Safer */ 12086 cpl_vector_set(vector[bin], count[bin], sdata[i]); 12087 count[bin]++; 12088 } 12089 } 12090 12091 12092 /* 12093 * Compute the median flux for each wavelength bin, and destroy 12094 * at the same time the used vectors 12095 */ 12096 12097 sky_spectrum = cpl_calloc(nbin, sizeof(double)); 12098 for (i = 0; i < nbin; i++) { 12099 if (vector[i]) { 12100 sky_spectrum[i] = cpl_vector_get_median_const(vector[i]); 12101 cpl_vector_delete(vector[i]); 12102 } 12103 } 12104 12105 cpl_free(vector); 12106 12107 12108 /* 12109 * Here possible gaps in the final spectrum should be filled 12110 * by interpolation 12111 */ 12112 12113 /* ... */ 12114 12115 /* 12116 * Create the output table 12117 */ 12118 12119 sky = cpl_table_new(nbin); 12120 cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE); 12121 cpl_table_set_column_unit(sky, "wavelength", "pixel"); 12122 cpl_table_wrap_double(sky, sky_spectrum, "sky"); 12123 cpl_table_wrap_int(sky, count, "npoints"); 12124 for (i = 0; i < nbin; i++) 12125 cpl_table_set_double(sky, "wavelength", i, 12126 firstLambda + (i+0.5)*dispersion); 12127 12128 12129 /* 12130 * Fill the sky map 12131 */ 12132 12133 data = cpl_image_get_data(wavemap); 12134 sdata = cpl_image_get_data(spectra); 12135 kdata = cpl_image_get_data(skymap); 12136 wdata = cpl_table_get_data_double(sky, "wavelength"); 12137 12138 for (i = 0; i < npix; i++) { 12139 12140 /* 12141 * Currently based on linear interpolation 12142 */ 12143 12144 lambda = data[i]; 12145 if (lambda < 1.0) 12146 continue; 12147 bin = (lambda - firstLambda) / dispersion; 12148 lambda1 = wdata[bin]; 12149 value1 = sky_spectrum[bin]; 12150 if (lambda1 < lambda) { 12151 bin++; 12152 if (bin < nbin) { 12153 lambda2 = wdata[bin]; 12154 value2 = sky_spectrum[bin]; 12155 value = ((lambda2 - lambda)*value1 12156 + (lambda - lambda1)*value2) / dispersion; 12157 } 12158 else { 12159 value = value1; 12160 } 12161 } 12162 else { 12163 if (bin > 0) { 12164 bin--; 12165 lambda2 = lambda1; 12166 value2 = value1; 12167 lambda1 = wdata[bin]; 12168 value1 = sky_spectrum[bin]; 12169 value = ((lambda2 - lambda)*value1 12170 + (lambda - lambda1)*value2)/dispersion; 12171 } 12172 else { 12173 value = value1; 12174 } 12175 } 12176 kdata[i] = value; 12177 } 12178 12179 return sky; 12180 12181 } 12182 12183 12199 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits) 12200 { 12201 const char *func = "mos_sky_local_old"; 12202 12203 cpl_image *exslit; 12204 cpl_image *sky; 12205 cpl_image *skymap; 12206 float *data; 12207 float *sdata; 12208 int nx, ny; 12209 int xlow, ylow, xhig, yhig; 12210 int nslits; 12211 int *slit_id; 12212 int *position; 12213 int *length; 12214 int i, j, k; 12215 12216 12217 if (spectra == NULL) { 12218 cpl_msg_error(func, 12219 "A scientific rectified spectral image must be given"); 12220 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12221 return NULL; 12222 } 12223 12224 if (slits == NULL) { 12225 cpl_msg_error(func, "A slits position table must be given"); 12226 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12227 return NULL; 12228 } 12229 12230 nslits = cpl_table_get_nrow(slits); 12231 slit_id = cpl_table_get_data_int(slits, "slit_id"); 12232 position = cpl_table_get_data_int(slits, "position"); 12233 length = cpl_table_get_data_int(slits, "length"); 12234 12235 nx = cpl_image_get_size_x(spectra); 12236 ny = cpl_image_get_size_y(spectra); 12237 12238 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12239 12240 xlow = 1; 12241 xhig = nx; 12242 for (i = 0; i < nslits; i++) { 12243 12244 if (length[i] == 0) 12245 continue; 12246 12247 /* 12248 * Define the extraction boundaries. We DON'T write: 12249 * 12250 * ylow = position[i]; 12251 * yhig = ylow + length[i]; 12252 * 12253 * because the cpl_image pixels are counted from 1, and because in 12254 * cpl_image_extract() the coordinates of the last pixel are inclusive. 12255 */ 12256 12257 ylow = position[i] + 1; 12258 yhig = ylow + length[i] - 1; 12259 12260 exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig); 12261 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 12262 cpl_image_delete(exslit); 12263 12264 data = cpl_image_get_data(skymap); 12265 data += nx * position[i]; 12266 12267 for (j = 0; j < length[i]; j++) { 12268 sdata = cpl_image_get_data(sky); 12269 for (k = 0; k < nx; k++) { 12270 *data++ = *sdata++; 12271 } 12272 } 12273 12274 cpl_image_delete(sky); 12275 } 12276 12277 return skymap; 12278 12279 } 12280 12281 12301 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order) 12302 { 12303 const char *func = "mos_sky_local"; 12304 12305 char name[MAX_COLNAME]; 12306 12307 cpl_polynomial *fit; 12308 cpl_vector *points; 12309 cpl_vector *values; 12310 cpl_vector *keep_points; 12311 cpl_vector *keep_values; 12312 cpl_image *exslit; 12313 cpl_image *sky; 12314 cpl_image *subtracted; 12315 cpl_image *profile; 12316 cpl_image *skymap; 12317 cpl_table *objects; 12318 float *data; 12319 float *sdata; 12320 float *xdata; 12321 double *vdata; 12322 double *pdata; 12323 double median; 12324 int nx, ny; 12325 int xlow, ylow, xhig, yhig; 12326 int nslits; 12327 int *slit_id; 12328 int *position; 12329 int *length; 12330 int *is_sky; 12331 int nsky, nbad; 12332 int maxobjects; 12333 int margin = 3; 12334 int radius = 6; 12335 int i, j, k; 12336 12337 12338 if (spectra == NULL) { 12339 cpl_msg_error(func, 12340 "A scientific rectified spectral image must be given"); 12341 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12342 return NULL; 12343 } 12344 12345 if (slits == NULL) { 12346 cpl_msg_error(func, "A slits position table must be given"); 12347 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12348 return NULL; 12349 } 12350 12351 if (order < 0) { 12352 cpl_msg_error(func, "Invalid fit order"); 12353 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12354 return NULL; 12355 } 12356 12357 nslits = cpl_table_get_nrow(slits); 12358 slit_id = cpl_table_get_data_int(slits, "slit_id"); 12359 position = cpl_table_get_data_int(slits, "position"); 12360 length = cpl_table_get_data_int(slits, "length"); 12361 12362 nx = cpl_image_get_size_x(spectra); 12363 ny = cpl_image_get_size_y(spectra); 12364 12365 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12366 12367 xlow = 1; 12368 xhig = nx; 12369 for (i = 0; i < nslits; i++) { 12370 12371 if (length[i] == 0) 12372 continue; 12373 12374 /* 12375 * Define the extraction boundaries. We DON'T write: 12376 * 12377 * ylow = position[i]; 12378 * yhig = ylow + length[i]; 12379 * 12380 * because the cpl_image pixels are counted from 1, and because in 12381 * cpl_image_extract() the coordinates of the last pixel are inclusive. 12382 */ 12383 12384 ylow = position[i] + 1; 12385 yhig = ylow + length[i] - 1; 12386 12387 exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig); 12388 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 12389 cpl_image_delete(exslit); 12390 12391 data = cpl_image_get_data(skymap); 12392 data += nx * position[i]; 12393 12394 for (j = 0; j < length[i]; j++) { 12395 sdata = cpl_image_get_data(sky); 12396 for (k = 0; k < nx; k++) { 12397 *data++ = *sdata++; 12398 } 12399 } 12400 12401 cpl_image_delete(sky); 12402 } 12403 12404 12405 /* 12406 * Preliminary sky-subtracted image 12407 */ 12408 12409 subtracted = cpl_image_duplicate(spectra); 12410 cpl_image_subtract(subtracted, skymap); 12411 cpl_image_delete(skymap); 12412 12413 12414 /* 12415 * Detect objects positions in all slits 12416 */ 12417 12418 objects = cpl_table_duplicate(slits); 12419 profile = mos_detect_objects(subtracted, objects, margin, radius, 0); 12420 cpl_image_delete(profile); 12421 cpl_image_delete(subtracted); 12422 12423 12424 /* 12425 * Flag the sky pixels. Note that maxobjects is intentionally 12426 * the max number of objects increased by one. 12427 */ 12428 12429 maxobjects = 1; 12430 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 12431 while (cpl_table_has_column(objects, name)) { 12432 maxobjects++; 12433 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 12434 } 12435 12436 is_sky = cpl_calloc(ny, sizeof(int)); 12437 12438 for (i = 0; i < nslits; i++) { 12439 12440 if (length[i] == 0) 12441 continue; 12442 12443 ylow = position[i] + margin; 12444 yhig = position[i] + length[i] - margin; 12445 12446 for (j = ylow; j < yhig; j++) 12447 is_sky[j] = 1; 12448 12449 for (j = 1; j < maxobjects; j++) { 12450 snprintf(name, MAX_COLNAME, "object_%d", j); 12451 if (cpl_table_is_valid(objects, name, i)) { 12452 snprintf(name, MAX_COLNAME, "start_%d", j); 12453 ylow = cpl_table_get_int(objects, name, i, NULL); 12454 snprintf(name, MAX_COLNAME, "end_%d", j); 12455 yhig = cpl_table_get_int(objects, name, i, NULL); 12456 for (k = ylow; k <= yhig; k++) 12457 is_sky[k] = 0; 12458 } 12459 } 12460 12461 12462 /* 12463 * Eliminate isolated sky points 12464 */ 12465 12466 ylow = position[i] + margin + 1; 12467 yhig = position[i] + length[i] - margin - 1; 12468 12469 for (j = ylow; j < yhig; j++) 12470 if (is_sky[j]) 12471 if (is_sky[j-1] == 0 && is_sky[j+1] == 0) 12472 is_sky[j] = 0; 12473 12474 } 12475 12476 12477 /* 12478 * Determination of the sky map 12479 */ 12480 12481 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12482 12483 for (i = 0; i < nslits; i++) { 12484 12485 if (length[i] == 0) 12486 continue; 12487 12488 ylow = position[i]; 12489 yhig = ylow + length[i]; 12490 12491 nsky = 0; 12492 for (j = ylow; j < yhig; j++) 12493 if (is_sky[j]) 12494 nsky++; 12495 12496 if (nsky > order + 1) { 12497 if (order) { 12498 points = cpl_vector_new(nsky); 12499 nsky = 0; 12500 for (j = ylow; j < yhig; j++) { 12501 if (is_sky[j]) { 12502 cpl_vector_set(points, nsky, j); 12503 nsky++; 12504 } 12505 } 12506 12507 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig); 12508 xdata = cpl_image_get_data(exslit); 12509 values = cpl_vector_new(nsky); 12510 12511 for (j = 0; j < nx; j++) { 12512 nsky = 0; 12513 for (k = ylow; k < yhig; k++) { 12514 if (is_sky[k]) { 12515 cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]); 12516 nsky++; 12517 } 12518 } 12519 12520 /* 12521 * Eliminate obvious outliers 12522 */ 12523 12524 median = cpl_vector_get_median_const(values); 12525 vdata = cpl_vector_get_data(values); 12526 pdata = cpl_vector_get_data(points); 12527 nbad = 0; 12528 for (k = 0; k < nsky; k++) { 12529 if (fabs(vdata[k] - median) < 100) { 12530 if (nbad) { 12531 vdata[k-nbad] = vdata[k]; 12532 pdata[k-nbad] = pdata[k]; 12533 } 12534 } 12535 else 12536 nbad++; 12537 } 12538 12539 if (nsky == nbad) 12540 continue; 12541 12542 if (nbad && nsky - nbad > order + 1) { 12543 keep_values = values; 12544 keep_points = points; 12545 values = cpl_vector_wrap(nsky-nbad, vdata); 12546 points = cpl_vector_wrap(nsky-nbad, pdata); 12547 } 12548 12549 if (nsky - nbad > order + 1) { 12550 12551 fit = cpl_polynomial_fit_1d_create(points, values, 12552 order, NULL); 12553 12554 if (fit) { 12555 for (k = ylow; k < yhig; k++) { 12556 xdata[j+(k-ylow)*nx] = 12557 cpl_polynomial_eval_1d(fit, k, NULL); 12558 } 12559 12560 cpl_polynomial_delete(fit); 12561 } 12562 else 12563 cpl_error_reset(); 12564 } 12565 else { 12566 for (k = 0; k < nsky; k++) { 12567 xdata[j+k*nx] = median; 12568 } 12569 } 12570 12571 if (nbad && nsky - nbad > order + 1) { 12572 cpl_vector_unwrap(values); 12573 cpl_vector_unwrap(points); 12574 values = keep_values; 12575 points = keep_points; 12576 } 12577 12578 if (nbad) { 12579 nsky = 0; 12580 for (k = ylow; k < yhig; k++) { 12581 if (is_sky[k]) { 12582 cpl_vector_set(points, nsky, k); 12583 nsky++; 12584 } 12585 } 12586 } 12587 12588 } 12589 12590 cpl_vector_delete(values); 12591 cpl_vector_delete(points); 12592 12593 cpl_image_copy(skymap, exslit, 1, ylow+1); 12594 cpl_image_delete(exslit); 12595 12596 } 12597 else { 12598 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig); 12599 xdata = cpl_image_get_data(exslit); 12600 values = cpl_vector_new(nsky); 12601 12602 for (j = 0; j < nx; j++) { 12603 nsky = 0; 12604 for (k = ylow; k < yhig; k++) { 12605 if (is_sky[k]) { 12606 cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]); 12607 nsky++; 12608 } 12609 } 12610 12611 median = cpl_vector_get_median_const(values); 12612 12613 for (k = ylow; k < yhig; k++) 12614 xdata[j+(k-ylow)*nx] = median; 12615 12616 } 12617 12618 cpl_vector_delete(values); 12619 12620 cpl_image_copy(skymap, exslit, 1, ylow+1); 12621 cpl_image_delete(exslit); 12622 } 12623 } 12624 else 12625 cpl_msg_warning(func, "Too few sky points in slit %d", i + 1); 12626 } 12627 12628 cpl_free(is_sky); 12629 12630 return skymap; 12631 12632 } 12633 12634 12656 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain, 12657 float threshold, float ratio) 12658 { 12659 const char *func = "mos_clean_cosmics"; 12660 12661 cpl_image *smoothImage; 12662 cpl_table *table; 12663 cpl_matrix *kernel; 12664 int *xdata; 12665 int *ydata; 12666 float *idata; 12667 float *sdata; 12668 float sigma, sum, value, smoothValue; 12669 double noise; 12670 int count; 12671 float fMax; 12672 int iMin, iMax, jMin, jMax, iPosMax, jPosMax; 12673 int xLen; 12674 int yLen; 12675 int nPix; 12676 int first = 1; /* position of first cosmic ray candidate 12677 encountered while scanning the image */ 12678 int pos, i, j, k, l, ii, jj, iii = 0, jjj = 0; 12679 int numCosmic = 0; 12680 int found, foundContiguousCandidate; 12681 int *cosmic; 12682 12683 12684 if (image == NULL) 12685 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12686 12687 12688 /* 12689 * "cosmic" is a flags holder (initialized to zero): 12690 * 12691 * -1 = candidate for cosmic ray 12692 * 0 = not a cosmic 12693 * 1 = a cosmic ray 12694 * 2 = member of current group of contiguous candidates 12695 * 3 = examined member of current group 12696 */ 12697 12698 xLen = cpl_image_get_size_x(image); 12699 yLen = cpl_image_get_size_y(image); 12700 12701 if (xLen < 4 || yLen < 4) 12702 return CPL_ERROR_NONE; 12703 12704 nPix = xLen * yLen; 12705 12706 /* 12707 * Noise estimation from negative offsets in image. Note that this 12708 * assumes that the background level (skyLevel) has already been 12709 * subtracted from the data. In this way we estimate the noise due 12710 * to detector readout and to the background signal level (before 12711 * it were removed). Theoretically this is given by 12712 * 12713 * noise = sqrt(ron^2 + skyLevel/gain) 12714 * 12715 * where ron is the read-out-noise. To this we will sum the noise 12716 * contribution due to any increase of the signal above the background 12717 * by an amount scienceLevel. Theoretically the total noise is given by 12718 * 12719 * totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain) 12720 * 12721 * that is 12722 * 12723 * totalNoise = sqrt(noise^2 + scienceLevel/gain) 12724 * 12725 */ 12726 12727 idata = cpl_image_get_data(image); 12728 noise = 0.0; 12729 count = 0; 12730 12731 for (i = 0; i < nPix; i++) { 12732 if (idata[i] < -0.00001) { 12733 noise -= idata[i]; 12734 count++; 12735 } 12736 } 12737 12738 noise /= count; 12739 noise *= 1.25; /* Factor to convert average deviation to sigma */ 12740 12741 cosmic = cpl_calloc(nPix, sizeof(int)); 12742 12743 if (threshold < 0.) 12744 threshold = 4.0; 12745 if (ratio < 0.) 12746 ratio = 2.0; 12747 12748 kernel = cpl_matrix_new(3, 3); 12749 cpl_matrix_fill(kernel, 1.0); 12750 cpl_matrix_set(kernel, 1, 1, 0.0); 12751 smoothImage = cpl_image_filter_median(image, kernel); 12752 cpl_matrix_delete(kernel); 12753 12754 /* 12755 * Loop on images pixels, searching for cosmic rays candidates. 12756 * Border pixels are currently excluded (they cannot contain 12757 * candidates), to avoid that the search for groups of contiguous 12758 * pixels would ever go out of image boundaries. In future we may 12759 * overcome this limit, adding an appropriate check when contiguous 12760 * pixels are searched. 12761 */ 12762 12763 sdata = cpl_image_get_data(smoothImage); 12764 12765 for (j = 1; j < yLen - 1; j++) { 12766 for (i = 1; i < xLen - 1; i++) { 12767 value = idata[i + j * xLen]; 12768 smoothValue = sdata[i + j * xLen]; 12769 if (smoothValue < 1.0) 12770 smoothValue = 1.0; 12771 sigma = sqrt(noise * noise + smoothValue / gain); 12772 if (value - smoothValue >= threshold * sigma) 12773 cosmic[i + j * xLen] = -1; 12774 } 12775 } 12776 12777 cpl_image_delete(smoothImage); 12778 12779 12780 /* 12781 * Search for groups of contiguous cosmic rays candidates. 12782 */ 12783 12784 do { 12785 found = 0; 12786 for (pos = first; pos < nPix; pos++) { 12787 if (cosmic[pos] == -1) { 12788 cosmic[pos] = 2; /* Candidate found. */ 12789 i = pos % xLen; /* Its coordinates. */ 12790 j = pos / xLen; 12791 first = pos; 12792 first++; /* ??? really necessary? */ 12793 found = 1; 12794 break; 12795 } 12796 } 12797 12798 if (found) { 12799 12800 /* 12801 * Determine new group of contiguous cosmic rays candidates. 12802 * Initialize the working box boundaries, iMin, iMax, jMin, jMax, 12803 * and the value of the max pixel and its position, fMax, iPosMax, 12804 * jPosMax. 12805 */ 12806 12807 iMin = iMax = iPosMax = i; 12808 jMin = jMax = jPosMax = j; 12809 fMax = idata[i + j * xLen]; 12810 12811 do { 12812 foundContiguousCandidate = 0; 12813 for (l = 0; l <= 1; l++) { 12814 for (k = 0; k <= 1; k++) { 12815 12816 /* 12817 * Looping on 4 pixels to North, East, South and West 12818 */ 12819 12820 ii = i + k - l; 12821 jj = j + k + l - 1; 12822 if (cosmic[ii + jj * xLen] == -1) { 12823 foundContiguousCandidate = 1; 12824 cosmic[ii + jj * xLen] = 2; 12825 /* Candidate belongs to current group */ 12826 iii = ii; /* Keep its position */ 12827 jjj = jj; 12828 12829 /* 12830 * Upgrade search box 12831 */ 12832 12833 if (ii < iMin) 12834 iMin = ii; 12835 if (ii > iMax) 12836 iMax = ii; 12837 if (jj < jMin) 12838 jMin = jj; 12839 if (jj > jMax) 12840 jMax = jj; 12841 12842 if (idata[ii + jj * xLen] > fMax) { 12843 fMax = idata[ii + jj * xLen]; 12844 iPosMax = ii; 12845 jPosMax = jj; 12846 } 12847 } 12848 } 12849 } 12850 12851 /* 12852 * We are done exploring the "cross". Now mark as "examined" 12853 * the current candidate (at the center of the cross): 12854 */ 12855 12856 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */ 12857 12858 if (foundContiguousCandidate) { 12859 12860 /* 12861 * Pass (arbitrarily) the coordinates of the LAST found 12862 * candidate 12863 */ 12864 12865 i = iii; 12866 j = jjj; 12867 12868 /* 12869 * Skip the rest, continue loop on new candidate 12870 */ 12871 12872 continue; 12873 } 12874 12875 12876 /* 12877 * Look for leftovers in the (growing!) search box 12878 */ 12879 12880 for (l = jMin; l <= jMax; l++) { 12881 for (k = iMin; k <= iMax; k++) { 12882 if (cosmic[k + l * xLen] == 2) { 12883 i = k; 12884 j = l; 12885 foundContiguousCandidate = 1; 12886 break; 12887 } 12888 } 12889 if (foundContiguousCandidate) 12890 break; 12891 } 12892 } while (foundContiguousCandidate); 12893 12894 12895 /* 12896 * No more contiguous candidates are found. Decide now 12897 * whether the current group is a cosmic ray or not. 12898 */ 12899 12900 sum = 0.; /* Sum of 8 pixels around max position */ 12901 for (l = -1; l <= 1; l++) { 12902 for (k = -1; k <= 1; k++) { 12903 if (l != 0 || k != 0) { 12904 sum += idata[iPosMax + k + (jPosMax + l) * xLen]; 12905 } 12906 } 12907 } 12908 12909 sum /= 8.; 12910 if (fMax > ratio * sum) { 12911 for (l = jMin - 1; l <= jMax + 1; l++) { 12912 for (k = iMin - 1; k <= iMax + 1; k++) { 12913 if (cosmic[k + l * xLen] == 3) { 12914 cosmic[k + l * xLen] = 1; 12915 numCosmic++; 12916 } 12917 } 12918 } 12919 } 12920 else { 12921 for (l = jMin - 1; l <= jMax + 1; l++) { 12922 for (k = iMin - 1; k <= iMax + 1; k++) { 12923 if (cosmic[k + l * xLen] != -1) { 12924 if (cosmic[k + l * xLen] == 1) 12925 numCosmic--; 12926 cosmic[k + l * xLen] = 0; 12927 } 12928 } 12929 } 12930 } 12931 } 12932 } while (found); 12933 12934 12935 /* 12936 * Prepare table containing cosmic rays coordinates. 12937 */ 12938 12939 table = cpl_table_new(numCosmic); 12940 cpl_table_new_column(table, "x", CPL_TYPE_INT); 12941 cpl_table_new_column(table, "y", CPL_TYPE_INT); 12942 cpl_table_set_column_unit(table, "x", "pixel"); 12943 cpl_table_set_column_unit(table, "y", "pixel"); 12944 xdata = cpl_table_get_data_int(table, "x"); 12945 ydata = cpl_table_get_data_int(table, "y"); 12946 12947 for (pos = 0, i = 0; pos < nPix; pos++) { 12948 if (cosmic[pos] == 1) { 12949 xdata[i] = (pos % xLen); 12950 ydata[i] = (pos / xLen); 12951 i++; 12952 } 12953 } 12954 12955 mos_clean_bad_pixels(image, table, 1); 12956 12957 cpl_free(cosmic); 12958 cpl_table_delete(table); 12959 12960 return CPL_ERROR_NONE; 12961 12962 } 12963 12964 12965 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table, 12966 int spectral) 12967 { 12968 const char *func = "mos_clean_cosmics"; 12969 12970 float *idata; 12971 int *isBadPix; 12972 int i, j, k, d; 12973 int xlen, ylen, totPix; 12974 int nBadPixels = 0; 12975 int sign, foundFirst; 12976 int *xValue = NULL; 12977 int *yValue = NULL; 12978 float save = 0.; 12979 double sumd; 12980 int cx, cy; 12981 int nPairs; 12982 float estimate[4]; 12983 int sx[] = {0, 1, 1, 1}; 12984 int sy[] = {1,-1, 0, 1}; 12985 int searchHorizon = 100; 12986 int percent = 15; 12987 12988 12989 if (image == NULL || table == NULL) 12990 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12991 12992 if (1 != cpl_table_has_column(table, "x")) 12993 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 12994 12995 if (1 != cpl_table_has_column(table, "y")) 12996 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 12997 12998 if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x")) 12999 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 13000 13001 if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y")) 13002 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 13003 13004 nBadPixels = cpl_table_get_nrow(table); 13005 13006 if (nBadPixels) { 13007 xlen = cpl_image_get_size_x(image); 13008 ylen = cpl_image_get_size_y(image); 13009 idata = cpl_image_get_data(image); 13010 totPix = xlen * ylen; 13011 if (((float) nBadPixels) / ((float) totPix) < percent/100.) { 13012 isBadPix = cpl_calloc(totPix, sizeof(int)); 13013 } 13014 else { 13015 cpl_msg_warning(func, "Too many bad pixels (> %d%%): " 13016 "skip bad pixel correction", percent); 13017 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13018 } 13019 } 13020 else { 13021 cpl_msg_debug(func, "No pixel values to interpolate"); 13022 return CPL_ERROR_NONE; 13023 } 13024 13025 xValue = cpl_table_get_data_int(table, "x"); 13026 yValue = cpl_table_get_data_int(table, "y"); 13027 13028 for (i = 0; i < nBadPixels; i++) 13029 isBadPix[xValue[i] + yValue[i] * xlen] = 1; 13030 13031 for (i = 0; i < nBadPixels; i++) { 13032 13033 /* 13034 * Search for the closest good pixel along the 4 fundamental 13035 * directions (in both senses): 13036 * \ | / 13037 * \|/ 13038 * --- --- 13039 * /|\ 13040 * / | \ 13041 * 13042 * Then collect pairs of values to interpolate linearly. 13043 */ 13044 13045 nPairs = 0; 13046 for (j = 0; j < 4; j++) { 13047 13048 if (spectral) /* Just horizontal interpolation for spectral data */ 13049 if (j != 2) 13050 continue; 13051 13052 estimate[nPairs] = 0.; /* Pairs interpolations are stored here */ 13053 sumd = 0.; 13054 foundFirst = 0; 13055 for (k = 0; k < 2; k++) { 13056 sign = 2 * k - 1; 13057 d = 0; 13058 cx = xValue[i]; 13059 cy = yValue[i]; 13060 do { 13061 cx += sign * sx[j]; 13062 cy += sign * sy[j]; 13063 if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 13064 break; 13065 d++; 13066 } while (isBadPix[cx + cy * xlen] && d < searchHorizon); 13067 13068 if (cx >= 0 && cx < xlen && 13069 cy >= 0 && cy < ylen && d < searchHorizon) { 13070 13071 /* 13072 * In this block is cripted the linear interpolation... 13073 */ 13074 13075 save = idata[cx + cy * xlen]; 13076 estimate[nPairs] += save / d; 13077 sumd += 1. / (double) d; 13078 if (k) { 13079 estimate[nPairs] /= sumd; 13080 nPairs++; 13081 } 13082 else { 13083 foundFirst = 1; 13084 } 13085 } 13086 else { 13087 13088 /* 13089 * Image borders were crossed, incomplete pair of values 13090 */ 13091 13092 if (k) { 13093 if (foundFirst) { 13094 estimate[nPairs] = save; 13095 nPairs++; 13096 } 13097 } 13098 } 13099 } 13100 } 13101 13102 /* 13103 * Replace pixel value of the input image, corresponding to 13104 * the current bad pixel, with the median of the estimates 13105 * resulted from the 4 linear interpolations. 13106 */ 13107 13108 if (nPairs > 2) { 13109 idata[xValue[i] + yValue[i] * xlen] = 13110 cpl_tools_get_median_float(estimate, nPairs); 13111 } 13112 else if (nPairs == 2) { 13113 idata[xValue[i] + yValue[i] * xlen] = 13114 (estimate[0] + estimate[1]) / 2.; 13115 } 13116 else if (nPairs == 1) { 13117 idata[xValue[i] + yValue[i] * xlen] = estimate[0]; 13118 } 13119 else { 13120 cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n", 13121 xValue[i], yValue[i]); 13122 } 13123 } 13124 13125 cpl_free(isBadPix); 13126 13127 return CPL_ERROR_NONE; 13128 } 13129 13130 13160 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits, 13161 cpl_table *polytraces, double reference, 13162 double blue, double red, double dispersion) 13163 { 13164 const char *func = "mos_spatial_map"; 13165 13166 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 13167 /* Max order is 5 */ 13168 cpl_polynomial *polytop; 13169 cpl_polynomial *polybot; 13170 cpl_image *calibration; 13171 float *data; 13172 double top, bot; 13173 double coeff; 13174 double ytop, ybot; 13175 double ypos, yfra; 13176 double factor; 13177 int yint, yprev; 13178 int nslits; 13179 int npseudo; 13180 int *slit_id; 13181 int *length; 13182 int nx, ny; 13183 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 13184 int missing_top, missing_bot; 13185 int null; 13186 int order; 13187 int i, j; 13188 cpl_size k; 13189 13190 13191 if (spectra == NULL || slits == NULL || polytraces == NULL) { 13192 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13193 return NULL; 13194 } 13195 13196 if (dispersion <= 0.0) { 13197 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13198 return NULL; 13199 } 13200 13201 if (red - blue < dispersion) { 13202 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13203 return NULL; 13204 } 13205 13206 nx = cpl_image_get_size_x(spectra); 13207 ny = cpl_image_get_size_y(spectra); 13208 13209 calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 13210 data = cpl_image_get_data(calibration); 13211 13212 length = cpl_table_get_data_int(slits, "length"); 13213 nslits = cpl_table_get_nrow(slits); 13214 slit_id = cpl_table_get_data_int(slits, "slit_id"); 13215 order = cpl_table_get_ncol(polytraces) - 2; 13216 13217 /* 13218 * The spatial resampling is performed for a certain number of 13219 * pixels above and below the position of the reference wavelength: 13220 */ 13221 13222 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 13223 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 13224 13225 for (i = 0; i < nslits; i++) { 13226 13227 if (length[i] == 0) 13228 continue; 13229 13230 /* 13231 * Note that the x coordinate of the reference pixels on the CCD 13232 * is taken arbitrarily at the top end of each slit. This wouldn't 13233 * be entirely correct in case of curved slits, or in presence of 13234 * heavy distortions: in such cases the spatial resampling is 13235 * really performed across a wide range of wavelengths. But 13236 * the lag between top and bottom spectral curvature models 13237 * would introduce even in such cases negligible effects on 13238 * the spectral spatial resampling. 13239 */ 13240 13241 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 13242 13243 start_pixel = refpixel - pixel_below; 13244 if (start_pixel < 0) 13245 start_pixel = 0; 13246 13247 end_pixel = refpixel + pixel_above; 13248 if (end_pixel > nx) 13249 end_pixel = nx; 13250 13251 /* 13252 * Recover from the table of spectral curvature coefficients 13253 * the curvature polynomials. 13254 */ 13255 13256 missing_top = 0; 13257 polytop = cpl_polynomial_new(1); 13258 for (k = 0; k <= order; k++) { 13259 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 13260 if (null) { 13261 cpl_polynomial_delete(polytop); 13262 missing_top = 1; 13263 break; 13264 } 13265 cpl_polynomial_set_coeff(polytop, &k, coeff); 13266 } 13267 13268 missing_bot = 0; 13269 polybot = cpl_polynomial_new(1); 13270 for (k = 0; k <= order; k++) { 13271 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 13272 if (null) { 13273 cpl_polynomial_delete(polybot); 13274 missing_bot = 1; 13275 break; 13276 } 13277 cpl_polynomial_set_coeff(polybot, &k, coeff); 13278 } 13279 13280 if (missing_top && missing_bot) { 13281 cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 13282 slit_id[i]); 13283 continue; 13284 } 13285 13286 /* 13287 * In case just one of the two edges was not traced, the other 13288 * edge curvature model is duplicated and shifted to the other 13289 * end of the slit: better than nothing! 13290 */ 13291 13292 if (missing_top) { 13293 cpl_msg_warning(func, "Upper edge of slit %d was not traced: " 13294 "the spectral curvature of the lower edge " 13295 "is used instead.", slit_id[i]); 13296 polytop = cpl_polynomial_duplicate(polybot); 13297 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 13298 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 13299 k = 0; 13300 coeff = cpl_polynomial_get_coeff(polybot, &k); 13301 coeff += ytop - ybot; 13302 cpl_polynomial_set_coeff(polytop, &k, coeff); 13303 } 13304 13305 if (missing_bot) { 13306 cpl_msg_warning(func, "Lower edge of slit %d was not traced: " 13307 "the spectral curvature of the upper edge " 13308 "is used instead.", slit_id[i]); 13309 polybot = cpl_polynomial_duplicate(polytop); 13310 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 13311 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 13312 k = 0; 13313 coeff = cpl_polynomial_get_coeff(polytop, &k); 13314 coeff -= ytop - ybot; 13315 cpl_polynomial_set_coeff(polybot, &k, coeff); 13316 } 13317 13318 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 13319 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 13320 npseudo = ceil(top-bot) + 1; 13321 13322 if (npseudo < 1) { 13323 cpl_polynomial_delete(polytop); 13324 cpl_polynomial_delete(polybot); 13325 cpl_msg_warning(func, "Slit %d was badly traced: no extraction!", 13326 slit_id[i]); 13327 continue; 13328 } 13329 13330 for (j = start_pixel; j < end_pixel; j++) { 13331 top = cpl_polynomial_eval_1d(polytop, j, NULL); 13332 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 13333 factor = (top-bot)/npseudo; 13334 for (k = 0; k <= npseudo; k++) { 13335 ypos = top - k*factor; 13336 yint = ypos; 13337 yfra = ypos - yint; 13338 if (yint >= 0 && yint < ny-1) { 13339 data[j + nx*yint] = (top-yint)/factor; 13340 if (k) { 13341 13342 /* 13343 * This is added to recover lost pixels on 13344 * the CCD image (pixels are lost because 13345 * the CCD pixels are less than npseudo+1). 13346 */ 13347 13348 if (yprev - yint > 1) { 13349 data[j + nx*(yint+1)] = (top-yint-1)/factor; 13350 } 13351 } 13352 } 13353 yprev = yint; 13354 } 13355 } 13356 cpl_polynomial_delete(polytop); 13357 cpl_polynomial_delete(polybot); 13358 } 13359 13360 return calibration; 13361 } 13362 13363 13426 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin, 13427 int maxradius, int conradius) 13428 { 13429 const char *func = "mos_detect_objects"; 13430 13431 cpl_image *profile; 13432 float *pdata; 13433 float *p; 13434 13435 char name[MAX_COLNAME]; 13436 13437 int nslits; 13438 int npeaks; 13439 int nobjects, objpos, totobj; 13440 int maxobjects; 13441 int *position; 13442 int *length; 13443 int *reject; 13444 double *place; 13445 double *bright; 13446 double mindistance; 13447 int pos, count; 13448 int up; 13449 int low, hig; 13450 int row; 13451 int i, j, k; 13452 13453 const int min_pixels = 10; 13454 13455 13456 if (cpl_error_get_code() != CPL_ERROR_NONE) 13457 return NULL; 13458 13459 if (image == NULL || slits == NULL) { 13460 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13461 return NULL; 13462 } 13463 13464 if (margin < 0) 13465 margin = 0; 13466 13467 if (maxradius < 0) { 13468 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13469 return NULL; 13470 } 13471 13472 if (conradius < 0) { 13473 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13474 return NULL; 13475 } 13476 13477 nslits = cpl_table_get_nrow(slits); 13478 position = cpl_table_get_data_int(slits, "position"); 13479 length = cpl_table_get_data_int(slits, "length"); 13480 13481 profile = cpl_image_collapse_create(image, 1); 13482 cpl_image_divide_scalar(profile, cpl_image_get_size_x(image)); 13483 pdata = cpl_image_get_data(profile); 13484 13485 row = 1; 13486 maxobjects = 0; 13487 totobj = 0; 13488 for (i = 0; i < nslits; i++) { 13489 13490 if (length[i] == 0) 13491 continue; 13492 13493 pos = position[i] + margin; 13494 count = length[i] - 2*margin; 13495 13496 if (count < min_pixels) 13497 continue; 13498 13499 p = pdata + pos; 13500 13501 13502 /* 13503 * Count peaks candidates 13504 */ 13505 13506 npeaks = 0; 13507 if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) { 13508 npeaks++; 13509 } 13510 13511 up = 0; 13512 for (j = 0; j < count - 3; j++) { 13513 if (p[j] > 0) { 13514 if (p[j+1] > p[j]) { 13515 up++; 13516 } 13517 else { 13518 if (up > 2) { 13519 if (p[j+1] > p[j+2] && p[j+2] > 0) { 13520 if (p[j] > 5) 13521 npeaks++; 13522 } 13523 } 13524 else if (up > 1) { 13525 if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) { 13526 if (p[j] > 5) 13527 npeaks++; 13528 } 13529 } 13530 up = 0; 13531 } 13532 } 13533 else { 13534 up = 0; 13535 } 13536 } 13537 13538 if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 13539 && p[count-3] > p[count-4] && p[count-4] > 0) { 13540 npeaks++; 13541 } 13542 13543 if (npeaks == 0) 13544 continue; 13545 13546 13547 /* 13548 * Get candidates parameters 13549 */ 13550 13551 reject = cpl_calloc(npeaks, sizeof(int)); 13552 bright = cpl_calloc(npeaks, sizeof(double)); 13553 place = cpl_calloc(npeaks, sizeof(double)); 13554 13555 npeaks = 0; 13556 if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) { 13557 bright[0] = p[0]; 13558 place[0] = position[i] + margin; 13559 npeaks++; 13560 } 13561 13562 up = 0; 13563 for (j = 0; j < count - 3; j++) { 13564 if (p[j] > 0) { 13565 if (p[j+1] > p[j]) { 13566 up++; 13567 } 13568 else { 13569 if (up > 2) { 13570 if (p[j+1] > p[j+2] && p[j+2] > 0) { 13571 if (p[j] > 5) { 13572 bright[npeaks] = p[j]; 13573 place[npeaks] = position[i] + margin + j + 1 13574 + values_to_dx(p[j-1], p[j], p[j+1]); 13575 npeaks++; 13576 } 13577 } 13578 } 13579 else if (up > 1) { 13580 if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) { 13581 if (p[j] > 5) { 13582 bright[npeaks] = p[j]; 13583 place[npeaks] = position[i] + margin + j + 1 13584 + values_to_dx(p[j-1], p[j], p[j+1]); 13585 npeaks++; 13586 } 13587 } 13588 } 13589 up = 0; 13590 } 13591 } 13592 else { 13593 up = 0; 13594 } 13595 } 13596 13597 if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 13598 && p[count-3] > p[count-4] && p[count-4] > 0) { 13599 bright[npeaks] = p[count-1]; 13600 place[npeaks] = position[i] + count; 13601 npeaks++; 13602 } 13603 13604 13605 /* 13606 * Now select the uncontaminated peaks 13607 */ 13608 13609 if (fabs(place[0] - pos) < 1.0) 13610 reject[0] = 1; 13611 if (fabs(place[npeaks-1] - pos - count) < 1.0) 13612 reject[npeaks-1] = 1; 13613 for (j = 0; j < npeaks; j++) { 13614 for (k = 0; k < npeaks; k++) { 13615 if (k == j) 13616 continue; 13617 mindistance = conradius * bright[k] / bright[j] 13618 * bright[k] / bright[j]; 13619 if (fabs(place[j] - place[k]) < mindistance) 13620 reject[j] = 1; 13621 } 13622 } 13623 13624 /* new part */ 13625 for (j = 0; j < npeaks; j++) { 13626 if (reject[j]) 13627 continue; 13628 if (j) { 13629 low = (place[j-1]*bright[j] + place[j]*bright[j-1]) 13630 / (bright[j-1] + bright[j]) + 1; 13631 } 13632 else { 13633 low = pos; 13634 } 13635 if (j < npeaks - 1) { 13636 hig = (place[j+1]*bright[j] + place[j]*bright[j+1]) 13637 / (bright[j+1] + bright[j]) + 1; 13638 } 13639 else { 13640 hig = pos + count; 13641 } 13642 13643 if (low < pos) 13644 low = pos; 13645 if (hig > pos + count) 13646 hig = pos + count; 13647 if (place[j] - low > maxradius) 13648 low = place[j] - maxradius; 13649 if (hig - place[j] > maxradius) 13650 hig = place[j] + maxradius; 13651 if (hig == low) 13652 reject[j] = 1; 13653 } 13654 /* end new part */ 13655 13656 nobjects = npeaks; 13657 for (j = 0; j < npeaks; j++) 13658 if (reject[j]) 13659 nobjects--; 13660 13661 for (j = 0; j < nobjects; j++) { 13662 snprintf(name, MAX_COLNAME, "object_%d", j+1); 13663 if (cpl_table_has_column(slits, name)) 13664 continue; 13665 cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE); 13666 snprintf(name, MAX_COLNAME, "start_%d", j+1); 13667 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13668 cpl_table_set_column_unit(slits, name, "pixel"); 13669 snprintf(name, MAX_COLNAME, "end_%d", j+1); 13670 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13671 cpl_table_set_column_unit(slits, name, "pixel"); 13672 snprintf(name, MAX_COLNAME, "row_%d", j+1); 13673 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13674 cpl_table_set_column_unit(slits, name, "pixel"); 13675 } 13676 13677 objpos = nobjects; 13678 for (j = 0; j < npeaks; j++) { 13679 if (reject[j]) 13680 continue; 13681 if (j) { 13682 low = (place[j-1]*bright[j] + place[j]*bright[j-1]) 13683 / (bright[j-1] + bright[j]) + 1; 13684 } 13685 else { 13686 low = pos; 13687 } 13688 if (j < npeaks - 1) { 13689 hig = (place[j+1]*bright[j] + place[j]*bright[j+1]) 13690 / (bright[j+1] + bright[j]) + 1; 13691 } 13692 else { 13693 hig = pos + count; 13694 } 13695 13696 if (low < pos) 13697 low = pos; 13698 if (hig > pos + count) 13699 hig = pos + count; 13700 if (place[j] - low > maxradius) 13701 low = place[j] - maxradius; 13702 if (hig - place[j] > maxradius) 13703 hig = place[j] + maxradius; 13704 13705 snprintf(name, MAX_COLNAME, "object_%d", objpos); 13706 cpl_table_set_double(slits, name, i, place[j]); 13707 snprintf(name, MAX_COLNAME, "start_%d", objpos); 13708 cpl_table_set_int(slits, name, i, low); 13709 snprintf(name, MAX_COLNAME, "end_%d", objpos); 13710 cpl_table_set_int(slits, name, i, hig); 13711 snprintf(name, MAX_COLNAME, "row_%d", objpos); 13712 cpl_table_set_int(slits, name, i, row + objpos - 1); 13713 totobj++; 13714 objpos--; 13715 } 13716 13717 row += nobjects; 13718 13719 if (maxobjects < nobjects) 13720 maxobjects = nobjects; 13721 13722 cpl_free(reject); 13723 cpl_free(bright); 13724 cpl_free(place); 13725 13726 } 13727 13728 /* nobjects = row - nobjects; A bug, I think... */ 13729 row = cpl_table_get_nrow(slits); 13730 13731 for (i = 0; i < row; i++) { 13732 for (j = 0; j < maxobjects; j++) { 13733 snprintf(name, MAX_COLNAME, "row_%d", j+1); 13734 if (cpl_table_is_valid(slits, name, i)) 13735 cpl_table_set_int(slits, name, i, totobj - 13736 cpl_table_get_int(slits, name, i, NULL)); 13737 } 13738 } 13739 13740 for (i = 0; i < maxobjects; i++) { 13741 snprintf(name, MAX_COLNAME, "start_%d", i+1); 13742 cpl_table_fill_invalid_int(slits, name, -1); 13743 snprintf(name, MAX_COLNAME, "end_%d", i+1); 13744 cpl_table_fill_invalid_int(slits, name, -1); 13745 snprintf(name, MAX_COLNAME, "row_%d", i+1); 13746 cpl_table_fill_invalid_int(slits, name, -1); 13747 } 13748 13749 return profile; 13750 } 13751 13752 13777 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky, 13778 cpl_table *objects, int extraction, double ron, 13779 double gain, int ncombined) 13780 { 13781 const char *func = "mos_extract_objects"; 13782 13783 char name[MAX_COLNAME]; 13784 13785 cpl_image **output; 13786 cpl_image *extracted; 13787 cpl_image *extr_sky; 13788 cpl_image *error; 13789 cpl_image *sciwin; 13790 cpl_image *skywin; 13791 int nslits; 13792 int nobjects; 13793 int maxobjects; 13794 int nx, ny; 13795 int ylow, yhig; 13796 int i, j; 13797 13798 13799 if (science == NULL || sky == NULL) { 13800 cpl_msg_error(func, "Both scientific exposures are required in input"); 13801 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13802 return NULL; 13803 } 13804 13805 if (objects == NULL) { 13806 cpl_msg_error(func, "An object table is required in input"); 13807 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13808 return NULL; 13809 } 13810 13811 if (extraction < 0 || extraction > 1) { 13812 cpl_msg_error(func, "Invalid extraction mode (%d): it should be " 13813 "either 0 or 1", extraction); 13814 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13815 return NULL; 13816 } 13817 13818 if (ron < 0.0) { 13819 cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron); 13820 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13821 return NULL; 13822 } 13823 13824 if (gain < 0.1) { 13825 cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain); 13826 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13827 return NULL; 13828 } 13829 13830 if (ncombined < 1) { 13831 cpl_msg_error(func, "Invalid number of combined frames (%d): " 13832 "it should be at least 1", ncombined); 13833 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13834 return NULL; 13835 } 13836 13837 13838 /* 13839 * Count the max number of objects per slit. Note that maxobjects 13840 * is intentionally the max number of objects increased by one. 13841 */ 13842 13843 maxobjects = 1; 13844 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 13845 while (cpl_table_has_column(objects, name)) { 13846 maxobjects++; 13847 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 13848 } 13849 13850 13851 /* 13852 * Count objects to extract 13853 */ 13854 13855 nobjects = 0; 13856 nslits = cpl_table_get_nrow(objects); 13857 13858 for (i = 0; i < nslits; i++) { 13859 for (j = 1; j < maxobjects; j++) { 13860 snprintf(name, MAX_COLNAME, "object_%d", j); 13861 if (cpl_table_is_valid(objects, name, i)) 13862 nobjects++; 13863 } 13864 } 13865 13866 if (nobjects == 0) 13867 return NULL; 13868 13869 nx = cpl_image_get_size_x(science); 13870 ny = cpl_image_get_size_x(science); 13871 13872 output = cpl_calloc(3, sizeof(cpl_image *)); 13873 extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13874 extr_sky = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13875 error = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13876 13877 13878 /* 13879 * Extract objects 13880 */ 13881 13882 nobjects = 0; 13883 for (i = 0; i < nslits; i++) { 13884 for (j = 1; j < maxobjects; j++) { 13885 snprintf(name, MAX_COLNAME, "object_%d", j); 13886 if (cpl_table_is_valid(objects, name, i)) { 13887 snprintf(name, MAX_COLNAME, "start_%d", j); 13888 ylow = cpl_table_get_int(objects, name, i, NULL); 13889 snprintf(name, MAX_COLNAME, "end_%d", j); 13890 yhig = cpl_table_get_int(objects, name, i, NULL); 13891 snprintf(name, MAX_COLNAME, "row_%d", j); 13892 nobjects = cpl_table_get_int(objects, name, i, NULL); 13893 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig); 13894 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig); 13895 /* 13896 * Cleaning the cosmics locally was really NOT a good idea... 13897 * I leave it here, commented out, to never forget this mistake! 13898 13899 if (extraction) { 13900 mos_clean_cosmics(sciwin, gain, -1., -1.); 13901 } 13902 */ 13903 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 13904 nobjects, extraction, ron, gain, ncombined); 13905 13906 /* 13907 * Hidden check whether the spectrum was saturated or not 13908 */ 13909 13910 { 13911 cpl_image *total = cpl_image_add_create(sciwin, skywin); 13912 float *data = cpl_image_get_data_float(total); 13913 int size = cpl_image_get_size_x(total) 13914 * cpl_image_get_size_y(total); 13915 int k; 13916 char *saturation_level = getenv("SATURATION_LEVEL"); 13917 float saturation = 62000.0; 13918 char *max_saturated = getenv("MAX_SATURATED"); 13919 int max_satur = 10; 13920 int saturated; 13921 13922 if (saturation_level) 13923 saturation = atof(saturation_level); 13924 13925 if (max_saturated) 13926 max_satur = atoi(max_saturated); 13927 13928 saturated = 0; 13929 for (k = 0; k < size; k++) { 13930 if (data[k] > saturation) { 13931 saturated++; 13932 if (saturated > max_satur) { 13933 break; 13934 } 13935 } 13936 } 13937 13938 if (saturated > max_satur) 13939 saturated = 1; 13940 else 13941 saturated = 0; 13942 13943 data = cpl_image_get_data(extracted); 13944 data[nobjects * nx] = saturated; 13945 } 13946 13947 cpl_image_delete(sciwin); 13948 cpl_image_delete(skywin); 13949 nobjects++; 13950 } 13951 } 13952 } 13953 13954 return output; 13955 13956 } 13957 13958 13981 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 13982 double dispersion, int saturation, 13983 double *mfwhm, double *rmsfwhm, 13984 double *resolution, double *rmsres, int *nlines) 13985 { 13986 cpl_vector *vector; 13987 13988 int i, j, n, m; 13989 int position, maxpos; 13990 int xlen, ylen; 13991 int sp, ep; 13992 int radius; 13993 int sradius = 40; 13994 int threshold = 250; /* Peak must be so many ADUs above min */ 13995 13996 int ifwhm; 13997 double fwhm; 13998 double *buffer; 13999 double min, max, halfmax; 14000 double cut = 1.5; /* To cut outliers from FWHM values (pixel) */ 14001 double value, rms; 14002 14003 float *data; 14004 14005 14006 *resolution = 0.0; 14007 *rmsres = 0.0; 14008 *nlines = 0; 14009 14010 xlen = cpl_image_get_size_x(image); 14011 ylen = cpl_image_get_size_y(image); 14012 data = cpl_image_get_data(image); 14013 14014 buffer = cpl_malloc(ylen * sizeof(double)); 14015 14016 /* 14017 * Closest pixel to specified wavelength. 14018 */ 14019 14020 position = floor((lambda - startwave) / dispersion + 0.5); 14021 14022 sp = position - sradius; 14023 ep = position + sradius; 14024 14025 if (sp < 0 || ep > xlen) { 14026 cpl_free(buffer); 14027 return 0; 14028 } 14029 14030 for (i = 0, n = 0; i < ylen; i++) { /* For each row of each slit */ 14031 14032 /* 14033 * Search interval for peak. Abort if too close to image border. 14034 */ 14035 14036 radius = mos_lines_width(data + i*xlen + position - sradius, 14037 2*sradius + 1); 14038 if (radius < 5) 14039 radius = 5; 14040 14041 sp = position - radius; 14042 ep = position + radius; 14043 14044 if (sp < 0 || ep > xlen) { 14045 cpl_free(buffer); 14046 return 0; 14047 } 14048 14049 14050 /* 14051 * Determine min-max value and position. 14052 */ 14053 14054 maxpos = sp; 14055 min = max = data[sp + i * xlen]; 14056 for (j = sp; j < ep; j++) { 14057 if (data[j + i * xlen] > max) { 14058 max = data[j + i * xlen]; 14059 maxpos = j; 14060 } 14061 if (data[j + i * xlen] < min) { 14062 min = data[j + i * xlen]; 14063 } 14064 } 14065 14066 if (fabs(min) < 0.0000001) /* Truncated spectrum */ 14067 continue; 14068 14069 if (max - min < threshold) /* Low signal... */ 14070 continue; 14071 14072 if (max > saturation) /* Saturation */ 14073 continue; 14074 14075 /* 14076 * Determine FWHM counting pixels with value greater than 14077 * half of the max value, to the right and to the left of 14078 * the max. Linear interpolation between the pixels where 14079 * the transition happens. 14080 */ 14081 14082 halfmax = (max + min)/ 2.0; 14083 14084 fwhm = 0.0; 14085 ifwhm = 0; 14086 for (j = maxpos; j < maxpos + radius; j++) { 14087 if (j < xlen) { 14088 if (data[j + i * xlen] < halfmax) { 14089 fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax) 14090 / (data[j - 1 + i * xlen] - data[j + i * xlen]); 14091 break; 14092 } 14093 ifwhm++; 14094 } 14095 } 14096 14097 ifwhm = 0; 14098 for (j = maxpos; j > maxpos - radius; j--) { 14099 if (j >= 0) { 14100 if (data[j + i * xlen] < halfmax) { 14101 fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax) 14102 / (data[j + 1 + i * xlen] - data[j + i * xlen]); 14103 break; 14104 } 14105 ifwhm++; 14106 } 14107 } 14108 14109 if (fwhm > 3.0) { 14110 buffer[n] = fwhm - 2.0; 14111 n++; 14112 } 14113 14114 } 14115 14116 if (n == 0) { 14117 cpl_free(buffer); 14118 return 0; 14119 } 14120 14121 vector = cpl_vector_wrap(n, buffer); 14122 value = cpl_vector_get_median_const(vector); 14123 cpl_vector_unwrap(vector); 14124 14125 rms = 0.0; 14126 for (i = 0, m = 0; i < n; i++) { 14127 if (fabs(buffer[i] - value) < cut) { 14128 rms += fabs(buffer[i] - value); 14129 m++; 14130 } 14131 } 14132 14133 cpl_free(buffer); 14134 14135 if (m < 3) 14136 return 0; 14137 14138 rms /= m; 14139 rms *= 1.25; /* Factor to convert average deviation to sigma */ 14140 14141 value *= dispersion; 14142 rms *= dispersion; 14143 14144 *mfwhm = value; 14145 *rmsfwhm = rms; 14146 14147 *resolution = lambda / value; 14148 *rmsres = *resolution * rms / value; 14149 14150 *nlines = m; 14151 14152 return 1; 14153 } 14154 14155 14177 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 14178 double dispersion, int saturation, 14179 cpl_vector *lines) 14180 { 14181 14182 cpl_table *table; 14183 double *line; 14184 double fwhm; 14185 double rmsfwhm; 14186 double resolution; 14187 double rmsres; 14188 int nref; 14189 int nlines; 14190 int i; 14191 14192 14193 nref = cpl_vector_get_size(lines); 14194 line = cpl_vector_get_data(lines); 14195 14196 table = cpl_table_new(nref); 14197 cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE); 14198 cpl_table_set_column_unit(table, "wavelength", "Angstrom"); 14199 cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE); 14200 cpl_table_set_column_unit(table, "fwhm", "Angstrom"); 14201 cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE); 14202 cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom"); 14203 cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE); 14204 cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE); 14205 cpl_table_new_column(table, "nlines", CPL_TYPE_INT); 14206 14207 for (i = 0; i < nref; i++) { 14208 if (mos_spectral_resolution(image, line[i], startwave, dispersion, 14209 saturation, &fwhm, &rmsfwhm, 14210 &resolution, &rmsres, &nlines)) { 14211 cpl_table_set_double(table, "wavelength", i, line[i]); 14212 cpl_table_set_double(table, "fwhm", i, fwhm); 14213 cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm); 14214 cpl_table_set_double(table, "resolution", i, resolution); 14215 cpl_table_set_double(table, "resolution_rms", i, rmsres); 14216 cpl_table_set_int(table, "nlines", i, nlines); 14217 } 14218 else 14219 cpl_table_set_int(table, "nlines", i, 0); 14220 } 14221 14222 if (cpl_table_has_valid(table, "wavelength")) 14223 return table; 14224 14225 cpl_table_delete(table); 14226 14227 return NULL; 14228 14229 } 14230 14231 14249 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap, 14250 int ystart, int yend, double wstart, double wend) 14251 { 14252 const char *func = "mos_integrate_signal"; 14253 14254 double sum; 14255 float *sdata; 14256 float *wdata; 14257 int nx, ny; 14258 int x, y; 14259 14260 14261 if (image == NULL || wavemap == NULL) { 14262 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14263 return 0.0; 14264 } 14265 14266 if (ystart > yend || wstart >= wend) { 14267 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14268 return 0.0; 14269 } 14270 14271 nx = cpl_image_get_size_x(image); 14272 ny = cpl_image_get_size_y(image); 14273 14274 if (!(nx == cpl_image_get_size_x(wavemap) 14275 && ny == cpl_image_get_size_y(wavemap))) { 14276 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 14277 return 0.0; 14278 } 14279 14280 if (ystart < 0 || yend > ny) { 14281 cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE); 14282 return 0.0; 14283 } 14284 14285 sdata = cpl_image_get_data(image); 14286 wdata = cpl_image_get_data(wavemap); 14287 14288 sdata += ystart*nx; 14289 wdata += ystart*nx; 14290 14291 sum = 0.0; 14292 for (y = ystart; y < yend; y++) { 14293 for (x = 0; x < nx; x++) { 14294 if (wdata[x] < wstart || wdata[x] > wend) 14295 continue; 14296 sum += sdata[x]; 14297 } 14298 sdata += nx; 14299 wdata += nx; 14300 } 14301 14302 return sum; 14303 14304 } 14305 14306 /**************************************************************************** 14307 * From this point on, the instrument dependent functions are added: 14308 * they are functions that retrieve information that is stored in 14309 * the data headers in some instrument specific way, such as the 14310 * location of overscans, the slits positions on the telescope 14311 * focal plane, the gain factor, etc. 14312 */ 14313 14314 14337 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header) 14338 { 14339 const char *func = "mos_load_slits_fors_mxu"; 14340 14341 cpl_table *slits; 14342 char keyname[MAX_COLNAME]; 14343 const char *instrume; 14344 const char *target_name; 14345 float slit_x; 14346 float slit_y; 14347 float length; 14348 /* double arc2mm = 0.53316; */ 14349 double arc2mm = 0.528; 14350 int nslits; 14351 int slit_id; 14352 int fors; 14353 int chip; 14354 int found; 14355 14356 /* 14357 * The limits below are used to exclude from the loaded slit list 14358 * any slit that surely doesn't belong to the used chip. This is 14359 * a way to reduce the chance of ambiguous slit identification. 14360 */ 14361 14362 float low_limit1 = 10.0; 14363 float hig_limit2 = 30.0; 14364 14365 14366 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14367 return NULL; 14368 } 14369 14370 if (header == NULL) { 14371 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14372 return NULL; 14373 } 14374 14375 14376 /* 14377 * See if this is FORS1 or FORS2; 14378 */ 14379 14380 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14381 14382 fors = 0; 14383 if (instrume[4] == '1') 14384 fors = 1; 14385 if (instrume[4] == '2') 14386 fors = 2; 14387 14388 if (fors != 2) { 14389 cpl_msg_error(func, "Wrong instrument: %s\n" 14390 "FORS2 is expected for MXU data", instrume); 14391 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14392 return NULL; 14393 } 14394 14395 14396 /* 14397 * The master and slave chips can be identified by their positions 14398 * in the chip array in the case of FORS2 data (with fors1 the chip 14399 * is always 1). chip = 2 is the master, chip = 1 is the slave. 14400 */ 14401 14402 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14403 14404 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14405 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14406 "in FITS header"); 14407 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14408 return NULL; 14409 } 14410 14411 if (chip != 1 && chip != 2) { 14412 cpl_msg_error(func, "Unexpected chip position in keyword " 14413 "ESO DET CHIP1 Y: %d", chip); 14414 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14415 return NULL; 14416 } 14417 14418 14419 /* 14420 * Count slits in header (excluding reference slits, and the slits 14421 * that _surely_ belong to the other chip) 14422 */ 14423 14424 nslits = 0; 14425 slit_id = 0; 14426 found = 1; 14427 14428 while (found) { 14429 slit_id++; 14430 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100); 14431 if (cpl_propertylist_has(header, keyname)) { 14432 slit_y = cpl_propertylist_get_double(header, keyname); 14433 14434 if (chip == 1) 14435 if (slit_y < low_limit1) 14436 continue; 14437 if (chip == 2) 14438 if (slit_y > hig_limit2) 14439 continue; 14440 14441 snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 14442 slit_id + 100); 14443 if (cpl_propertylist_has(header, keyname)) { 14444 target_name = cpl_propertylist_get_string(header, keyname); 14445 if (strncmp(target_name, "refslit", 7)) 14446 nslits++; 14447 } 14448 else 14449 nslits++; 14450 } 14451 else 14452 found = 0; 14453 } 14454 14455 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14456 cpl_msg_error(func, "%s while loading slits coordinates from " 14457 "FITS header", cpl_error_get_message()); 14458 cpl_error_set_where(func); 14459 return NULL; 14460 } 14461 14462 if (nslits == 0) { 14463 cpl_msg_error(func, "No slits coordinates found in header"); 14464 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14465 return NULL; 14466 } 14467 14468 slits = cpl_table_new(nslits); 14469 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14470 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14471 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14472 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14473 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14474 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14475 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14476 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14477 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14478 14479 nslits = 0; 14480 slit_id = 0; 14481 found = 1; 14482 while (found) { 14483 slit_id++; 14484 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100); 14485 if (cpl_propertylist_has(header, keyname)) { 14486 slit_y = cpl_propertylist_get_double(header, keyname); 14487 14488 if (chip == 1) 14489 if (slit_y < low_limit1) 14490 continue; 14491 if (chip == 2) 14492 if (slit_y > hig_limit2) 14493 continue; 14494 14495 /* 14496 * Y-flip the slit position, to match CCD pixel coordinate 14497 * convention 14498 */ 14499 14500 slit_y = -slit_y; 14501 14502 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100); 14503 slit_x = cpl_propertylist_get_double(header, keyname); 14504 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14505 cpl_table_delete(slits); 14506 cpl_msg_error(func, "Missing keyword %s in FITS header", 14507 keyname); 14508 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14509 return NULL; 14510 } 14511 14512 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100); 14513 length = cpl_propertylist_get_double(header, keyname); 14514 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14515 cpl_table_delete(slits); 14516 cpl_msg_error(func, "Missing keyword %s in FITS header", 14517 keyname); 14518 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14519 return NULL; 14520 } 14521 14522 length *= arc2mm; 14523 14524 snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 14525 slit_id + 100); 14526 if (cpl_propertylist_has(header, keyname)) { 14527 target_name = cpl_propertylist_get_string(header, keyname); 14528 if (strncmp(target_name, "refslit", 7)) { 14529 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14530 cpl_table_set(slits, "xtop", nslits, slit_x); 14531 cpl_table_set(slits, "ytop", nslits, slit_y + length/2); 14532 cpl_table_set(slits, "xbottom", nslits, slit_x); 14533 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2); 14534 nslits++; 14535 } 14536 } 14537 else { 14538 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14539 cpl_table_set(slits, "xtop", nslits, slit_x); 14540 cpl_table_set(slits, "ytop", nslits, slit_y + length/2); 14541 cpl_table_set(slits, "xbottom", nslits, slit_x); 14542 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2); 14543 nslits++; 14544 } 14545 } 14546 else 14547 found = 0; 14548 } 14549 14550 return slits; 14551 } 14552 14553 14576 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header) 14577 { 14578 const char *func = "mos_load_slits_fors_mos"; 14579 14580 cpl_table *slits; 14581 char keyname[MAX_COLNAME]; 14582 const char *instrume; 14583 const char *chipname; 14584 float slit_x; 14585 int first_slit, last_slit; 14586 cpl_size nslits; 14587 int slit_id; 14588 int fors; 14589 int chip; 14590 int fors_is_old; 14591 14592 /* 14593 * The Y coordinates of the slits are fixed 14594 */ 14595 14596 float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3, 14597 41.9, 29.3, 17.9, 5.3, -6.1, -18.7, 14598 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 14599 -102.1 }; 14600 float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7, 14601 30.1, 18.7, 6.1, -5.3, -17.9, -29.3, 14602 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3, 14603 -113.9 }; 14604 14605 14606 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14607 return NULL; 14608 } 14609 14610 if (header == NULL) { 14611 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14612 return NULL; 14613 } 14614 14615 14616 /* 14617 * See if this is FORS1 or FORS2; 14618 */ 14619 14620 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14621 14622 fors = 0; 14623 if (instrume[4] == '1') 14624 fors = 1; 14625 if (instrume[4] == '2') 14626 fors = 2; 14627 14628 if (fors == 0) { 14629 cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 14630 instrume); 14631 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14632 return NULL; 14633 } 14634 14635 /* FIXME: 14636 * This is the way FORS1 data belong to the upgraded chips, 14637 * named "Marlene" and "Norma III". It's a quick solution, 14638 * there are hardcoded values here!!! 14639 */ 14640 14641 chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID"); 14642 14643 if (chipname[0] == 'M' || chipname[0] == 'N') 14644 fors_is_old = 0; 14645 else 14646 fors_is_old = 1; 14647 14648 if (fors == 1 && fors_is_old) { 14649 first_slit = 1; 14650 last_slit = 19; 14651 } 14652 else { 14653 14654 /* 14655 * The master and slave chips can be identified by their positions 14656 * in the chip array in the case of FORS2 data: chip = 2 is the 14657 * master, chip = 1 is the slave. 14658 */ 14659 14660 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14661 14662 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14663 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14664 "in FITS header"); 14665 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14666 return NULL; 14667 } 14668 14669 if (chip != 1 && chip != 2) { 14670 cpl_msg_error(func, "Unexpected chip position in keyword " 14671 "ESO DET CHIP1 Y: %d", chip); 14672 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14673 return NULL; 14674 } 14675 14676 if (chip == 1) { 14677 first_slit = 12; 14678 last_slit = 19; 14679 } 14680 else { 14681 first_slit = 1; 14682 last_slit = 11; 14683 } 14684 } 14685 14686 14687 /* 14688 * Count slits in header (excluding closed slits - i.e. those with 14689 * offsets greater than 115 mm - and the slits that do not belong 14690 * to this chip) 14691 */ 14692 14693 nslits = 0; 14694 slit_id = 0; 14695 for (slit_id = first_slit; slit_id <= last_slit; slit_id++) { 14696 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id); 14697 if (cpl_propertylist_has(header, keyname)) { 14698 slit_x = cpl_propertylist_get_double(header, keyname); 14699 if (fabs(slit_x) < 115.0) 14700 nslits++; 14701 } 14702 else { 14703 cpl_msg_error(func, "Missing keyword %s in FITS header", keyname); 14704 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14705 return NULL; 14706 } 14707 } 14708 14709 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14710 cpl_msg_error(func, "%s while loading slits coordinates from " 14711 "FITS header", cpl_error_get_message()); 14712 cpl_error_set_where(func); 14713 return NULL; 14714 } 14715 14716 if (nslits == 0) { 14717 cpl_msg_error(func, "No slits coordinates found in header"); 14718 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14719 return NULL; 14720 } 14721 14722 slits = cpl_table_new(nslits); 14723 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14724 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14725 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14726 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14727 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14728 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14729 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14730 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14731 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14732 14733 nslits = 0; 14734 slit_id = 0; 14735 for (slit_id = first_slit; slit_id <= last_slit; slit_id++) { 14736 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id); 14737 slit_x = cpl_propertylist_get_double(header, keyname); 14738 if (fabs(slit_x) < 115.0) { 14739 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14740 cpl_table_set(slits, "xtop", nslits, slit_x); 14741 cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]); 14742 cpl_table_set(slits, "xbottom", nslits, slit_x); 14743 cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]); 14744 nslits++; 14745 } 14746 } 14747 14748 return slits; 14749 } 14750 14751 14775 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header) 14776 { 14777 const char *func = "mos_load_slits_fors_lss"; 14778 14779 cpl_table *slits; 14780 char *slit_name; 14781 const char *instrume; 14782 int fors; 14783 int chip; 14784 float ytop; 14785 float ybottom; 14786 14787 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14788 return NULL; 14789 } 14790 14791 if (header == NULL) { 14792 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14793 return NULL; 14794 } 14795 14796 14797 /* 14798 * See if this is FORS1 or FORS2; 14799 */ 14800 14801 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14802 14803 fors = 0; 14804 if (instrume[4] == '1') 14805 fors = 1; 14806 if (instrume[4] == '2') 14807 fors = 2; 14808 14809 if (fors == 0) { 14810 cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 14811 instrume); 14812 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14813 return NULL; 14814 } 14815 14816 if (fors == 1) { 14817 ytop = 109.94; 14818 ybottom = -109.94; 14819 } 14820 else { 14821 14822 /* 14823 * The master and slave chips can be identified by their positions 14824 * in the chip array in the case of FORS2 data: chip = 2 is the 14825 * master, chip = 1 is the slave. 14826 */ 14827 14828 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14829 14830 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14831 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14832 "in FITS header"); 14833 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14834 return NULL; 14835 } 14836 14837 if (chip != 1 && chip != 2) { 14838 cpl_msg_error(func, "Unexpected chip position in keyword " 14839 "ESO DET CHIP1 Y: %d", chip); 14840 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14841 return NULL; 14842 } 14843 14844 if (chip == 1) { 14845 ytop = 30.0; 14846 ybottom = -109.94; 14847 } 14848 else { 14849 ytop = 109.94; 14850 ybottom = -20.0; 14851 } 14852 } 14853 14854 14855 slits = cpl_table_new(1); 14856 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14857 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14858 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14859 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14860 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14861 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14862 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14863 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14864 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14865 14866 slit_name = (char *)cpl_propertylist_get_string(header, 14867 "ESO INS SLIT NAME"); 14868 14869 cpl_table_set(slits, "ytop", 0, ytop); 14870 cpl_table_set(slits, "ybottom", 0, ybottom); 14871 14872 if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) { 14873 cpl_table_set_int(slits, "slit_id", 0, 1); 14874 cpl_table_set(slits, "xbottom", 0, -0.075); 14875 cpl_table_set(slits, "xtop", 0, 0.075); 14876 } 14877 else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) { 14878 cpl_table_set_int(slits, "slit_id", 0, 2); 14879 cpl_table_set(slits, "xbottom", 0, 5.895); 14880 cpl_table_set(slits, "xtop", 0, 6.105); 14881 } 14882 else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) { 14883 cpl_table_set_int(slits, "slit_id", 0, 3); 14884 cpl_table_set(slits, "xbottom", 0, -6.135); 14885 cpl_table_set(slits, "xtop", 0, -5.865); 14886 } 14887 else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) { 14888 cpl_table_set_int(slits, "slit_id", 0, 4); 14889 cpl_table_set(slits, "xbottom", 0, 11.815); 14890 cpl_table_set(slits, "xtop", 0, 12.185); 14891 } 14892 else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) { 14893 cpl_table_set_int(slits, "slit_id", 0, 5); 14894 cpl_table_set(slits, "xbottom", 0, -12.265); 14895 cpl_table_set(slits, "xtop", 0, -11.735); 14896 } 14897 else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) { 14898 cpl_table_set_int(slits, "slit_id", 0, 6); 14899 cpl_table_set(slits, "xbottom", 0, 17.655); 14900 cpl_table_set(slits, "xtop", 0, 18.345); 14901 } 14902 else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) { 14903 cpl_table_set_int(slits, "slit_id", 0, 7); 14904 cpl_table_set(slits, "xbottom", 0, -18.425); 14905 cpl_table_set(slits, "xtop", 0, -17.575); 14906 } 14907 else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) { 14908 cpl_table_set_int(slits, "slit_id", 0, 8); 14909 cpl_table_set(slits, "xbottom", 0, 23.475); 14910 cpl_table_set(slits, "xtop", 0, 24.525); 14911 } 14912 else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) { 14913 cpl_table_set_int(slits, "slit_id", 0, 9); 14914 cpl_table_set(slits, "xbottom", 0, -24.66); 14915 cpl_table_set(slits, "xtop", 0, -23.34); 14916 } 14917 else { 14918 cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME", 14919 slit_name); 14920 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14921 cpl_table_delete(slits); 14922 return NULL; 14923 } 14924 14925 return slits; 14926 } 14927 14928 14943 double mos_get_gain_vimos(cpl_propertylist *header) 14944 { 14945 const char *func = "mos_get_gain_vimos"; 14946 14947 double gain = -1.0; 14948 14949 14950 if (cpl_error_get_code() != CPL_ERROR_NONE) 14951 return gain; 14952 14953 if (header == NULL) { 14954 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14955 return gain; 14956 } 14957 14958 gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD"); 14959 if (cpl_error_get_code()) { 14960 cpl_error_set_where(func); 14961 gain = -1.0; 14962 } 14963 14964 return gain; 14965 14966 } 14967 14968 14988 cpl_table *mos_load_slits_vimos(cpl_propertylist *header) 14989 { 14990 const char *func = "mos_load_slits_vimos"; 14991 14992 cpl_table *slits; 14993 char keyname[MAX_COLNAME]; 14994 float slit_x; 14995 float slit_y; 14996 float dim_x; 14997 float dim_y; 14998 int nslits; 14999 int slit_id; 15000 int curved; 15001 int i; 15002 15003 15004 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15005 return NULL; 15006 } 15007 15008 if (header == NULL) { 15009 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15010 return NULL; 15011 } 15012 15013 nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO"); 15014 15015 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15016 cpl_error_set_where(func); 15017 return NULL; 15018 } 15019 15020 slits = cpl_table_new(nslits); 15021 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 15022 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 15023 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 15024 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 15025 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 15026 cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE); 15027 cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE); 15028 cpl_table_new_column(slits, "curved", CPL_TYPE_INT); 15029 cpl_table_set_column_unit(slits, "xtop", "pixel"); 15030 cpl_table_set_column_unit(slits, "ytop", "pixel"); 15031 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 15032 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 15033 cpl_table_set_column_unit(slits, "xwidth", "mm"); 15034 cpl_table_set_column_unit(slits, "ywidth", "mm"); 15035 15036 for (i = 0; i < nslits; i++) { 15037 sprintf(keyname, "ESO INS SLIT%d ID", i+1); 15038 slit_id = cpl_propertylist_get_int(header, keyname); 15039 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15040 cpl_error_set_where(func); 15041 return NULL; 15042 } 15043 sprintf(keyname, "ESO INS SLIT%d X", i+1); 15044 slit_x = cpl_propertylist_get_double(header, keyname); 15045 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15046 cpl_error_set_where(func); 15047 return NULL; 15048 } 15049 sprintf(keyname, "ESO INS SLIT%d Y", i+1); 15050 slit_y = cpl_propertylist_get_double(header, keyname); 15051 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15052 cpl_error_set_where(func); 15053 return NULL; 15054 } 15055 sprintf(keyname, "ESO INS SLIT%d DIMX", i+1); 15056 dim_x = cpl_propertylist_get_double(header, keyname); 15057 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15058 cpl_error_set_where(func); 15059 return NULL; 15060 } 15061 15062 sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1); 15063 if (cpl_propertylist_has(header, keyname)) { 15064 curved = 1; 15065 } 15066 else { 15067 sprintf(keyname, "ESO INS SLIT%d DIMY", i+1); 15068 curved = 0; 15069 } 15070 dim_y = cpl_propertylist_get_double(header, keyname); 15071 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15072 cpl_error_set_where(func); 15073 return NULL; 15074 } 15075 15076 cpl_table_set_int(slits, "slit_id", i, slit_id); 15077 cpl_table_set(slits, "xtop", i, slit_x - dim_x/2); 15078 cpl_table_set(slits, "ytop", i, slit_y); 15079 cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2); 15080 cpl_table_set(slits, "ybottom", i, slit_y); 15081 cpl_table_set(slits, "xwidth", i, dim_x); 15082 cpl_table_set(slits, "ywidth", i, dim_y); 15083 cpl_table_set_int(slits, "curved", i, curved); 15084 } 15085 15086 return slits; 15087 } 15088 15089 15099 int mos_check_multiplex(cpl_table *slits) 15100 { 15101 cpl_propertylist *sort; 15102 int nrow; 15103 int i, multiplex, xprev, xcur; 15104 double prev, cur; 15105 double tolerance = 1.0; // About spatially aligned slits (mm) 15106 15107 15108 /* 15109 * Create an auxiliary column containing a sort of integer 15110 * x coordinate of the slit, to guarantee that slits at the 15111 * same spatial offset are recognised immediately as in spectral 15112 * multiplexing. 15113 */ 15114 15115 sort = cpl_propertylist_new(); 15116 cpl_propertylist_append_bool(sort, "xtop", 0); 15117 cpl_table_sort(slits, sort); 15118 cpl_propertylist_delete(sort); 15119 15120 prev = cpl_table_get_double(slits, "xtop", 0, NULL); 15121 cpl_table_new_column(slits, "xind", CPL_TYPE_INT); 15122 cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional 15123 nrow = cpl_table_get_nrow(slits); 15124 for (i = 1; i < nrow; i++) { 15125 cur = cpl_table_get_double(slits, "xtop", i, NULL); 15126 if (fabs(prev - cur) > tolerance) 15127 prev = cur; 15128 cpl_table_set_int(slits, "xind", i, prev); 15129 } 15130 15131 /* 15132 * Now sort according to increasing (integer) x positions, and when 15133 * those are equal (multiplexed) according to the increasing y position. 15134 */ 15135 15136 sort = cpl_propertylist_new(); 15137 cpl_propertylist_append_bool(sort, "xind", 0); 15138 cpl_propertylist_append_bool(sort, "ytop", 0); 15139 cpl_table_sort(slits, sort); 15140 cpl_propertylist_delete(sort); 15141 15142 /* 15143 * Now assign to each slit its multiplex order. 15144 */ 15145 15146 multiplex = 0; 15147 cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT); 15148 xprev = cpl_table_get_int(slits, "xind", 0, NULL); 15149 cpl_table_set_int(slits, "multiplex", 0, multiplex); 15150 nrow = cpl_table_get_nrow(slits); 15151 for (i = 1; i < nrow; i++) { 15152 xcur = cpl_table_get_int(slits, "xind", i, NULL); 15153 if (xcur == xprev) { 15154 multiplex++; 15155 } 15156 else { 15157 xprev = xcur; 15158 multiplex = 0; 15159 } 15160 cpl_table_set_int(slits, "multiplex", i, multiplex); 15161 } 15162 15163 cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT); 15164 15165 cpl_table_erase_column(slits, "xind"); 15166 15167 return 1 + cpl_table_get_column_max(slits, "multiplex"); 15168 15169 } 15170 15171 15198 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 15199 int check_consistency) 15200 { 15201 const char *func = "mos_load_overscans_vimos"; 15202 15203 int nx = 0; 15204 int ny = 0; 15205 int px = 0; 15206 int py = 0; 15207 int ox = 0; 15208 int oy = 0; 15209 int vx = 0; 15210 int vy = 0; 15211 int nrows; 15212 cpl_table *overscans; 15213 15214 15215 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15216 cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message()); 15217 return NULL; 15218 } 15219 15220 if (header == NULL) { 15221 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15222 return NULL; 15223 } 15224 15225 if (cpl_propertylist_has(header, "NAXIS1")) 15226 nx = cpl_propertylist_get_int(header, "NAXIS1"); 15227 if (cpl_propertylist_has(header, "NAXIS2")) 15228 ny = cpl_propertylist_get_int(header, "NAXIS2"); 15229 if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX")) 15230 px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX"); 15231 if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY")) 15232 py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY"); 15233 if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX")) 15234 ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX"); 15235 if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY")) 15236 oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY"); 15237 if (cpl_propertylist_has(header, "ESO DET OUT1 NX")) 15238 vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX"); 15239 if (cpl_propertylist_has(header, "ESO DET OUT1 NY")) 15240 vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY"); 15241 15242 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15243 cpl_msg_error(func, "Missing overscan keywords in header"); 15244 cpl_error_set_where(func); 15245 return NULL; 15246 } 15247 15248 if (px < 0 || py < 0 || ox < 0 || oy < 0) { 15249 cpl_msg_error(func, "Missing overscan keywords in header"); 15250 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15251 return NULL; 15252 } 15253 15254 if ((px + vx + ox != nx) || (py + vy + oy != ny)) { 15255 if (check_consistency) { 15256 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15257 return NULL; 15258 } 15259 else { 15260 cpl_msg_debug(func, "Overscans description conflicts with " 15261 "reported image sizes, " 15262 "%d + %d + %d != %d or " 15263 "%d + %d + %d != %d", 15264 px, vx, ox, nx, 15265 py, vy, oy, ny); 15266 } 15267 } 15268 15269 nrows = 0; 15270 if (px > 0) 15271 nrows++; 15272 if (ox > 0) 15273 nrows++; 15274 if (py > 0) 15275 nrows++; 15276 if (oy > 0) 15277 nrows++; 15278 15279 if (nrows > 2) { 15280 cpl_msg_error(func, "Unexpected overscan regions " 15281 "(both in X and Y direction)"); 15282 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15283 return NULL; 15284 } 15285 15286 15287 /* 15288 * A row is added for the description of the valid region of the 15289 * exposure the input header belongs to. 15290 */ 15291 15292 nrows++; 15293 15294 overscans = cpl_table_new(nrows); 15295 cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT); 15296 cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT); 15297 cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT); 15298 cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT); 15299 15300 nrows = 0; 15301 15302 cpl_table_set_int(overscans, "xlow", nrows, px); 15303 cpl_table_set_int(overscans, "ylow", nrows, py); 15304 cpl_table_set_int(overscans, "xhig", nrows, nx - ox); 15305 cpl_table_set_int(overscans, "yhig", nrows, ny - oy); 15306 nrows++; 15307 15308 if (px > 0) { 15309 cpl_table_set_int(overscans, "xlow", nrows, 0); 15310 cpl_table_set_int(overscans, "ylow", nrows, 0); 15311 cpl_table_set_int(overscans, "xhig", nrows, px); 15312 cpl_table_set_int(overscans, "yhig", nrows, ny); 15313 nrows++; 15314 } 15315 15316 if (ox > 0) { 15317 cpl_table_set_int(overscans, "xlow", nrows, nx - ox); 15318 cpl_table_set_int(overscans, "ylow", nrows, 0); 15319 cpl_table_set_int(overscans, "xhig", nrows, nx); 15320 cpl_table_set_int(overscans, "yhig", nrows, ny); 15321 nrows++; 15322 } 15323 15324 if (py > 0) { 15325 cpl_table_set_int(overscans, "xlow", nrows, 0); 15326 cpl_table_set_int(overscans, "ylow", nrows, 0); 15327 cpl_table_set_int(overscans, "xhig", nrows, nx); 15328 cpl_table_set_int(overscans, "yhig", nrows, py); 15329 nrows++; 15330 } 15331 15332 if (oy > 0) { 15333 cpl_table_set_int(overscans, "xlow", nrows, 0); 15334 cpl_table_set_int(overscans, "ylow", nrows, ny - oy); 15335 cpl_table_set_int(overscans, "xhig", nrows, nx); 15336 cpl_table_set_int(overscans, "yhig", nrows, ny); 15337 nrows++; 15338 } 15339 15340 return overscans; 15341 15342 } 15343 15344 15345 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header) 15346 { 15347 const char *func = "mos_load_overscans_fors"; 15348 15349 int nports; 15350 int nx = 0; 15351 int ny = 0; 15352 int px = 0; 15353 int py = 0; 15354 int ox = 0; 15355 int oy = 0; 15356 int rebin; 15357 int nrows; 15358 cpl_table *overscans; 15359 15360 15361 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15362 cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message()); 15363 return NULL; 15364 } 15365 15366 if (header == NULL) { 15367 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15368 return NULL; 15369 } 15370 15371 if (cpl_propertylist_has(header, "ESO DET OUTPUTS")) 15372 nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS"); 15373 15374 if (nports == 4 && 15375 cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") && 15376 cpl_propertylist_has(header, "ESO DET WIN1 BINX")) { 15377 15378 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX"); 15379 15380 overscans = cpl_table_new(3); 15381 cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT); 15382 cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT); 15383 cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT); 15384 cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT); 15385 15386 px = 16 / rebin; 15387 ox = 16 / rebin; 15388 nx = 2080 / rebin; 15389 ny = 2048 / rebin; 15390 nrows = 0; 15391 15392 cpl_table_set_int(overscans, "xlow", nrows, px); 15393 cpl_table_set_int(overscans, "ylow", nrows, py); 15394 cpl_table_set_int(overscans, "xhig", nrows, nx - ox); 15395 cpl_table_set_int(overscans, "yhig", nrows, ny - oy); 15396 nrows++; 15397 15398 cpl_table_set_int(overscans, "xlow", nrows, 0); 15399 cpl_table_set_int(overscans, "ylow", nrows, 0); 15400 cpl_table_set_int(overscans, "xhig", nrows, px); 15401 cpl_table_set_int(overscans, "yhig", nrows, ny); 15402 nrows++; 15403 15404 cpl_table_set_int(overscans, "xlow", nrows, nx - ox); 15405 cpl_table_set_int(overscans, "ylow", nrows, 0); 15406 cpl_table_set_int(overscans, "xhig", nrows, nx); 15407 cpl_table_set_int(overscans, "yhig", nrows, ny); 15408 nrows++; 15409 } 15410 else { 15411 overscans = mos_load_overscans_vimos(header, 0); 15412 } 15413 15414 return overscans; 15415 15416 } 15417 15449 #define READY 1 15450 #ifdef READY 15451 15452 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 15453 int samples, int order) 15454 { 15455 15456 const char *func = "mos_montecarlo_polyfit"; 15457 15458 cpl_polynomial *p; 15459 cpl_polynomial *q; 15460 cpl_vector *listx; 15461 cpl_vector *listy; 15462 double err; 15463 double *x; 15464 double *px; 15465 double *x_eval; 15466 double *px_eval; 15467 double *sigma; 15468 double *vy; 15469 double *dy; 15470 int npoints, nevaluate; 15471 int i, j; 15472 15473 15474 if (points == NULL || evaluate == NULL) { 15475 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15476 return NULL; 15477 } 15478 15479 if (!cpl_table_has_column(points, "x")) { 15480 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15481 return NULL; 15482 } 15483 15484 if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) { 15485 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15486 return NULL; 15487 } 15488 15489 if (cpl_table_has_invalid(points, "x")) { 15490 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15491 return NULL; 15492 } 15493 15494 if (!cpl_table_has_column(points, "y")) { 15495 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15496 return NULL; 15497 } 15498 15499 if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) { 15500 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15501 return NULL; 15502 } 15503 15504 if (cpl_table_has_invalid(points, "y")) { 15505 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15506 return NULL; 15507 } 15508 15509 if (cpl_table_has_column(points, "y_err")) { 15510 15511 if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) { 15512 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15513 return NULL; 15514 } 15515 15516 if (cpl_table_has_invalid(points, "y_err")) { 15517 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15518 return NULL; 15519 } 15520 } 15521 15522 if (!cpl_table_has_column(evaluate, "x")) { 15523 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15524 return NULL; 15525 } 15526 15527 if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) { 15528 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15529 return NULL; 15530 } 15531 15532 if (cpl_table_has_invalid(evaluate, "x")) { 15533 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15534 return NULL; 15535 } 15536 15537 if (samples < 2 || order < 0) { 15538 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15539 return NULL; 15540 } 15541 15542 npoints = cpl_table_get_nrow(points); 15543 listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x")); 15544 listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y")); 15545 15546 p = cpl_polynomial_fit_1d_create(listx, listy, order, &err); 15547 15548 if (!cpl_table_has_column(points, "y_err")) { 15549 err = sqrt(err); 15550 cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE); 15551 cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err); 15552 cpl_msg_info(func, "Error column not found - set to %f\n", err); 15553 } 15554 15555 /* 15556 * Create columns containing modeled values at each x 15557 */ 15558 15559 if (cpl_table_has_column(points, "px")) 15560 cpl_table_erase_column(points, "px"); 15561 cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE); 15562 cpl_table_fill_column_window_double(points, "px", 0, npoints, 0); 15563 x = cpl_table_get_data_double(points, "x"); 15564 px = cpl_table_get_data_double(points, "px"); 15565 for (i = 0; i < npoints; i++) 15566 px[i] = cpl_polynomial_eval_1d(p, x[i], NULL); 15567 15568 nevaluate = cpl_table_get_nrow(evaluate); 15569 15570 if (cpl_table_has_column(evaluate, "px")) 15571 cpl_table_erase_column(evaluate, "px"); 15572 cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE); 15573 cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0); 15574 x_eval = cpl_table_get_data_double(evaluate, "x"); 15575 px_eval = cpl_table_get_data_double(evaluate, "px"); 15576 for (i = 0; i < nevaluate; i++) 15577 px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL); 15578 15579 /* 15580 * Initialise column with sigma 15581 */ 15582 15583 if (cpl_table_has_column(evaluate, "sigma")) 15584 cpl_table_erase_column(evaluate, "sigma"); 15585 cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE); 15586 cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0); 15587 sigma = cpl_table_get_data_double(evaluate, "sigma"); 15588 15589 /* 15590 * Compute varied y cordinates to fit 15591 */ 15592 15593 if (cpl_table_has_column(points, "vy")) 15594 cpl_table_erase_column(points, "vy"); 15595 cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE); 15596 cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0); 15597 vy = cpl_table_get_data_double(points, "vy"); 15598 dy = cpl_table_get_data_double(points, "y_err"); 15599 cpl_vector_unwrap(listy); 15600 listy = cpl_vector_wrap(npoints, vy); 15601 15602 for (i = 0; i < samples; i++) { 15603 for (j = 0; j < npoints; j++) 15604 vy[j] = px[j] + dy[j] * mos_randg(1); 15605 q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL); 15606 for (j = 0; j < nevaluate; j++) 15607 sigma[j] += fabs(px_eval[j] 15608 - cpl_polynomial_eval_1d(q, x_eval[j], NULL)); 15609 cpl_polynomial_delete(q); 15610 } 15611 15612 /* 15613 * Factor 1.25 to convert average deviation to sigma 15614 */ 15615 15616 cpl_table_multiply_scalar(evaluate, "sigma", 1.25); 15617 cpl_table_divide_scalar(evaluate, "sigma", samples); 15618 15619 cpl_vector_unwrap(listx); 15620 cpl_vector_unwrap(listy); 15621 15622 return p; 15623 } 15624 15625 #endif 15626 15649 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 15650 double gain, double bias) 15651 { 15652 float *data; 15653 int npix, i; 15654 15655 15656 if (image == NULL) 15657 return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 15658 15659 if (ron < 0.0 || gain <= FLT_EPSILON) 15660 return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT); 15661 15662 data = cpl_image_get_data_float(image); 15663 npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image); 15664 ron *= ron; 15665 15666 for (i = 0; i < npix; i++) { 15667 if (data[i] < bias) { 15668 data[i] += sqrt(ron) * mos_randg(1); 15669 } 15670 else { 15671 data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1); 15672 } 15673 } 15674 15675 return CPL_ERROR_NONE; 15676 } 15677 15678 15693 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask, 15694 cpl_image *master_flat, 15695 double level) 15696 { 15697 int nx = cpl_mask_get_size_x(refmask); 15698 int ny = cpl_mask_get_size_y(refmask); 15699 15700 int * xpos = cpl_calloc(sizeof(int), ny); 15701 15702 cpl_image * filtered = cpl_image_duplicate(master_flat); 15703 cpl_mask * kernel = cpl_mask_new(9, 9); 15704 cpl_vector * v = cpl_vector_new(ny); 15705 cpl_vector * truev; 15706 int nvalid = 0; 15707 double * flats = cpl_vector_get_data(v); 15708 15709 double median, stdev, delta; 15710 15711 int i, kill; 15712 15713 15714 cpl_mask_not(kernel); 15715 cpl_image_filter_mask(filtered, master_flat, kernel, 15716 CPL_FILTER_MEDIAN, CPL_BORDER_COPY); 15717 cpl_mask_delete(kernel); 15718 15719 for (i = 1; i <= ny; i++) { 15720 int j = 0; 15721 15722 do j++; 15723 while (!cpl_mask_get(refmask, j, i) && j < nx); 15724 15725 if (j < nx) { 15726 int rejected; 15727 15728 xpos[i - 1] = j; 15729 flats[nvalid] = cpl_image_get(filtered, j, i, &rejected); 15730 nvalid++; 15731 } 15732 else { 15733 xpos[i - 1] = -1; 15734 } 15735 } 15736 15737 if (nvalid == 0) 15738 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 15739 15740 truev = cpl_vector_wrap(nvalid, flats); 15741 15742 median = cpl_vector_get_median(truev); 15743 15744 if (level < 0.0) 15745 stdev = cpl_vector_get_stdev(truev); 15746 15747 cpl_vector_unwrap(truev); 15748 cpl_vector_delete(v); 15749 15750 for (i = 1; i <= ny; i++) { 15751 if (xpos[i - 1] > 0) { 15752 int rejected; 15753 double kappa = 1.0; 15754 15755 delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median; 15756 15757 if (level < 0.0) 15758 kill = fabs(delta) > stdev * kappa; 15759 else 15760 kill = delta < level; 15761 15762 if (kill) { 15763 int j = 0; 15764 15765 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) { 15766 cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0); 15767 j++; 15768 } 15769 } 15770 } 15771 } 15772 15773 cpl_image_delete(filtered); 15774 cpl_free(xpos); 15775 15776 return cpl_error_get_code(); 15777 } 15778 15786 cpl_error_code mos_saturation_process(cpl_image * image) 15787 { 15788 int nx = cpl_image_get_size_x(image); 15789 int ny = cpl_image_get_size_y(image); 15790 int npix = nx * ny; 15791 float * sdata = cpl_image_get_data_float(image); 15792 15793 int count, i, j, k; 15794 15795 /* 15796 * This is used to avoid saturation level coded with pixel value zero 15797 * To make it more robust against random 0.0 values, check that also 15798 * next pixel along the spatial direction is 0.0. 15799 */ 15800 15801 for (i = 0; i < npix - nx; i++) 15802 if (sdata[i] == 0.0 && sdata[i + nx] == 0.0) 15803 sdata[i] = 65535.0; 15804 15805 for (i = npix - nx; i < npix; i++) 15806 if (sdata[i] == 0.0) 15807 sdata[i] = 65535.0; 15808 15809 /* 15810 * This is a dirty trick to overcome saturations (making up a false 15811 * tip on their flat tops). This should be useless with a better 15812 * peak detection algorithm. 15813 */ 15814 15815 for (i = 0; i < npix; i++) { 15816 if (sdata[i] >= 65535.0) { 15817 count = 0; 15818 for (j = i; j < npix; j++) { 15819 if (sdata[j] < 65535.0) { 15820 break; 15821 } 15822 else { 15823 count++; 15824 } 15825 } 15826 if (count < 30 && count > 2) { 15827 for (j = i; j < i + count/2; j++) 15828 sdata[j] = sdata[i] + 1000.0 * (j - i); 15829 if (count % 2 != 0) { 15830 sdata[j] = sdata[j-1] + 1000.0; 15831 j++; 15832 } 15833 for (k = j; k <= i + count; k++) 15834 sdata[k] = sdata[i] - 1000.0 * (k - i - count); 15835 i = k; 15836 } 15837 } 15838 } 15839 15840 return cpl_error_get_code(); 15841 } 15842 15843 15852 cpl_error_code mos_subtract_background(cpl_image * image) 15853 { 15854 /* 15855 * Create and subtract background 15856 */ 15857 15858 cpl_image * bimage = mos_arc_background(image, 15, 15); 15859 cpl_image_subtract(image, bimage); 15860 cpl_image_delete(bimage); 15861 15862 return cpl_error_get_code(); 15863 } 15864 15865 15882 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 15883 int nscience, float tolerance) 15884 { 15885 int i, j; 15886 15887 cpl_table *summary; 15888 int summary_nobjs = 0; 15889 15890 int nobjs; 15891 15892 int nmatches; 15893 int nslits = cpl_table_get_nrow(slitss[0]); 15894 15895 int maxobjs; 15896 int k, m; 15897 int nstokes, sstokes; 15898 15899 cpl_table **work; 15900 15901 work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience); 15902 15903 15904 /* 15905 * First we build a table listing the offset of each detected 15906 * object at each angle and each beam, from the bottom of each 15907 * slit spectrum, and the pair that slit spectrum belongs to. 15908 * This summary table will have as many rows as objects found 15909 * in total at all angles. 15910 */ 15911 15912 for (j = 0; j < nscience; j++) { 15913 int c_nobjs = mos_get_nobjects(slitss[j]); 15914 if (!c_nobjs) 15915 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 15916 summary_nobjs += c_nobjs; 15917 } 15918 15919 summary = cpl_table_new(summary_nobjs); 15920 15921 cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE); 15922 cpl_table_new_column(summary, "pair", CPL_TYPE_INT); 15923 cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE); 15924 cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE); 15925 15926 /* 15927 * Fill the summary table with data from all objects: 15928 */ 15929 15930 nobjs = 0; 15931 15932 /* Loop on all object tables (one for each angle) */ 15933 for (j = 0; j < nscience; j++) { 15934 int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]); 15935 15936 /* Loop on all slits found on first - i.e., ALL - object table */ 15937 for (k = 0; k < nslits; k++) { 15938 15939 /* Loop on all objects found on each object table */ 15940 for (m = 0; m < c_maxobjs; m++) { 15941 int null; 15942 char *name = cpl_sprintf("object_%d", m + 1); 15943 double obj = cpl_table_get_double(slitss[j], name, k, &null); 15944 int pos; 15945 int pair; 15946 15947 cpl_free(name); 15948 15949 if (null) 15950 break; /* No object #m+1 in this slit - go to next slit */ 15951 15952 /* 15953 * Copy necessary object data to summary table. Note 15954 * that the absolute object position (row) in the 15955 * rectified image is made relative to the bottom 15956 * position (row) of the current slit. 15957 */ 15958 15959 pos = cpl_table_get_int(slitss[j], "position", k, &null); 15960 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null); 15961 cpl_table_set(summary, "absolute", nobjs, obj); 15962 cpl_table_set(summary, "pos", nobjs, pos); 15963 cpl_table_set(summary, "offset", nobjs, obj - pos); 15964 cpl_table_set(summary, "pair", nobjs, pair); 15965 15966 nobjs++; 15967 } 15968 } 15969 } 15970 15971 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT); 15972 15973 /* 15974 * Perform the intersection: what are the objects belonging 15975 * to the same slit (same pair ordinary + extraordinary) which 15976 * are observed at the same offset at all angles? Those are 15977 * the polarimetric objects. 15978 */ 15979 15980 nmatches = 0; 15981 maxobjs = mos_get_maxobjs_per_slit(slitss[0]); 15982 15983 /* 15984 * We loop on the objects of the first-angle object table as 15985 * reference, and check whether those objects are present also 15986 * at *all* other angles. Note that the loop advances by pairs. 15987 * If the top (k = 0) slit spectrum is not an ordinary beam, 15988 * it is ignored. The loop advances by pairs, starting at the 15989 * first complete pair. It is implicitely assumed that the 15990 * slit spectrum on top is always from the ordinary beam, and 15991 * the spectrum below (k+1) its extraordinary match. 15992 */ 15993 15994 for (k = 0; k < nslits; k+=2) { 15995 int slitmatches = 0; 15996 15997 if (k == 0) { 15998 if (cpl_table_get_int(slitss[0], "pair_id", 0, NULL) != 15999 cpl_table_get_int(slitss[0], "pair_id", 1, NULL)) { 16000 16001 /* 16002 * This is not an ordinary beam - advance to next slit. 16003 */ 16004 16005 k++; 16006 continue; 16007 } 16008 } 16009 16010 for (m = 0; m < maxobjs; m++) { 16011 int null; 16012 char *name = cpl_sprintf("object_%d", m + 1); 16013 double obj = cpl_table_get_double(slitss[0], name, k, &null); 16014 double pos; 16015 int pair; 16016 16017 char *name_obj = NULL; 16018 char *name_start = NULL; 16019 char *name_end = NULL; 16020 char *name_row = NULL; 16021 char *name_row_s = NULL; 16022 16023 char *name_start_o = NULL; 16024 char *name_end_o = NULL; 16025 char *name_row_o = NULL; 16026 char *name_start_v = NULL; 16027 char *name_end_v = NULL; 16028 char *name_obj_v = NULL; 16029 16030 int start, end; 16031 int length; 16032 16033 int selected; 16034 int v, start_v, end_v; 16035 double min_v, obj_v; 16036 16037 16038 cpl_free(name); 16039 16040 if (null) 16041 break; 16042 16043 /* 16044 * Each object of the first object table belongs to a 16045 * slit spectrum (k). This slit spectrum has a position 16046 * in the rectified image, and it belongs to a given 16047 * ordinary + extraordinary pair. 16048 */ 16049 16050 pos = cpl_table_get_int(slitss[0], "position", k, &null); 16051 pair = cpl_table_get_int(slitss[0], "pair_id", k, &null); 16052 16053 /* 16054 * Now from the summary table we can select all objects 16055 * which have the same offset (obj - pos) within all slit 16056 * spectra belonging to the same ordinary + extraordinary 16057 * pair (at all angles). 16058 */ 16059 16060 cpl_table_select_all(summary); /* Reset selection */ 16061 16062 cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair); 16063 cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN, 16064 obj - pos + tolerance); 16065 selected = 16066 cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN, 16067 obj - pos - tolerance); 16068 16069 16070 /* 16071 * If this object were observed at all angles (nscience) and 16072 * at all beams (2), we should have selected exactly 2*nscience 16073 * objects. If not, this is not a polarimetric object, and it 16074 * is discarded from the intersection. 16075 */ 16076 16077 if (selected != nscience * 2) 16078 continue; 16079 16080 /* 16081 * If we reach this point we have found one valid polarimetric 16082 * object, that must be inserted in the intersection object 16083 * table. 16084 */ 16085 16086 slitmatches++; 16087 16088 /* 16089 * Names of the columns of the output table where the 16090 * object information needs to be copied. Note that a 16091 * new column is created, the "row_stokes_#", where the 16092 * row number of the extracted polarimetric signal is 16093 * also computed. For the moment this column will be 16094 * left empty - it will be filled only when all matches 16095 * are collected. 16096 */ 16097 16098 name_obj = cpl_sprintf("object_%d", slitmatches); 16099 name_start = cpl_sprintf("start_%d", slitmatches); 16100 name_end = cpl_sprintf("end_%d", slitmatches); 16101 name_row = cpl_sprintf("row_%d", slitmatches); 16102 name_row_s = cpl_sprintf("row_stokes_%d", slitmatches); 16103 16104 /* 16105 * Names of the columns of the input table where the 16106 * object information is available. 16107 */ 16108 16109 name_start_o = cpl_sprintf("start_%d", m + 1); 16110 name_end_o = cpl_sprintf("end_%d", m + 1); 16111 name_row_o = cpl_sprintf("row_%d", m + 1); 16112 16113 /* 16114 * If the output columns do not exist yet, create them. 16115 */ 16116 16117 if (!cpl_table_has_column(origslits, name_obj)) { 16118 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE); 16119 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT); 16120 cpl_table_new_column(origslits, name_end, CPL_TYPE_INT); 16121 cpl_table_new_column(origslits, name_row, CPL_TYPE_INT); 16122 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT); 16123 } 16124 16125 /* 16126 * The current slit spectrum is k. The slit spectrum immediately 16127 * below (in the rectified image) is k+1. We need the length of 16128 * the spectrum below for computing the _absolute_ coordinates 16129 * of the objects in the rectified image in both beams. 16130 */ 16131 16132 length = cpl_table_get_int(origslits, "length", k + 1, &null); 16133 16134 /* NEW: 16135 * Names of the columns of the input table where 16136 * the information of the corresponding object of 16137 * the next beam is available. 16138 */ 16139 16140 for (v = 0; v < maxobjs; v++) { 16141 char *name_v = cpl_sprintf("object_%d", v + 1); 16142 double obj_v = cpl_table_get_double(slitss[0], name_v, 16143 k + 1, &null); 16144 16145 cpl_free(name_v); 16146 16147 if (null) 16148 break; 16149 16150 if (v) { 16151 if (fabs(obj - length - obj_v) < min_v) { 16152 min_v = fabs(obj - length - obj_v); 16153 cpl_free(name_start_v); 16154 cpl_free(name_end_v); 16155 cpl_free(name_obj_v); 16156 name_start_v = cpl_sprintf("start_%d", v + 1); 16157 name_end_v = cpl_sprintf("end_%d", v + 1); 16158 name_obj_v = cpl_sprintf("object_%d", v + 1); 16159 } 16160 } 16161 else { 16162 min_v = fabs(obj - length - obj_v); 16163 name_start_v = cpl_sprintf("start_%d", v + 1); 16164 name_end_v = cpl_sprintf("end_%d", v + 1); 16165 name_obj_v = cpl_sprintf("object_%d", v + 1); 16166 } 16167 } 16168 16169 /* 16170 * Read from the first input object table (first angle) 16171 * the spatial window enclosing the object. 16172 */ 16173 16174 start = cpl_table_get_int(slitss[0], name_start_o, k, &null); 16175 end = cpl_table_get_int(slitss[0], name_end_o, k, &null); 16176 16177 /* NEW: 16178 * Spatial window of the matching object in the next beam. 16179 */ 16180 16181 start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null); 16182 end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null); 16183 obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null); 16184 16185 /* 16186 * Write the object coordinates in the same slit, and in the 16187 * slit below. Note that here we assume that all slits were 16188 * traced perfectly, and we compute the theoretical coords 16189 * (obj - length) within the next slit spectrum (k + 1). In 16190 * principle we should read them as well from the input 16191 * table! 16192 */ 16193 16194 cpl_table_set_double(origslits, name_obj, k, obj); 16195 cpl_table_set_double(origslits, name_obj, k + 1, obj_v); 16196 // cpl_table_set_double(origslits, name_obj, k + 1, obj - length); 16197 16198 cpl_table_set_int(origslits, name_start, k, start); 16199 cpl_table_set_int(origslits, name_start, k + 1, start_v); 16200 // cpl_table_set_int(origslits, name_start, k + 1, start - length); 16201 16202 cpl_table_set_int(origslits, name_end, k, end); 16203 cpl_table_set_int(origslits, name_end, k + 1, end_v); 16204 // cpl_table_set_int(origslits, name_end, k + 1, end - length); 16205 16206 /* 16207 * "nmatches" is counting at what "reduced" image row the 16208 * extracted spectra are. Note that this is s preliminary 16209 * numbering - which is wrong: other objects may be found 16210 * in the same slit, and then the indeces would not be in 16211 * sequence. What is important is that at the end of this 16212 * loop "nmatches" would be the total number of matching 16213 * objects. The two cpl_table_set_int() calls made here 16214 * cannot be removed - they "validate" those table elements 16215 * (see ahead). 16216 */ 16217 16218 cpl_table_set_int(origslits, name_row, k, nmatches); 16219 nmatches++; 16220 cpl_table_set_int(origslits, name_row, k + 1, nmatches); 16221 nmatches++; 16222 16223 cpl_free(name_obj); 16224 cpl_free(name_start); 16225 cpl_free(name_end); 16226 cpl_free(name_row); 16227 cpl_free(name_row_s); 16228 16229 cpl_free(name_start_o); 16230 cpl_free(name_end_o); 16231 cpl_free(name_row_o); 16232 16233 cpl_free(name_start_v); name_start_v = NULL; 16234 cpl_free(name_end_v); name_end_v = NULL; 16235 cpl_free(name_obj_v); name_obj_v = NULL; 16236 } 16237 } 16238 16239 /* 16240 * The summary table has fulfilled its function. If no matching 16241 * objects are found, the function returns with an error. 16242 */ 16243 16244 cpl_table_delete(summary); 16245 16246 if (!nmatches) 16247 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 16248 16249 /* 16250 * Now we consider the resulting intersection object table, 16251 * listing all matches. As seen, the image row number reported 16252 * in the columns "row_#" was not really performed sequentially. 16253 * We need to renumber sequentially... 16254 * We need also to fill the "row_stokes_#" column the way the 16255 * extracted polarimetric signal will be stored in the 16256 * reduced_pol_images... 16257 */ 16258 16259 maxobjs = mos_get_maxobjs_per_slit(origslits); 16260 nstokes = nmatches / 2; /* nmatches is always an even number */ 16261 16262 for (k = 0; k < nslits; k++) { 16263 if (k % 2) { /* Extraordinary beam */ 16264 nstokes = sstokes; /* Use same start value as for ordinary */ 16265 } 16266 else { /* Ordinary beam */ 16267 sstokes = nstokes; /* Memorise start value at ordinary beam */ 16268 } 16269 16270 for (m = 0; m < maxobjs; m++) { 16271 char *name = cpl_sprintf("row_%d", m + 1); 16272 char *namestokes = cpl_sprintf("row_stokes_%d", m + 1); 16273 16274 if (!cpl_table_is_valid(origslits, name, k)) { 16275 cpl_free(name); 16276 cpl_free(namestokes); 16277 break; 16278 } 16279 else { 16280 nmatches--; 16281 nstokes--; 16282 cpl_table_set_int(origslits, name, k, nmatches); 16283 cpl_table_set_int(origslits, namestokes, k, nstokes); 16284 } 16285 16286 cpl_free(name); 16287 cpl_free(namestokes); 16288 } 16289 } 16290 16291 16292 /* 16293 * This is done to avoid the NULL value is zero (it would invalidate 16294 * also the row_# = 0 or start_# = 0 for an object), and to enable 16295 * working directly with the column data buffers, when using this 16296 * table afterwards. 16297 */ 16298 16299 for (j = 0; j < maxobjs; j++) { 16300 char *name = cpl_sprintf("object_%d", j + 1); 16301 cpl_table_fill_invalid_double(origslits, name, -1); 16302 cpl_free(name); 16303 16304 name = cpl_sprintf("start_%d", j + 1); 16305 cpl_table_fill_invalid_int(origslits, name, -1); 16306 cpl_free(name); 16307 16308 name = cpl_sprintf("end_%d", j + 1); 16309 cpl_table_fill_invalid_int(origslits, name, -1); 16310 cpl_free(name); 16311 16312 name = cpl_sprintf("row_%d", j + 1); 16313 cpl_table_fill_invalid_int(origslits, name, -1); 16314 cpl_free(name); 16315 16316 name = cpl_sprintf("row_stokes_%d", j + 1); 16317 cpl_table_fill_invalid_int(origslits, name, -1); 16318 cpl_free(name); 16319 } 16320 16321 /********************************************************************* 16322 * This tail has been added to propagate the selection of valid 16323 * objects also to the input slitss[] tables. Just eliminate all 16324 * this final part to suppress this behaviour. 16325 */ 16326 16327 /* 16328 * First of all, make a working copy and remove all columns related 16329 * to objects from the input object tables. 16330 */ 16331 16332 for (i = 0; i < nscience; i++) { 16333 int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]); 16334 16335 work[i] = cpl_table_duplicate(slitss[i]); 16336 16337 for (m = 0; m < c_maxobjs; m++) { 16338 char *object_o = cpl_sprintf("object_%d", m + 1); 16339 char *start_o = cpl_sprintf("start_%d", m + 1); 16340 char *end_o = cpl_sprintf("end_%d", m + 1); 16341 char *row_o = cpl_sprintf("row_%d", m + 1); 16342 16343 cpl_table_erase_column(slitss[i], object_o); 16344 cpl_table_erase_column(slitss[i], start_o); 16345 cpl_table_erase_column(slitss[i], end_o); 16346 cpl_table_erase_column(slitss[i], row_o); 16347 } 16348 } 16349 16350 /* 16351 * Now just consider all the objects in the intersection table. 16352 */ 16353 16354 for (k = 0; k < nslits; k++) { 16355 for (j = 0; j < maxobjs; j++) { 16356 double object_w, object_r; 16357 int start_w, start_r; 16358 int end_w, end_r; 16359 int row_w, row_r; 16360 16361 char *object_i = cpl_sprintf("object_%d", j + 1); 16362 char *start_i = cpl_sprintf("start_%d", j + 1); 16363 char *end_i = cpl_sprintf("end_%d", j + 1); 16364 char *row_i = cpl_sprintf("row_%d", j + 1); 16365 16366 16367 if (!cpl_table_is_valid(origslits, object_i, k)) 16368 break; 16369 16370 /* 16371 * We have found a valid object (valid because it belongs 16372 * to the intersection). Now we look for this object in each 16373 * one of the original tables, we get its parameters, and 16374 * copy them at the right position (i.e., same position as 16375 * in intersection table). The object will be the one closest 16376 * to the object position (column object_i) in the intersection 16377 * table. Note that we examine the same row, k, in all tables. 16378 */ 16379 16380 object_w = cpl_table_get_double(origslits, object_i, k, NULL); 16381 start_w = cpl_table_get_int (origslits, start_i, k, NULL); 16382 end_w = cpl_table_get_int (origslits, end_i, k, NULL); 16383 row_w = cpl_table_get_int (origslits, row_i, k, NULL); 16384 16385 for (i = 0; i < nscience; i++) { 16386 int c_maxobjs = mos_get_maxobjs_per_slit(work[i]); 16387 int minpos; 16388 double mindiff, diff; 16389 char *object_o; 16390 char *start_o; 16391 char *end_o; 16392 char *row_o; 16393 16394 for (m = 0; m < c_maxobjs; m++) { 16395 object_o = cpl_sprintf("object_%d", m + 1); 16396 start_o = cpl_sprintf("start_%d", m + 1); 16397 end_o = cpl_sprintf("end_%d", m + 1); 16398 row_o = cpl_sprintf("row_%d", m + 1); 16399 16400 if (!cpl_table_is_valid(work[i], object_o, k)) 16401 break; 16402 16403 object_r = cpl_table_get_double(work[i], object_o, k, NULL); 16404 start_r = cpl_table_get_int (work[i], start_o, k, NULL); 16405 end_r = cpl_table_get_int (work[i], end_o, k, NULL); 16406 row_r = cpl_table_get_int (work[i], row_o, k, NULL); 16407 16408 diff = fabs(object_w - object_r); 16409 if (m) { 16410 if (mindiff > diff) { 16411 mindiff = diff; 16412 minpos = m; 16413 } 16414 } 16415 else { 16416 mindiff = diff; 16417 minpos = 0; 16418 } 16419 16420 cpl_free(object_o); 16421 cpl_free(start_o); 16422 cpl_free(end_o); 16423 cpl_free(row_o); 16424 } 16425 16426 object_o = cpl_sprintf("object_%d", minpos + 1); 16427 start_o = cpl_sprintf("start_%d", minpos + 1); 16428 end_o = cpl_sprintf("end_%d", minpos + 1); 16429 row_o = cpl_sprintf("row_%d", minpos + 1); 16430 16431 if (!cpl_table_has_column(slitss[i], object_i)) { 16432 cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE); 16433 cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT); 16434 cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT); 16435 cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT); 16436 cpl_table_fill_invalid_double(slitss[i], object_i, -1); 16437 cpl_table_fill_invalid_int (slitss[i], start_i, -1); 16438 cpl_table_fill_invalid_int (slitss[i], end_i, -1); 16439 cpl_table_fill_invalid_int (slitss[i], row_i, -1); 16440 } 16441 16442 cpl_table_set_double(slitss[i], object_i, k, 16443 cpl_table_get_double(work[i], object_o, 16444 k, NULL)); 16445 cpl_table_set_int(slitss[i], start_i , k, 16446 cpl_table_get_int(work[i], start_o, k, NULL)); 16447 cpl_table_set_int(slitss[i], end_i , k, 16448 cpl_table_get_int(work[i], end_o, k, NULL)); 16449 cpl_table_set_int(slitss[i], row_i , k, row_w); 16450 16451 cpl_free(object_o); 16452 cpl_free(start_o); 16453 cpl_free(end_o); 16454 cpl_free(row_o); 16455 } 16456 16457 cpl_free(object_i); 16458 cpl_free(start_i); 16459 cpl_free(end_i); 16460 cpl_free(row_i); 16461 } 16462 } 16463 16464 for (i = 0; i < nscience; i++) 16465 cpl_table_delete(work[i]); 16466 16467 cpl_free(work); 16468 16469 16470 return cpl_error_get_code(); 16471 } 16472 16473 16481 int mos_get_maxobjs_per_slit(cpl_table * slits) 16482 { 16483 int maxobjs = 1; 16484 16485 char * colname = cpl_sprintf("object_%d", maxobjs); 16486 16487 while (cpl_table_has_column(slits, colname)) { 16488 maxobjs++; 16489 cpl_free(colname); 16490 colname = cpl_sprintf("object_%d", maxobjs); 16491 } 16492 16493 cpl_free(colname); 16494 16495 maxobjs--; 16496 16497 return maxobjs; 16498 } 16499 16507 int mos_get_nobjects(cpl_table * slits) 16508 { 16509 int nobjs = 0; 16510 16511 int nslits = cpl_table_get_nrow(slits); 16512 int maxobjs = mos_get_maxobjs_per_slit(slits); 16513 16514 int k, m; 16515 16516 for (k = 0; k < nslits; k++) { 16517 for (m = 0; m < maxobjs; m++) { 16518 char * name = cpl_sprintf("object_%d", m + 1); 16519 int null = !cpl_table_is_valid(slits, name, k); 16520 16521 cpl_free(name); 16522 16523 if (null) break; 16524 else nobjs++; 16525 } 16526 } 16527 16528 return nobjs; 16529 } 16530 16538 int mos_check_slits(cpl_table *slits, float rescale) 16539 { 16540 16541 cpl_propertylist *sort; 16542 16543 int nslits = cpl_table_get_nrow(slits); 16544 16545 int k, null; 16546 16547 const float interval = 90.0 * rescale; 16548 const float offset = (90.0 - 5) * rescale; 16549 16550 16551 for (k = 0; k < nslits; k++) { 16552 double ytop = cpl_table_get_double(slits, "ytop", k, &null); 16553 double ybottom = cpl_table_get_double(slits, "ybottom", k, &null); 16554 16555 double xtop = cpl_table_get_double(slits, "xtop", k, &null); 16556 double xbottom = cpl_table_get_double(slits, "xbottom", k, &null); 16557 16558 int nmiss = (int)((ytop - ybottom) / interval + 0.5); 16559 16560 if (nmiss > 1) { 16561 cpl_msg_warning(cpl_func, 16562 "Some slits could not be properly detected. " 16563 "There might be accountable inaccuracies."); 16564 while (nmiss > 1) { 16565 cpl_table_set_size(slits, nslits + 1); 16566 16567 /* Fill in new slit 'cut' */ 16568 16569 /* x coordinates be the same (acceptable approximation) */ 16570 cpl_table_set_double(slits, "xtop", nslits, xtop); 16571 cpl_table_set_double(slits, "xbottom", nslits, xbottom); 16572 16573 /* Cut */ 16574 if (k == 0) { 16575 cpl_table_set_double(slits, "ybottom", nslits, ybottom); 16576 cpl_table_set_double(slits, "ytop", nslits, ybottom 16577 + offset); 16578 ybottom += interval; 16579 cpl_table_set_double(slits, "ybottom", k, ybottom); 16580 } else { 16581 cpl_table_set_double(slits, "ytop", nslits, ytop); 16582 cpl_table_set_double(slits, "ybottom", nslits, ytop 16583 - offset); 16584 ytop -= interval; 16585 cpl_table_set_double(slits, "ytop", k, ytop); 16586 } 16587 16588 nslits++; nmiss--; 16589 } 16590 } 16591 } 16592 16593 sort = cpl_propertylist_new(); 16594 cpl_propertylist_append_bool(sort, "ytop", 1); 16595 cpl_table_sort(slits, sort); 16596 cpl_propertylist_delete(sort); 16597 16598 /* 16599 * Add here an ad hoc check on the last slit: is it too long 16600 * (by more than 10%)? Then shorten it... 16601 */ 16602 16603 k = cpl_table_get_nrow(slits) - 1; 16604 16605 { 16606 double ytop = cpl_table_get_double(slits, "ytop", k, &null); 16607 double ybottom = cpl_table_get_double(slits, "ybottom", k, &null); 16608 double length = (ytop - ybottom) / interval; 16609 16610 if (length > 1.1) { 16611 cpl_table_set_double(slits, "ybottom", k, ytop - offset); 16612 } 16613 16614 } 16615 16616 return 0; 16617 } 16618 16641 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header) 16642 { 16643 int m, null; 16644 int halfsize; 16645 16646 cpl_propertylist * sort; 16647 cpl_table * slits; 16648 16649 slits = mos_load_slits_fors_mos(header); 16650 halfsize = cpl_table_get_nrow(slits); 16651 16652 cpl_table_set_size(slits, 2 * halfsize); 16653 16654 for (m = 0; m < halfsize; m++) { 16655 16656 double gap = 1.4; 16657 16658 double length = 16659 cpl_table_get(slits, "ytop", m, &null) - 16660 cpl_table_get(slits, "ybottom", m, &null); 16661 16662 if (m) { 16663 double interval = 16664 cpl_table_get(slits, "ybottom", m - 1, &null) - 16665 cpl_table_get(slits, "ytop", m, &null); 16666 16667 gap = (interval - length) / 2; 16668 } 16669 16670 cpl_table_set(slits, "slit_id", m + halfsize, 16671 cpl_table_get(slits, "slit_id", m, &null) - 1); 16672 16673 cpl_table_set(slits, "xtop", m + halfsize, 16674 cpl_table_get(slits, "xtop", m, &null)); 16675 16676 cpl_table_set(slits, "xbottom", m + halfsize, 16677 cpl_table_get(slits, "xbottom", m, &null)); 16678 16679 cpl_table_set(slits, "ytop", m + halfsize, 16680 cpl_table_get(slits, "ytop", m, &null) + gap + length); 16681 16682 cpl_table_set(slits, "ybottom", m + halfsize, 16683 cpl_table_get(slits, "ytop", m, &null) + gap); 16684 } 16685 16686 for (m = 0; m < 2 * halfsize; m++) { 16687 cpl_table_set(slits, "ytop", m, 16688 cpl_table_get(slits, "ytop", m, &null) - 5.3); 16689 16690 cpl_table_set(slits, "ybottom", m, 16691 cpl_table_get(slits, "ybottom", m, &null) - 5.3); 16692 16693 } 16694 16695 sort = cpl_propertylist_new(); 16696 cpl_propertylist_append_bool(sort, "ytop", 1); 16697 cpl_table_sort(slits, sort); 16698 16699 cpl_propertylist_delete(sort); 16700 16701 return slits; 16702 } 16703 16704 int * fors_get_nobjs_perslit(cpl_table * slits) 16705 { 16706 int nslits = cpl_table_get_nrow(slits); 16707 int maxobjs = mos_get_maxobjs_per_slit(slits); 16708 16709 int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits); 16710 16711 int k, m; 16712 16713 for (k = 0; k < nslits; k++) { 16714 int nobjs = 0; 16715 for (m = 0; m < maxobjs; m++) { 16716 char * name = cpl_sprintf("object_%d", m + 1); 16717 int null = !cpl_table_is_valid(slits, name, k); 16718 16719 cpl_free(name); 16720 16721 if (null) break; 16722 else nobjs++; 16723 } 16724 16725 nobjs_per_slit[k] = nobjs; 16726 } 16727 16728 return nobjs_per_slit; 16729 } 16730 16731 double fors_get_object_position(cpl_table *slits, int slit, int object) 16732 { 16733 char *name = cpl_sprintf("object_%d", object); 16734 double position; 16735 16736 position = cpl_table_get_double(slits, name, slit, NULL) 16737 - cpl_table_get_int(slits, "position", slit, NULL); 16738 16739 cpl_free(name); 16740 16741 return position; 16742 } 16743 16744 int mos_rebin_signal(cpl_image **image, int rebin) 16745 { 16746 cpl_image *rebinned; 16747 16748 16749 if (*image == NULL) 16750 return 1; 16751 16752 if (rebin == 1) 16753 return 0; 16754 16755 rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1); 16756 16757 cpl_image_delete(*image); 16758 16759 *image = rebinned; 16760 16761 return 0; 16762 } 16763 16764 int mos_rebin_error(cpl_image **image, int rebin) 16765 { 16766 if (*image == NULL) 16767 return 1; 16768 16769 if (rebin == 1) 16770 return 0; 16771 16772 cpl_image_power(*image, 2); 16773 mos_rebin_signal(image, rebin); 16774 cpl_image_power(*image, 0.5); 16775 16776 return 0; 16777 } 16778 16779 /* 16780 * @brief 16781 * Map table values into a 1D image 16782 * 16783 * @param image Target image 16784 * @param start Coordinate of first pixel in image 16785 * @param step Coordinate step for one pixel in image 16786 * @param table Source table 16787 * @param xname Name of coordinate column 16788 * @param yname Name of values column 16789 * 16790 * @return 0 on success 16791 * 16792 * The values in @em yname are linearly interpolated at the @em image 16793 * pixel coordinates. The @em image must have Nx1 size. 16794 */ 16795 16796 int map_table(cpl_image *image, double start, double step, 16797 cpl_table *table, char *xname, char *yname) 16798 { 16799 int length = cpl_image_get_size_x(image); 16800 int nrows = cpl_table_get_nrow(table); 16801 float *data = cpl_image_get_data_float(image); 16802 float *fdata = NULL; 16803 double *xdata = NULL; 16804 double *ydata = NULL; 16805 cpl_type xtype = cpl_table_get_column_type(table, xname); 16806 cpl_type ytype = cpl_table_get_column_type(table, yname); 16807 double xzero, pos; 16808 int i, j, n; 16809 16810 16811 /* 16812 * Initialization of output image at 0.0 - this value is left 16813 * on non-overlapping portions. 16814 */ 16815 16816 for (i = 0; i < length; i++) 16817 data[i] = 0.0; 16818 16819 16820 /* 16821 * Do everything in double precision 16822 */ 16823 16824 if (xtype == CPL_TYPE_FLOAT) { 16825 fdata = cpl_table_get_data_float(table, xname); 16826 xdata = cpl_malloc(nrows * sizeof(double)); 16827 for (i = 0; i < nrows; i++) { 16828 xdata[i] = fdata[i]; 16829 } 16830 } 16831 else { 16832 xdata = cpl_table_get_data_double(table, xname); 16833 } 16834 16835 if (ytype == CPL_TYPE_FLOAT) { 16836 fdata = cpl_table_get_data_float(table, yname); 16837 ydata = cpl_malloc(nrows * sizeof(double)); 16838 for (i = 0; i < nrows; i++) { 16839 ydata[i] = fdata[i]; 16840 } 16841 } 16842 else { 16843 ydata = cpl_table_get_data_double(table, yname); 16844 } 16845 16846 /* 16847 * Mapping 16848 */ 16849 16850 n = 0; 16851 xzero = xdata[n]; 16852 16853 for (i = 0; i < length; i++) { 16854 pos = start + step * i; 16855 if (pos < xzero) 16856 continue; 16857 for (j = n; j < nrows; j++) { 16858 if (xdata[j] > pos) { 16859 n = j; 16860 data[i] = ydata[j-1] 16861 + (ydata[j] - ydata[j-1]) 16862 * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]); 16863 break; 16864 } 16865 } 16866 } 16867 16868 if (xtype == CPL_TYPE_FLOAT) 16869 cpl_free(xdata); 16870 16871 if (ytype == CPL_TYPE_FLOAT) 16872 cpl_free(ydata); 16873 16874 return 0; 16875 } 16876 16877 16878 /* 16879 * @brief 16880 * Fit overall trend of a Nx1 image 16881 * 16882 * @param image Values to smooth 16883 * @param order Order of fitting polynomial 16884 * @param hw Half width of smoothing window 16885 * 16886 * @return Smoothed image, or NULL on failure. 16887 * 16888 * Heavily smooth and fit data in the input Nx1 size @em image. 16889 */ 16890 16891 static cpl_image *polysmooth(cpl_image *image, int order, int hw) 16892 { 16893 int npoints; 16894 cpl_vector *x; 16895 cpl_vector *y; 16896 double *xdata; 16897 double *ydata; 16898 cpl_polynomial *poly; 16899 cpl_vector *ysmooth; 16900 cpl_image *smoothed; 16901 float *sdata; 16902 int i; 16903 16904 16905 npoints = cpl_image_get_size_x(image); 16906 16907 if (2 * hw + 1 > npoints) 16908 return NULL; 16909 16910 x = cpl_vector_new(npoints); 16911 y = cpl_vector_new(npoints); 16912 xdata = cpl_vector_get_data(x); 16913 ydata = cpl_vector_get_data(y); 16914 16915 smoothed = cpl_image_duplicate(image); 16916 sdata = cpl_image_get_data_float(smoothed); 16917 16918 for (i = 0; i < npoints; i++) { 16919 xdata[i] = i; 16920 ydata[i] = sdata[i]; 16921 } 16922 16923 ysmooth = cpl_vector_filter_median_create(y, hw); 16924 cpl_vector_delete(y); 16925 16926 poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL); 16927 cpl_vector_delete(x); 16928 cpl_vector_delete(ysmooth); 16929 16930 if (poly) { 16931 for (i = 0; i < npoints; i++) 16932 sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL); 16933 16934 cpl_polynomial_delete(poly); 16935 } 16936 else { 16937 cpl_image_delete(smoothed); 16938 return NULL; 16939 } 16940 16941 return smoothed; 16942 } 16943 16944 #undef cleanup 16945 #define cleanup \ 16946 do { \ 16947 cpl_image_delete(spectrum); \ 16948 cpl_image_delete(flux); \ 16949 cpl_image_delete(efficiency); \ 16950 cpl_image_delete(smo_efficiency); \ 16951 cpl_image_delete(extinction); \ 16952 cpl_image_delete(response); \ 16953 cpl_image_delete(smo_response); \ 16954 cpl_image_delete(physical); \ 16955 } while (0) 16956 16980 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 16981 double dispersion, double gain, 16982 double exptime, cpl_table *ext_table, 16983 double airmass, cpl_table *flux_table, 16984 int order) 16985 { 16986 16987 cpl_image *spectrum = NULL; // Extracted standard star spectrum 16988 float *data; 16989 cpl_image *extinction = NULL; // Extinction binned as "spectrum" 16990 float *ext_data; 16991 cpl_image *flux = NULL; // Standard star flux binned as "spectrum" 16992 float *flux_data; 16993 cpl_image *physical = NULL; // Physical units of above 16994 float *phys_data; 16995 cpl_image *efficiency = NULL; // Raw efficiency curve 16996 float *eff_data; 16997 cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve 16998 float *smo_eff_data; 16999 cpl_image *response = NULL; // Raw response curve 17000 float *res_data; 17001 cpl_image *smo_response = NULL; // Smoothed response curve 17002 float *smo_res_data; 17003 cpl_image *image; 17004 cpl_image *smo_image; 17005 cpl_table *table; 17006 float lambda; 17007 int nx, ny; 17008 int ext_count, ext_pos; 17009 int eff_count, eff_pos; 17010 int flux_count, flux_pos; 17011 int start, end; 17012 int i; 17013 17014 17015 if (spectra == NULL || ext_table == NULL || flux_table == NULL) { 17016 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17017 return NULL; 17018 } 17019 17020 if (!cpl_table_has_column(ext_table, "WAVE")) { 17021 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17022 "Column WAVE in atmospheric extinction table"); 17023 return NULL; 17024 } 17025 17026 if (!cpl_table_has_column(ext_table, "EXTINCTION")) { 17027 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17028 "Column EXTINCTION in atmospheric extinction table"); 17029 return NULL; 17030 } 17031 17032 if (!cpl_table_has_column(flux_table, "WAVE")) { 17033 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17034 "Column WAVE in standard star flux table"); 17035 return NULL; 17036 } 17037 17038 if (!cpl_table_has_column(flux_table, "FLUX")) { 17039 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17040 "Column FLUX in standard star flux table"); 17041 return NULL; 17042 } 17043 17044 if (gain < 0.1) { 17045 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17046 "Invalid gain factor (%.2f)", gain); 17047 return NULL; 17048 } 17049 17050 if (exptime < 0.001) { 17051 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17052 "Invalid exposure time (%.2f)", exptime); 17053 return NULL; 17054 } 17055 17056 if (dispersion < 0.001) { 17057 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17058 "Invalid dispersion (%.2f)", dispersion); 17059 return NULL; 17060 } 17061 17062 if (order < 2) { 17063 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17064 "Order of the polynomial fitting the " 17065 "instrument response must be at least 2"); 17066 return NULL; 17067 } 17068 17069 nx = cpl_image_get_size_x(spectra); 17070 ny = cpl_image_get_size_y(spectra); 17071 17072 17073 /* 17074 * Find brightest spectrum and duplicate it. 17075 */ 17076 17077 if (ny == 1) { 17078 spectrum = cpl_image_duplicate(spectra); 17079 } 17080 else { 17081 cpl_size x, y; 17082 cpl_image *brights = cpl_image_collapse_create(spectra, 1); 17083 17084 cpl_image_get_maxpos(brights, &x, &y); 17085 cpl_image_delete(brights); 17086 spectrum = cpl_image_extract(spectra, 1, y, nx, y); 17087 } 17088 17089 17090 /* 17091 * Convert standard star spectrum in electrons per second per Angstrom. 17092 */ 17093 17094 cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion); 17095 17096 17097 /* 17098 * Map the atmospheric extinction factors to the same lambda sampling 17099 * of the extracted spectrum. 17100 */ 17101 17102 extinction = cpl_image_duplicate(spectrum); 17103 map_table(extinction, startwave + dispersion/2, dispersion, 17104 ext_table, "WAVE", "EXTINCTION"); 17105 17106 17107 /* 17108 * Convert from magnitudes to actual flux loss. 17109 */ 17110 17111 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17112 cpl_image_exponential(extinction, 10.); 17113 17114 17115 /* 17116 * Correct the scientific spectrum to airmass 0 17117 */ 17118 17119 cpl_image_multiply(spectrum, extinction); 17120 17121 17122 /* 17123 * Find in what pixel interval (start at "ext_pos", for "ext_count" 17124 * pixels) the atmospheric extinction is available. 17125 */ 17126 17127 ext_data = cpl_image_get_data_float(extinction); 17128 17129 ext_count = 0; 17130 ext_pos = 0; 17131 for (i = 0; i < nx; i++) { 17132 if (ext_data[i] > 0.0) { 17133 if (ext_count == 0) { 17134 ext_pos = i; 17135 } 17136 ext_count++; 17137 } 17138 else { 17139 if (ext_count) { 17140 break; 17141 } 17142 } 17143 } 17144 17145 cpl_image_delete(extinction); extinction = NULL; 17146 17147 17148 /* 17149 * Map the standard star catalog flux to the same lambda sampling 17150 * of the extracted spectrum. 17151 */ 17152 17153 flux = cpl_image_duplicate(spectrum); 17154 map_table(flux, startwave + dispersion/2, dispersion, 17155 flux_table, "WAVE", "FLUX"); 17156 17157 17158 /* 17159 * Find in what pixel interval (start at "flux_pos", for "flux_count" 17160 * pixels) the standard star flux is available. 17161 */ 17162 17163 flux_data = cpl_image_get_data_float(flux); 17164 17165 flux_count = 0; 17166 flux_pos = 0; 17167 for (i = 0; i < nx; i++) { 17168 if (flux_data[i] > 0.0) { 17169 if (flux_count == 0) { 17170 flux_pos = i; 17171 } 17172 flux_count++; 17173 } 17174 else { 17175 if (flux_count) { 17176 break; 17177 } 17178 } 17179 } 17180 17181 17182 /* 17183 * Intersection with previous selection 17184 */ 17185 17186 start = ext_pos > flux_pos ? ext_pos : flux_pos; 17187 end = (ext_pos + ext_count) < (flux_pos + flux_count) ? 17188 (ext_pos + ext_count) : (flux_pos + flux_count); 17189 flux_pos = start; 17190 flux_count = end - start; 17191 17192 17193 /* 17194 * Convert the flux to photons (per second per Angstrom). 17195 * std_flux is in units of erg / cm^2 / s / Angstrom. This 17196 * must be multiplied by the efficient area of the telescope, 17197 * 5.18E+5 cm^2, and divided by hv (v = frequency). With 17198 * hc = 1.98E-8 erg*Angstrom one obtains the following: 17199 */ 17200 17201 physical = cpl_image_duplicate(spectrum); 17202 phys_data = cpl_image_get_data_float(physical); 17203 17204 for (i = 0; i < nx; i++) { 17205 lambda = startwave + dispersion * (i + 0.5); 17206 phys_data[i] = 0.0026 * lambda * flux_data[i]; 17207 } 17208 17209 efficiency = cpl_image_duplicate(spectrum); 17210 eff_data = cpl_image_get_data_float(efficiency); 17211 data = cpl_image_get_data_float(spectrum); 17212 17213 for (i = 0; i < nx; i++) { 17214 if (phys_data[i] > 0.0) 17215 eff_data[i] = data[i] / phys_data[i]; 17216 else 17217 eff_data[i] = 0.0; 17218 } 17219 17220 cpl_image_delete(physical); physical = NULL; 17221 17222 17223 /* 17224 * Find interval (longer than 300 pixels) where efficiency is 17225 * greater than 1% 17226 */ 17227 17228 eff_count = 0; 17229 eff_pos = 0; 17230 for (i = 0; i < nx; i++) { 17231 if (eff_data[i] > 0.01) { 17232 if (eff_count == 0) { 17233 eff_pos = i; 17234 } 17235 eff_count++; 17236 } 17237 else { 17238 if (eff_count > 300) { 17239 break; 17240 } 17241 } 17242 } 17243 17244 17245 /* 17246 * Intersection with previous selection 17247 */ 17248 17249 start = eff_pos > flux_pos ? eff_pos : flux_pos; 17250 end = (eff_pos + eff_count) < (flux_pos + flux_count) ? 17251 (eff_pos + eff_count) : (flux_pos + flux_count); 17252 eff_pos = start; 17253 eff_count = end - start; 17254 17255 if (eff_count < 1) { 17256 cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT, 17257 "No overlap between catalog and spectrum"); 17258 cleanup; 17259 return NULL; 17260 } 17261 17262 17263 /* 17264 * Extract only data to fit, i.e., where the efficiency is available. 17265 */ 17266 17267 image = cpl_image_extract(efficiency, eff_pos + 1, 1, 17268 eff_pos + eff_count, 1); 17269 17270 smo_image = polysmooth(image, order, 50); 17271 cpl_image_delete(image); 17272 17273 smo_efficiency = cpl_image_duplicate(efficiency); 17274 smo_eff_data = cpl_image_get_data_float(smo_efficiency); 17275 cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1); 17276 17277 cpl_image_delete(smo_image); 17278 17279 17280 /* 17281 * Compute instrument response as the ratio between the catalog 17282 * spectrum and the observed spectrum (converted in physical units). 17283 * The polynomial smoothing, however, is performed on the inverse 17284 * of this ration, for obvious reasons (i.e., no divergence at zero 17285 * efficiency). 17286 */ 17287 17288 response = cpl_image_duplicate(spectrum); 17289 res_data = cpl_image_get_data_float(response); 17290 17291 for (i = 0; i < nx; i++) { 17292 if (eff_data[i] > 0.01 && flux_data[i] > 0.0) 17293 res_data[i] = data[i] / flux_data[i]; 17294 else 17295 res_data[i] = 0.0; 17296 } 17297 17298 17299 /* 17300 * Extract only data to fit, i.e., where the response is available. 17301 */ 17302 17303 image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1); 17304 17305 smo_image = polysmooth(image, order, 50); 17306 cpl_image_delete(image); 17307 17308 smo_response = cpl_image_duplicate(response); 17309 smo_res_data = cpl_image_get_data_float(smo_response); 17310 cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1); 17311 17312 cpl_image_delete(smo_image); 17313 17314 for (i = 0; i < nx; i++) { 17315 if (eff_data[i] > 0.01) { 17316 res_data[i] = 1 / res_data[i]; 17317 smo_res_data[i] = 1 / smo_res_data[i]; 17318 } 17319 else { 17320 res_data[i] = 0.0; 17321 smo_res_data[i] = 0.0; 17322 } 17323 } 17324 17325 17326 /* 17327 * Assemble the product spectrophotometric table. 17328 */ 17329 17330 table = cpl_table_new(nx); 17331 17332 cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT); 17333 cpl_table_set_column_unit(table, "WAVE", "Angstrom"); 17334 17335 for (i = 0; i < nx; i++) 17336 cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5)); 17337 17338 cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT); 17339 cpl_table_set_column_unit(table, "STD_FLUX", 17340 "10^(-16) erg/(cm^2 s Angstrom)"); 17341 cpl_table_copy_data_float(table, "STD_FLUX", flux_data); 17342 cpl_image_delete(flux); flux = NULL; 17343 17344 cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT); 17345 cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)"); 17346 cpl_table_copy_data_float(table, "OBS_FLUX", data); 17347 cpl_image_delete(spectrum); spectrum = NULL; 17348 17349 cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT); 17350 cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon"); 17351 cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data); 17352 cpl_image_delete(efficiency); efficiency = NULL; 17353 17354 cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT); 17355 cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon"); 17356 cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data); 17357 cpl_image_delete(smo_efficiency); smo_efficiency = NULL; 17358 17359 cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT); 17360 cpl_table_set_column_unit(table, "RAW_RESPONSE", 17361 "10^(-16) erg/(cm^2 electron)"); 17362 cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data); 17363 cpl_image_delete(response); response = NULL; 17364 17365 cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT); 17366 cpl_table_set_column_unit(table, 17367 "RESPONSE", "10^(-16) erg/(cm^2 electron)"); 17368 cpl_table_copy_data_float(table, "RESPONSE", smo_res_data); 17369 cpl_image_delete(smo_response); smo_response = NULL; 17370 17371 cleanup; 17372 17373 return table; 17374 } 17375 17376 static double ksigma_vector(cpl_vector *values, 17377 double klow, double khigh, int kiter, int *good) 17378 { 17379 cpl_vector *accepted; 17380 double mean = 0.0; 17381 double sigma = 0.0; 17382 double *data = cpl_vector_get_data(values); 17383 int n = cpl_vector_get_size(values); 17384 int ngood = n; 17385 int count = 0; 17386 int i; 17387 17388 17389 /* 17390 * At first iteration the mean is taken as the median, and the 17391 * standard deviation relative to this value is computed. 17392 */ 17393 17394 mean = cpl_vector_get_median(values); 17395 17396 for (i = 0; i < n; i++) 17397 sigma += (mean - data[i]) * (mean - data[i]); 17398 17399 sigma = sqrt(sigma / (n - 1)); 17400 17401 while (kiter) { 17402 count = 0; 17403 for (i = 0; i < ngood; i++) { 17404 if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) { 17405 data[count] = data[i]; 17406 ++count; 17407 } 17408 } 17409 17410 if (count == 0) // This cannot happen at first iteration. 17411 break; // So we can break: we have already computed a mean. 17412 17413 /* 17414 * The mean must be computed even if no element was rejected 17415 * (count == ngood), because at first iteration median instead 17416 * of mean was computed. 17417 */ 17418 17419 accepted = cpl_vector_wrap(count, data); 17420 mean = cpl_vector_get_mean(accepted); 17421 if (count > 1) 17422 sigma = cpl_vector_get_stdev(accepted); 17423 cpl_vector_unwrap(accepted); 17424 17425 if (count == ngood || count == 1) 17426 break; 17427 17428 ngood = count; 17429 --kiter; 17430 } 17431 17432 if (good) 17433 *good = ngood; 17434 17435 return mean; 17436 } 17437 17438 17457 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 17458 double klow, double khigh, int kiter, 17459 cpl_image **good) 17460 { 17461 int ni, nx, ny, npix; 17462 cpl_image *out_ima; 17463 float *pout_ima; 17464 float *good_ima; 17465 cpl_image *image; 17466 float **data; 17467 cpl_vector *time_line; 17468 double *ptime_line; 17469 int ngood; 17470 int i, j; 17471 17472 17473 ni = cpl_imagelist_get_size(imlist); 17474 17475 image = cpl_imagelist_get(imlist, 0); 17476 nx = cpl_image_get_size_x(image); 17477 ny = cpl_image_get_size_y(image); 17478 npix = nx * ny; 17479 17480 out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 17481 pout_ima = cpl_image_get_data_float(out_ima); 17482 17483 if (good) { 17484 *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 17485 good_ima = cpl_image_get_data_float(*good); 17486 } 17487 17488 time_line = cpl_vector_new(ni); 17489 ptime_line = cpl_vector_get_data(time_line); 17490 17491 data = cpl_calloc(sizeof(float *), ni); 17492 17493 for (i = 0; i < ni; i++) { 17494 image = cpl_imagelist_get(imlist, i); 17495 data[i] = cpl_image_get_data_float(image); 17496 } 17497 17498 for (i = 0; i < npix; i++) { 17499 for (j = 0; j < ni; j++) { 17500 ptime_line[j] = data[j][i]; 17501 } 17502 pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood); 17503 if (good) { 17504 good_ima[i] = ngood; 17505 } 17506 } 17507 17508 cpl_free(data); 17509 cpl_vector_delete(time_line); 17510 17511 return out_ima; 17512 17513 } 17514 17515 17532 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response, 17533 cpl_table *ext_table, double startwave, 17534 double dispersion, double gain, 17535 double exptime, double airmass) 17536 { 17537 cpl_image *extinction; 17538 cpl_image *outspectra; 17539 cpl_image *mapresponse; 17540 float *res_data; 17541 float *out_data; 17542 float *ext_data; 17543 int tlength, xlength, ylength; 17544 int i, j, k; 17545 17546 17547 if (spectra == NULL || ext_table == NULL || response == NULL) { 17548 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17549 return NULL; 17550 } 17551 17552 res_data = cpl_table_get_data_float(response, "RESPONSE"); 17553 17554 if (res_data == NULL) { 17555 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17556 return NULL; 17557 } 17558 17559 tlength = cpl_table_get_nrow(response); 17560 xlength = cpl_image_get_size_x(spectra); 17561 ylength = cpl_image_get_size_y(spectra); 17562 17563 if (xlength != tlength) { 17564 mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17565 map_table(mapresponse, startwave + dispersion/2, dispersion, 17566 response, "WAVE", "RESPONSE"); 17567 res_data = cpl_image_get_data_float(mapresponse); 17568 } 17569 17570 /* 17571 * Map the atmospheric extinction factors to the same lambda sampling 17572 * of the extracted spectrum. 17573 */ 17574 17575 extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17576 map_table(extinction, startwave + dispersion/2, dispersion, 17577 ext_table, "WAVE", "EXTINCTION"); 17578 17579 17580 /* 17581 * Convert from magnitudes to actual flux loss. 17582 */ 17583 17584 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17585 cpl_image_exponential(extinction, 10.); 17586 17587 outspectra = cpl_image_duplicate(spectra); 17588 17589 ext_data = cpl_image_get_data_float(extinction); 17590 out_data = cpl_image_get_data_float(outspectra); 17591 17592 for (k = 0, i = 0; i < ylength; i++) { 17593 for (j = 0; j < xlength; j++, k++) { 17594 out_data[k] *= ext_data[j] * res_data[j]; 17595 } 17596 } 17597 17598 cpl_image_delete(extinction); 17599 if (xlength != tlength) { 17600 cpl_image_delete(mapresponse); 17601 } 17602 17603 cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion); 17604 17605 return outspectra; 17606 } 17607 17608 17625 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 17626 cpl_image *errors, 17627 cpl_table *response, 17628 cpl_table *ext_table, 17629 double startwave, 17630 double dispersion, double gain, 17631 double exptime, double airmass) 17632 { 17633 cpl_image *extinction; 17634 cpl_image *outerrors; 17635 cpl_image *mapresponse; 17636 cpl_image *maperror; 17637 float *err_data; 17638 float *out_data; 17639 float *ext_data; 17640 float *res_data; 17641 float *spe_data; 17642 int tlength, xlength, ylength; 17643 int i, j, k; 17644 17645 17646 if (errors == NULL || ext_table == NULL || response == NULL) { 17647 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17648 return NULL; 17649 } 17650 17651 if (!cpl_table_has_column(response, "ERROR")) { 17652 return mos_apply_photometry(errors, response, ext_table, startwave, 17653 dispersion, gain, exptime, airmass); 17654 } 17655 17656 res_data = cpl_table_get_data_float(response, "RESPONSE"); 17657 17658 if (res_data == NULL) { 17659 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17660 return NULL; 17661 } 17662 17663 err_data = cpl_table_get_data_float(response, "ERROR"); 17664 17665 if (err_data == NULL) { 17666 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17667 return NULL; 17668 } 17669 17670 tlength = cpl_table_get_nrow(response); 17671 xlength = cpl_image_get_size_x(errors); 17672 ylength = cpl_image_get_size_y(errors); 17673 17674 if (xlength != tlength) { 17675 mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17676 map_table(mapresponse, startwave + dispersion/2, dispersion, 17677 response, "WAVE", "RESPONSE"); 17678 res_data = cpl_image_get_data_float(mapresponse); 17679 17680 maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17681 map_table(maperror, startwave + dispersion/2, dispersion, 17682 response, "WAVE", "ERROR"); 17683 err_data = cpl_image_get_data_float(maperror); 17684 } 17685 17686 /* 17687 * Map the atmospheric extinction factors to the same lambda sampling 17688 * of the extracted spectrum. 17689 */ 17690 17691 extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17692 map_table(extinction, startwave + dispersion/2, dispersion, 17693 ext_table, "WAVE", "EXTINCTION"); 17694 17695 17696 /* 17697 * Convert from magnitudes to actual flux loss. 17698 */ 17699 17700 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17701 cpl_image_exponential(extinction, 10.); 17702 17703 outerrors = cpl_image_duplicate(errors); 17704 17705 ext_data = cpl_image_get_data_float(extinction); 17706 out_data = cpl_image_get_data_float(outerrors); 17707 spe_data = cpl_image_get_data_float(spectra); 17708 17709 for (k = 0, i = 0; i < ylength; i++) { 17710 for (j = 0; j < xlength; j++, k++) { 17711 out_data[k] = ext_data[j] * 17712 sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] + 17713 res_data[j] * res_data[j] * out_data[k] * out_data[k]); 17714 } 17715 } 17716 17717 cpl_image_delete(extinction); 17718 if (xlength != tlength) { 17719 cpl_image_delete(maperror); 17720 } 17721 17722 cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion); 17723 17724 return outerrors; 17725 } 17726 17727 17803 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error, 17804 cpl_image *u_image, cpl_image *u_error, 17805 double startwave, double dispersion, 17806 double band, cpl_table *pol_sta, 17807 double ra, double dec, char *filter, 17808 int *polarisation, 17809 double *p_offset, double *p_error, 17810 double *a_offset, double *a_error) 17811 { 17812 cpl_table *standard; 17813 cpl_image *q_noise; 17814 cpl_image *q_signal; 17815 cpl_image *u_noise; 17816 cpl_image *u_signal; 17817 cpl_image *noise; 17818 double *q_ndata; 17819 double *q_sdata; 17820 double *u_ndata; 17821 double *u_sdata; 17822 double arctol = 0.5; /* Arc tolerance in degrees */ 17823 double mindist; 17824 double cwave; 17825 double bwave[] = {3650., 4450., 5510., 6580., 8060}; 17826 char *bands = "UBVRI"; 17827 char p_label[] = {' ', 'p', '\0'}; 17828 char dp_label[] = {' ', 'd', 'p', '\0'}; 17829 char a_label[] = {' ', 'a', '\0'}; 17830 char da_label[] = {' ', 'd', 'a', '\0'}; 17831 int nbands = strlen(bands); 17832 int selected; 17833 int first, last, count, center; 17834 int nx; 17835 cpl_size col, row; 17836 int i, found, closest; 17837 int pband; 17838 int polarised; 17839 double q_obs; 17840 double q_err; 17841 double u_obs; 17842 double u_err; 17843 double p_obs; 17844 double p_err; 17845 double p_ref; 17846 double dp_ref; 17847 double a_obs; 17848 double a_err; 17849 double a_ref; 17850 double da_ref; 17851 17852 17853 *filter = '\0'; 17854 *polarisation = 0; 17855 *p_offset = 0.0; 17856 *p_error = 0.0; 17857 *a_offset = 0.0; 17858 *a_error = 0.0; 17859 17860 /* 17861 * Select reference standard star 17862 */ 17863 17864 cpl_table_select_all(pol_sta); 17865 cpl_table_and_selected_double(pol_sta, "RA", CPL_GREATER_THAN, ra-arctol); 17866 cpl_table_and_selected_double(pol_sta, "RA", CPL_LESS_THAN, ra+arctol); 17867 cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol); 17868 selected = 17869 cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN, dec+arctol); 17870 17871 if (selected == 0) { 17872 cpl_msg_warning(cpl_func, "No standard star found in FOV"); 17873 return 1; 17874 } 17875 17876 if (selected > 1) { 17877 cpl_msg_warning(cpl_func, 17878 "Ambiguity: %d standard stars found in FOV", selected); 17879 return 1; 17880 } 17881 17882 standard = cpl_table_extract_selected(pol_sta); 17883 17884 cpl_msg_info(cpl_func, "Standard star: %s", 17885 cpl_table_get_string(standard, "name", 0)); 17886 17887 /* 17888 * Check whether the star is polarised or not 17889 */ 17890 17891 polarised = cpl_table_get_int(standard, "polarised", 0, NULL); 17892 17893 cpl_msg_info(cpl_func, "This star is%sexpected to be polarised", 17894 polarised ? " " : " not "); 17895 17896 17897 /* 17898 * Determine the image row with the smallest median noise: this 17899 * row is assumed to refer to the standard star. 17900 * (note: the higher the S/N ratio of the original spectra, the 17901 * smaller the noise of the Stokes parameters Q and U). 17902 */ 17903 17904 nx = cpl_image_get_size_x(q_error); 17905 17906 noise = cpl_image_collapse_median_create(q_error, 1, 0, 0); 17907 cpl_image_get_minpos(noise, &col, &row); 17908 17909 cpl_image_delete(noise); 17910 17911 if (col != 1) { 17912 cpl_table_delete(standard); 17913 cpl_msg_error(cpl_func, 17914 "Assertion failure!!! col = %d (it should be 1)", col); 17915 return 1; 17916 } 17917 17918 q_signal = cpl_image_extract(q_image, 1, row, nx, row); 17919 q_noise = cpl_image_extract(q_error, 1, row, nx, row); 17920 u_signal = cpl_image_extract(u_image, 1, row, nx, row); 17921 u_noise = cpl_image_extract(u_error, 1, row, nx, row); 17922 17923 q_sdata = cpl_image_get_data_double(q_signal); 17924 q_ndata = cpl_image_get_data_double(q_noise); 17925 u_sdata = cpl_image_get_data_double(u_signal); 17926 u_ndata = cpl_image_get_data_double(u_noise); 17927 17928 17929 /* 17930 * Determine valid interval in input images (where error is positive). 17931 */ 17932 17933 first = -1; 17934 last = nx = cpl_image_get_size_x(q_signal); 17935 for (i = 0; i < nx; i++) { 17936 if (first < 0) { 17937 if (q_ndata[i] > 0.0) { 17938 first = i; 17939 } 17940 } 17941 else { 17942 if (q_ndata[i] <= 0.0) { 17943 last = i - 1; 17944 break; 17945 } 17946 } 17947 } 17948 17949 count = last - first + 1; 17950 17951 if (first < 0 || count < band) { 17952 cpl_table_delete(standard); 17953 cpl_image_delete(q_signal); 17954 cpl_image_delete(q_noise); 17955 cpl_image_delete(u_signal); 17956 cpl_image_delete(u_noise); 17957 cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count); 17958 return 1; 17959 } 17960 17961 center = (first + last) / 2; // Center of valid spectrum 17962 cwave = startwave + dispersion * center; // Corresponding wavelength 17963 17964 17965 /* 17966 * Find the band UBVRI closest to the central wavelength. 17967 */ 17968 17969 found = 0; 17970 for (i = 0; i < nbands; i++) { 17971 p_label[0] = bands[i]; 17972 if (cpl_table_is_valid(standard, p_label, 0)) { 17973 if (found == 0) { 17974 found = 1; 17975 mindist = fabs(bwave[i] - cwave); 17976 closest = i; 17977 } 17978 else if (mindist > fabs(bwave[i] - cwave)) { 17979 mindist = fabs(bwave[i] - cwave); 17980 closest = i; 17981 } 17982 } 17983 } 17984 17985 if (!found) { 17986 cpl_table_delete(standard); 17987 cpl_image_delete(q_signal); 17988 cpl_image_delete(q_noise); 17989 cpl_image_delete(u_signal); 17990 cpl_image_delete(u_noise); 17991 cpl_msg_warning(cpl_func, "No reference value available"); 17992 return 1; 17993 } 17994 17995 center = (bwave[closest] - startwave) / dispersion; // Center of band (pix) 17996 cwave = bwave[closest]; // Wavelength of band 17997 17998 17999 /* 18000 * Check that the integration interval is entirely contained 18001 * in the valid interval, or give it up. 18002 */ 18003 18004 pband = floor(band / dispersion); // Band width in pixels 18005 18006 if (center - pband/2 < first || center + pband/2 > last) { 18007 cpl_table_delete(standard); 18008 cpl_image_delete(q_signal); 18009 cpl_image_delete(q_noise); 18010 cpl_image_delete(u_signal); 18011 cpl_image_delete(u_noise); 18012 cpl_msg_warning(cpl_func, "No reference value available"); 18013 return 1; 18014 } 18015 18016 first = center - pband/2; 18017 last = center + pband/2; 18018 18019 /* 18020 * Collect reference values. Note that if angle info is not available, 18021 * angle stuff is set automaticaly to zero. 18022 */ 18023 18024 p_label[0] = bands[closest]; 18025 dp_label[0] = bands[closest]; 18026 a_label[0] = bands[closest]; 18027 da_label[0] = bands[closest]; 18028 18029 p_ref = cpl_table_get(standard, p_label, 0, NULL); 18030 dp_ref = cpl_table_get(standard, dp_label, 0, NULL); 18031 a_ref = cpl_table_get(standard, a_label, 0, NULL); 18032 da_ref = cpl_table_get(standard, da_label, 0, NULL); 18033 18034 cpl_msg_info(cpl_func, 18035 "The expected polarisation is %.2f +- %.2f %%", 18036 p_ref, dp_ref); 18037 18038 if (polarised) { 18039 cpl_msg_info(cpl_func, 18040 "The expected polarisation angle is %.2f +- %.2f degrees", 18041 a_ref, da_ref); 18042 } 18043 18044 /* 18045 * Find median signal and median error. 18046 */ 18047 18048 q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1); 18049 q_err = cpl_image_get_median_window(q_error, first, 1, last, 1); 18050 u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1); 18051 u_err = cpl_image_get_median_window(u_error, first, 1, last, 1); 18052 18053 /* 18054 * Measured linear polarisation and its error 18055 */ 18056 18057 p_obs = sqrt(q_obs * q_obs + u_obs * u_obs); 18058 p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err); 18059 18060 /* 18061 * Measured polarisation angle 18062 */ 18063 18064 a_obs = 0.0; 18065 if (polarised) { 18066 if (fabs(q_obs) < 0.00001) { 18067 if (u_obs > 0.0) { 18068 a_obs = 45.0; 18069 } 18070 else { 18071 a_obs = 135.0; 18072 } 18073 } 18074 else { 18075 a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI; 18076 if (q_obs > 0.0) { 18077 if (u_obs < 0.0) { 18078 a_obs += 180.; 18079 } 18080 } 18081 else { 18082 a_obs += 90.; 18083 } 18084 } 18085 } 18086 18087 /* 18088 * Error on polarisation angle 18089 */ 18090 18091 a_err = 0.0; 18092 if (polarised) { 18093 a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err) 18094 / (p_obs * p_obs) 18095 * 90 / CPL_MATH_PI; 18096 } 18097 18098 p_obs *= 100; 18099 p_err *= 100; 18100 cpl_msg_info(cpl_func, 18101 "The measured polarisation is %.2f +- %.2f %%", 18102 p_obs, p_err); 18103 18104 if (polarised) { 18105 cpl_msg_info(cpl_func, 18106 "The measured polarisation angle is %.2f +- %.2f degrees", 18107 a_obs, a_err); 18108 } 18109 18110 *filter = bands[closest]; 18111 *polarisation = polarised; 18112 18113 if (polarised) { 18114 *p_offset = (p_obs - p_ref) / p_ref; 18115 *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref; 18116 } 18117 else { 18118 *p_offset = p_obs - p_ref; 18119 *p_error = sqrt(p_err * p_err + dp_ref * dp_ref); 18120 } 18121 18122 *a_offset = a_obs - a_ref; 18123 *a_error = sqrt(a_err*a_err + da_ref*da_ref); 18124 18125 return 0; 18126 18127 } 18128 18129 18161 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset) 18162 { 18163 cpl_array *offsets; 18164 int noffset; 18165 int nslits = cpl_table_get_nrow(reference); 18166 int *nref; 18167 int *nobj; 18168 int corr, maxcorr; 18169 int shift, best_shift; 18170 int i, j, k; 18171 18172 cpl_error_code status = CPL_ERROR_NONE; 18173 18174 18175 *offset = 0.0; 18176 18177 if (nslits != cpl_table_get_nrow(objects)) 18178 return CPL_ERROR_INCOMPATIBLE_INPUT; 18179 18180 nref = fors_get_nobjs_perslit(reference); 18181 nobj = fors_get_nobjs_perslit(objects); 18182 18183 noffset = 0; 18184 for (i = 0; i < nslits; i++) 18185 noffset += nobj[i]; 18186 18187 if (noffset == 0) { 18188 cpl_free(nref); 18189 cpl_free(nobj); 18190 return CPL_ERROR_DATA_NOT_FOUND; 18191 } 18192 18193 noffset = 0; 18194 for (i = 0; i < nslits; i++) 18195 noffset += nref[i]; 18196 18197 if (noffset == 0) { 18198 cpl_free(nref); 18199 cpl_free(nobj); 18200 return CPL_ERROR_DATA_NOT_FOUND; 18201 } 18202 18203 offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE); 18204 18205 noffset = 0; // The real number of offsets found will be counted. 18206 18207 for (i = 0; i < nslits; i++) { 18208 if (nref[i] > 0 && nobj[i] > 0) { 18209 double shift; 18210 int length = cpl_table_get_int(objects, "length", i, NULL); 18211 double ytop = cpl_table_get_double(objects, "xtop", i, NULL); 18212 double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL); 18213 int *aref = cpl_calloc(length, sizeof(int)); 18214 int *aobj = cpl_calloc(length, sizeof(int)); 18215 float *pref = cpl_calloc(nref[i], sizeof(float)); 18216 float *pobj = cpl_calloc(nobj[i], sizeof(float)); 18217 18218 for (j = 0; j < nref[i]; j++) { 18219 pref[j] = fors_get_object_position(reference, i, j + 1); 18220 aref[(int)pref[j]] = 1; 18221 } 18222 18223 for (j = 0; j < nobj[i]; j++) { 18224 pobj[j] = fors_get_object_position(objects, i, j + 1); 18225 aobj[(int)pobj[j]] = 1; 18226 } 18227 18228 /* 18229 * Do not consider objects at border 18230 */ 18231 18232 aref[0] = 0; 18233 aref[length - 1] = 0; 18234 aobj[0] = 0; 18235 aobj[length - 1] = 0; 18236 18237 //for (j = 0; j < nref[i]; j++) 18238 //printf("references: %f, ", pref[j]); 18239 //printf("\n"); 18240 //for (j = 0; j < nref[i]; j++) 18241 //printf("objects : %f, ", pobj[j]); 18242 //printf("\n"); 18243 //for (j = 0; j < length; j++) 18244 //printf("%d", aref[j]); 18245 //printf("\n"); 18246 //for (j = 0; j < length; j++) 18247 //printf("%d", aobj[j]); 18248 //printf("\n"); 18249 18250 /* 18251 * Object matching by correlation 18252 */ 18253 18254 maxcorr = 0; 18255 best_shift = length; 18256 18257 for (shift = length/2, j = 0; j <= length; shift--, j++) { 18258 int rstart, ostart, count; 18259 18260 if (shift > 0) { 18261 rstart = shift; 18262 ostart = 0; 18263 count = length - shift; 18264 } 18265 else { 18266 rstart = 0; 18267 ostart = -shift; 18268 count = length + shift; 18269 } 18270 18271 corr = 0; 18272 for (k = 0; k < count; k++) { 18273 corr += aref[rstart + k] * aobj[ostart + k]; 18274 } 18275 18276 if (maxcorr < corr) { 18277 maxcorr = corr; 18278 best_shift = shift; 18279 } 18280 } 18281 18282 if (best_shift == length) { // No shift found 18283 //printf("%d: No shift found\n", i); 18284 cpl_free(aref); 18285 cpl_free(aobj); 18286 cpl_free(pref); 18287 cpl_free(pobj); 18288 continue; 18289 } 18290 //printf("%d: Integer shift found = %d\n", i, best_shift); 18291 18292 for (j = 0; j < nref[i]; j++) { 18293 for (k = 0; k < nobj[i]; k++) { 18294 if (fabs(pref[j] - pobj[k] - best_shift) < 2) { 18295 double ccd_offset = (pref[j] - pobj[k]) 18296 * (ytop - ybottom) 18297 / length; 18298 18299 //printf("%d: Match found: %f\n", i, ccd_offset); 18300 /* 18301 * The matching object is found, store the 18302 * corresponding offset 18303 */ 18304 18305 cpl_array_set(offsets, noffset, ccd_offset); 18306 noffset++; 18307 break; 18308 } 18309 } 18310 } 18311 18312 cpl_free(aref); 18313 cpl_free(aobj); 18314 cpl_free(pref); 18315 cpl_free(pobj); 18316 } 18317 //else 18318 //printf("%d: No object found\n", i); 18319 } 18320 18321 cpl_free(nref); 18322 cpl_free(nobj); 18323 18324 //printf("%d offsets found in total\n", noffset); 18325 if (noffset > 0) { 18326 if (noffset % 2) { 18327 *offset = cpl_array_get_median(offsets); 18328 } 18329 else { 18330 double *a = cpl_malloc(sizeof(double) * noffset); 18331 for (i = 0; i < noffset; i++) { 18332 a[i] = cpl_array_get_double(offsets, i, NULL); 18333 } 18334 *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) + 18335 fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0; 18336 cpl_free(a); 18337 } 18338 } 18339 else 18340 status = CPL_ERROR_DATA_NOT_FOUND; 18341 //printf("Median offset: %f\n", *offset); 18342 18343 cpl_array_delete(offsets); 18344 18345 return status; 18346 18347 } 18348 18349 18361 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy) 18362 { 18363 cpl_image *source; 18364 int nx = cpl_image_get_size_x(image); 18365 int ny = cpl_image_get_size_y(image); 18366 float *idata; 18367 float *sdata; 18368 int i, j, pos; 18369 double xpos, ypos, xfrac, yfrac; 18370 int xint, yint; 18371 18372 18373 if (fabs(dx) >= nx || fabs(dy) >= ny) 18374 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18375 18376 source = cpl_image_duplicate(image); 18377 idata = cpl_image_get_data_float(image); 18378 sdata = cpl_image_get_data_float(source); 18379 18380 /* 18381 * Shift in y 18382 */ 18383 18384 yfrac = - dy - floor(- dy); 18385 xfrac = - dx - floor(- dx); 18386 18387 for (pos = 0, j = 0; j < ny; j++) { 18388 ypos = j - dy; 18389 yint = floor(ypos); 18390 for (i = 0; i < nx; i++) { 18391 xpos = i - dx; 18392 xint = floor(xpos); 18393 if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) { 18394 idata[pos] = 0.0; 18395 } 18396 else { 18397 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac) 18398 + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac) 18399 + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac 18400 + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac; 18401 } 18402 pos++; 18403 } 18404 } 18405 18406 cpl_image_delete(source); 18407 18408 return CPL_ERROR_NONE; 18409 } 18410 18422 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny) 18423 { 18424 #ifdef CPL_SIZE_FORMAT 18425 cpl_size row; 18426 #else 18427 int row; 18428 #endif 18429 18430 cpl_table_duplicate_column(slits, "x", slits, "xtop"); 18431 cpl_table_add_columns(slits, "x", "xbottom"); 18432 cpl_table_divide_scalar(slits, "x", 2); // Mean x position 18433 cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center 18434 cpl_table_multiply_columns(slits, "x", "x"); // Squared 18435 18436 cpl_table_duplicate_column(slits, "y", slits, "ytop"); 18437 cpl_table_add_columns(slits, "y", "ybottom"); 18438 cpl_table_divide_scalar(slits, "y", 2); // Mean y position 18439 cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center 18440 cpl_table_multiply_columns(slits, "y", "y"); // Squared 18441 18442 cpl_table_add_columns(slits, "x", "y"); // Distance from center 18443 cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center 18444 18445 cpl_table_erase_column(slits, "x"); 18446 cpl_table_erase_column(slits, "y"); 18447 18448 return row; 18449 } 18450 18470 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 18471 double xwidth, double ywidth, 18472 int dx, double gain, double *o_flux, double *o_err) 18473 { 18474 int nx = cpl_image_get_size_x(image); 18475 int ny = cpl_image_get_size_y(image); 18476 int slit = mos_slit_closest_to_center(slits, nx, ny); 18477 int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL); 18478 int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL); 18479 int dy = ytop - ybottom; 18480 int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) + 18481 cpl_table_get(slits, "xbottom", slit, NULL)) / 2); 18482 int xleft = xcenter - dx; 18483 int xright = xcenter + dx + 1; 18484 double area = xwidth * ywidth; // squared mm 18485 int npix = (2*dx + 1) * dy; 18486 int count = 0; 18487 float *data = cpl_image_get_data_float(image); 18488 double flux = 0.0; 18489 double error = 0.0; 18490 int satur = 60000; 18491 int x, y; 18492 18493 18494 if (cpl_table_has_column(slits, "ywidth")) { 18495 area = cpl_table_get(slits, "xwidth", slit, NULL) 18496 * cpl_table_get(slits, "ywidth", slit, NULL); 18497 } 18498 18499 *o_flux = 0.0; 18500 *o_err = 0.0; 18501 18502 if (xleft < 0) 18503 xleft = 0; 18504 18505 if (xleft > nx) 18506 xleft = nx; 18507 18508 if (xright < 0) 18509 xright = 0; 18510 18511 if (xright > nx) 18512 xright = nx; 18513 18514 if (ytop < 0) 18515 ytop = 0; 18516 18517 if (ytop > ny) 18518 ytop = ny; 18519 18520 if (ybottom < 0) 18521 ybottom = 0; 18522 18523 if (ybottom > ny) 18524 ybottom = ny; 18525 18526 count = (xright - xleft) * (ytop - ybottom); 18527 18528 if (count == 0) 18529 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18530 18531 count = 0; 18532 18533 for (y = ybottom; y < ytop; y++) { 18534 for (x = xleft; x < xright; x++) { 18535 double value = data[x + y * nx]; 18536 if (value < satur) { 18537 flux += value; 18538 count++; 18539 } 18540 } 18541 } 18542 18543 if (count == 0) 18544 return CPL_ERROR_DIVISION_BY_ZERO; 18545 18546 error = sqrt(flux/gain); 18547 18548 /* 18549 * Flux correction for lost pixels 18550 */ 18551 18552 flux *= (float)npix / count; 18553 error *= (float)npix / count; 18554 18555 flux /= area; 18556 error /= area; 18557 18558 *o_flux = flux; 18559 *o_err = error; 18560 18561 return CPL_ERROR_NONE; 18562 } 18563 18564 18587 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits, 18588 double xwidth, double ywidth, 18589 double lambda, double startwave, 18590 double dispersion, int dx, double gain, 18591 double *o_flux, double *o_err) 18592 { 18593 int nx = cpl_image_get_size_x(image); 18594 int ny = cpl_image_get_size_y(image); 18595 int slit = mos_slit_closest_to_center(slits, nx, ny); 18596 int dy = (int)cpl_table_get(slits, "length", slit, NULL); 18597 int ybottom = (int)cpl_table_get(slits, "position", slit, NULL); 18598 int ytop = ybottom + dy; 18599 int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5); 18600 int xleft = xcenter - dx; 18601 int xright = xcenter + dx + 1; 18602 double area = xwidth * ywidth; 18603 int npix = (2*dx + 1) * dy; 18604 int count = 0; 18605 float *data = cpl_image_get_data_float(image); 18606 double flux = 0.0; 18607 double error = 0.0; 18608 int satur = 60000; 18609 int x, y; 18610 18611 18612 if (cpl_table_has_column(slits, "ywidth")) { 18613 area = cpl_table_get(slits, "xwidth", slit, NULL) 18614 * cpl_table_get(slits, "ywidth", slit, NULL); 18615 } 18616 18617 *o_flux = 0.0; 18618 *o_err = 0.0; 18619 18620 if (xleft < 0) 18621 xleft = 0; 18622 18623 if (xleft > nx) 18624 xleft = nx; 18625 18626 if (xright < 0) 18627 xright = 0; 18628 18629 if (xright > nx) 18630 xright = nx; 18631 18632 if (ytop < 0) 18633 ytop = 0; 18634 18635 if (ytop > ny) 18636 ytop = ny; 18637 18638 if (ybottom < 0) 18639 ybottom = 0; 18640 18641 if (ybottom > ny) 18642 ybottom = ny; 18643 18644 count = (xright - xleft) * (ytop - ybottom); 18645 18646 if (count == 0) 18647 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18648 18649 count = 0; 18650 18651 for (y = ybottom; y < ytop; y++) { 18652 for (x = xleft; x < xright; x++) { 18653 double value = data[x + y * nx]; 18654 if (value < satur) { 18655 flux += value; 18656 count++; 18657 } 18658 } 18659 } 18660 18661 if (count == 0) 18662 return CPL_ERROR_DIVISION_BY_ZERO; 18663 18664 error = sqrt(flux/gain); 18665 18666 /* 18667 * Flux correction for lost pixels 18668 */ 18669 18670 flux *= (float)npix / count; 18671 error *= (float)npix / count; 18672 18673 flux /= area; 18674 error /= area; 18675 18676 *o_flux = flux; 18677 *o_err = error; 18678 18679 return CPL_ERROR_NONE; 18680 18681 } 18682 18683 18697 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 18698 char *label, double *mvalue) 18699 { 18700 int position = cpl_table_get_int(slits, "position", slit, NULL); 18701 int length = cpl_table_get_int(slits, "length", slit, NULL); 18702 cpl_table *tmp = cpl_table_extract(table, position, length); 18703 18704 *mvalue = cpl_table_get_column_median(tmp, label); 18705 cpl_table_delete(tmp); 18706 18707 if (cpl_error_get_code() != CPL_ERROR_NONE) 18708 return 1; 18709 18710 return 0; 18711 } 18712 18713 18725 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny) 18726 { 18727 cpl_mask *kernel = cpl_mask_new(nx, ny); 18728 cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image), 18729 cpl_image_get_size_y(image), 18730 cpl_image_get_type(image)); 18731 18732 cpl_mask_not(kernel); 18733 cpl_image_filter_mask(filtered, image, kernel, 18734 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER); 18735 cpl_mask_delete(kernel); 18736 18737 return filtered; 18738 }