FORS Pipeline Reference Manual 4.9.20
|
00001 /* $Id: moses.c,v 1.100 2013/02/28 15:11:12 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: 2013/02/28 15:11:12 $ 00024 * $Revision: 1.100 $ 00025 * $Name: fors-4_9_20 $ 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 /* FIXME: This has to be removed!! */ 00051 00052 float cpl_tools_get_median_float(float *, cpl_size); 00053 00054 #define MAX_COLNAME (80) 00055 #define STRETCH_FACTOR (1.20) 00056 00057 // Related to mos_identify_peaks(), used in multiplex mode 00058 00059 static int mos_multiplex = -1; 00060 static int mos_region_size = 800; 00061 00062 static double default_lines_hi[] = { /* Default sky line catalog */ 00063 5577.338, /* for high res data */ 00064 5889.953, 00065 5895.923, 00066 5915.301, 00067 5932.862, 00068 5953.420, 00069 6257.961, 00070 6287.434, 00071 6300.304, 00072 6306.869, 00073 6363.780, 00074 6498.729, 00075 6533.044, 00076 6553.617, 00077 6841.945, 00078 6863.955, 00079 6870.994, 00080 6889.288, 00081 6900.833, 00082 6912.623, 00083 6923.220, 00084 6939.521, 00085 6969.930, 00086 7003.858, 00087 7244.907, 00088 7276.405, 00089 7284.439, 00090 7316.282, 00091 7329.148, 00092 7340.885, 00093 7358.659, 00094 7571.746, 00095 7750.640, 00096 7759.996, 00097 7794.112, 00098 7808.467, 00099 7821.503, 00100 7841.266, 00101 7913.708, 00102 7949.204, 00103 7964.650, 00104 7993.332, 00105 8014.059, 00106 8310.719, 00107 8344.602, 00108 8382.392, 00109 8399.170, 00110 8415.231, 00111 8430.174, 00112 8452.250, 00113 8493.389, 00114 8791.186, 00115 8827.096, 00116 8885.850, 00117 8903.114, 00118 8943.395, 00119 8988.366 00120 }; 00121 00122 static double default_lines_lo[] = { /* Default sky line catalog */ 00123 5577.338, /* for low res data */ 00124 6300.304, 00125 6863.955, 00126 7571.746, 00127 7964.650, 00128 7993.332 00129 }; 00130 00131 00141 /* 00142 * The following macros and function for finding the k-th smallest 00143 * value on a float array will be accessible from cpl_tools once 00144 * this module will be moved into the CPL. 00145 */ 00146 00147 /**** 00148 00149 #define PIX_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; } 00150 00151 static float kthSmallest(float a[], int n, int k) 00152 { 00153 register int i,j,l,m; 00154 register float x; 00155 00156 l = 0; 00157 m = n-1; 00158 while (l < m) { 00159 x = a[k]; 00160 i = l; 00161 j = m; 00162 do { 00163 while (a[i] < x) { 00164 i++; 00165 } 00166 while (x < a[j]) { 00167 j--; 00168 } 00169 if (i <= j) { 00170 PIX_SWAP(a[i],a[j]); 00171 i++; 00172 j--; 00173 } 00174 } while (i <= j); 00175 00176 if (j < k) { 00177 l = i; 00178 } 00179 if (k < i) { 00180 m = j; 00181 } 00182 00183 } 00184 return(a[k]); 00185 } 00186 00187 #define medianWirth(a, n) kthSmallest(a, n, (((n)&1) ? ((n)/2) : (((n)/2)-1))) 00188 00189 ****/ 00190 00191 /* 00192 * Return random number with gaussian distribution (mean = 0, variance = 1) 00193 * (Box-Mueller method). The mos_randg() argument is either true or false, 00194 * indicating whether to "seed" or not the sequence of generated random 00195 * numbers. The "seeding" is performed just at the first mos_randg(1) call, 00196 * and at further calls the input argument is ignored. This function 00197 * generates two random numbers at each call, returning the first one 00198 * at odd calls, and the second one at even calls. 00199 */ 00200 00201 static void mos_seed(void) 00202 { 00203 srand((unsigned int)time((time_t *)0)); 00204 } 00205 00206 static double mos_randg(int seme) 00207 { 00208 static int doit = 1; 00209 static int gotit = 1; 00210 double x1, x2, w, y1; 00211 static double y2; 00212 00213 if (gotit && seme) { 00214 mos_seed(); 00215 gotit = 0; 00216 } 00217 00218 if (doit) { 00219 doit = 0; 00220 do { 00221 x1 = 2.0 * (double)rand() / RAND_MAX - 1.0; 00222 x2 = 2.0 * (double)rand() / RAND_MAX - 1.0; 00223 w = x1 * x1 + x2 * x2; 00224 } while (w >= 1.0 || w == 0.0); 00225 00226 w = sqrt( (-2.0 * log(w)) / w); 00227 00228 y1 = x1 * w; 00229 y2 = x2 * w; 00230 return y1; 00231 } 00232 00233 doit = 1; 00234 return y2; 00235 } 00236 00237 /* 00238 * This function contained a dependency on the VIMOS library 00239 * (function medianPixelvalue()): it should be removed as soon as an 00240 * image median filtering with generic kernel will be implemented 00241 * in the CPL. Currently it has been solved by a direct call to 00242 * a cpl_tool function. 00243 */ 00244 00245 static cpl_image *mos_image_vertical_median_filter(cpl_image *ima_in, 00246 int filtsizey, int refrow, 00247 int above, int below, int step) 00248 { 00249 00250 const char *func = "mos_image_general_median_filter"; 00251 00252 cpl_image *filt_img = NULL; 00253 int col, row; 00254 float *buf = NULL; 00255 float *data; 00256 float *fdata; 00257 int upright_y, loleft_y; 00258 int j; 00259 int yIsEven = !(filtsizey - (filtsizey/2)*2); 00260 int f2y; 00261 int nx = cpl_image_get_size_x(ima_in); 00262 int ny = cpl_image_get_size_y(ima_in); 00263 int firstRow; 00264 00265 00266 if (yIsEven) filtsizey++; 00267 00268 if (ny <= filtsizey) { 00269 cpl_msg_error(func, 00270 "Median filter size: %d, image size: %d", filtsizey, ny); 00271 return NULL; 00272 } 00273 00274 f2y = filtsizey / 2; 00275 00276 filt_img = cpl_image_duplicate(ima_in); 00277 buf = cpl_malloc(filtsizey * sizeof(float)); 00278 data = cpl_image_get_data(ima_in); 00279 fdata = cpl_image_get_data(filt_img); 00280 00281 firstRow = refrow - step * (below / step); 00282 if (firstRow < f2y) 00283 firstRow += step; 00284 00285 for (col = 0; col < nx; col++) { 00286 for (row = firstRow; row < refrow + above; row += step) { 00287 if (row >= ny - f2y) 00288 break; 00289 loleft_y = row - f2y; 00290 upright_y = row + f2y + 1; 00291 for (j = loleft_y; j < upright_y; j++) 00292 buf[j - loleft_y] = data[col + j * nx]; 00293 00294 fdata[col + row * nx] = cpl_tools_get_median_float(buf, filtsizey); 00295 } 00296 } 00297 00298 cpl_free(buf); 00299 00300 return filt_img; 00301 00302 } 00303 00304 00305 /* 00306 * The following static function is used to find an accurate position 00307 * of a peak within a short interval (however at least 5 pixels long). 00308 * The basic idea is to find the baricenter of all the pixel values 00309 * that pass a threshold level between the median value and the maximum 00310 * value within the examined interval (in case such levels are equal, 00311 * the input is considered flat and no position is returned). At least 00312 * minPoints must pass this threshold, or no position is computed. To 00313 * evaluate the significance of the computed baricenter, the variance 00314 * of the contributing positions (relative to the found baricenter) is 00315 * also evaluated, and compared with the expected variance for a uniform 00316 * distribution of positions. If the observed variance is greater than 00317 * 80% of the variance of the uniform distribution, the found position 00318 * is rejected. 00319 */ 00320 00321 static int peakPosition(const float *data, int size, float *position, 00322 int minPoints) 00323 { 00324 int i; 00325 int count = 0; 00326 float *copy; 00327 float max, median, level, pos, variance, uniformVariance; 00328 double sum, weights; 00329 00330 00331 if (data == NULL) 00332 return 1; 00333 00334 if (size < 5) /* Hardcoded, I know... */ 00335 return 1; 00336 00337 00338 /* 00339 * Find median level 00340 */ 00341 00342 copy = (float *) cpl_malloc(size*sizeof(float)); 00343 for (i = 0; i < size; i++) 00344 copy[i] = data[i]; 00345 median = cpl_tools_get_median_float(copy, size); 00346 cpl_free(copy); 00347 00348 00349 /* 00350 * Find max 00351 */ 00352 00353 max = data[0]; 00354 for (i = 1; i < size; i++) 00355 if (data[i] > max) 00356 max = data[i]; 00357 00358 00359 /* 00360 * If the max equals the median we have a flat input, therefore 00361 * no peak is found. 00362 */ 00363 00364 if (max-median < 0.00001) 00365 return 1; 00366 00367 00368 /* 00369 * Discrimination level: only pixels with values above this 00370 * level are considered in baricenter calculation. 00371 */ 00372 00373 level = (max + median) / 2; 00374 00375 00376 /* 00377 * Of the values above this level compute the baricenter and 00378 * then the variance of the positions used. Note that the weights 00379 * are taken as the difference between the pixels values and 00380 * the median level (supposedly the background). 00381 */ 00382 00383 count = 0; 00384 for (i = 0, sum = 0., weights = 0.; i < size; i++) { 00385 if (data[i] > level) { 00386 count++; 00387 weights += (data[i] - median); 00388 sum += i * (data[i] - median); 00389 } 00390 } 00391 00392 00393 /* 00394 * If too few values are above threshold, refuse the position 00395 * as insignificant 00396 */ 00397 00398 if (count < minPoints) 00399 return 1; 00400 00401 pos = sum / weights; 00402 for (i = 0, sum = 0., weights = 0.; i < size; i++) { 00403 if (data[i] > level) { 00404 weights++; 00405 sum += (i - pos) * (i - pos); 00406 } 00407 } 00408 variance = sqrt(sum / weights); 00409 00410 00411 /* 00412 * The "uniform variance" is the variance that should be obtained 00413 * in the case of uniform distribution of the points positions in 00414 * the selected interval. If the real variance is comparable with 00415 * this value, the peak is considered not found. 00416 */ 00417 00418 uniformVariance = sqrt(size*size/3 - pos*size + pos*pos); 00419 00420 if (variance > 0.8 * uniformVariance) 00421 return 1; 00422 00423 *position = pos + 0.5; 00424 00425 return 0; 00426 } 00427 00428 00429 /* 00430 * The following static function determines the quantity dx to be 00431 * added to the position of the highest pixel of a fiber profile, 00432 * to get the true position of the profile maximum. All is needed 00433 * is the maximum observed value v2 in the profile, and the observed 00434 * values v1 and v3 of the previous and the next pixels in the profile. 00435 * 00436 * The following ratio is defined: 00437 * 00438 * R = 0.5 (v3 - v1) / (2*v2 - v3 - v1) 00439 * 00440 * This is a conventional ratio that wouldn't diverge for any set of 00441 * pixel values, and that would not depend on the presence of background 00442 * (with the assumption that the background level is the same for the 00443 * three pixels). R has also been chosen in such a way that its value 00444 * is already quite close to the real dx. It should be noted that the 00445 * following condition should be fulfilled: 00446 * 00447 * v1 <= v2 and v3 < v2 00448 * or 00449 * v1 < v2 and v3 <= v2 00450 * 00451 * This implies that dx varies between -0.5 and 0.5 pixels. In such 00452 * boundary cases, one has: 00453 * 00454 * v2 = v1 and R = dx = -0.5 00455 * v2 = v3 and R = dx = 0.5 00456 * 00457 * Another special case is when the observed pixel values are perfectly 00458 * symmetrical: 00459 * 00460 * v1 = v3 and R = dx = 0.0 00461 * 00462 * In all the intermediate cases the relation between R and dx depends 00463 * on the shape of the fiber profile, that has been determined elsewhere. 00464 * Using the accurate reconstruction of the fiber profile obtained by 00465 * the * functions ifuProfile() and rebinProfile(), it can be shown 00466 * that R differs from dx always less than 0.01 pixels. If the condition 00467 * 00468 * v1 <= v2 and v3 < v2 00469 * or 00470 * v1 < v2 and v3 <= v2 00471 * 00472 * is not fulfilled, then this function returns the value 2.0. 00473 */ 00474 00475 static double values_to_dx(double v1, double v2, double v3) 00476 { 00477 00478 static double epsilon = 0.00000001; 00479 double r = 2.0; 00480 00481 00482 if (v1 > v2 || v3 > v2) 00483 return r; 00484 00485 if (2 * v2 - v1 - v3 < epsilon) 00486 return r; 00487 00488 r = 0.5 * (v3 - v1) / (2 * v2 - v3 - v1); 00489 00490 return r; 00491 00492 } 00493 00494 00495 /* 00496 * The following static function passes a min filter of given box 00497 * size on the data buffer. The box size must be a positive odd integer. 00498 */ 00499 00500 static float *min_filter(float *buffer, int length, int size) 00501 { 00502 float *minf = cpl_calloc(length, sizeof(float)); 00503 float min; 00504 int start = size / 2; 00505 int end = length - size / 2; 00506 int i, j; 00507 00508 00509 for (i = start; i < end; i++) { 00510 min = buffer[i-start]; 00511 for (j = i - start + 1; j <= i + start; j++) 00512 if (min > buffer[j]) 00513 min = buffer[j]; 00514 minf[i] = min; 00515 } 00516 00517 for (i = 0; i < start; i++) 00518 minf[i] = minf[start]; 00519 00520 for (i = end; i < length; i++) 00521 minf[i] = minf[end-1]; 00522 00523 return minf; 00524 } 00525 00526 00527 /* 00528 * The following static function passes a max filter of given box 00529 * size on the data buffer. The box size must be a positive odd integer. 00530 */ 00531 00532 static float *max_filter(float *buffer, int length, int size) 00533 { 00534 float *maxf = cpl_calloc(length, sizeof(float)); 00535 float max; 00536 int start = size / 2; 00537 int end = length - size / 2; 00538 int i, j; 00539 00540 00541 for (i = start; i < end; i++) { 00542 max = buffer[i-start]; 00543 for (j = i - start + 1; j <= i + start; j++) 00544 if (max < buffer[j]) 00545 max = buffer[j]; 00546 maxf[i] = max; 00547 } 00548 00549 for (i = 0; i < start; i++) 00550 maxf[i] = maxf[start]; 00551 00552 for (i = end; i < length; i++) 00553 maxf[i] = maxf[end-1]; 00554 00555 return maxf; 00556 } 00557 00558 00559 /* 00560 * The following static function passes a running average of given box 00561 * size on the data buffer. The box size must be a positive odd integer. 00562 */ 00563 00564 static float *smo_filter(float *buffer, int length, int size) 00565 { 00566 float *smof = cpl_calloc(length, sizeof(float)); 00567 double sum; 00568 int start = size / 2; 00569 int end = length - size / 2; 00570 int i, j; 00571 00572 00573 for (i = start; i < end; i++) { 00574 sum = 0.0; 00575 for (j = i - start; j <= i + start; j++) 00576 sum += buffer[j]; 00577 smof[i] = sum / size; 00578 } 00579 00580 for (i = 0; i < start; i++) 00581 smof[i] = smof[start]; 00582 00583 for (i = end; i < length; i++) 00584 smof[i] = smof[end-1]; 00585 00586 return smof; 00587 } 00588 00589 /* 00590 * The following two static functions are used to read and write from the 00591 * global distortion table the different model components. Conventionally 00592 * the table consists of 6 columns and 10 rows. Each row is just ordered 00593 * storage for model coefficients, and these functions guarantee that the 00594 * coefficients are read in and written out correctly, independent on their 00595 * physical meaning. The first 6 table rows are a description of the IDS 00596 * coefficients, followed by a row containing only the used reference 00597 * wavelength. The remaining 3 are a description of the spectral curvature. 00598 * The first row is a description of coefficient c0, the second of coefficient 00599 * c1, etc., of the IDS. The 8th row is a description of coefficient c0, 00600 * the 9th of coefficient c1, etc., of the spectral curvature. All are 00601 * bivariate polynomialx on x,y mask coordinates. If the input table 00602 * to the write routine is NULL, it is allocated and initialised. Also 00603 * the input polynomial could be NULL, and nothing would be written to 00604 * the table. If both pointers are NULL the function is basically a 00605 * constructor of the global distortion table. 00606 */ 00607 00608 static cpl_polynomial *read_global_distortion(cpl_table *global, cpl_size row) 00609 { 00610 cpl_polynomial *poly = NULL; 00611 cpl_size p[2]; 00612 cpl_size degree = 2; 00613 int null; 00614 double coeff; 00615 00616 char name[MAX_COLNAME]; 00617 00618 00619 for (p[0] = 0; p[0] <= degree; p[0]++) { 00620 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00621 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00622 coeff = cpl_table_get_double(global, name, row, &null); 00623 if (null) 00624 continue; 00625 if (poly == NULL) 00626 poly = cpl_polynomial_new(2); 00627 cpl_polynomial_set_coeff(poly, p, coeff); 00628 } 00629 } 00630 00631 return poly; 00632 } 00633 00634 static cpl_table *write_global_distortion(cpl_table *global, int row, 00635 cpl_polynomial *poly) 00636 { 00637 cpl_table *table; 00638 cpl_size p[2]; 00639 cpl_size degree = 2; 00640 int nrow = 10; 00641 00642 char name[MAX_COLNAME]; 00643 00644 00645 if (global) { 00646 table = global; 00647 } 00648 else { 00649 table = cpl_table_new(nrow); 00650 for (p[0] = 0; p[0] <= degree; p[0]++) { 00651 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00652 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00653 cpl_table_new_column(table, name, CPL_TYPE_DOUBLE); 00654 } 00655 } 00656 } 00657 00658 if (poly) { 00659 for (p[0] = 0; p[0] <= degree; p[0]++) { 00660 for (p[1] = 0; p[1] <= degree - p[0]; p[1]++) { 00661 snprintf(name, MAX_COLNAME, "a%"CPL_SIZE_FORMAT"%"CPL_SIZE_FORMAT"", p[0], p[1]); 00662 cpl_table_set_double(table, name, row, 00663 cpl_polynomial_get_coeff(poly, p)); 00664 } 00665 } 00666 } 00667 00668 return table; 00669 } 00670 00671 00672 /* 00673 * The following static function is performing a robust linear fit 00674 * (drawn from the VIMOS library, and originally from ESO-Eclipse). 00675 * 00676 * ----> y = a + b * x 00677 * 00678 * This function return 0 on success. 00679 */ 00680 00681 #define SEGNO(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) 00682 static int robustLinearFit(cpl_bivector *list, double *a, double *b, 00683 double *abdev) 00684 { 00685 cpl_vector *vx; 00686 cpl_vector *vy; 00687 cpl_vector *va; 00688 00689 double aa, bb, bcomp, b1, b2, del, abdevt, f, f1, f2, sigb, temp, d, sum; 00690 double sx, sy, sxy, sxx, chisq; 00691 double *arr; 00692 double aa_ls, bb_ls; 00693 double *x; 00694 double *y; 00695 int np; 00696 int iter; 00697 int max_iterate = 30; 00698 int i; 00699 00700 00701 np = cpl_bivector_get_size(list); 00702 vx = cpl_bivector_get_x(list); 00703 vy = cpl_bivector_get_y(list); 00704 x = cpl_vector_get_data(vx); 00705 y = cpl_vector_get_data(vy); 00706 00707 sx = sy = sxx = sxy = 0.00; 00708 for (i = 0; i < np; i++) { 00709 sx += x[i]; 00710 sy += y[i]; 00711 sxy += x[i] * y[i]; 00712 sxx += x[i] * x[i]; 00713 } 00714 00715 del = np * sxx - sx * sx; 00716 aa_ls = aa = (sxx * sy - sx * sxy) / del; 00717 bb_ls = bb = (np * sxy - sx * sy) / del; 00718 00719 chisq = 0.00; 00720 for (i = 0; i < np; i++) { 00721 temp = y[i] - (aa+bb*x[i]); 00722 temp *= temp; 00723 chisq += temp; 00724 } 00725 00726 va = cpl_vector_new(np); 00727 arr = cpl_vector_get_data(va); 00728 sigb = sqrt(chisq/del); 00729 b1 = bb; 00730 00731 bcomp = b1; 00732 sum = 0.00; 00733 for (i = 0; i < np; i++) { 00734 arr[i] = y[i] - bcomp * x[i]; 00735 } 00736 aa = cpl_vector_get_median_const(va); 00737 abdevt = 0.0; 00738 for (i = 0; i < np; i++) { 00739 d = y[i] - (bcomp * x[i] + aa); 00740 abdevt += fabs(d); 00741 if (y[i] != 0.0) 00742 d /= fabs(y[i]); 00743 if (fabs(d) > 1e-7) 00744 sum += (d >= 0.0 ? x[i] : -x[i]); 00745 } 00746 f1 = sum; 00747 00748 b2 = bb + SEGNO(3.0 * sigb, f1); 00749 00750 bcomp = b2; 00751 sum = 0.00; 00752 for (i = 0; i < np; i++) { 00753 arr[i] = y[i] - bcomp * x[i]; 00754 } 00755 aa = cpl_vector_get_median_const(va); 00756 abdevt = 0.0; 00757 for (i = 0; i < np; i++) { 00758 d = y[i] - (bcomp * x[i] + aa); 00759 abdevt += fabs(d); 00760 if (y[i] != 0.0) 00761 d /= fabs(y[i]); 00762 if (fabs(d) > 1e-7) 00763 sum += (d >= 0.0 ? x[i] : -x[i]); 00764 } 00765 f2 = sum; 00766 00767 if (fabs(b2-b1)<1e-7) { 00768 *a = aa; 00769 *b = bb; 00770 *abdev = abdevt / (double)np; 00771 cpl_vector_delete(va); 00772 return 0; 00773 } 00774 00775 iter = 0; 00776 while (f1*f2 > 0.0) { 00777 bb = 2.0*b2-b1; 00778 b1 = b2; 00779 f1 = f2; 00780 b2 = bb; 00781 00782 bcomp = b2; 00783 sum = 0.00; 00784 for (i = 0; i < np; i++) { 00785 arr[i] = y[i] - bcomp * x[i]; 00786 } 00787 aa = cpl_vector_get_median_const(va); 00788 abdevt = 0.0; 00789 for (i = 0; i < np; i++) { 00790 d = y[i] - (bcomp * x[i] + aa); 00791 abdevt += fabs(d); 00792 if (y[i] != 0.0) 00793 d /= fabs(y[i]); 00794 if (fabs(d) > 1e-7) 00795 sum += (d >= 0.0 ? x[i] : -x[i]); 00796 } 00797 f2 = sum; 00798 iter++; 00799 if (iter >= max_iterate) 00800 break; 00801 } 00802 if (iter >= max_iterate) { 00803 *a = aa_ls; 00804 *b = bb_ls; 00805 *abdev = -1.0; 00806 cpl_vector_delete(va); 00807 return 1; 00808 } 00809 00810 sigb = 0.01 * sigb; 00811 while (fabs(b2-b1) > sigb) { 00812 bb = 0.5 * (b1 + b2); 00813 if ((fabs(bb-b1) < 1e-7) || (fabs(bb-b2) < 1e-7)) 00814 break; 00815 bcomp = bb; 00816 sum = 0.0; 00817 for (i = 0; i < np; i++) { 00818 arr[i] = y[i] - bcomp * x[i]; 00819 } 00820 aa = cpl_vector_get_median_const(va); 00821 abdevt = 0.0; 00822 for (i = 0; i < np; i++) { 00823 d = y[i] - (bcomp * x[i] + aa); 00824 abdevt += fabs(d); 00825 if (y[i] != 0.0) 00826 d /= fabs(y[i]); 00827 if (fabs(d) > 1e-7) 00828 sum += (d >= 0.0 ? x[i] : -x[i]); 00829 } 00830 f = sum; 00831 00832 if (f*f1 >= 0.0) { 00833 f1=f; 00834 b1=bb; 00835 } 00836 else { 00837 f2=f; 00838 b2=bb; 00839 } 00840 } 00841 cpl_vector_delete(va); 00842 *a = aa; 00843 *b = bb; 00844 *abdev = abdevt / np; 00845 return 0; 00846 } 00847 #undef SEGNO 00848 00849 /* 00850 * The following static function applies the Hough transform from a table 00851 * of points to another table of points. Given the points p_i = (x_i,y_i) 00852 * and p_j = (x_j,y_j), the point (X,Y) with X = (y_i - y_j)/(x_i - x_j) 00853 * and Y = y_i - X*x_i is computed and added to the output table for each 00854 * p_i, p_j pair. This means that if the input table has N points, the 00855 * output table has N*(N-1)/2 points. 00856 */ 00857 00858 /* static */ 00859 cpl_table *mos_hough_table(cpl_table *table, const char *x, const char *y) 00860 { 00861 cpl_table *output; 00862 double *xdata; 00863 double *ydata; 00864 double *xodata; 00865 double *yodata; 00866 int npoints; 00867 int opoints; 00868 int i, j, k; 00869 00870 00871 npoints = cpl_table_get_nrow(table); 00872 opoints = npoints*(npoints-1)/2; 00873 00874 output = cpl_table_new(opoints); 00875 cpl_table_new_column(output, "m", CPL_TYPE_DOUBLE); 00876 cpl_table_new_column(output, "q", CPL_TYPE_DOUBLE); 00877 cpl_table_fill_column_window_double(output, "m", 0, opoints, 0.0); 00878 cpl_table_fill_column_window_double(output, "q", 0, opoints, 0.0); 00879 00880 xodata = cpl_table_get_data_double(output, "m"); 00881 yodata = cpl_table_get_data_double(output, "q"); 00882 00883 cpl_table_cast_column(table, x, "x", CPL_TYPE_DOUBLE); 00884 cpl_table_cast_column(table, y, "y", CPL_TYPE_DOUBLE); 00885 00886 xdata = cpl_table_get_data_double(table, "x"); 00887 ydata = cpl_table_get_data_double(table, "y"); 00888 00889 k = 0; 00890 for (i = 0; i < npoints; i++) { 00891 for (j = i+1; j < npoints; j++) { 00892 xodata[k] = (ydata[i]-ydata[j])/(xdata[i]-xdata[j]); 00893 yodata[k] = ydata[i] - xodata[k] * xdata[i]; 00894 k++; 00895 } 00896 } 00897 00898 if (k != opoints) 00899 printf("Assert k = %d, expected %d\n", k, opoints); 00900 00901 cpl_table_erase_column(table, "x"); 00902 cpl_table_erase_column(table, "y"); 00903 00904 return output; 00905 } 00906 00907 00908 /* 00909 * The following static function is performing the spectral 00910 * extraction for the function mos_extract_objects() 00911 */ 00912 00913 static void mos_extraction(cpl_image *sciwin, cpl_image *skywin, 00914 cpl_image *extracted, cpl_image *sky, 00915 cpl_image *error, int nobjects, int extraction, 00916 double ron, double conad, int ncomb) 00917 { 00918 00919 cpl_vector *vprofile; 00920 cpl_matrix *kernel; 00921 cpl_image *smowin; 00922 00923 int i, j; 00924 int specLen; 00925 int numRows; 00926 int index; 00927 int iter; 00928 int maxIter = 2; /* Not less than 2 !!! */ 00929 int smoothBox = 31; /* Not less than 5 !!! */ 00930 double nsigma = 5.0; 00931 00932 double sumWeight, sum, sumSky, sumProf, variance, weight; 00933 double *profile; 00934 double *buffer; 00935 float *edata; 00936 float *ekdata; 00937 float *endata; 00938 float *sdata; 00939 float *kdata; 00940 float *fdata; 00941 00942 double value; 00943 00944 00945 specLen = cpl_image_get_size_x(sciwin); 00946 numRows = cpl_image_get_size_y(sciwin); 00947 00948 edata = cpl_image_get_data(extracted); 00949 edata += nobjects * specLen; 00950 00951 ekdata = cpl_image_get_data(sky); 00952 ekdata += nobjects * specLen; 00953 00954 endata = cpl_image_get_data(error); 00955 endata += nobjects * specLen; 00956 00957 sdata = cpl_image_get_data(sciwin); 00958 kdata = cpl_image_get_data(skywin); 00959 00960 00961 /* 00962 * Initial spectrum estimate 00963 if (sdata[i + j * specLen] > 0.0) 00964 */ 00965 00966 if (extraction && numRows > 5) { 00967 smowin = mos_image_filter_median(sciwin, 3, 3); 00968 fdata = cpl_image_get_data(smowin); 00969 for (i = 0; i < specLen; i++) 00970 for (j = 0, edata[i] = 0.0; j < numRows; j++) 00971 edata[i] += fdata[i + j * specLen]; 00972 cpl_image_delete(smowin); 00973 } 00974 else { 00975 for (i = 0; i < specLen; i++) 00976 for (j = 0, edata[i] = 0.0; j < numRows; j++) 00977 edata[i] += sdata[i + j * specLen]; 00978 } 00979 00980 if (extraction) { 00981 00982 profile = cpl_calloc(specLen * numRows, sizeof(double)); 00983 buffer = cpl_calloc(specLen, sizeof(double)); 00984 00985 for (iter = 0; iter < maxIter; iter++) { 00986 00987 /* 00988 * Normalised spatial profile 00989 */ 00990 00991 for (i = 0; i < specLen; i++) { 00992 for (j = 0; j < numRows; j++) { 00993 index = i + j * specLen; 00994 /* if (sdata[index] > 0.0 && edata[i] > 0.00001) */ 00995 if (fabs(edata[i]) > 0.00001) 00996 profile[index] = sdata[index] / edata[i]; 00997 else 00998 profile[index] = 0.0; 00999 } 01000 } 01001 01002 for (j = 0; j < numRows; j++) { 01003 01004 /* 01005 * Smooth each row in the dispersion direction, and enforce positivity 01006 */ 01007 01008 for (i = 0; i < specLen - smoothBox; i++) { 01009 vprofile = cpl_vector_wrap(smoothBox, profile + i + j*specLen); 01010 value = cpl_vector_get_median_const(vprofile); 01011 cpl_vector_unwrap(vprofile); 01012 if (value < 0) 01013 value = 0.0; 01014 buffer[i + smoothBox / 2] = value; 01015 } 01016 01017 /* 01018 * Replace the end portions (i.e., not median filtered) with a mean 01019 */ 01020 01021 vprofile = cpl_vector_wrap(smoothBox / 2, profile + j*specLen); 01022 value = cpl_vector_get_mean(vprofile); 01023 cpl_vector_unwrap(vprofile); 01024 01025 if (value < 0) 01026 value = 0.0; 01027 01028 for (i = 0; i < smoothBox / 2; i++) 01029 buffer[i] = value; 01030 01031 vprofile = cpl_vector_wrap(smoothBox / 2, 01032 profile + specLen - smoothBox/2 + j*specLen); 01033 value = cpl_vector_get_mean(vprofile); 01034 cpl_vector_unwrap(vprofile); 01035 01036 if (value < 0) 01037 value = 0.0; 01038 01039 for (i = 0; i < smoothBox / 2; i++) 01040 buffer[i + specLen - smoothBox / 2] = value; 01041 01042 for (i = 0; i < specLen; i++) 01043 profile[i + j * specLen] = buffer[i]; 01044 01045 } 01046 01047 /* 01048 * Enforce normalization of spatial profile after smoothing 01049 */ 01050 01051 for (i = 0; i < specLen; i++) { 01052 for (j = 0, value = 0.0; j < numRows; j++) 01053 value += profile[i + j * specLen]; 01054 if (value > 0.00001) 01055 for (j = 0; j < numRows; j++) 01056 profile[i + j * specLen] /= value; 01057 else 01058 for (j = 0; j < numRows; j++) 01059 profile[i + j * specLen] = 0.0; 01060 } 01061 01062 01063 /* 01064 * Optimal extraction 01065 */ 01066 01067 for (i = 0; i < specLen; i++) { 01068 sum = 0.0; 01069 sumSky = 0.0; 01070 sumWeight = 0.0; 01071 sumProf = 0.0; 01072 for (j = 0; j < numRows; j++) { 01073 index = i + j * specLen; 01074 /* 01075 if (sdata[index] > 0.0) { 01076 */ 01077 variance = ron*ron + fabs(edata[i] * profile[index] + kdata[index]) 01078 / conad; 01079 variance /= ncomb; /* If input dataset is sum of ncomb images */ 01080 value = sdata[index] - edata[i] * profile[index]; 01081 if (fabs(value) / sqrt(variance) < nsigma) { 01082 weight = 1000000 * profile[index] / variance; 01083 sum += weight * sdata[index]; 01084 sumSky += weight * kdata[index]; 01085 sumWeight += weight * profile[index]; 01086 sumProf += profile[index]; 01087 } 01088 /* 01089 } 01090 */ 01091 } 01092 01093 if (sumWeight > 0.00001) { 01094 edata[i] = sum / sumWeight; 01095 ekdata[i] = sumSky / sumWeight; 01096 endata[i] = 1000 * sqrt(sumProf / sumWeight); 01097 } 01098 else { 01099 /* 01100 edata[i] = 0.0; 01101 ekdata[i] = 0.0; 01102 endata[i] = 0.0; 01103 */ 01104 endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad); 01105 } 01106 } 01107 } 01108 cpl_free(profile); 01109 cpl_free(buffer); 01110 } 01111 else { 01112 01113 /* 01114 * Add sky estimation for the simple aperture extraction. 01115 if (kdata[i + j * specLen] > 0.0) 01116 */ 01117 01118 for (i = 0; i < specLen; i++) 01119 for (j = 0, ekdata[i] = 0.0; j < numRows; j++) 01120 ekdata[i] += kdata[i + j * specLen]; 01121 01122 /* 01123 * Add error estimation for the simple aperture extraction. 01124 */ 01125 01126 for (i = 0; i < specLen; i++) 01127 endata[i] = sqrt(ron*ron + fabs(edata[i] + ekdata[i]) / conad); 01128 01129 } 01130 01131 } 01132 01133 01180 cpl_table *mos_global_distortion(cpl_table *slits, cpl_table *maskslits, 01181 cpl_table *ids, cpl_table *crv, 01182 double reference) 01183 { 01184 const char *func = "mos_global_distortion"; 01185 01186 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01187 01188 cpl_table *global = NULL; 01189 cpl_table *coeff; 01190 cpl_table *dummy; 01191 cpl_vector *ci; 01192 cpl_vector *xmask; 01193 cpl_vector *ymask; 01194 cpl_bivector *mask; 01195 cpl_vector *xccd; 01196 cpl_vector *yccd; 01197 cpl_bivector *ccd; 01198 cpl_polynomial *poly; 01199 double *xtop; 01200 double *ytop; 01201 double *xbottom; 01202 double *ybottom; 01203 double *mxtop; 01204 double *mytop; 01205 double *mxbottom; 01206 double *mybottom; 01207 int *position; 01208 int *length; 01209 int *slit_id; 01210 int *mslit_id; 01211 int nslits, nmaskslits, npoints; 01212 int order; 01213 int i, j; 01214 int minslit = 6; // 12; 01215 01216 01217 /* *+ 01218 printf("error1: %s\n", cpl_error_get_message()); 01219 +* */ 01220 if (slits == NULL || maskslits == NULL || ids == NULL || crv == NULL) { 01221 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01222 return NULL; 01223 } 01224 /* *+ 01225 printf("error1a: %s\n", cpl_error_get_message()); 01226 +* */ 01227 01228 nslits = cpl_table_get_nrow(slits); 01229 /* *+ 01230 printf("error1b: %s\n", cpl_error_get_message()); 01231 +* */ 01232 01233 if (nslits < minslit) { 01234 cpl_msg_warning(func, "Too few slits (%d < %d) for global " 01235 "distortion model determination", nslits, minslit); 01236 return NULL; 01237 } 01238 /* *+ 01239 printf("error1c: %s\n", cpl_error_get_message()); 01240 +* */ 01241 01242 nmaskslits = cpl_table_get_nrow(maskslits); 01243 01244 length = cpl_table_get_data_int(slits, "length"); 01245 position = cpl_table_get_data_int(slits, "position"); 01246 slit_id = cpl_table_get_data_int(slits, "slit_id"); 01247 mslit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01248 xtop = cpl_table_get_data_double(slits, "xtop"); 01249 ytop = cpl_table_get_data_double(slits, "ytop"); 01250 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01251 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01252 mxtop = cpl_table_get_data_double(maskslits, "xtop"); 01253 mytop = cpl_table_get_data_double(maskslits, "ytop"); 01254 mxbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01255 mybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01256 01257 01258 /* 01259 * Global IDS 01260 */ 01261 01262 coeff = cpl_table_new(nslits); 01263 cpl_table_copy_structure(coeff, ids); 01264 cpl_table_new_column(coeff, "xccd", CPL_TYPE_DOUBLE); 01265 cpl_table_new_column(coeff, "yccd", CPL_TYPE_DOUBLE); 01266 cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE); 01267 cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE); 01268 01269 /* *+ 01270 printf("error2: %s\n", cpl_error_get_message()); 01271 +* */ 01272 for (i = 0; i < nslits; i++) { 01273 for (j = 0; j < nmaskslits; j++) { 01274 if (slit_id[i] == mslit_id[j]) { 01275 cpl_table_set_double(coeff, "xmask", i, 01276 (mxtop[j] + mxbottom[j]) / 2); 01277 cpl_table_set_double(coeff, "ymask", i, 01278 (mytop[j] + mybottom[j]) / 2); 01279 } 01280 } 01281 } 01282 01283 if (cpl_table_has_invalid(coeff, "xmask")) { 01284 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 01285 cpl_table_delete(coeff); 01286 return NULL; 01287 } 01288 01289 for (i = 0; i < nslits; i++) { 01290 cpl_table_set_double(coeff, "xccd", i, (xtop[i] + xbottom[i]) / 2); 01291 cpl_table_set_double(coeff, "yccd", i, (ytop[i] + ybottom[i]) / 2); 01292 } 01293 01294 /* *+ 01295 printf("error3: %s\n", cpl_error_get_message()); 01296 +* */ 01297 for (i = 0; i < nslits; i++) { 01298 01299 if (length[i] == 0) 01300 continue; 01301 01302 cpl_table_and_selected_window(ids, position[i], length[i]); 01303 dummy = cpl_table_extract_selected(ids); 01304 for (j = 0; j < 6; j++) { 01305 if (cpl_table_has_column(dummy, clab[j])) { 01306 if (length[i] - cpl_table_count_invalid(dummy, clab[j]) > 10) { 01307 cpl_table_set_double(coeff, clab[j], i, 01308 cpl_table_get_column_median(dummy, clab[j])); 01309 } 01310 } 01311 } 01312 01313 cpl_table_delete(dummy); 01314 cpl_table_select_all(ids); 01315 01316 } 01317 01318 /* *+ 01319 printf("error4: %s\n", cpl_error_get_message()); 01320 +* */ 01321 for (j = 0; j < 6; j++) { 01322 if (cpl_table_has_column(coeff, clab[j])) { 01323 cpl_table_and_selected_invalid(coeff, clab[j]); 01324 01325 if (cpl_table_not_selected(coeff)) 01326 dummy = cpl_table_extract_selected(coeff); 01327 else 01328 break; 01329 01330 npoints = cpl_table_get_nrow(dummy); 01331 01332 if (npoints >= 6) { 01333 01334 if (npoints >= 12) 01335 order = 2; 01336 else 01337 order = 1; 01338 01339 ci = cpl_vector_wrap(npoints, 01340 cpl_table_get_data_double(dummy, clab[j])); 01341 if (j) { 01342 xccd = cpl_vector_wrap(npoints, 01343 cpl_table_get_data_double(dummy, "xccd")); 01344 yccd = cpl_vector_wrap(npoints, 01345 cpl_table_get_data_double(dummy, "yccd")); 01346 ccd = cpl_bivector_wrap_vectors(xccd, yccd); 01347 01348 /* %%% */ 01349 poly = cpl_polynomial_fit_2d_create(ccd, ci, order, NULL); 01350 01351 cpl_bivector_unwrap_vectors(ccd); 01352 cpl_vector_unwrap(xccd); 01353 cpl_vector_unwrap(yccd); 01354 cpl_vector_unwrap(ci); 01355 } 01356 else { 01357 xmask = cpl_vector_wrap(npoints, 01358 cpl_table_get_data_double(dummy, "xmask")); 01359 ymask = cpl_vector_wrap(npoints, 01360 cpl_table_get_data_double(dummy, "ymask")); 01361 mask = cpl_bivector_wrap_vectors(xmask, ymask); 01362 01363 /* %%% */ 01364 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL); 01365 01366 cpl_bivector_unwrap_vectors(mask); 01367 cpl_vector_unwrap(xmask); 01368 cpl_vector_unwrap(ymask); 01369 cpl_vector_unwrap(ci); 01370 } 01371 } 01372 else { 01373 cpl_size p[2] = {0, 0}; 01374 poly = cpl_polynomial_new(2); 01375 cpl_polynomial_set_coeff(poly, p, 01376 cpl_table_get_column_median(dummy, clab[j])); 01377 } 01378 01379 cpl_table_delete(dummy); 01380 01381 global = write_global_distortion(global, j, poly); 01382 01383 cpl_polynomial_delete(poly); 01384 01385 cpl_table_select_all(coeff); 01386 } 01387 } 01388 01389 /* *+ 01390 printf("error5: %s\n", cpl_error_get_message()); 01391 +* */ 01392 cpl_table_delete(coeff); 01393 /* *+ 01394 printf("error6: %s\n", cpl_error_get_message()); 01395 +* */ 01396 01397 01398 /* 01399 * Add model's reference wavelength 01400 */ 01401 01402 cpl_table_set_double(global, "a00", 6, reference); 01403 01404 01405 /* 01406 * Global curvature model 01407 */ 01408 01409 coeff = cpl_table_duplicate(crv); 01410 cpl_table_new_column(coeff, "xmask", CPL_TYPE_DOUBLE); 01411 cpl_table_new_column(coeff, "ymask", CPL_TYPE_DOUBLE); 01412 slit_id = cpl_table_get_data_int(coeff, "slit_id"); 01413 npoints = cpl_table_get_nrow(coeff); 01414 01415 /* *+ 01416 printf("error7: %s\n", cpl_error_get_message()); 01417 +* */ 01418 for (i = 0; i < npoints; i++) { 01419 for (j = 0; j < nmaskslits; j++) { 01420 if (slit_id[i] == mslit_id[j]) { 01421 if (i%2) { 01422 cpl_table_set_double(coeff, "xmask", i, mxbottom[j]); 01423 cpl_table_set_double(coeff, "ymask", i, mybottom[j]); 01424 } 01425 else { 01426 cpl_table_set_double(coeff, "xmask", i, mxtop[j]); 01427 cpl_table_set_double(coeff, "ymask", i, mytop[j]); 01428 } 01429 } 01430 } 01431 } 01432 01433 /* *+ 01434 printf("error8: %s\n", cpl_error_get_message()); 01435 +* */ 01436 if (cpl_table_has_invalid(coeff, "xmask")) { 01437 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 01438 cpl_table_delete(coeff); 01439 return NULL; 01440 } 01441 01442 /* *+ 01443 printf("error9: %s\n", cpl_error_get_message()); 01444 +* */ 01445 for (j = 0; j < 3; j++) { 01446 if (cpl_table_has_column(coeff, clab[j])) { 01447 cpl_table_and_selected_invalid(coeff, clab[j]); 01448 01449 if (cpl_table_not_selected(coeff)) 01450 dummy = cpl_table_extract_selected(coeff); 01451 else 01452 break; 01453 01454 npoints = cpl_table_get_nrow(dummy); 01455 01456 if (npoints >= 6) { 01457 01458 if (npoints >= 12) 01459 order = 2; 01460 else 01461 order = 1; 01462 01463 ci = cpl_vector_wrap(npoints, 01464 cpl_table_get_data_double(dummy, clab[j])); 01465 xmask = cpl_vector_wrap(npoints, 01466 cpl_table_get_data_double(dummy, "xmask")); 01467 ymask = cpl_vector_wrap(npoints, 01468 cpl_table_get_data_double(dummy, "ymask")); 01469 mask = cpl_bivector_wrap_vectors(xmask, ymask); 01470 01471 poly = cpl_polynomial_fit_2d_create(mask, ci, order, NULL); 01472 01473 cpl_bivector_unwrap_vectors(mask); 01474 cpl_vector_unwrap(ci); 01475 cpl_vector_unwrap(xmask); 01476 cpl_vector_unwrap(ymask); 01477 } 01478 else { 01479 cpl_size p[2] = {0, 0}; 01480 poly = cpl_polynomial_new(2); 01481 cpl_polynomial_set_coeff(poly, p, 01482 cpl_table_get_column_median(dummy, clab[j])); 01483 } 01484 01485 cpl_table_delete(dummy); 01486 01487 global = write_global_distortion(global, j + 7, poly); 01488 01489 cpl_polynomial_delete(poly); 01490 cpl_table_select_all(coeff); 01491 } 01492 } 01493 01494 /* *+ 01495 printf("error10: %s\n", cpl_error_get_message()); 01496 +* */ 01497 cpl_table_delete(coeff); 01498 /* *+ 01499 printf("error11: %s\n", cpl_error_get_message()); 01500 +* */ 01501 01502 return global; 01503 01504 } 01505 01506 01544 cpl_table *mos_build_slit_location(cpl_table *global, cpl_table *maskslits, 01545 int ysize) 01546 { 01547 const char *func = "mos_build_slit_location"; 01548 01549 cpl_propertylist *sort_col; 01550 cpl_polynomial *ids0; 01551 cpl_polynomial *crv[3]; 01552 cpl_polynomial *loc_crv; 01553 cpl_vector *point; 01554 cpl_table *slits; 01555 cpl_size nslits; 01556 int *slit_id; 01557 double *dpoint; 01558 double *xtop; 01559 double *ytop; 01560 double *xbottom; 01561 double *ybottom; 01562 double *mxtop; 01563 double *mytop; 01564 double *mxbottom; 01565 double *mybottom; 01566 cpl_size i; 01567 cpl_size j; 01568 01569 01570 if (global == NULL || maskslits == NULL) { 01571 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01572 return NULL; 01573 } 01574 01575 nslits = cpl_table_get_nrow(maskslits); 01576 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01577 mxtop = cpl_table_get_data_double(maskslits, "xtop"); 01578 mytop = cpl_table_get_data_double(maskslits, "ytop"); 01579 mxbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01580 mybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01581 01582 slits = cpl_table_duplicate(maskslits); 01583 01584 xtop = cpl_table_get_data_double(slits, "xtop"); 01585 ytop = cpl_table_get_data_double(slits, "ytop"); 01586 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01587 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01588 01589 ids0 = read_global_distortion(global, 0); 01590 crv[0] = read_global_distortion(global, 7); 01591 crv[1] = read_global_distortion(global, 8); 01592 crv[2] = read_global_distortion(global, 9); 01593 01594 loc_crv = cpl_polynomial_new(1); 01595 01596 point = cpl_vector_new(2); 01597 dpoint = cpl_vector_get_data(point); 01598 01599 for (i = 0; i < nslits; i++) { 01600 dpoint[0] = mxtop[i]; 01601 dpoint[1] = mytop[i]; 01602 01603 xtop[i] = cpl_polynomial_eval(ids0, point); 01604 01605 for (j = 0; j < 3; j++) 01606 if (crv[j]) 01607 cpl_polynomial_set_coeff(loc_crv, &j, 01608 cpl_polynomial_eval(crv[j], point)); 01609 01610 ytop[i] = cpl_polynomial_eval_1d(loc_crv, xtop[i], NULL); 01611 01612 dpoint[0] = mxbottom[i]; 01613 dpoint[1] = mybottom[i]; 01614 xbottom[i] = cpl_polynomial_eval(ids0, point); 01615 01616 for (j = 0; j < 3; j++) 01617 if (crv[j]) 01618 cpl_polynomial_set_coeff(loc_crv, &j, 01619 cpl_polynomial_eval(crv[j], point)); 01620 01621 ybottom[i] = cpl_polynomial_eval_1d(loc_crv, xbottom[i], NULL); 01622 } 01623 01624 cpl_vector_delete(point); 01625 cpl_polynomial_delete(ids0); 01626 cpl_polynomial_delete(loc_crv); 01627 for (j = 0; j < 3; j++) 01628 cpl_polynomial_delete(crv[j]); 01629 01630 sort_col = cpl_propertylist_new(); 01631 cpl_propertylist_append_bool(sort_col, "ytop", 1); 01632 cpl_table_sort(slits, sort_col); 01633 cpl_table_sort(maskslits, sort_col); 01634 cpl_propertylist_delete(sort_col); 01635 01636 /* 01637 * Eliminate slits which are _entirely_ outside the CCD 01638 */ 01639 01640 cpl_table_and_selected_double(slits, "ybottom", CPL_GREATER_THAN, ysize-1); 01641 cpl_table_or_selected_double(slits, "ytop", CPL_LESS_THAN, 0); 01642 cpl_table_erase_selected(slits); 01643 01644 nslits = cpl_table_get_nrow(slits); 01645 01646 if (nslits == 0) { 01647 cpl_msg_warning(func, "No slits found on the CCD"); 01648 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01649 cpl_table_delete(slits); 01650 return NULL; 01651 } 01652 01653 if (nslits > 1) 01654 cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slits are entirely or partially " 01655 "contained in CCD", nslits); 01656 else 01657 cpl_msg_info(func, "Slit location: %"CPL_SIZE_FORMAT" slit is entirely or partially " 01658 "contained in CCD", nslits); 01659 01660 return slits; 01661 01662 } 01663 01664 01691 cpl_table *mos_build_curv_coeff(cpl_table *global, cpl_table *maskslits, 01692 cpl_table *slits) 01693 { 01694 const char *func = "mos_build_curv_coeff"; 01695 01696 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01697 /* Max order is 5 */ 01698 01699 cpl_polynomial *crv[3]; 01700 cpl_vector *point; 01701 cpl_table *polytraces; 01702 double *dpoint; 01703 double *xtop; 01704 double *ytop; 01705 double *xbottom; 01706 double *ybottom; 01707 int *slit_id; 01708 int *valid_id; 01709 int nslits, nvalid; 01710 int found; 01711 int i, j, k; 01712 01713 01714 if (global == NULL || slits == NULL || maskslits == NULL) { 01715 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01716 return NULL; 01717 } 01718 01719 nslits = cpl_table_get_nrow(maskslits); 01720 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 01721 xtop = cpl_table_get_data_double(maskslits, "xtop"); 01722 ytop = cpl_table_get_data_double(maskslits, "ytop"); 01723 xbottom = cpl_table_get_data_double(maskslits, "xbottom"); 01724 ybottom = cpl_table_get_data_double(maskslits, "ybottom"); 01725 01726 polytraces = cpl_table_new(2*nslits); 01727 cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT); 01728 for (i = 0; i < 3; i++) 01729 cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE); 01730 01731 crv[0] = read_global_distortion(global, 7); 01732 crv[1] = read_global_distortion(global, 8); 01733 crv[2] = read_global_distortion(global, 9); 01734 01735 point = cpl_vector_new(2); 01736 dpoint = cpl_vector_get_data(point); 01737 01738 for (i = 0; i < nslits; i++) { 01739 for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */ 01740 01741 cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]); 01742 01743 if (j) { 01744 dpoint[0] = xbottom[i]; 01745 dpoint[1] = ybottom[i]; 01746 } 01747 else { 01748 dpoint[0] = xtop[i]; 01749 dpoint[1] = ytop[i]; 01750 } 01751 01752 for (k = 0; k < 3; k++) 01753 if (crv[j]) 01754 cpl_table_set_double(polytraces, clab[k], 2*i+j, 01755 cpl_polynomial_eval(crv[k], point)); 01756 } 01757 } 01758 01759 cpl_vector_delete(point); 01760 for (j = 0; j < 3; j++) 01761 cpl_polynomial_delete(crv[j]); 01762 01763 /* 01764 * Eliminate slits which are _entirely_ outside the CCD 01765 */ 01766 01767 nvalid = cpl_table_get_nrow(slits); 01768 valid_id = cpl_table_get_data_int(slits, "slit_id"); 01769 cpl_table_unselect_all(polytraces); 01770 for (i = 0; i < nslits; i++) { 01771 found = 0; 01772 for (j = 0; j < nvalid; j++) { 01773 if (slit_id[i] == valid_id[j]) { 01774 found = 1; 01775 break; 01776 } 01777 } 01778 if (!found) { 01779 cpl_table_select_row(polytraces, 2*i); 01780 cpl_table_select_row(polytraces, 2*i + 1); 01781 } 01782 } 01783 cpl_table_erase_selected(polytraces); 01784 01785 nslits = cpl_table_get_nrow(polytraces); 01786 01787 if (nslits == 0) { 01788 cpl_msg_warning(func, "No slits found on the CCD"); 01789 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01790 cpl_table_delete(polytraces); 01791 return NULL; 01792 } 01793 01794 if (nslits > 2) 01795 cpl_msg_info(func, "Curvature model: %d slits are entirely or " 01796 "partially contained in CCD", nslits / 2); 01797 else 01798 cpl_msg_info(func, "Curvature model: %d slit is entirely or " 01799 "partially contained in CCD", nslits / 2); 01800 01801 return polytraces; 01802 } 01803 01804 01846 cpl_table *mos_build_disp_coeff(cpl_table *global, cpl_table *slits) 01847 { 01848 const char *func = "mos_build_disp_coeff"; 01849 01850 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01851 01852 cpl_polynomial *ids[6]; 01853 cpl_vector *point; 01854 cpl_table *idscoeff; 01855 double *dpoint; 01856 double *xtop; 01857 double *ytop; 01858 double *xbottom; 01859 double *ybottom; 01860 int *position; 01861 int *length; 01862 int nslits; 01863 int nrows; 01864 int order; 01865 int ylow, yhig; 01866 int i, j, k; 01867 01868 01869 if (global == NULL || slits == NULL) { 01870 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 01871 return NULL; 01872 } 01873 01874 nslits = cpl_table_get_nrow(slits); 01875 position = cpl_table_get_data_int(slits, "position"); 01876 length = cpl_table_get_data_int(slits, "length"); 01877 xtop = cpl_table_get_data_double(slits, "xtop"); 01878 ytop = cpl_table_get_data_double(slits, "ytop"); 01879 xbottom = cpl_table_get_data_double(slits, "xbottom"); 01880 ybottom = cpl_table_get_data_double(slits, "ybottom"); 01881 01882 for (i = 0; i < 6; i++) 01883 ids[i] = read_global_distortion(global, i); 01884 01885 for (i = 0; i < 6; i++) 01886 if (ids[i] == NULL) 01887 break; 01888 01889 order = i - 1; 01890 01891 if (order < 1) { 01892 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 01893 return NULL; 01894 } 01895 01896 nrows = 0; 01897 for (i = 0; i < nslits; i++) 01898 nrows += length[i]; 01899 01900 idscoeff = cpl_table_new(nrows); 01901 01902 for (j = 0; j <= order; j++) 01903 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 01904 01905 cpl_table_new_column(idscoeff, "error", CPL_TYPE_DOUBLE); 01906 cpl_table_fill_column_window_double(idscoeff, "error", 0, nrows, 0.0); 01907 cpl_table_new_column(idscoeff, "nlines", CPL_TYPE_INT); 01908 cpl_table_fill_column_window_int(idscoeff, "nlines", 0, nrows, 0); 01909 01910 point = cpl_vector_new(2); 01911 dpoint = cpl_vector_get_data(point); 01912 01913 for (i = 0; i < nslits; i++) { 01914 01915 if (length[i] == 0) 01916 continue; 01917 01918 ylow = position[i]; 01919 yhig = ylow + length[i]; 01920 01921 for (j = 0; j <= order; j++) { 01922 if (j) { 01923 for (k = 0; k < length[i]; k++) { 01924 dpoint[0] = xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]; 01925 dpoint[1] = ybottom[i] + k*(ytop[i]-ybottom[i])/length[i]; 01926 cpl_table_set_double(idscoeff, clab[j], ylow + k, 01927 cpl_polynomial_eval(ids[j], point)); 01928 } 01929 } 01930 else { 01931 for (k = 0; k < length[i]; k++) { 01932 cpl_table_set_double(idscoeff, clab[0], ylow + k, 01933 xbottom[i] + k*(xtop[i]-xbottom[i])/length[i]); 01934 } 01935 } 01936 } 01937 } 01938 01939 cpl_vector_delete(point); 01940 for (j = 0; j < 6; j++) 01941 cpl_polynomial_delete(ids[j]); 01942 01943 return idscoeff; 01944 01945 } 01946 01947 01970 cpl_image *mos_subtract_sky(cpl_image *science, cpl_table *slits, 01971 cpl_table *polytraces, double reference, 01972 double blue, double red, double dispersion) 01973 { 01974 const char *func = "mos_subtract_sky"; 01975 01976 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 01977 /* Max order is 5 */ 01978 01979 cpl_image *sky; 01980 cpl_bivector *list; 01981 cpl_vector *listx; 01982 cpl_vector *listy; 01983 cpl_polynomial *polytop; 01984 cpl_polynomial *polybot; 01985 cpl_polynomial *trend; 01986 01987 int *slit_id; 01988 double *dlistx; 01989 double *dlisty; 01990 float *sdata; 01991 float *kdata; 01992 double top, bot; 01993 int itop, ibot; 01994 double coeff; 01995 double ytop, ybot; 01996 double m, q, err; 01997 int npix; 01998 01999 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 02000 int nx, ny; 02001 int nslits; 02002 int *length; 02003 int missing_top, missing_bot; 02004 int order; 02005 int null; 02006 int window = 50; /* Longer slits have polynomial sky model */ 02007 int count; 02008 int i, j; 02009 cpl_size k; 02010 02011 02012 if (science == NULL || slits == NULL || polytraces == NULL) { 02013 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02014 return NULL; 02015 } 02016 02017 if (dispersion <= 0.0) { 02018 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02019 return NULL; 02020 } 02021 02022 if (red - blue < dispersion) { 02023 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02024 return NULL; 02025 } 02026 02027 nx = cpl_image_get_size_x(science); 02028 ny = cpl_image_get_size_y(science); 02029 02030 sky = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 02031 02032 sdata = cpl_image_get_data(science); 02033 kdata = cpl_image_get_data(sky); 02034 02035 nslits = cpl_table_get_nrow(slits); 02036 order = cpl_table_get_ncol(polytraces) - 2; 02037 length = cpl_table_get_data_int(slits, "length"); 02038 slit_id = cpl_table_get_data_int(slits, "slit_id"); 02039 02040 /* 02041 * The spatial resampling is performed for a certain number of 02042 * pixels above and below the position of the reference wavelength: 02043 */ 02044 02045 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 02046 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 02047 02048 for (i = 0; i < nslits; i++) { 02049 02050 if (length[i] == 0) 02051 continue; 02052 02053 02054 /* 02055 * Recover from the table of spectral curvature coefficients 02056 * the curvature polynomials. 02057 */ 02058 02059 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 02060 02061 start_pixel = refpixel - pixel_below; 02062 if (start_pixel < 0) 02063 start_pixel = 0; 02064 02065 end_pixel = refpixel + pixel_above; 02066 if (end_pixel > nx) 02067 end_pixel = nx; 02068 02069 missing_top = 0; 02070 polytop = cpl_polynomial_new(1); 02071 for (k = 0; k <= order; k++) { 02072 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 02073 if (null) { 02074 cpl_polynomial_delete(polytop); 02075 missing_top = 1; 02076 break; 02077 } 02078 cpl_polynomial_set_coeff(polytop, &k, coeff); 02079 } 02080 02081 missing_bot = 0; 02082 polybot = cpl_polynomial_new(1); 02083 for (k = 0; k <= order; k++) { 02084 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 02085 if (null) { 02086 cpl_polynomial_delete(polybot); 02087 missing_bot = 1; 02088 break; 02089 } 02090 cpl_polynomial_set_coeff(polybot, &k, coeff); 02091 } 02092 02093 if (missing_top && missing_bot) { 02094 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 02095 slit_id[i]); 02096 continue; 02097 } 02098 02099 /* 02100 * In case just one of the two edges was not traced, the other 02101 * edge curvature model is duplicated and shifted to the other 02102 * end of the slit: better than nothing! 02103 */ 02104 02105 if (missing_top) { 02106 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 02107 "the spectral curvature of the lower edge " 02108 "is used instead.", slit_id[i]); 02109 polytop = cpl_polynomial_duplicate(polybot); 02110 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02111 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02112 k = 0; 02113 coeff = cpl_polynomial_get_coeff(polybot, &k); 02114 coeff += ytop - ybot; 02115 cpl_polynomial_set_coeff(polytop, &k, coeff); 02116 } 02117 02118 if (missing_bot) { 02119 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 02120 "the spectral curvature of the upper edge " 02121 "is used instead.", slit_id[i]); 02122 polybot = cpl_polynomial_duplicate(polytop); 02123 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02124 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02125 k = 0; 02126 coeff = cpl_polynomial_get_coeff(polytop, &k); 02127 coeff -= ytop - ybot; 02128 cpl_polynomial_set_coeff(polybot, &k, coeff); 02129 } 02130 02131 02132 /* 02133 * Now read pixel values along spatial direction, and fit them. 02134 */ 02135 02136 for (j = start_pixel; j < end_pixel; j++) { 02137 top = cpl_polynomial_eval_1d(polytop, j, NULL); 02138 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 02139 itop = floor(top + 0.5) + 1; 02140 ibot = floor(bot + 0.5); 02141 if (itop > ny) 02142 itop = ny; 02143 if (ibot < 0) 02144 ibot = 0; 02145 npix = itop - ibot; 02146 if (npix < 5) 02147 break; 02148 02149 list = cpl_bivector_new(npix); 02150 listx = cpl_bivector_get_x(list); 02151 listy = cpl_bivector_get_y(list); 02152 dlistx = cpl_vector_get_data(listx); 02153 dlisty = cpl_vector_get_data(listy); 02154 02155 for (k = 0; k < npix; k++) { 02156 dlistx[k] = k; 02157 dlisty[k] = sdata[j + (ibot + k)*nx]; 02158 } 02159 02160 if (robustLinearFit(list, &q, &m, &err)) { 02161 cpl_bivector_delete(list); 02162 continue; 02163 } 02164 02165 cpl_bivector_delete(list); 02166 02167 for (k = 0; k < npix; k++) { 02168 kdata[j + (ibot + k)*nx] = m*k + q; 02169 } 02170 02171 if (npix > window) { 02172 02173 /* 02174 * Polynomial iteration 02175 */ 02176 02177 err = 3*sqrt(err); 02178 02179 count = 0; 02180 for (k = 0; k < npix; k++) 02181 if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) 02182 count++; 02183 02184 if (count < 10) 02185 continue; 02186 02187 list = cpl_bivector_new(count); 02188 listx = cpl_bivector_get_x(list); 02189 listy = cpl_bivector_get_y(list); 02190 dlistx = cpl_vector_get_data(listx); 02191 dlisty = cpl_vector_get_data(listy); 02192 02193 count = 0; 02194 for (k = 0; k < npix; k++) { 02195 if (fabs(sdata[j + (ibot + k)*nx] - m*k - q) < err) { 02196 dlistx[count] = k; 02197 dlisty[count] = sdata[j + (ibot + k)*nx]; 02198 count++; 02199 } 02200 } 02201 02202 trend = cpl_polynomial_fit_1d_create(listx, listy, 2, &err); 02203 02204 cpl_bivector_delete(list); 02205 02206 err = 3*sqrt(err); 02207 02208 count = 0; 02209 for (k = 0; k < npix; k++) 02210 if (fabs(sdata[j + (ibot + k)*nx] 02211 - cpl_polynomial_eval_1d(trend, k, NULL)) < err) 02212 count++; 02213 02214 if (count < 10) { 02215 cpl_polynomial_delete(trend); 02216 continue; 02217 } 02218 02219 list = cpl_bivector_new(count); 02220 listx = cpl_bivector_get_x(list); 02221 listy = cpl_bivector_get_y(list); 02222 dlistx = cpl_vector_get_data(listx); 02223 dlisty = cpl_vector_get_data(listy); 02224 02225 count = 0; 02226 for (k = 0; k < npix; k++) { 02227 if (fabs(sdata[j + (ibot + k)*nx] 02228 - cpl_polynomial_eval_1d(trend, k, NULL)) < err) { 02229 dlistx[count] = k; 02230 dlisty[count] = sdata[j + (ibot + k)*nx]; 02231 count++; 02232 } 02233 } 02234 02235 cpl_polynomial_delete(trend); 02236 02237 trend = cpl_polynomial_fit_1d_create(listx, listy, 3, &err); 02238 02239 cpl_bivector_delete(list); 02240 02241 for (k = 0; k < npix; k++) { 02242 kdata[j + (ibot + k)*nx] = cpl_polynomial_eval_1d(trend, 02243 k, NULL); 02244 } 02245 02246 cpl_polynomial_delete(trend); 02247 } 02248 } 02249 cpl_polynomial_delete(polytop); 02250 cpl_polynomial_delete(polybot); 02251 } 02252 02253 cpl_image_subtract(science, sky); 02254 02255 return sky; 02256 } 02257 02258 02291 cpl_image *mos_normalise_flat(cpl_image *flat, cpl_image *spatial, 02292 cpl_table *slits, cpl_table *polytraces, 02293 double reference, double blue, double red, 02294 double dispersion, int sradius, int polyorder) 02295 { 02296 const char *func = "mos_normalise_flat"; 02297 02298 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 02299 /* Max order is 5 */ 02300 02301 cpl_image *rectified; 02302 cpl_image *smo_flat; 02303 cpl_image *exslit; 02304 cpl_vector *positions; 02305 cpl_vector *flux; 02306 cpl_vector *smo_flux; 02307 cpl_polynomial *trend; 02308 cpl_polynomial *polytop; 02309 cpl_polynomial *polybot; 02310 02311 int *slit_id; 02312 float *p; 02313 float *data; 02314 double *fdata; 02315 double *pdata; 02316 float *sdata; 02317 float *xdata; 02318 float *wdata; 02319 double vtop, vbot, value; 02320 double top, bot; 02321 double coeff; 02322 double ytop, ybot; 02323 double ypos; 02324 double fvalue; 02325 int ivalue; 02326 int yint, yprev; 02327 int npseudo; 02328 02329 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 02330 int nx, ny, nsubx, nsuby; 02331 int xlow, ylow, xhig, yhig; 02332 int nslits; 02333 int *position; 02334 int *length; 02335 int missing_top, missing_bot; 02336 int order; 02337 int npoints; 02338 int uradius; 02339 int null; 02340 int i, j; 02341 cpl_size k; 02342 02343 /* int exclude = 5; Number of excluded pixels at edges */ 02344 02345 /* For debug puposes only: cpl_image *smo_rectified; */ 02346 02347 02348 if (flat == NULL || slits == NULL || polytraces == NULL) { 02349 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02350 return NULL; 02351 } 02352 02353 if (dispersion <= 0.0) { 02354 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02355 return NULL; 02356 } 02357 02358 if (red - blue < dispersion) { 02359 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02360 return NULL; 02361 } 02362 02363 rectified = mos_spatial_calibration(flat, slits, polytraces, reference, 02364 blue, red, dispersion, 0, NULL); 02365 02366 nx = cpl_image_get_size_x(rectified); 02367 ny = cpl_image_get_size_y(rectified); 02368 02369 smo_flat = cpl_image_new(cpl_image_get_size_x(spatial), 02370 cpl_image_get_size_y(spatial), CPL_TYPE_FLOAT); 02371 wdata = cpl_image_get_data(smo_flat); 02372 02373 nslits = cpl_table_get_nrow(slits); 02374 order = cpl_table_get_ncol(polytraces) - 2; 02375 position = cpl_table_get_data_int(slits, "position"); 02376 length = cpl_table_get_data_int(slits, "length"); 02377 slit_id = cpl_table_get_data_int(slits, "slit_id"); 02378 02379 /* 02380 * The spatial resampling is performed for a certain number of 02381 * pixels above and below the position of the reference wavelength: 02382 */ 02383 02384 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 02385 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 02386 02387 xlow = 1; 02388 xhig = nx; 02389 for (i = 0; i < nslits; i++) { 02390 02391 if (length[i] == 0) 02392 continue; 02393 02394 /* 02395 * We DON'T write: 02396 * 02397 * ylow = position[i]; 02398 * yhig = ylow + length[i]; 02399 * 02400 * because the cpl_image pixels are counted from 1, and because in 02401 * cpl_image_extract() the coordinates of the last pixel are inclusive. 02402 */ 02403 02404 ylow = position[i] + 1; 02405 yhig = ylow + length[i] - 1; 02406 02407 exslit = cpl_image_extract(rectified, xlow, ylow, xhig, yhig); 02408 02409 if (polyorder < 0) { 02410 02411 cpl_image_turn(exslit, -1); /* For faster memory access */ 02412 02413 nsubx = cpl_image_get_size_x(exslit); 02414 nsuby = cpl_image_get_size_y(exslit); 02415 data = cpl_image_get_data(exslit); 02416 flux = cpl_vector_new(nsubx); 02417 02418 uradius = nsubx / 2; 02419 if (uradius > sradius) 02420 uradius = sradius; 02421 02422 for (j = 0; j < nsuby; j++) { 02423 fdata = cpl_vector_get_data(flux); 02424 p = data; 02425 for (k = 0; k < nsubx; k++) 02426 *fdata++ = *p++; 02427 smo_flux = cpl_vector_filter_median_create(flux, uradius); 02428 fdata = cpl_vector_get_data(smo_flux); 02429 p = data; 02430 for (k = 0; k < nsubx; k++) 02431 *p++ = *fdata++; 02432 cpl_vector_delete(smo_flux); 02433 data += nsubx; 02434 } 02435 02436 cpl_vector_delete(flux); 02437 02438 02439 /* 02440 * First fit fluxes along the spatial direction with a low-degree 02441 * polynomial (excluding the first and the last pixels, close to 02442 * the edges) 02443 */ 02444 /* 02445 if (nsubx-2*exclude > 10) { 02446 flux = cpl_vector_new(nsubx-2*exclude); 02447 fdata = cpl_vector_get_data(flux); 02448 positions = cpl_vector_new(nsubx-2*exclude); 02449 for (j = 0; j < nsubx-2*exclude; j++) 02450 cpl_vector_set(positions, j, j+exclude); 02451 02452 for (k = 0; k < nsuby; k++) { 02453 for (j = 0; j < nsubx-2*exclude; j++) 02454 fdata[j] = data[j+exclude]; 02455 trend = cpl_polynomial_fit_1d_create(positions, flux, 02456 1, NULL); 02457 for (j = 0; j < nsubx; j++) 02458 data[j] = cpl_polynomial_eval_1d(trend, j, NULL); 02459 cpl_polynomial_delete(trend); 02460 data += nsubx; 02461 } 02462 02463 cpl_vector_delete(flux); 02464 cpl_vector_delete(positions); 02465 } 02466 */ 02467 02468 /* 02469 * Now smooth along the dispersion direction 02470 */ 02471 02472 cpl_image_turn(exslit, 1); /* For faster memory access */ 02473 nsubx = cpl_image_get_size_x(exslit); 02474 nsuby = cpl_image_get_size_y(exslit); 02475 data = cpl_image_get_data(exslit); 02476 02477 for (j = 0; j < nsuby; j++) { 02478 flux = cpl_vector_new(nsubx); 02479 fdata = cpl_vector_get_data(flux); 02480 p = data; 02481 for (k = 0; k < nsubx; k++) 02482 *fdata++ = *p++; 02483 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02484 cpl_vector_delete(flux); 02485 fdata = cpl_vector_get_data(smo_flux); 02486 p = data; 02487 for (k = 0; k < nsubx; k++) 02488 *p++ = *fdata++; 02489 cpl_vector_delete(smo_flux); 02490 data += nsubx; 02491 } 02492 } 02493 else { 02494 02495 /* 02496 * Fit with a polynomial the flat field trend row by row. 02497 */ 02498 02499 nsubx = cpl_image_get_size_x(exslit); 02500 nsuby = cpl_image_get_size_y(exslit); 02501 data = cpl_image_get_data(exslit); 02502 02503 for (j = 0; j < nsuby; j++) { 02504 02505 /* 02506 * First get the size of the vectors to allocate: 02507 */ 02508 02509 npoints = 0; 02510 p = data + j*nsubx; 02511 for (k = 0; k < nsubx; k++) 02512 if (p[k] > 1.0) 02513 npoints++; 02514 02515 if (npoints > polyorder + 1) { 02516 02517 /* 02518 * Fill the vectors for the fitting 02519 */ 02520 02521 flux = cpl_vector_new(npoints); 02522 fdata = cpl_vector_get_data(flux); 02523 positions = cpl_vector_new(npoints); 02524 pdata = cpl_vector_get_data(positions); 02525 02526 npoints = 0; 02527 p = data + j*nsubx; 02528 for (k = 0; k < nsubx; k++) { 02529 if (p[k] > 1.0) { 02530 fdata[npoints] = p[k]; 02531 pdata[npoints] = k; 02532 npoints++; 02533 } 02534 } 02535 02536 trend = cpl_polynomial_fit_1d_create(positions, flux, 02537 polyorder, NULL); 02538 02539 cpl_vector_delete(flux); 02540 cpl_vector_delete(positions); 02541 02542 if (trend) { 02543 p = data + j*nsubx; 02544 for (k = 0; k < nsubx; k++) 02545 if (p[k] > 1.0) 02546 p[k] = cpl_polynomial_eval_1d(trend, k, NULL); 02547 cpl_polynomial_delete(trend); 02548 } 02549 else { 02550 cpl_msg_warning(func, "Invalid flat field flux fit " 02551 "(ignored)"); 02552 } 02553 } 02554 } 02555 } 02556 02557 02558 /* 02559 * Recover from the table of spectral curvature coefficients 02560 * the curvature polynomials. 02561 */ 02562 02563 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 02564 02565 start_pixel = refpixel - pixel_below; 02566 if (start_pixel < 0) 02567 start_pixel = 0; 02568 02569 end_pixel = refpixel + pixel_above; 02570 if (end_pixel > nx) 02571 end_pixel = nx; 02572 02573 missing_top = 0; 02574 polytop = cpl_polynomial_new(1); 02575 for (k = 0; k <= order; k++) { 02576 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 02577 if (null) { 02578 cpl_polynomial_delete(polytop); 02579 missing_top = 1; 02580 break; 02581 } 02582 cpl_polynomial_set_coeff(polytop, &k, coeff); 02583 } 02584 02585 missing_bot = 0; 02586 polybot = cpl_polynomial_new(1); 02587 for (k = 0; k <= order; k++) { 02588 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 02589 if (null) { 02590 cpl_polynomial_delete(polybot); 02591 missing_bot = 1; 02592 break; 02593 } 02594 cpl_polynomial_set_coeff(polybot, &k, coeff); 02595 } 02596 02597 if (missing_top && missing_bot) { 02598 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 02599 slit_id[i]); 02600 continue; 02601 } 02602 02603 /* 02604 * In case just one of the two edges was not traced, the other 02605 * edge curvature model is duplicated and shifted to the other 02606 * end of the slit: better than nothing! 02607 */ 02608 02609 if (missing_top) { 02610 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 02611 "the spectral curvature of the lower edge " 02612 "is used instead.", slit_id[i]); 02613 polytop = cpl_polynomial_duplicate(polybot); 02614 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02615 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02616 k = 0; 02617 coeff = cpl_polynomial_get_coeff(polybot, &k); 02618 coeff += ytop - ybot; 02619 cpl_polynomial_set_coeff(polytop, &k, coeff); 02620 } 02621 02622 if (missing_bot) { 02623 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 02624 "the spectral curvature of the upper edge " 02625 "is used instead.", slit_id[i]); 02626 polybot = cpl_polynomial_duplicate(polytop); 02627 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02628 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02629 k = 0; 02630 coeff = cpl_polynomial_get_coeff(polytop, &k); 02631 coeff -= ytop - ybot; 02632 cpl_polynomial_set_coeff(polybot, &k, coeff); 02633 } 02634 02635 02636 /* 02637 * Now map smoothed image to CCD. 02638 * Note that the npseudo value related to this slit is equal 02639 * to the number of spatial pseudo-pixels decreased by 1 02640 * (compare with function mos_spatial_calibration()). 02641 */ 02642 02643 nx = cpl_image_get_size_x(flat); 02644 ny = cpl_image_get_size_y(flat); 02645 02646 sdata = cpl_image_get_data(spatial); 02647 xdata = cpl_image_get_data(exslit); 02648 npseudo = cpl_image_get_size_y(exslit) - 1; 02649 02650 /* 02651 * Write interpolated smoothed values to CCD image 02652 */ 02653 02654 for (j = start_pixel; j < end_pixel; j++) { 02655 top = cpl_polynomial_eval_1d(polytop, j, NULL); 02656 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 02657 for (k = 0; k <= npseudo; k++) { 02658 ypos = top - k*(top-bot)/npseudo; 02659 yint = ypos; 02660 02661 /* 02662 * The line: 02663 * value = sdata[j + nx*yint]; 02664 * should be equivalent to: 02665 * value = npseudo*(top-yint)/(top-bot); 02666 */ 02667 02668 if (yint < 0 || yint >= ny-1) { 02669 yprev = yint; 02670 continue; 02671 } 02672 02673 value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */ 02674 ivalue = value; /* Nearest spatial pixels: */ 02675 fvalue = value - ivalue; /* ivalue and ivalue+1 */ 02676 if (ivalue < npseudo && ivalue >= 0) { 02677 vtop = xdata[j + nx*(npseudo-ivalue)]; 02678 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 02679 wdata[j + nx*yint] = vtop*(1-fvalue) + vbot*fvalue; 02680 02681 if (k) { 02682 02683 /* 02684 * This is added to recover lost pixels on 02685 * the CCD image (pixels are lost because 02686 * the CCD pixels are less than npseudo+1). 02687 */ 02688 02689 if (yprev - yint > 1) { 02690 value = sdata[j + nx*(yint+1)]; 02691 ivalue = value; 02692 fvalue = value - ivalue; 02693 if (ivalue < npseudo && ivalue >= 0) { 02694 vtop = xdata[j + nx*(npseudo-ivalue)]; 02695 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 02696 wdata[j + nx*(yint+1)] = vtop*(1-fvalue) 02697 + vbot*fvalue; 02698 } 02699 } 02700 } 02701 } 02702 yprev = yint; 02703 } 02704 } 02705 cpl_polynomial_delete(polytop); 02706 cpl_polynomial_delete(polybot); 02707 cpl_image_delete(exslit); 02708 } 02709 02710 cpl_image_delete(rectified); 02711 02712 cpl_image_divide(flat, smo_flat); 02713 02714 return smo_flat; 02715 } 02716 02717 02742 cpl_image *mos_normalise_longflat(cpl_image *flat, int sradius, int dradius, 02743 int polyorder) 02744 { 02745 const char *func = "mos_normalise_longflat"; 02746 02747 cpl_image *smo_flat; 02748 cpl_image *profile; 02749 cpl_vector *flux; 02750 cpl_vector *smo_flux; 02751 cpl_vector *positions; 02752 cpl_polynomial *trend; 02753 02754 float *level; 02755 float *p; 02756 float *data; 02757 double *fdata; 02758 double *pdata; 02759 02760 int nx, ny; 02761 int npoints; 02762 int i, j; 02763 02764 02765 if (flat == NULL) { 02766 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 02767 return NULL; 02768 } 02769 02770 if (sradius < 1 || dradius < 1) { 02771 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 02772 return NULL; 02773 } 02774 02775 smo_flat = cpl_image_duplicate(flat); 02776 02777 if (polyorder < 0) { 02778 02779 /* 02780 * First smooth along the spatial direction 02781 */ 02782 02783 cpl_image_turn(smo_flat, -1); /* For faster memory access */ 02784 02785 nx = cpl_image_get_size_x(smo_flat); 02786 ny = cpl_image_get_size_y(smo_flat); 02787 data = cpl_image_get_data(smo_flat); 02788 02789 for (i = 0; i < ny; i++) { 02790 flux = cpl_vector_new(nx); 02791 fdata = cpl_vector_get_data(flux); 02792 p = data; 02793 for (j = 0; j < nx; j++) 02794 *fdata++ = *p++; 02795 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02796 cpl_vector_delete(flux); 02797 fdata = cpl_vector_get_data(smo_flux); 02798 p = data; 02799 for (j = 0; j < nx; j++) 02800 *p++ = *fdata++; 02801 cpl_vector_delete(smo_flux); 02802 data += nx; 02803 } 02804 02805 /* 02806 * Second smooth along the dispersion direction 02807 */ 02808 02809 cpl_image_turn(smo_flat, 1); /* For faster memory access */ 02810 02811 nx = cpl_image_get_size_x(smo_flat); 02812 ny = cpl_image_get_size_y(smo_flat); 02813 data = cpl_image_get_data(smo_flat); 02814 02815 for (i = 0; i < ny; i++) { 02816 flux = cpl_vector_new(nx); 02817 fdata = cpl_vector_get_data(flux); 02818 p = data; 02819 for (j = 0; j < nx; j++) 02820 *fdata++ = *p++; 02821 smo_flux = cpl_vector_filter_median_create(flux, sradius); 02822 cpl_vector_delete(flux); 02823 fdata = cpl_vector_get_data(smo_flux); 02824 p = data; 02825 for (j = 0; j < nx; j++) 02826 *p++ = *fdata++; 02827 cpl_vector_delete(smo_flux); 02828 data += nx; 02829 } 02830 } 02831 else { 02832 02833 /* 02834 * Fit with a polynomial the flat field trend column by column. 02835 */ 02836 02837 cpl_image_turn(smo_flat, -1); /* For faster memory access */ 02838 02839 nx = cpl_image_get_size_x(smo_flat); 02840 ny = cpl_image_get_size_y(smo_flat); 02841 data = cpl_image_get_data(smo_flat); 02842 02843 profile = cpl_image_collapse_median_create(smo_flat, 1, 0, 0); 02844 level = cpl_image_get_data(profile); 02845 02846 for (i = 0; i < ny; i++) { 02847 02848 /* 02849 * First get the size of the vectors to allocate: 02850 * eliminate from fit any value more than 20% away 02851 * from median level in current column. 02852 */ 02853 02854 npoints = 0; 02855 p = data + i*nx; 02856 for (j = 0; j < nx; j++) 02857 if (fabs(p[j]/level[i] - 1) < 0.20) 02858 npoints++; 02859 02860 if (npoints > polyorder + 1) { 02861 02862 /* 02863 * Fill the vectors for the fitting 02864 */ 02865 02866 flux = cpl_vector_new(npoints); 02867 fdata = cpl_vector_get_data(flux); 02868 positions = cpl_vector_new(npoints); 02869 pdata = cpl_vector_get_data(positions); 02870 02871 npoints = 0; 02872 p = data + i*nx; 02873 for (j = 0; j < nx; j++) { 02874 if (fabs(p[j]/level[i] - 1) < 0.20) { 02875 fdata[npoints] = p[j]; 02876 pdata[npoints] = j; 02877 npoints++; 02878 } 02879 } 02880 02881 trend = cpl_polynomial_fit_1d_create(positions, flux, 02882 polyorder, NULL); 02883 02884 cpl_vector_delete(flux); 02885 cpl_vector_delete(positions); 02886 02887 if (trend) { 02888 p = data + i*nx; 02889 for (j = 0; j < nx; j++) 02890 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 02891 cpl_polynomial_delete(trend); 02892 } 02893 else { 02894 cpl_msg_warning(func, 02895 "Invalid flat field flux fit (ignored)"); 02896 } 02897 } 02898 } 02899 02900 cpl_image_delete(profile); 02901 cpl_image_turn(smo_flat, 1); 02902 02903 } 02904 02905 cpl_image_divide(flat, smo_flat); 02906 02907 return smo_flat; 02908 } 02909 02910 02933 cpl_error_code mos_interpolate_wavecalib_slit(cpl_table *idscoeff, 02934 cpl_table *slits, 02935 int order, int global) 02936 { 02937 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 02938 /* Max order is 5 */ 02939 int nrow = cpl_table_get_nrow(slits); 02940 int i, j; 02941 02942 02943 if (order < 0) 02944 return CPL_ERROR_NONE; 02945 02946 cpl_table_new_column(idscoeff, "x", CPL_TYPE_DOUBLE); 02947 cpl_table_new_column(idscoeff, "y", CPL_TYPE_DOUBLE); 02948 02949 for (i = 0; i < nrow; i++) { 02950 int position = cpl_table_get_int (slits, "position", i, NULL); 02951 int length = cpl_table_get_int (slits, "length", i, NULL); 02952 double xtop = cpl_table_get_double(slits, "xtop", i, NULL); 02953 double xbot = cpl_table_get_double(slits, "xbottom", i, NULL); 02954 double ytop = cpl_table_get_double(slits, "ytop", i, NULL); 02955 double ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 02956 double dx = xtop - xbot; 02957 double dy = ytop - ybot; 02958 cpl_table *table = cpl_table_extract(idscoeff, position, length); 02959 02960 if (mos_interpolate_wavecalib(table, NULL, 2, order)) 02961 continue; 02962 02963 cpl_table_erase_window(idscoeff, position, length); 02964 cpl_table_insert(idscoeff, table, position); 02965 02966 cpl_table_delete(table); 02967 02968 for (j = 0; j < length; j++) { 02969 cpl_table_set_double(idscoeff, "x", j + position, 02970 xbot + j*(dx/length)); 02971 cpl_table_set_double(idscoeff, "y", j + position, 02972 ybot + j*(dy/length)); 02973 } 02974 } 02975 02976 if (global) { 02977 02978 /* 02979 * Now fit a global solution 02980 */ 02981 02982 nrow = cpl_table_get_nrow(idscoeff); 02983 02984 for (i = 0; i < 6; i++) { 02985 cpl_table *dummy; 02986 cpl_vector *x; 02987 cpl_vector *y; 02988 cpl_bivector *z; 02989 cpl_vector *c; 02990 cpl_polynomial *p; 02991 cpl_vector *point; 02992 double *dpoint; 02993 int npoints; 02994 02995 if (!cpl_table_has_column(idscoeff, clab[i])) 02996 break; 02997 02998 npoints = nrow - cpl_table_count_invalid(idscoeff, clab[i]); 02999 if (npoints < 18) 03000 break; 03001 03002 dummy = cpl_table_new(nrow); 03003 cpl_table_duplicate_column(dummy, "x", idscoeff, "x"); 03004 cpl_table_duplicate_column(dummy, "y", idscoeff, "y"); 03005 cpl_table_duplicate_column(dummy, clab[i], idscoeff, clab[i]); 03006 cpl_table_erase_invalid(dummy); 03007 03008 x = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "x")); 03009 y = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, "y")); 03010 z = cpl_bivector_wrap_vectors(x, y); 03011 c = cpl_vector_wrap(npoints, cpl_table_get_data_double(dummy, 03012 clab[i])); 03013 p = cpl_polynomial_fit_2d_create(z, c, 2, NULL); 03014 cpl_bivector_unwrap_vectors(z); 03015 cpl_vector_unwrap(x); 03016 cpl_vector_unwrap(y); 03017 cpl_vector_unwrap(c); 03018 cpl_table_delete(dummy); 03019 03020 point = cpl_vector_new(2); 03021 dpoint = cpl_vector_get_data(point); 03022 for (j = 0; j < nrow; j++) { 03023 dpoint[0] = cpl_table_get_double(idscoeff, "x", j, NULL); 03024 dpoint[1] = cpl_table_get_double(idscoeff, "y", j, NULL); 03025 cpl_table_set_double(idscoeff, clab[i], j, 03026 cpl_polynomial_eval(p, point)); 03027 } 03028 cpl_vector_delete(point); 03029 cpl_polynomial_delete(p); 03030 } 03031 } 03032 03033 return CPL_ERROR_NONE; 03034 } 03035 03036 03062 cpl_error_code mos_interpolate_wavecalib(cpl_table *idscoeff, 03063 cpl_image *wavemap, int mode, 03064 int degree) 03065 { 03066 const char *func = "mos_interpolate_wavecalib"; 03067 03068 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 03069 /* Max order is 5 */ 03070 03071 cpl_vector *wave; 03072 cpl_vector *positions; 03073 cpl_polynomial *trend; 03074 03075 float *p; 03076 float *data; 03077 double *wdata; 03078 double *pdata; 03079 03080 double c; 03081 double mse, ksigma; 03082 03083 int order; 03084 int nrows, first_row, last_row; 03085 int nx, ny; 03086 int npoints, rpoints; 03087 int null; 03088 int i, j, k; 03089 03090 int polyorder = 4; /* Candidate input argument */ 03091 03092 03093 if (idscoeff == NULL) 03094 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03095 03096 if (mode < 0 || mode > 2) 03097 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 03098 03099 if (mode == 0 || degree < 0) 03100 return CPL_ERROR_NONE; 03101 03102 if (wavemap) { 03103 03104 /* 03105 * Fit with a polynomial the wavelength trend column by column. 03106 */ 03107 03108 cpl_image_turn(wavemap, -1); /* For faster memory access */ 03109 03110 nx = cpl_image_get_size_x(wavemap); 03111 ny = cpl_image_get_size_y(wavemap); 03112 data = cpl_image_get_data(wavemap); 03113 03114 for (i = 0; i < ny; i++) { 03115 03116 /* 03117 * First get the size of the vectors to allocate: 03118 * eliminate from fit any value with "impossible" wavelength. 03119 */ 03120 03121 npoints = 0; 03122 p = data + i*nx; 03123 for (j = 0; j < nx; j++) 03124 if (p[j] > 1.0) 03125 npoints++; 03126 03127 if (npoints > polyorder + 1) { 03128 03129 /* 03130 * Fill the vectors for the fitting 03131 */ 03132 03133 wave = cpl_vector_new(npoints); 03134 wdata = cpl_vector_get_data(wave); 03135 positions = cpl_vector_new(npoints); 03136 pdata = cpl_vector_get_data(positions); 03137 03138 npoints = 0; 03139 p = data + i*nx; 03140 for (j = 0; j < nx; j++) { 03141 if (p[j] > 1.0) { 03142 wdata[npoints] = p[j]; 03143 pdata[npoints] = j; 03144 npoints++; 03145 } 03146 } 03147 03148 trend = cpl_polynomial_fit_1d_create(positions, wave, 03149 polyorder, &mse); 03150 03151 ksigma = 3*sqrt(mse); 03152 03153 cpl_vector_delete(wave); 03154 cpl_vector_delete(positions); 03155 03156 if (trend) { 03157 03158 /* 03159 * Apply 3-sigma rejection 03160 */ 03161 03162 rpoints = 0; 03163 p = data + i*nx; 03164 for (j = 0; j < nx; j++) 03165 if (p[j] > 1.0) 03166 if (fabs(cpl_polynomial_eval_1d(trend, j, NULL) 03167 - p[j]) < ksigma) 03168 rpoints++; 03169 03170 if (rpoints < npoints && rpoints > polyorder + 1) { 03171 03172 wave = cpl_vector_new(rpoints); 03173 wdata = cpl_vector_get_data(wave); 03174 positions = cpl_vector_new(rpoints); 03175 pdata = cpl_vector_get_data(positions); 03176 03177 npoints = 0; 03178 p = data + i*nx; 03179 for (j = 0; j < nx; j++) { 03180 if (p[j] > 1.0) { 03181 if (fabs(cpl_polynomial_eval_1d(trend, 03182 j, NULL) - p[j]) 03183 < ksigma) { 03184 wdata[npoints] = p[j]; 03185 pdata[npoints] = j; 03186 npoints++; 03187 } 03188 } 03189 } 03190 03191 cpl_polynomial_delete(trend); 03192 trend = cpl_polynomial_fit_1d_create(positions, wave, 03193 polyorder, NULL); 03194 03195 cpl_vector_delete(wave); 03196 cpl_vector_delete(positions); 03197 } 03198 } 03199 03200 if (trend) { 03201 p = data + i*nx; 03202 if (mode == 1) { 03203 for (j = 0; j < nx; j++) 03204 if (p[j] < 1.0) 03205 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 03206 } 03207 else if (mode == 2) { 03208 for (j = 0; j < nx; j++) 03209 p[j] = cpl_polynomial_eval_1d(trend, j, NULL); 03210 } 03211 cpl_polynomial_delete(trend); 03212 } 03213 else { 03214 cpl_msg_warning(func, 03215 "Invalid wavelength field fit (ignored)"); 03216 } 03217 } 03218 03219 } 03220 03221 cpl_image_turn(wavemap, 1); 03222 03223 } 03224 03225 03226 /* 03227 * Interpolating the IDS coefficients 03228 */ 03229 03230 nrows = cpl_table_get_nrow(idscoeff); 03231 03232 order = 0; 03233 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 03234 ++order; 03235 --order; 03236 03237 if (degree == 0) { 03238 for (k = 0; k <= order; k++) { 03239 double m; 03240 if (cpl_table_has_column(idscoeff, clab[k])) { 03241 m = cpl_table_get_column_median(idscoeff, clab[k]); 03242 cpl_table_fill_column_window_double(idscoeff, clab[k], 03243 0, nrows, m); 03244 } 03245 } 03246 03247 return CPL_ERROR_NONE; 03248 } 03249 03250 first_row = 0; 03251 while (!cpl_table_is_valid(idscoeff, clab[0], first_row)) 03252 first_row++; 03253 03254 last_row = nrows - 1; 03255 while (!cpl_table_is_valid(idscoeff, clab[0], last_row)) 03256 last_row--; 03257 03258 for (k = 0; k <= order; k++) { 03259 03260 npoints = nrows - cpl_table_count_invalid(idscoeff, clab[k]); 03261 wave = cpl_vector_new(npoints); 03262 wdata = cpl_vector_get_data(wave); 03263 positions = cpl_vector_new(npoints); 03264 pdata = cpl_vector_get_data(positions); 03265 03266 npoints = 0; 03267 for (i = first_row; i <= last_row; i++) { 03268 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03269 if (null == 0) { 03270 wdata[npoints] = c; 03271 pdata[npoints] = i; 03272 npoints++; 03273 } 03274 } 03275 03276 // This doesn't seem to provide good results, I have not understood why. 03277 // Restore for robust linear fitting. 03278 // 03279 if (degree == 1) { 03280 cpl_vector *p; 03281 cpl_vector *w; 03282 cpl_bivector *list; 03283 double q, m; 03284 03285 if (npoints > 4) { 03286 p = cpl_vector_extract(positions, 2, npoints - 2, 1); 03287 w = cpl_vector_extract(wave, 2, npoints - 2, 1); 03288 } 03289 else { 03290 p = positions; 03291 w = wave; 03292 } 03293 03294 list = cpl_bivector_wrap_vectors(p, w); 03295 03296 robustLinearFit(list, &q, &m, &mse); 03297 cpl_bivector_unwrap_vectors(list); 03298 for (i = first_row; i <= last_row; i++) 03299 cpl_table_set_double(idscoeff, clab[k], i, q + m*i); 03300 03301 if (npoints > 4) { 03302 cpl_vector_delete(p); 03303 cpl_vector_delete(w); 03304 } 03305 03306 continue; 03307 } 03308 03309 // End robust linear fitting 03310 03311 trend = cpl_polynomial_fit_1d_create(positions, wave, degree, &mse); 03312 03313 ksigma = 3*sqrt(mse); 03314 03315 cpl_vector_delete(wave); 03316 cpl_vector_delete(positions); 03317 03318 /* 03319 * Iteration 03320 */ 03321 03322 if (trend) { 03323 rpoints = 0; 03324 for (i = first_row; i <= last_row; i++) { 03325 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03326 if (null == 0) { 03327 if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 03328 < ksigma) { 03329 rpoints++; 03330 } 03331 } 03332 } 03333 03334 if (rpoints > 0 && rpoints < npoints) { 03335 cpl_msg_debug(func, "%d points rejected from " 03336 "wavelength calibration fit", 03337 npoints - rpoints); 03338 03339 wave = cpl_vector_new(rpoints); 03340 wdata = cpl_vector_get_data(wave); 03341 positions = cpl_vector_new(rpoints); 03342 pdata = cpl_vector_get_data(positions); 03343 03344 npoints = 0; 03345 for (i = first_row; i <= last_row; i++) { 03346 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 03347 if (null == 0) { 03348 if (fabs(cpl_polynomial_eval_1d(trend, i, NULL) - c) 03349 < ksigma) { 03350 wdata[npoints] = c; 03351 pdata[npoints] = i; 03352 npoints++; 03353 } 03354 } 03355 } 03356 03357 if (npoints) { 03358 cpl_polynomial_delete(trend); 03359 trend = cpl_polynomial_fit_1d_create(positions, 03360 wave, degree, NULL); 03361 } 03362 03363 cpl_vector_delete(wave); 03364 cpl_vector_delete(positions); 03365 03366 } 03367 } 03368 03369 if (trend) { 03370 for (i = first_row; i <= last_row; i++) { 03371 if (mode == 1) { 03372 if (!cpl_table_is_valid(idscoeff, clab[k], i)) { 03373 cpl_table_set_double(idscoeff, clab[k], i, 03374 cpl_polynomial_eval_1d(trend, i, 03375 NULL)); 03376 } 03377 } 03378 else if (mode == 2) { 03379 cpl_table_set_double(idscoeff, clab[k], i, 03380 cpl_polynomial_eval_1d(trend, i, NULL)); 03381 } 03382 } 03383 cpl_polynomial_delete(trend); 03384 } 03385 else { 03386 cpl_msg_warning(func, "Invalid IDS coefficient fit (ignored)"); 03387 } 03388 03389 } 03390 03391 return CPL_ERROR_NONE; 03392 } 03393 03394 03395 03421 cpl_image *mos_remove_bias(cpl_image *image, cpl_image *bias, 03422 cpl_table *overscans) 03423 { 03424 const char *func = "mos_remove_bias"; 03425 03426 cpl_image *unbiased; 03427 cpl_image *overscan; 03428 double mean_bias_level; 03429 double mean_overscans_level; 03430 int count; 03431 int nrows; 03432 int xlow, ylow, xhig, yhig; 03433 int i; 03434 03435 03436 if (image == NULL || overscans == NULL) { 03437 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03438 return NULL; 03439 } 03440 03441 nrows = cpl_table_get_nrow(overscans); 03442 03443 if (nrows == 0) { 03444 cpl_msg_error(func, "Empty overscan table"); 03445 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 03446 return NULL; 03447 } 03448 03449 if (bias) { 03450 if (nrows == 1) { 03451 unbiased = cpl_image_subtract_create(image, bias); 03452 if (unbiased == NULL) { 03453 cpl_msg_error(func, "Incompatible master bias"); 03454 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03455 } 03456 return unbiased; 03457 } 03458 mean_bias_level = cpl_image_get_mean(bias); 03459 } 03460 else { 03461 if (nrows == 1) { 03462 cpl_msg_error(func, "No master bias in input, and no overscan " 03463 "regions in input image: bias subtraction " 03464 "cannot be performed!"); 03465 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 03466 return NULL; 03467 } 03468 mean_bias_level = 0.0; 03469 } 03470 03471 mean_overscans_level = 0.0; 03472 count = 0; 03473 for (i = 0; i < nrows; i++) { 03474 xlow = cpl_table_get_int(overscans, "xlow", i, NULL); 03475 ylow = cpl_table_get_int(overscans, "ylow", i, NULL); 03476 xhig = cpl_table_get_int(overscans, "xhig", i, NULL); 03477 yhig = cpl_table_get_int(overscans, "yhig", i, NULL); 03478 03479 if (i == 0) { 03480 unbiased = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig); 03481 if (unbiased == NULL) { 03482 cpl_msg_error(func, "Incompatible overscan table"); 03483 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03484 return NULL; 03485 } 03486 if (bias) { 03487 if (cpl_image_subtract(unbiased, bias)) { 03488 cpl_msg_error(func, "Incompatible master bias"); 03489 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03490 cpl_image_delete(unbiased); 03491 return NULL; 03492 } 03493 } 03494 } 03495 else { 03496 overscan = cpl_image_extract(image, xlow+1, ylow+1, xhig, yhig); 03497 if (overscan == NULL) { 03498 cpl_msg_error(func, "Incompatible overscan table"); 03499 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 03500 cpl_image_delete(unbiased); 03501 return NULL; 03502 } 03503 03504 mean_overscans_level += cpl_image_get_median(overscan); 03505 count++; 03506 03507 /*** 03508 * Here the mean level was used: not very robust... 03509 03510 mean_overscans_level += cpl_image_get_flux(overscan); 03511 count += cpl_image_get_size_x(overscan) 03512 * cpl_image_get_size_y(overscan); 03513 ***/ 03514 cpl_image_delete(overscan); 03515 } 03516 } 03517 03518 /* 03519 * Overscan correction 03520 */ 03521 03522 mean_overscans_level /= count; 03523 03524 cpl_image_subtract_scalar(unbiased, mean_overscans_level - mean_bias_level); 03525 03526 cpl_msg_info(cpl_func, 03527 "Ratio between mean overscans level and mean bias level: %.2f", 03528 mean_overscans_level / mean_bias_level); 03529 03530 return unbiased; 03531 03532 } 03533 03534 03593 cpl_error_code mos_arc_background_1D(float *spectrum, float *back, 03594 int length, int msize, int fsize) 03595 { 03596 const char *func = "mos_arc_background_1D"; 03597 03598 float *minf; 03599 float *maxf; 03600 float *smof; 03601 int i; 03602 03603 03604 if (spectrum == NULL || back == NULL) 03605 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03606 03607 if (msize % 2 == 0) 03608 msize++; 03609 03610 if (fsize % 2 == 0) 03611 fsize++; 03612 03613 if (msize < 3 || fsize < msize || length < 2*fsize) 03614 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 03615 03616 03617 minf = min_filter(spectrum, length, msize); 03618 smof = smo_filter(minf, length, fsize); 03619 cpl_free(minf); 03620 maxf = max_filter(smof, length, 2*msize+1); 03621 cpl_free(smof); 03622 smof = smo_filter(maxf, length, 2*fsize+1); 03623 cpl_free(maxf); 03624 minf = min_filter(smof, length, 2*msize+1); 03625 cpl_free(smof); 03626 smof = smo_filter(minf, length, 2*fsize+1); 03627 cpl_free(minf); 03628 03629 for (i = 0; i < length; i++) 03630 back[i] = smof[i]; 03631 03632 cpl_free(smof); 03633 03634 return CPL_ERROR_NONE; 03635 03636 } 03637 03638 03695 cpl_image *mos_arc_background(cpl_image *image, int msize, int fsize) 03696 { 03697 const char *func = "mos_arc_background"; 03698 03699 cpl_image *fimage; 03700 cpl_image *bimage; 03701 cpl_matrix *kernel; 03702 float *data; 03703 float *bdata; 03704 float *row; 03705 float *brow; 03706 int nx, ny; 03707 int i; 03708 03709 03710 if (image == NULL) { 03711 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03712 return NULL; 03713 } 03714 03715 if (msize % 2 == 0) 03716 msize++; 03717 03718 if (fsize % 2 == 0) 03719 fsize++; 03720 03721 nx = cpl_image_get_size_x(image); 03722 ny = cpl_image_get_size_y(image); 03723 03724 bimage = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 03725 03726 fimage = mos_image_filter_median(image, 3, 3); 03727 03728 data = cpl_image_get_data_float(fimage); 03729 bdata = cpl_image_get_data_float(bimage); 03730 03731 for (i = 0; i < ny; i++) { 03732 row = data + i * nx; 03733 brow = bdata + i * nx; 03734 if (mos_arc_background_1D(row, brow, nx, msize, fsize)) { 03735 cpl_error_set_where(func); 03736 cpl_image_delete(fimage); 03737 cpl_image_delete(bimage); 03738 return NULL; 03739 } 03740 } 03741 03742 cpl_image_delete(fimage); 03743 03744 return bimage; 03745 } 03746 03747 03768 int mos_lines_width(const float *spectrum, int length) 03769 { 03770 03771 const char *func = "mos_lines_width"; 03772 03773 double *profile1 = cpl_calloc(length - 1, sizeof(double)); 03774 double *profile2 = cpl_calloc(length - 1, sizeof(double)); 03775 03776 double norm, value, max; 03777 int radius = 20; 03778 int short_length = length - 2*radius - 1; 03779 int width; 03780 int i, j, k; 03781 03782 03783 /* 03784 * Derivative, and separation of positive and negative derivatives 03785 */ 03786 03787 for (j = 0, i = 1; i < length; j++, i++) { 03788 profile1[j] = profile2[j] = spectrum[i] - spectrum[j]; 03789 if (profile1[j] < 0) 03790 profile1[j] = 0; 03791 if (profile2[j] > 0) 03792 profile2[j] = 0; 03793 else 03794 profile2[j] = -profile2[j]; 03795 } 03796 03797 03798 /* 03799 * Profiles normalisation 03800 */ 03801 03802 length--; 03803 03804 norm = 0; 03805 for (i = 0; i < length; i++) 03806 if (norm < profile1[i]) 03807 norm = profile1[i]; 03808 03809 for (i = 0; i < length; i++) { 03810 profile1[i] /= norm; 03811 profile2[i] /= norm; 03812 } 03813 03814 03815 /* 03816 * Cross-correlation 03817 */ 03818 03819 max = -1; 03820 for (i = 0; i <= radius; i++) { 03821 value = 0; 03822 for (j = 0; j < short_length; j++) { 03823 k = radius+j; 03824 value += profile1[k] * profile2[k+i]; 03825 } 03826 if (max < value) { 03827 max = value; 03828 width = i; 03829 } 03830 } 03831 03832 cpl_free(profile1); 03833 cpl_free(profile2); 03834 03835 if (max < 0.0) { 03836 cpl_msg_debug(func, "Cannot estimate line width"); 03837 width = 1; 03838 } 03839 03840 return width; 03841 03842 } 03843 03844 03871 cpl_vector *mos_peak_candidates(const float *spectrum, 03872 int length, float level, 03873 float exp_width) 03874 { 03875 03876 const char *func = "mos_peak_candidates"; 03877 03878 int i, j; 03879 int nint = length - 1; 03880 int n = 0; 03881 int width = 2 * ceil(exp_width / 2) + 1; 03882 int start = width / 2; 03883 int end = length - width / 2; 03884 int step; 03885 float *smo; 03886 double *data = cpl_calloc(length/2, sizeof(double)); 03887 03888 03889 if (spectrum == NULL) { 03890 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03891 return NULL; 03892 } 03893 03894 03895 /* 03896 * If lines have a flat top (as in the case of broad slit), smooth 03897 * before determining the max. 03898 */ 03899 03900 if (width > 7) { 03901 smo = cpl_calloc(length, sizeof(float)); 03902 start = width / 2; 03903 end = length - width / 2; 03904 for (i = 0; i < start; i++) 03905 smo[i] = spectrum[i]; 03906 for (i = start; i < end; i++) { 03907 for (j = i - start; j <= i + start; j++) 03908 smo[i] += spectrum[j]; 03909 smo[i] /= width; 03910 } 03911 for (i = end; i < length; i++) 03912 smo[i] = spectrum[i]; 03913 } 03914 else { 03915 smo = (float *)spectrum; 03916 } 03917 03918 /* 03919 * Collect all relative maxima along spectrum, that are higher than the 03920 * specified level. 03921 */ 03922 03923 if (width > 20) 03924 step = width / 2; 03925 else 03926 step = 1; 03927 03928 for (i = step; i < nint - step + 1; i += step) { 03929 if (smo[i] > level) { 03930 if (smo[i] >= smo[i-step] && smo[i] > smo[i+step]) { 03931 if (smo[i-step] != 0.0 && smo[i+step] != 0.0) { 03932 data[n] = i + step * values_to_dx(smo[i-step], smo[i], smo[i+step]); 03933 ++n; 03934 } 03935 } 03936 } 03937 } 03938 03939 if (width > 7) { 03940 cpl_free(smo); 03941 } 03942 03943 if (n == 0) { 03944 cpl_free(data); 03945 return NULL; 03946 } 03947 03948 return cpl_vector_wrap(n, data); 03949 03950 } 03951 03952 03974 cpl_vector *mos_refine_peaks(const float *spectrum, int length, 03975 cpl_vector *peaks, int sradius) 03976 { 03977 03978 const char *func = "mos_refine_peaks"; 03979 03980 double *data; 03981 float pos; 03982 int npeaks; 03983 int startPos, endPos; 03984 int window = 2*sradius+1; 03985 int i, j; 03986 03987 03988 if (peaks == NULL || spectrum == NULL) { 03989 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 03990 return NULL; 03991 } 03992 03993 npeaks = cpl_vector_get_size(peaks); 03994 data = cpl_vector_unwrap(peaks); 03995 03996 for (i = 0; i < npeaks; i++) { 03997 startPos = data[i] - window/2; 03998 endPos = startPos + window; 03999 if (startPos < 0 || endPos >= length) 04000 continue; 04001 04002 if (0 == peakPosition(spectrum + startPos, window, &pos, 1)) { 04003 pos += startPos; 04004 data[i] = pos; 04005 } 04006 } 04007 04008 for (i = 1; i < npeaks; i++) 04009 if (data[i] - data[i-1] < 0.5) 04010 data[i-1] = -1.0; 04011 04012 for (i = 0, j = 0; i < npeaks; i++) { 04013 if (data[i] > 0.0) { 04014 if (i != j) 04015 data[j] = data[i]; 04016 j++; 04017 } 04018 } 04019 04020 return cpl_vector_wrap(j, data); 04021 04022 } 04023 04024 04025 void mos_set_multiplex(int multiplex) 04026 { 04027 mos_multiplex = multiplex; 04028 } 04029 04083 cpl_bivector *mos_identify_peaks(cpl_vector *peaks, cpl_vector *lines, 04084 double min_disp, double max_disp, 04085 double tolerance) 04086 { 04087 04088 int i, j, k, l; 04089 int nlint, npint; 04090 int minpos; 04091 float min; 04092 double lratio, pratio; 04093 double lo_start, lo_end, hi_start, hi_end, denom; 04094 double disp, variation, prev_variation; 04095 int max, maxpos, minl, mink; 04096 int ambiguous; 04097 int npeaks_lo, npeaks_hi; 04098 int *peak_lo; 04099 int *peak_hi; 04100 int **ident; 04101 int *nident; 04102 int *lident; 04103 04104 double *peak; 04105 double *line; 04106 int npeaks, nlines; 04107 04108 double *xpos; 04109 double *lambda; 04110 int *ilambda; 04111 double *tmp_xpos; 04112 double *tmp_lambda; 04113 int *tmp_ilambda; 04114 int *flag; 04115 int n = 0; 04116 int nn; 04117 int nseq = 0; 04118 int gap; 04119 int *seq_length; 04120 int found; 04121 04122 peak = cpl_vector_get_data(peaks); 04123 npeaks = cpl_vector_get_size(peaks); 04124 line = cpl_vector_get_data(lines); 04125 nlines = cpl_vector_get_size(lines); 04126 04127 if (npeaks < 4) 04128 return NULL; 04129 04130 peak_lo = cpl_malloc(npeaks * sizeof(int)); 04131 peak_hi = cpl_malloc(npeaks * sizeof(int)); 04132 nident = cpl_calloc(npeaks, sizeof(int)); 04133 lident = cpl_calloc(nlines, sizeof(int)); 04134 xpos = cpl_calloc(npeaks, sizeof(double)); 04135 lambda = cpl_calloc(npeaks, sizeof(double)); 04136 ilambda = cpl_calloc(npeaks, sizeof(int)); 04137 tmp_xpos = cpl_calloc(npeaks, sizeof(double)); 04138 tmp_lambda = cpl_calloc(npeaks, sizeof(double)); 04139 tmp_ilambda = cpl_calloc(npeaks, sizeof(int)); 04140 flag = cpl_calloc(npeaks, sizeof(int)); 04141 seq_length = cpl_calloc(npeaks, sizeof(int)); 04142 ident = cpl_malloc(npeaks * sizeof(int *)); 04143 for (i = 0; i < npeaks; i++) 04144 ident[i] = cpl_malloc(3 * npeaks * sizeof(int)); 04145 04146 /* 04147 * This is just the number of intervals - one less than the number 04148 * of points (catalog wavelengths, or detected peaks). 04149 */ 04150 04151 nlint = nlines - 1; 04152 npint = npeaks - 1; 04153 04154 04155 /* 04156 * Here the big loops on catalog lines begins. 04157 */ 04158 04159 for (i = 1; i < nlint; i++) { 04160 04161 04162 /* 04163 * For each catalog wavelength I take the previous and the next one, 04164 * and compute the ratio of the corresponding wavelength intervals. 04165 * This ratio will be compared to all the ratios obtained doing the 04166 * same with all the detected peaks positions. 04167 */ 04168 04169 lratio = (line[i+1] - line[i]) / (line[i] - line[i-1]); 04170 04171 04172 /* 04173 * Here the loop on detected peaks positions begins. 04174 */ 04175 04176 for (j = 1; j < npint; j++) { 04177 04178 /* 04179 * Not all peaks are used for computing ratios: just the ones 04180 * that are compatible with the expected spectral dispersion 04181 * are taken into consideration. Therefore, I define the pixel 04182 * intervals before and after any peak that are compatible with 04183 * the specified dispersion interval, and select just the peaks 04184 * within such intervals. If either of the two intervals doesn't 04185 * contain any peak, then I skip the current peak and continue 04186 * with the next. 04187 */ 04188 04189 lo_start = peak[j] - (line[i] - line[i-1]) / min_disp; 04190 lo_end = peak[j] - (line[i] - line[i-1]) / max_disp; 04191 hi_start = peak[j] + (line[i+1] - line[i]) / max_disp; 04192 hi_end = peak[j] + (line[i+1] - line[i]) / min_disp; 04193 04194 for (npeaks_lo = 0, k = 0; k < npeaks; k++) { 04195 if (peak[k] > lo_end) 04196 break; 04197 if (peak[k] > lo_start) { 04198 peak_lo[npeaks_lo] = k; 04199 ++npeaks_lo; 04200 } 04201 } 04202 04203 if (npeaks_lo == 0) 04204 continue; 04205 04206 for (npeaks_hi = 0, k = 0; k < npeaks; k++) { 04207 if (peak[k] > hi_end) 04208 break; 04209 if (peak[k] > hi_start) { 04210 peak_hi[npeaks_hi] = k; 04211 ++npeaks_hi; 04212 } 04213 } 04214 04215 if (npeaks_hi == 0) 04216 continue; 04217 04218 04219 /* 04220 * Now I have all peaks that may help for a local identification. 04221 * peak_lo[k] is the sequence number of the k-th peak of the lower 04222 * interval; peak_hi[l] is the sequence number of the l-th peak of 04223 * the higher interval. j is, of course, the sequence number of the 04224 * current peak (second big loop). 04225 */ 04226 04227 prev_variation = 1000.0; 04228 minl = mink = 0; 04229 04230 for (k = 0; k < npeaks_lo; k++) { 04231 denom = peak[j] - peak[peak_lo[k]]; 04232 for (l = 0; l < npeaks_hi; l++) { 04233 04234 /* 04235 * For any pair of peaks - one from the lower and the other 04236 * from the higher interval - I compute the same ratio that 04237 * was computed with the current line catalog wavelength. 04238 */ 04239 04240 pratio = (peak[peak_hi[l]] - peak[j]) / denom; 04241 04242 /* 04243 * If the two ratios are compatible within the specified 04244 * tolerance, we have a preliminary identification. This 04245 * will be marked in the matrix ident[][], where the first 04246 * index corresponds to a peak sequence number, and the second 04247 * index is the counter of the identifications made during 04248 * this whole process. The array of counters is nident[]. 04249 * If more than one interval pair fulfills the specified 04250 * tolerance, the closest to the expected ratio is selected. 04251 */ 04252 04253 variation = fabs(lratio-pratio) / pratio; 04254 04255 if (variation < tolerance) { 04256 if (variation < prev_variation) { 04257 prev_variation = variation; 04258 minl = l; 04259 mink = k; 04260 } 04261 } 04262 } 04263 } 04264 if (prev_variation < tolerance) { 04265 ident[j][nident[j]] = i; 04266 ident[peak_hi[minl]][nident[peak_hi[minl]]] = i + 1; 04267 ident[peak_lo[mink]][nident[peak_lo[mink]]] = i - 1; 04268 ++nident[j]; 04269 ++nident[peak_hi[minl]]; 04270 ++nident[peak_lo[mink]]; 04271 } 04272 } /* End loop on positions */ 04273 } /* End loop on lines */ 04274 04275 04276 /* 04277 * At this point I have filled the ident matrix with all my preliminary 04278 * identifications. Ambiguous identifications must be eliminated. 04279 */ 04280 04281 04282 for (i = 0; i < npeaks; i++) { 04283 04284 04285 /* 04286 * I don't take into consideration peaks that were never identified. 04287 * They are likely contaminations, or emission lines that were not 04288 * listed in the input wavelength catalog. 04289 */ 04290 04291 if (nident[i] > 1) { 04292 04293 04294 /* 04295 * Initialise the histogram of wavelengths assigned to the i-th peak. 04296 */ 04297 04298 for (j = 0; j < nlines; j++) 04299 lident[j] = 0; 04300 04301 04302 /* 04303 * Count how many times each catalog wavelength was assigned 04304 * to the i-th peak. 04305 */ 04306 04307 for (j = 0; j < nident[i]; j++) 04308 ++lident[ident[i][j]]; 04309 04310 04311 /* 04312 * What wavelength was most frequently assigned to the i-th peak? 04313 */ 04314 04315 max = 0; 04316 maxpos = 0; 04317 for (j = 0; j < nlines; j++) { 04318 if (max < lident[j]) { 04319 max = lident[j]; 04320 maxpos = j; 04321 } 04322 } 04323 04324 04325 /* 04326 * Were there other wavelengths assigned with the same frequency? 04327 * This would be the case of an ambiguous identification. It is 04328 * safer to reject this peak... 04329 */ 04330 04331 ambiguous = 0; 04332 04333 for (k = maxpos + 1; k < nlines; k++) { 04334 if (lident[k] == max) { 04335 ambiguous = 1; 04336 break; 04337 } 04338 } 04339 04340 if (ambiguous) 04341 continue; 04342 04343 04344 /* 04345 * Otherwise, I assign to the i-th peak the wavelength that was 04346 * most often assigned to it. 04347 */ 04348 04349 tmp_xpos[n] = peak[i]; 04350 tmp_lambda[n] = line[maxpos]; 04351 tmp_ilambda[n] = maxpos; 04352 04353 ++n; 04354 04355 } 04356 04357 } 04358 04359 04360 /* 04361 * Check on identified peaks. Contaminations from other spectra might 04362 * be present and should be excluded: this type of contamination 04363 * consists of peaks that have been _correctly_ identified! The non- 04364 * spectral type of light contamination should have been almost all 04365 * removed already in the previous steps, but it may still be present. 04366 * Here, the self-consistent sequences of identified peaks are 04367 * separated one from the other. At the moment, just the longest of 04368 * such sequences is selected (in other words, spectral multiplexing 04369 * is ignored). 04370 */ 04371 04372 if (n > 1) { 04373 nn = 0; /* Number of peaks in the list of sequences */ 04374 nseq = 0; /* Current sequence */ 04375 for (k = 0; k < n; k++) { 04376 if (flag[k] == 0) { /* Was peak k already assigned to a sequence? */ 04377 flag[k] = 1; 04378 xpos[nn] = tmp_xpos[k]; /* Begin the nseq-th sequence */ 04379 lambda[nn] = tmp_lambda[k]; 04380 ilambda[nn] = tmp_ilambda[k]; 04381 ++seq_length[nseq]; 04382 ++nn; 04383 04384 /* 04385 * Now look for all the following peaks that are compatible 04386 * with the expected spectral dispersion, and add them in 04387 * sequence to xpos. Note that missing peaks are not a problem... 04388 */ 04389 04390 i = k; 04391 while (i < n - 1) { 04392 found = 0; 04393 for (j = i + 1; j < n; j++) { 04394 if (flag[j] == 0) { 04395 disp = (tmp_lambda[j] - tmp_lambda[i]) 04396 / (tmp_xpos[j] - tmp_xpos[i]); 04397 if (disp >= min_disp && disp <= max_disp) { 04398 flag[j] = 1; 04399 xpos[nn] = tmp_xpos[j]; 04400 lambda[nn] = tmp_lambda[j]; 04401 ilambda[nn] = tmp_ilambda[j]; 04402 ++seq_length[nseq]; 04403 ++nn; 04404 i = j; 04405 found = 1; 04406 break; 04407 } 04408 } 04409 } 04410 if (!found) 04411 break; 04412 } 04413 04414 /* 04415 * Current sequence is completed: begin new sequence on the 04416 * excluded peaks, starting the loop on peaks again. 04417 */ 04418 04419 ++nseq; 04420 k = 0; 04421 } 04422 } 04423 04424 04425 /* 04426 * Find the longest sequence of self-consistent peaks. 04427 */ 04428 04429 maxpos = max = 0; 04430 04431 if (mos_multiplex < 0) { 04432 for (i = 0; i < nseq; i++) { 04433 if (seq_length[i] > max) { 04434 max = seq_length[i]; 04435 maxpos = i; 04436 } 04437 } 04438 } 04439 else { 04440 04441 /* 04442 * Now consider the sequence which lays in the specified 04443 * CCD region (indicated by mos_multiplex): that is, _most_ 04444 * of its lines (more than half) must be in that region... 04445 */ 04446 04447 nn = 0; 04448 found = 0; 04449 04450 for (i = 0; i < nseq; i++) { 04451 n = seq_length[i]; 04452 if (n > 5) { 04453 cpl_array *regions = cpl_array_new(n, CPL_TYPE_INT); 04454 int region; 04455 04456 for (j = 0; j < n; j++) 04457 cpl_array_set_int(regions, j, 04458 ((int)floor(xpos[nn + j])) / mos_region_size); 04459 04460 region = (int)cpl_array_get_median(regions); 04461 cpl_array_delete(regions); 04462 04463 if (mos_multiplex == region) { 04464 if (found) { 04465 cpl_msg_debug(cpl_func, "More than one spectrum found in " 04466 "region %d (only the first one is extracted)", 04467 mos_multiplex); 04468 break; 04469 } 04470 found = 1; 04471 max = seq_length[i]; 04472 maxpos = i; 04473 } 04474 } 04475 nn += seq_length[i]; 04476 } 04477 } 04478 04479 /* 04480 * Find where this sequence starts in the whole peak position 04481 * storage. 04482 */ 04483 04484 nn = 0; 04485 for (i = 0; i < maxpos; i++) 04486 nn += seq_length[i]; 04487 04488 /* 04489 * Move the longest sequence at the beginning of the returned lists 04490 */ 04491 04492 n = max; 04493 for (i = 0; i < n; i++, nn++) { 04494 xpos[i] = xpos[nn]; 04495 lambda[i] = lambda[nn]; 04496 ilambda[i] = ilambda[nn]; 04497 } 04498 04499 04500 /* 04501 * Are some wavelengths missing? Recover them. 04502 */ 04503 04504 for (i = 1; i < n; i++) { 04505 gap = ilambda[i] - ilambda[i-1]; 04506 for (j = 1; j < gap; j++) { 04507 04508 if (j == 1) { 04509 04510 /* 04511 * Determine the local dispersion from the current pair of peaks 04512 */ 04513 04514 disp = (lambda[i] - lambda[i-1]) / (xpos[i] - xpos[i-1]); 04515 } 04516 04517 /* 04518 * With this, find the expected position of the missing 04519 * peak by linear interpolation. 04520 */ 04521 04522 hi_start = xpos[i-1] + (line[ilambda[i-1] + j] - lambda[i-1]) / disp; 04523 04524 /* 04525 * Is there a peak at that position? Here a peak from the 04526 * original list is searched, that is closer than 2 pixels 04527 * to the expected position. If it is found, insert it at 04528 * the current position on the list of identified peaks, 04529 * and leave immediately the loop (taking the new position 04530 * for the following linear interpolation, in case more 04531 * than one peak is missing in the current interval). 04532 * If it is not found, stay in the loop, looking for 04533 * the following missing peaks in this interval. 04534 */ 04535 04536 found = 0; 04537 for (k = 0; k < npeaks; k++) { 04538 if (fabs(peak[k] - hi_start) < 2) { 04539 for (l = n; l > i; l--) { 04540 xpos[l] = xpos[l-1]; 04541 lambda[l] = lambda[l-1]; 04542 ilambda[l] = ilambda[l-1]; 04543 } 04544 xpos[i] = peak[k]; 04545 lambda[i] = line[ilambda[i-1] + j]; 04546 ilambda[i] = ilambda[i-1] + j; 04547 ++n; 04548 found = 1; 04549 break; 04550 } 04551 } 04552 if (found) 04553 break; 04554 } 04555 } 04556 04557 04558 /* 04559 * Try to extrapolate forward 04560 */ 04561 04562 found = 1; 04563 while (ilambda[n-1] < nlines - 1 && found) { 04564 04565 /* 04566 * Determine the local dispersion from the last pair of 04567 * identified peaks 04568 */ 04569 04570 if (n > 1) 04571 disp = (lambda[n-1] - lambda[n-2]) / (xpos[n-1] - xpos[n-2]); 04572 else 04573 disp = 0.0; 04574 04575 if (disp > max_disp || disp < min_disp) 04576 break; 04577 04578 04579 /* 04580 * With this, find the expected position of the missing 04581 * peak by linear interpolation. 04582 */ 04583 04584 hi_start = xpos[n-1] + (line[ilambda[n-1] + 1] - lambda[n-1]) / disp; 04585 04586 /* 04587 * Is there a peak at that position? Here a peak from the 04588 * original list is searched, that is closer than 6 pixels 04589 * to the expected position. If it is found, insert it at 04590 * the end of the list of identified peaks. If it is not 04591 * found, leave the loop. 04592 */ 04593 04594 found = 0; 04595 min = fabs(peak[0] - hi_start); 04596 minpos = 0; 04597 for (k = 1; k < npeaks; k++) { 04598 if (min > fabs(peak[k] - hi_start)) { 04599 min = fabs(peak[k] - hi_start); 04600 minpos = k; 04601 } 04602 } 04603 if (min < 6 && fabs(peak[minpos] - xpos[n-1]) > 1.0) { 04604 xpos[n] = peak[minpos]; 04605 lambda[n] = line[ilambda[n-1] + 1]; 04606 ilambda[n] = ilambda[n-1] + 1; 04607 ++n; 04608 found = 1; 04609 } 04610 } 04611 04612 04613 /* 04614 * Try to extrapolate backward 04615 */ 04616 04617 found = 1; 04618 while (ilambda[0] > 0 && found) { 04619 04620 /* 04621 * Determine the local dispersion from the first pair of 04622 * identified peaks 04623 */ 04624 04625 disp = (lambda[1] - lambda[0]) / (xpos[1] - xpos[0]); 04626 04627 if (disp > max_disp || disp < min_disp) 04628 break; 04629 04630 04631 /* 04632 * With this, find the expected position of the missing 04633 * peak by linear interpolation. 04634 */ 04635 04636 hi_start = xpos[0] - (lambda[0] - line[ilambda[0] - 1]) / disp; 04637 04638 04639 /* 04640 * Is there a peak at that position? Here a peak from the 04641 * original list is searched, that is closer than 6 pixels 04642 * to the expected position. If it is found, insert it at 04643 * the beginning of the list of identified peaks. If it is not 04644 * found, leave the loop. 04645 */ 04646 04647 found = 0; 04648 min = fabs(peak[0] - hi_start); 04649 minpos = 0; 04650 for (k = 1; k < npeaks; k++) { 04651 if (min > fabs(peak[k] - hi_start)) { 04652 min = fabs(peak[k] - hi_start); 04653 minpos = k; 04654 } 04655 } 04656 if (min < 6 && fabs(peak[minpos] - xpos[0]) > 1.0) { 04657 for (j = n; j > 0; j--) { 04658 xpos[j] = xpos[j-1]; 04659 lambda[j] = lambda[j-1]; 04660 ilambda[j] = ilambda[j-1]; 04661 } 04662 xpos[0] = peak[minpos]; 04663 lambda[0] = line[ilambda[0] - 1]; 04664 ilambda[0] = ilambda[0] - 1; 04665 ++n; 04666 found = 1; 04667 } 04668 } 04669 } 04670 04671 04672 /* 04673 * At this point all peaks are processed. Free memory, and return 04674 * the result. 04675 */ 04676 04677 /************************************************+ 04678 for (i = 0; i < npeaks; i++) { 04679 printf("Peak %d:\n ", i); 04680 for (j = 0; j < nident[i]; j++) 04681 printf("%.2f, ", line[ident[i][j]]); 04682 printf("\n"); 04683 } 04684 04685 printf("\n"); 04686 04687 for (i = 0; i < n; i++) 04688 printf("%.2f, %.2f\n", xpos[i], lambda[i]); 04689 +************************************************/ 04690 for (i = 0; i < npeaks; i++) 04691 cpl_free(ident[i]); 04692 cpl_free(ident); 04693 cpl_free(nident); 04694 cpl_free(lident); 04695 cpl_free(ilambda); 04696 cpl_free(tmp_xpos); 04697 cpl_free(tmp_lambda); 04698 cpl_free(tmp_ilambda); 04699 cpl_free(peak_lo); 04700 cpl_free(flag); 04701 cpl_free(seq_length); 04702 cpl_free(peak_hi); 04703 04704 if (n == 0) { 04705 cpl_free(xpos); 04706 cpl_free(lambda); 04707 return NULL; 04708 } 04709 04710 return cpl_bivector_wrap_vectors(cpl_vector_wrap(n, xpos), 04711 cpl_vector_wrap(n, lambda)); 04712 } 04713 04714 04732 /* 04733 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 04734 double refwave, double pixel) 04735 { 04736 double yellow; 04737 double cpixel; 04738 double tolerance = 0.02; 04739 int max_iter = 20; 04740 int iter = 0; 04741 04742 04743 if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel) 04744 return 0.0; 04745 04746 if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel) 04747 return 0.0; 04748 04749 yellow = (blue + red) / 2; 04750 04751 cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL); 04752 04753 while (fabs(cpixel - pixel) > tolerance && iter < max_iter) { 04754 04755 if (cpixel > pixel) 04756 red = yellow; 04757 else 04758 blue = yellow; 04759 04760 yellow = (blue + red) / 2; 04761 cpixel = cpl_polynomial_eval_1d(ids, yellow-refwave, NULL); 04762 04763 iter++; 04764 04765 } 04766 04767 return yellow; 04768 04769 } 04770 */ 04771 04772 double mos_eval_dds(cpl_polynomial *ids, double blue, double red, 04773 double refwave, double pixel) 04774 { 04775 double yellow; 04776 double coeff; 04777 cpl_size zero = 0; 04778 04779 if (cpl_polynomial_eval_1d(ids, blue-refwave, NULL) > pixel) 04780 return 0.0; 04781 04782 if (cpl_polynomial_eval_1d(ids, red-refwave, NULL) < pixel) 04783 return 0.0; 04784 04785 yellow = (blue + red) / 2 - refwave; 04786 04787 coeff = cpl_polynomial_get_coeff(ids, &zero); 04788 cpl_polynomial_set_coeff(ids, &zero, coeff - pixel); 04789 04790 cpl_polynomial_solve_1d(ids, yellow, &yellow, 1); 04791 if (cpl_error_get_code() != CPL_ERROR_NONE) { 04792 cpl_error_reset(); 04793 return 0.0; 04794 } 04795 04796 cpl_polynomial_set_coeff(ids, &zero, coeff); 04797 04798 return yellow + refwave; 04799 04800 } 04801 04827 cpl_polynomial *mos_poly_wav2pix(cpl_bivector *pixwav, int order, 04828 double reject, int minlines, 04829 int *nlines, double *err) 04830 { 04831 const char *func = "mos_poly_wav2pix"; 04832 04833 cpl_bivector *pixwav2; 04834 cpl_vector *wavel; 04835 cpl_vector *pixel; 04836 double *d_wavel; 04837 double *d_pixel; 04838 double pixpos; 04839 int fitlines; 04840 int rejection = 0; 04841 int i, j; 04842 04843 cpl_polynomial *ids; 04844 04845 04846 *nlines = 0; 04847 *err = 0; 04848 04849 if (pixwav == NULL) { 04850 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 04851 return NULL; 04852 } 04853 04854 fitlines = cpl_bivector_get_size(pixwav); 04855 04856 if (fitlines < minlines) { 04857 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 04858 return NULL; 04859 } 04860 04861 04862 /* 04863 * If outliers rejection was requested, allocate a working 04864 * vector (that can be modified as soon as outliers are removed) 04865 */ 04866 04867 if (reject > 0.0) 04868 rejection = 1; 04869 04870 if (rejection) 04871 pixwav2 = cpl_bivector_duplicate(pixwav); 04872 else 04873 pixwav2 = pixwav; 04874 04875 04876 /* 04877 * The single vectors are extracted just because the fitting routine 04878 * requires it 04879 */ 04880 04881 pixel = cpl_bivector_get_x(pixwav2); 04882 wavel = cpl_bivector_get_y(pixwav2); 04883 04884 04885 /* 04886 * Get rid of the wrapper, in case of duplication 04887 */ 04888 04889 if (rejection) 04890 cpl_bivector_unwrap_vectors(pixwav2); 04891 04892 04893 /* 04894 * Here begins the iterative fit of identified lines 04895 */ 04896 04897 while (fitlines >= minlines) { 04898 04899 ids = cpl_polynomial_fit_1d_create(wavel, pixel, order, err); 04900 *err = sqrt(*err); 04901 04902 if (ids == NULL) { 04903 cpl_msg_debug(cpl_error_get_where(), cpl_error_get_message()); 04904 cpl_msg_debug(func, "Fitting IDS"); 04905 cpl_error_set_where(func); 04906 if (rejection) { 04907 cpl_vector_delete(wavel); 04908 cpl_vector_delete(pixel); 04909 } 04910 return NULL; 04911 } 04912 04913 if (rejection) { 04914 04915 04916 /* 04917 * Now work directly with the vector data buffers... 04918 */ 04919 04920 d_pixel = cpl_vector_unwrap(pixel); 04921 d_wavel = cpl_vector_unwrap(wavel); 04922 04923 for (i = 0, j = 0; i < fitlines; i++) { 04924 pixpos = cpl_polynomial_eval_1d(ids, d_wavel[i], NULL); 04925 if (fabs(pixpos - d_pixel[i]) < reject) { 04926 d_pixel[j] = d_pixel[i]; 04927 d_wavel[j] = d_wavel[i]; 04928 j++; 04929 } 04930 } 04931 04932 if (j == fitlines) { /* No rejection in last iteration */ 04933 cpl_free(d_wavel); 04934 cpl_free(d_pixel); 04935 *nlines = fitlines; 04936 return ids; 04937 } 04938 else { /* Some lines were rejected */ 04939 fitlines = j; 04940 cpl_polynomial_delete(ids); 04941 if (fitlines >= minlines) { 04942 pixel = cpl_vector_wrap(fitlines, d_pixel); 04943 wavel = cpl_vector_wrap(fitlines, d_wavel); 04944 } 04945 else { /* Too few lines: failure */ 04946 cpl_free(d_wavel); 04947 cpl_free(d_pixel); 04948 cpl_error_set(func, CPL_ERROR_CONTINUE); 04949 return NULL; 04950 } 04951 } 04952 } 04953 else { 04954 *nlines = fitlines; 04955 return ids; /* Exit at first iteration if no rejection */ 04956 } 04957 } 04958 04959 return ids; /* To avoid compiler warnings */ 04960 } 04961 04962 04987 cpl_polynomial *mos_poly_pix2wav(cpl_bivector *pixwav, int order, 04988 double reject, int minlines, 04989 int *nlines, double *err) 04990 { 04991 04992 cpl_bivector *wavpix; 04993 cpl_vector *wavel; 04994 cpl_vector *pixel; 04995 04996 cpl_polynomial *dds; 04997 04998 04999 /* 05000 * Swap vectors in bivector, in order to reuse mos_poly_wav2pix() 05001 */ 05002 05003 pixel = cpl_bivector_get_x(pixwav); 05004 wavel = cpl_bivector_get_y(pixwav); 05005 05006 wavpix = cpl_bivector_wrap_vectors(wavel, pixel); 05007 05008 dds = mos_poly_wav2pix(wavpix, order, reject, minlines, nlines, err); 05009 05010 cpl_bivector_unwrap_vectors(wavpix); 05011 05012 return dds; 05013 05014 } 05015 05016 05039 cpl_bivector *mos_find_peaks(const float *spectrum, int length, 05040 cpl_vector *lines, cpl_polynomial *ids, 05041 double refwave, int sradius) 05042 { 05043 const char *func = "mos_find_peaks"; 05044 05045 double *data; 05046 double *d_pixel; 05047 double *d_wavel; 05048 float pos; 05049 int nlines; 05050 int pixel; 05051 int i, j; 05052 05053 05054 if (spectrum == NULL || lines == NULL || ids == NULL) { 05055 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05056 return NULL; 05057 } 05058 05059 nlines = cpl_vector_get_size(lines); 05060 05061 if (sradius < 1 || length < 2*sradius+1 || nlines < 1) { 05062 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05063 return NULL; 05064 } 05065 05066 d_wavel = cpl_malloc(nlines * sizeof(double)); 05067 d_pixel = cpl_malloc(nlines * sizeof(double)); 05068 05069 data = cpl_vector_get_data(lines); 05070 05071 for (i = 0, j = 0; i < nlines; i++) { 05072 pixel = cpl_polynomial_eval_1d(ids, data[i]-refwave, NULL) + 0.5; 05073 if (pixel - sradius < 0 || pixel + sradius >= length) 05074 continue; 05075 if (0 == peakPosition(spectrum+pixel-sradius, 2*sradius+1, &pos, 1)) { 05076 pos += pixel - sradius; 05077 d_pixel[j] = pos; 05078 d_wavel[j] = data[i]; 05079 j++; 05080 } 05081 } 05082 05083 if (j > 0) { 05084 return cpl_bivector_wrap_vectors(cpl_vector_wrap(j, d_pixel), 05085 cpl_vector_wrap(j, d_wavel)); 05086 } 05087 else { 05088 cpl_free(d_wavel); 05089 cpl_free(d_pixel); 05090 cpl_error_set(func, CPL_ERROR_ILLEGAL_OUTPUT); 05091 return NULL; 05092 } 05093 } 05094 05095 05213 cpl_image *mos_wavelength_calibration_raw(const cpl_image *image, 05214 cpl_vector *lines, 05215 double dispersion, float level, 05216 int sradius, int order, 05217 double reject, double refwave, 05218 double *wavestart, double *waveend, 05219 int *nlines, double *error, 05220 cpl_table *idscoeff, 05221 cpl_image *calibration, 05222 cpl_image *residuals, 05223 cpl_table *restable, 05224 cpl_mask *refmask) 05225 { 05226 05227 const char *func = "mos_wavelength_calibration_raw"; 05228 05229 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 05230 /* Max order is 5 */ 05231 05232 double tolerance = 20.0; /* Probably forever... */ 05233 int step = 10; /* Compute restable every "step" rows */ 05234 05235 char name[MAX_COLNAME]; 05236 cpl_image *resampled; 05237 cpl_bivector *output; 05238 cpl_bivector *new_output; 05239 cpl_vector *peaks; 05240 cpl_vector *wavel; 05241 cpl_polynomial *ids; 05242 cpl_polynomial *lin; 05243 cpl_matrix *kernel; 05244 double ids_err; 05245 double max_disp, min_disp; 05246 double *line; 05247 double firstLambda, lastLambda, lambda; 05248 double value, wave, pixe; 05249 cpl_binary *mdata; 05250 const float *sdata; 05251 float *rdata; 05252 float *idata; 05253 float *ddata; 05254 float v1, v2, vi; 05255 float fpixel; 05256 int *have_it; 05257 int pixstart, pixend; 05258 int extrapolation; 05259 int nref; 05260 int nl, nx, ny, pixel; 05261 int countLines, usedLines; 05262 int uorder; 05263 int in, first, last; 05264 int width, uradius; 05265 int i, j; 05266 cpl_size k; 05267 05268 05269 if (dispersion == 0.0) { 05270 cpl_msg_error(func, "The expected dispersion (A/pixel) must be given"); 05271 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05272 return NULL; 05273 } 05274 05275 if (dispersion < 0.0) { 05276 cpl_msg_error(func, "The expected dispersion must be positive"); 05277 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05278 return NULL; 05279 } 05280 05281 max_disp = dispersion + dispersion * tolerance / 100; 05282 min_disp = dispersion - dispersion * tolerance / 100; 05283 05284 if (order < 1) { 05285 cpl_msg_error(func, "The order of the fitting polynomial " 05286 "must be at least 1"); 05287 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 05288 return NULL; 05289 } 05290 05291 if (image == NULL || lines == NULL) { 05292 cpl_msg_error(func, "Both spectral exposure and reference line " 05293 "catalog are required in input"); 05294 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05295 return NULL; 05296 } 05297 05298 nx = cpl_image_get_size_x(image); 05299 ny = cpl_image_get_size_y(image); 05300 sdata = cpl_image_get_data_float_const(image); 05301 05302 nref = cpl_vector_get_size(lines); 05303 line = cpl_vector_get_data(lines); 05304 05305 if (*wavestart < 1.0 && *waveend < 1.0) { 05306 firstLambda = line[0]; 05307 lastLambda = line[nref-1]; 05308 extrapolation = (lastLambda - firstLambda) / 10; 05309 firstLambda -= extrapolation; 05310 lastLambda += extrapolation; 05311 *wavestart = firstLambda; 05312 *waveend = lastLambda; 05313 } 05314 else { 05315 firstLambda = *wavestart; 05316 lastLambda = *waveend; 05317 } 05318 05319 nl = (lastLambda - firstLambda) / dispersion; 05320 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 05321 rdata = cpl_image_get_data_float(resampled); 05322 05323 if (calibration) 05324 idata = cpl_image_get_data_float(calibration); 05325 05326 if (residuals) 05327 ddata = cpl_image_get_data_float(residuals); 05328 05329 if (idscoeff) 05330 for (j = 0; j <= order; j++) 05331 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 05332 05333 if (restable) { 05334 cpl_table_set_size(restable, nref); 05335 cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE); 05336 cpl_table_copy_data_double(restable, "wavelength", line); 05337 for (i = 0; i < ny; i += step) { 05338 snprintf(name, MAX_COLNAME, "r%d", i); 05339 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05340 snprintf(name, MAX_COLNAME, "d%d", i); 05341 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05342 snprintf(name, MAX_COLNAME, "p%d", i); 05343 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 05344 } 05345 } 05346 05347 /* 05348 * Here is the real thing: detecting and identifying peaks, 05349 * and then fit the transformation from wavelength to pixel 05350 * and from pixel to wavelength. 05351 */ 05352 05353 for (i = 0; i < ny; i++) { 05354 width = mos_lines_width(sdata + i*nx, nx); 05355 if (sradius > 0) { 05356 if (width > sradius) { 05357 uradius = width; 05358 } 05359 else { 05360 uradius = sradius; 05361 } 05362 } 05363 if (width < 5) 05364 width = 5; 05365 peaks = mos_peak_candidates(sdata + i*nx, nx, level, width); 05366 if (peaks) { 05367 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width); 05368 } 05369 if (peaks) { 05370 output = mos_identify_peaks(peaks, lines, min_disp, max_disp, 0.05); 05371 if (output) { 05372 countLines = cpl_bivector_get_size(output); 05373 if (countLines < 4) { 05374 cpl_bivector_delete(output); 05375 cpl_vector_delete(peaks); 05376 if (nlines) 05377 nlines[i] = 0; 05378 if (error) 05379 error[i] = 0.0; 05380 continue; 05381 } 05382 05383 /* 05384 * Set reference wavelength as zero point 05385 */ 05386 05387 wavel = cpl_bivector_get_y(output); 05388 cpl_vector_subtract_scalar(wavel, refwave); 05389 05390 uorder = countLines / 2 - 1; 05391 if (uorder > order) 05392 uorder = order; 05393 05394 /* This part is now commented out. In case the first-guess iteration 05395 * was requested, the first fit was made with a lower polynomial degree: 05396 * more robust, and still accurate enough to be used as a first-guess. 05397 05398 if (sradius > 0 && uorder > 2) 05399 --uorder; 05400 05401 * End of commented part */ 05402 05403 ids = mos_poly_wav2pix(output, uorder, reject, 05404 2 * (uorder + 1), &usedLines, 05405 &ids_err); 05406 05407 if (ids == NULL) { 05408 cpl_bivector_delete(output); 05409 cpl_vector_delete(peaks); 05410 if (nlines) 05411 nlines[i] = 0; 05412 if (error) 05413 error[i] = 0.0; 05414 cpl_error_reset(); 05415 continue; 05416 } 05417 05418 if (idscoeff) { 05419 05420 /* 05421 * Write it anyway, even in case a first-guess based 05422 * solution will be searched afterwards: in case of 05423 * failure, the "blind" solution is kept. 05424 */ 05425 05426 for (k = 0; k <= order; k++) { 05427 if (k > uorder) { 05428 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 05429 } 05430 else { 05431 cpl_table_set_double(idscoeff, clab[k], i, 05432 cpl_polynomial_get_coeff(ids, &k)); 05433 } 05434 } 05435 } 05436 05437 if (sradius > 0) { 05438 05439 /* 05440 * Use ids as a first-guess 05441 */ 05442 05443 new_output = mos_find_peaks(sdata + i*nx, nx, lines, 05444 ids, refwave, uradius); 05445 05446 if (new_output) { 05447 cpl_bivector_delete(output); 05448 output = new_output; 05449 } 05450 else 05451 cpl_error_reset(); 05452 05453 05454 cpl_polynomial_delete(ids); 05455 05456 countLines = cpl_bivector_get_size(output); 05457 05458 if (countLines < 4) { 05459 cpl_bivector_delete(output); 05460 cpl_vector_delete(peaks); 05461 05462 /* 05463 * With the following code a decision is taken: 05464 * if using the first-guess gives no results, 05465 * then also the "blind" solution is rejected. 05466 */ 05467 05468 if (nlines) 05469 nlines[i] = 0; 05470 if (error) 05471 error[i] = 0.0; 05472 if (idscoeff) 05473 for (k = 0; k <= order; k++) 05474 cpl_table_set_invalid(idscoeff, clab[k], i); 05475 continue; 05476 } 05477 05478 wavel = cpl_bivector_get_y(output); 05479 cpl_vector_subtract_scalar(wavel, refwave); 05480 05481 uorder = countLines / 2 - 1; 05482 if (uorder > order) 05483 uorder = order; 05484 05485 ids = mos_poly_wav2pix(output, uorder, reject, 05486 2 * (uorder + 1), &usedLines, 05487 &ids_err); 05488 05489 if (ids == NULL) { 05490 cpl_bivector_delete(output); 05491 cpl_vector_delete(peaks); 05492 05493 /* 05494 * With the following code a decision is taken: 05495 * if using the first-guess gives no results, 05496 * then also the "blind" solution is rejected. 05497 */ 05498 05499 if (nlines) 05500 nlines[i] = 0; 05501 if (error) 05502 error[i] = 0.0; 05503 if (idscoeff) 05504 for (k = 0; k <= order; k++) 05505 cpl_table_set_invalid(idscoeff, clab[k], i); 05506 cpl_error_reset(); 05507 continue; 05508 } 05509 05510 if (idscoeff) { 05511 for (k = 0; k <= order; k++) { 05512 if (k > uorder) { 05513 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 05514 } 05515 else { 05516 cpl_table_set_double(idscoeff, clab[k], i, 05517 cpl_polynomial_get_coeff(ids, &k)); 05518 } 05519 } 05520 } 05521 05522 } /* End of "use ids as a first-guess" */ 05523 05524 if (nlines) 05525 nlines[i] = usedLines; 05526 if (error) 05527 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 05528 05529 pixstart = cpl_polynomial_eval_1d(ids, 05530 cpl_bivector_get_y_data(output)[0], NULL); 05531 pixend = cpl_polynomial_eval_1d(ids, 05532 cpl_bivector_get_y_data(output)[countLines-1], NULL); 05533 extrapolation = (pixend - pixstart) / 5; 05534 pixstart -= extrapolation; 05535 pixend += extrapolation; 05536 if (pixstart < 0) 05537 pixstart = 0; 05538 if (pixend > nx) 05539 pixend = nx; 05540 05541 /* 05542 * Wavelength calibrated image (if requested): 05543 */ 05544 05545 if (calibration) { 05546 for (j = pixstart; j < pixend; j++) { 05547 (idata + i*nx)[j] = mos_eval_dds(ids, firstLambda, 05548 lastLambda, refwave, 05549 j); 05550 } 05551 } 05552 05553 /* 05554 * Resampled image: 05555 */ 05556 05557 for (j = 0; j < nl; j++) { 05558 lambda = firstLambda + j * dispersion; 05559 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 05560 NULL); 05561 pixel = fpixel; 05562 if (pixel >= 0 && pixel < nx-1) { 05563 v1 = (sdata + i*nx)[pixel]; 05564 v2 = (sdata + i*nx)[pixel+1]; 05565 vi = v1 + (v2-v1)*(fpixel-pixel); 05566 (rdata + i*nl)[j] = vi; 05567 } 05568 } 05569 05570 /* 05571 * Residuals image 05572 */ 05573 05574 if (residuals || (restable && !(i%step))) { 05575 if (restable && !(i%step)) { 05576 lin = cpl_polynomial_new(1); 05577 for (k = 0; k < 2; k++) 05578 cpl_polynomial_set_coeff(lin, &k, 05579 cpl_polynomial_get_coeff(ids, &k)); 05580 } 05581 for (j = 0; j < countLines; j++) { 05582 pixe = cpl_bivector_get_x_data(output)[j]; 05583 wave = cpl_bivector_get_y_data(output)[j]; 05584 value = pixe - cpl_polynomial_eval_1d(ids, wave, NULL); 05585 if (residuals) { 05586 pixel = pixe + 0.5; 05587 (ddata + i*nx)[pixel] = value; 05588 } 05589 if (restable && !(i%step)) { 05590 for (k = 0; k < nref; k++) { 05591 if (fabs(line[k] - refwave - wave) < 0.1) { 05592 snprintf(name, MAX_COLNAME, "r%d", i); 05593 cpl_table_set_double(restable, name, 05594 k, value); 05595 value = pixe 05596 - cpl_polynomial_eval_1d(lin, wave, 05597 NULL); 05598 snprintf(name, MAX_COLNAME, "d%d", i); 05599 cpl_table_set_double(restable, name, 05600 k, value); 05601 snprintf(name, MAX_COLNAME, "p%d", i); 05602 cpl_table_set_double(restable, name, 05603 k, pixe); 05604 break; 05605 } 05606 } 05607 } 05608 } 05609 if (restable && !(i%step)) { 05610 cpl_polynomial_delete(lin); 05611 } 05612 } 05613 05614 /* 05615 * Mask at reference wavelength 05616 */ 05617 05618 if (refmask) { 05619 mdata = cpl_mask_get_data(refmask); 05620 pixel = cpl_polynomial_eval_1d(ids, 0.0, NULL) + 0.5; 05621 if (pixel - 1 >= 0 && pixel + 1 < nx) { 05622 mdata[pixel-1 + i*nx] = CPL_BINARY_1; 05623 mdata[pixel + i*nx] = CPL_BINARY_1; 05624 mdata[pixel+1 + i*nx] = CPL_BINARY_1; 05625 } 05626 } 05627 05628 cpl_polynomial_delete(ids); 05629 cpl_bivector_delete(output); 05630 } 05631 cpl_vector_delete(peaks); 05632 } 05633 } 05634 05635 if (refmask) { 05636 kernel = cpl_matrix_new(3, 3); 05637 cpl_matrix_set(kernel, 0, 1, 1.0); 05638 cpl_matrix_set(kernel, 1, 1, 1.0); 05639 cpl_matrix_set(kernel, 2, 1, 1.0); 05640 05641 cpl_mask_dilation(refmask, kernel); 05642 cpl_mask_erosion(refmask, kernel); 05643 cpl_mask_erosion(refmask, kernel); 05644 cpl_mask_dilation(refmask, kernel); 05645 05646 cpl_matrix_delete(kernel); 05647 05648 /* 05649 * Fill possible gaps 05650 */ 05651 05652 mdata = cpl_mask_get_data(refmask); 05653 have_it = cpl_calloc(ny, sizeof(int)); 05654 05655 for (i = 0; i < ny; i++, mdata += nx) { 05656 for (j = 0; j < nx; j++) { 05657 if (mdata[j] == CPL_BINARY_1) { 05658 have_it[i] = j; 05659 break; 05660 } 05661 } 05662 } 05663 05664 mdata = cpl_mask_get_data(refmask); 05665 in = 0; 05666 first = last = 0; 05667 05668 for (i = 0; i < ny; i++) { 05669 if (have_it[i]) { 05670 if (!in) { 05671 in = 1; 05672 if (first) { 05673 last = i; 05674 if (abs(have_it[first] - have_it[last]) < 3) { 05675 for (j = first; j < last; j++) { 05676 mdata[have_it[first] + nx*j + 0] = CPL_BINARY_1; 05677 mdata[have_it[first] + nx*j + 1] = CPL_BINARY_1; 05678 mdata[have_it[first] + nx*j + 2] = CPL_BINARY_1; 05679 } 05680 } 05681 } 05682 } 05683 } 05684 else { 05685 if (in) { 05686 in = 0; 05687 first = i - 1; 05688 } 05689 } 05690 } 05691 05692 cpl_free(have_it); 05693 05694 } 05695 05696 /* 05697 for (i = 0; i < ny; i++) { 05698 if (nlines[i] == 0) { 05699 for (k = 0; k <= order; k++) { 05700 cpl_table_set_invalid(idscoeff, clab[k], i); 05701 } 05702 } 05703 } 05704 */ 05705 05706 return resampled; 05707 } 05708 05709 05731 /* 05732 cpl_table *mos_locate_spectra_bis(cpl_mask *mask) 05733 { 05734 const char *func = "mos_locate_spectra_bis"; 05735 05736 cpl_apertures *slits; 05737 cpl_image *labimage; 05738 cpl_image *refimage; 05739 cpl_binary *mdata; 05740 cpl_table *slitpos; 05741 cpl_propertylist *sort_col; 05742 int nslits; 05743 int *have_it; 05744 int in, first, last; 05745 int i, j; 05746 05747 05748 if (mask == NULL) { 05749 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05750 return NULL; 05751 } 05752 05753 nx = cpl_mask_get_size_x(mask); 05754 ny = cpl_mask_get_size_y(mask); 05755 05756 mdata = cpl_mask_get_data(refmask); 05757 have_it = cpl_calloc(ny, sizeof(int)); 05758 05759 for (i = 0; i < ny; i++, mdata += nx) { 05760 for (j = 0; j < nx; j++) { 05761 if (mdata[j] == CPL_BINARY_1) { 05762 have_it[i] = j + 1; 05763 break; 05764 } 05765 } 05766 } 05767 05768 mdata = cpl_mask_get_data(refmask); 05769 in = 0; 05770 first = last = 0; 05771 nslits = 0; 05772 05773 for (i = 0; i < ny; i++) { 05774 if (have_it[i]) { 05775 if (in) { 05776 if (i) { 05777 if (abs(have_it[i] - have_it[i-1]) > 3) { 05778 nslits++; 05779 } 05780 } 05781 } 05782 else { 05783 in = 1; 05784 nslits++; 05785 } 05786 } 05787 else { 05788 if (in) { 05789 in = 0; 05790 } 05791 } 05792 } 05793 } 05794 */ 05795 05796 05818 cpl_table *mos_locate_spectra(cpl_mask *mask) 05819 { 05820 const char *func = "mos_locate_spectra"; 05821 05822 cpl_apertures *slits; 05823 cpl_image *labimage; 05824 cpl_image *refimage; 05825 cpl_table *slitpos; 05826 cpl_propertylist *sort_col; 05827 cpl_size nslits; 05828 int i; 05829 05830 05831 if (mask == NULL) { 05832 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05833 return NULL; 05834 } 05835 05836 labimage = cpl_image_labelise_mask_create(mask, &nslits); 05837 05838 if (nslits < 1) { 05839 cpl_image_delete(labimage); 05840 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05841 return NULL; 05842 } 05843 05844 refimage = cpl_image_new_from_mask(mask); 05845 05846 slits = cpl_apertures_new_from_image(refimage, labimage); 05847 05848 cpl_image_delete(labimage); 05849 cpl_image_delete(refimage); 05850 05851 nslits = cpl_apertures_get_size(slits); /* Overwriting nslits - safer! */ 05852 if (nslits < 1) { 05853 cpl_apertures_delete(slits); 05854 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05855 return NULL; 05856 } 05857 05858 slitpos = cpl_table_new(nslits); 05859 cpl_table_new_column(slitpos, "xtop", CPL_TYPE_DOUBLE); 05860 cpl_table_new_column(slitpos, "ytop", CPL_TYPE_DOUBLE); 05861 cpl_table_new_column(slitpos, "xbottom", CPL_TYPE_DOUBLE); 05862 cpl_table_new_column(slitpos, "ybottom", CPL_TYPE_DOUBLE); 05863 cpl_table_set_column_unit(slitpos, "xtop", "pixel"); 05864 cpl_table_set_column_unit(slitpos, "ytop", "pixel"); 05865 cpl_table_set_column_unit(slitpos, "xbottom", "pixel"); 05866 cpl_table_set_column_unit(slitpos, "ybottom", "pixel"); 05867 05868 for (i = 0; i < nslits; i++) { 05869 cpl_table_set_double(slitpos, "xtop", i, 05870 cpl_apertures_get_top_x(slits, i+1) - 1); 05871 cpl_table_set_double(slitpos, "ytop", i, 05872 cpl_apertures_get_top(slits, i+1)); 05873 cpl_table_set_double(slitpos, "xbottom", i, 05874 cpl_apertures_get_bottom_x(slits, i+1) - 1); 05875 cpl_table_set_double(slitpos, "ybottom", i, 05876 cpl_apertures_get_bottom(slits, i+1)); 05877 } 05878 05879 cpl_apertures_delete(slits); 05880 05881 sort_col = cpl_propertylist_new(); 05882 cpl_propertylist_append_bool(sort_col, "ytop", 1); 05883 cpl_table_sort(slitpos, sort_col); 05884 cpl_propertylist_delete(sort_col); 05885 05886 return slitpos; 05887 05888 } 05889 05890 05906 cpl_error_code mos_validate_slits(cpl_table *slits) 05907 { 05908 const char *func = "mos_validate_slits"; 05909 05910 05911 if (slits == NULL) 05912 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 05913 05914 if (1 != cpl_table_has_column(slits, "xtop")) 05915 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05916 05917 if (1 != cpl_table_has_column(slits, "ytop")) 05918 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05919 05920 if (1 != cpl_table_has_column(slits, "xbottom")) 05921 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05922 05923 if (1 != cpl_table_has_column(slits, "ybottom")) 05924 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 05925 05926 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xtop")) 05927 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05928 05929 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ytop")) 05930 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05931 05932 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "xbottom")) 05933 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05934 05935 if (CPL_TYPE_DOUBLE != cpl_table_get_column_type(slits, "ybottom")) 05936 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 05937 05938 return CPL_ERROR_NONE; 05939 } 05940 05941 05970 cpl_error_code mos_rotate_slits(cpl_table *slits, int rotation, int nx, int ny) 05971 { 05972 const char *func = "mos_rotate_slits"; 05973 05974 cpl_error_code error; 05975 char aux_name[] = "_0"; 05976 int i; 05977 05978 05979 rotation %= 4; 05980 if (rotation < 0) 05981 rotation += 4; 05982 05983 if (rotation == 0) 05984 return CPL_ERROR_NONE; 05985 05986 error = mos_validate_slits(slits); 05987 if (error) 05988 return cpl_error_set(func, error); 05989 05990 if (rotation == 1 || rotation == 3) { 05991 05992 /* 05993 * Swap x and y column names 05994 */ 05995 05996 for (i = 0; i < 77; i++) 05997 if (1 == cpl_table_has_column(slits, aux_name)) 05998 aux_name[1]++; 05999 if (1 == cpl_table_has_column(slits, aux_name)) 06000 return cpl_error_set(func, CPL_ERROR_CONTINUE); 06001 cpl_table_name_column(slits, "xtop", aux_name); 06002 cpl_table_name_column(slits, "ytop", "xtop"); 06003 cpl_table_name_column(slits, aux_name, "ytop"); 06004 cpl_table_name_column(slits, "xbottom", aux_name); 06005 cpl_table_name_column(slits, "ybottom", "xbottom"); 06006 cpl_table_name_column(slits, aux_name, "ybottom"); 06007 } 06008 06009 if (rotation == 1 || rotation == 2) { 06010 cpl_table_multiply_scalar(slits, "xtop", -1.0); 06011 cpl_table_multiply_scalar(slits, "xbottom", -1.0); 06012 cpl_table_add_scalar(slits, "xtop", nx); 06013 cpl_table_add_scalar(slits, "xbottom", nx); 06014 } 06015 06016 if (rotation == 3 || rotation == 2) { 06017 cpl_table_multiply_scalar(slits, "ytop", -1.0); 06018 cpl_table_multiply_scalar(slits, "ybottom", -1.0); 06019 cpl_table_add_scalar(slits, "ytop", ny); 06020 cpl_table_add_scalar(slits, "ybottom", ny); 06021 } 06022 06023 return CPL_ERROR_NONE; 06024 } 06025 06026 06084 cpl_table *mos_identify_slits(cpl_table *slits, cpl_table *maskslits, 06085 cpl_table *global) 06086 { 06087 cpl_array *top_ident = NULL;; 06088 cpl_array *bot_ident = NULL;; 06089 cpl_matrix *mdata; 06090 cpl_matrix *mpattern; 06091 cpl_matrix *top_data; 06092 cpl_matrix *top_pattern; 06093 cpl_matrix *top_mdata; 06094 cpl_matrix *top_mpattern; 06095 cpl_matrix *bot_data; 06096 cpl_matrix *bot_pattern; 06097 cpl_matrix *bot_mdata; 06098 cpl_matrix *bot_mpattern; 06099 cpl_propertylist *sort_col; 06100 double *xtop; 06101 double *ytop; 06102 double *xmtop; 06103 double *ymtop; 06104 double *xbot; 06105 double *ybot; 06106 double *xmbot; 06107 double *ymbot; 06108 double top_scale, bot_scale; 06109 double angle, top_angle, bot_angle; 06110 double xmse, ymse; 06111 double xrms, top_xrms, bot_xrms; 06112 double yrms, top_yrms, bot_yrms; 06113 int nslits; 06114 int nmaskslits, use_pattern; 06115 int found_slits, found_slits_top, found_slits_bot; 06116 int degree; 06117 int i; 06118 cpl_table *positions; 06119 cpl_error_code error; 06120 06121 cpl_vector *point; 06122 double *dpoint; 06123 cpl_vector *xpos; 06124 cpl_vector *ypos; 06125 cpl_vector *xmpos; 06126 cpl_vector *ympos; 06127 cpl_bivector *mpos; 06128 cpl_polynomial *xpoly = NULL; 06129 cpl_polynomial *ypoly = NULL; 06130 cpl_polynomial *top_xpoly = NULL; 06131 cpl_polynomial *top_ypoly = NULL; 06132 cpl_polynomial *bot_xpoly = NULL; 06133 cpl_polynomial *bot_ypoly = NULL; 06134 06135 char *msg_multiplex = " "; 06136 06137 06138 error = mos_validate_slits(slits); 06139 if (error) { 06140 cpl_msg_error(cpl_func, "CCD slits table validation: %s", 06141 cpl_error_get_message()); 06142 cpl_error_set(cpl_func, error); 06143 return NULL; 06144 } 06145 06146 error = mos_validate_slits(maskslits); 06147 if (error) { 06148 cpl_msg_error(cpl_func, "Mask slits table validation: %s", 06149 cpl_error_get_message()); 06150 cpl_error_set(cpl_func, error); 06151 return NULL; 06152 } 06153 06154 if (1 != cpl_table_has_column(maskslits, "slit_id")) { 06155 cpl_msg_error(cpl_func, "Missing slits identifiers"); 06156 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 06157 return NULL; 06158 } 06159 06160 if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) { 06161 cpl_msg_error(cpl_func, "Wrong type used for slits identifiers"); 06162 cpl_error_set(cpl_func, CPL_ERROR_INVALID_TYPE); 06163 return NULL; 06164 } 06165 06166 nslits = cpl_table_get_nrow(slits); 06167 nmaskslits = cpl_table_get_nrow(maskslits); 06168 06169 if (nslits == 0 || nmaskslits == 0) { 06170 cpl_msg_error(cpl_func, "Empty slits table"); 06171 cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT); 06172 return NULL; 06173 } 06174 06175 if (nslits > 25 && mos_multiplex < 0) { 06176 cpl_msg_info(cpl_func, "Many slits: using 'fast' pattern matching..."); 06177 positions = mos_identify_slits_fast(slits, maskslits, global); 06178 if (positions == NULL) 06179 cpl_error_set_where(cpl_func); 06180 return positions; 06181 } 06182 06183 /* 06184 * Guarantee that both input tables are sorted in the same way 06185 */ 06186 06187 sort_col = cpl_propertylist_new(); 06188 cpl_propertylist_append_bool(sort_col, "ytop", 1); 06189 cpl_table_sort(slits, sort_col); 06190 cpl_table_sort(maskslits, sort_col); 06191 cpl_propertylist_delete(sort_col); 06192 06193 /* 06194 * First we handle all the special cases (too few slits...) 06195 */ 06196 06197 if (nslits < 3 && nmaskslits > nslits) { 06198 06199 /* 06200 * If there are just 1 or 2 slits on the CCD, and more on the 06201 * mask, the ambiguity cannot be solved, and an error is returned. 06202 * This is a case that must be solved with a first-guess relation 06203 * between mask and CCD. 06204 */ 06205 06206 if (nslits > 1) 06207 cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits " 06208 "with the %d mask slits: process will continue " 06209 "using the detected CCD slits positions", nslits, 06210 nmaskslits); 06211 else 06212 cpl_msg_warning(cpl_func, "Cannot match the found CCD slit with " 06213 "the %d mask slits: process will continue using " 06214 "the detected CCD slit position", nmaskslits); 06215 return NULL; 06216 } 06217 06218 if (nmaskslits < 3 && nslits > nmaskslits) { 06219 06220 /* 06221 * If there are less than 3 slits on the mask the ambiguity cannot 06222 * be solved, and an error is returned. This is a case that must 06223 * be solved with a first-guess relation between mask and CCD. 06224 */ 06225 06226 cpl_msg_warning(cpl_func, "Cannot match the %d found CCD slits with " 06227 "the %d mask slits: process will continue using " 06228 "the detected CCD slits positions", nslits, 06229 nmaskslits); 06230 return NULL; 06231 } 06232 06233 /* 06234 * Pattern matching related operations begin here. Two pattern 06235 * matching will be run, one based on the "top" and another one 06236 * based on the "bottom" slit coordinates. The one with the 06237 * smallest rms in the Y coordinate will be chosen. 06238 */ 06239 06240 xtop = cpl_table_get_data_double(slits, "xtop"); 06241 ytop = cpl_table_get_data_double(slits, "ytop"); 06242 xmtop = cpl_table_get_data_double(maskslits, "xtop"); 06243 ymtop = cpl_table_get_data_double(maskslits, "ytop"); 06244 06245 xbot = cpl_table_get_data_double(slits, "xbottom"); 06246 ybot = cpl_table_get_data_double(slits, "ybottom"); 06247 xmbot = cpl_table_get_data_double(maskslits, "xbottom"); 06248 ymbot = cpl_table_get_data_double(maskslits, "ybottom"); 06249 06250 top_data = cpl_matrix_new(2, nslits); 06251 top_pattern = cpl_matrix_new(2, nmaskslits); 06252 bot_data = cpl_matrix_new(2, nslits); 06253 bot_pattern = cpl_matrix_new(2, nmaskslits); 06254 06255 for (i = 0; i < nslits; i++) 06256 cpl_matrix_set(top_data, 0, i, xtop[i]); 06257 06258 for (i = 0; i < nslits; i++) 06259 cpl_matrix_set(top_data, 1, i, ytop[i]); 06260 06261 for (i = 0; i < nmaskslits; i++) 06262 cpl_matrix_set(top_pattern, 0, i, xmtop[i]); 06263 06264 for (i = 0; i < nmaskslits; i++) 06265 cpl_matrix_set(top_pattern, 1, i, ymtop[i]); 06266 06267 for (i = 0; i < nslits; i++) 06268 cpl_matrix_set(bot_data, 0, i, xbot[i]); 06269 06270 for (i = 0; i < nslits; i++) 06271 cpl_matrix_set(bot_data, 1, i, ybot[i]); 06272 06273 for (i = 0; i < nmaskslits; i++) 06274 cpl_matrix_set(bot_pattern, 0, i, xmbot[i]); 06275 06276 for (i = 0; i < nmaskslits; i++) 06277 cpl_matrix_set(bot_pattern, 1, i, ymbot[i]); 06278 06279 if (nmaskslits > nslits) 06280 use_pattern = nslits; 06281 else 06282 use_pattern = nmaskslits; 06283 06284 top_ident = cpl_ppm_match_points(top_data, nslits, 1.0, top_pattern, 06285 use_pattern, 0.0, 0.1, 5, &top_mdata, 06286 &top_mpattern, &top_scale, &top_angle); 06287 06288 bot_ident = cpl_ppm_match_points(bot_data, nslits, 1.0, bot_pattern, 06289 use_pattern, 0.0, 0.1, 5, &bot_mdata, 06290 &bot_mpattern, &bot_scale, &bot_angle); 06291 06292 if (top_ident == NULL && bot_ident == NULL) { 06293 cpl_msg_warning(cpl_func, "Pattern matching failure: cannot match " 06294 "the %d found CCD slits with the %d mask slits: " 06295 "process will continue using the detected CCD " 06296 "slits positions", nslits, nmaskslits); 06297 return NULL; 06298 } 06299 06300 found_slits_top = 0; 06301 found_slits_bot = 0; 06302 if (top_ident && bot_ident) { 06303 cpl_msg_info(cpl_func, "Median platescale: %f +/- %f pixel/mm", 06304 (top_scale + bot_scale) / 2, fabs(top_scale - bot_scale)); 06305 cpl_msg_info(cpl_func, "Median rotation: %f +/- %f degrees", 06306 (top_angle + bot_angle) / 2, fabs(top_angle - bot_angle)); 06307 if (fabs(top_angle) < fabs(bot_angle)) 06308 angle = fabs(top_angle); 06309 else 06310 angle = fabs(bot_angle); 06311 found_slits_top = cpl_matrix_get_ncol(top_mdata); 06312 found_slits_bot = cpl_matrix_get_ncol(bot_mdata); 06313 } 06314 else if (top_ident) { 06315 cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", top_scale); 06316 cpl_msg_info(cpl_func, "Median rotation: %f degrees", top_angle); 06317 angle = fabs(top_angle); 06318 found_slits_top = cpl_matrix_get_ncol(top_mdata); 06319 } 06320 else { 06321 cpl_msg_info(cpl_func, "Median platescale: %f pixel/mm", bot_scale); 06322 cpl_msg_info(cpl_func, "Median rotation: %f degrees", bot_angle); 06323 angle = fabs(bot_angle); 06324 found_slits_bot = cpl_matrix_get_ncol(bot_mdata); 06325 } 06326 06327 cpl_array_delete(top_ident); 06328 cpl_array_delete(bot_ident); 06329 06330 if (angle > 4.0) { 06331 cpl_msg_warning(cpl_func, "Uncertain pattern matching: the rotation " 06332 "angle is expected to be around zero. This match is " 06333 "rejected: the process will continue using the %d " 06334 "detected CCD slits positions", nslits); 06335 return NULL; 06336 } 06337 06338 found_slits = found_slits_top; 06339 if (found_slits < found_slits_bot) 06340 found_slits = found_slits_bot; /* Max value */ 06341 06342 if (found_slits < 4) { 06343 cpl_msg_warning(cpl_func, 06344 "Too few safely identified slits: %d out of %d " 06345 "candidates (%d expected). Process will continue " 06346 "using the detected CCD slits positions", found_slits, 06347 nslits, nmaskslits); 06348 return NULL; 06349 } 06350 06351 cpl_msg_info(cpl_func, "Preliminary identified slits: %d out of %d " 06352 "candidates\n(%d expected)", found_slits, nslits, 06353 nmaskslits); 06354 06355 if (found_slits_top < 4) 06356 found_slits_top = 0; 06357 06358 if (found_slits_bot < 4) 06359 found_slits_bot = 0; 06360 06361 /* 06362 * Now for each set select the points of the identified slits, and fit 06363 * two bivariate polynomials to determine a first approximate relation 06364 * between positions on the mask and positions on the CCD. 06365 */ 06366 06367 for (i = 0; i < 2; i++) { 06368 if (i) { 06369 found_slits = found_slits_top; 06370 mdata = top_mdata; 06371 mpattern = top_mpattern; 06372 } 06373 else { 06374 found_slits = found_slits_bot; 06375 mdata = bot_mdata; 06376 mpattern = bot_mpattern; 06377 } 06378 06379 if (found_slits == 0) 06380 continue; 06381 else if (found_slits < 10) 06382 degree = 1; 06383 else 06384 degree = 2; 06385 06386 xpos = cpl_vector_wrap(found_slits, 06387 cpl_matrix_get_data(mdata) ); 06388 ypos = cpl_vector_wrap(found_slits, 06389 cpl_matrix_get_data(mdata) + found_slits); 06390 xmpos = cpl_vector_wrap(found_slits, 06391 cpl_matrix_get_data(mpattern) ); 06392 ympos = cpl_vector_wrap(found_slits, 06393 cpl_matrix_get_data(mpattern) + found_slits); 06394 mpos = cpl_bivector_wrap_vectors(xmpos, ympos); 06395 xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse); 06396 ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse); 06397 06398 cpl_bivector_unwrap_vectors(mpos); 06399 cpl_vector_unwrap(xpos); 06400 cpl_vector_unwrap(ypos); 06401 cpl_vector_unwrap(xmpos); 06402 cpl_vector_unwrap(ympos); 06403 cpl_matrix_delete(mdata); 06404 cpl_matrix_delete(mpattern); 06405 06406 if (i) { 06407 top_xpoly = xpoly; 06408 top_ypoly = ypoly; 06409 top_xrms = sqrt(xmse*2*degree/(found_slits - 1)); 06410 top_yrms = sqrt(ymse*2*degree/(found_slits - 1)); 06411 } 06412 else { 06413 bot_xpoly = xpoly; 06414 bot_ypoly = ypoly; 06415 bot_xrms = sqrt(xmse*2*degree/(found_slits - 1)); 06416 bot_yrms = sqrt(ymse*2*degree/(found_slits - 1)); 06417 } 06418 } 06419 06420 if (top_xpoly && bot_xpoly) { 06421 if (top_xrms < bot_xrms) { /* top X solution wins... */ 06422 xrms = top_xrms; 06423 xpoly = top_xpoly; 06424 cpl_polynomial_delete(bot_xpoly); 06425 } 06426 else { /* bottom X solution wins... */ 06427 xrms = bot_xrms; 06428 xpoly = bot_xpoly; 06429 cpl_polynomial_delete(top_xpoly); 06430 } 06431 } 06432 else if (top_xpoly) { 06433 xrms = top_xrms; 06434 xpoly = top_xpoly; 06435 } 06436 else { 06437 xrms = bot_xrms; 06438 xpoly = bot_xpoly; 06439 } 06440 06441 if (top_ypoly && bot_ypoly) { 06442 if (top_yrms < bot_yrms) { /* top Y solution wins... */ 06443 yrms = top_yrms; 06444 ypoly = top_ypoly; 06445 cpl_polynomial_delete(bot_ypoly); 06446 } 06447 else { /* bottom Y solution wins... */ 06448 yrms = bot_yrms; 06449 ypoly = bot_ypoly; 06450 cpl_polynomial_delete(top_ypoly); 06451 } 06452 } 06453 else if (top_ypoly) { 06454 yrms = top_yrms; 06455 ypoly = top_ypoly; 06456 } 06457 else { 06458 yrms = bot_yrms; 06459 ypoly = bot_ypoly; 06460 } 06461 06462 if (xpoly == NULL || ypoly == NULL) { 06463 cpl_msg_warning(cpl_func, "Fit failure: the accuracy of the " 06464 "identified slits positions cannot be improved."); 06465 cpl_polynomial_delete(xpoly); 06466 cpl_polynomial_delete(ypoly); 06467 cpl_error_reset(); 06468 return NULL; 06469 } 06470 06471 cpl_msg_info(cpl_func, 06472 "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 06473 xrms, yrms); 06474 06475 if (global) { 06476 write_global_distortion(global, 0, xpoly); 06477 write_global_distortion(global, 7, ypoly); 06478 } 06479 06480 /* 06481 * The fit was successful: use the polynomials to obtain a new 06482 * position table with the improved positions of the slits 06483 */ 06484 06485 positions = cpl_table_duplicate(maskslits); 06486 cpl_table_duplicate_column(positions, "xmtop", positions, "xtop"); 06487 cpl_table_duplicate_column(positions, "ymtop", positions, "ytop"); 06488 cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom"); 06489 cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom"); 06490 06491 point = cpl_vector_new(2); 06492 dpoint = cpl_vector_get_data(point); 06493 06494 for (i = 0; i < nmaskslits; i++) { 06495 double position; 06496 06497 dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL); 06498 dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL); 06499 position = cpl_polynomial_eval(xpoly, point); 06500 // if (mos_multiplex >= 0) { 06501 // if (mos_multiplex != ((int)floor(position)) / mos_region_size) { 06502 // cpl_table_unselect_row(positions, i); 06503 // continue; 06504 // } 06505 // } 06506 cpl_table_set_double(positions, "xtop", i, position); 06507 position = cpl_polynomial_eval(ypoly, point); 06508 cpl_table_set_double(positions, "ytop", i, position); 06509 dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL); 06510 dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL); 06511 position = cpl_polynomial_eval(xpoly, point); 06512 cpl_table_set_double(positions, "xbottom", i, position); 06513 position = cpl_polynomial_eval(ypoly, point); 06514 cpl_table_set_double(positions, "ybottom", i, position); 06515 } 06516 06517 // if (mos_multiplex >= 0) { 06518 // cpl_table_not_selected(positions); 06519 // cpl_table_erase_selected(positions); 06520 // nmaskslits = cpl_table_get_nrow(positions); 06521 // } 06522 06523 cpl_vector_delete(point); 06524 cpl_polynomial_delete(xpoly); 06525 cpl_polynomial_delete(ypoly); 06526 06527 cpl_table_erase_column(positions, "xmtop"); 06528 cpl_table_erase_column(positions, "ymtop"); 06529 cpl_table_erase_column(positions, "xmbottom"); 06530 cpl_table_erase_column(positions, "ymbottom"); 06531 06532 if (mos_multiplex >= 0) { 06533 msg_multiplex = 06534 cpl_sprintf("in the CCD section between %d and %d pixel", 06535 mos_multiplex * mos_region_size, 06536 (mos_multiplex + 1) * mos_region_size); 06537 } 06538 06539 if (nmaskslits > nslits) 06540 cpl_msg_info(cpl_func, 06541 "Finally identified slits: %d out of %d expected %s\n" 06542 "(%d recovered)", nmaskslits, nmaskslits, msg_multiplex, 06543 nmaskslits - nslits); 06544 else if (nmaskslits < nslits) 06545 cpl_msg_info(cpl_func, 06546 "Finally identified slits: %d out of %d expected %s\n" 06547 "(%d rejected)", nmaskslits, nmaskslits, msg_multiplex, 06548 nslits - nmaskslits); 06549 else 06550 cpl_msg_info(cpl_func, 06551 "Finally identified slits: %d out of %d expected %s", 06552 nmaskslits, nmaskslits, msg_multiplex); 06553 06554 if (mos_multiplex >= 0) { 06555 cpl_free(msg_multiplex); 06556 } 06557 06558 return positions; 06559 06560 } 06561 06562 06563 cpl_table *mos_identify_slits_fast(cpl_table *slits, cpl_table *maskslits, 06564 cpl_table *global) 06565 { 06566 const char *func = "mos_identify_slits_fast"; 06567 06568 cpl_propertylist *sort_col; 06569 cpl_table *positions; 06570 cpl_vector *scales; 06571 cpl_vector *angles; 06572 cpl_vector *point; 06573 cpl_vector *xpos; 06574 cpl_vector *ypos; 06575 cpl_vector *xmpos; 06576 cpl_vector *ympos; 06577 cpl_bivector *mpos; 06578 cpl_polynomial *xpoly = NULL; 06579 cpl_polynomial *ypoly = NULL; 06580 cpl_error_code error; 06581 int nslits; 06582 int nmaskslits; 06583 int found_slits; 06584 int i, j, k; 06585 06586 double dist1, dist2, dist3, dist, mindist; 06587 double scale, minscale, maxscale; 06588 double angle, minangle, maxangle; 06589 double *dscale; 06590 double *dangle; 06591 double *dpoint; 06592 double *xtop; 06593 double *ytop; 06594 double *xbottom; 06595 double *ybottom; 06596 double *xcenter; 06597 double *ycenter; 06598 double *xpseudo; 06599 double *ypseudo; 06600 int *slit_id; 06601 double *xmtop; 06602 double *ymtop; 06603 double *xmbottom; 06604 double *ymbottom; 06605 double *xmcenter; 06606 double *ymcenter; 06607 double *xmpseudo; 06608 double *ympseudo; 06609 double xmse, ymse; 06610 int *mslit_id; 06611 int *good; 06612 int minpos; 06613 int degree; 06614 06615 double sradius = 0.01; /* Candidate input argument... */ 06616 int in_sradius; 06617 06618 double pi = 3.14159265358979323846; 06619 06620 06621 error = mos_validate_slits(slits); 06622 if (error) { 06623 cpl_msg_error(func, "CCD slits table validation: %s", 06624 cpl_error_get_message()); 06625 cpl_error_set(func, error); 06626 return NULL; 06627 } 06628 06629 error = mos_validate_slits(maskslits); 06630 if (error) { 06631 cpl_msg_error(func, "Mask slits table validation: %s", 06632 cpl_error_get_message()); 06633 cpl_error_set(func, error); 06634 return NULL; 06635 } 06636 06637 if (1 != cpl_table_has_column(maskslits, "slit_id")) { 06638 cpl_msg_error(func, "Missing slits identifiers"); 06639 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 06640 return NULL; 06641 } 06642 06643 if (CPL_TYPE_INT != cpl_table_get_column_type(maskslits, "slit_id")) { 06644 cpl_msg_error(func, "Wrong type used for slits identifiers"); 06645 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 06646 return NULL; 06647 } 06648 06649 nslits = cpl_table_get_nrow(slits); 06650 nmaskslits = cpl_table_get_nrow(maskslits); 06651 06652 if (nslits == 0 || nmaskslits == 0) { 06653 cpl_msg_error(func, "Empty slits table"); 06654 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 06655 return NULL; 06656 } 06657 06658 06659 /* 06660 * Compute middle point coordinates for each slit listed in both 06661 * input tables. 06662 */ 06663 06664 if (cpl_table_has_column(slits, "xcenter")) 06665 cpl_table_erase_column(slits, "xcenter"); 06666 06667 if (cpl_table_has_column(slits, "ycenter")) 06668 cpl_table_erase_column(slits, "ycenter"); 06669 06670 if (cpl_table_has_column(maskslits, "xcenter")) 06671 cpl_table_erase_column(maskslits, "xcenter"); 06672 06673 if (cpl_table_has_column(maskslits, "ycenter")) 06674 cpl_table_erase_column(maskslits, "ycenter"); 06675 06676 cpl_table_duplicate_column(slits, "xcenter", slits, "xtop"); 06677 cpl_table_add_columns(slits, "xcenter", "xbottom"); 06678 cpl_table_divide_scalar(slits, "xcenter", 2.0); 06679 cpl_table_duplicate_column(slits, "ycenter", slits, "ytop"); 06680 cpl_table_add_columns(slits, "ycenter", "ybottom"); 06681 cpl_table_divide_scalar(slits, "ycenter", 2.0); 06682 06683 cpl_table_duplicate_column(maskslits, "xcenter", maskslits, "xtop"); 06684 cpl_table_add_columns(maskslits, "xcenter", "xbottom"); 06685 cpl_table_divide_scalar(maskslits, "xcenter", 2.0); 06686 cpl_table_duplicate_column(maskslits, "ycenter", maskslits, "ytop"); 06687 cpl_table_add_columns(maskslits, "ycenter", "ybottom"); 06688 cpl_table_divide_scalar(maskslits, "ycenter", 2.0); 06689 06690 06691 /* 06692 * Guarantee that both input tables are sorted in the same way 06693 */ 06694 06695 sort_col = cpl_propertylist_new(); 06696 cpl_propertylist_append_bool(sort_col, "ycenter", 1); 06697 cpl_table_sort(slits, sort_col); 06698 cpl_table_sort(maskslits, sort_col); 06699 cpl_propertylist_delete(sort_col); 06700 06701 06702 /* 06703 * First we handle all the special cases (too few slits...) 06704 */ 06705 06706 if (nslits < 3 && nmaskslits > nslits) { 06707 06708 /* 06709 * If there are just 1 or 2 slits on the CCD, and more on the 06710 * mask, the ambiguity cannot be solved, and an error is returned. 06711 * This is a case that must be solved with a first-guess relation 06712 * between mask and CCD. 06713 */ 06714 06715 if (nslits > 1) 06716 cpl_msg_warning(func, "Cannot match the found CCD slit with the " 06717 "%d mask slits: process will continue using the " 06718 "detected CCD slit position", nmaskslits); 06719 else 06720 cpl_msg_warning(func, "Cannot match the %d found CCD slits with " 06721 "the %d mask slits: process will continue using " 06722 "the detected CCD slits positions", nslits, 06723 nmaskslits); 06724 return NULL; 06725 } 06726 06727 if (nslits <= 3 && nslits == nmaskslits) { 06728 06729 cpl_msg_warning(func, "Too few slits (%d) on mask and CCD", nslits); 06730 cpl_msg_warning(func, "Their detected positions are left unchanged"); 06731 06732 /* 06733 * If there are just up to 3 slits, both on the mask and on the CCD, 06734 * we can reasonably hope that those slits were found, and accept 06735 * that their positions on the CCD cannot be improved. We prepare 06736 * therefore an output position table containing the slits with 06737 * their original positions. We can however give an estimate of 06738 * the platescale if there is more than one slit. 06739 */ 06740 06741 positions = cpl_table_duplicate(slits); 06742 cpl_table_erase_column(slits, "xcenter"); 06743 cpl_table_erase_column(slits, "ycenter"); 06744 cpl_table_duplicate_column(positions, "xmtop", maskslits, "xtop"); 06745 cpl_table_duplicate_column(positions, "ymtop", maskslits, "ytop"); 06746 cpl_table_duplicate_column(positions, "xmbottom", maskslits, "xbottom"); 06747 cpl_table_duplicate_column(positions, "ymbottom", maskslits, "ybottom"); 06748 cpl_table_duplicate_column(positions, "xmcenter", maskslits, "xcenter"); 06749 cpl_table_duplicate_column(positions, "ymcenter", maskslits, "ycenter"); 06750 cpl_table_duplicate_column(positions, "slit_id", maskslits, "slit_id"); 06751 cpl_table_erase_column(maskslits, "xcenter"); 06752 cpl_table_erase_column(maskslits, "ycenter"); 06753 06754 if (nslits > 1) { 06755 xcenter = cpl_table_get_data_double(positions, "xcenter"); 06756 ycenter = cpl_table_get_data_double(positions, "ycenter"); 06757 xmcenter = cpl_table_get_data_double(positions, "xmcenter"); 06758 ymcenter = cpl_table_get_data_double(positions, "ymcenter"); 06759 06760 dist1 = (xcenter[0] - xcenter[1])*(xcenter[0] - xcenter[1]) 06761 + (ycenter[0] - ycenter[1])*(ycenter[0] - ycenter[1]); 06762 dist2 = (xmcenter[0] - xmcenter[1])*(xmcenter[0] - xmcenter[1]) 06763 + (ymcenter[0] - ymcenter[1])*(ymcenter[0] - ymcenter[1]); 06764 scale = sqrt(dist1/dist2); 06765 06766 if (nslits == 3) { 06767 dist1 = (xcenter[1] - xcenter[2])*(xcenter[1] - xcenter[2]) 06768 + (ycenter[1] - ycenter[2])*(ycenter[1] - ycenter[2]); 06769 dist2 = (xmcenter[1] - xmcenter[2])*(xmcenter[1] - xmcenter[2]) 06770 + (ymcenter[1] - ymcenter[2])*(ymcenter[1] - ymcenter[2]); 06771 scale += sqrt(dist1/dist2); 06772 scale /= 2; 06773 } 06774 06775 cpl_msg_info(func, "Platescale: %f pixel/mm", scale); 06776 } 06777 06778 return positions; 06779 } 06780 06781 if (nmaskslits < 3 && nslits > nmaskslits) { 06782 06783 /* 06784 * If there are less than 3 slits on the mask the ambiguity cannot 06785 * be solved, and an error is returned. This is a case that must 06786 * be solved with a first-guess relation between mask and CCD. 06787 */ 06788 06789 cpl_msg_warning(func, "Cannot match the %d found CCD slits with " 06790 "the %d mask slits: process will continue using " 06791 "the detected CCD slits positions", nslits, 06792 nmaskslits); 06793 return NULL; 06794 } 06795 06796 06797 /* 06798 * At this point of the program all the region of the plane 06799 * (nslits, nmaskslits) where either or both mask and CCD display 06800 * less than 3 slits are handled in some way. It would be better 06801 * to add in this place a special handling for identifying slits 06802 * in case of a very reduced number of slits (say, below 6). 06803 * It is also clear that if there are many more slits on the 06804 * mask than on the CCD, or many more on the CCD than on the 06805 * mask, something went deeply wrong with the preliminary 06806 * wavelength calibration. Such cases should be handled with 06807 * a _complete_ pattern-recognition algorithm based on the 06808 * construction of all possible triangles. For the moment, 06809 * we go directly to the limited pattern-recognition applied 06810 * below, based on triangles build only for consecutive slits. 06811 * This is reasonably safe, since the preliminary wavelength 06812 * calibration performed by mos_identify_peaks() is generally 06813 * robust. 06814 */ 06815 06816 06817 /* 06818 * Compute (X, Y) coordinates on pseudo-plane describing the 06819 * different position ratios of successive slits, in both 06820 * input tables. 06821 */ 06822 06823 if (cpl_table_has_column(slits, "xpseudo")) 06824 cpl_table_erase_column(slits, "xpseudo"); 06825 06826 if (cpl_table_has_column(slits, "ypseudo")) 06827 cpl_table_erase_column(slits, "ypseudo"); 06828 06829 if (cpl_table_has_column(maskslits, "xpseudo")) 06830 cpl_table_erase_column(maskslits, "xpseudo"); 06831 06832 if (cpl_table_has_column(maskslits, "ypseudo")) 06833 cpl_table_erase_column(maskslits, "ypseudo"); 06834 06835 cpl_table_duplicate_column(slits, "xpseudo", slits, "xcenter"); 06836 cpl_table_duplicate_column(slits, "ypseudo", slits, "ycenter"); 06837 06838 xcenter = cpl_table_get_data_double(slits, "xcenter"); 06839 ycenter = cpl_table_get_data_double(slits, "ycenter"); 06840 xpseudo = cpl_table_get_data_double(slits, "xpseudo"); 06841 ypseudo = cpl_table_get_data_double(slits, "ypseudo"); 06842 06843 for (i = 1; i < nslits - 1; i++) { 06844 dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i]) 06845 + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]); 06846 dist2 = (xcenter[i-1] - xcenter[i+1]) * (xcenter[i-1] - xcenter[i+1]) 06847 + (ycenter[i-1] - ycenter[i+1]) * (ycenter[i-1] - ycenter[i+1]); 06848 dist3 = (xcenter[i] - xcenter[i+1]) * (xcenter[i] - xcenter[i+1]) 06849 + (ycenter[i] - ycenter[i+1]) * (ycenter[i] - ycenter[i+1]); 06850 xpseudo[i] = sqrt(dist1/dist2); 06851 ypseudo[i] = sqrt(dist3/dist2); 06852 } 06853 06854 cpl_table_set_invalid(slits, "xpseudo", 0); 06855 cpl_table_set_invalid(slits, "xpseudo", nslits-1); 06856 cpl_table_set_invalid(slits, "ypseudo", 0); 06857 cpl_table_set_invalid(slits, "ypseudo", nslits-1); 06858 06859 cpl_table_duplicate_column(maskslits, "xpseudo", maskslits, "xcenter"); 06860 cpl_table_duplicate_column(maskslits, "ypseudo", maskslits, "ycenter"); 06861 06862 xcenter = cpl_table_get_data_double(maskslits, "xcenter"); 06863 ycenter = cpl_table_get_data_double(maskslits, "ycenter"); 06864 xmpseudo = cpl_table_get_data_double(maskslits, "xpseudo"); 06865 ympseudo = cpl_table_get_data_double(maskslits, "ypseudo"); 06866 06867 for (i = 1; i < nmaskslits - 1; i++) { 06868 dist1 = (xcenter[i-1] - xcenter[i])*(xcenter[i-1] - xcenter[i]) 06869 + (ycenter[i-1] - ycenter[i])*(ycenter[i-1] - ycenter[i]); 06870 dist2 = (xcenter[i-1] - xcenter[i+1])*(xcenter[i-1] - xcenter[i+1]) 06871 + (ycenter[i-1] - ycenter[i+1])*(ycenter[i-1] - ycenter[i+1]); 06872 dist3 = (xcenter[i] - xcenter[i+1])*(xcenter[i] - xcenter[i+1]) 06873 + (ycenter[i] - ycenter[i+1])*(ycenter[i] - ycenter[i+1]); 06874 xmpseudo[i] = sqrt(dist1/dist2); 06875 ympseudo[i] = sqrt(dist3/dist2); 06876 } 06877 06878 cpl_table_set_invalid(maskslits, "xpseudo", 0); 06879 cpl_table_set_invalid(maskslits, "xpseudo", nmaskslits-1); 06880 cpl_table_set_invalid(maskslits, "ypseudo", 0); 06881 cpl_table_set_invalid(maskslits, "ypseudo", nmaskslits-1); 06882 06883 06884 /* 06885 * For each (X, Y) on the pseudo-plane related to the mask positions, 06886 * find the closest (X, Y) on the pseudo-plane related to the CCD 06887 * positions. If the closest point is closer than a given search 06888 * radius, a triangle has been found, and 3 slits are identified. 06889 * However, if more than one point is found within the search 06890 * radius, we have an ambiguity and this is rejected. 06891 */ 06892 06893 if (cpl_table_has_column(slits, "slit_id")) 06894 cpl_table_erase_column(slits, "slit_id"); 06895 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 06896 slit_id = cpl_table_get_data_int(maskslits, "slit_id"); 06897 06898 for (i = 1; i < nmaskslits - 1; i++) { 06899 in_sradius = 0; 06900 mindist = (xmpseudo[i] - xpseudo[1]) * (xmpseudo[i] - xpseudo[1]) 06901 + (ympseudo[i] - ypseudo[1]) * (ympseudo[i] - ypseudo[1]); 06902 minpos = 1; 06903 if (mindist < sradius*sradius) 06904 in_sradius++; 06905 for (j = 2; j < nslits - 1; j++) { 06906 dist = (xmpseudo[i] - xpseudo[j]) * (xmpseudo[i] - xpseudo[j]) 06907 + (ympseudo[i] - ypseudo[j]) * (ympseudo[i] - ypseudo[j]); 06908 if (dist < sradius*sradius) 06909 in_sradius++; 06910 if (in_sradius > 1) /* More than one triangle within radius */ 06911 break; 06912 if (mindist > dist) { 06913 mindist = dist; 06914 minpos = j; 06915 } 06916 } 06917 06918 mindist = sqrt(mindist); 06919 06920 if (mindist < sradius && in_sradius == 1) { 06921 cpl_table_set_int(slits, "slit_id", minpos-1, slit_id[i-1]); 06922 cpl_table_set_int(slits, "slit_id", minpos, slit_id[i]); 06923 cpl_table_set_int(slits, "slit_id", minpos+1, slit_id[i+1]); 06924 } 06925 } 06926 06927 06928 /* 06929 * At this point, the slit_id column contains invalid elements 06930 * corresponding to unidentified slits. 06931 */ 06932 06933 found_slits = nslits - cpl_table_count_invalid(slits, "slit_id"); 06934 06935 if (found_slits < 3) { 06936 cpl_msg_warning(func, "Too few preliminarily identified slits: " 06937 "%d out of %d", found_slits, nslits); 06938 if (nslits == nmaskslits) { 06939 cpl_msg_warning(func, "(this is not an error, it could be caused " 06940 "by a mask with regularly located slits)"); 06941 cpl_msg_warning(func, "The detected slits positions are left " 06942 "unchanged"); 06943 06944 /* 06945 * If there are less than 3 identified slits, this is probably 06946 * a mask with regularly spaced slits (leading to an ambiguous 06947 * pattern). Only in the case all expected slits appear to have 06948 * been found on the CCD we can proceed... 06949 */ 06950 06951 cpl_table_erase_column(slits, "slit_id"); 06952 cpl_table_erase_column(slits, "xpseudo"); 06953 cpl_table_erase_column(slits, "ypseudo"); 06954 positions = cpl_table_duplicate(slits); 06955 cpl_table_erase_column(slits, "xcenter"); 06956 cpl_table_erase_column(slits, "ycenter"); 06957 06958 cpl_table_erase_column(maskslits, "xpseudo"); 06959 cpl_table_erase_column(maskslits, "ypseudo"); 06960 cpl_table_duplicate_column(positions, "xmtop", 06961 maskslits, "xtop"); 06962 cpl_table_duplicate_column(positions, "ymtop", 06963 maskslits, "ytop"); 06964 cpl_table_duplicate_column(positions, "xmbottom", 06965 maskslits, "xbottom"); 06966 cpl_table_duplicate_column(positions, "ymbottom", 06967 maskslits, "ybottom"); 06968 cpl_table_duplicate_column(positions, "xmcenter", 06969 maskslits, "xcenter"); 06970 cpl_table_duplicate_column(positions, "ymcenter", 06971 maskslits, "ycenter"); 06972 cpl_table_duplicate_column(positions, "slit_id", 06973 maskslits, "slit_id"); 06974 cpl_table_erase_column(maskslits, "xcenter"); 06975 cpl_table_erase_column(maskslits, "ycenter"); 06976 return positions; 06977 } 06978 else { 06979 cpl_table_erase_column(slits, "slit_id"); 06980 cpl_table_erase_column(slits, "xpseudo"); 06981 cpl_table_erase_column(slits, "ypseudo"); 06982 positions = cpl_table_duplicate(slits); 06983 cpl_table_erase_column(slits, "xcenter"); 06984 cpl_table_erase_column(slits, "ycenter"); 06985 cpl_msg_warning(func, "(the failure could be caused " 06986 "by a mask with regularly located slits)"); 06987 return NULL; 06988 } 06989 } 06990 else { 06991 cpl_msg_info(func, "Preliminarily identified slits: %d out of %d " 06992 "candidates (%d expected)", found_slits, nslits, 06993 nmaskslits); 06994 } 06995 06996 06997 /* 06998 * Create a table with the coordinates of the preliminarily identified 06999 * slits, both on CCD and mask. The original order of the slits positions 07000 * is preserved. 07001 */ 07002 07003 positions = cpl_table_new(found_slits); 07004 cpl_table_new_column(positions, "slit_id", CPL_TYPE_INT); 07005 cpl_table_new_column(positions, "xtop", CPL_TYPE_DOUBLE); 07006 cpl_table_new_column(positions, "ytop", CPL_TYPE_DOUBLE); 07007 cpl_table_new_column(positions, "xbottom", CPL_TYPE_DOUBLE); 07008 cpl_table_new_column(positions, "ybottom", CPL_TYPE_DOUBLE); 07009 cpl_table_new_column(positions, "xcenter", CPL_TYPE_DOUBLE); 07010 cpl_table_new_column(positions, "ycenter", CPL_TYPE_DOUBLE); 07011 cpl_table_new_column(positions, "xmtop", CPL_TYPE_DOUBLE); 07012 cpl_table_new_column(positions, "ymtop", CPL_TYPE_DOUBLE); 07013 cpl_table_new_column(positions, "xmbottom", CPL_TYPE_DOUBLE); 07014 cpl_table_new_column(positions, "ymbottom", CPL_TYPE_DOUBLE); 07015 cpl_table_new_column(positions, "xmcenter", CPL_TYPE_DOUBLE); 07016 cpl_table_new_column(positions, "ymcenter", CPL_TYPE_DOUBLE); 07017 cpl_table_new_column(positions, "good", CPL_TYPE_INT); 07018 cpl_table_fill_column_window_int(positions, "good", 0, found_slits, 0); 07019 07020 slit_id = cpl_table_get_data_int (slits, "slit_id"); 07021 xtop = cpl_table_get_data_double(slits, "xtop"); 07022 ytop = cpl_table_get_data_double(slits, "ytop"); 07023 xbottom = cpl_table_get_data_double(slits, "xbottom"); 07024 ybottom = cpl_table_get_data_double(slits, "ybottom"); 07025 xcenter = cpl_table_get_data_double(slits, "xcenter"); 07026 ycenter = cpl_table_get_data_double(slits, "ycenter"); 07027 07028 mslit_id = cpl_table_get_data_int (maskslits, "slit_id"); 07029 xmtop = cpl_table_get_data_double(maskslits, "xtop"); 07030 ymtop = cpl_table_get_data_double(maskslits, "ytop"); 07031 xmbottom = cpl_table_get_data_double(maskslits, "xbottom"); 07032 ymbottom = cpl_table_get_data_double(maskslits, "ybottom"); 07033 xmcenter = cpl_table_get_data_double(maskslits, "xcenter"); 07034 ymcenter = cpl_table_get_data_double(maskslits, "ycenter"); 07035 07036 07037 /* 07038 * Transferring the valid slits information to the new table. 07039 * Note that invalid elements are coded as 0 in the internal 07040 * buffer, and this is the way they are recognised and excluded. 07041 */ 07042 07043 k = 0; 07044 cpl_table_fill_invalid_int(slits, "slit_id", 0); 07045 for (i = 0; i < nmaskslits; i++) { 07046 for (j = 0; j < nslits; j++) { 07047 if (slit_id[j] == 0) 07048 continue; /* Skip invalid slit */ 07049 if (mslit_id[i] == slit_id[j]) { 07050 cpl_table_set_int (positions, "slit_id", k, slit_id[j]); 07051 07052 cpl_table_set_double(positions, "xtop", k, xtop[j]); 07053 cpl_table_set_double(positions, "ytop", k, ytop[j]); 07054 cpl_table_set_double(positions, "xbottom", k, xbottom[j]); 07055 cpl_table_set_double(positions, "ybottom", k, ybottom[j]); 07056 cpl_table_set_double(positions, "xcenter", k, xcenter[j]); 07057 cpl_table_set_double(positions, "ycenter", k, ycenter[j]); 07058 07059 cpl_table_set_double(positions, "xmtop", k, xmtop[i]); 07060 cpl_table_set_double(positions, "ymtop", k, ymtop[i]); 07061 cpl_table_set_double(positions, "xmbottom", k, xmbottom[i]); 07062 cpl_table_set_double(positions, "ymbottom", k, ymbottom[i]); 07063 cpl_table_set_double(positions, "xmcenter", k, xmcenter[i]); 07064 cpl_table_set_double(positions, "ymcenter", k, ymcenter[i]); 07065 07066 k++; 07067 07068 break; 07069 } 07070 } 07071 } 07072 07073 found_slits = k; 07074 07075 cpl_table_erase_column(slits, "slit_id"); 07076 cpl_table_erase_column(slits, "xpseudo"); 07077 cpl_table_erase_column(slits, "ypseudo"); 07078 cpl_table_erase_column(slits, "xcenter"); 07079 cpl_table_erase_column(slits, "ycenter"); 07080 cpl_table_erase_column(maskslits, "xpseudo"); 07081 cpl_table_erase_column(maskslits, "ypseudo"); 07082 cpl_table_erase_column(maskslits, "xcenter"); 07083 cpl_table_erase_column(maskslits, "ycenter"); 07084 07085 07086 /* 07087 * Find the median platescale and rotation angle from the identified 07088 * slits, and then exclude slits outlaying more than 10% from the 07089 * median platescale, and more than 2 degrees from the median 07090 * rotation angle. 07091 */ 07092 07093 ytop = cpl_table_get_data_double(positions, "ytop"); 07094 ybottom = cpl_table_get_data_double(positions, "ybottom"); 07095 xcenter = cpl_table_get_data_double(positions, "xcenter"); 07096 ycenter = cpl_table_get_data_double(positions, "ycenter"); 07097 xmcenter = cpl_table_get_data_double(positions, "xmcenter"); 07098 ymcenter = cpl_table_get_data_double(positions, "ymcenter"); 07099 07100 scales = cpl_vector_new(found_slits - 1); 07101 dscale = cpl_vector_get_data(scales); 07102 angles = cpl_vector_new(found_slits - 1); 07103 dangle = cpl_vector_get_data(angles); 07104 07105 for (i = 1; i < found_slits; i++) { 07106 dist1 = (xcenter[i-1] - xcenter[i]) * (xcenter[i-1] - xcenter[i]) 07107 + (ycenter[i-1] - ycenter[i]) * (ycenter[i-1] - ycenter[i]); 07108 dist2 = (xmcenter[i-1] - xmcenter[i]) * (xmcenter[i-1] - xmcenter[i]) 07109 + (ymcenter[i-1] - ymcenter[i]) * (ymcenter[i-1] - ymcenter[i]); 07110 dscale[i-1] = sqrt(dist1/dist2); 07111 dangle[i-1] = atan2(ycenter[i-1] - ycenter[i], 07112 xcenter[i-1] - xcenter[i]) 07113 - atan2(ymcenter[i-1] - ymcenter[i], 07114 xmcenter[i-1] - xmcenter[i]); 07115 dangle[i-1] *= 180; 07116 dangle[i-1] /= pi; 07117 } 07118 07119 minscale = cpl_vector_get_min(scales); 07120 scale = cpl_vector_get_median_const(scales); 07121 maxscale = cpl_vector_get_max(scales); 07122 07123 minangle = cpl_vector_get_min(angles); 07124 angle = cpl_vector_get_median_const(angles); 07125 maxangle = cpl_vector_get_max(angles); 07126 07127 cpl_msg_info(func, "Median platescale: %f pixel/mm", scale); 07128 cpl_msg_info(func, "Minmax platescale: %f, %f pixel/mm", 07129 minscale, maxscale); 07130 07131 cpl_msg_info(func, "Median rotation: %f degrees", angle); 07132 cpl_msg_info(func, "Minmax rotation: %f, %f degrees", 07133 minangle, maxangle); 07134 07135 good = cpl_table_get_data_int(positions, "good"); 07136 07137 good[0] = good[found_slits - 1] = 1; 07138 for (i = 1; i < found_slits; i++) { 07139 if (fabs((dscale[i-1] - scale)/scale) < 0.10 07140 && fabs(dangle[i-1] - angle) < 2) { 07141 good[i-1]++; 07142 good[i]++; 07143 } 07144 } 07145 07146 for (i = 0; i < found_slits; i++) { 07147 if (good[i] < 2) 07148 good[i] = 0; 07149 else 07150 good[i] = 1; 07151 } 07152 07153 /* 07154 for (i = 1; i < found_slits; i++) 07155 if (fabs((dscale[i-1] - scale)/scale) < 0.10) 07156 good[i-1] = good[i] = 1; 07157 */ 07158 07159 /* DEBUG ************+ 07160 for (i = 0; i < found_slits; i++) { 07161 if (good[i]) { 07162 if (i == found_slits - 1) 07163 printf("include slit %d, prev = %f, %f\n", 07164 i, dscale[i-1], dangle[i-1]); 07165 else if (i == 0) 07166 printf("include slit %d, next %f, %f\n", 07167 i, dscale[i], dangle[i]); 07168 else 07169 printf("include slit %d, prev = %f, %f, next %f, %f\n", i, 07170 dscale[i-1], dangle[i-1], dscale[i], dangle[i]); 07171 } 07172 else { 07173 if (i == found_slits - 1) 07174 printf("EXclude slit %d, prev = %f, %f\n", 07175 i, dscale[i-1], dangle[i-1]); 07176 else if (i == 0) 07177 printf("EXclude slit %d, next %f, %f\n", 07178 i, dscale[i], dangle[i]); 07179 else 07180 printf("EXclude slit %d, prev = %f, %f, next %f, %f\n", i, 07181 dscale[i-1], dangle[i-1], dscale[i], dangle[i]); 07182 } 07183 } 07184 +*********** DEBUG */ 07185 07186 cpl_vector_delete(scales); 07187 cpl_vector_delete(angles); 07188 07189 cpl_table_and_selected_int(positions, "good", CPL_EQUAL_TO, 0); 07190 cpl_table_erase_selected(positions); 07191 cpl_table_erase_column(positions, "good"); 07192 found_slits = cpl_table_get_nrow(positions); 07193 07194 if (found_slits < 4) { 07195 07196 /* 07197 * If the self-consistency check gives such a poor result, 07198 * something must have gone really wrong in the preliminary 07199 * wavelength calibration... Nothing can be done. 07200 */ 07201 07202 cpl_msg_warning(func, "Too few safely identified slits: %d out of %d " 07203 "candidates (%d expected). Process will continue " 07204 "using the detected CCD slits positions", found_slits, 07205 nslits, nmaskslits); 07206 cpl_table_delete(positions); 07207 return NULL; 07208 } 07209 else { 07210 cpl_msg_info(func, "Safely identified slits: %d out of %d " 07211 "candidates\n(%d expected)", found_slits, nslits, 07212 nmaskslits); 07213 } 07214 07215 07216 /* 07217 * Now select the central points of the identified slits, and 07218 * fit two bivariate polynomials to determine a first approximate 07219 * relation between positions on the mask and positions on the CCD. 07220 */ 07221 07222 xpos = cpl_vector_wrap(found_slits, 07223 cpl_table_get_data_double(positions, "xcenter")); 07224 ypos = cpl_vector_wrap(found_slits, 07225 cpl_table_get_data_double(positions, "ycenter")); 07226 xmpos = cpl_vector_wrap(found_slits, 07227 cpl_table_get_data_double(positions, "xmcenter")); 07228 ympos = cpl_vector_wrap(found_slits, 07229 cpl_table_get_data_double(positions, "ymcenter")); 07230 mpos = cpl_bivector_wrap_vectors(xmpos, ympos); 07231 07232 if (found_slits < 10) 07233 degree = 1; 07234 else 07235 degree = 2; 07236 07237 xpoly = cpl_polynomial_fit_2d_create(mpos, xpos, degree, &xmse); 07238 if (xpoly != NULL) 07239 ypoly = cpl_polynomial_fit_2d_create(mpos, ypos, degree, &ymse); 07240 cpl_bivector_unwrap_vectors(mpos); 07241 cpl_vector_unwrap(xpos); 07242 cpl_vector_unwrap(ypos); 07243 cpl_vector_unwrap(xmpos); 07244 cpl_vector_unwrap(ympos); 07245 if (ypoly == NULL) { 07246 if (found_slits == nmaskslits) { 07247 cpl_msg_warning(func, "Fit failure: the accuracy of the " 07248 "identified slits positions is not improved."); 07249 07250 /* 07251 * The determination of the transformation from mask to CCD 07252 * failed, but since all slits have been already identified 07253 * this is not a problem: data can be reduced also without 07254 * such transformation. Simply the accuracy of the slits 07255 * positions cannot be improved. 07256 */ 07257 07258 } else { 07259 cpl_msg_info(func, "Fit failure: not all slits have been " 07260 "identified. Process will continue using " 07261 "the detected CCD slits positions"); 07262 } 07263 07264 cpl_polynomial_delete(xpoly); 07265 return positions; 07266 } 07267 07268 cpl_msg_info(func, "Fit successful: X rms = %.3g, Y rms = %.3g (pixel)", 07269 sqrt(xmse), sqrt(ymse)); 07270 07271 if (global) { 07272 write_global_distortion(global, 0, xpoly); 07273 write_global_distortion(global, 7, ypoly); 07274 } 07275 07276 /* 07277 * The fit was successful: use the polynomials to obtain a new 07278 * position table with the improved positions of the slits 07279 */ 07280 07281 cpl_table_delete(positions); 07282 07283 positions = cpl_table_duplicate(maskslits); 07284 cpl_table_duplicate_column(positions, "xmtop", positions, "xtop"); 07285 cpl_table_duplicate_column(positions, "ymtop", positions, "ytop"); 07286 cpl_table_duplicate_column(positions, "xmbottom", positions, "xbottom"); 07287 cpl_table_duplicate_column(positions, "ymbottom", positions, "ybottom"); 07288 07289 point = cpl_vector_new(2); 07290 dpoint = cpl_vector_get_data(point); 07291 07292 for (i = 0; i < nmaskslits; i++) { 07293 dpoint[0] = cpl_table_get_double(positions, "xmtop", i, NULL); 07294 dpoint[1] = cpl_table_get_double(positions, "ymtop", i, NULL); 07295 cpl_table_set_double(positions, "xtop", i, 07296 cpl_polynomial_eval(xpoly, point)); 07297 cpl_table_set_double(positions, "ytop", i, 07298 cpl_polynomial_eval(ypoly, point)); 07299 dpoint[0] = cpl_table_get_double(positions, "xmbottom", i, NULL); 07300 dpoint[1] = cpl_table_get_double(positions, "ymbottom", i, NULL); 07301 cpl_table_set_double(positions, "xbottom", i, 07302 cpl_polynomial_eval(xpoly, point)); 07303 cpl_table_set_double(positions, "ybottom", i, 07304 cpl_polynomial_eval(ypoly, point)); 07305 } 07306 07307 cpl_vector_delete(point); 07308 cpl_polynomial_delete(xpoly); 07309 cpl_polynomial_delete(ypoly); 07310 07311 cpl_table_erase_column(positions, "xmtop"); 07312 cpl_table_erase_column(positions, "ymtop"); 07313 cpl_table_erase_column(positions, "xmbottom"); 07314 cpl_table_erase_column(positions, "ymbottom"); 07315 07316 if (nmaskslits > nslits) 07317 cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n" 07318 "(%d recovered)", nmaskslits, nmaskslits, nmaskslits - nslits); 07319 else if (nmaskslits < nslits) 07320 cpl_msg_info(func, "Finally identified slits: %d out of %d expected\n" 07321 "(%d rejected)", nmaskslits, nmaskslits, nslits - nmaskslits); 07322 else 07323 cpl_msg_info(func, "Finally identified slits: %d out of %d expected", 07324 nmaskslits, nmaskslits); 07325 07326 return positions; 07327 } 07328 07329 07371 cpl_table *mos_trace_flat(cpl_image *flat, cpl_table *slits, double reference, 07372 double blue, double red, double dispersion) 07373 { 07374 07375 const char *func = "mos_trace_flat"; 07376 07377 cpl_image *gradient; 07378 cpl_image *sgradient; 07379 float *dgradient; 07380 float level = 500; /* It was 250... */ 07381 cpl_vector *row; 07382 cpl_vector *srow; 07383 cpl_vector **peaks; 07384 double *peak; 07385 int *slit_id; 07386 float *g; 07387 double *r; 07388 double *xtop; 07389 double *ytop; 07390 double *xbottom; 07391 double *ybottom; 07392 double min, dist; 07393 double sradius; 07394 double tolerance; 07395 double start_y, prev_y; 07396 int minpos; 07397 int nslits; 07398 int nrows; 07399 int step = 10; 07400 int filtbox = 15; 07401 int nx, ny, npix; 07402 int pos, ypos; 07403 int npeaks; 07404 int pixel_above, pixel_below; 07405 int i, j, k, l; 07406 char trace_id[MAX_COLNAME]; 07407 07408 cpl_table *traces; 07409 07410 07411 if (flat == NULL || slits == NULL) { 07412 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07413 return NULL; 07414 } 07415 07416 if (dispersion <= 0.0) { 07417 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07418 return NULL; 07419 } 07420 07421 if (red - blue < dispersion) { 07422 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07423 return NULL; 07424 } 07425 07426 /* 07427 * Create a dummy slit_id column if it is missing in the 07428 * input slits table 07429 */ 07430 07431 nslits = cpl_table_get_nrow(slits); 07432 if (1 != cpl_table_has_column(slits, "slit_id")) { 07433 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 07434 for (i = 0; i < nslits; i++) 07435 cpl_table_set_int(slits, "slit_id", i, -(i+1)); /* it was (i+1) */ 07436 } 07437 07438 slit_id = cpl_table_get_data_int(slits, "slit_id"); 07439 07440 nx = cpl_image_get_size_x(flat); 07441 ny = cpl_image_get_size_y(flat); 07442 npix = nx * ny; 07443 07444 gradient = cpl_image_duplicate(flat); 07445 dgradient = cpl_image_get_data_float(gradient); 07446 07447 for (i = 0; i < ny - 1; i++) { 07448 k = i * nx; 07449 for (j = 0; j < nx; j++) { 07450 l = k + j; 07451 dgradient[l] = fabs(dgradient[l] - dgradient[l + nx]); 07452 } 07453 } 07454 07455 npix--; 07456 for (j = 0; j < nx; j++) 07457 dgradient[npix - j] = 0.0; 07458 07459 cpl_image_turn(gradient, -1); 07460 nx = cpl_image_get_size_x(gradient); 07461 ny = cpl_image_get_size_y(gradient); 07462 sgradient = mos_image_vertical_median_filter(gradient, 07463 filtbox, 0, ny, 0, step); 07464 cpl_image_delete(gradient); 07465 07466 07467 /* 07468 * Remove background from processed image rows 07469 */ 07470 07471 dgradient = cpl_image_get_data_float(sgradient); 07472 07473 for (i = 1; i <= ny; i += step) { 07474 row = cpl_vector_new_from_image_row(sgradient, i); 07475 srow = cpl_vector_filter_median_create(row, filtbox); 07476 cpl_vector_subtract(row, srow); 07477 cpl_vector_delete(srow); 07478 g = dgradient + (i-1)*nx; 07479 r = cpl_vector_get_data(row); 07480 for (j = 0; j < nx; j++) 07481 g[j] = r[j]; 07482 cpl_vector_delete(row); 07483 } 07484 07485 07486 /* 07487 * Rotate (temporarily) the input slits table, to get coordinates 07488 * compatible with the rotated gradient image. 07489 */ 07490 07491 mos_rotate_slits(slits, 1, nx, ny); 07492 xtop = cpl_table_get_data_double(slits, "xtop"); 07493 ytop = cpl_table_get_data_double(slits, "ytop"); 07494 xbottom = cpl_table_get_data_double(slits, "xbottom"); 07495 ybottom = cpl_table_get_data_double(slits, "ybottom"); 07496 07497 07498 /* 07499 * Get positions of peaks candidates for each processed gradient 07500 * image row 07501 */ 07502 07503 peaks = cpl_calloc(ny, sizeof(cpl_vector *)); 07504 07505 for (i = 0; i < ny; i += step) { 07506 g = dgradient + i*nx; 07507 peaks[i] = mos_peak_candidates(g, nx, level, 1.0); 07508 07509 /* I thought this would be required, but apparently I was wrong. 07510 * Check twice... */ 07511 if (peaks[i]) 07512 cpl_vector_subtract_scalar(peaks[i], 0.5); 07513 07514 } 07515 07516 cpl_image_delete(sgradient); 07517 07518 07519 /* 07520 * Tracing the flat field spectra edges, starting from the 07521 * slits positions obtained at reference wavelength. The 07522 * gradient maximum closest to each slits ends is found and 07523 * accepted only within a given search radius: 07524 */ 07525 07526 sradius = 5.0; /* Pixel */ 07527 07528 /* 07529 * The tracing proceeds along the processed gradient image rows, 07530 * above and below the start position, finding the closest peak 07531 * to the previous obtained position, accepting the new position 07532 * only if it is closer than a given tolerance: 07533 */ 07534 07535 tolerance = 0.9; /* Pixel */ 07536 07537 /* 07538 * The trace is attempted for a certain number of pixels above 07539 * and below the position of the reference wavelength: 07540 */ 07541 07542 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 07543 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 07544 07545 07546 /* 07547 * Prepare the structure of the output table: 07548 */ 07549 07550 nrows = (ny-1)/step + 1; 07551 traces = cpl_table_new(nrows); 07552 cpl_table_new_column(traces, "x", CPL_TYPE_DOUBLE); 07553 cpl_table_set_column_unit(traces, "x", "pixel"); 07554 for (i = 0, j = 0; i < ny; i += step, j++) 07555 cpl_table_set(traces, "x", j, i); 07556 07557 for (i = 0; i < nslits; i++) { 07558 07559 /* 07560 * Find the closest processed gradient image row 07561 */ 07562 07563 ypos = ytop[i]; 07564 07565 if (ypos < 0) 07566 ypos = 0; 07567 if (ypos >= ny) 07568 ypos = ny - 1; 07569 07570 pos = ypos / step; 07571 pos *= step; 07572 07573 /* 07574 * Find the peak in that row that is closest to xtop[i] 07575 */ 07576 07577 if (peaks[pos]) { 07578 peak = cpl_vector_get_data(peaks[pos]); 07579 npeaks = cpl_vector_get_size(peaks[pos]); 07580 07581 min = fabs(peak[0] - xtop[i]); 07582 minpos = 0; 07583 for (j = 1; j < npeaks; j++) { 07584 dist = fabs(peak[j] - xtop[i]); 07585 if (min > dist) { 07586 min = dist; 07587 minpos = j; 07588 } 07589 } 07590 } 07591 else { 07592 npeaks = 0; 07593 } 07594 07595 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]); 07596 cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE); 07597 07598 if (min > sradius || npeaks == 0) { 07599 cpl_msg_warning(func, "Cannot find spectrum edge for " 07600 "top (or left) end of slit %d", slit_id[i]); 07601 } 07602 else { 07603 07604 /* 07605 * Add to output table the start y position. Note that 07606 * y positions are written in coordinates of the unrotated 07607 * image, i.e., with horizontal dispersion. Currently nx 07608 * is the x size of the temporarily rotated image (for 07609 * faster memory access), but it has the sense of a y size. 07610 */ 07611 07612 cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]); 07613 start_y = peak[minpos]; 07614 07615 /* 07616 * Perform the tracing of current edge. Above: 07617 */ 07618 07619 prev_y = start_y; 07620 07621 for (j = pos + step; j < ny; j += step) { 07622 if (j - pos > pixel_above) 07623 break; 07624 if (peaks[j]) { 07625 peak = cpl_vector_get_data(peaks[j]); 07626 npeaks = cpl_vector_get_size(peaks[j]); 07627 min = fabs(peak[0] - prev_y); 07628 minpos = 0; 07629 for (k = 1; k < npeaks; k++) { 07630 dist = fabs(peak[k] - prev_y); 07631 if (min > dist) { 07632 min = dist; 07633 minpos = k; 07634 } 07635 } 07636 if (min < tolerance) { 07637 cpl_table_set(traces, trace_id, j/step, 07638 nx - peak[minpos]); 07639 prev_y = peak[minpos]; 07640 } 07641 } 07642 } 07643 07644 /* 07645 * Perform the tracing of current edge. Below: 07646 */ 07647 07648 prev_y = start_y; 07649 07650 for (j = pos - step; j >= 0; j -= step) { 07651 if (pos - j > pixel_below) 07652 break; 07653 if (peaks[j]) { 07654 peak = cpl_vector_get_data(peaks[j]); 07655 npeaks = cpl_vector_get_size(peaks[j]); 07656 min = fabs(peak[0] - prev_y); 07657 minpos = 0; 07658 for (k = 1; k < npeaks; k++) { 07659 dist = fabs(peak[k] - prev_y); 07660 if (min > dist) { 07661 min = dist; 07662 minpos = k; 07663 } 07664 } 07665 if (min < tolerance) { 07666 cpl_table_set(traces, trace_id, j/step, 07667 nx - peak[minpos]); 07668 prev_y = peak[minpos]; 07669 } 07670 } 07671 } 07672 } 07673 07674 07675 /* 07676 * Find the peak in that row that is closest to xbottom[i] 07677 */ 07678 07679 if (peaks[pos]) { 07680 peak = cpl_vector_get_data(peaks[pos]); 07681 npeaks = cpl_vector_get_size(peaks[pos]); 07682 07683 min = fabs(peak[0] - xbottom[i]); 07684 minpos = 0; 07685 for (j = 1; j < npeaks; j++) { 07686 dist = fabs(peak[j] - xbottom[i]); 07687 if (min > dist) { 07688 min = dist; 07689 minpos = j; 07690 } 07691 } 07692 } 07693 else { 07694 npeaks = 0; 07695 } 07696 07697 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]); 07698 cpl_table_new_column(traces, trace_id, CPL_TYPE_DOUBLE); 07699 07700 if (min > sradius || npeaks == 0) { 07701 cpl_msg_warning(func, "Cannot find spectrum edge for " 07702 "bottom (or right) end of slit %d", slit_id[i]); 07703 } 07704 else { 07705 07706 cpl_table_set(traces, trace_id, pos/step, nx - peak[minpos]); 07707 start_y = peak[minpos]; 07708 07709 /* 07710 * Perform the tracing of current edge. Above: 07711 */ 07712 07713 prev_y = start_y; 07714 07715 for (j = pos + step; j < ny; j += step) { 07716 if (j - pos > pixel_above) 07717 break; 07718 if (peaks[j]) { 07719 peak = cpl_vector_get_data(peaks[j]); 07720 npeaks = cpl_vector_get_size(peaks[j]); 07721 min = fabs(peak[0] - prev_y); 07722 minpos = 0; 07723 for (k = 1; k < npeaks; k++) { 07724 dist = fabs(peak[k] - prev_y); 07725 if (min > dist) { 07726 min = dist; 07727 minpos = k; 07728 } 07729 } 07730 if (min < tolerance) { 07731 cpl_table_set(traces, trace_id, j/step, 07732 nx - peak[minpos]); 07733 prev_y = peak[minpos]; 07734 } 07735 } 07736 } 07737 07738 /* 07739 * Perform the tracing of current edge. Below: 07740 */ 07741 07742 prev_y = start_y; 07743 07744 for (j = pos - step; j >= 0; j -= step) { 07745 if (pos - j > pixel_below) 07746 break; 07747 if (peaks[j]) { 07748 peak = cpl_vector_get_data(peaks[j]); 07749 npeaks = cpl_vector_get_size(peaks[j]); 07750 min = fabs(peak[0] - prev_y); 07751 minpos = 0; 07752 for (k = 1; k < npeaks; k++) { 07753 dist = fabs(peak[k] - prev_y); 07754 if (min > dist) { 07755 min = dist; 07756 minpos = k; 07757 } 07758 } 07759 if (min < tolerance) { 07760 cpl_table_set(traces, trace_id, j/step, 07761 nx - peak[minpos]); 07762 prev_y = peak[minpos]; 07763 } 07764 } 07765 } 07766 } 07767 07768 } /* End of loop on slits */ 07769 07770 for (i = 0; i < ny; i += step) 07771 cpl_vector_delete(peaks[i]); 07772 cpl_free(peaks); 07773 07774 /* 07775 * Restore original orientation of slits positions table 07776 */ 07777 07778 mos_rotate_slits(slits, -1, ny, nx); 07779 07780 return traces; 07781 07782 } 07783 07784 07805 cpl_table *mos_poly_trace(cpl_table *slits, cpl_table *traces, int order) 07806 { 07807 const char *func = "mos_poly_trace"; 07808 07809 cpl_table *polytraces; 07810 cpl_table *dummy; 07811 cpl_vector *x; 07812 cpl_vector *trace; 07813 cpl_polynomial *polytrace; 07814 char trace_id[MAX_COLNAME]; 07815 char trace_res[MAX_COLNAME]; 07816 char trace_mod[MAX_COLNAME]; 07817 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 07818 /* Max order is 5 */ 07819 double *xdata; 07820 int *slit_id; 07821 int nslits; 07822 int nrows; 07823 int npoints; 07824 int i, j; 07825 cpl_size k; 07826 07827 07828 if (traces == NULL || slits == NULL) { 07829 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07830 return NULL; 07831 } 07832 07833 if (order > 5) { 07834 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 07835 return NULL; 07836 } 07837 07838 nrows = cpl_table_get_nrow(traces); 07839 xdata = cpl_table_get_data_double(traces, "x"); 07840 nslits = cpl_table_get_nrow(slits); 07841 slit_id = cpl_table_get_data_int(slits, "slit_id"); 07842 07843 polytraces = cpl_table_new(2*nslits); 07844 cpl_table_new_column(polytraces, "slit_id", CPL_TYPE_INT); 07845 for (i = 0; i <= order; i++) 07846 cpl_table_new_column(polytraces, clab[i], CPL_TYPE_DOUBLE); 07847 07848 for (i = 0; i < nslits; i++) { 07849 for (j = 0; j < 2; j++) { /* For top and bottom trace of each slit */ 07850 07851 if (j) { 07852 snprintf(trace_id, MAX_COLNAME, "b%d", slit_id[i]); 07853 snprintf(trace_res, MAX_COLNAME, "b%d_res", slit_id[i]); 07854 snprintf(trace_mod, MAX_COLNAME, "b%d_mod", slit_id[i]); 07855 } 07856 else { 07857 snprintf(trace_id, MAX_COLNAME, "t%d", slit_id[i]); 07858 snprintf(trace_res, MAX_COLNAME, "t%d_res", slit_id[i]); 07859 snprintf(trace_mod, MAX_COLNAME, "t%d_mod", slit_id[i]); 07860 } 07861 07862 cpl_table_set_int(polytraces, "slit_id", 2*i+j, slit_id[i]); 07863 07864 /* 07865 * The "dummy" table is just a tool for eliminating invalid 07866 * points from the vectors to be fitted. 07867 */ 07868 07869 dummy = cpl_table_new(nrows); 07870 cpl_table_duplicate_column(dummy, "x", traces, "x"); 07871 cpl_table_duplicate_column(dummy, trace_id, traces, trace_id); 07872 npoints = nrows - cpl_table_count_invalid(dummy, trace_id); 07873 if (npoints < 2 * order) { 07874 cpl_table_delete(dummy); 07875 continue; 07876 } 07877 cpl_table_erase_invalid(dummy); 07878 x = cpl_vector_wrap(npoints, 07879 cpl_table_get_data_double(dummy, "x")); 07880 trace = cpl_vector_wrap(npoints, 07881 cpl_table_get_data_double(dummy, trace_id)); 07882 polytrace = cpl_polynomial_fit_1d_create(x, trace, order, NULL); 07883 cpl_vector_unwrap(x); 07884 cpl_vector_unwrap(trace); 07885 cpl_table_delete(dummy); 07886 07887 /* 07888 * Screen bad solutions. At the moment, a primitive screening 07889 * consists in excluding solutions displaying excessive 07890 * curvature (larger than 1E-5 / pixel) 07891 */ 07892 07893 k = 2; 07894 if (fabs(cpl_polynomial_get_coeff(polytrace, &k)) > 1.E-4) { 07895 cpl_polynomial_delete(polytrace); 07896 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE); 07897 cpl_table_duplicate_column(traces, trace_res, traces, 07898 trace_mod); 07899 if (j) 07900 cpl_msg_warning(func, "Exclude bad curvature solution " 07901 "for bottom (right) edge of slit %d", slit_id[i]); 07902 else 07903 cpl_msg_warning(func, "Exclude bad curvature solution " 07904 "for top (left) edge of slit %d", slit_id[i]); 07905 continue; 07906 } 07907 07908 /* 07909 * Write polynomial coefficients to the output table, 07910 * tagged with the appropriate slit_id 07911 */ 07912 07913 for (k = 0; k <= order; k++) 07914 cpl_table_set_double(polytraces, clab[k], 2*i+j, 07915 cpl_polynomial_get_coeff(polytrace, &k)); 07916 07917 /* 07918 * Add column of residuals to input traces table 07919 */ 07920 07921 cpl_table_new_column(traces, trace_mod, CPL_TYPE_DOUBLE); 07922 cpl_table_set_column_unit(traces, trace_mod, "pixel"); 07923 07924 for (k = 0; k < nrows; k++) { 07925 cpl_table_set_double(traces, trace_mod, k, 07926 cpl_polynomial_eval_1d(polytrace, xdata[k], NULL)); 07927 } 07928 07929 cpl_polynomial_delete(polytrace); 07930 07931 cpl_table_duplicate_column(traces, trace_res, traces, trace_mod); 07932 cpl_table_subtract_columns(traces, trace_res, trace_id); 07933 cpl_table_multiply_scalar(traces, trace_res, -1.0); 07934 07935 } 07936 } 07937 07938 return polytraces; 07939 07940 } 07941 07942 07966 cpl_error_code mos_global_trace(cpl_table *slits, cpl_table *polytraces, 07967 int mode) 07968 { 07969 const char *func = "mos_global_trace"; 07970 07971 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 07972 /* Max order is 5 */ 07973 cpl_table *table; 07974 cpl_vector *c0; 07975 cpl_vector *cn; 07976 cpl_bivector *list; 07977 /* alternative (not robust) 07978 cpl_polynomial *poly; 07979 */ 07980 07981 double *offset; 07982 double rms, q, m; 07983 07984 int order, nrows, nslits; 07985 int i, j; 07986 07987 07988 if (polytraces == NULL) { 07989 cpl_msg_error(func, "Missing spectral curvature table"); 07990 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07991 } 07992 07993 if (slits == NULL) { 07994 cpl_msg_error(func, "Missing slits positions table"); 07995 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 07996 } 07997 07998 nslits = cpl_table_get_nrow(slits); 07999 08000 table = cpl_table_duplicate(polytraces); 08001 cpl_table_erase_invalid(table); 08002 08003 nrows = cpl_table_get_nrow(table); 08004 08005 if (nrows < 4) { 08006 cpl_msg_warning(func, "Too few successful spectral curvature tracings " 08007 "(%d): the determination of a global curvature model " 08008 "failed", nrows); 08009 return CPL_ERROR_NONE; 08010 } 08011 08012 order = cpl_table_get_ncol(polytraces) - 2; 08013 08014 for (i = 0; i <= order; i++) { 08015 if (!cpl_table_has_column(table, clab[i])) { 08016 cpl_msg_error(func, "Wrong spectral curvature table"); 08017 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08018 } 08019 } 08020 08021 08022 /* 08023 * Fill in advance the missing offset terms 08024 */ 08025 08026 for (i = 0; i < nslits; i++) { 08027 if (!cpl_table_is_valid(polytraces, clab[0], 2*i)) { 08028 cpl_table_set_double(polytraces, clab[0], 2*i, 08029 cpl_table_get_double(slits, "ytop", i, NULL)); 08030 } 08031 if (!cpl_table_is_valid(polytraces, clab[0], 2*i+1)) { 08032 cpl_table_set_double(polytraces, clab[0], 2*i+1, 08033 cpl_table_get_double(slits, "ybottom", i, NULL)); 08034 } 08035 } 08036 08037 offset = cpl_table_get_data_double(polytraces, clab[0]); 08038 08039 08040 /* 08041 * Fit the global model and modify polytraces table accordingly 08042 */ 08043 08044 c0 = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[0])); 08045 08046 for (i = 1; i <= order; i++) { 08047 cn = cpl_vector_wrap(nrows, cpl_table_get_data_double(table, clab[i])); 08048 list = cpl_bivector_wrap_vectors(c0, cn); 08049 robustLinearFit(list, &q, &m, &rms); 08050 /* alternative (not robust) 08051 poly = cpl_polynomial_fit_1d_create(c0, cn, 1, NULL); 08052 */ 08053 for (j = 0; j < 2*nslits; j++) { 08054 if (mode == 1) 08055 if (cpl_table_is_valid(polytraces, clab[i], j)) 08056 continue; 08057 cpl_table_set_double(polytraces, clab[i], j, offset[j]*m + q); 08058 /* alternative (not robust) 08059 cpl_table_set_double(polytraces, clab[i], j, 08060 cpl_polynomial_eval_1d(poly, offset[j], NULL)); 08061 */ 08062 } 08063 cpl_bivector_unwrap_vectors(list); 08064 /* alternative (not robust) 08065 cpl_polynomial_delete(poly); 08066 */ 08067 cpl_vector_unwrap(cn); 08068 } 08069 08070 cpl_vector_unwrap(c0); 08071 cpl_table_delete(table); 08072 08073 return CPL_ERROR_NONE; 08074 08075 } 08076 08077 08147 cpl_image *mos_spatial_calibration(cpl_image *spectra, cpl_table *slits, 08148 cpl_table *polytraces, double reference, 08149 double blue, double red, double dispersion, 08150 int flux, cpl_image *calibration) 08151 { 08152 const char *func = "mos_spatial_calibration"; 08153 08154 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 08155 /* Max order is 5 */ 08156 cpl_polynomial *polytop; 08157 cpl_polynomial *polybot; 08158 cpl_image **exslit; 08159 cpl_image *resampled; 08160 float *data; 08161 float *sdata; 08162 float *xdata; 08163 double vtop, vbot, value; 08164 double top, bot; 08165 double coeff; 08166 double ytop, ybot; 08167 double ypos, yfra; 08168 double factor; 08169 int yint, ysize, yprev; 08170 int nslits; 08171 int npseudo; 08172 int *slit_id; 08173 int *length; 08174 int nx, ny; 08175 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 08176 int missing_top, missing_bot; 08177 int null; 08178 int order; 08179 int i, j; 08180 cpl_size k; 08181 08182 int create_position = 1; 08183 08184 08185 if (spectra == NULL || slits == NULL || polytraces == NULL) { 08186 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08187 return NULL; 08188 } 08189 08190 if (dispersion <= 0.0) { 08191 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08192 return NULL; 08193 } 08194 08195 if (red - blue < dispersion) { 08196 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08197 return NULL; 08198 } 08199 08200 nx = cpl_image_get_size_x(spectra); 08201 ny = cpl_image_get_size_y(spectra); 08202 sdata = cpl_image_get_data(spectra); 08203 if (calibration) 08204 data = cpl_image_get_data(calibration); 08205 08206 if (cpl_table_has_column(slits, "position")) 08207 create_position = 0; 08208 08209 if (create_position) { 08210 cpl_table_new_column(slits, "position", CPL_TYPE_INT); 08211 cpl_table_new_column(slits, "length", CPL_TYPE_INT); 08212 cpl_table_set_column_unit(slits, "position", "pixel"); 08213 cpl_table_set_column_unit(slits, "length", "pixel"); 08214 } 08215 else 08216 length = cpl_table_get_data_int(slits, "length"); 08217 08218 nslits = cpl_table_get_nrow(slits); 08219 slit_id = cpl_table_get_data_int(slits, "slit_id"); 08220 order = cpl_table_get_ncol(polytraces) - 2; 08221 08222 /* 08223 * The spatial resampling is performed for a certain number of 08224 * pixels above and below the position of the reference wavelength: 08225 */ 08226 08227 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 08228 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 08229 08230 exslit = cpl_calloc(nslits, sizeof(cpl_image *)); 08231 08232 for (i = 0; i < nslits; i++) { 08233 08234 if (create_position == 0) 08235 if (length[i] == 0) 08236 continue; 08237 08238 /* 08239 * Note that the x coordinate of the reference pixels on the CCD 08240 * is taken arbitrarily at the top end of each slit. This wouldn't 08241 * be entirely correct in case of curved slits, or in presence of 08242 * heavy distortions: in such cases the spatial resampling is 08243 * really performed across a wide range of wavelengths. But 08244 * the lag between top and bottom spectral curvature models 08245 * would introduce even in such cases negligible effects on 08246 * the spectral spatial resampling. 08247 */ 08248 08249 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 08250 08251 start_pixel = refpixel - pixel_below; 08252 if (start_pixel < 0) 08253 start_pixel = 0; 08254 08255 end_pixel = refpixel + pixel_above; 08256 if (end_pixel > nx) 08257 end_pixel = nx; 08258 08259 /* 08260 * Recover from the table of spectral curvature coefficients 08261 * the curvature polynomials. 08262 */ 08263 08264 missing_top = 0; 08265 polytop = cpl_polynomial_new(1); 08266 for (k = 0; k <= order; k++) { 08267 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 08268 if (null) { 08269 cpl_polynomial_delete(polytop); 08270 missing_top = 1; 08271 break; 08272 } 08273 cpl_polynomial_set_coeff(polytop, &k, coeff); 08274 } 08275 08276 missing_bot = 0; 08277 polybot = cpl_polynomial_new(1); 08278 for (k = 0; k <= order; k++) { 08279 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 08280 if (null) { 08281 cpl_polynomial_delete(polybot); 08282 missing_bot = 1; 08283 break; 08284 } 08285 cpl_polynomial_set_coeff(polybot, &k, coeff); 08286 } 08287 08288 if (missing_top && missing_bot) { 08289 cpl_msg_warning(func, "Spatial calibration, slit %d was not " 08290 "traced: no extraction!", 08291 slit_id[i]); 08292 continue; 08293 } 08294 08295 /* 08296 * In case just one of the two edges was not traced, the other 08297 * edge curvature model is duplicated and shifted to the other 08298 * end of the slit: better than nothing! 08299 */ 08300 08301 if (missing_top) { 08302 cpl_msg_warning(func, "Upper edge of slit %d was not traced: " 08303 "the spectral curvature of the lower edge " 08304 "is used instead.", slit_id[i]); 08305 polytop = cpl_polynomial_duplicate(polybot); 08306 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 08307 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 08308 k = 0; 08309 coeff = cpl_polynomial_get_coeff(polybot, &k); 08310 coeff += ytop - ybot; 08311 cpl_polynomial_set_coeff(polytop, &k, coeff); 08312 } 08313 08314 if (missing_bot) { 08315 cpl_msg_warning(func, "Lower edge of slit %d was not traced: " 08316 "the spectral curvature of the upper edge " 08317 "is used instead.", slit_id[i]); 08318 polybot = cpl_polynomial_duplicate(polytop); 08319 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 08320 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 08321 k = 0; 08322 coeff = cpl_polynomial_get_coeff(polytop, &k); 08323 coeff -= ytop - ybot; 08324 cpl_polynomial_set_coeff(polybot, &k, coeff); 08325 } 08326 08327 /* 08328 * Allocate image for current extracted slit 08329 */ 08330 08331 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 08332 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 08333 npseudo = ceil(top-bot) + 1; 08334 08335 if (npseudo < 1) { 08336 cpl_polynomial_delete(polytop); 08337 cpl_polynomial_delete(polybot); 08338 cpl_msg_warning(func, "Slit %d was badly traced: no extraction!", 08339 slit_id[i]); 08340 continue; 08341 } 08342 08343 exslit[i] = cpl_image_new(nx, npseudo+1, CPL_TYPE_FLOAT); 08344 xdata = cpl_image_get_data(exslit[i]); 08345 08346 /* 08347 * Write interpolated values to slit image. 08348 */ 08349 08350 for (j = start_pixel; j < end_pixel; j++) { 08351 top = cpl_polynomial_eval_1d(polytop, j, NULL); 08352 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 08353 factor = (top-bot)/npseudo; 08354 for (k = 0; k <= npseudo; k++) { 08355 ypos = top - k*factor; 08356 yint = ypos; 08357 yfra = ypos - yint; 08358 if (yint >= 0 && yint < ny-1) { 08359 vtop = sdata[j + nx*yint]; 08360 vbot = sdata[j + nx*(yint+1)]; 08361 value = vtop*(1-yfra) + vbot*yfra; 08362 if (flux) 08363 value *= factor; 08364 xdata[j + nx*(npseudo-k)] = value; 08365 if (calibration) { 08366 data[j + nx*yint] = (top-yint)/factor; 08367 if (k) { 08368 08369 /* 08370 * This is added to recover lost pixels on 08371 * the CCD image (pixels are lost because 08372 * the CCD pixels are less than npseudo+1). 08373 */ 08374 08375 if (yprev - yint > 1) { 08376 data[j + nx*(yint+1)] = (top-yint-1)/factor; 08377 } 08378 } 08379 } 08380 } 08381 yprev = yint; 08382 } 08383 } 08384 cpl_polynomial_delete(polytop); 08385 cpl_polynomial_delete(polybot); 08386 } 08387 08388 /* 08389 * Now all the slits images are copied to a single image 08390 */ 08391 08392 ysize = 0; 08393 for (i = 0; i < nslits; i++) 08394 if (exslit[i]) 08395 ysize += cpl_image_get_size_y(exslit[i]); 08396 08397 resampled = cpl_image_new(nx, ysize, CPL_TYPE_FLOAT); 08398 08399 yint = -1; 08400 for (i = 0; i < nslits; i++) { 08401 if (exslit[i]) { 08402 yint += cpl_image_get_size_y(exslit[i]); 08403 cpl_image_copy(resampled, exslit[i], 1, ysize - yint); 08404 if (create_position) { 08405 cpl_table_set_int(slits, "position", i, ysize - yint - 1); 08406 cpl_table_set_int(slits, "length", i, 08407 cpl_image_get_size_y(exslit[i])); 08408 } 08409 cpl_image_delete(exslit[i]); 08410 } 08411 else if (create_position) { 08412 cpl_table_set_int(slits, "position", i, -1); 08413 cpl_table_set_int(slits, "length", i, 0); 08414 } 08415 } 08416 08417 08418 /* 08419 * Elimination of non-traced slits from slit position table: we cannot do 08420 * it because we would lose sync with polytraces and other slit-oriented 08421 * tables. COMMENTED OUT. 08422 * 08423 08424 if (create_position) { 08425 08426 if (cpl_table_and_selected_int(slits, "position", CPL_EQUAL_TO, -1)) 08427 cpl_table_erase_selected(slits); 08428 08429 } 08430 08431 */ 08432 08433 cpl_free(exslit); 08434 08435 return resampled; 08436 08437 } 08438 08439 08546 cpl_image *mos_wavelength_calibration_final(cpl_image *image, cpl_table *slits, 08547 cpl_vector *lines, 08548 double dispersion, float level, 08549 int sradius, int order, 08550 double reject, double refwave, 08551 double *wavestart, double *waveend, 08552 int *nlines, double *error, 08553 cpl_table *idscoeff, 08554 cpl_image *calibration, 08555 cpl_image *residuals, 08556 cpl_table *restable) 08557 { 08558 08559 const char *func = "mos_wavelength_calibration_final"; 08560 08561 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 08562 /* Max order is 5 */ 08563 08564 double tolerance = 20.0; /* Probably forever... */ 08565 int step = 10; /* Compute restable every "step" rows */ 08566 08567 char name[MAX_COLNAME]; 08568 08569 cpl_image *resampled; 08570 cpl_bivector *output; 08571 cpl_vector *wavel; 08572 cpl_vector *peaks; 08573 cpl_polynomial *ids; 08574 cpl_polynomial *lin; 08575 cpl_polynomial *fguess; 08576 cpl_table *coeff; 08577 double ids_err; 08578 double max_disp, min_disp; 08579 double *line; 08580 double firstLambda, lastLambda, lambda; 08581 double wave, pixe, value; 08582 double c; 08583 float *sdata; 08584 float *rdata; 08585 float *idata; 08586 float *ddata; 08587 float v1, v2, vi; 08588 float fpixel; 08589 int *length; 08590 int pixstart, pixend; 08591 int row_top, row_bot; 08592 int extrapolation; 08593 int nref; 08594 int nslits; 08595 int nfits; 08596 int nl, nx, ny, pixel; 08597 int countLines, usedLines; 08598 int uorder; 08599 int missing; 08600 int null; 08601 int width, uradius; 08602 int i, j, s; 08603 cpl_size k; 08604 08605 08606 if (dispersion == 0.0) { 08607 cpl_msg_error(func, "The expected dispersion (A/pixel) must be given"); 08608 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08609 return NULL; 08610 } 08611 08612 if (dispersion < 0.0) { 08613 cpl_msg_error(func, "The expected dispersion must be positive"); 08614 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08615 return NULL; 08616 } 08617 08618 if (idscoeff == NULL) { 08619 cpl_msg_error(func, "A preallocated IDS coeff table must be given"); 08620 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08621 return NULL; 08622 } 08623 08624 max_disp = dispersion + dispersion * tolerance / 100; 08625 min_disp = dispersion - dispersion * tolerance / 100; 08626 08627 if (order < 1) { 08628 cpl_msg_error(func, "The order of the fitting polynomial " 08629 "must be at least 1"); 08630 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 08631 return NULL; 08632 } 08633 08634 if (image == NULL || lines == NULL) { 08635 cpl_msg_error(func, "Both spectral exposure and reference line " 08636 "catalog are required in input"); 08637 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 08638 return NULL; 08639 } 08640 08641 nx = cpl_image_get_size_x(image); 08642 ny = cpl_image_get_size_y(image); 08643 sdata = cpl_image_get_data_float(image); 08644 08645 nref = cpl_vector_get_size(lines); 08646 line = cpl_vector_get_data(lines); 08647 08648 if (*wavestart < 1.0 && *waveend < 1.0) { 08649 firstLambda = line[0]; 08650 lastLambda = line[nref-1]; 08651 extrapolation = (lastLambda - firstLambda) / 10; 08652 firstLambda -= extrapolation; 08653 lastLambda += extrapolation; 08654 *wavestart = firstLambda; 08655 *waveend = lastLambda; 08656 } 08657 else { 08658 firstLambda = *wavestart; 08659 lastLambda = *waveend; 08660 } 08661 08662 nl = (lastLambda - firstLambda) / dispersion; 08663 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 08664 rdata = cpl_image_get_data_float(resampled); 08665 08666 /* 08667 * Allocate total output table of IDS coefficients 08668 */ 08669 08670 for (j = 0; j <= order; j++) 08671 cpl_table_new_column(idscoeff, clab[j], CPL_TYPE_DOUBLE); 08672 08673 if (calibration) 08674 idata = cpl_image_get_data_float(calibration); 08675 08676 if (residuals) 08677 ddata = cpl_image_get_data_float(residuals); 08678 08679 if (restable) { 08680 cpl_table_set_size(restable, nref); 08681 cpl_table_new_column(restable, "wavelength", CPL_TYPE_DOUBLE); 08682 cpl_table_copy_data_double(restable, "wavelength", line); 08683 for (i = 0; i < ny; i += step) { 08684 snprintf(name, MAX_COLNAME, "r%d", i); 08685 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08686 snprintf(name, MAX_COLNAME, "d%d", i); 08687 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08688 snprintf(name, MAX_COLNAME, "p%d", i); 08689 cpl_table_new_column(restable, name, CPL_TYPE_DOUBLE); 08690 } 08691 } 08692 08693 08694 /* 08695 * Process all slits separately. 08696 */ 08697 08698 nslits = cpl_table_get_nrow(slits); 08699 length = cpl_table_get_data_int(slits, "length"); 08700 08701 row_top = ny; 08702 for (s = 0; s < nslits; s++) { 08703 08704 if (length[s] == 0) 08705 continue; 08706 08707 /* 08708 * row_top and row_bot define the boundaries of the current slit. 08709 * Here we begin (arbitrarily...) from the top slit. 08710 */ 08711 08712 row_bot = cpl_table_get_int(slits, "position", s, NULL); 08713 08714 if (sradius > 0) { 08715 08716 /* 08717 * If a search radius was defined, allocate the table of 08718 * the fitting polynomials coefficients. This table is 08719 * just used to generate the first-guess polynomial made 08720 * of the median coefficients of all polynomials found 08721 * for this slit. 08722 */ 08723 08724 coeff = cpl_table_new(row_top - row_bot); 08725 for (j = 0; j <= order; j++) 08726 cpl_table_new_column(coeff, clab[j], CPL_TYPE_DOUBLE); 08727 } 08728 08729 /* 08730 * Here is the loop on all rows of the current slit. They are 08731 * wavelength calibrated one by one. 08732 */ 08733 08734 for (i = row_bot; i < row_top; i++) { 08735 width = mos_lines_width(sdata + i*nx, nx); 08736 if (width < 5) 08737 width = 5; 08738 peaks = mos_peak_candidates(sdata + i*nx, nx, level, width); 08739 if (peaks) { 08740 peaks = mos_refine_peaks(sdata + i*nx, nx, peaks, width); 08741 } 08742 if (peaks) { 08743 int keep_multiplex = mos_multiplex; 08744 mos_multiplex = -1; 08745 output = mos_identify_peaks(peaks, lines, 08746 min_disp, max_disp, 0.05); 08747 mos_multiplex = keep_multiplex; 08748 if (output) { 08749 countLines = cpl_bivector_get_size(output); 08750 if (countLines < 4) { 08751 cpl_bivector_delete(output); 08752 cpl_vector_delete(peaks); 08753 if (nlines) 08754 nlines[i] = 0; 08755 if (error) 08756 error[i] = 0.0; 08757 continue; 08758 } 08759 08760 /* 08761 * Set reference wavelength as zero point 08762 */ 08763 08764 wavel = cpl_bivector_get_y(output); 08765 cpl_vector_subtract_scalar(wavel, refwave); 08766 08767 uorder = countLines / 2 - 1; 08768 if (uorder > order) 08769 uorder = order; 08770 08771 ids = mos_poly_wav2pix(output, uorder, reject, 08772 2 * (uorder + 1), &usedLines, 08773 &ids_err); 08774 08775 if (ids == NULL) { 08776 cpl_bivector_delete(output); 08777 cpl_vector_delete(peaks); 08778 if (nlines) 08779 nlines[i] = 0; 08780 if (error) 08781 error[i] = 0.0; 08782 cpl_error_reset(); 08783 continue; 08784 } 08785 08786 if (sradius > 0) { 08787 for (k = 0; k <= order; k++) { 08788 if (k > uorder) { 08789 cpl_table_set_double(coeff, clab[k], 08790 i - row_bot, 0.0); 08791 } 08792 else { 08793 cpl_table_set_double(coeff, clab[k], 08794 i - row_bot, cpl_polynomial_get_coeff(ids, &k)); 08795 } 08796 } 08797 } 08798 /* else { */ 08799 if (calibration) { 08800 pixstart = cpl_polynomial_eval_1d(ids, 08801 cpl_bivector_get_y_data(output)[0], 08802 NULL); 08803 pixend = cpl_polynomial_eval_1d(ids, 08804 cpl_bivector_get_y_data(output)[countLines-1], 08805 NULL); 08806 extrapolation = (pixend - pixstart) / 5; 08807 pixstart -= extrapolation; 08808 pixend += extrapolation; 08809 if (pixstart < 0) 08810 pixstart = 0; 08811 if (pixend > nx) 08812 pixend = nx; 08813 08814 for (j = pixstart; j < pixend; j++) { 08815 (idata + i*nx)[j] = mos_eval_dds(ids, 08816 firstLambda, lastLambda, refwave, j); 08817 } 08818 } 08819 08820 /* 08821 * Residuals image 08822 */ 08823 08824 if (residuals || (restable && !(i%step))) { 08825 if (restable && !(i%step)) { 08826 lin = cpl_polynomial_new(1); 08827 for (k = 0; k < 2; k++) 08828 cpl_polynomial_set_coeff(lin, &k, 08829 cpl_polynomial_get_coeff(ids, &k)); 08830 } 08831 for (j = 0; j < countLines; j++) { 08832 pixe = cpl_bivector_get_x_data(output)[j]; 08833 wave = cpl_bivector_get_y_data(output)[j]; 08834 value = pixe 08835 - cpl_polynomial_eval_1d(ids, wave, NULL); 08836 if (residuals) { 08837 pixel = pixe + 0.5; 08838 (ddata + i*nx)[pixel] = value; 08839 } 08840 if (restable && !(i%step)) { 08841 for (k = 0; k < nref; k++) { 08842 if (fabs(line[k]-refwave-wave) < 0.1) { 08843 snprintf(name, MAX_COLNAME, 08844 "r%d", i); 08845 cpl_table_set_double(restable, name, 08846 k, value); 08847 value = pixe 08848 - cpl_polynomial_eval_1d(lin, 08849 wave, NULL); 08850 snprintf(name, MAX_COLNAME, 08851 "d%d", i); 08852 cpl_table_set_double(restable, name, 08853 k, value); 08854 snprintf(name, MAX_COLNAME, 08855 "p%d", i); 08856 cpl_table_set_double(restable, name, 08857 k, pixe); 08858 break; 08859 } 08860 } 08861 } 08862 } 08863 if (restable && !(i%step)) { 08864 cpl_polynomial_delete(lin); 08865 } 08866 /*** 08867 for (j = 0; j < countLines; j++) { 08868 pixel = cpl_bivector_get_x_data(output)[j] 08869 + 0.5; 08870 (ddata + i*nx)[pixel] = 08871 cpl_bivector_get_x_data(output)[j] 08872 - cpl_polynomial_eval_1d(ids, 08873 cpl_bivector_get_y_data(output)[j], 08874 NULL); 08875 } 08876 ***/ 08877 } 08878 /* } */ 08879 08880 /* 08881 * Write it anyway, even in case a first-guess based 08882 * solution will be searched afterwards: in case of 08883 * failure, the "blind" solution is kept. 08884 */ 08885 08886 if (nlines) 08887 nlines[i] = usedLines; 08888 if (error) 08889 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 08890 08891 for (k = 0; k <= order; k++) { 08892 if (k > uorder) { 08893 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 08894 } 08895 else { 08896 cpl_table_set_double(idscoeff, clab[k], i, 08897 cpl_polynomial_get_coeff(ids, &k)); 08898 } 08899 } 08900 08901 cpl_polynomial_delete(ids); 08902 cpl_bivector_delete(output); 08903 } 08904 cpl_vector_delete(peaks); 08905 } 08906 } /* End of loop on current slit's rows */ 08907 08908 08909 if (sradius > 0) { 08910 08911 /* 08912 * See whether there are valid fits at all... 08913 */ 08914 08915 nfits = row_top - row_bot - cpl_table_count_invalid(coeff, clab[0]); 08916 08917 if (nfits) { 08918 int slope = 0; 08919 08920 fguess = cpl_polynomial_new(1); 08921 08922 if (mos_interpolate_wavecalib(coeff, NULL, 2, 1)) { 08923 08924 slope = 0; 08925 08926 /* 08927 * Compute a median IDS polynomial for the current slit 08928 */ 08929 08930 for (k = 0; k <= order; k++) { 08931 c = cpl_table_get_column_median(coeff, clab[k]); 08932 cpl_polynomial_set_coeff(fguess, &k, c); 08933 } 08934 } 08935 else { 08936 slope = 1; 08937 } 08938 08939 for (i = row_bot; i < row_top; i++) { 08940 08941 /* 08942 * Use first-guess to find the reference lines again 08943 */ 08944 08945 width = mos_lines_width(sdata + i*nx, nx); 08946 if (width > sradius) { 08947 uradius = width; 08948 } 08949 else { 08950 uradius = sradius; 08951 } 08952 08953 if (slope) { 08954 for (k = 0; k <= order; k++) { 08955 c = cpl_table_get_double(coeff, clab[k], 08956 i - row_bot, NULL); 08957 cpl_polynomial_set_coeff(fguess, &k, c); 08958 } 08959 } 08960 08961 output = mos_find_peaks(sdata + i*nx, nx, lines, 08962 fguess, refwave, uradius); 08963 08964 if (output == NULL) { 08965 cpl_error_reset(); 08966 continue; 08967 } 08968 08969 countLines = cpl_bivector_get_size(output); 08970 08971 if (countLines < 4) { 08972 cpl_bivector_delete(output); 08973 continue; 08974 } 08975 08976 /* 08977 * Set reference wavelength as zero point 08978 */ 08979 08980 wavel = cpl_bivector_get_y(output); 08981 cpl_vector_subtract_scalar(wavel, refwave); 08982 08983 uorder = countLines / 2 - 1; 08984 if (uorder > order) 08985 uorder = order; 08986 08987 ids = mos_poly_wav2pix(output, uorder, reject, 08988 2 * (uorder + 1), &usedLines, 08989 &ids_err); 08990 08991 if (ids == NULL) { 08992 cpl_error_reset(); 08993 cpl_bivector_delete(output); 08994 continue; 08995 } 08996 08997 if (nlines) 08998 nlines[i] = usedLines; 08999 if (error) 09000 error[i] = ids_err / sqrt(usedLines/(uorder + 1)); 09001 09002 if (calibration) { 09003 pixstart = cpl_polynomial_eval_1d(ids, 09004 cpl_bivector_get_y_data(output)[0], 09005 NULL); 09006 pixend = cpl_polynomial_eval_1d(ids, 09007 cpl_bivector_get_y_data(output)[countLines-1], 09008 NULL); 09009 extrapolation = (pixend - pixstart) / 5; 09010 pixstart -= extrapolation; 09011 pixend += extrapolation; 09012 if (pixstart < 0) 09013 pixstart = 0; 09014 if (pixend > nx) 09015 pixend = nx; 09016 09017 for (j = pixstart; j < pixend; j++) { 09018 (idata + i*nx)[j] = mos_eval_dds(ids, 09019 firstLambda, lastLambda, refwave, j); 09020 } 09021 } 09022 09023 /* 09024 * Residuals image 09025 */ 09026 09027 if (residuals || (restable && !(i%step))) { 09028 if (restable && !(i%step)) { 09029 lin = cpl_polynomial_new(1); 09030 for (k = 0; k < 2; k++) 09031 cpl_polynomial_set_coeff(lin, &k, 09032 cpl_polynomial_get_coeff(ids, &k)); 09033 } 09034 for (j = 0; j < countLines; j++) { 09035 pixe = cpl_bivector_get_x_data(output)[j]; 09036 wave = cpl_bivector_get_y_data(output)[j]; 09037 value = pixe 09038 - cpl_polynomial_eval_1d(ids, wave, NULL); 09039 if (residuals) { 09040 pixel = pixe + 0.5; 09041 (ddata + i*nx)[pixel] = value; 09042 } 09043 if (restable && !(i%step)) { 09044 for (k = 0; k < nref; k++) { 09045 if (fabs(line[k]-refwave-wave) < 0.1) { 09046 snprintf(name, MAX_COLNAME, 09047 "r%d", i); 09048 cpl_table_set_double(restable, name, 09049 k, value); 09050 value = pixe 09051 - cpl_polynomial_eval_1d(lin, 09052 wave, NULL); 09053 snprintf(name, MAX_COLNAME, 09054 "d%d", i); 09055 cpl_table_set_double(restable, name, 09056 k, value); 09057 snprintf(name, MAX_COLNAME, 09058 "p%d", i); 09059 cpl_table_set_double(restable, name, 09060 k, pixe); 09061 break; 09062 } 09063 } 09064 } 09065 } 09066 if (restable && !(i%step)) { 09067 cpl_polynomial_delete(lin); 09068 } 09069 /*** 09070 for (j = 0; j < countLines; j++) { 09071 pixel = cpl_bivector_get_x_data(output)[j] 09072 + 0.5; 09073 (ddata + i*nx)[pixel] = 09074 cpl_bivector_get_x_data(output)[j] 09075 - cpl_polynomial_eval_1d(ids, 09076 cpl_bivector_get_y_data(output)[j], 09077 NULL); 09078 } 09079 ***/ 09080 } 09081 09082 for (k = 0; k <= order; k++) { 09083 if (k > uorder) { 09084 cpl_table_set_double(idscoeff, clab[k], i, 0.0); 09085 } 09086 else { 09087 cpl_table_set_double(idscoeff, clab[k], i, 09088 cpl_polynomial_get_coeff(ids, &k)); 09089 } 09090 } 09091 09092 cpl_bivector_delete(output); 09093 cpl_polynomial_delete(ids); 09094 09095 } /* End of loop "use ids as a first-guess" */ 09096 09097 cpl_polynomial_delete(fguess); 09098 } 09099 09100 cpl_table_delete(coeff); 09101 09102 } 09103 09104 row_top = row_bot; 09105 09106 } /* End of loop on slits */ 09107 09108 09109 /* 09110 * At this point the idscoeff table has been filled with all the 09111 * fits coefficients obtained for all the rows of the input image. 09112 * Now we apply these coefficients to resample the input image 09113 * at constant wavelength step. 09114 */ 09115 09116 for (i = 0; i < ny; i++) { 09117 09118 missing = 0; 09119 ids = cpl_polynomial_new(1); 09120 for (k = 0; k <= order; k++) { 09121 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 09122 if (null) { 09123 cpl_polynomial_delete(ids); 09124 missing = 1; 09125 break; 09126 } 09127 cpl_polynomial_set_coeff(ids, &k, c); 09128 } 09129 if (missing) 09130 continue; 09131 09132 pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL); 09133 pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL); 09134 if (pixstart < 0) 09135 pixstart = 0; 09136 if (pixend > nx) 09137 pixend = nx; 09138 09139 /* 09140 * Resampled image: 09141 */ 09142 09143 for (j = 0; j < nl; j++) { 09144 lambda = firstLambda + j * dispersion; 09145 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, NULL); 09146 pixel = fpixel; 09147 if (pixel >= 0 && pixel < nx-1) { 09148 v1 = (sdata + i*nx)[pixel]; 09149 v2 = (sdata + i*nx)[pixel+1]; 09150 vi = v1 + (v2-v1)*(fpixel-pixel); 09151 (rdata + i*nl)[j] = vi; 09152 } 09153 } 09154 09155 cpl_polynomial_delete(ids); 09156 } 09157 09158 return resampled; 09159 } 09160 09161 09188 cpl_image *mos_wavelength_calibration(cpl_image *image, double refwave, 09189 double firstLambda, double lastLambda, 09190 double dispersion, cpl_table *idscoeff, 09191 int flux) 09192 { 09193 09194 const char *func = "mos_wavelength_calibration"; 09195 09196 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 09197 /* Max order is 5 */ 09198 09199 cpl_image *resampled; 09200 cpl_polynomial *ids; 09201 double pixel_per_lambda; 09202 double lambda; 09203 double c; 09204 float *sdata; 09205 float *rdata; 09206 float v0, v1, v2, v3, vi; 09207 float fpixel; 09208 int order; 09209 int pixstart, pixend; 09210 int nl, nx, ny, pixel; 09211 int missing; 09212 int null; 09213 int i, j; 09214 cpl_size k; 09215 09216 09217 if (dispersion <= 0.0) { 09218 cpl_msg_error(func, "The resampling step must be positive"); 09219 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 09220 return NULL; 09221 } 09222 09223 if (lastLambda - firstLambda < dispersion) { 09224 cpl_msg_error(func, "Invalid spectral range: %.2f to %.2f", 09225 firstLambda, lastLambda); 09226 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 09227 return NULL; 09228 } 09229 09230 if (idscoeff == NULL) { 09231 cpl_msg_error(func, "An IDS coeff table must be given"); 09232 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09233 return NULL; 09234 } 09235 09236 if (image == NULL) { 09237 cpl_msg_error(func, "A scientific spectral image must be given"); 09238 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09239 return NULL; 09240 } 09241 09242 nx = cpl_image_get_size_x(image); 09243 ny = cpl_image_get_size_y(image); 09244 sdata = cpl_image_get_data_float(image); 09245 09246 nl = (lastLambda - firstLambda) / dispersion; 09247 resampled = cpl_image_new(nl, ny, CPL_TYPE_FLOAT); 09248 rdata = cpl_image_get_data_float(resampled); 09249 09250 order = 0; 09251 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 09252 ++order; 09253 --order; 09254 09255 for (i = 0; i < ny; i++) { 09256 09257 missing = 0; 09258 ids = cpl_polynomial_new(1); 09259 for (k = 0; k <= order; k++) { 09260 c = cpl_table_get_double(idscoeff, clab[k], i, &null); 09261 if (null) { 09262 cpl_polynomial_delete(ids); 09263 missing = 1; 09264 break; 09265 } 09266 cpl_polynomial_set_coeff(ids, &k, c); 09267 } 09268 if (missing) 09269 continue; 09270 09271 pixstart = cpl_polynomial_eval_1d(ids, firstLambda - refwave, NULL); 09272 pixend = cpl_polynomial_eval_1d(ids, lastLambda - refwave, NULL); 09273 if (pixstart < 0) 09274 pixstart = 0; 09275 if (pixend > nx) 09276 pixend = nx; 09277 09278 /* 09279 * Resampled image: 09280 */ 09281 09282 for (j = 0; j < nl; j++) { 09283 lambda = firstLambda + j * dispersion; 09284 fpixel = cpl_polynomial_eval_1d(ids, lambda - refwave, 09285 &pixel_per_lambda); 09286 09287 /* 09288 * The local dispersion is 1 / pixel_per_lambda 09289 * and this factor is used for applying the flux 09290 * conservation correction (if requested). 09291 */ 09292 09293 pixel = fpixel; 09294 09295 // if (dispersion * pixel_per_lambda < 2.0) { 09296 if (1) { /* Old behaviour: this is safe. */ 09297 09298 /* 09299 * In this case we just sample interpolating the 09300 * signal of nearby pixels. 09301 */ 09302 09303 if (pixel >= 1 && pixel < nx-2) { 09304 v0 = (sdata + i*nx)[pixel-1]; 09305 v1 = (sdata + i*nx)[pixel]; 09306 v2 = (sdata + i*nx)[pixel+1]; 09307 v3 = (sdata + i*nx)[pixel+2]; 09308 vi = (fpixel-pixel)*(fpixel-pixel)*(v3 - v2 - v1 + v0) 09309 + (fpixel-pixel)*(3*v2 - v3 - v1 - v0) 09310 + 2*v1; 09311 vi /= 2; 09312 if (v1 > v2) { 09313 if (vi > v1) { 09314 vi = v1; 09315 } 09316 else if (vi < v2) { 09317 vi = v2; 09318 } 09319 } 09320 else { 09321 if (vi > v2) { 09322 vi = v2; 09323 } 09324 else if (vi < v1) { 09325 vi = v1; 09326 } 09327 } 09328 if (flux) 09329 vi *= dispersion * pixel_per_lambda; 09330 (rdata + i*nl)[j] = vi; 09331 } 09332 else if (pixel >= 0 && pixel < nx-1) { 09333 v1 = (sdata + i*nx)[pixel]; 09334 v2 = (sdata + i*nx)[pixel+1]; 09335 vi = v1 + (v2-v1)*(fpixel-pixel); 09336 if (flux) 09337 vi *= dispersion * pixel_per_lambda; 09338 (rdata + i*nl)[j] = vi; 09339 } 09340 } 09341 else { 09342 09343 /* 09344 * Here instead we integrate the pixel values in 09345 * the interval centered at the interpolation point. 09346 * This interval is long dispersion * pixel_per_lambda 09347 * of the original pixels, and is centered at fpixel. 09348 * So it starts at fpixel - dispersion * pixel_per_lambda / 2, 09349 * and it ends at fpixel + dispersion * pixel_per_lambda / 2. 09350 */ 09351 09352 double spos = fpixel - dispersion * pixel_per_lambda / 2; 09353 double epos = fpixel + dispersion * pixel_per_lambda / 2; 09354 09355 /* 09356 * Brutal sum over all involved pixels 09357 */ 09358 09359 int spix = spos; 09360 int epix = epos + 1; 09361 09362 if (spix < 0) 09363 spix = 0; 09364 09365 if (epix > nx) 09366 epix = nx; 09367 09368 vi = 0.0; 09369 for (k = spix; k < epix; k++) { 09370 if (pixel >= 0 && pixel < nx) { 09371 vi += (sdata + i*nx)[k]; 09372 } 09373 } 09374 09375 /* 09376 * Correct integrated flux by true length 09377 * of interval. This is clearly an approximation, 09378 * but it's good enough if the PSF is much larger 09379 * than the original pix. 09380 */ 09381 09382 vi *= dispersion * pixel_per_lambda / (epix - spix); 09383 09384 /* 09385 * Flux conservation is a geometric factor that is applied 09386 * always in the same way... 09387 */ 09388 09389 if (flux) 09390 vi *= dispersion * pixel_per_lambda; 09391 09392 (rdata + i*nl)[j] = vi; 09393 } 09394 } 09395 09396 cpl_polynomial_delete(ids); 09397 } 09398 09399 return resampled; 09400 } 09401 09402 09469 cpl_table *mos_wavelength_align(cpl_image *image, cpl_table *slits, 09470 double refwave, double firstLambda, 09471 double lastLambda, cpl_table *idscoeff, 09472 cpl_vector *skylines, int highres, int order, 09473 cpl_image *calibration, int sradius) 09474 { 09475 const char *func = "mos_wavelength_align"; 09476 09477 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 09478 /* Max order is 5 */ 09479 double *line; 09480 double *data; 09481 double expPos, offset; 09482 double c; 09483 double lambda1, lambda2; 09484 double rms; 09485 float pos; 09486 float *sdata; 09487 float *cdata; 09488 int *idata; 09489 int startPos, endPos; 09490 int window = 2*sradius + 1; 09491 int nlines; 09492 int nslits; 09493 int npoints; 09494 int nrows; 09495 int nx, ny; 09496 int xlow, ylow, xhig, yhig; 09497 int idsorder, uorder; 09498 int *slit_id; 09499 int *position; 09500 int *length; 09501 int missing; 09502 int null; 09503 int i; 09504 cpl_size j, k; 09505 09506 char offname[MAX_COLNAME]; 09507 char name[MAX_COLNAME]; 09508 09509 cpl_polynomial *ids; 09510 cpl_polynomial *polycorr; 09511 cpl_image *exslit; 09512 cpl_image *sky; 09513 cpl_table *offsets; 09514 cpl_table *dummy; 09515 cpl_vector *wave; 09516 cpl_vector *offs; 09517 09518 09519 if (idscoeff == NULL) { 09520 cpl_msg_error(func, "An IDS coeff table must be given"); 09521 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09522 return NULL; 09523 } 09524 09525 if (image == NULL) { 09526 cpl_msg_error(func, "A scientific spectral image must be given"); 09527 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09528 return NULL; 09529 } 09530 09531 if (slits == NULL) { 09532 cpl_msg_error(func, "A slit position table must be given"); 09533 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 09534 return NULL; 09535 } 09536 09537 if (skylines) { 09538 line = cpl_vector_get_data(skylines); 09539 nlines = cpl_vector_get_size(skylines); 09540 } 09541 else { 09542 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 09543 "given: using internal list of reference sky lines"); 09544 if (highres) { 09545 line = default_lines_hi; 09546 nlines = sizeof(default_lines_hi) / sizeof(double); 09547 } 09548 else { 09549 line = default_lines_lo; 09550 nlines = sizeof(default_lines_lo) / sizeof(double); 09551 } 09552 } 09553 09554 if (calibration) 09555 cdata = cpl_image_get_data(calibration); 09556 09557 nx = cpl_image_get_size_x(image); 09558 ny = cpl_image_get_size_y(image); 09559 09560 nslits = cpl_table_get_nrow(slits); 09561 slit_id = cpl_table_get_data_int(slits, "slit_id"); 09562 position = cpl_table_get_data_int(slits, "position"); 09563 length = cpl_table_get_data_int(slits, "length"); 09564 09565 09566 /* 09567 * Define the output table of offsets 09568 */ 09569 09570 nrows = 0; 09571 for (i = 0; i < nlines; i++) 09572 if (line[i] > firstLambda && line[i] < lastLambda) 09573 nrows++; 09574 09575 offsets = cpl_table_new(nrows); 09576 cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE); 09577 cpl_table_set_column_unit(offsets, "wave", "Angstrom"); 09578 09579 nrows = 0; 09580 for (i = 0; i < nlines; i++) { 09581 if (line[i] > firstLambda && line[i] < lastLambda) { 09582 cpl_table_set_double(offsets, "wave", nrows, line[i]); 09583 nrows++; 09584 } 09585 } 09586 09587 /* 09588 * Here "line" is made to point to the new list of selected wavelengths 09589 */ 09590 09591 line = cpl_table_get_data_double(offsets, "wave"); 09592 nlines = nrows; 09593 09594 idsorder = 0; 09595 while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder])) 09596 ++idsorder; 09597 --idsorder; 09598 09599 xlow = 1; 09600 xhig = nx; 09601 for (i = 0; i < nslits; i++) { 09602 09603 if (length[i] == 0) 09604 continue; 09605 09606 snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]); 09607 cpl_table_new_column(offsets, offname, CPL_TYPE_DOUBLE); 09608 09609 /* 09610 * Define the extraction boundaries. We DON'T write: 09611 * 09612 * ylow = position[i]; 09613 * yhig = ylow + length[i]; 09614 * 09615 * because the cpl_image pixels are counted from 1, and because in 09616 * cpl_image_extract() the coordinates of the last pixel are inclusive. 09617 */ 09618 09619 ylow = position[i] + 1; 09620 yhig = ylow + length[i] - 1; 09621 09622 exslit = cpl_image_extract(image, xlow, ylow, xhig, yhig); 09623 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 09624 sdata = cpl_image_get_data(sky); 09625 09626 cpl_image_delete(exslit); 09627 09628 /* 09629 * Return here to a decent way of counting pixels (i.e., starting 09630 * from 0) 09631 */ 09632 09633 ylow--; 09634 09635 /* 09636 * Allocate a dummy table for collecting all the offsets 09637 * for all the lines: this is only needed for the computation 09638 * of the median offset for each sky line 09639 */ 09640 09641 dummy = cpl_table_new(yhig - ylow); 09642 for (j = 0; j < nlines; j++) { 09643 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j); 09644 cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE); 09645 } 09646 09647 for (j = ylow; j < yhig; j++) { 09648 09649 /* 09650 * Get the IDS polynomial for the current slit row 09651 */ 09652 09653 missing = 0; 09654 ids = cpl_polynomial_new(1); 09655 for (k = 0; k <= idsorder; k++) { 09656 c = cpl_table_get_double(idscoeff, clab[k], j, &null); 09657 if (null) { 09658 cpl_polynomial_delete(ids); 09659 missing = 1; 09660 break; 09661 } 09662 cpl_polynomial_set_coeff(ids, &k, c); 09663 } 09664 if (missing) 09665 continue; 09666 09667 for (k = 0; k < nlines; k++) { 09668 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL); 09669 startPos = expPos - sradius; 09670 endPos = startPos + window; 09671 if (startPos < 0 || endPos >= nx) 09672 continue; 09673 09674 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) { 09675 pos += startPos; 09676 offset = pos - expPos; 09677 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, k); 09678 cpl_table_set_double(dummy, name, j - ylow, offset); 09679 } 09680 } 09681 09682 cpl_polynomial_delete(ids); 09683 } 09684 09685 cpl_image_delete(sky); 09686 09687 for (j = 0; j < nlines; j++) { 09688 snprintf(name, MAX_COLNAME, "%"CPL_SIZE_FORMAT, j); 09689 if (cpl_table_has_valid(dummy, name)) { 09690 offset = cpl_table_get_column_median(dummy, name); 09691 cpl_table_set_double(offsets, offname, j, offset); 09692 } 09693 } 09694 09695 cpl_table_delete(dummy); 09696 09697 } 09698 09699 09700 /* 09701 * In the following the input idscoeff table is modified by simply 09702 * adding the coefficients of the polynomial used to fit the sky 09703 * line residuals to the coefficients of the IDS polynomials. 09704 */ 09705 09706 for (i = 0; i < nslits; i++) { 09707 09708 if (length[i] == 0) 09709 continue; 09710 09711 snprintf(offname, MAX_COLNAME, "offset%d", slit_id[i]); 09712 09713 /* 09714 * In the following, the "dummy" table is just a tool for 09715 * eliminating invalid points from the vectors to be fitted. 09716 */ 09717 09718 dummy = cpl_table_new(nlines); 09719 cpl_table_duplicate_column(dummy, "wave", offsets, "wave"); 09720 cpl_table_duplicate_column(dummy, "offset", offsets, offname); 09721 09722 npoints = nlines - cpl_table_count_invalid(dummy, "offset"); 09723 if (npoints == 0) { 09724 cpl_msg_warning(func, "No sky lines alignment was possible " 09725 "for slit ID=%d: no sky line found", slit_id[i]); 09726 cpl_table_delete(dummy); 09727 continue; 09728 } 09729 09730 uorder = order; 09731 if (npoints <= uorder) { 09732 uorder = npoints - 1; 09733 if (uorder) { 09734 cpl_msg_warning(func, "Just %d sky lines detected for slit " 09735 "ID=%d, while a polynomial order %d was " 09736 "requested. Using polynomial order %d for " 09737 "this slit!", npoints, slit_id[i], order, 09738 uorder); 09739 } 09740 else { 09741 cpl_msg_warning(func, "Just %d sky lines detected for slit " 09742 "ID=%d, while a polynomial order %d was " 09743 "requested. Computing a median offset for " 09744 "this slit!", npoints, slit_id[i], order); 09745 } 09746 } 09747 09748 cpl_table_erase_invalid(dummy); 09749 09750 if (uorder > 1) { 09751 09752 /* 09753 * Model offsets with polynomial fitting 09754 */ 09755 09756 wave = cpl_vector_wrap(npoints, 09757 cpl_table_get_data_double(dummy, "wave")); 09758 offs = cpl_vector_wrap(npoints, 09759 cpl_table_get_data_double(dummy, "offset")); 09760 09761 /* 09762 * Set reference wavelength as zero point 09763 */ 09764 09765 cpl_vector_subtract_scalar(wave, refwave); 09766 09767 polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms); 09768 09769 rms = sqrt(rms * (uorder + 1) / npoints); 09770 09771 cpl_vector_unwrap(wave); 09772 cpl_vector_unwrap(offs); 09773 cpl_table_delete(dummy); 09774 09775 /* 09776 * Now correct the coefficients of the corresponding IDS 09777 * polynomials related to this slit: 09778 */ 09779 09780 ylow = position[i]; 09781 yhig = ylow + length[i]; 09782 09783 for (j = 0; j <= uorder; j++) { 09784 data = cpl_table_get_data_double(idscoeff, clab[j]); 09785 c = cpl_polynomial_get_coeff(polycorr, &j); 09786 for (k = ylow; k < yhig; k++) 09787 data[k] += c; 09788 } 09789 09790 data = cpl_table_get_data_double(idscoeff, "error"); 09791 for (k = ylow; k < yhig; k++) 09792 data[k] = sqrt(data[k]*data[k] + rms*rms); 09793 09794 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09795 for (k = ylow; k < yhig; k++) 09796 idata[k] = npoints; 09797 09798 /* 09799 * If a wavelengths map was provided, correct it to keep 09800 * into account the alignment to skylines: 09801 */ 09802 09803 if (calibration) { 09804 for (j = ylow; j < yhig; j++) { 09805 for (k = 1; k < nx; k++) { 09806 lambda1 = cdata[k - 1 + j*nx]; 09807 lambda2 = cdata[k + j*nx]; 09808 if (lambda1 < 1.0 || lambda2 < 1.0) 09809 continue; 09810 offset = cpl_polynomial_eval_1d(polycorr, 09811 lambda1-refwave, NULL); 09812 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09813 } 09814 } 09815 } 09816 09817 cpl_polynomial_delete(polycorr); 09818 } 09819 else if (uorder == 1) { 09820 09821 /* 09822 * Model offsets with robust linear fitting 09823 */ 09824 09825 double q, m; 09826 cpl_bivector *list; 09827 09828 09829 wave = cpl_vector_wrap(npoints, 09830 cpl_table_get_data_double(dummy, "wave")); 09831 offs = cpl_vector_wrap(npoints, 09832 cpl_table_get_data_double(dummy, "offset")); 09833 09834 list = cpl_bivector_wrap_vectors(wave, offs); 09835 09836 /* 09837 * Set reference wavelength as zero point 09838 */ 09839 09840 cpl_vector_subtract_scalar(wave, refwave); 09841 09842 robustLinearFit(list, &q, &m, &rms); 09843 09844 rms = sqrt(rms * (uorder + 1) / npoints); 09845 09846 cpl_bivector_unwrap_vectors(list); 09847 cpl_vector_unwrap(wave); 09848 cpl_vector_unwrap(offs); 09849 cpl_table_delete(dummy); 09850 09851 /* 09852 * Now correct the coefficients of the corresponding IDS 09853 * polynomials related to this slit: 09854 */ 09855 09856 ylow = position[i]; 09857 yhig = ylow + length[i]; 09858 09859 for (j = 0; j <= uorder; j++) { 09860 data = cpl_table_get_data_double(idscoeff, clab[j]); 09861 if (j) 09862 c = m; 09863 else 09864 c = q; 09865 for (k = ylow; k < yhig; k++) 09866 data[k] += c; 09867 } 09868 09869 data = cpl_table_get_data_double(idscoeff, "error"); 09870 for (k = ylow; k < yhig; k++) 09871 data[k] = sqrt(data[k]*data[k] + rms*rms); 09872 09873 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09874 for (k = ylow; k < yhig; k++) 09875 idata[k] = npoints; 09876 09877 /* 09878 * If a wavelengths map was provided, correct it to keep 09879 * into account the alignment to skylines: 09880 */ 09881 09882 if (calibration) { 09883 for (j = ylow; j < yhig; j++) { 09884 for (k = 1; k < nx; k++) { 09885 lambda1 = cdata[k - 1 + j*nx]; 09886 lambda2 = cdata[k + j*nx]; 09887 if (lambda1 < 1.0 || lambda2 < 1.0) 09888 continue; 09889 offset = q + m*(lambda1-refwave); 09890 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09891 } 09892 } 09893 } 09894 } 09895 else { 09896 09897 /* 09898 * Just compute median offset 09899 */ 09900 09901 offs = cpl_vector_wrap(npoints, 09902 cpl_table_get_data_double(dummy, "offset")); 09903 09904 offset = cpl_vector_get_median_const(offs); 09905 09906 if (npoints > 1) 09907 rms = cpl_table_get_column_stdev(dummy, "offset"); 09908 else 09909 rms = 0.0; 09910 09911 rms /= sqrt(npoints); 09912 09913 cpl_vector_unwrap(offs); 09914 cpl_table_delete(dummy); 09915 09916 /* 09917 * Now correct the constant term of the corresponding IDS 09918 * polynomials related to this slit: 09919 */ 09920 09921 ylow = position[i]; 09922 yhig = ylow + length[i]; 09923 09924 data = cpl_table_get_data_double(idscoeff, clab[0]); 09925 for (k = ylow; k < yhig; k++) 09926 data[k] += offset; 09927 09928 data = cpl_table_get_data_double(idscoeff, "error"); 09929 for (k = ylow; k < yhig; k++) 09930 data[k] = sqrt(data[k]*data[k] + rms*rms); 09931 09932 idata = cpl_table_get_data_int(idscoeff, "nlines"); 09933 for (k = ylow; k < yhig; k++) 09934 idata[k] = npoints; 09935 09936 /* 09937 * If a wavelengths map was provided, correct it to keep 09938 * into account the alignment to skylines. Note that 09939 * the offset must be converted from pixels to wavelengths. 09940 */ 09941 09942 if (calibration) { 09943 for (j = ylow; j < yhig; j++) { 09944 for (k = 1; k < nx; k++) { 09945 lambda1 = cdata[k - 1 + j*nx]; 09946 lambda2 = cdata[k + j*nx]; 09947 if (lambda1 < 1.0 || lambda2 < 1.0) 09948 continue; 09949 cdata[k - 1 + j*nx] -= offset * (lambda2-lambda1); 09950 } 09951 } 09952 } 09953 } 09954 } 09955 09956 return offsets; 09957 09958 } 09959 09960 10022 cpl_table *mos_wavelength_align_lss(cpl_image *image, double refwave, 10023 double firstLambda, double lastLambda, 10024 cpl_table *idscoeff, cpl_vector *skylines, 10025 int highres, int order, 10026 cpl_image *calibration, int sradius) 10027 { 10028 const char *func = "mos_wavelength_align_lss"; 10029 10030 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10031 /* Max order is 5 */ 10032 double *line; 10033 double *data; 10034 double *wdata; 10035 double *odata; 10036 double expPos, offset; 10037 double c; 10038 double lambda1, lambda2; 10039 double rms; 10040 float pos; 10041 float *sdata; 10042 float *cdata; 10043 int *idata; 10044 int startPos, endPos; 10045 int window = 2*sradius + 1; 10046 int nlines; 10047 int npoints; 10048 int nrows; 10049 int nx, ny; 10050 int idsorder, uorder; 10051 int missing; 10052 int i; 10053 cpl_size j, k; 10054 10055 char name[MAX_COLNAME]; 10056 char fname[MAX_COLNAME]; 10057 10058 cpl_polynomial *ids; 10059 cpl_polynomial *polycorr; 10060 cpl_table *offsets; 10061 cpl_table *fittable; 10062 cpl_table *dummy; 10063 cpl_vector *wave; 10064 cpl_vector *offs; 10065 cpl_vector *row; 10066 10067 10068 if (idscoeff == NULL) { 10069 cpl_msg_error(func, "An IDS coeff table must be given"); 10070 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10071 return NULL; 10072 } 10073 10074 if (image == NULL) { 10075 cpl_msg_error(func, "A scientific spectral image must be given"); 10076 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10077 return NULL; 10078 } 10079 10080 if (skylines) { 10081 line = cpl_vector_get_data(skylines); 10082 nlines = cpl_vector_get_size(skylines); 10083 } 10084 else { 10085 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 10086 "given: using internal list of reference sky lines"); 10087 if (highres) { 10088 line = default_lines_hi; 10089 nlines = sizeof(default_lines_hi) / sizeof(double); 10090 } 10091 else { 10092 line = default_lines_lo; 10093 nlines = sizeof(default_lines_lo) / sizeof(double); 10094 } 10095 } 10096 10097 if (calibration) 10098 cdata = cpl_image_get_data(calibration); 10099 10100 nx = cpl_image_get_size_x(image); 10101 ny = cpl_image_get_size_y(image); 10102 10103 sdata = cpl_image_get_data(image); 10104 10105 if (ny != cpl_table_get_nrow(idscoeff)) { 10106 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 10107 return NULL; 10108 } 10109 10110 10111 /*FIXME: This is a remnant of the adaptation of the function 10112 * mos_wavelength_align(), where an offset table was created. 10113 * I leave it here because I am in a hurry, it is just used to 10114 * hold the list of selected sky lines. 10115 * 10116 * Define table of wavelengths 10117 */ 10118 10119 nrows = 0; 10120 for (i = 0; i < nlines; i++) 10121 if (line[i] > firstLambda && line[i] < lastLambda) 10122 nrows++; 10123 10124 offsets = cpl_table_new(nrows); 10125 cpl_table_new_column(offsets, "wave", CPL_TYPE_DOUBLE); 10126 cpl_table_set_column_unit(offsets, "wave", "Angstrom"); 10127 10128 nrows = 0; 10129 for (i = 0; i < nlines; i++) { 10130 if (line[i] > firstLambda && line[i] < lastLambda) { 10131 cpl_table_set_double(offsets, "wave", nrows, line[i]); 10132 nrows++; 10133 } 10134 } 10135 10136 /* 10137 * Here "line" is made to point to the new list of selected wavelengths 10138 */ 10139 10140 line = cpl_table_get_data_double(offsets, "wave"); 10141 nlines = nrows; 10142 10143 idsorder = 0; 10144 while (idsorder < 6 && cpl_table_has_column(idscoeff, clab[idsorder])) 10145 ++idsorder; 10146 --idsorder; 10147 10148 10149 /* 10150 * Allocate a dummy table for collecting all the offsets 10151 * for all the lines 10152 */ 10153 10154 dummy = cpl_table_new(ny); 10155 for (j = 0; j < nlines; j++) { 10156 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10157 snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]); 10158 cpl_table_new_column(dummy, name, CPL_TYPE_DOUBLE); 10159 cpl_table_new_column(dummy, fname, CPL_TYPE_DOUBLE); 10160 } 10161 10162 for (j = 0; j < ny; j++, sdata += nx) { 10163 10164 /* 10165 * Get the IDS polynomial for the current slit row 10166 */ 10167 10168 missing = 0; 10169 ids = cpl_polynomial_new(1); 10170 for (k = 0; k <= idsorder; k++) { 10171 c = cpl_table_get_double(idscoeff, clab[k], j, &missing); 10172 if (missing) { 10173 cpl_polynomial_delete(ids); 10174 break; 10175 } 10176 cpl_polynomial_set_coeff(ids, &k, c); 10177 } 10178 if (missing) 10179 continue; 10180 10181 for (k = 0; k < nlines; k++) { 10182 expPos = cpl_polynomial_eval_1d(ids, line[k] - refwave, NULL); 10183 startPos = expPos - sradius; 10184 endPos = startPos + window; 10185 if (startPos < 0 || endPos >= nx) 10186 continue; 10187 10188 if (0 == peakPosition(sdata + startPos, window, &pos, 1)) { 10189 pos += startPos; 10190 offset = pos - expPos; 10191 snprintf(name, MAX_COLNAME, "off_%d", (int)line[k]); 10192 cpl_table_set_double(dummy, name, j, offset); 10193 } 10194 } 10195 10196 cpl_polynomial_delete(ids); 10197 } 10198 10199 10200 /* 10201 * At this point for each sky line we model its offset along 10202 * the image rows using a robust linear fitting 10203 */ 10204 10205 for (j = 0; j < nlines; j++) { 10206 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10207 snprintf(fname, MAX_COLNAME, "fit_%d", (int)line[j]); 10208 if (cpl_table_has_valid(dummy, name)) { 10209 10210 /* 10211 * In the following, the "fittable" is just a tool for 10212 * eliminating invalid points from the vectors to be fitted. 10213 */ 10214 10215 double q, m; 10216 cpl_bivector *list; 10217 10218 fittable = cpl_table_new(ny); 10219 cpl_table_new_column(fittable, "row", CPL_TYPE_DOUBLE); 10220 cpl_table_set_column_unit(fittable, "row", "pixel"); 10221 for (k = 0; k < ny; k++) 10222 cpl_table_set_double(fittable, "row", k, k); 10223 cpl_table_duplicate_column(fittable, "offset", dummy, name); 10224 npoints = ny - cpl_table_count_invalid(fittable, "offset"); 10225 cpl_table_erase_invalid(fittable); 10226 row = cpl_vector_wrap(npoints, 10227 cpl_table_get_data_double(fittable, "row")); 10228 offs = cpl_vector_wrap(npoints, 10229 cpl_table_get_data_double(fittable, "offset")); 10230 list = cpl_bivector_wrap_vectors(row, offs); 10231 robustLinearFit(list, &q, &m, &rms); 10232 cpl_bivector_unwrap_vectors(list); 10233 cpl_vector_unwrap(row); 10234 cpl_vector_unwrap(offs); 10235 cpl_table_delete(fittable); 10236 for (k = 0; k < ny; k++) 10237 cpl_table_set_double(dummy, fname, k, q + m*k); 10238 } 10239 } 10240 10241 10242 /* 10243 * Now each dummy table row consists of a sequence of offsets, 10244 * one for each wavelength. A table row corresponds to an image row. 10245 * We must fit a polynomial to each one of these rows, in order to 10246 * express the offsets as a function of wavelength. The obtained 10247 * polynomial coefficients are used to correct the IDS coefficients. 10248 */ 10249 10250 for (i = 0; i < ny; i++) { 10251 10252 if (!cpl_table_is_valid(idscoeff, clab[0], i)) 10253 continue; 10254 10255 npoints = 0; 10256 for (j = 0; j < nlines; j++) { 10257 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10258 if (cpl_table_is_valid(dummy, name, i)) 10259 npoints++; 10260 } 10261 10262 if (npoints == 0) 10263 continue; 10264 10265 uorder = order; 10266 if (npoints <= uorder) 10267 uorder = npoints - 1; 10268 10269 if (uorder > 1) { 10270 10271 /* 10272 * Model offsets with polynomial fitting 10273 */ 10274 10275 wave = cpl_vector_new(npoints); 10276 wdata = cpl_vector_get_data(wave); 10277 offs = cpl_vector_new(npoints); 10278 odata = cpl_vector_get_data(offs); 10279 10280 npoints = 0; 10281 for (j = 0; j < nlines; j++) { 10282 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10283 if (cpl_table_is_valid(dummy, name, i)) { 10284 wdata[npoints] = line[j] - refwave; 10285 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10286 npoints++; 10287 } 10288 } 10289 10290 polycorr = cpl_polynomial_fit_1d_create(wave, offs, uorder, &rms); 10291 10292 rms = sqrt(rms * (uorder + 1) / npoints); 10293 10294 cpl_vector_delete(wave); 10295 cpl_vector_delete(offs); 10296 10297 /* 10298 * Now correct the coefficients of the corresponding IDS 10299 * polynomials related to this slit: 10300 */ 10301 10302 for (j = 0; j <= uorder; j++) { 10303 data = cpl_table_get_data_double(idscoeff, clab[j]); 10304 c = cpl_polynomial_get_coeff(polycorr, &j); 10305 data[i] += c; 10306 } 10307 10308 data = cpl_table_get_data_double(idscoeff, "error"); 10309 data[i] = sqrt(data[i]*data[i] + rms*rms); 10310 10311 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10312 idata[i] = npoints; 10313 10314 /* 10315 * If a wavelengths map was provided, correct it to keep 10316 * into account the alignment to skylines: 10317 */ 10318 10319 if (calibration) { 10320 for (k = 1; k < nx; k++) { 10321 lambda1 = cdata[k - 1 + i*nx]; 10322 lambda2 = cdata[k + i*nx]; 10323 if (lambda1 < 1.0 || lambda2 < 1.0) 10324 continue; 10325 offset = cpl_polynomial_eval_1d(polycorr, 10326 lambda1-refwave, NULL); 10327 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10328 } 10329 } 10330 10331 cpl_polynomial_delete(polycorr); 10332 10333 } 10334 else if (uorder == 1) { 10335 10336 /* 10337 * Model offsets with robust linear fitting 10338 */ 10339 10340 cpl_bivector *list; 10341 double q, m; 10342 10343 wave = cpl_vector_new(npoints); 10344 wdata = cpl_vector_get_data(wave); 10345 offs = cpl_vector_new(npoints); 10346 odata = cpl_vector_get_data(offs); 10347 10348 npoints = 0; 10349 for (j = 0; j < nlines; j++) { 10350 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10351 if (cpl_table_is_valid(dummy, name, i)) { 10352 wdata[npoints] = line[j] - refwave; 10353 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10354 npoints++; 10355 } 10356 } 10357 10358 list = cpl_bivector_wrap_vectors(wave, offs); 10359 robustLinearFit(list, &q, &m, &rms); 10360 10361 rms = sqrt(rms * (uorder + 1) / npoints); 10362 10363 cpl_bivector_unwrap_vectors(list); 10364 cpl_vector_delete(wave); 10365 cpl_vector_delete(offs); 10366 10367 /* 10368 * Now correct the coefficients of the corresponding IDS 10369 * polynomials related to this row: 10370 */ 10371 10372 for (j = 0; j <= uorder; j++) { 10373 data = cpl_table_get_data_double(idscoeff, clab[j]); 10374 if (j) 10375 c = m; 10376 else 10377 c = q; 10378 data[i] += c; 10379 } 10380 10381 data = cpl_table_get_data_double(idscoeff, "error"); 10382 data[i] = sqrt(data[i]*data[i] + rms*rms); 10383 10384 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10385 idata[i] = npoints; 10386 10387 /* 10388 * If a wavelengths map was provided, correct it to keep 10389 * into account the alignment to skylines: 10390 */ 10391 10392 if (calibration) { 10393 for (k = 1; k < nx; k++) { 10394 lambda1 = cdata[k - 1 + i*nx]; 10395 lambda2 = cdata[k + i*nx]; 10396 if (lambda1 < 1.0 || lambda2 < 1.0) 10397 continue; 10398 offset = q + m*(lambda1-refwave); 10399 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10400 } 10401 } 10402 } 10403 else { 10404 10405 /* 10406 * Just compute median offset 10407 */ 10408 10409 offs = cpl_vector_new(npoints); 10410 odata = cpl_vector_get_data(offs); 10411 10412 npoints = 0; 10413 for (j = 0; j < nlines; j++) { 10414 snprintf(name, MAX_COLNAME, "fit_%d", (int)line[j]); 10415 if (cpl_table_is_valid(dummy, name, i)) { 10416 odata[npoints] = cpl_table_get_double(dummy, name, i, NULL); 10417 npoints++; 10418 } 10419 } 10420 10421 offset = cpl_vector_get_median_const(offs); 10422 10423 if (npoints > 1) { 10424 rms = cpl_vector_get_stdev(offs); 10425 } 10426 else if (npoints == 1) { 10427 snprintf(name, MAX_COLNAME, "off_%d", (int)line[0]); 10428 if (cpl_table_has_valid(dummy, name)) { 10429 rms = cpl_table_get_column_stdev(dummy, name); 10430 rms /= sqrt(ny - cpl_table_count_invalid(dummy, name)); 10431 } 10432 else { 10433 rms = 0.0; 10434 } 10435 } 10436 else { 10437 rms = 0.0; 10438 } 10439 10440 rms /= sqrt(npoints); 10441 10442 cpl_vector_delete(offs); 10443 10444 /* 10445 * Now correct the constant term of the corresponding IDS 10446 * polynomials related to this slit: 10447 */ 10448 10449 data = cpl_table_get_data_double(idscoeff, clab[0]); 10450 data[i] += offset; 10451 10452 data = cpl_table_get_data_double(idscoeff, "error"); 10453 data[i] = sqrt(data[i]*data[i] + rms*rms); 10454 10455 idata = cpl_table_get_data_int(idscoeff, "nlines"); 10456 idata[i] = npoints; 10457 10458 /* 10459 * If a wavelengths map was provided, correct it to keep 10460 * into account the alignment to skylines. Note that 10461 * the offset must be converted from pixels to wavelengths. 10462 */ 10463 10464 if (calibration) { 10465 for (k = 1; k < nx; k++) { 10466 lambda1 = cdata[k - 1 + i*nx]; 10467 lambda2 = cdata[k + i*nx]; 10468 if (lambda1 < 1.0 || lambda2 < 1.0) 10469 continue; 10470 cdata[k - 1 + i*nx] -= offset * (lambda2-lambda1); 10471 } 10472 } 10473 } 10474 } 10475 10476 missing = 1; 10477 for (j = 0; j < nlines; j++) { 10478 snprintf(name, MAX_COLNAME, "off_%d", (int)line[j]); 10479 if (cpl_table_has_valid(dummy, name)) { 10480 missing = 0; 10481 offset = cpl_table_get_column_median(dummy, name); 10482 cpl_msg_info(func, "Median offset for %.3f: %.3f pixel", 10483 line[j], offset); 10484 } 10485 else { 10486 cpl_msg_info(func, 10487 "Median offset for %.2f: not available", line[j]); 10488 } 10489 } 10490 10491 cpl_table_delete(offsets); 10492 10493 if (missing) { 10494 cpl_table_delete(dummy); 10495 dummy = NULL; 10496 } 10497 10498 return dummy; 10499 10500 } 10501 10502 10530 double mos_distortions_rms(cpl_image *rectified, cpl_vector *lines, 10531 double wavestart, double dispersion, int radius, 10532 int highres) 10533 { 10534 10535 const char *func = "mos_distortions_rms"; 10536 10537 int xlen; 10538 int ylen; 10539 int numLines; 10540 int cpix, npix, nzero; 10541 int sp, ep; 10542 int i, j, k; 10543 int npeaks, allPeaks; 10544 10545 float *profile; 10546 float peak, expectPeak, offset; 10547 double lambda; 10548 10549 double average; 10550 double rms, oneRms; 10551 10552 float *sdata; 10553 double *wdata; 10554 10555 10556 xlen = cpl_image_get_size_x(rectified); 10557 ylen = cpl_image_get_size_y(rectified); 10558 sdata = cpl_image_get_data(rectified); 10559 10560 if (lines) { 10561 wdata = cpl_vector_get_data(lines); 10562 numLines = cpl_vector_get_size(lines); 10563 } 10564 else { 10565 cpl_msg_warning(func, "A catalog of sky lines wavelengths was not " 10566 "given: using internal list of reference sky lines"); 10567 if (highres) { 10568 wdata = default_lines_hi; 10569 numLines = sizeof(default_lines_hi) / sizeof(double); 10570 } 10571 else { 10572 wdata = default_lines_lo; 10573 numLines = sizeof(default_lines_lo) / sizeof(double); 10574 } 10575 } 10576 10577 npix = 2 * radius + 1; 10578 profile = cpl_calloc(npix, sizeof(float)); 10579 10580 rms = 0.0; 10581 allPeaks = 0; 10582 10583 for (i = 0; i < numLines; i++) { 10584 10585 /* 10586 * Expected peak and closest pixel to specified wavelength. 10587 */ 10588 10589 lambda = wdata[i]; 10590 expectPeak = (lambda - wavestart) / dispersion; 10591 cpix = floor(expectPeak + 0.5); 10592 10593 /* 10594 * Search interval for peak. Abort if too close to image border. 10595 */ 10596 10597 sp = cpix - radius; 10598 ep = cpix + radius; 10599 10600 if (sp < 0 || ep > xlen) 10601 continue; 10602 10603 average = 0.0; 10604 npeaks = 0; 10605 oneRms = 0.0; 10606 10607 for (j = 0; j < ylen; j++) { /* For each row of each slit */ 10608 nzero = 0; 10609 for (k = 0; k < npix; k++) { 10610 profile[k] = sdata[sp + k + j * xlen]; 10611 if (fabs(profile[k]) < 0.0001) 10612 nzero++; /* Count number of 0 pixels (spectrum truncated) */ 10613 } 10614 if (nzero > 0) 10615 continue; 10616 10617 if (peakPosition(profile, npix, &peak, 1) == 0) { 10618 offset = (sp + peak) - expectPeak; 10619 average += offset; 10620 rms += fabs(offset); 10621 oneRms += fabs(offset); 10622 npeaks++; 10623 allPeaks++; 10624 } 10625 } 10626 10627 if (npeaks) 10628 cpl_msg_info(func, "RMS for %.2f: %.3f pixel (%d points)", 10629 lambda, oneRms / npeaks * 1.25, npeaks); 10630 else 10631 cpl_msg_info(func, "RMS for %.2f: line not available", lambda); 10632 } 10633 10634 cpl_free(profile); 10635 10636 if (allPeaks < 10) 10637 return 0.0; 10638 10639 rms /= allPeaks; 10640 rms *= 1.25; /* Factor to convert average deviation to sigma */ 10641 10642 return rms; 10643 10644 } 10645 10646 10667 cpl_image *mos_map_pixel(cpl_table *idscoeff, double reference, 10668 double blue, double red, double dispersion, int trend) 10669 { 10670 const char *func = "mos_map_pixel"; 10671 10672 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10673 /* Max order is 5 */ 10674 10675 cpl_polynomial *ids; 10676 cpl_image *map; 10677 float *mdata; 10678 double lambda; 10679 double c; 10680 int order; 10681 int xsize, ysize; 10682 int missing; 10683 int i, j; 10684 cpl_size k; 10685 10686 10687 if (idscoeff == NULL) { 10688 cpl_msg_error(func, "An IDS coeff table must be given"); 10689 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10690 return NULL; 10691 } 10692 10693 xsize = (red - blue) / dispersion; 10694 ysize = cpl_table_get_nrow(idscoeff); 10695 map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT); 10696 mdata = cpl_image_get_data(map); 10697 10698 order = 0; 10699 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 10700 ++order; 10701 --order; 10702 10703 for (i = 0; i < ysize; i++, mdata += xsize) { 10704 10705 missing = 0; 10706 ids = cpl_polynomial_new(1); 10707 for (k = trend; k <= order; k++) { 10708 c = cpl_table_get_double(idscoeff, clab[k], i, &missing); 10709 if (missing) { 10710 cpl_polynomial_delete(ids); 10711 break; 10712 } 10713 cpl_polynomial_set_coeff(ids, &k, c); 10714 } 10715 if (missing) 10716 continue; 10717 10718 for (j = 0; j < xsize; j++) { 10719 lambda = blue + j*dispersion; 10720 mdata[j] = cpl_polynomial_eval_1d(ids, lambda-reference, NULL); 10721 } 10722 10723 cpl_polynomial_delete(ids); 10724 } 10725 10726 return map; 10727 10728 } 10729 10730 10752 cpl_image *mos_map_idscoeff(cpl_table *idscoeff, int xsize, double reference, 10753 double blue, double red) 10754 { 10755 const char *func = "mos_map_idscoeff"; 10756 10757 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10758 /* Max order is 5 */ 10759 10760 cpl_polynomial *ids; 10761 cpl_image *map; 10762 float *mdata; 10763 double lambda; 10764 double c; 10765 int order; 10766 int ysize; 10767 int missing; 10768 int i, j; 10769 cpl_size k; 10770 10771 10772 if (idscoeff == NULL) { 10773 cpl_msg_error(func, "An IDS coeff table must be given"); 10774 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10775 return NULL; 10776 } 10777 10778 if (xsize < 1) { 10779 cpl_msg_error(func, "Invalid image size"); 10780 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10781 return NULL; 10782 } 10783 10784 if (xsize < 20 || xsize > 5000) { 10785 cpl_msg_warning(func, "Do you really have a detector %d pixels long?", 10786 xsize); 10787 } 10788 10789 ysize = cpl_table_get_nrow(idscoeff); 10790 map = cpl_image_new(xsize, ysize, CPL_TYPE_FLOAT); 10791 mdata = cpl_image_get_data(map); 10792 10793 order = 0; 10794 while (order < 6 && cpl_table_has_column(idscoeff, clab[order])) 10795 ++order; 10796 --order; 10797 10798 for (i = 0; i < ysize; i++, mdata += xsize) { 10799 10800 missing = 0; 10801 ids = cpl_polynomial_new(1); 10802 for (k = 0; k <= order; k++) { 10803 c = cpl_table_get_double(idscoeff, clab[k], i, &missing); 10804 if (missing) { 10805 cpl_polynomial_delete(ids); 10806 break; 10807 } 10808 cpl_polynomial_set_coeff(ids, &k, c); 10809 } 10810 if (missing) 10811 continue; 10812 10813 for (j = 0; j < xsize; j++) { 10814 lambda = mos_eval_dds(ids, blue, red, reference, j); 10815 10816 if (lambda >= blue && lambda <= red) { 10817 mdata[j] = lambda; 10818 } 10819 } 10820 10821 cpl_polynomial_delete(ids); 10822 } 10823 10824 return map; 10825 10826 } 10827 10828 10863 cpl_image *mos_map_wavelengths(cpl_image *spatial, cpl_image *calibration, 10864 cpl_table *slits, cpl_table *polytraces, 10865 double reference, double blue, double red, 10866 double dispersion) 10867 { 10868 const char *func = "mos_map_wavelengths"; 10869 10870 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 10871 /* Max order is 5 */ 10872 cpl_polynomial *polytop; 10873 cpl_polynomial *polybot; 10874 cpl_image *remapped; 10875 float *data; 10876 float *wdata; 10877 float *sdata; 10878 float *xdata; 10879 double vtop, vbot, value; 10880 double top, bot; 10881 double coeff; 10882 double ytop, ybot; 10883 double ypos; 10884 double fvalue; 10885 int ivalue; 10886 int yint, ysize, yprev; 10887 int nslits; 10888 int npseudo; 10889 int *slit_id; 10890 int *position; 10891 int *length; 10892 int nx, ny; 10893 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 10894 int missing_top, missing_bot; 10895 int null; 10896 int order; 10897 int i, j; 10898 cpl_size k; 10899 10900 10901 if (spatial == NULL || calibration == NULL || 10902 slits == NULL || polytraces == NULL) { 10903 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 10904 return NULL; 10905 } 10906 10907 if (dispersion <= 0.0) { 10908 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10909 return NULL; 10910 } 10911 10912 if (red - blue < dispersion) { 10913 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 10914 return NULL; 10915 } 10916 10917 nx = cpl_image_get_size_x(spatial); 10918 ny = cpl_image_get_size_y(spatial); 10919 ysize = cpl_image_get_size_y(calibration); 10920 remapped = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 10921 data = cpl_image_get_data(remapped); 10922 sdata = cpl_image_get_data(spatial); 10923 wdata = cpl_image_get_data(calibration); 10924 10925 nslits = cpl_table_get_nrow(slits); 10926 slit_id = cpl_table_get_data_int(slits, "slit_id"); 10927 order = cpl_table_get_ncol(polytraces) - 2; 10928 position = cpl_table_get_data_int(slits, "position"); 10929 length = cpl_table_get_data_int(slits, "length"); 10930 10931 /* 10932 * The spatial resampling is performed for a certain number of 10933 * pixels above and below the position of the reference wavelength: 10934 */ 10935 10936 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 10937 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 10938 10939 for (i = 0; i < nslits; i++) { 10940 10941 if (length[i] == 0) 10942 continue; 10943 10944 /* 10945 * Note that the x coordinate of the reference pixels on the CCD 10946 * is taken arbitrarily at the top end of each slit. This wouldn't 10947 * be entirely correct in case of curved slits, or in presence of 10948 * heavy distortions: in such cases the spatial resampling is 10949 * really performed across a wide range of wavelengths. But 10950 * the lag between top and bottom spectral curvature models 10951 * would introduce even in such cases negligible effects on 10952 * the spectral spatial resampling. 10953 */ 10954 10955 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 10956 10957 start_pixel = refpixel - pixel_below; 10958 if (start_pixel < 0) 10959 start_pixel = 0; 10960 10961 end_pixel = refpixel + pixel_above; 10962 if (end_pixel > nx) 10963 end_pixel = nx; 10964 10965 /* 10966 * Recover from the table of spectral curvature coefficients 10967 * the curvature polynomials. 10968 */ 10969 10970 missing_top = 0; 10971 polytop = cpl_polynomial_new(1); 10972 for (k = 0; k <= order; k++) { 10973 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 10974 if (null) { 10975 cpl_polynomial_delete(polytop); 10976 missing_top = 1; 10977 break; 10978 } 10979 cpl_polynomial_set_coeff(polytop, &k, coeff); 10980 } 10981 10982 missing_bot = 0; 10983 polybot = cpl_polynomial_new(1); 10984 for (k = 0; k <= order; k++) { 10985 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 10986 if (null) { 10987 cpl_polynomial_delete(polybot); 10988 missing_bot = 1; 10989 break; 10990 } 10991 cpl_polynomial_set_coeff(polybot, &k, coeff); 10992 } 10993 10994 if (missing_top && missing_bot) { 10995 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 10996 slit_id[i]); 10997 continue; 10998 } 10999 11000 /* 11001 * In case just one of the two edges was not traced, the other 11002 * edge curvature model is duplicated and shifted to the other 11003 * end of the slit: better than nothing! 11004 */ 11005 11006 if (missing_top) { 11007 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 11008 "the spectral curvature of the lower edge " 11009 "is used instead.", slit_id[i]); 11010 polytop = cpl_polynomial_duplicate(polybot); 11011 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11012 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11013 k = 0; 11014 coeff = cpl_polynomial_get_coeff(polybot, &k); 11015 coeff += ytop - ybot; 11016 cpl_polynomial_set_coeff(polytop, &k, coeff); 11017 } 11018 11019 if (missing_bot) { 11020 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 11021 "the spectral curvature of the upper edge " 11022 "is used instead.", slit_id[i]); 11023 polybot = cpl_polynomial_duplicate(polytop); 11024 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11025 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11026 k = 0; 11027 coeff = cpl_polynomial_get_coeff(polytop, &k); 11028 coeff -= ytop - ybot; 11029 cpl_polynomial_set_coeff(polybot, &k, coeff); 11030 } 11031 11032 /* 11033 * Point to current slit on wavelength calibration image. 11034 * Note that the npseudo value related to this slit is equal 11035 * to the number of spatial pseudo-pixels decreased by 1 11036 * (compare with function mos_spatial_calibration()). 11037 */ 11038 11039 xdata = wdata + nx*position[i]; 11040 npseudo = length[i] - 1; 11041 11042 /* 11043 * Write interpolated wavelengths to CCD image 11044 */ 11045 11046 for (j = start_pixel; j < end_pixel; j++) { 11047 top = cpl_polynomial_eval_1d(polytop, j, NULL); 11048 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 11049 for (k = 0; k <= npseudo; k++) { 11050 ypos = top - k*(top-bot)/npseudo; 11051 yint = ypos; 11052 11053 /* 11054 * The line: 11055 * value = sdata[j + nx*yint]; 11056 * should be equivalent to: 11057 * value = npseudo*(top-yint)/(top-bot); 11058 */ 11059 11060 if (yint < 0 || yint >= ny-1) { 11061 yprev = yint; 11062 continue; 11063 } 11064 11065 value = sdata[j + nx*yint]; /* Spatial coordinate on CCD */ 11066 ivalue = value; /* Nearest spatial pixels: */ 11067 fvalue = value - ivalue; /* ivalue and ivalue+1 */ 11068 if (ivalue < npseudo && ivalue >= 0) { 11069 vtop = xdata[j + nx*(npseudo-ivalue)]; 11070 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 11071 if (vtop < 1.0) { /* Impossible wavelength */ 11072 if (vbot < 1.0) { 11073 value = 0.0; 11074 } 11075 else { 11076 value = vbot; 11077 } 11078 } 11079 else if (vbot < 1.0) { 11080 if (k) 11081 value = vtop; 11082 else 11083 value = 0.0; 11084 } 11085 else if (fabs(vbot-vtop) > 10*dispersion) { 11086 value = 0.0; 11087 } 11088 else { 11089 value = vtop*(1-fvalue) + vbot*fvalue; 11090 } 11091 data[j + nx*yint] = value; 11092 11093 if (k) { 11094 11095 /* 11096 * This is added to recover lost pixels on 11097 * the CCD image (pixels are lost because 11098 * the CCD pixels are less than npseudo+1). 11099 */ 11100 11101 if (yprev - yint > 1) { 11102 value = sdata[j + nx*(yint+1)]; 11103 ivalue = value; 11104 fvalue = value - ivalue; 11105 if (ivalue < npseudo && ivalue >= 0) { 11106 vtop = xdata[j + nx*(npseudo-ivalue)]; 11107 vbot = xdata[j + nx*(npseudo-ivalue-1)]; 11108 if (vtop < 1.0) { 11109 if (vbot < 1.0) { 11110 value = data[j + nx*(yint+1)]; 11111 } 11112 else { 11113 value = vbot; 11114 } 11115 } 11116 else if (vbot < 1.0) { 11117 value = vtop; 11118 } 11119 else if (fabs(vbot-vtop) > 2*dispersion) { 11120 value = vtop; 11121 } 11122 else { 11123 value = vtop*(1-fvalue) + vbot*fvalue; 11124 } 11125 data[j + nx*(yint+1)] = value; 11126 } 11127 } 11128 } 11129 } 11130 yprev = yint; 11131 } 11132 } 11133 cpl_polynomial_delete(polytop); 11134 cpl_polynomial_delete(polybot); 11135 } 11136 11137 return remapped; 11138 } 11139 11213 cpl_image *mos_map_spectrum(cpl_image *spectra, cpl_image *wavecalib, 11214 cpl_image *spatial, cpl_table *slits, 11215 cpl_table *polytraces, double reference, 11216 double blue, double red, double dispersion, 11217 int flux) 11218 { 11219 const char *func = "mos_map_spectrum"; 11220 11221 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 11222 /* Max order is 5 */ 11223 cpl_polynomial *polytop; 11224 cpl_polynomial *polybot; 11225 cpl_image *remapped; 11226 cpl_image **exslit; 11227 float *data; 11228 float *wdata; 11229 float *sdata; 11230 float *xdata; 11231 double lambda00, lambda01, lambda10, lambda11, lambda; 11232 double space00, space01, space10, space11, space; 11233 double value00, value01, value10, value11, value0, value1, value; 11234 double dL, dS; 11235 double top, bot; 11236 double coeff; 11237 double ytop, ybot; 11238 double xfrac, yfrac; 11239 int yint, ysize; 11240 int itop, ibot; 11241 int shift; 11242 int L, S; 11243 int nslits; 11244 int npseudo; 11245 int *slit_id; 11246 int *position; 11247 int *length; 11248 int nx, ny; 11249 int x, y; 11250 int nlambda; 11251 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 11252 int missing_top, missing_bot; 11253 int null; 11254 int order; 11255 int i; 11256 cpl_size k; 11257 11258 11259 flux += flux; 11260 11261 if (spectra == NULL || spatial == NULL || wavecalib == NULL || 11262 slits == NULL || polytraces == NULL) { 11263 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11264 return NULL; 11265 } 11266 11267 if (dispersion <= 0.0) { 11268 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11269 return NULL; 11270 } 11271 11272 if (red - blue < dispersion) { 11273 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11274 return NULL; 11275 } 11276 11277 nx = cpl_image_get_size_x(spectra); 11278 ny = cpl_image_get_size_y(spectra); 11279 11280 if (nx != cpl_image_get_size_x(spatial) || 11281 ny != cpl_image_get_size_y(spatial) || 11282 nx != cpl_image_get_size_x(wavecalib) || 11283 ny != cpl_image_get_size_y(wavecalib)) { 11284 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 11285 return NULL; 11286 } 11287 11288 nlambda = STRETCH_FACTOR * (red - blue) / dispersion; 11289 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 11290 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 11291 11292 data = cpl_image_get_data(spectra); 11293 sdata = cpl_image_get_data(spatial); 11294 wdata = cpl_image_get_data(wavecalib); 11295 11296 nslits = cpl_table_get_nrow(slits); 11297 slit_id = cpl_table_get_data_int(slits, "slit_id"); 11298 order = cpl_table_get_ncol(polytraces) - 2; 11299 position = cpl_table_get_data_int(slits, "position"); 11300 length = cpl_table_get_data_int(slits, "length"); 11301 11302 exslit = cpl_calloc(nslits, sizeof(cpl_image *)); 11303 11304 for (i = 0; i < nslits; i++) { 11305 11306 if (length == 0) 11307 continue; 11308 11309 /* 11310 * Note that the x coordinate of the reference pixels on the CCD 11311 * is taken arbitrarily at the top end of each slit. This wouldn't 11312 * be entirely correct in case of curved slits, or in presence of 11313 * heavy distortions: in such cases the spatial resampling is 11314 * really performed across a wide range of wavelengths. But 11315 * the lag between top and bottom spectral curvature models 11316 * would introduce even in such cases negligible effects on 11317 * the spectral spatial resampling. 11318 */ 11319 11320 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 11321 11322 start_pixel = refpixel - pixel_below; 11323 if (start_pixel < 1) 11324 start_pixel = 1; 11325 11326 end_pixel = refpixel + pixel_above; 11327 if (end_pixel > nx) 11328 end_pixel = nx; 11329 11330 /* 11331 * Recover from the table of spectral curvature coefficients 11332 * the curvature polynomials. 11333 */ 11334 11335 missing_top = 0; 11336 polytop = cpl_polynomial_new(1); 11337 for (k = 0; k <= order; k++) { 11338 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 11339 if (null) { 11340 cpl_polynomial_delete(polytop); 11341 missing_top = 1; 11342 break; 11343 } 11344 cpl_polynomial_set_coeff(polytop, &k, coeff); 11345 } 11346 11347 missing_bot = 0; 11348 polybot = cpl_polynomial_new(1); 11349 for (k = 0; k <= order; k++) { 11350 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 11351 if (null) { 11352 cpl_polynomial_delete(polybot); 11353 missing_bot = 1; 11354 break; 11355 } 11356 cpl_polynomial_set_coeff(polybot, &k, coeff); 11357 } 11358 11359 if (missing_top && missing_bot) { 11360 cpl_msg_debug(func, "Slit %d was not traced: no extraction!", 11361 slit_id[i]); 11362 continue; 11363 } 11364 11365 /* 11366 * In case just one of the two edges was not traced, the other 11367 * edge curvature model is duplicated and shifted to the other 11368 * end of the slit: better than nothing! 11369 */ 11370 11371 if (missing_top) { 11372 cpl_msg_debug(func, "Upper edge of slit %d was not traced: " 11373 "the spectral curvature of the lower edge " 11374 "is used instead.", slit_id[i]); 11375 polytop = cpl_polynomial_duplicate(polybot); 11376 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11377 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11378 k = 0; 11379 coeff = cpl_polynomial_get_coeff(polybot, &k); 11380 coeff += ytop - ybot; 11381 cpl_polynomial_set_coeff(polytop, &k, coeff); 11382 } 11383 11384 if (missing_bot) { 11385 cpl_msg_debug(func, "Lower edge of slit %d was not traced: " 11386 "the spectral curvature of the upper edge " 11387 "is used instead.", slit_id[i]); 11388 polybot = cpl_polynomial_duplicate(polytop); 11389 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 11390 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 11391 k = 0; 11392 coeff = cpl_polynomial_get_coeff(polytop, &k); 11393 coeff -= ytop - ybot; 11394 cpl_polynomial_set_coeff(polybot, &k, coeff); 11395 } 11396 11397 /* 11398 * Allocate image for current extracted slit 11399 */ 11400 11401 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 11402 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 11403 npseudo = ceil(top-bot) + 1; 11404 11405 if (npseudo < 1) { 11406 cpl_polynomial_delete(polytop); 11407 cpl_polynomial_delete(polybot); 11408 cpl_msg_debug(func, "Slit %d was badly traced: no extraction!", 11409 slit_id[i]); 11410 continue; 11411 } 11412 11413 exslit[i] = cpl_image_new(nlambda, npseudo+1, CPL_TYPE_FLOAT); 11414 xdata = cpl_image_get_data(exslit[i]); 11415 11416 /* 11417 * Write interpolated spectral values to remapped slit spectrum. 11418 */ 11419 11420 for (x = start_pixel; x < end_pixel; x++) { 11421 top = cpl_polynomial_eval_1d(polytop, x, NULL); 11422 bot = cpl_polynomial_eval_1d(polybot, x, NULL); 11423 itop = top + 1; 11424 ibot = bot; 11425 if (itop < 0) 11426 itop = 0; 11427 if (itop > ny - 1) 11428 itop = ny - 1; 11429 if (ibot < 0) 11430 ibot = 0; 11431 if (ibot > ny - 1) 11432 ibot = ny - 1; 11433 for (y = ibot; y < itop; y++) { 11434 lambda11 = wdata[x + y*nx]; 11435 if (lambda11 < 1.0) /* Impossible wavelength */ 11436 continue; 11437 space11 = sdata[x + y*nx]; 11438 if (space11 < 0.0) /* Impossible spatial coordinate */ 11439 continue; 11440 lambda01 = wdata[x - 1 + y*nx]; 11441 if (lambda01 < 1.0) /* Impossible wavelength */ 11442 continue; 11443 space01 = sdata[x - 1 + y*nx]; 11444 if (space01 < 0.0) /* Impossible spatial coordinate */ 11445 continue; 11446 11447 shift = 0; 11448 11449 /****+ 11450 if (wdata[x + (y+1)*nx] > 1.0) { 11451 if (wdata[x + (y+1)*nx] - lambda11 > 0) { 11452 shift = -1; 11453 while (wdata[x + shift + (y+1)*nx] - lambda11 > 0) 11454 shift--; 11455 if (lambda11 - wdata[x + shift + (y+1)*nx] > 11456 wdata[x + shift + 1 + (y+1)*nx] - lambda11) { 11457 shift++; 11458 } 11459 } 11460 else { 11461 shift = 1; 11462 while (wdata[x + shift + (y+1)*nx] - lambda11 < 0) 11463 shift++; 11464 if (wdata[x + shift + (y+1)*nx] - lambda11 > 11465 lambda11 - wdata[x + shift + 1 + (y+1)*nx]) { 11466 shift--; 11467 } 11468 } 11469 } 11470 ****/ 11471 11472 /**** 11473 printf("y = %d, shift = %d\n", y, shift); 11474 ****/ 11475 11476 lambda10 = wdata[x + shift + (y+1)*nx]; 11477 if (lambda10 < 1.0) /* Impossible wavelength */ 11478 continue; 11479 space10 = sdata[x + shift + (y+1)*nx]; 11480 if (space10 < 0.0) /* Impossible spatial coordinate */ 11481 continue; 11482 lambda00 = wdata[x - 1 + shift + (y+1)*nx]; 11483 if (lambda00 < 1.0) /* Impossible wavelength */ 11484 continue; 11485 space00 = sdata[x - 1 + shift + (y+1)*nx]; 11486 if (space00 < 0.0) /* Impossible spatial coordinate */ 11487 continue; 11488 11489 /* 11490 * Find the variation in lambda and space in this 11491 * position for each CCD pixel (both quantities are 11492 * expected to be positive). 11493 */ 11494 11495 dL = lambda11 - lambda01; 11496 dS = space11 - space10; 11497 11498 /* 11499 * Find the position (L,S) of the output pixel 11500 * (by integer truncation). 11501 */ 11502 11503 L = (lambda11 - blue)/dispersion + 0.5; 11504 S = space11 + 0.5; /* Counted from top! */ 11505 11506 if (L < 0 || L >= nlambda) 11507 continue; 11508 if (S < 0 || S > npseudo) 11509 continue; 11510 11511 /* 11512 * Find the coordinate of pixel (L,S) 11513 */ 11514 11515 lambda = blue + L*dispersion; 11516 space = S; 11517 11518 /* 11519 * Find the interpolation point on the CCD: it is 11520 * defined as the (positive) distance from current 11521 * CCD pixel (x,y) of the interpolation point (x',y'), 11522 * measured in CCD pixels. The interpolation point 11523 * is located between the four CCD pixels selected 11524 * above. 11525 */ 11526 11527 xfrac = (lambda11-lambda)/dL; 11528 yfrac = (space11-space)/dS; 11529 11530 /* 11531 if (xfrac < 0.0 || xfrac > 1.0 || yfrac < 0.0 || yfrac > 1.0) 11532 printf("xyfrac = %f, %f\n", xfrac, yfrac); 11533 */ 11534 11535 /* 11536 * Get the four values to interpolate 11537 */ 11538 11539 value11 = data[x + y*nx]; 11540 value01 = data[x - 1 + y*nx]; 11541 value10 = data[x + shift + (y+1)*nx]; 11542 value00 = data[x + shift - 1 + (y+1)*nx]; 11543 11544 /* 11545 * Interpolation 11546 */ 11547 11548 value1 = (1-xfrac)*value11 + xfrac*value01; 11549 value0 = (1-xfrac)*value10 + xfrac*value00; 11550 value = (1-yfrac)*value1 + yfrac*value0; 11551 11552 /* 11553 * Write this value to the appropriate (L,S) coordinate 11554 * on output slit 11555 */ 11556 11557 xdata[L + nlambda*(npseudo-S)] = value; 11558 11559 } 11560 } 11561 cpl_polynomial_delete(polytop); 11562 cpl_polynomial_delete(polybot); 11563 } 11564 11565 /* 11566 * Now all the slits images are copied to a single image 11567 */ 11568 11569 ysize = 0; 11570 for (i = 0; i < nslits; i++) 11571 if (exslit[i]) 11572 ysize += cpl_image_get_size_y(exslit[i]); 11573 11574 remapped = cpl_image_new(nlambda, ysize, CPL_TYPE_FLOAT); 11575 11576 yint = -1; 11577 for (i = 0; i < nslits; i++) { 11578 if (exslit[i]) { 11579 yint += cpl_image_get_size_y(exslit[i]); 11580 cpl_image_copy(remapped, exslit[i], 1, ysize - yint); 11581 cpl_image_delete(exslit[i]); 11582 cpl_table_set_int(slits, "position", i, ysize - yint - 1); 11583 } 11584 } 11585 11586 cpl_free(exslit); 11587 11588 return remapped; 11589 11590 } 11591 11592 11625 cpl_table *mos_sky_map_super(cpl_image *spectra, cpl_image *wavemap, 11626 double dispersion, double factor, int minpoints, 11627 cpl_image *skymap) 11628 { 11629 const char *func = "mos_sky_map_super"; 11630 11631 cpl_vector **vector; 11632 cpl_vector **wvector; 11633 double firstLambda, lastLambda; 11634 double lambda, lambda1, lambda2; 11635 double value, value1, value2; 11636 double frac; 11637 float min, max; 11638 int *count; 11639 int nbin, bin; 11640 int nx, ny, npix; 11641 int first_valid, valid_bins; 11642 int i, j; 11643 11644 cpl_table *sky; 11645 double *sky_spectrum; 11646 double *sky_wave; 11647 float *data; 11648 float *sdata; 11649 float *kdata; 11650 11651 11652 if (spectra == NULL || wavemap == NULL || skymap == NULL) { 11653 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11654 return NULL; 11655 } 11656 11657 if (dispersion <= 0.0) { 11658 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11659 cpl_msg_error(func, "Negative dispersion: %s", cpl_error_get_message()); 11660 return NULL; 11661 } 11662 11663 nx = cpl_image_get_size_x(spectra); 11664 ny = cpl_image_get_size_y(spectra); 11665 npix = nx * ny; 11666 11667 if (nx != cpl_image_get_size_x(wavemap) || 11668 ny != cpl_image_get_size_y(wavemap) || 11669 nx != cpl_image_get_size_x(skymap) || 11670 ny != cpl_image_get_size_y(skymap)) { 11671 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 11672 cpl_msg_error(func, "Image sizes: %s", cpl_error_get_message()); 11673 return NULL; 11674 } 11675 11676 if (factor < 1.0) { 11677 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11678 cpl_msg_error(func, "Undersampling (%f): %s", factor, 11679 cpl_error_get_message()); 11680 return NULL; 11681 } 11682 11683 if (minpoints < 0) { 11684 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11685 cpl_msg_error(func, "Negative threshold: %s", cpl_error_get_message()); 11686 return NULL; 11687 } 11688 11689 dispersion /= factor; 11690 11691 11692 /* 11693 * Find bluest and reddest wavelengths in the whole image 11694 */ 11695 11696 data = cpl_image_get_data(wavemap); 11697 11698 for (i = 0; i < npix; i++) { 11699 if (data[i] > 1.0) { 11700 min = max = data[i]; 11701 j = i+1; 11702 break; 11703 } 11704 } 11705 11706 for (i = j; i < npix; i++) { 11707 if (data[i] < 1.0) /* Impossible wavelength */ 11708 continue; 11709 if (min > data[i]) 11710 min = data[i]; 11711 if (max < data[i]) 11712 max = data[i]; 11713 } 11714 11715 firstLambda = min; 11716 lastLambda = max; 11717 11718 11719 /* 11720 * Determine length of median spectrum 11721 */ 11722 11723 nbin = (lastLambda - firstLambda) / dispersion; 11724 11725 /* 11726 * Count how many values will be found for each spectral bin. 11727 * The ith bin has a wavelength range from firstLambda + i*dispersion 11728 * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and 11729 * it is assigned to its central wavelength. 11730 */ 11731 11732 count = cpl_calloc(nbin, sizeof(int)); 11733 11734 data = cpl_image_get_data(wavemap); 11735 11736 for (i = 0; i < npix; i++) { 11737 if (data[i] < 1.0) 11738 continue; 11739 bin = (data[i] - firstLambda) / dispersion; 11740 if (bin < nbin) /* Safer */ 11741 count[bin]++; 11742 } 11743 11744 valid_bins = 0; 11745 for (i = 0; i < nbin; i++) 11746 if (count[i] >= minpoints) 11747 valid_bins++; 11748 11749 if (valid_bins < nbin/3) { 11750 cpl_msg_warning(func, "Cannot determine a good global sky " 11751 "spectrum from input data"); 11752 return NULL; 11753 } 11754 11755 11756 /* 11757 * Allocate an array of vectors with the appropriate size, to 11758 * contain a list of all the spectral pixels values. At the same 11759 * time, reset the array of counters (because we will have to 11760 * count again...). 11761 */ 11762 11763 vector = cpl_calloc(nbin, sizeof(cpl_vector *)); 11764 wvector = cpl_calloc(nbin, sizeof(cpl_vector *)); 11765 for (i = 0; i < nbin; i++) { 11766 if (count[i] >= minpoints) { 11767 vector[i] = cpl_vector_new(count[i]); 11768 wvector[i] = cpl_vector_new(count[i]); 11769 } 11770 count[i] = 0; 11771 } 11772 11773 11774 /* 11775 * Read the wavemap and the spectral images, and add the data values 11776 * to the appropriate wavelength bins 11777 */ 11778 11779 data = cpl_image_get_data(wavemap); 11780 sdata = cpl_image_get_data(spectra); 11781 11782 for (i = 0; i < npix; i++) { 11783 if (data[i] < 1.0) 11784 continue; 11785 bin = (data[i] - firstLambda) / dispersion; 11786 if (bin < nbin) { /* Safer */ 11787 if (vector[bin]) { 11788 cpl_vector_set(vector[bin], count[bin], sdata[i]); 11789 cpl_vector_set(wvector[bin], count[bin], data[i]); 11790 } 11791 count[bin]++; 11792 } 11793 } 11794 11795 11796 /* 11797 * Compute the median flux for each wavelength bin, and destroy 11798 * at the same time the used vectors 11799 */ 11800 11801 sky_spectrum = cpl_calloc(nbin, sizeof(double)); 11802 sky_wave = cpl_calloc(nbin, sizeof(double)); 11803 for (i = 0; i < nbin; i++) { 11804 if (vector[i]) { 11805 sky_spectrum[i] = cpl_vector_get_median_const(vector[i]); 11806 sky_wave[i] = cpl_vector_get_median_const(wvector[i]); 11807 cpl_vector_delete(vector[i]); 11808 cpl_vector_delete(wvector[i]); 11809 } 11810 } 11811 11812 cpl_free(vector); 11813 cpl_free(wvector); 11814 11815 11816 /* 11817 * Here possible gaps in the final spectrum are filled by interpolation 11818 */ 11819 11820 for (i = 0; i < nbin; i++) { 11821 if (count[i] >= minpoints) { 11822 first_valid = i; 11823 break; 11824 } 11825 } 11826 11827 for (i = first_valid; i < nbin; i++) { 11828 if (count[i] < minpoints) { 11829 sky_wave[i] = firstLambda + (i+0.5)*dispersion; 11830 for (j = i+1; j < nbin; j++) { 11831 if (count[j] >= minpoints) { 11832 if (sky_wave[j] - sky_wave[i-1] < 0.1) { 11833 sky_spectrum[i] = (sky_spectrum[j] + sky_spectrum[i-1]) 11834 / 2; 11835 } 11836 else { 11837 frac = (sky_wave[i] - sky_wave[i-1]) 11838 / (sky_wave[j] - sky_wave[i-1]); 11839 sky_spectrum[i] = frac * sky_spectrum[j] 11840 + (1 - frac) * sky_spectrum[i-1]; 11841 } 11842 } 11843 } 11844 } 11845 } 11846 11847 11848 /* 11849 * Create the output table 11850 */ 11851 11852 sky = cpl_table_new(nbin); 11853 cpl_table_wrap_double(sky, sky_wave, "wavelength"); 11854 cpl_table_wrap_double(sky, sky_spectrum, "sky"); 11855 cpl_table_wrap_int(sky, count, "npoints"); 11856 11857 11858 /* 11859 * Fill the sky map 11860 */ 11861 11862 data = cpl_image_get_data(wavemap); 11863 sdata = cpl_image_get_data(spectra); 11864 kdata = cpl_image_get_data(skymap); 11865 11866 for (i = 0; i < npix; i++) { 11867 11868 /* 11869 * Currently based on linear interpolation 11870 */ 11871 11872 lambda = data[i]; 11873 if (lambda < 1.0) 11874 continue; 11875 bin = (lambda - firstLambda) / dispersion; 11876 lambda1 = sky_wave[bin]; 11877 value1 = sky_spectrum[bin]; 11878 if (lambda1 < lambda) { 11879 bin++; 11880 if (bin < nbin) { 11881 lambda2 = sky_wave[bin]; 11882 value2 = sky_spectrum[bin]; 11883 if (lambda2 - lambda1 < 0.1) { 11884 value = (value1 + value2) / 2; 11885 } 11886 else { 11887 frac = (lambda - lambda1) / (lambda2 - lambda1); 11888 value = frac * value2 + (1 - frac) * value1; 11889 } 11890 } 11891 else { 11892 value = value1; 11893 } 11894 } 11895 else { 11896 if (bin > 0) { 11897 bin--; 11898 lambda2 = lambda1; 11899 value2 = value1; 11900 lambda1 = sky_wave[bin]; 11901 value1 = sky_spectrum[bin]; 11902 if (lambda2 - lambda1 < 0.1) { 11903 value = (value1 + value2) / 2; 11904 } 11905 else { 11906 frac = (lambda - lambda1) / (lambda2 - lambda1); 11907 value = frac * value2 + (1 - frac) * value1; 11908 } 11909 } 11910 else { 11911 value = value1; 11912 } 11913 } 11914 kdata[i] = value; 11915 } 11916 11917 if (first_valid) 11918 cpl_table_erase_window(sky, 0, first_valid); 11919 11920 return sky; 11921 11922 } 11923 11924 11958 cpl_table *mos_sky_map(cpl_image *spectra, cpl_image *wavemap, 11959 double dispersion, cpl_image *skymap) 11960 { 11961 const char *func = "mos_sky_map"; 11962 11963 cpl_vector **vector; 11964 double firstLambda, lastLambda; 11965 double lambda, lambda1, lambda2; 11966 double value, value1, value2; 11967 float min, max; 11968 int *count; 11969 int nbin, bin; 11970 int nx, ny, npix; 11971 int i, j; 11972 11973 cpl_table *sky; 11974 double *sky_spectrum; 11975 float *data; 11976 float *sdata; 11977 float *kdata; 11978 double *wdata; 11979 11980 11981 if (spectra == NULL || wavemap == NULL || skymap == NULL) { 11982 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 11983 return NULL; 11984 } 11985 11986 if (dispersion <= 0.0) { 11987 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 11988 return NULL; 11989 } 11990 11991 nx = cpl_image_get_size_x(spectra); 11992 ny = cpl_image_get_size_y(spectra); 11993 npix = nx * ny; 11994 11995 if (nx != cpl_image_get_size_x(wavemap) || 11996 ny != cpl_image_get_size_y(wavemap) || 11997 nx != cpl_image_get_size_x(skymap) || 11998 ny != cpl_image_get_size_y(skymap)) { 11999 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 12000 return NULL; 12001 } 12002 12003 12004 /* 12005 * Find bluest and reddest wavelengths in the whole image 12006 */ 12007 12008 data = cpl_image_get_data(wavemap); 12009 12010 for (i = 0; i < npix; i++) { 12011 if (data[i] > 1.0) { 12012 min = max = data[i]; 12013 j = i+1; 12014 break; 12015 } 12016 } 12017 12018 for (i = j; i < npix; i++) { 12019 if (data[i] < 1.0) /* Impossible wavelength */ 12020 continue; 12021 if (min > data[i]) 12022 min = data[i]; 12023 if (max < data[i]) 12024 max = data[i]; 12025 } 12026 12027 firstLambda = min; 12028 lastLambda = max; 12029 12030 12031 /* 12032 * Determine length of median spectrum 12033 */ 12034 12035 nbin = (lastLambda - firstLambda) / dispersion; 12036 12037 /* 12038 * Count how many values will be found for each spectral bin. 12039 * The ith bin has a wavelength range from firstLambda + i*dispersion 12040 * (inclusive) to firstLambda + (i+1)*dispersion (exclusive), and 12041 * it is assigned to its central wavelength. 12042 */ 12043 12044 count = cpl_calloc(nbin, sizeof(int)); 12045 12046 data = cpl_image_get_data(wavemap); 12047 12048 for (i = 0; i < npix; i++) { 12049 if (data[i] < 1.0) 12050 continue; 12051 bin = (data[i] - firstLambda) / dispersion; 12052 if (bin < nbin) /* Safer */ 12053 count[bin]++; 12054 } 12055 12056 12057 /* 12058 * Allocate an array of vectors with the appropriate size, to 12059 * contain a list of all the spectral pixels values. At the same 12060 * time, reset the array of counters (because we will have to 12061 * count again...). 12062 */ 12063 12064 vector = cpl_calloc(nbin, sizeof(cpl_vector *)); 12065 for (i = 0; i < nbin; i++) { 12066 if (count[i]) 12067 vector[i] = cpl_vector_new(count[i]); 12068 else 12069 vector[i] = NULL; 12070 count[i] = 0; 12071 } 12072 12073 12074 /* 12075 * Read the wavemap and the spectral images, and add the data values 12076 * to the appropriate wavelength bins 12077 */ 12078 12079 data = cpl_image_get_data(wavemap); 12080 sdata = cpl_image_get_data(spectra); 12081 12082 for (i = 0; i < npix; i++) { 12083 if (data[i] < 1.0) 12084 continue; 12085 bin = (data[i] - firstLambda) / dispersion; 12086 if (bin < nbin) { /* Safer */ 12087 cpl_vector_set(vector[bin], count[bin], sdata[i]); 12088 count[bin]++; 12089 } 12090 } 12091 12092 12093 /* 12094 * Compute the median flux for each wavelength bin, and destroy 12095 * at the same time the used vectors 12096 */ 12097 12098 sky_spectrum = cpl_calloc(nbin, sizeof(double)); 12099 for (i = 0; i < nbin; i++) { 12100 if (vector[i]) { 12101 sky_spectrum[i] = cpl_vector_get_median_const(vector[i]); 12102 cpl_vector_delete(vector[i]); 12103 } 12104 } 12105 12106 cpl_free(vector); 12107 12108 12109 /* 12110 * Here possible gaps in the final spectrum should be filled 12111 * by interpolation 12112 */ 12113 12114 /* ... */ 12115 12116 /* 12117 * Create the output table 12118 */ 12119 12120 sky = cpl_table_new(nbin); 12121 cpl_table_new_column(sky, "wavelength", CPL_TYPE_DOUBLE); 12122 cpl_table_set_column_unit(sky, "wavelength", "pixel"); 12123 cpl_table_wrap_double(sky, sky_spectrum, "sky"); 12124 cpl_table_wrap_int(sky, count, "npoints"); 12125 for (i = 0; i < nbin; i++) 12126 cpl_table_set_double(sky, "wavelength", i, 12127 firstLambda + (i+0.5)*dispersion); 12128 12129 12130 /* 12131 * Fill the sky map 12132 */ 12133 12134 data = cpl_image_get_data(wavemap); 12135 sdata = cpl_image_get_data(spectra); 12136 kdata = cpl_image_get_data(skymap); 12137 wdata = cpl_table_get_data_double(sky, "wavelength"); 12138 12139 for (i = 0; i < npix; i++) { 12140 12141 /* 12142 * Currently based on linear interpolation 12143 */ 12144 12145 lambda = data[i]; 12146 if (lambda < 1.0) 12147 continue; 12148 bin = (lambda - firstLambda) / dispersion; 12149 lambda1 = wdata[bin]; 12150 value1 = sky_spectrum[bin]; 12151 if (lambda1 < lambda) { 12152 bin++; 12153 if (bin < nbin) { 12154 lambda2 = wdata[bin]; 12155 value2 = sky_spectrum[bin]; 12156 value = ((lambda2 - lambda)*value1 12157 + (lambda - lambda1)*value2) / dispersion; 12158 } 12159 else { 12160 value = value1; 12161 } 12162 } 12163 else { 12164 if (bin > 0) { 12165 bin--; 12166 lambda2 = lambda1; 12167 value2 = value1; 12168 lambda1 = wdata[bin]; 12169 value1 = sky_spectrum[bin]; 12170 value = ((lambda2 - lambda)*value1 12171 + (lambda - lambda1)*value2)/dispersion; 12172 } 12173 else { 12174 value = value1; 12175 } 12176 } 12177 kdata[i] = value; 12178 } 12179 12180 return sky; 12181 12182 } 12183 12184 12200 cpl_image *mos_sky_local_old(cpl_image *spectra, cpl_table *slits) 12201 { 12202 const char *func = "mos_sky_local_old"; 12203 12204 cpl_image *exslit; 12205 cpl_image *sky; 12206 cpl_image *skymap; 12207 float *data; 12208 float *sdata; 12209 int nx, ny; 12210 int xlow, ylow, xhig, yhig; 12211 int nslits; 12212 int *slit_id; 12213 int *position; 12214 int *length; 12215 int i, j, k; 12216 12217 12218 if (spectra == NULL) { 12219 cpl_msg_error(func, 12220 "A scientific rectified spectral image must be given"); 12221 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12222 return NULL; 12223 } 12224 12225 if (slits == NULL) { 12226 cpl_msg_error(func, "A slits position table must be given"); 12227 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12228 return NULL; 12229 } 12230 12231 nslits = cpl_table_get_nrow(slits); 12232 slit_id = cpl_table_get_data_int(slits, "slit_id"); 12233 position = cpl_table_get_data_int(slits, "position"); 12234 length = cpl_table_get_data_int(slits, "length"); 12235 12236 nx = cpl_image_get_size_x(spectra); 12237 ny = cpl_image_get_size_y(spectra); 12238 12239 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12240 12241 xlow = 1; 12242 xhig = nx; 12243 for (i = 0; i < nslits; i++) { 12244 12245 if (length[i] == 0) 12246 continue; 12247 12248 /* 12249 * Define the extraction boundaries. We DON'T write: 12250 * 12251 * ylow = position[i]; 12252 * yhig = ylow + length[i]; 12253 * 12254 * because the cpl_image pixels are counted from 1, and because in 12255 * cpl_image_extract() the coordinates of the last pixel are inclusive. 12256 */ 12257 12258 ylow = position[i] + 1; 12259 yhig = ylow + length[i] - 1; 12260 12261 exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig); 12262 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 12263 cpl_image_delete(exslit); 12264 12265 data = cpl_image_get_data(skymap); 12266 data += nx * position[i]; 12267 12268 for (j = 0; j < length[i]; j++) { 12269 sdata = cpl_image_get_data(sky); 12270 for (k = 0; k < nx; k++) { 12271 *data++ = *sdata++; 12272 } 12273 } 12274 12275 cpl_image_delete(sky); 12276 } 12277 12278 return skymap; 12279 12280 } 12281 12282 12302 cpl_image *mos_sky_local(cpl_image *spectra, cpl_table *slits, int order) 12303 { 12304 const char *func = "mos_sky_local"; 12305 12306 char name[MAX_COLNAME]; 12307 12308 cpl_polynomial *fit; 12309 cpl_vector *points; 12310 cpl_vector *values; 12311 cpl_vector *keep_points; 12312 cpl_vector *keep_values; 12313 cpl_image *exslit; 12314 cpl_image *sky; 12315 cpl_image *subtracted; 12316 cpl_image *profile; 12317 cpl_image *skymap; 12318 cpl_table *objects; 12319 float *data; 12320 float *sdata; 12321 float *xdata; 12322 double *vdata; 12323 double *pdata; 12324 double median; 12325 int nx, ny; 12326 int xlow, ylow, xhig, yhig; 12327 int nslits; 12328 int *slit_id; 12329 int *position; 12330 int *length; 12331 int *is_sky; 12332 int nsky, nbad; 12333 int maxobjects; 12334 int margin = 3; 12335 int radius = 6; 12336 int i, j, k; 12337 12338 12339 if (spectra == NULL) { 12340 cpl_msg_error(func, 12341 "A scientific rectified spectral image must be given"); 12342 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12343 return NULL; 12344 } 12345 12346 if (slits == NULL) { 12347 cpl_msg_error(func, "A slits position table must be given"); 12348 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12349 return NULL; 12350 } 12351 12352 if (order < 0) { 12353 cpl_msg_error(func, "Invalid fit order"); 12354 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12355 return NULL; 12356 } 12357 12358 nslits = cpl_table_get_nrow(slits); 12359 slit_id = cpl_table_get_data_int(slits, "slit_id"); 12360 position = cpl_table_get_data_int(slits, "position"); 12361 length = cpl_table_get_data_int(slits, "length"); 12362 12363 nx = cpl_image_get_size_x(spectra); 12364 ny = cpl_image_get_size_y(spectra); 12365 12366 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12367 12368 xlow = 1; 12369 xhig = nx; 12370 for (i = 0; i < nslits; i++) { 12371 12372 if (length[i] == 0) 12373 continue; 12374 12375 /* 12376 * Define the extraction boundaries. We DON'T write: 12377 * 12378 * ylow = position[i]; 12379 * yhig = ylow + length[i]; 12380 * 12381 * because the cpl_image pixels are counted from 1, and because in 12382 * cpl_image_extract() the coordinates of the last pixel are inclusive. 12383 */ 12384 12385 ylow = position[i] + 1; 12386 yhig = ylow + length[i] - 1; 12387 12388 exslit = cpl_image_extract(spectra, xlow, ylow, xhig, yhig); 12389 sky = cpl_image_collapse_median_create(exslit, 0, 0, 1); 12390 cpl_image_delete(exslit); 12391 12392 data = cpl_image_get_data(skymap); 12393 data += nx * position[i]; 12394 12395 for (j = 0; j < length[i]; j++) { 12396 sdata = cpl_image_get_data(sky); 12397 for (k = 0; k < nx; k++) { 12398 *data++ = *sdata++; 12399 } 12400 } 12401 12402 cpl_image_delete(sky); 12403 } 12404 12405 12406 /* 12407 * Preliminary sky-subtracted image 12408 */ 12409 12410 subtracted = cpl_image_duplicate(spectra); 12411 cpl_image_subtract(subtracted, skymap); 12412 cpl_image_delete(skymap); 12413 12414 12415 /* 12416 * Detect objects positions in all slits 12417 */ 12418 12419 objects = cpl_table_duplicate(slits); 12420 profile = mos_detect_objects(subtracted, objects, margin, radius, 0); 12421 cpl_image_delete(profile); 12422 cpl_image_delete(subtracted); 12423 12424 12425 /* 12426 * Flag the sky pixels. Note that maxobjects is intentionally 12427 * the max number of objects increased by one. 12428 */ 12429 12430 maxobjects = 1; 12431 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 12432 while (cpl_table_has_column(objects, name)) { 12433 maxobjects++; 12434 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 12435 } 12436 12437 is_sky = cpl_calloc(ny, sizeof(int)); 12438 12439 for (i = 0; i < nslits; i++) { 12440 12441 if (length[i] == 0) 12442 continue; 12443 12444 ylow = position[i] + margin; 12445 yhig = position[i] + length[i] - margin; 12446 12447 for (j = ylow; j < yhig; j++) 12448 is_sky[j] = 1; 12449 12450 for (j = 1; j < maxobjects; j++) { 12451 snprintf(name, MAX_COLNAME, "object_%d", j); 12452 if (cpl_table_is_valid(objects, name, i)) { 12453 snprintf(name, MAX_COLNAME, "start_%d", j); 12454 ylow = cpl_table_get_int(objects, name, i, NULL); 12455 snprintf(name, MAX_COLNAME, "end_%d", j); 12456 yhig = cpl_table_get_int(objects, name, i, NULL); 12457 for (k = ylow; k <= yhig; k++) 12458 is_sky[k] = 0; 12459 } 12460 } 12461 12462 12463 /* 12464 * Eliminate isolated sky points 12465 */ 12466 12467 ylow = position[i] + margin + 1; 12468 yhig = position[i] + length[i] - margin - 1; 12469 12470 for (j = ylow; j < yhig; j++) 12471 if (is_sky[j]) 12472 if (is_sky[j-1] == 0 && is_sky[j+1] == 0) 12473 is_sky[j] = 0; 12474 12475 } 12476 12477 12478 /* 12479 * Determination of the sky map 12480 */ 12481 12482 skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 12483 12484 for (i = 0; i < nslits; i++) { 12485 12486 if (length[i] == 0) 12487 continue; 12488 12489 ylow = position[i]; 12490 yhig = ylow + length[i]; 12491 12492 nsky = 0; 12493 for (j = ylow; j < yhig; j++) 12494 if (is_sky[j]) 12495 nsky++; 12496 12497 if (nsky > order + 1) { 12498 if (order) { 12499 points = cpl_vector_new(nsky); 12500 nsky = 0; 12501 for (j = ylow; j < yhig; j++) { 12502 if (is_sky[j]) { 12503 cpl_vector_set(points, nsky, j); 12504 nsky++; 12505 } 12506 } 12507 12508 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig); 12509 xdata = cpl_image_get_data(exslit); 12510 values = cpl_vector_new(nsky); 12511 12512 for (j = 0; j < nx; j++) { 12513 nsky = 0; 12514 for (k = ylow; k < yhig; k++) { 12515 if (is_sky[k]) { 12516 cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]); 12517 nsky++; 12518 } 12519 } 12520 12521 /* 12522 * Eliminate obvious outliers 12523 */ 12524 12525 median = cpl_vector_get_median_const(values); 12526 vdata = cpl_vector_get_data(values); 12527 pdata = cpl_vector_get_data(points); 12528 nbad = 0; 12529 for (k = 0; k < nsky; k++) { 12530 if (fabs(vdata[k] - median) < 100) { 12531 if (nbad) { 12532 vdata[k-nbad] = vdata[k]; 12533 pdata[k-nbad] = pdata[k]; 12534 } 12535 } 12536 else 12537 nbad++; 12538 } 12539 12540 if (nsky == nbad) 12541 continue; 12542 12543 if (nbad && nsky - nbad > order + 1) { 12544 keep_values = values; 12545 keep_points = points; 12546 values = cpl_vector_wrap(nsky-nbad, vdata); 12547 points = cpl_vector_wrap(nsky-nbad, pdata); 12548 } 12549 12550 if (nsky - nbad > order + 1) { 12551 12552 fit = cpl_polynomial_fit_1d_create(points, values, 12553 order, NULL); 12554 12555 if (fit) { 12556 for (k = ylow; k < yhig; k++) { 12557 xdata[j+(k-ylow)*nx] = 12558 cpl_polynomial_eval_1d(fit, k, NULL); 12559 } 12560 12561 cpl_polynomial_delete(fit); 12562 } 12563 else 12564 cpl_error_reset(); 12565 } 12566 else { 12567 for (k = 0; k < nsky; k++) { 12568 xdata[j+k*nx] = median; 12569 } 12570 } 12571 12572 if (nbad && nsky - nbad > order + 1) { 12573 cpl_vector_unwrap(values); 12574 cpl_vector_unwrap(points); 12575 values = keep_values; 12576 points = keep_points; 12577 } 12578 12579 if (nbad) { 12580 nsky = 0; 12581 for (k = ylow; k < yhig; k++) { 12582 if (is_sky[k]) { 12583 cpl_vector_set(points, nsky, k); 12584 nsky++; 12585 } 12586 } 12587 } 12588 12589 } 12590 12591 cpl_vector_delete(values); 12592 cpl_vector_delete(points); 12593 12594 cpl_image_copy(skymap, exslit, 1, ylow+1); 12595 cpl_image_delete(exslit); 12596 12597 } 12598 else { 12599 exslit = cpl_image_extract(spectra, 1, ylow+1, nx, yhig); 12600 xdata = cpl_image_get_data(exslit); 12601 values = cpl_vector_new(nsky); 12602 12603 for (j = 0; j < nx; j++) { 12604 nsky = 0; 12605 for (k = ylow; k < yhig; k++) { 12606 if (is_sky[k]) { 12607 cpl_vector_set(values, nsky, xdata[j+(k-ylow)*nx]); 12608 nsky++; 12609 } 12610 } 12611 12612 median = cpl_vector_get_median_const(values); 12613 12614 for (k = ylow; k < yhig; k++) 12615 xdata[j+(k-ylow)*nx] = median; 12616 12617 } 12618 12619 cpl_vector_delete(values); 12620 12621 cpl_image_copy(skymap, exslit, 1, ylow+1); 12622 cpl_image_delete(exslit); 12623 } 12624 } 12625 else 12626 cpl_msg_warning(func, "Too few sky points in slit %d", i + 1); 12627 } 12628 12629 cpl_free(is_sky); 12630 12631 return skymap; 12632 12633 } 12634 12635 12657 cpl_error_code mos_clean_cosmics(cpl_image *image, float gain, 12658 float threshold, float ratio) 12659 { 12660 const char *func = "mos_clean_cosmics"; 12661 12662 cpl_image *smoothImage; 12663 cpl_table *table; 12664 cpl_matrix *kernel; 12665 int *xdata; 12666 int *ydata; 12667 float *idata; 12668 float *sdata; 12669 float sigma, sum, value, smoothValue; 12670 double noise; 12671 int count; 12672 float fMax; 12673 int iMin, iMax, jMin, jMax, iPosMax, jPosMax; 12674 int xLen; 12675 int yLen; 12676 int nPix; 12677 int first = 1; /* position of first cosmic ray candidate 12678 encountered while scanning the image */ 12679 int pos, i, j, k, l, ii, jj, iii = 0, jjj = 0; 12680 int numCosmic = 0; 12681 int found, foundContiguousCandidate; 12682 int *cosmic; 12683 12684 12685 if (image == NULL) 12686 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12687 12688 12689 /* 12690 * "cosmic" is a flags holder (initialized to zero): 12691 * 12692 * -1 = candidate for cosmic ray 12693 * 0 = not a cosmic 12694 * 1 = a cosmic ray 12695 * 2 = member of current group of contiguous candidates 12696 * 3 = examined member of current group 12697 */ 12698 12699 xLen = cpl_image_get_size_x(image); 12700 yLen = cpl_image_get_size_y(image); 12701 12702 if (xLen < 4 || yLen < 4) 12703 return CPL_ERROR_NONE; 12704 12705 nPix = xLen * yLen; 12706 12707 /* 12708 * Noise estimation from negative offsets in image. Note that this 12709 * assumes that the background level (skyLevel) has already been 12710 * subtracted from the data. In this way we estimate the noise due 12711 * to detector readout and to the background signal level (before 12712 * it were removed). Theoretically this is given by 12713 * 12714 * noise = sqrt(ron^2 + skyLevel/gain) 12715 * 12716 * where ron is the read-out-noise. To this we will sum the noise 12717 * contribution due to any increase of the signal above the background 12718 * by an amount scienceLevel. Theoretically the total noise is given by 12719 * 12720 * totalNoise = sqrt(ron^2 + (skyLevel+scienceLevel)/gain) 12721 * 12722 * that is 12723 * 12724 * totalNoise = sqrt(noise^2 + scienceLevel/gain) 12725 * 12726 */ 12727 12728 idata = cpl_image_get_data(image); 12729 noise = 0.0; 12730 count = 0; 12731 12732 for (i = 0; i < nPix; i++) { 12733 if (idata[i] < -0.00001) { 12734 noise -= idata[i]; 12735 count++; 12736 } 12737 } 12738 12739 noise /= count; 12740 noise *= 1.25; /* Factor to convert average deviation to sigma */ 12741 12742 cosmic = cpl_calloc(nPix, sizeof(int)); 12743 12744 if (threshold < 0.) 12745 threshold = 4.0; 12746 if (ratio < 0.) 12747 ratio = 2.0; 12748 12749 kernel = cpl_matrix_new(3, 3); 12750 cpl_matrix_fill(kernel, 1.0); 12751 cpl_matrix_set(kernel, 1, 1, 0.0); 12752 smoothImage = cpl_image_filter_median(image, kernel); 12753 cpl_matrix_delete(kernel); 12754 12755 /* 12756 * Loop on images pixels, searching for cosmic rays candidates. 12757 * Border pixels are currently excluded (they cannot contain 12758 * candidates), to avoid that the search for groups of contiguous 12759 * pixels would ever go out of image boundaries. In future we may 12760 * overcome this limit, adding an appropriate check when contiguous 12761 * pixels are searched. 12762 */ 12763 12764 sdata = cpl_image_get_data(smoothImage); 12765 12766 for (j = 1; j < yLen - 1; j++) { 12767 for (i = 1; i < xLen - 1; i++) { 12768 value = idata[i + j * xLen]; 12769 smoothValue = sdata[i + j * xLen]; 12770 if (smoothValue < 1.0) 12771 smoothValue = 1.0; 12772 sigma = sqrt(noise * noise + smoothValue / gain); 12773 if (value - smoothValue >= threshold * sigma) 12774 cosmic[i + j * xLen] = -1; 12775 } 12776 } 12777 12778 cpl_image_delete(smoothImage); 12779 12780 12781 /* 12782 * Search for groups of contiguous cosmic rays candidates. 12783 */ 12784 12785 do { 12786 found = 0; 12787 for (pos = first; pos < nPix; pos++) { 12788 if (cosmic[pos] == -1) { 12789 cosmic[pos] = 2; /* Candidate found. */ 12790 i = pos % xLen; /* Its coordinates. */ 12791 j = pos / xLen; 12792 first = pos; 12793 first++; /* ??? really necessary? */ 12794 found = 1; 12795 break; 12796 } 12797 } 12798 12799 if (found) { 12800 12801 /* 12802 * Determine new group of contiguous cosmic rays candidates. 12803 * Initialize the working box boundaries, iMin, iMax, jMin, jMax, 12804 * and the value of the max pixel and its position, fMax, iPosMax, 12805 * jPosMax. 12806 */ 12807 12808 iMin = iMax = iPosMax = i; 12809 jMin = jMax = jPosMax = j; 12810 fMax = idata[i + j * xLen]; 12811 12812 do { 12813 foundContiguousCandidate = 0; 12814 for (l = 0; l <= 1; l++) { 12815 for (k = 0; k <= 1; k++) { 12816 12817 /* 12818 * Looping on 4 pixels to North, East, South and West 12819 */ 12820 12821 ii = i + k - l; 12822 jj = j + k + l - 1; 12823 if (cosmic[ii + jj * xLen] == -1) { 12824 foundContiguousCandidate = 1; 12825 cosmic[ii + jj * xLen] = 2; 12826 /* Candidate belongs to current group */ 12827 iii = ii; /* Keep its position */ 12828 jjj = jj; 12829 12830 /* 12831 * Upgrade search box 12832 */ 12833 12834 if (ii < iMin) 12835 iMin = ii; 12836 if (ii > iMax) 12837 iMax = ii; 12838 if (jj < jMin) 12839 jMin = jj; 12840 if (jj > jMax) 12841 jMax = jj; 12842 12843 if (idata[ii + jj * xLen] > fMax) { 12844 fMax = idata[ii + jj * xLen]; 12845 iPosMax = ii; 12846 jPosMax = jj; 12847 } 12848 } 12849 } 12850 } 12851 12852 /* 12853 * We are done exploring the "cross". Now mark as "examined" 12854 * the current candidate (at the center of the cross): 12855 */ 12856 12857 cosmic[i + j * xLen] = 3; /* It may probably be set to 1 now */ 12858 12859 if (foundContiguousCandidate) { 12860 12861 /* 12862 * Pass (arbitrarily) the coordinates of the LAST found 12863 * candidate 12864 */ 12865 12866 i = iii; 12867 j = jjj; 12868 12869 /* 12870 * Skip the rest, continue loop on new candidate 12871 */ 12872 12873 continue; 12874 } 12875 12876 12877 /* 12878 * Look for leftovers in the (growing!) search box 12879 */ 12880 12881 for (l = jMin; l <= jMax; l++) { 12882 for (k = iMin; k <= iMax; k++) { 12883 if (cosmic[k + l * xLen] == 2) { 12884 i = k; 12885 j = l; 12886 foundContiguousCandidate = 1; 12887 break; 12888 } 12889 } 12890 if (foundContiguousCandidate) 12891 break; 12892 } 12893 } while (foundContiguousCandidate); 12894 12895 12896 /* 12897 * No more contiguous candidates are found. Decide now 12898 * whether the current group is a cosmic ray or not. 12899 */ 12900 12901 sum = 0.; /* Sum of 8 pixels around max position */ 12902 for (l = -1; l <= 1; l++) { 12903 for (k = -1; k <= 1; k++) { 12904 if (l != 0 || k != 0) { 12905 sum += idata[iPosMax + k + (jPosMax + l) * xLen]; 12906 } 12907 } 12908 } 12909 12910 sum /= 8.; 12911 if (fMax > ratio * sum) { 12912 for (l = jMin - 1; l <= jMax + 1; l++) { 12913 for (k = iMin - 1; k <= iMax + 1; k++) { 12914 if (cosmic[k + l * xLen] == 3) { 12915 cosmic[k + l * xLen] = 1; 12916 numCosmic++; 12917 } 12918 } 12919 } 12920 } 12921 else { 12922 for (l = jMin - 1; l <= jMax + 1; l++) { 12923 for (k = iMin - 1; k <= iMax + 1; k++) { 12924 if (cosmic[k + l * xLen] != -1) { 12925 if (cosmic[k + l * xLen] == 1) 12926 numCosmic--; 12927 cosmic[k + l * xLen] = 0; 12928 } 12929 } 12930 } 12931 } 12932 } 12933 } while (found); 12934 12935 12936 /* 12937 * Prepare table containing cosmic rays coordinates. 12938 */ 12939 12940 table = cpl_table_new(numCosmic); 12941 cpl_table_new_column(table, "x", CPL_TYPE_INT); 12942 cpl_table_new_column(table, "y", CPL_TYPE_INT); 12943 cpl_table_set_column_unit(table, "x", "pixel"); 12944 cpl_table_set_column_unit(table, "y", "pixel"); 12945 xdata = cpl_table_get_data_int(table, "x"); 12946 ydata = cpl_table_get_data_int(table, "y"); 12947 12948 for (pos = 0, i = 0; pos < nPix; pos++) { 12949 if (cosmic[pos] == 1) { 12950 xdata[i] = (pos % xLen); 12951 ydata[i] = (pos / xLen); 12952 i++; 12953 } 12954 } 12955 12956 mos_clean_bad_pixels(image, table, 1); 12957 12958 cpl_free(cosmic); 12959 cpl_table_delete(table); 12960 12961 return CPL_ERROR_NONE; 12962 12963 } 12964 12965 12966 cpl_error_code mos_clean_bad_pixels(cpl_image *image, cpl_table *table, 12967 int spectral) 12968 { 12969 const char *func = "mos_clean_cosmics"; 12970 12971 float *idata; 12972 int *isBadPix; 12973 int i, j, k, d; 12974 int xlen, ylen, totPix; 12975 int nBadPixels = 0; 12976 int sign, foundFirst; 12977 int *xValue = NULL; 12978 int *yValue = NULL; 12979 float save = 0.; 12980 double sumd; 12981 int cx, cy; 12982 int nPairs; 12983 float estimate[4]; 12984 int sx[] = {0, 1, 1, 1}; 12985 int sy[] = {1,-1, 0, 1}; 12986 int searchHorizon = 100; 12987 int percent = 15; 12988 12989 12990 if (image == NULL || table == NULL) 12991 return cpl_error_set(func, CPL_ERROR_NULL_INPUT); 12992 12993 if (1 != cpl_table_has_column(table, "x")) 12994 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 12995 12996 if (1 != cpl_table_has_column(table, "y")) 12997 return cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 12998 12999 if (CPL_TYPE_INT != cpl_table_get_column_type(table, "x")) 13000 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 13001 13002 if (CPL_TYPE_INT != cpl_table_get_column_type(table, "y")) 13003 return cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 13004 13005 nBadPixels = cpl_table_get_nrow(table); 13006 13007 if (nBadPixels) { 13008 xlen = cpl_image_get_size_x(image); 13009 ylen = cpl_image_get_size_y(image); 13010 idata = cpl_image_get_data(image); 13011 totPix = xlen * ylen; 13012 if (((float) nBadPixels) / ((float) totPix) < percent/100.) { 13013 isBadPix = cpl_calloc(totPix, sizeof(int)); 13014 } 13015 else { 13016 cpl_msg_warning(func, "Too many bad pixels (> %d%%): " 13017 "skip bad pixel correction", percent); 13018 return cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13019 } 13020 } 13021 else { 13022 cpl_msg_debug(func, "No pixel values to interpolate"); 13023 return CPL_ERROR_NONE; 13024 } 13025 13026 xValue = cpl_table_get_data_int(table, "x"); 13027 yValue = cpl_table_get_data_int(table, "y"); 13028 13029 for (i = 0; i < nBadPixels; i++) 13030 isBadPix[xValue[i] + yValue[i] * xlen] = 1; 13031 13032 for (i = 0; i < nBadPixels; i++) { 13033 13034 /* 13035 * Search for the closest good pixel along the 4 fundamental 13036 * directions (in both senses): 13037 * \ | / 13038 * \|/ 13039 * --- --- 13040 * /|\ 13041 * / | \ 13042 * 13043 * Then collect pairs of values to interpolate linearly. 13044 */ 13045 13046 nPairs = 0; 13047 for (j = 0; j < 4; j++) { 13048 13049 if (spectral) /* Just horizontal interpolation for spectral data */ 13050 if (j != 2) 13051 continue; 13052 13053 estimate[nPairs] = 0.; /* Pairs interpolations are stored here */ 13054 sumd = 0.; 13055 foundFirst = 0; 13056 for (k = 0; k < 2; k++) { 13057 sign = 2 * k - 1; 13058 d = 0; 13059 cx = xValue[i]; 13060 cy = yValue[i]; 13061 do { 13062 cx += sign * sx[j]; 13063 cy += sign * sy[j]; 13064 if (cx < 0 || cx >= xlen || cy < 0 || cy >= ylen) 13065 break; 13066 d++; 13067 } while (isBadPix[cx + cy * xlen] && d < searchHorizon); 13068 13069 if (cx >= 0 && cx < xlen && 13070 cy >= 0 && cy < ylen && d < searchHorizon) { 13071 13072 /* 13073 * In this block is cripted the linear interpolation... 13074 */ 13075 13076 save = idata[cx + cy * xlen]; 13077 estimate[nPairs] += save / d; 13078 sumd += 1. / (double) d; 13079 if (k) { 13080 estimate[nPairs] /= sumd; 13081 nPairs++; 13082 } 13083 else { 13084 foundFirst = 1; 13085 } 13086 } 13087 else { 13088 13089 /* 13090 * Image borders were crossed, incomplete pair of values 13091 */ 13092 13093 if (k) { 13094 if (foundFirst) { 13095 estimate[nPairs] = save; 13096 nPairs++; 13097 } 13098 } 13099 } 13100 } 13101 } 13102 13103 /* 13104 * Replace pixel value of the input image, corresponding to 13105 * the current bad pixel, with the median of the estimates 13106 * resulted from the 4 linear interpolations. 13107 */ 13108 13109 if (nPairs > 2) { 13110 idata[xValue[i] + yValue[i] * xlen] = 13111 cpl_tools_get_median_float(estimate, nPairs); 13112 } 13113 else if (nPairs == 2) { 13114 idata[xValue[i] + yValue[i] * xlen] = 13115 (estimate[0] + estimate[1]) / 2.; 13116 } 13117 else if (nPairs == 1) { 13118 idata[xValue[i] + yValue[i] * xlen] = estimate[0]; 13119 } 13120 else { 13121 cpl_msg_debug(func, "Cannot correct bad pixel %d,%d\n", 13122 xValue[i], yValue[i]); 13123 } 13124 } 13125 13126 cpl_free(isBadPix); 13127 13128 return CPL_ERROR_NONE; 13129 } 13130 13131 13161 cpl_image *mos_spatial_map(cpl_image *spectra, cpl_table *slits, 13162 cpl_table *polytraces, double reference, 13163 double blue, double red, double dispersion) 13164 { 13165 const char *func = "mos_spatial_map"; 13166 13167 const char *clab[6] = {"c0", "c1", "c2", "c3", "c4", "c5"}; 13168 /* Max order is 5 */ 13169 cpl_polynomial *polytop; 13170 cpl_polynomial *polybot; 13171 cpl_image *calibration; 13172 float *data; 13173 double top, bot; 13174 double coeff; 13175 double ytop, ybot; 13176 double ypos, yfra; 13177 double factor; 13178 int yint, yprev; 13179 int nslits; 13180 int npseudo; 13181 int *slit_id; 13182 int *length; 13183 int nx, ny; 13184 int pixel_above, pixel_below, refpixel, start_pixel, end_pixel; 13185 int missing_top, missing_bot; 13186 int null; 13187 int order; 13188 int i, j; 13189 cpl_size k; 13190 13191 13192 if (spectra == NULL || slits == NULL || polytraces == NULL) { 13193 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13194 return NULL; 13195 } 13196 13197 if (dispersion <= 0.0) { 13198 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13199 return NULL; 13200 } 13201 13202 if (red - blue < dispersion) { 13203 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13204 return NULL; 13205 } 13206 13207 nx = cpl_image_get_size_x(spectra); 13208 ny = cpl_image_get_size_y(spectra); 13209 13210 calibration = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 13211 data = cpl_image_get_data(calibration); 13212 13213 length = cpl_table_get_data_int(slits, "length"); 13214 nslits = cpl_table_get_nrow(slits); 13215 slit_id = cpl_table_get_data_int(slits, "slit_id"); 13216 order = cpl_table_get_ncol(polytraces) - 2; 13217 13218 /* 13219 * The spatial resampling is performed for a certain number of 13220 * pixels above and below the position of the reference wavelength: 13221 */ 13222 13223 pixel_above = STRETCH_FACTOR * (red - reference) / dispersion; 13224 pixel_below = STRETCH_FACTOR * (reference - blue) / dispersion; 13225 13226 for (i = 0; i < nslits; i++) { 13227 13228 if (length[i] == 0) 13229 continue; 13230 13231 /* 13232 * Note that the x coordinate of the reference pixels on the CCD 13233 * is taken arbitrarily at the top end of each slit. This wouldn't 13234 * be entirely correct in case of curved slits, or in presence of 13235 * heavy distortions: in such cases the spatial resampling is 13236 * really performed across a wide range of wavelengths. But 13237 * the lag between top and bottom spectral curvature models 13238 * would introduce even in such cases negligible effects on 13239 * the spectral spatial resampling. 13240 */ 13241 13242 refpixel = cpl_table_get_double(slits, "xtop", i, NULL); 13243 13244 start_pixel = refpixel - pixel_below; 13245 if (start_pixel < 0) 13246 start_pixel = 0; 13247 13248 end_pixel = refpixel + pixel_above; 13249 if (end_pixel > nx) 13250 end_pixel = nx; 13251 13252 /* 13253 * Recover from the table of spectral curvature coefficients 13254 * the curvature polynomials. 13255 */ 13256 13257 missing_top = 0; 13258 polytop = cpl_polynomial_new(1); 13259 for (k = 0; k <= order; k++) { 13260 coeff = cpl_table_get_double(polytraces, clab[k], 2*i, &null); 13261 if (null) { 13262 cpl_polynomial_delete(polytop); 13263 missing_top = 1; 13264 break; 13265 } 13266 cpl_polynomial_set_coeff(polytop, &k, coeff); 13267 } 13268 13269 missing_bot = 0; 13270 polybot = cpl_polynomial_new(1); 13271 for (k = 0; k <= order; k++) { 13272 coeff = cpl_table_get_double(polytraces, clab[k], 2*i+1, &null); 13273 if (null) { 13274 cpl_polynomial_delete(polybot); 13275 missing_bot = 1; 13276 break; 13277 } 13278 cpl_polynomial_set_coeff(polybot, &k, coeff); 13279 } 13280 13281 if (missing_top && missing_bot) { 13282 cpl_msg_warning(func, "Spatial map, slit %d was not traced!", 13283 slit_id[i]); 13284 continue; 13285 } 13286 13287 /* 13288 * In case just one of the two edges was not traced, the other 13289 * edge curvature model is duplicated and shifted to the other 13290 * end of the slit: better than nothing! 13291 */ 13292 13293 if (missing_top) { 13294 cpl_msg_warning(func, "Upper edge of slit %d was not traced: " 13295 "the spectral curvature of the lower edge " 13296 "is used instead.", slit_id[i]); 13297 polytop = cpl_polynomial_duplicate(polybot); 13298 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 13299 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 13300 k = 0; 13301 coeff = cpl_polynomial_get_coeff(polybot, &k); 13302 coeff += ytop - ybot; 13303 cpl_polynomial_set_coeff(polytop, &k, coeff); 13304 } 13305 13306 if (missing_bot) { 13307 cpl_msg_warning(func, "Lower edge of slit %d was not traced: " 13308 "the spectral curvature of the upper edge " 13309 "is used instead.", slit_id[i]); 13310 polybot = cpl_polynomial_duplicate(polytop); 13311 ytop = cpl_table_get_double(slits, "ytop", i, NULL); 13312 ybot = cpl_table_get_double(slits, "ybottom", i, NULL); 13313 k = 0; 13314 coeff = cpl_polynomial_get_coeff(polytop, &k); 13315 coeff -= ytop - ybot; 13316 cpl_polynomial_set_coeff(polybot, &k, coeff); 13317 } 13318 13319 top = cpl_polynomial_eval_1d(polytop, refpixel, NULL); 13320 bot = cpl_polynomial_eval_1d(polybot, refpixel, NULL); 13321 npseudo = ceil(top-bot) + 1; 13322 13323 if (npseudo < 1) { 13324 cpl_polynomial_delete(polytop); 13325 cpl_polynomial_delete(polybot); 13326 cpl_msg_warning(func, "Slit %d was badly traced: no extraction!", 13327 slit_id[i]); 13328 continue; 13329 } 13330 13331 for (j = start_pixel; j < end_pixel; j++) { 13332 top = cpl_polynomial_eval_1d(polytop, j, NULL); 13333 bot = cpl_polynomial_eval_1d(polybot, j, NULL); 13334 factor = (top-bot)/npseudo; 13335 for (k = 0; k <= npseudo; k++) { 13336 ypos = top - k*factor; 13337 yint = ypos; 13338 yfra = ypos - yint; 13339 if (yint >= 0 && yint < ny-1) { 13340 data[j + nx*yint] = (top-yint)/factor; 13341 if (k) { 13342 13343 /* 13344 * This is added to recover lost pixels on 13345 * the CCD image (pixels are lost because 13346 * the CCD pixels are less than npseudo+1). 13347 */ 13348 13349 if (yprev - yint > 1) { 13350 data[j + nx*(yint+1)] = (top-yint-1)/factor; 13351 } 13352 } 13353 } 13354 yprev = yint; 13355 } 13356 } 13357 cpl_polynomial_delete(polytop); 13358 cpl_polynomial_delete(polybot); 13359 } 13360 13361 return calibration; 13362 } 13363 13364 13427 cpl_image *mos_detect_objects(cpl_image *image, cpl_table *slits, int margin, 13428 int maxradius, int conradius) 13429 { 13430 const char *func = "mos_detect_objects"; 13431 13432 cpl_image *profile; 13433 float *pdata; 13434 float *p; 13435 13436 char name[MAX_COLNAME]; 13437 13438 int nslits; 13439 int npeaks; 13440 int nobjects, objpos, totobj; 13441 int maxobjects; 13442 int *position; 13443 int *length; 13444 int *reject; 13445 double *place; 13446 double *bright; 13447 double mindistance; 13448 int pos, count; 13449 int up; 13450 int low, hig; 13451 int row; 13452 int i, j, k; 13453 13454 const int min_pixels = 10; 13455 13456 13457 if (cpl_error_get_code() != CPL_ERROR_NONE) 13458 return NULL; 13459 13460 if (image == NULL || slits == NULL) { 13461 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13462 return NULL; 13463 } 13464 13465 if (margin < 0) 13466 margin = 0; 13467 13468 if (maxradius < 0) { 13469 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13470 return NULL; 13471 } 13472 13473 if (conradius < 0) { 13474 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13475 return NULL; 13476 } 13477 13478 nslits = cpl_table_get_nrow(slits); 13479 position = cpl_table_get_data_int(slits, "position"); 13480 length = cpl_table_get_data_int(slits, "length"); 13481 13482 profile = cpl_image_collapse_create(image, 1); 13483 cpl_image_divide_scalar(profile, cpl_image_get_size_x(image)); 13484 pdata = cpl_image_get_data(profile); 13485 13486 row = 1; 13487 maxobjects = 0; 13488 totobj = 0; 13489 for (i = 0; i < nslits; i++) { 13490 13491 if (length[i] == 0) 13492 continue; 13493 13494 pos = position[i] + margin; 13495 count = length[i] - 2*margin; 13496 13497 if (count < min_pixels) 13498 continue; 13499 13500 p = pdata + pos; 13501 13502 13503 /* 13504 * Count peaks candidates 13505 */ 13506 13507 npeaks = 0; 13508 if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) { 13509 npeaks++; 13510 } 13511 13512 up = 0; 13513 for (j = 0; j < count - 3; j++) { 13514 if (p[j] > 0) { 13515 if (p[j+1] > p[j]) { 13516 up++; 13517 } 13518 else { 13519 if (up > 2) { 13520 if (p[j+1] > p[j+2] && p[j+2] > 0) { 13521 if (p[j] > 5) 13522 npeaks++; 13523 } 13524 } 13525 else if (up > 1) { 13526 if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) { 13527 if (p[j] > 5) 13528 npeaks++; 13529 } 13530 } 13531 up = 0; 13532 } 13533 } 13534 else { 13535 up = 0; 13536 } 13537 } 13538 13539 if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 13540 && p[count-3] > p[count-4] && p[count-4] > 0) { 13541 npeaks++; 13542 } 13543 13544 if (npeaks == 0) 13545 continue; 13546 13547 13548 /* 13549 * Get candidates parameters 13550 */ 13551 13552 reject = cpl_calloc(npeaks, sizeof(int)); 13553 bright = cpl_calloc(npeaks, sizeof(double)); 13554 place = cpl_calloc(npeaks, sizeof(double)); 13555 13556 npeaks = 0; 13557 if (p[0] > p[1] && p[1] > p[2] && p[2] > p[3] && p[3] > 0) { 13558 bright[0] = p[0]; 13559 place[0] = position[i] + margin; 13560 npeaks++; 13561 } 13562 13563 up = 0; 13564 for (j = 0; j < count - 3; j++) { 13565 if (p[j] > 0) { 13566 if (p[j+1] > p[j]) { 13567 up++; 13568 } 13569 else { 13570 if (up > 2) { 13571 if (p[j+1] > p[j+2] && p[j+2] > 0) { 13572 if (p[j] > 5) { 13573 bright[npeaks] = p[j]; 13574 place[npeaks] = position[i] + margin + j + 1 13575 + values_to_dx(p[j-1], p[j], p[j+1]); 13576 npeaks++; 13577 } 13578 } 13579 } 13580 else if (up > 1) { 13581 if (p[j+1] > p[j+2] && p[j+2] > p[j+3] && p[j+3] > 0) { 13582 if (p[j] > 5) { 13583 bright[npeaks] = p[j]; 13584 place[npeaks] = position[i] + margin + j + 1 13585 + values_to_dx(p[j-1], p[j], p[j+1]); 13586 npeaks++; 13587 } 13588 } 13589 } 13590 up = 0; 13591 } 13592 } 13593 else { 13594 up = 0; 13595 } 13596 } 13597 13598 if (p[count-1] > p[count-2] && p[count-2] > p[count-3] 13599 && p[count-3] > p[count-4] && p[count-4] > 0) { 13600 bright[npeaks] = p[count-1]; 13601 place[npeaks] = position[i] + count; 13602 npeaks++; 13603 } 13604 13605 13606 /* 13607 * Now select the uncontaminated peaks 13608 */ 13609 13610 if (fabs(place[0] - pos) < 1.0) 13611 reject[0] = 1; 13612 if (fabs(place[npeaks-1] - pos - count) < 1.0) 13613 reject[npeaks-1] = 1; 13614 for (j = 0; j < npeaks; j++) { 13615 for (k = 0; k < npeaks; k++) { 13616 if (k == j) 13617 continue; 13618 mindistance = conradius * bright[k] / bright[j] 13619 * bright[k] / bright[j]; 13620 if (fabs(place[j] - place[k]) < mindistance) 13621 reject[j] = 1; 13622 } 13623 } 13624 13625 /* new part */ 13626 for (j = 0; j < npeaks; j++) { 13627 if (reject[j]) 13628 continue; 13629 if (j) { 13630 low = (place[j-1]*bright[j] + place[j]*bright[j-1]) 13631 / (bright[j-1] + bright[j]) + 1; 13632 } 13633 else { 13634 low = pos; 13635 } 13636 if (j < npeaks - 1) { 13637 hig = (place[j+1]*bright[j] + place[j]*bright[j+1]) 13638 / (bright[j+1] + bright[j]) + 1; 13639 } 13640 else { 13641 hig = pos + count; 13642 } 13643 13644 if (low < pos) 13645 low = pos; 13646 if (hig > pos + count) 13647 hig = pos + count; 13648 if (place[j] - low > maxradius) 13649 low = place[j] - maxradius; 13650 if (hig - place[j] > maxradius) 13651 hig = place[j] + maxradius; 13652 if (hig == low) 13653 reject[j] = 1; 13654 } 13655 /* end new part */ 13656 13657 nobjects = npeaks; 13658 for (j = 0; j < npeaks; j++) 13659 if (reject[j]) 13660 nobjects--; 13661 13662 for (j = 0; j < nobjects; j++) { 13663 snprintf(name, MAX_COLNAME, "object_%d", j+1); 13664 if (cpl_table_has_column(slits, name)) 13665 continue; 13666 cpl_table_new_column(slits, name, CPL_TYPE_DOUBLE); 13667 snprintf(name, MAX_COLNAME, "start_%d", j+1); 13668 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13669 cpl_table_set_column_unit(slits, name, "pixel"); 13670 snprintf(name, MAX_COLNAME, "end_%d", j+1); 13671 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13672 cpl_table_set_column_unit(slits, name, "pixel"); 13673 snprintf(name, MAX_COLNAME, "row_%d", j+1); 13674 cpl_table_new_column(slits, name, CPL_TYPE_INT); 13675 cpl_table_set_column_unit(slits, name, "pixel"); 13676 } 13677 13678 objpos = nobjects; 13679 for (j = 0; j < npeaks; j++) { 13680 if (reject[j]) 13681 continue; 13682 if (j) { 13683 low = (place[j-1]*bright[j] + place[j]*bright[j-1]) 13684 / (bright[j-1] + bright[j]) + 1; 13685 } 13686 else { 13687 low = pos; 13688 } 13689 if (j < npeaks - 1) { 13690 hig = (place[j+1]*bright[j] + place[j]*bright[j+1]) 13691 / (bright[j+1] + bright[j]) + 1; 13692 } 13693 else { 13694 hig = pos + count; 13695 } 13696 13697 if (low < pos) 13698 low = pos; 13699 if (hig > pos + count) 13700 hig = pos + count; 13701 if (place[j] - low > maxradius) 13702 low = place[j] - maxradius; 13703 if (hig - place[j] > maxradius) 13704 hig = place[j] + maxradius; 13705 13706 snprintf(name, MAX_COLNAME, "object_%d", objpos); 13707 cpl_table_set_double(slits, name, i, place[j]); 13708 snprintf(name, MAX_COLNAME, "start_%d", objpos); 13709 cpl_table_set_int(slits, name, i, low); 13710 snprintf(name, MAX_COLNAME, "end_%d", objpos); 13711 cpl_table_set_int(slits, name, i, hig); 13712 snprintf(name, MAX_COLNAME, "row_%d", objpos); 13713 cpl_table_set_int(slits, name, i, row + objpos - 1); 13714 totobj++; 13715 objpos--; 13716 } 13717 13718 row += nobjects; 13719 13720 if (maxobjects < nobjects) 13721 maxobjects = nobjects; 13722 13723 cpl_free(reject); 13724 cpl_free(bright); 13725 cpl_free(place); 13726 13727 } 13728 13729 /* nobjects = row - nobjects; A bug, I think... */ 13730 row = cpl_table_get_nrow(slits); 13731 13732 for (i = 0; i < row; i++) { 13733 for (j = 0; j < maxobjects; j++) { 13734 snprintf(name, MAX_COLNAME, "row_%d", j+1); 13735 if (cpl_table_is_valid(slits, name, i)) 13736 cpl_table_set_int(slits, name, i, totobj - 13737 cpl_table_get_int(slits, name, i, NULL)); 13738 } 13739 } 13740 13741 for (i = 0; i < maxobjects; i++) { 13742 snprintf(name, MAX_COLNAME, "start_%d", i+1); 13743 cpl_table_fill_invalid_int(slits, name, -1); 13744 snprintf(name, MAX_COLNAME, "end_%d", i+1); 13745 cpl_table_fill_invalid_int(slits, name, -1); 13746 snprintf(name, MAX_COLNAME, "row_%d", i+1); 13747 cpl_table_fill_invalid_int(slits, name, -1); 13748 } 13749 13750 return profile; 13751 } 13752 13753 13778 cpl_image **mos_extract_objects(cpl_image *science, cpl_image *sky, 13779 cpl_table *objects, int extraction, double ron, 13780 double gain, int ncombined) 13781 { 13782 const char *func = "mos_extract_objects"; 13783 13784 char name[MAX_COLNAME]; 13785 13786 cpl_image **output; 13787 cpl_image *extracted; 13788 cpl_image *extr_sky; 13789 cpl_image *error; 13790 cpl_image *sciwin; 13791 cpl_image *skywin; 13792 int nslits; 13793 int nobjects; 13794 int maxobjects; 13795 int nx, ny; 13796 int ylow, yhig; 13797 int i, j; 13798 13799 13800 if (science == NULL || sky == NULL) { 13801 cpl_msg_error(func, "Both scientific exposures are required in input"); 13802 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13803 return NULL; 13804 } 13805 13806 if (objects == NULL) { 13807 cpl_msg_error(func, "An object table is required in input"); 13808 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 13809 return NULL; 13810 } 13811 13812 if (extraction < 0 || extraction > 1) { 13813 cpl_msg_error(func, "Invalid extraction mode (%d): it should be " 13814 "either 0 or 1", extraction); 13815 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13816 return NULL; 13817 } 13818 13819 if (ron < 0.0) { 13820 cpl_msg_error(func, "Invalid read-out-noise (%f ADU)", ron); 13821 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13822 return NULL; 13823 } 13824 13825 if (gain < 0.1) { 13826 cpl_msg_error(func, "Invalid gain factor (%f e-/ADU)", gain); 13827 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13828 return NULL; 13829 } 13830 13831 if (ncombined < 1) { 13832 cpl_msg_error(func, "Invalid number of combined frames (%d): " 13833 "it should be at least 1", ncombined); 13834 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 13835 return NULL; 13836 } 13837 13838 13839 /* 13840 * Count the max number of objects per slit. Note that maxobjects 13841 * is intentionally the max number of objects increased by one. 13842 */ 13843 13844 maxobjects = 1; 13845 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 13846 while (cpl_table_has_column(objects, name)) { 13847 maxobjects++; 13848 snprintf(name, MAX_COLNAME, "object_%d", maxobjects); 13849 } 13850 13851 13852 /* 13853 * Count objects to extract 13854 */ 13855 13856 nobjects = 0; 13857 nslits = cpl_table_get_nrow(objects); 13858 13859 for (i = 0; i < nslits; i++) { 13860 for (j = 1; j < maxobjects; j++) { 13861 snprintf(name, MAX_COLNAME, "object_%d", j); 13862 if (cpl_table_is_valid(objects, name, i)) 13863 nobjects++; 13864 } 13865 } 13866 13867 if (nobjects == 0) 13868 return NULL; 13869 13870 nx = cpl_image_get_size_x(science); 13871 ny = cpl_image_get_size_x(science); 13872 13873 output = cpl_calloc(3, sizeof(cpl_image *)); 13874 extracted = output[0] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13875 extr_sky = output[1] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13876 error = output[2] = cpl_image_new(nx, nobjects, CPL_TYPE_FLOAT); 13877 13878 13879 /* 13880 * Extract objects 13881 */ 13882 13883 nobjects = 0; 13884 for (i = 0; i < nslits; i++) { 13885 for (j = 1; j < maxobjects; j++) { 13886 snprintf(name, MAX_COLNAME, "object_%d", j); 13887 if (cpl_table_is_valid(objects, name, i)) { 13888 snprintf(name, MAX_COLNAME, "start_%d", j); 13889 ylow = cpl_table_get_int(objects, name, i, NULL); 13890 snprintf(name, MAX_COLNAME, "end_%d", j); 13891 yhig = cpl_table_get_int(objects, name, i, NULL); 13892 snprintf(name, MAX_COLNAME, "row_%d", j); 13893 nobjects = cpl_table_get_int(objects, name, i, NULL); 13894 sciwin = cpl_image_extract(science, 1, ylow+1, nx, yhig); 13895 skywin = cpl_image_extract(sky, 1, ylow+1, nx, yhig); 13896 /* 13897 * Cleaning the cosmics locally was really NOT a good idea... 13898 * I leave it here, commented out, to never forget this mistake! 13899 13900 if (extraction) { 13901 mos_clean_cosmics(sciwin, gain, -1., -1.); 13902 } 13903 */ 13904 mos_extraction(sciwin, skywin, extracted, extr_sky, error, 13905 nobjects, extraction, ron, gain, ncombined); 13906 13907 /* 13908 * Hidden check whether the spectrum was saturated or not 13909 */ 13910 13911 { 13912 cpl_image *total = cpl_image_add_create(sciwin, skywin); 13913 float *data = cpl_image_get_data_float(total); 13914 int size = cpl_image_get_size_x(total) 13915 * cpl_image_get_size_y(total); 13916 int k; 13917 char *saturation_level = getenv("SATURATION_LEVEL"); 13918 float saturation = 62000.0; 13919 char *max_saturated = getenv("MAX_SATURATED"); 13920 int max_satur = 10; 13921 int saturated; 13922 13923 if (saturation_level) 13924 saturation = atof(saturation_level); 13925 13926 if (max_saturated) 13927 max_satur = atoi(max_saturated); 13928 13929 saturated = 0; 13930 for (k = 0; k < size; k++) { 13931 if (data[k] > saturation) { 13932 saturated++; 13933 if (saturated > max_satur) { 13934 break; 13935 } 13936 } 13937 } 13938 13939 if (saturated > max_satur) 13940 saturated = 1; 13941 else 13942 saturated = 0; 13943 13944 data = cpl_image_get_data(extracted); 13945 data[nobjects * nx] = saturated; 13946 } 13947 13948 cpl_image_delete(sciwin); 13949 cpl_image_delete(skywin); 13950 nobjects++; 13951 } 13952 } 13953 } 13954 13955 return output; 13956 13957 } 13958 13959 13982 int mos_spectral_resolution(cpl_image *image, double lambda, double startwave, 13983 double dispersion, int saturation, 13984 double *mfwhm, double *rmsfwhm, 13985 double *resolution, double *rmsres, int *nlines) 13986 { 13987 cpl_vector *vector; 13988 13989 int i, j, n, m; 13990 int position, maxpos; 13991 int xlen, ylen; 13992 int sp, ep; 13993 int radius; 13994 int sradius = 40; 13995 int threshold = 250; /* Peak must be so many ADUs above min */ 13996 13997 int ifwhm; 13998 double fwhm; 13999 double *buffer; 14000 double min, max, halfmax; 14001 double cut = 1.5; /* To cut outliers from FWHM values (pixel) */ 14002 double value, rms; 14003 14004 float *data; 14005 14006 14007 *resolution = 0.0; 14008 *rmsres = 0.0; 14009 *nlines = 0; 14010 14011 xlen = cpl_image_get_size_x(image); 14012 ylen = cpl_image_get_size_y(image); 14013 data = cpl_image_get_data(image); 14014 14015 buffer = cpl_malloc(ylen * sizeof(double)); 14016 14017 /* 14018 * Closest pixel to specified wavelength. 14019 */ 14020 14021 position = floor((lambda - startwave) / dispersion + 0.5); 14022 14023 sp = position - sradius; 14024 ep = position + sradius; 14025 14026 if (sp < 0 || ep > xlen) { 14027 cpl_free(buffer); 14028 return 0; 14029 } 14030 14031 for (i = 0, n = 0; i < ylen; i++) { /* For each row of each slit */ 14032 14033 /* 14034 * Search interval for peak. Abort if too close to image border. 14035 */ 14036 14037 radius = mos_lines_width(data + i*xlen + position - sradius, 14038 2*sradius + 1); 14039 if (radius < 5) 14040 radius = 5; 14041 14042 sp = position - radius; 14043 ep = position + radius; 14044 14045 if (sp < 0 || ep > xlen) { 14046 cpl_free(buffer); 14047 return 0; 14048 } 14049 14050 14051 /* 14052 * Determine min-max value and position. 14053 */ 14054 14055 maxpos = sp; 14056 min = max = data[sp + i * xlen]; 14057 for (j = sp; j < ep; j++) { 14058 if (data[j + i * xlen] > max) { 14059 max = data[j + i * xlen]; 14060 maxpos = j; 14061 } 14062 if (data[j + i * xlen] < min) { 14063 min = data[j + i * xlen]; 14064 } 14065 } 14066 14067 if (fabs(min) < 0.0000001) /* Truncated spectrum */ 14068 continue; 14069 14070 if (max - min < threshold) /* Low signal... */ 14071 continue; 14072 14073 if (max > saturation) /* Saturation */ 14074 continue; 14075 14076 /* 14077 * Determine FWHM counting pixels with value greater than 14078 * half of the max value, to the right and to the left of 14079 * the max. Linear interpolation between the pixels where 14080 * the transition happens. 14081 */ 14082 14083 halfmax = (max + min)/ 2.0; 14084 14085 fwhm = 0.0; 14086 ifwhm = 0; 14087 for (j = maxpos; j < maxpos + radius; j++) { 14088 if (j < xlen) { 14089 if (data[j + i * xlen] < halfmax) { 14090 fwhm = ifwhm + (data[j - 1 + i * xlen] - halfmax) 14091 / (data[j - 1 + i * xlen] - data[j + i * xlen]); 14092 break; 14093 } 14094 ifwhm++; 14095 } 14096 } 14097 14098 ifwhm = 0; 14099 for (j = maxpos; j > maxpos - radius; j--) { 14100 if (j >= 0) { 14101 if (data[j + i * xlen] < halfmax) { 14102 fwhm += ifwhm + (data[j + 1 + i * xlen] - halfmax) 14103 / (data[j + 1 + i * xlen] - data[j + i * xlen]); 14104 break; 14105 } 14106 ifwhm++; 14107 } 14108 } 14109 14110 if (fwhm > 3.0) { 14111 buffer[n] = fwhm - 2.0; 14112 n++; 14113 } 14114 14115 } 14116 14117 if (n == 0) { 14118 cpl_free(buffer); 14119 return 0; 14120 } 14121 14122 vector = cpl_vector_wrap(n, buffer); 14123 value = cpl_vector_get_median_const(vector); 14124 cpl_vector_unwrap(vector); 14125 14126 rms = 0.0; 14127 for (i = 0, m = 0; i < n; i++) { 14128 if (fabs(buffer[i] - value) < cut) { 14129 rms += fabs(buffer[i] - value); 14130 m++; 14131 } 14132 } 14133 14134 cpl_free(buffer); 14135 14136 if (m < 3) 14137 return 0; 14138 14139 rms /= m; 14140 rms *= 1.25; /* Factor to convert average deviation to sigma */ 14141 14142 value *= dispersion; 14143 rms *= dispersion; 14144 14145 *mfwhm = value; 14146 *rmsfwhm = rms; 14147 14148 *resolution = lambda / value; 14149 *rmsres = *resolution * rms / value; 14150 14151 *nlines = m; 14152 14153 return 1; 14154 } 14155 14156 14178 cpl_table *mos_resolution_table(cpl_image *image, double startwave, 14179 double dispersion, int saturation, 14180 cpl_vector *lines) 14181 { 14182 14183 cpl_table *table; 14184 double *line; 14185 double fwhm; 14186 double rmsfwhm; 14187 double resolution; 14188 double rmsres; 14189 int nref; 14190 int nlines; 14191 int i; 14192 14193 14194 nref = cpl_vector_get_size(lines); 14195 line = cpl_vector_get_data(lines); 14196 14197 table = cpl_table_new(nref); 14198 cpl_table_new_column(table, "wavelength", CPL_TYPE_DOUBLE); 14199 cpl_table_set_column_unit(table, "wavelength", "Angstrom"); 14200 cpl_table_new_column(table, "fwhm", CPL_TYPE_DOUBLE); 14201 cpl_table_set_column_unit(table, "fwhm", "Angstrom"); 14202 cpl_table_new_column(table, "fwhm_rms", CPL_TYPE_DOUBLE); 14203 cpl_table_set_column_unit(table, "fwhm_rms", "Angstrom"); 14204 cpl_table_new_column(table, "resolution", CPL_TYPE_DOUBLE); 14205 cpl_table_new_column(table, "resolution_rms", CPL_TYPE_DOUBLE); 14206 cpl_table_new_column(table, "nlines", CPL_TYPE_INT); 14207 14208 for (i = 0; i < nref; i++) { 14209 if (mos_spectral_resolution(image, line[i], startwave, dispersion, 14210 saturation, &fwhm, &rmsfwhm, 14211 &resolution, &rmsres, &nlines)) { 14212 cpl_table_set_double(table, "wavelength", i, line[i]); 14213 cpl_table_set_double(table, "fwhm", i, fwhm); 14214 cpl_table_set_double(table, "fwhm_rms", i, rmsfwhm); 14215 cpl_table_set_double(table, "resolution", i, resolution); 14216 cpl_table_set_double(table, "resolution_rms", i, rmsres); 14217 cpl_table_set_int(table, "nlines", i, nlines); 14218 } 14219 else 14220 cpl_table_set_int(table, "nlines", i, 0); 14221 } 14222 14223 if (cpl_table_has_valid(table, "wavelength")) 14224 return table; 14225 14226 cpl_table_delete(table); 14227 14228 return NULL; 14229 14230 } 14231 14232 14250 double mos_integrate_signal(cpl_image *image, cpl_image *wavemap, 14251 int ystart, int yend, double wstart, double wend) 14252 { 14253 const char *func = "mos_integrate_signal"; 14254 14255 double sum; 14256 float *sdata; 14257 float *wdata; 14258 int nx, ny; 14259 int x, y; 14260 14261 14262 if (image == NULL || wavemap == NULL) { 14263 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14264 return 0.0; 14265 } 14266 14267 if (ystart > yend || wstart >= wend) { 14268 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14269 return 0.0; 14270 } 14271 14272 nx = cpl_image_get_size_x(image); 14273 ny = cpl_image_get_size_y(image); 14274 14275 if (!(nx == cpl_image_get_size_x(wavemap) 14276 && ny == cpl_image_get_size_y(wavemap))) { 14277 cpl_error_set(func, CPL_ERROR_INCOMPATIBLE_INPUT); 14278 return 0.0; 14279 } 14280 14281 if (ystart < 0 || yend > ny) { 14282 cpl_error_set(func, CPL_ERROR_ACCESS_OUT_OF_RANGE); 14283 return 0.0; 14284 } 14285 14286 sdata = cpl_image_get_data(image); 14287 wdata = cpl_image_get_data(wavemap); 14288 14289 sdata += ystart*nx; 14290 wdata += ystart*nx; 14291 14292 sum = 0.0; 14293 for (y = ystart; y < yend; y++) { 14294 for (x = 0; x < nx; x++) { 14295 if (wdata[x] < wstart || wdata[x] > wend) 14296 continue; 14297 sum += sdata[x]; 14298 } 14299 sdata += nx; 14300 wdata += nx; 14301 } 14302 14303 return sum; 14304 14305 } 14306 14307 /**************************************************************************** 14308 * From this point on, the instrument dependent functions are added: 14309 * they are functions that retrieve information that is stored in 14310 * the data headers in some instrument specific way, such as the 14311 * location of overscans, the slits positions on the telescope 14312 * focal plane, the gain factor, etc. 14313 */ 14314 14315 14338 cpl_table *mos_load_slits_fors_mxu(cpl_propertylist *header) 14339 { 14340 const char *func = "mos_load_slits_fors_mxu"; 14341 14342 cpl_table *slits; 14343 char keyname[MAX_COLNAME]; 14344 const char *instrume; 14345 const char *target_name; 14346 float slit_x; 14347 float slit_y; 14348 float length; 14349 /* double arc2mm = 0.53316; */ 14350 double arc2mm = 0.528; 14351 int nslits; 14352 int slit_id; 14353 int fors; 14354 int chip; 14355 int found; 14356 14357 /* 14358 * The limits below are used to exclude from the loaded slit list 14359 * any slit that surely doesn't belong to the used chip. This is 14360 * a way to reduce the chance of ambiguous slit identification. 14361 */ 14362 14363 float low_limit1 = 10.0; 14364 float hig_limit2 = 30.0; 14365 14366 14367 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14368 return NULL; 14369 } 14370 14371 if (header == NULL) { 14372 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14373 return NULL; 14374 } 14375 14376 14377 /* 14378 * See if this is FORS1 or FORS2; 14379 */ 14380 14381 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14382 14383 fors = 0; 14384 if (instrume[4] == '1') 14385 fors = 1; 14386 if (instrume[4] == '2') 14387 fors = 2; 14388 14389 if (fors != 2) { 14390 cpl_msg_error(func, "Wrong instrument: %s\n" 14391 "FORS2 is expected for MXU data", instrume); 14392 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14393 return NULL; 14394 } 14395 14396 14397 /* 14398 * The master and slave chips can be identified by their positions 14399 * in the chip array in the case of FORS2 data (with fors1 the chip 14400 * is always 1). chip = 2 is the master, chip = 1 is the slave. 14401 */ 14402 14403 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14404 14405 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14406 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14407 "in FITS header"); 14408 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14409 return NULL; 14410 } 14411 14412 if (chip != 1 && chip != 2) { 14413 cpl_msg_error(func, "Unexpected chip position in keyword " 14414 "ESO DET CHIP1 Y: %d", chip); 14415 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14416 return NULL; 14417 } 14418 14419 14420 /* 14421 * Count slits in header (excluding reference slits, and the slits 14422 * that _surely_ belong to the other chip) 14423 */ 14424 14425 nslits = 0; 14426 slit_id = 0; 14427 found = 1; 14428 14429 while (found) { 14430 slit_id++; 14431 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100); 14432 if (cpl_propertylist_has(header, keyname)) { 14433 slit_y = cpl_propertylist_get_double(header, keyname); 14434 14435 if (chip == 1) 14436 if (slit_y < low_limit1) 14437 continue; 14438 if (chip == 2) 14439 if (slit_y > hig_limit2) 14440 continue; 14441 14442 snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 14443 slit_id + 100); 14444 if (cpl_propertylist_has(header, keyname)) { 14445 target_name = cpl_propertylist_get_string(header, keyname); 14446 if (strncmp(target_name, "refslit", 7)) 14447 nslits++; 14448 } 14449 else 14450 nslits++; 14451 } 14452 else 14453 found = 0; 14454 } 14455 14456 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14457 cpl_msg_error(func, "%s while loading slits coordinates from " 14458 "FITS header", cpl_error_get_message()); 14459 cpl_error_set_where(func); 14460 return NULL; 14461 } 14462 14463 if (nslits == 0) { 14464 cpl_msg_error(func, "No slits coordinates found in header"); 14465 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14466 return NULL; 14467 } 14468 14469 slits = cpl_table_new(nslits); 14470 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14471 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14472 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14473 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14474 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14475 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14476 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14477 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14478 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14479 14480 nslits = 0; 14481 slit_id = 0; 14482 found = 1; 14483 while (found) { 14484 slit_id++; 14485 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d YPOS", slit_id + 100); 14486 if (cpl_propertylist_has(header, keyname)) { 14487 slit_y = cpl_propertylist_get_double(header, keyname); 14488 14489 if (chip == 1) 14490 if (slit_y < low_limit1) 14491 continue; 14492 if (chip == 2) 14493 if (slit_y > hig_limit2) 14494 continue; 14495 14496 /* 14497 * Y-flip the slit position, to match CCD pixel coordinate 14498 * convention 14499 */ 14500 14501 slit_y = -slit_y; 14502 14503 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d XPOS", slit_id + 100); 14504 slit_x = cpl_propertylist_get_double(header, keyname); 14505 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14506 cpl_table_delete(slits); 14507 cpl_msg_error(func, "Missing keyword %s in FITS header", 14508 keyname); 14509 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14510 return NULL; 14511 } 14512 14513 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d LEN", slit_id + 100); 14514 length = cpl_propertylist_get_double(header, keyname); 14515 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14516 cpl_table_delete(slits); 14517 cpl_msg_error(func, "Missing keyword %s in FITS header", 14518 keyname); 14519 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14520 return NULL; 14521 } 14522 14523 length *= arc2mm; 14524 14525 snprintf(keyname, MAX_COLNAME, "ESO INS TARG%d NAME", 14526 slit_id + 100); 14527 if (cpl_propertylist_has(header, keyname)) { 14528 target_name = cpl_propertylist_get_string(header, keyname); 14529 if (strncmp(target_name, "refslit", 7)) { 14530 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14531 cpl_table_set(slits, "xtop", nslits, slit_x); 14532 cpl_table_set(slits, "ytop", nslits, slit_y + length/2); 14533 cpl_table_set(slits, "xbottom", nslits, slit_x); 14534 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2); 14535 nslits++; 14536 } 14537 } 14538 else { 14539 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14540 cpl_table_set(slits, "xtop", nslits, slit_x); 14541 cpl_table_set(slits, "ytop", nslits, slit_y + length/2); 14542 cpl_table_set(slits, "xbottom", nslits, slit_x); 14543 cpl_table_set(slits, "ybottom", nslits, slit_y - length/2); 14544 nslits++; 14545 } 14546 } 14547 else 14548 found = 0; 14549 } 14550 14551 return slits; 14552 } 14553 14554 14578 cpl_table *mos_load_slits_fors_mos(cpl_propertylist *header, 14579 int * nslits_out_det) 14580 { 14581 const char *func = "mos_load_slits_fors_mos"; 14582 14583 cpl_table *slits; 14584 char keyname[MAX_COLNAME]; 14585 const char *instrume; 14586 const char *chipname; 14587 float slit_x; 14588 int first_slit, last_slit; 14589 cpl_size nslits; 14590 int slit_id; 14591 int fors; 14592 int chip; 14593 int fors_is_old; 14594 14595 /* 14596 * The Y coordinates of the slits are fixed 14597 */ 14598 14599 float ytop[19] = { 113.9, 101.3, 89.9, 77.3, 65.9, 53.3, 14600 41.9, 29.3, 17.9, 5.3, -6.1, -18.7, 14601 -30.1, -42.7, -54.1, -66.7, -78.1, -90.7, 14602 -102.1 }; 14603 float ybottom[19] = { 102.1, 90.7, 78.1, 66.7, 54.1, 42.7, 14604 30.1, 18.7, 6.1, -5.3, -17.9, -29.3, 14605 -41.9, -53.3, -65.9, -77.3, -89.9, -101.3, 14606 -113.9 }; 14607 14608 14609 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14610 return NULL; 14611 } 14612 14613 if (header == NULL) { 14614 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14615 return NULL; 14616 } 14617 14618 14619 /* 14620 * See if this is FORS1 or FORS2; 14621 */ 14622 14623 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14624 14625 fors = 0; 14626 if (instrume[4] == '1') 14627 fors = 1; 14628 if (instrume[4] == '2') 14629 fors = 2; 14630 14631 if (fors == 0) { 14632 cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 14633 instrume); 14634 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14635 return NULL; 14636 } 14637 14638 /* FIXME: 14639 * This is the way FORS1 data belong to the upgraded chips, 14640 * named "Marlene" and "Norma III". It's a quick solution, 14641 * there are hardcoded values here!!! 14642 */ 14643 14644 chipname = cpl_propertylist_get_string(header, "ESO DET CHIP1 ID"); 14645 14646 if (chipname[0] == 'M' || chipname[0] == 'N') 14647 fors_is_old = 0; 14648 else 14649 fors_is_old = 1; 14650 14651 if (fors == 1 && fors_is_old) { 14652 first_slit = 1; 14653 last_slit = 19; 14654 } 14655 else { 14656 14657 /* 14658 * The master and slave chips can be identified by their positions 14659 * in the chip array in the case of FORS2 data: chip = 2 is the 14660 * master, chip = 1 is the slave. 14661 */ 14662 14663 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14664 14665 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14666 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14667 "in FITS header"); 14668 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14669 return NULL; 14670 } 14671 14672 if (chip != 1 && chip != 2) { 14673 cpl_msg_error(func, "Unexpected chip position in keyword " 14674 "ESO DET CHIP1 Y: %d", chip); 14675 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14676 return NULL; 14677 } 14678 14679 if (chip == 1) { 14680 first_slit = 12; 14681 last_slit = 19; 14682 } 14683 else { 14684 first_slit = 1; 14685 last_slit = 11; 14686 } 14687 } 14688 14689 14690 /* 14691 * Count slits in header (excluding closed slits - i.e. those with 14692 * offsets greater than 115 mm - and the slits that do not belong 14693 * to this chip) 14694 */ 14695 14696 nslits = 0; 14697 slit_id = 0; 14698 for (slit_id = first_slit; slit_id <= last_slit; slit_id++) { 14699 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id); 14700 if (cpl_propertylist_has(header, keyname)) { 14701 slit_x = cpl_propertylist_get_double(header, keyname); 14702 if (fabs(slit_x) < 115.0) 14703 nslits++; 14704 else 14705 (*nslits_out_det)++; 14706 } 14707 else { 14708 cpl_msg_error(func, "Missing keyword %s in FITS header", keyname); 14709 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14710 return NULL; 14711 } 14712 } 14713 14714 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14715 cpl_msg_error(func, "%s while loading slits coordinates from " 14716 "FITS header", cpl_error_get_message()); 14717 cpl_error_set_where(func); 14718 return NULL; 14719 } 14720 14721 if (nslits == 0) { 14722 cpl_msg_error(func, "No slits coordinates found in header"); 14723 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14724 return NULL; 14725 } 14726 14727 slits = cpl_table_new(nslits); 14728 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14729 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14730 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14731 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14732 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14733 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14734 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14735 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14736 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14737 14738 nslits = 0; 14739 slit_id = 0; 14740 for (slit_id = first_slit; slit_id <= last_slit; slit_id++) { 14741 snprintf(keyname, MAX_COLNAME, "ESO INS MOS%d POS", slit_id); 14742 slit_x = cpl_propertylist_get_double(header, keyname); 14743 if (fabs(slit_x) < 115.0) { 14744 cpl_table_set_int(slits, "slit_id", nslits, slit_id); 14745 cpl_table_set(slits, "xtop", nslits, slit_x); 14746 cpl_table_set(slits, "ytop", nslits, ytop[slit_id-1]); 14747 cpl_table_set(slits, "xbottom", nslits, slit_x); 14748 cpl_table_set(slits, "ybottom", nslits, ybottom[slit_id-1]); 14749 nslits++; 14750 } 14751 } 14752 14753 return slits; 14754 } 14755 14756 14780 cpl_table *mos_load_slits_fors_lss(cpl_propertylist *header) 14781 { 14782 const char *func = "mos_load_slits_fors_lss"; 14783 14784 cpl_table *slits; 14785 char *slit_name; 14786 const char *instrume; 14787 int fors; 14788 int chip; 14789 float ytop; 14790 float ybottom; 14791 14792 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14793 return NULL; 14794 } 14795 14796 if (header == NULL) { 14797 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14798 return NULL; 14799 } 14800 14801 14802 /* 14803 * See if this is FORS1 or FORS2; 14804 */ 14805 14806 instrume = cpl_propertylist_get_string(header, "INSTRUME"); 14807 14808 fors = 0; 14809 if (instrume[4] == '1') 14810 fors = 1; 14811 if (instrume[4] == '2') 14812 fors = 2; 14813 14814 if (fors == 0) { 14815 cpl_msg_error(func, "Wrong instrument found in FITS header: %s", 14816 instrume); 14817 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14818 return NULL; 14819 } 14820 14821 if (fors == 1) { 14822 ytop = 109.94; 14823 ybottom = -109.94; 14824 } 14825 else { 14826 14827 /* 14828 * The master and slave chips can be identified by their positions 14829 * in the chip array in the case of FORS2 data: chip = 2 is the 14830 * master, chip = 1 is the slave. 14831 */ 14832 14833 chip = cpl_propertylist_get_int(header, "ESO DET CHIP1 Y"); 14834 14835 if (cpl_error_get_code() != CPL_ERROR_NONE) { 14836 cpl_msg_error(func, "Missing keyword ESO DET CHIP1 Y " 14837 "in FITS header"); 14838 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14839 return NULL; 14840 } 14841 14842 if (chip != 1 && chip != 2) { 14843 cpl_msg_error(func, "Unexpected chip position in keyword " 14844 "ESO DET CHIP1 Y: %d", chip); 14845 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 14846 return NULL; 14847 } 14848 14849 if (chip == 1) { 14850 ytop = 30.0; 14851 ybottom = -109.94; 14852 } 14853 else { 14854 ytop = 109.94; 14855 ybottom = -20.0; 14856 } 14857 } 14858 14859 14860 slits = cpl_table_new(1); 14861 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 14862 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 14863 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 14864 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 14865 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 14866 cpl_table_set_column_unit(slits, "xtop", "pixel"); 14867 cpl_table_set_column_unit(slits, "ytop", "pixel"); 14868 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 14869 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 14870 14871 slit_name = (char *)cpl_propertylist_get_string(header, 14872 "ESO INS SLIT NAME"); 14873 14874 cpl_table_set(slits, "ytop", 0, ytop); 14875 cpl_table_set(slits, "ybottom", 0, ybottom); 14876 14877 if (!strncmp(slit_name, "lSlit0_3arcsec", 14)) { 14878 cpl_table_set_int(slits, "slit_id", 0, 1); 14879 cpl_table_set(slits, "xbottom", 0, -0.075); 14880 cpl_table_set(slits, "xtop", 0, 0.075); 14881 } 14882 else if (!strncmp(slit_name, "lSlit0_4arcsec", 14)) { 14883 cpl_table_set_int(slits, "slit_id", 0, 2); 14884 cpl_table_set(slits, "xbottom", 0, 5.895); 14885 cpl_table_set(slits, "xtop", 0, 6.105); 14886 } 14887 else if (!strncmp(slit_name, "lSlit0_5arcsec", 14)) { 14888 cpl_table_set_int(slits, "slit_id", 0, 3); 14889 cpl_table_set(slits, "xbottom", 0, -6.135); 14890 cpl_table_set(slits, "xtop", 0, -5.865); 14891 } 14892 else if (!strncmp(slit_name, "lSlit0_7arcsec", 14)) { 14893 cpl_table_set_int(slits, "slit_id", 0, 4); 14894 cpl_table_set(slits, "xbottom", 0, 11.815); 14895 cpl_table_set(slits, "xtop", 0, 12.185); 14896 } 14897 else if (!strncmp(slit_name, "lSlit1_0arcsec", 14)) { 14898 cpl_table_set_int(slits, "slit_id", 0, 5); 14899 cpl_table_set(slits, "xbottom", 0, -12.265); 14900 cpl_table_set(slits, "xtop", 0, -11.735); 14901 } 14902 else if (!strncmp(slit_name, "lSlit1_3arcsec", 14)) { 14903 cpl_table_set_int(slits, "slit_id", 0, 6); 14904 cpl_table_set(slits, "xbottom", 0, 17.655); 14905 cpl_table_set(slits, "xtop", 0, 18.345); 14906 } 14907 else if (!strncmp(slit_name, "lSlit1_6arcsec", 14)) { 14908 cpl_table_set_int(slits, "slit_id", 0, 7); 14909 cpl_table_set(slits, "xbottom", 0, -18.425); 14910 cpl_table_set(slits, "xtop", 0, -17.575); 14911 } 14912 else if (!strncmp(slit_name, "lSlit2_0arcsec", 14)) { 14913 cpl_table_set_int(slits, "slit_id", 0, 8); 14914 cpl_table_set(slits, "xbottom", 0, 23.475); 14915 cpl_table_set(slits, "xtop", 0, 24.525); 14916 } 14917 else if (!strncmp(slit_name, "lSlit2_5arcsec", 14)) { 14918 cpl_table_set_int(slits, "slit_id", 0, 9); 14919 cpl_table_set(slits, "xbottom", 0, -24.66); 14920 cpl_table_set(slits, "xtop", 0, -23.34); 14921 } 14922 else { 14923 cpl_msg_error(func, "Invalid slit %s in keyword ESO INS SLIT NAME", 14924 slit_name); 14925 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 14926 cpl_table_delete(slits); 14927 return NULL; 14928 } 14929 14930 return slits; 14931 } 14932 14933 14948 double mos_get_gain_vimos(cpl_propertylist *header) 14949 { 14950 const char *func = "mos_get_gain_vimos"; 14951 14952 double gain = -1.0; 14953 14954 14955 if (cpl_error_get_code() != CPL_ERROR_NONE) 14956 return gain; 14957 14958 if (header == NULL) { 14959 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 14960 return gain; 14961 } 14962 14963 gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD"); 14964 if (cpl_error_get_code()) { 14965 cpl_error_set_where(func); 14966 gain = -1.0; 14967 } 14968 14969 return gain; 14970 14971 } 14972 14973 14993 cpl_table *mos_load_slits_vimos(cpl_propertylist *header) 14994 { 14995 const char *func = "mos_load_slits_vimos"; 14996 14997 cpl_table *slits; 14998 char keyname[MAX_COLNAME]; 14999 float slit_x; 15000 float slit_y; 15001 float dim_x; 15002 float dim_y; 15003 int nslits; 15004 int slit_id; 15005 int curved; 15006 int i; 15007 15008 15009 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15010 return NULL; 15011 } 15012 15013 if (header == NULL) { 15014 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15015 return NULL; 15016 } 15017 15018 nslits = cpl_propertylist_get_int(header, "ESO INS SLIT NO"); 15019 15020 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15021 cpl_error_set_where(func); 15022 return NULL; 15023 } 15024 15025 slits = cpl_table_new(nslits); 15026 cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT); 15027 cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE); 15028 cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE); 15029 cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE); 15030 cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE); 15031 cpl_table_new_column(slits, "xwidth", CPL_TYPE_DOUBLE); 15032 cpl_table_new_column(slits, "ywidth", CPL_TYPE_DOUBLE); 15033 cpl_table_new_column(slits, "curved", CPL_TYPE_INT); 15034 cpl_table_set_column_unit(slits, "xtop", "pixel"); 15035 cpl_table_set_column_unit(slits, "ytop", "pixel"); 15036 cpl_table_set_column_unit(slits, "xbottom", "pixel"); 15037 cpl_table_set_column_unit(slits, "ybottom", "pixel"); 15038 cpl_table_set_column_unit(slits, "xwidth", "mm"); 15039 cpl_table_set_column_unit(slits, "ywidth", "mm"); 15040 15041 for (i = 0; i < nslits; i++) { 15042 sprintf(keyname, "ESO INS SLIT%d ID", i+1); 15043 slit_id = cpl_propertylist_get_int(header, keyname); 15044 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15045 cpl_error_set_where(func); 15046 return NULL; 15047 } 15048 sprintf(keyname, "ESO INS SLIT%d X", i+1); 15049 slit_x = cpl_propertylist_get_double(header, keyname); 15050 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15051 cpl_error_set_where(func); 15052 return NULL; 15053 } 15054 sprintf(keyname, "ESO INS SLIT%d Y", i+1); 15055 slit_y = cpl_propertylist_get_double(header, keyname); 15056 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15057 cpl_error_set_where(func); 15058 return NULL; 15059 } 15060 sprintf(keyname, "ESO INS SLIT%d DIMX", i+1); 15061 dim_x = cpl_propertylist_get_double(header, keyname); 15062 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15063 cpl_error_set_where(func); 15064 return NULL; 15065 } 15066 15067 sprintf(keyname, "ESO INS SLIT%d BEZIER DY", i+1); 15068 if (cpl_propertylist_has(header, keyname)) { 15069 curved = 1; 15070 } 15071 else { 15072 sprintf(keyname, "ESO INS SLIT%d DIMY", i+1); 15073 curved = 0; 15074 } 15075 dim_y = cpl_propertylist_get_double(header, keyname); 15076 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15077 cpl_error_set_where(func); 15078 return NULL; 15079 } 15080 15081 cpl_table_set_int(slits, "slit_id", i, slit_id); 15082 cpl_table_set(slits, "xtop", i, slit_x - dim_x/2); 15083 cpl_table_set(slits, "ytop", i, slit_y); 15084 cpl_table_set(slits, "xbottom", i, slit_x + dim_x/2); 15085 cpl_table_set(slits, "ybottom", i, slit_y); 15086 cpl_table_set(slits, "xwidth", i, dim_x); 15087 cpl_table_set(slits, "ywidth", i, dim_y); 15088 cpl_table_set_int(slits, "curved", i, curved); 15089 } 15090 15091 return slits; 15092 } 15093 15094 15104 int mos_check_multiplex(cpl_table *slits) 15105 { 15106 cpl_propertylist *sort; 15107 int nrow; 15108 int i, multiplex, xprev, xcur; 15109 double prev, cur; 15110 double tolerance = 1.0; // About spatially aligned slits (mm) 15111 15112 15113 /* 15114 * Create an auxiliary column containing a sort of integer 15115 * x coordinate of the slit, to guarantee that slits at the 15116 * same spatial offset are recognised immediately as in spectral 15117 * multiplexing. 15118 */ 15119 15120 sort = cpl_propertylist_new(); 15121 cpl_propertylist_append_bool(sort, "xtop", 0); 15122 cpl_table_sort(slits, sort); 15123 cpl_propertylist_delete(sort); 15124 15125 prev = cpl_table_get_double(slits, "xtop", 0, NULL); 15126 cpl_table_new_column(slits, "xind", CPL_TYPE_INT); 15127 cpl_table_set_int(slits, "xind", 0, prev); // cast to int is intentional 15128 nrow = cpl_table_get_nrow(slits); 15129 for (i = 1; i < nrow; i++) { 15130 cur = cpl_table_get_double(slits, "xtop", i, NULL); 15131 if (fabs(prev - cur) > tolerance) 15132 prev = cur; 15133 cpl_table_set_int(slits, "xind", i, prev); 15134 } 15135 15136 /* 15137 * Now sort according to increasing (integer) x positions, and when 15138 * those are equal (multiplexed) according to the increasing y position. 15139 */ 15140 15141 sort = cpl_propertylist_new(); 15142 cpl_propertylist_append_bool(sort, "xind", 0); 15143 cpl_propertylist_append_bool(sort, "ytop", 0); 15144 cpl_table_sort(slits, sort); 15145 cpl_propertylist_delete(sort); 15146 15147 /* 15148 * Now assign to each slit its multiplex order. 15149 */ 15150 15151 multiplex = 0; 15152 cpl_table_new_column(slits, "multiplex", CPL_TYPE_INT); 15153 xprev = cpl_table_get_int(slits, "xind", 0, NULL); 15154 cpl_table_set_int(slits, "multiplex", 0, multiplex); 15155 nrow = cpl_table_get_nrow(slits); 15156 for (i = 1; i < nrow; i++) { 15157 xcur = cpl_table_get_int(slits, "xind", i, NULL); 15158 if (xcur == xprev) { 15159 multiplex++; 15160 } 15161 else { 15162 xprev = xcur; 15163 multiplex = 0; 15164 } 15165 cpl_table_set_int(slits, "multiplex", i, multiplex); 15166 } 15167 15168 cpl_table_save(slits, NULL, NULL, "multiplex.fits", CPL_IO_DEFAULT); 15169 15170 cpl_table_erase_column(slits, "xind"); 15171 15172 return 1 + cpl_table_get_column_max(slits, "multiplex"); 15173 15174 } 15175 15176 15203 cpl_table *mos_load_overscans_vimos(const cpl_propertylist *header, 15204 int check_consistency) 15205 { 15206 const char *func = "mos_load_overscans_vimos"; 15207 15208 int nx = 0; 15209 int ny = 0; 15210 int px = 0; 15211 int py = 0; 15212 int ox = 0; 15213 int oy = 0; 15214 int vx = 0; 15215 int vy = 0; 15216 int nrows; 15217 cpl_table *overscans; 15218 15219 15220 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15221 cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message()); 15222 return NULL; 15223 } 15224 15225 if (header == NULL) { 15226 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15227 return NULL; 15228 } 15229 15230 if (cpl_propertylist_has(header, "NAXIS1")) 15231 nx = cpl_propertylist_get_int(header, "NAXIS1"); 15232 if (cpl_propertylist_has(header, "NAXIS2")) 15233 ny = cpl_propertylist_get_int(header, "NAXIS2"); 15234 if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCX")) 15235 px = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCX"); 15236 if (cpl_propertylist_has(header, "ESO DET OUT1 PRSCY")) 15237 py = cpl_propertylist_get_int(header, "ESO DET OUT1 PRSCY"); 15238 if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCX")) 15239 ox = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCX"); 15240 if (cpl_propertylist_has(header, "ESO DET OUT1 OVSCY")) 15241 oy = cpl_propertylist_get_int(header, "ESO DET OUT1 OVSCY"); 15242 if (cpl_propertylist_has(header, "ESO DET OUT1 NX")) 15243 vx = cpl_propertylist_get_int(header, "ESO DET OUT1 NX"); 15244 if (cpl_propertylist_has(header, "ESO DET OUT1 NY")) 15245 vy = cpl_propertylist_get_int(header, "ESO DET OUT1 NY"); 15246 15247 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15248 cpl_msg_error(func, "Missing overscan keywords in header"); 15249 cpl_error_set_where(func); 15250 return NULL; 15251 } 15252 15253 if (px < 0 || py < 0 || ox < 0 || oy < 0) { 15254 cpl_msg_error(func, "Missing overscan keywords in header"); 15255 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15256 return NULL; 15257 } 15258 15259 if ((px + vx + ox != nx) || (py + vy + oy != ny)) { 15260 if (check_consistency) { 15261 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15262 return NULL; 15263 } 15264 else { 15265 cpl_msg_debug(func, "Overscans description conflicts with " 15266 "reported image sizes, " 15267 "%d + %d + %d != %d or " 15268 "%d + %d + %d != %d", 15269 px, vx, ox, nx, 15270 py, vy, oy, ny); 15271 } 15272 } 15273 15274 nrows = 0; 15275 if (px > 0) 15276 nrows++; 15277 if (ox > 0) 15278 nrows++; 15279 if (py > 0) 15280 nrows++; 15281 if (oy > 0) 15282 nrows++; 15283 15284 if (nrows > 2) { 15285 cpl_msg_error(func, "Unexpected overscan regions " 15286 "(both in X and Y direction)"); 15287 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15288 return NULL; 15289 } 15290 15291 15292 /* 15293 * A row is added for the description of the valid region of the 15294 * exposure the input header belongs to. 15295 */ 15296 15297 nrows++; 15298 15299 overscans = cpl_table_new(nrows); 15300 cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT); 15301 cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT); 15302 cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT); 15303 cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT); 15304 15305 nrows = 0; 15306 15307 cpl_table_set_int(overscans, "xlow", nrows, px); 15308 cpl_table_set_int(overscans, "ylow", nrows, py); 15309 cpl_table_set_int(overscans, "xhig", nrows, nx - ox); 15310 cpl_table_set_int(overscans, "yhig", nrows, ny - oy); 15311 nrows++; 15312 15313 if (px > 0) { 15314 cpl_table_set_int(overscans, "xlow", nrows, 0); 15315 cpl_table_set_int(overscans, "ylow", nrows, 0); 15316 cpl_table_set_int(overscans, "xhig", nrows, px); 15317 cpl_table_set_int(overscans, "yhig", nrows, ny); 15318 nrows++; 15319 } 15320 15321 if (ox > 0) { 15322 cpl_table_set_int(overscans, "xlow", nrows, nx - ox); 15323 cpl_table_set_int(overscans, "ylow", nrows, 0); 15324 cpl_table_set_int(overscans, "xhig", nrows, nx); 15325 cpl_table_set_int(overscans, "yhig", nrows, ny); 15326 nrows++; 15327 } 15328 15329 if (py > 0) { 15330 cpl_table_set_int(overscans, "xlow", nrows, 0); 15331 cpl_table_set_int(overscans, "ylow", nrows, 0); 15332 cpl_table_set_int(overscans, "xhig", nrows, nx); 15333 cpl_table_set_int(overscans, "yhig", nrows, py); 15334 nrows++; 15335 } 15336 15337 if (oy > 0) { 15338 cpl_table_set_int(overscans, "xlow", nrows, 0); 15339 cpl_table_set_int(overscans, "ylow", nrows, ny - oy); 15340 cpl_table_set_int(overscans, "xhig", nrows, nx); 15341 cpl_table_set_int(overscans, "yhig", nrows, ny); 15342 nrows++; 15343 } 15344 15345 return overscans; 15346 15347 } 15348 15349 15350 cpl_table *mos_load_overscans_fors(const cpl_propertylist *header) 15351 { 15352 const char *func = "mos_load_overscans_fors"; 15353 15354 int nports; 15355 int nx = 0; 15356 int ny = 0; 15357 int px = 0; 15358 int py = 0; 15359 int ox = 0; 15360 int oy = 0; 15361 int rebin; 15362 int nrows; 15363 cpl_table *overscans; 15364 15365 15366 if (cpl_error_get_code() != CPL_ERROR_NONE) { 15367 cpl_msg_error(func, "Reset your error: %s", cpl_error_get_message()); 15368 return NULL; 15369 } 15370 15371 if (header == NULL) { 15372 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15373 return NULL; 15374 } 15375 15376 if (cpl_propertylist_has(header, "ESO DET OUTPUTS")) 15377 nports = cpl_propertylist_get_int(header, "ESO DET OUTPUTS"); 15378 15379 if (nports == 4 && 15380 cpl_propertylist_has(header, "ESO DET OUT1 PRSCX") && 15381 cpl_propertylist_has(header, "ESO DET WIN1 BINX")) { 15382 15383 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX"); 15384 15385 overscans = cpl_table_new(3); 15386 cpl_table_new_column(overscans, "xlow", CPL_TYPE_INT); 15387 cpl_table_new_column(overscans, "ylow", CPL_TYPE_INT); 15388 cpl_table_new_column(overscans, "xhig", CPL_TYPE_INT); 15389 cpl_table_new_column(overscans, "yhig", CPL_TYPE_INT); 15390 15391 px = 16 / rebin; 15392 ox = 16 / rebin; 15393 nx = 2080 / rebin; 15394 ny = 2048 / rebin; 15395 nrows = 0; 15396 15397 cpl_table_set_int(overscans, "xlow", nrows, px); 15398 cpl_table_set_int(overscans, "ylow", nrows, py); 15399 cpl_table_set_int(overscans, "xhig", nrows, nx - ox); 15400 cpl_table_set_int(overscans, "yhig", nrows, ny - oy); 15401 nrows++; 15402 15403 cpl_table_set_int(overscans, "xlow", nrows, 0); 15404 cpl_table_set_int(overscans, "ylow", nrows, 0); 15405 cpl_table_set_int(overscans, "xhig", nrows, px); 15406 cpl_table_set_int(overscans, "yhig", nrows, ny); 15407 nrows++; 15408 15409 cpl_table_set_int(overscans, "xlow", nrows, nx - ox); 15410 cpl_table_set_int(overscans, "ylow", nrows, 0); 15411 cpl_table_set_int(overscans, "xhig", nrows, nx); 15412 cpl_table_set_int(overscans, "yhig", nrows, ny); 15413 nrows++; 15414 } 15415 else { 15416 overscans = mos_load_overscans_vimos(header, 0); 15417 } 15418 15419 return overscans; 15420 15421 } 15422 15454 #define READY 1 15455 #ifdef READY 15456 15457 cpl_polynomial *mos_montecarlo_polyfit(cpl_table *points, cpl_table *evaluate, 15458 int samples, int order) 15459 { 15460 15461 const char *func = "mos_montecarlo_polyfit"; 15462 15463 cpl_polynomial *p; 15464 cpl_polynomial *q; 15465 cpl_vector *listx; 15466 cpl_vector *listy; 15467 double err; 15468 double *x; 15469 double *px; 15470 double *x_eval; 15471 double *px_eval; 15472 double *sigma; 15473 double *vy; 15474 double *dy; 15475 int npoints, nevaluate; 15476 int i, j; 15477 15478 15479 if (points == NULL || evaluate == NULL) { 15480 cpl_error_set(func, CPL_ERROR_NULL_INPUT); 15481 return NULL; 15482 } 15483 15484 if (!cpl_table_has_column(points, "x")) { 15485 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15486 return NULL; 15487 } 15488 15489 if (cpl_table_get_column_type(points, "x") != CPL_TYPE_DOUBLE) { 15490 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15491 return NULL; 15492 } 15493 15494 if (cpl_table_has_invalid(points, "x")) { 15495 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15496 return NULL; 15497 } 15498 15499 if (!cpl_table_has_column(points, "y")) { 15500 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15501 return NULL; 15502 } 15503 15504 if (cpl_table_get_column_type(points, "y") != CPL_TYPE_DOUBLE) { 15505 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15506 return NULL; 15507 } 15508 15509 if (cpl_table_has_invalid(points, "y")) { 15510 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15511 return NULL; 15512 } 15513 15514 if (cpl_table_has_column(points, "y_err")) { 15515 15516 if (cpl_table_get_column_type(points, "y_err") != CPL_TYPE_DOUBLE) { 15517 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15518 return NULL; 15519 } 15520 15521 if (cpl_table_has_invalid(points, "y_err")) { 15522 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15523 return NULL; 15524 } 15525 } 15526 15527 if (!cpl_table_has_column(evaluate, "x")) { 15528 cpl_error_set(func, CPL_ERROR_DATA_NOT_FOUND); 15529 return NULL; 15530 } 15531 15532 if (cpl_table_get_column_type(evaluate, "x") != CPL_TYPE_DOUBLE) { 15533 cpl_error_set(func, CPL_ERROR_INVALID_TYPE); 15534 return NULL; 15535 } 15536 15537 if (cpl_table_has_invalid(evaluate, "x")) { 15538 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15539 return NULL; 15540 } 15541 15542 if (samples < 2 || order < 0) { 15543 cpl_error_set(func, CPL_ERROR_ILLEGAL_INPUT); 15544 return NULL; 15545 } 15546 15547 npoints = cpl_table_get_nrow(points); 15548 listx = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "x")); 15549 listy = cpl_vector_wrap(npoints, cpl_table_get_data_double(points, "y")); 15550 15551 p = cpl_polynomial_fit_1d_create(listx, listy, order, &err); 15552 15553 if (!cpl_table_has_column(points, "y_err")) { 15554 err = sqrt(err); 15555 cpl_table_new_column(points, "y_err", CPL_TYPE_DOUBLE); 15556 cpl_table_fill_column_window_double(points, "y_err", 0, npoints, err); 15557 cpl_msg_info(func, "Error column not found - set to %f\n", err); 15558 } 15559 15560 /* 15561 * Create columns containing modeled values at each x 15562 */ 15563 15564 if (cpl_table_has_column(points, "px")) 15565 cpl_table_erase_column(points, "px"); 15566 cpl_table_new_column(points, "px", CPL_TYPE_DOUBLE); 15567 cpl_table_fill_column_window_double(points, "px", 0, npoints, 0); 15568 x = cpl_table_get_data_double(points, "x"); 15569 px = cpl_table_get_data_double(points, "px"); 15570 for (i = 0; i < npoints; i++) 15571 px[i] = cpl_polynomial_eval_1d(p, x[i], NULL); 15572 15573 nevaluate = cpl_table_get_nrow(evaluate); 15574 15575 if (cpl_table_has_column(evaluate, "px")) 15576 cpl_table_erase_column(evaluate, "px"); 15577 cpl_table_new_column(evaluate, "px", CPL_TYPE_DOUBLE); 15578 cpl_table_fill_column_window_double(evaluate, "px", 0, nevaluate, 0); 15579 x_eval = cpl_table_get_data_double(evaluate, "x"); 15580 px_eval = cpl_table_get_data_double(evaluate, "px"); 15581 for (i = 0; i < nevaluate; i++) 15582 px_eval[i] = cpl_polynomial_eval_1d(p, x_eval[i], NULL); 15583 15584 /* 15585 * Initialise column with sigma 15586 */ 15587 15588 if (cpl_table_has_column(evaluate, "sigma")) 15589 cpl_table_erase_column(evaluate, "sigma"); 15590 cpl_table_new_column(evaluate, "sigma", CPL_TYPE_DOUBLE); 15591 cpl_table_fill_column_window_double(evaluate, "sigma", 0, nevaluate, 0); 15592 sigma = cpl_table_get_data_double(evaluate, "sigma"); 15593 15594 /* 15595 * Compute varied y cordinates to fit 15596 */ 15597 15598 if (cpl_table_has_column(points, "vy")) 15599 cpl_table_erase_column(points, "vy"); 15600 cpl_table_new_column(points, "vy", CPL_TYPE_DOUBLE); 15601 cpl_table_fill_column_window_double(points, "vy", 0, npoints, 0); 15602 vy = cpl_table_get_data_double(points, "vy"); 15603 dy = cpl_table_get_data_double(points, "y_err"); 15604 cpl_vector_unwrap(listy); 15605 listy = cpl_vector_wrap(npoints, vy); 15606 15607 for (i = 0; i < samples; i++) { 15608 for (j = 0; j < npoints; j++) 15609 vy[j] = px[j] + dy[j] * mos_randg(1); 15610 q = cpl_polynomial_fit_1d_create(listx, listy, order, NULL); 15611 for (j = 0; j < nevaluate; j++) 15612 sigma[j] += fabs(px_eval[j] 15613 - cpl_polynomial_eval_1d(q, x_eval[j], NULL)); 15614 cpl_polynomial_delete(q); 15615 } 15616 15617 /* 15618 * Factor 1.25 to convert average deviation to sigma 15619 */ 15620 15621 cpl_table_multiply_scalar(evaluate, "sigma", 1.25); 15622 cpl_table_divide_scalar(evaluate, "sigma", samples); 15623 15624 cpl_vector_unwrap(listx); 15625 cpl_vector_unwrap(listy); 15626 15627 return p; 15628 } 15629 15630 #endif 15631 15654 cpl_error_code mos_randomise_image(cpl_image *image, double ron, 15655 double gain, double bias) 15656 { 15657 float *data; 15658 int npix, i; 15659 15660 15661 if (image == NULL) 15662 return cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 15663 15664 if (ron < 0.0 || gain <= FLT_EPSILON) 15665 return cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT); 15666 15667 data = cpl_image_get_data_float(image); 15668 npix = cpl_image_get_size_x(image) * cpl_image_get_size_y(image); 15669 ron *= ron; 15670 15671 for (i = 0; i < npix; i++) { 15672 if (data[i] < bias) { 15673 data[i] += sqrt(ron) * mos_randg(1); 15674 } 15675 else { 15676 data[i] += sqrt(ron + (data[i] - bias) / gain) * mos_randg(1); 15677 } 15678 } 15679 15680 return CPL_ERROR_NONE; 15681 } 15682 15683 15698 cpl_error_code mos_refmask_find_gaps(cpl_mask *refmask, 15699 cpl_image *master_flat, 15700 double level) 15701 { 15702 int nx = cpl_mask_get_size_x(refmask); 15703 int ny = cpl_mask_get_size_y(refmask); 15704 15705 int * xpos = cpl_calloc(sizeof(int), ny); 15706 15707 cpl_image * filtered = cpl_image_duplicate(master_flat); 15708 cpl_mask * kernel = cpl_mask_new(9, 9); 15709 cpl_vector * v = cpl_vector_new(ny); 15710 cpl_vector * truev; 15711 int nvalid = 0; 15712 double * flats = cpl_vector_get_data(v); 15713 15714 double median, stdev, delta; 15715 15716 int i, kill; 15717 15718 15719 cpl_mask_not(kernel); 15720 cpl_image_filter_mask(filtered, master_flat, kernel, 15721 CPL_FILTER_MEDIAN, CPL_BORDER_COPY); 15722 cpl_mask_delete(kernel); 15723 15724 for (i = 1; i <= ny; i++) { 15725 int j = 0; 15726 15727 do j++; 15728 while (!cpl_mask_get(refmask, j, i) && j < nx); 15729 15730 if (j < nx) { 15731 int rejected; 15732 15733 xpos[i - 1] = j; 15734 flats[nvalid] = cpl_image_get(filtered, j, i, &rejected); 15735 nvalid++; 15736 } 15737 else { 15738 xpos[i - 1] = -1; 15739 } 15740 } 15741 15742 if (nvalid == 0) 15743 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 15744 15745 truev = cpl_vector_wrap(nvalid, flats); 15746 15747 median = cpl_vector_get_median(truev); 15748 15749 if (level < 0.0) 15750 stdev = cpl_vector_get_stdev(truev); 15751 15752 cpl_vector_unwrap(truev); 15753 cpl_vector_delete(v); 15754 15755 for (i = 1; i <= ny; i++) { 15756 if (xpos[i - 1] > 0) { 15757 int rejected; 15758 double kappa = 1.5; 15759 15760 delta = cpl_image_get(filtered, xpos[i - 1], i, &rejected) - median; 15761 15762 if (level < 0.0) 15763 kill = fabs(delta) > stdev * kappa; 15764 else 15765 kill = delta < level; 15766 15767 if (kill) { 15768 int j = 0; 15769 15770 while (cpl_mask_get(refmask, xpos[i - 1] + j, i)) { 15771 cpl_mask_set(refmask, xpos[i - 1] + j, i, CPL_BINARY_0); 15772 j++; 15773 } 15774 } 15775 } 15776 } 15777 15778 cpl_image_delete(filtered); 15779 cpl_free(xpos); 15780 15781 return cpl_error_get_code(); 15782 } 15783 15791 cpl_error_code mos_saturation_process(cpl_image * image) 15792 { 15793 int nx = cpl_image_get_size_x(image); 15794 int ny = cpl_image_get_size_y(image); 15795 int npix = nx * ny; 15796 float * sdata = cpl_image_get_data_float(image); 15797 15798 int count, i, j, k; 15799 15800 /* 15801 * This is used to avoid saturation level coded with pixel value zero 15802 * To make it more robust against random 0.0 values, check that also 15803 * next pixel along the spatial direction is 0.0. 15804 */ 15805 15806 for (i = 0; i < npix - nx; i++) 15807 if (sdata[i] == 0.0 && sdata[i + nx] == 0.0) 15808 sdata[i] = 65535.0; 15809 15810 for (i = npix - nx; i < npix; i++) 15811 if (sdata[i] == 0.0) 15812 sdata[i] = 65535.0; 15813 15814 /* 15815 * This is a dirty trick to overcome saturations (making up a false 15816 * tip on their flat tops). This should be useless with a better 15817 * peak detection algorithm. 15818 */ 15819 15820 for (i = 0; i < npix; i++) { 15821 if (sdata[i] >= 65535.0) { 15822 count = 0; 15823 for (j = i; j < npix; j++) { 15824 if (sdata[j] < 65535.0) { 15825 break; 15826 } 15827 else { 15828 count++; 15829 } 15830 } 15831 if (count < 30 && count > 2) { 15832 for (j = i; j < i + count/2; j++) 15833 sdata[j] = sdata[i] + 1000.0 * (j - i); 15834 if (count % 2 != 0) { 15835 sdata[j] = sdata[j-1] + 1000.0; 15836 j++; 15837 } 15838 for (k = j; k <= i + count; k++) 15839 sdata[k] = sdata[i] - 1000.0 * (k - i - count); 15840 i = k; 15841 } 15842 } 15843 } 15844 15845 return cpl_error_get_code(); 15846 } 15847 15848 15857 cpl_error_code mos_subtract_background(cpl_image * image) 15858 { 15859 /* 15860 * Create and subtract background 15861 */ 15862 15863 cpl_image * bimage = mos_arc_background(image, 15, 15); 15864 cpl_image_subtract(image, bimage); 15865 cpl_image_delete(bimage); 15866 15867 return cpl_error_get_code(); 15868 } 15869 15870 15887 cpl_error_code mos_object_intersect(cpl_table **slitss, cpl_table *origslits, 15888 int nscience, float tolerance) 15889 { 15890 int i, j; 15891 15892 cpl_table *summary; 15893 int summary_nobjs = 0; 15894 15895 int nobjs; 15896 15897 int nmatches; 15898 int nslits = cpl_table_get_nrow(slitss[0]); 15899 15900 int maxobjs; 15901 int k, m; 15902 int nstokes, sstokes; 15903 15904 cpl_table **work; 15905 15906 work = (cpl_table **)cpl_malloc(sizeof(cpl_table *) * nscience); 15907 15908 15909 /* 15910 * First we build a table listing the offset of each detected 15911 * object at each angle and each beam, from the bottom of each 15912 * slit spectrum, and the pair that slit spectrum belongs to. 15913 * This summary table will have as many rows as objects found 15914 * in total at all angles. 15915 */ 15916 15917 for (j = 0; j < nscience; j++) { 15918 int c_nobjs = mos_get_nobjects(slitss[j]); 15919 if (!c_nobjs) 15920 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 15921 summary_nobjs += c_nobjs; 15922 } 15923 15924 summary = cpl_table_new(summary_nobjs); 15925 15926 cpl_table_new_column(summary, "offset", CPL_TYPE_DOUBLE); 15927 cpl_table_new_column(summary, "pair", CPL_TYPE_INT); 15928 cpl_table_new_column(summary, "absolute", CPL_TYPE_DOUBLE); 15929 cpl_table_new_column(summary, "pos", CPL_TYPE_DOUBLE); 15930 15931 /* 15932 * Fill the summary table with data from all objects: 15933 */ 15934 15935 nobjs = 0; 15936 15937 /* Loop on all object tables (one for each angle) */ 15938 for (j = 0; j < nscience; j++) { 15939 int c_maxobjs = mos_get_maxobjs_per_slit(slitss[j]); 15940 15941 /* Loop on all slits found on first - i.e., ALL - object table */ 15942 for (k = 0; k < nslits; k++) { 15943 15944 /* Loop on all objects found on each object table */ 15945 for (m = 0; m < c_maxobjs; m++) { 15946 int null; 15947 char *name = cpl_sprintf("object_%d", m + 1); 15948 double obj = cpl_table_get_double(slitss[j], name, k, &null); 15949 int pos; 15950 int pair; 15951 15952 cpl_free(name); 15953 15954 if (null) 15955 break; /* No object #m+1 in this slit - go to next slit */ 15956 15957 /* 15958 * Copy necessary object data to summary table. Note 15959 * that the absolute object position (row) in the 15960 * rectified image is made relative to the bottom 15961 * position (row) of the current slit. 15962 */ 15963 15964 pos = cpl_table_get_int(slitss[j], "position", k, &null); 15965 pair = cpl_table_get_int(slitss[j], "pair_id", k, &null); 15966 cpl_table_set(summary, "absolute", nobjs, obj); 15967 cpl_table_set(summary, "pos", nobjs, pos); 15968 cpl_table_set(summary, "offset", nobjs, obj - pos); 15969 cpl_table_set(summary, "pair", nobjs, pair); 15970 15971 nobjs++; 15972 } 15973 } 15974 } 15975 15976 // cpl_table_save(summary, NULL, NULL, "susu.fits", CPL_IO_DEFAULT); 15977 15978 /* 15979 * Perform the intersection: what are the objects belonging 15980 * to the same slit (same pair ordinary + extraordinary) which 15981 * are observed at the same offset at all angles? Those are 15982 * the polarimetric objects. 15983 */ 15984 15985 nmatches = 0; 15986 maxobjs = mos_get_maxobjs_per_slit(slitss[0]); 15987 15988 /* 15989 * We loop on the objects of the first-angle object table as 15990 * reference, and check whether those objects are present also 15991 * at *all* other angles. Note that the loop advances by pairs. 15992 * If the top (k = 0) slit spectrum is not an ordinary beam, 15993 * it is ignored. The loop advances by pairs, starting at the 15994 * first complete pair. It is implicitely assumed that the 15995 * slit spectrum on top is always from the ordinary beam, and 15996 * the spectrum below (k+1) its extraordinary match. 15997 */ 15998 15999 for (k = 0; k < nslits; k+=2) { 16000 int slitmatches = 0; 16001 16002 if (k == 0) { 16003 if (cpl_table_get_int(slitss[0], "pair_id", 0, NULL) != 16004 cpl_table_get_int(slitss[0], "pair_id", 1, NULL)) { 16005 16006 /* 16007 * This is not an ordinary beam - advance to next slit. 16008 */ 16009 16010 k++; 16011 continue; 16012 } 16013 } 16014 16015 for (m = 0; m < maxobjs; m++) { 16016 int null; 16017 char *name = cpl_sprintf("object_%d", m + 1); 16018 double obj = cpl_table_get_double(slitss[0], name, k, &null); 16019 double pos; 16020 int pair; 16021 16022 char *name_obj = NULL; 16023 char *name_start = NULL; 16024 char *name_end = NULL; 16025 char *name_row = NULL; 16026 char *name_row_s = NULL; 16027 16028 char *name_start_o = NULL; 16029 char *name_end_o = NULL; 16030 char *name_row_o = NULL; 16031 char *name_start_v = NULL; 16032 char *name_end_v = NULL; 16033 char *name_obj_v = NULL; 16034 16035 int start, end; 16036 int length; 16037 16038 int selected; 16039 int v, start_v, end_v; 16040 double min_v, obj_v; 16041 16042 16043 cpl_free(name); 16044 16045 if (null) 16046 break; 16047 16048 /* 16049 * Each object of the first object table belongs to a 16050 * slit spectrum (k). This slit spectrum has a position 16051 * in the rectified image, and it belongs to a given 16052 * ordinary + extraordinary pair. 16053 */ 16054 16055 pos = cpl_table_get_int(slitss[0], "position", k, &null); 16056 pair = cpl_table_get_int(slitss[0], "pair_id", k, &null); 16057 16058 /* 16059 * Now from the summary table we can select all objects 16060 * which have the same offset (obj - pos) within all slit 16061 * spectra belonging to the same ordinary + extraordinary 16062 * pair (at all angles). 16063 */ 16064 16065 cpl_table_select_all(summary); /* Reset selection */ 16066 16067 cpl_table_and_selected_int(summary, "pair", CPL_EQUAL_TO, pair); 16068 cpl_table_and_selected_double(summary, "offset", CPL_LESS_THAN, 16069 obj - pos + tolerance); 16070 selected = 16071 cpl_table_and_selected_double(summary, "offset", CPL_GREATER_THAN, 16072 obj - pos - tolerance); 16073 16074 16075 /* 16076 * If this object were observed at all angles (nscience) and 16077 * at all beams (2), we should have selected exactly 2*nscience 16078 * objects. If not, this is not a polarimetric object, and it 16079 * is discarded from the intersection. 16080 */ 16081 16082 if (selected != nscience * 2) 16083 continue; 16084 16085 /* 16086 * If we reach this point we have found one valid polarimetric 16087 * object, that must be inserted in the intersection object 16088 * table. 16089 */ 16090 16091 slitmatches++; 16092 16093 /* 16094 * Names of the columns of the output table where the 16095 * object information needs to be copied. Note that a 16096 * new column is created, the "row_stokes_#", where the 16097 * row number of the extracted polarimetric signal is 16098 * also computed. For the moment this column will be 16099 * left empty - it will be filled only when all matches 16100 * are collected. 16101 */ 16102 16103 name_obj = cpl_sprintf("object_%d", slitmatches); 16104 name_start = cpl_sprintf("start_%d", slitmatches); 16105 name_end = cpl_sprintf("end_%d", slitmatches); 16106 name_row = cpl_sprintf("row_%d", slitmatches); 16107 name_row_s = cpl_sprintf("row_stokes_%d", slitmatches); 16108 16109 /* 16110 * Names of the columns of the input table where the 16111 * object information is available. 16112 */ 16113 16114 name_start_o = cpl_sprintf("start_%d", m + 1); 16115 name_end_o = cpl_sprintf("end_%d", m + 1); 16116 name_row_o = cpl_sprintf("row_%d", m + 1); 16117 16118 /* 16119 * If the output columns do not exist yet, create them. 16120 */ 16121 16122 if (!cpl_table_has_column(origslits, name_obj)) { 16123 cpl_table_new_column(origslits, name_obj, CPL_TYPE_DOUBLE); 16124 cpl_table_new_column(origslits, name_start, CPL_TYPE_INT); 16125 cpl_table_new_column(origslits, name_end, CPL_TYPE_INT); 16126 cpl_table_new_column(origslits, name_row, CPL_TYPE_INT); 16127 cpl_table_new_column(origslits, name_row_s, CPL_TYPE_INT); 16128 } 16129 16130 /* 16131 * The current slit spectrum is k. The slit spectrum immediately 16132 * below (in the rectified image) is k+1. We need the length of 16133 * the spectrum below for computing the _absolute_ coordinates 16134 * of the objects in the rectified image in both beams. 16135 */ 16136 16137 length = cpl_table_get_int(origslits, "length", k + 1, &null); 16138 16139 /* NEW: 16140 * Names of the columns of the input table where 16141 * the information of the corresponding object of 16142 * the next beam is available. 16143 */ 16144 16145 for (v = 0; v < maxobjs; v++) { 16146 char *name_v = cpl_sprintf("object_%d", v + 1); 16147 double obj_v = cpl_table_get_double(slitss[0], name_v, 16148 k + 1, &null); 16149 16150 cpl_free(name_v); 16151 16152 if (null) 16153 break; 16154 16155 if (v) { 16156 if (fabs(obj - length - obj_v) < min_v) { 16157 min_v = fabs(obj - length - obj_v); 16158 cpl_free(name_start_v); 16159 cpl_free(name_end_v); 16160 cpl_free(name_obj_v); 16161 name_start_v = cpl_sprintf("start_%d", v + 1); 16162 name_end_v = cpl_sprintf("end_%d", v + 1); 16163 name_obj_v = cpl_sprintf("object_%d", v + 1); 16164 } 16165 } 16166 else { 16167 min_v = fabs(obj - length - obj_v); 16168 name_start_v = cpl_sprintf("start_%d", v + 1); 16169 name_end_v = cpl_sprintf("end_%d", v + 1); 16170 name_obj_v = cpl_sprintf("object_%d", v + 1); 16171 } 16172 } 16173 16174 /* 16175 * Read from the first input object table (first angle) 16176 * the spatial window enclosing the object. 16177 */ 16178 16179 start = cpl_table_get_int(slitss[0], name_start_o, k, &null); 16180 end = cpl_table_get_int(slitss[0], name_end_o, k, &null); 16181 16182 /* NEW: 16183 * Spatial window of the matching object in the next beam. 16184 */ 16185 16186 start_v = cpl_table_get_int(slitss[0], name_start_v, k + 1, &null); 16187 end_v = cpl_table_get_int(slitss[0], name_end_v, k + 1, &null); 16188 obj_v = cpl_table_get_double(slitss[0], name_obj_v, k + 1, &null); 16189 16190 /* 16191 * Write the object coordinates in the same slit, and in the 16192 * slit below. Note that here we assume that all slits were 16193 * traced perfectly, and we compute the theoretical coords 16194 * (obj - length) within the next slit spectrum (k + 1). In 16195 * principle we should read them as well from the input 16196 * table! 16197 */ 16198 16199 cpl_table_set_double(origslits, name_obj, k, obj); 16200 cpl_table_set_double(origslits, name_obj, k + 1, obj_v); 16201 // cpl_table_set_double(origslits, name_obj, k + 1, obj - length); 16202 16203 cpl_table_set_int(origslits, name_start, k, start); 16204 cpl_table_set_int(origslits, name_start, k + 1, start_v); 16205 // cpl_table_set_int(origslits, name_start, k + 1, start - length); 16206 16207 cpl_table_set_int(origslits, name_end, k, end); 16208 cpl_table_set_int(origslits, name_end, k + 1, end_v); 16209 // cpl_table_set_int(origslits, name_end, k + 1, end - length); 16210 16211 /* 16212 * "nmatches" is counting at what "reduced" image row the 16213 * extracted spectra are. Note that this is s preliminary 16214 * numbering - which is wrong: other objects may be found 16215 * in the same slit, and then the indeces would not be in 16216 * sequence. What is important is that at the end of this 16217 * loop "nmatches" would be the total number of matching 16218 * objects. The two cpl_table_set_int() calls made here 16219 * cannot be removed - they "validate" those table elements 16220 * (see ahead). 16221 */ 16222 16223 cpl_table_set_int(origslits, name_row, k, nmatches); 16224 nmatches++; 16225 cpl_table_set_int(origslits, name_row, k + 1, nmatches); 16226 nmatches++; 16227 16228 cpl_free(name_obj); 16229 cpl_free(name_start); 16230 cpl_free(name_end); 16231 cpl_free(name_row); 16232 cpl_free(name_row_s); 16233 16234 cpl_free(name_start_o); 16235 cpl_free(name_end_o); 16236 cpl_free(name_row_o); 16237 16238 cpl_free(name_start_v); name_start_v = NULL; 16239 cpl_free(name_end_v); name_end_v = NULL; 16240 cpl_free(name_obj_v); name_obj_v = NULL; 16241 } 16242 } 16243 16244 /* 16245 * The summary table has fulfilled its function. If no matching 16246 * objects are found, the function returns with an error. 16247 */ 16248 16249 cpl_table_delete(summary); 16250 16251 if (!nmatches) 16252 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 16253 16254 /* 16255 * Now we consider the resulting intersection object table, 16256 * listing all matches. As seen, the image row number reported 16257 * in the columns "row_#" was not really performed sequentially. 16258 * We need to renumber sequentially... 16259 * We need also to fill the "row_stokes_#" column the way the 16260 * extracted polarimetric signal will be stored in the 16261 * reduced_pol_images... 16262 */ 16263 16264 maxobjs = mos_get_maxobjs_per_slit(origslits); 16265 nstokes = nmatches / 2; /* nmatches is always an even number */ 16266 16267 for (k = 0; k < nslits; k++) { 16268 if (k % 2) { /* Extraordinary beam */ 16269 nstokes = sstokes; /* Use same start value as for ordinary */ 16270 } 16271 else { /* Ordinary beam */ 16272 sstokes = nstokes; /* Memorise start value at ordinary beam */ 16273 } 16274 16275 for (m = 0; m < maxobjs; m++) { 16276 char *name = cpl_sprintf("row_%d", m + 1); 16277 char *namestokes = cpl_sprintf("row_stokes_%d", m + 1); 16278 16279 if (!cpl_table_is_valid(origslits, name, k)) { 16280 cpl_free(name); 16281 cpl_free(namestokes); 16282 break; 16283 } 16284 else { 16285 nmatches--; 16286 nstokes--; 16287 cpl_table_set_int(origslits, name, k, nmatches); 16288 cpl_table_set_int(origslits, namestokes, k, nstokes); 16289 } 16290 16291 cpl_free(name); 16292 cpl_free(namestokes); 16293 } 16294 } 16295 16296 16297 /* 16298 * This is done to avoid the NULL value is zero (it would invalidate 16299 * also the row_# = 0 or start_# = 0 for an object), and to enable 16300 * working directly with the column data buffers, when using this 16301 * table afterwards. 16302 */ 16303 16304 for (j = 0; j < maxobjs; j++) { 16305 char *name = cpl_sprintf("object_%d", j + 1); 16306 cpl_table_fill_invalid_double(origslits, name, -1); 16307 cpl_free(name); 16308 16309 name = cpl_sprintf("start_%d", j + 1); 16310 cpl_table_fill_invalid_int(origslits, name, -1); 16311 cpl_free(name); 16312 16313 name = cpl_sprintf("end_%d", j + 1); 16314 cpl_table_fill_invalid_int(origslits, name, -1); 16315 cpl_free(name); 16316 16317 name = cpl_sprintf("row_%d", j + 1); 16318 cpl_table_fill_invalid_int(origslits, name, -1); 16319 cpl_free(name); 16320 16321 name = cpl_sprintf("row_stokes_%d", j + 1); 16322 cpl_table_fill_invalid_int(origslits, name, -1); 16323 cpl_free(name); 16324 } 16325 16326 /********************************************************************* 16327 * This tail has been added to propagate the selection of valid 16328 * objects also to the input slitss[] tables. Just eliminate all 16329 * this final part to suppress this behaviour. 16330 */ 16331 16332 /* 16333 * First of all, make a working copy and remove all columns related 16334 * to objects from the input object tables. 16335 */ 16336 16337 for (i = 0; i < nscience; i++) { 16338 int c_maxobjs = mos_get_maxobjs_per_slit(slitss[i]); 16339 16340 work[i] = cpl_table_duplicate(slitss[i]); 16341 16342 for (m = 0; m < c_maxobjs; m++) { 16343 char *object_o = cpl_sprintf("object_%d", m + 1); 16344 char *start_o = cpl_sprintf("start_%d", m + 1); 16345 char *end_o = cpl_sprintf("end_%d", m + 1); 16346 char *row_o = cpl_sprintf("row_%d", m + 1); 16347 16348 cpl_table_erase_column(slitss[i], object_o); 16349 cpl_table_erase_column(slitss[i], start_o); 16350 cpl_table_erase_column(slitss[i], end_o); 16351 cpl_table_erase_column(slitss[i], row_o); 16352 } 16353 } 16354 16355 /* 16356 * Now just consider all the objects in the intersection table. 16357 */ 16358 16359 for (k = 0; k < nslits; k++) { 16360 for (j = 0; j < maxobjs; j++) { 16361 double object_w, object_r; 16362 int start_w, start_r; 16363 int end_w, end_r; 16364 int row_w, row_r; 16365 16366 char *object_i = cpl_sprintf("object_%d", j + 1); 16367 char *start_i = cpl_sprintf("start_%d", j + 1); 16368 char *end_i = cpl_sprintf("end_%d", j + 1); 16369 char *row_i = cpl_sprintf("row_%d", j + 1); 16370 16371 16372 if (!cpl_table_is_valid(origslits, object_i, k)) 16373 break; 16374 16375 /* 16376 * We have found a valid object (valid because it belongs 16377 * to the intersection). Now we look for this object in each 16378 * one of the original tables, we get its parameters, and 16379 * copy them at the right position (i.e., same position as 16380 * in intersection table). The object will be the one closest 16381 * to the object position (column object_i) in the intersection 16382 * table. Note that we examine the same row, k, in all tables. 16383 */ 16384 16385 object_w = cpl_table_get_double(origslits, object_i, k, NULL); 16386 start_w = cpl_table_get_int (origslits, start_i, k, NULL); 16387 end_w = cpl_table_get_int (origslits, end_i, k, NULL); 16388 row_w = cpl_table_get_int (origslits, row_i, k, NULL); 16389 16390 for (i = 0; i < nscience; i++) { 16391 int c_maxobjs = mos_get_maxobjs_per_slit(work[i]); 16392 int minpos; 16393 double mindiff, diff; 16394 char *object_o; 16395 char *start_o; 16396 char *end_o; 16397 char *row_o; 16398 16399 for (m = 0; m < c_maxobjs; m++) { 16400 object_o = cpl_sprintf("object_%d", m + 1); 16401 start_o = cpl_sprintf("start_%d", m + 1); 16402 end_o = cpl_sprintf("end_%d", m + 1); 16403 row_o = cpl_sprintf("row_%d", m + 1); 16404 16405 if (!cpl_table_is_valid(work[i], object_o, k)) 16406 break; 16407 16408 object_r = cpl_table_get_double(work[i], object_o, k, NULL); 16409 start_r = cpl_table_get_int (work[i], start_o, k, NULL); 16410 end_r = cpl_table_get_int (work[i], end_o, k, NULL); 16411 row_r = cpl_table_get_int (work[i], row_o, k, NULL); 16412 16413 diff = fabs(object_w - object_r); 16414 if (m) { 16415 if (mindiff > diff) { 16416 mindiff = diff; 16417 minpos = m; 16418 } 16419 } 16420 else { 16421 mindiff = diff; 16422 minpos = 0; 16423 } 16424 16425 cpl_free(object_o); 16426 cpl_free(start_o); 16427 cpl_free(end_o); 16428 cpl_free(row_o); 16429 } 16430 16431 object_o = cpl_sprintf("object_%d", minpos + 1); 16432 start_o = cpl_sprintf("start_%d", minpos + 1); 16433 end_o = cpl_sprintf("end_%d", minpos + 1); 16434 row_o = cpl_sprintf("row_%d", minpos + 1); 16435 16436 if (!cpl_table_has_column(slitss[i], object_i)) { 16437 cpl_table_new_column(slitss[i], object_i, CPL_TYPE_DOUBLE); 16438 cpl_table_new_column(slitss[i], start_i, CPL_TYPE_INT); 16439 cpl_table_new_column(slitss[i], end_i, CPL_TYPE_INT); 16440 cpl_table_new_column(slitss[i], row_i, CPL_TYPE_INT); 16441 cpl_table_fill_invalid_double(slitss[i], object_i, -1); 16442 cpl_table_fill_invalid_int (slitss[i], start_i, -1); 16443 cpl_table_fill_invalid_int (slitss[i], end_i, -1); 16444 cpl_table_fill_invalid_int (slitss[i], row_i, -1); 16445 } 16446 16447 cpl_table_set_double(slitss[i], object_i, k, 16448 cpl_table_get_double(work[i], object_o, 16449 k, NULL)); 16450 cpl_table_set_int(slitss[i], start_i , k, 16451 cpl_table_get_int(work[i], start_o, k, NULL)); 16452 cpl_table_set_int(slitss[i], end_i , k, 16453 cpl_table_get_int(work[i], end_o, k, NULL)); 16454 cpl_table_set_int(slitss[i], row_i , k, row_w); 16455 16456 cpl_free(object_o); 16457 cpl_free(start_o); 16458 cpl_free(end_o); 16459 cpl_free(row_o); 16460 } 16461 16462 cpl_free(object_i); 16463 cpl_free(start_i); 16464 cpl_free(end_i); 16465 cpl_free(row_i); 16466 } 16467 } 16468 16469 for (i = 0; i < nscience; i++) 16470 cpl_table_delete(work[i]); 16471 16472 cpl_free(work); 16473 16474 16475 return cpl_error_get_code(); 16476 } 16477 16478 16486 int mos_get_maxobjs_per_slit(cpl_table * slits) 16487 { 16488 int maxobjs = 1; 16489 16490 char * colname = cpl_sprintf("object_%d", maxobjs); 16491 16492 while (cpl_table_has_column(slits, colname)) { 16493 maxobjs++; 16494 cpl_free(colname); 16495 colname = cpl_sprintf("object_%d", maxobjs); 16496 } 16497 16498 cpl_free(colname); 16499 16500 maxobjs--; 16501 16502 return maxobjs; 16503 } 16504 16512 int mos_get_nobjects(cpl_table * slits) 16513 { 16514 int nobjs = 0; 16515 16516 int nslits = cpl_table_get_nrow(slits); 16517 int maxobjs = mos_get_maxobjs_per_slit(slits); 16518 16519 int k, m; 16520 16521 for (k = 0; k < nslits; k++) { 16522 for (m = 0; m < maxobjs; m++) { 16523 char * name = cpl_sprintf("object_%d", m + 1); 16524 int null = !cpl_table_is_valid(slits, name, k); 16525 16526 cpl_free(name); 16527 16528 if (null) break; 16529 else nobjs++; 16530 } 16531 } 16532 16533 return nobjs; 16534 } 16535 16543 int mos_check_slits(cpl_table *slits, float rescale) 16544 { 16545 16546 cpl_propertylist *sort; 16547 16548 int nslits = cpl_table_get_nrow(slits); 16549 16550 int k, null; 16551 16552 const float interval = 90.0 * rescale; 16553 const float offset = (90.0 - 5) * rescale; 16554 16555 16556 for (k = 0; k < nslits; k++) { 16557 double ytop = cpl_table_get_double(slits, "ytop", k, &null); 16558 double ybottom = cpl_table_get_double(slits, "ybottom", k, &null); 16559 16560 double xtop = cpl_table_get_double(slits, "xtop", k, &null); 16561 double xbottom = cpl_table_get_double(slits, "xbottom", k, &null); 16562 16563 int nmiss = (int)((ytop - ybottom) / interval + 0.5); 16564 16565 if (nmiss > 1) { 16566 cpl_msg_warning(cpl_func, 16567 "Some slits could not be properly detected. " 16568 "There might be accountable inaccuracies."); 16569 while (nmiss > 1) { 16570 cpl_table_set_size(slits, nslits + 1); 16571 16572 /* Fill in new slit 'cut' */ 16573 16574 /* x coordinates be the same (acceptable approximation) */ 16575 cpl_table_set_double(slits, "xtop", nslits, xtop); 16576 cpl_table_set_double(slits, "xbottom", nslits, xbottom); 16577 16578 /* Cut */ 16579 if (k == 0) { 16580 cpl_table_set_double(slits, "ybottom", nslits, ybottom); 16581 cpl_table_set_double(slits, "ytop", nslits, ybottom 16582 + offset); 16583 ybottom += interval; 16584 cpl_table_set_double(slits, "ybottom", k, ybottom); 16585 } else { 16586 cpl_table_set_double(slits, "ytop", nslits, ytop); 16587 cpl_table_set_double(slits, "ybottom", nslits, ytop 16588 - offset); 16589 ytop -= interval; 16590 cpl_table_set_double(slits, "ytop", k, ytop); 16591 } 16592 16593 nslits++; nmiss--; 16594 } 16595 } 16596 } 16597 16598 sort = cpl_propertylist_new(); 16599 cpl_propertylist_append_bool(sort, "ytop", 1); 16600 cpl_table_sort(slits, sort); 16601 cpl_propertylist_delete(sort); 16602 16603 /* 16604 * Add here an ad hoc check on the last slit: is it too long 16605 * (by more than 10%)? Then shorten it... 16606 */ 16607 16608 k = cpl_table_get_nrow(slits) - 1; 16609 16610 { 16611 double ytop = cpl_table_get_double(slits, "ytop", k, &null); 16612 double ybottom = cpl_table_get_double(slits, "ybottom", k, &null); 16613 double length = (ytop - ybottom) / interval; 16614 16615 if (length > 1.1) { 16616 cpl_table_set_double(slits, "ybottom", k, ytop - offset); 16617 } 16618 16619 } 16620 16621 return 0; 16622 } 16623 16646 cpl_table *mos_load_slits_fors_pmos(cpl_propertylist *header, 16647 int * nslits_out_det) 16648 { 16649 int m, null; 16650 int halfsize; 16651 16652 cpl_propertylist * sort; 16653 cpl_table * slits; 16654 16655 slits = mos_load_slits_fors_mos(header, nslits_out_det); 16656 halfsize = cpl_table_get_nrow(slits); 16657 16658 cpl_table_set_size(slits, 2 * halfsize); 16659 16660 for (m = 0; m < halfsize; m++) { 16661 16662 double gap = 1.4; 16663 16664 double length = 16665 cpl_table_get(slits, "ytop", m, &null) - 16666 cpl_table_get(slits, "ybottom", m, &null); 16667 16668 if (m) { 16669 double interval = 16670 cpl_table_get(slits, "ybottom", m - 1, &null) - 16671 cpl_table_get(slits, "ytop", m, &null); 16672 16673 gap = (interval - length) / 2; 16674 } 16675 16676 cpl_table_set(slits, "slit_id", m + halfsize, 16677 cpl_table_get(slits, "slit_id", m, &null) - 1); 16678 16679 cpl_table_set(slits, "xtop", m + halfsize, 16680 cpl_table_get(slits, "xtop", m, &null)); 16681 16682 cpl_table_set(slits, "xbottom", m + halfsize, 16683 cpl_table_get(slits, "xbottom", m, &null)); 16684 16685 cpl_table_set(slits, "ytop", m + halfsize, 16686 cpl_table_get(slits, "ytop", m, &null) + gap + length); 16687 16688 cpl_table_set(slits, "ybottom", m + halfsize, 16689 cpl_table_get(slits, "ytop", m, &null) + gap); 16690 } 16691 16692 for (m = 0; m < 2 * halfsize; m++) { 16693 cpl_table_set(slits, "ytop", m, 16694 cpl_table_get(slits, "ytop", m, &null) - 5.3); 16695 16696 cpl_table_set(slits, "ybottom", m, 16697 cpl_table_get(slits, "ybottom", m, &null) - 5.3); 16698 16699 } 16700 16701 sort = cpl_propertylist_new(); 16702 cpl_propertylist_append_bool(sort, "ytop", 1); 16703 cpl_table_sort(slits, sort); 16704 16705 cpl_propertylist_delete(sort); 16706 16707 return slits; 16708 } 16709 16710 int * fors_get_nobjs_perslit(cpl_table * slits) 16711 { 16712 int nslits = cpl_table_get_nrow(slits); 16713 int maxobjs = mos_get_maxobjs_per_slit(slits); 16714 16715 int * nobjs_per_slit = cpl_malloc(sizeof(int) * nslits); 16716 16717 int k, m; 16718 16719 for (k = 0; k < nslits; k++) { 16720 int nobjs = 0; 16721 for (m = 0; m < maxobjs; m++) { 16722 char * name = cpl_sprintf("object_%d", m + 1); 16723 int null = !cpl_table_is_valid(slits, name, k); 16724 16725 cpl_free(name); 16726 16727 if (null) break; 16728 else nobjs++; 16729 } 16730 16731 nobjs_per_slit[k] = nobjs; 16732 } 16733 16734 return nobjs_per_slit; 16735 } 16736 16737 double fors_get_object_position(cpl_table *slits, int slit, int object) 16738 { 16739 char *name = cpl_sprintf("object_%d", object); 16740 double position; 16741 16742 position = cpl_table_get_double(slits, name, slit, NULL) 16743 - cpl_table_get_int(slits, "position", slit, NULL); 16744 16745 cpl_free(name); 16746 16747 return position; 16748 } 16749 16750 int mos_rebin_signal(cpl_image **image, int rebin) 16751 { 16752 cpl_image *rebinned; 16753 16754 16755 if (*image == NULL) 16756 return 1; 16757 16758 if (rebin == 1) 16759 return 0; 16760 16761 rebinned = cpl_image_rebin(*image, 1, 1, rebin, 1); 16762 16763 cpl_image_delete(*image); 16764 16765 *image = rebinned; 16766 16767 return 0; 16768 } 16769 16770 int mos_rebin_error(cpl_image **image, int rebin) 16771 { 16772 if (*image == NULL) 16773 return 1; 16774 16775 if (rebin == 1) 16776 return 0; 16777 16778 cpl_image_power(*image, 2); 16779 mos_rebin_signal(image, rebin); 16780 cpl_image_power(*image, 0.5); 16781 16782 return 0; 16783 } 16784 16785 /* 16786 * @brief 16787 * Map table values into a 1D image 16788 * 16789 * @param image Target image 16790 * @param start Coordinate of first pixel in image 16791 * @param step Coordinate step for one pixel in image 16792 * @param table Source table 16793 * @param xname Name of coordinate column 16794 * @param yname Name of values column 16795 * 16796 * @return 0 on success 16797 * 16798 * The values in @em yname are linearly interpolated at the @em image 16799 * pixel coordinates. The @em image must have Nx1 size. 16800 */ 16801 16802 int map_table(cpl_image *image, double start, double step, 16803 cpl_table *table, char *xname, char *yname) 16804 { 16805 int length = cpl_image_get_size_x(image); 16806 int nrows = cpl_table_get_nrow(table); 16807 float *data = cpl_image_get_data_float(image); 16808 float *fdata = NULL; 16809 double *xdata = NULL; 16810 double *ydata = NULL; 16811 cpl_type xtype = cpl_table_get_column_type(table, xname); 16812 cpl_type ytype = cpl_table_get_column_type(table, yname); 16813 double xzero, pos; 16814 int i, j, n; 16815 16816 16817 /* 16818 * Initialization of output image at 0.0 - this value is left 16819 * on non-overlapping portions. 16820 */ 16821 16822 for (i = 0; i < length; i++) 16823 data[i] = 0.0; 16824 16825 16826 /* 16827 * Do everything in double precision 16828 */ 16829 16830 if (xtype == CPL_TYPE_FLOAT) { 16831 fdata = cpl_table_get_data_float(table, xname); 16832 xdata = cpl_malloc(nrows * sizeof(double)); 16833 for (i = 0; i < nrows; i++) { 16834 xdata[i] = fdata[i]; 16835 } 16836 } 16837 else { 16838 xdata = cpl_table_get_data_double(table, xname); 16839 } 16840 16841 if (ytype == CPL_TYPE_FLOAT) { 16842 fdata = cpl_table_get_data_float(table, yname); 16843 ydata = cpl_malloc(nrows * sizeof(double)); 16844 for (i = 0; i < nrows; i++) { 16845 ydata[i] = fdata[i]; 16846 } 16847 } 16848 else { 16849 ydata = cpl_table_get_data_double(table, yname); 16850 } 16851 16852 /* 16853 * Mapping 16854 */ 16855 16856 n = 0; 16857 xzero = xdata[n]; 16858 16859 for (i = 0; i < length; i++) { 16860 pos = start + step * i; 16861 if (pos < xzero) 16862 continue; 16863 for (j = n; j < nrows; j++) { 16864 if (xdata[j] > pos) { 16865 n = j; 16866 data[i] = ydata[j-1] 16867 + (ydata[j] - ydata[j-1]) 16868 * (pos - xdata[j-1]) / (xdata[j] - xdata[j-1]); 16869 break; 16870 } 16871 } 16872 } 16873 16874 if (xtype == CPL_TYPE_FLOAT) 16875 cpl_free(xdata); 16876 16877 if (ytype == CPL_TYPE_FLOAT) 16878 cpl_free(ydata); 16879 16880 return 0; 16881 } 16882 16883 16884 /* 16885 * @brief 16886 * Fit overall trend of a Nx1 image 16887 * 16888 * @param image Values to smooth 16889 * @param order Order of fitting polynomial 16890 * @param hw Half width of smoothing window 16891 * 16892 * @return Smoothed image, or NULL on failure. 16893 * 16894 * Heavily smooth and fit data in the input Nx1 size @em image. 16895 */ 16896 16897 static cpl_image *polysmooth(cpl_image *image, int order, int hw) 16898 { 16899 int npoints; 16900 cpl_vector *x; 16901 cpl_vector *y; 16902 double *xdata; 16903 double *ydata; 16904 cpl_polynomial *poly; 16905 cpl_vector *ysmooth; 16906 cpl_image *smoothed; 16907 float *sdata; 16908 int i; 16909 16910 16911 npoints = cpl_image_get_size_x(image); 16912 16913 if (2 * hw + 1 > npoints) 16914 return NULL; 16915 16916 x = cpl_vector_new(npoints); 16917 y = cpl_vector_new(npoints); 16918 xdata = cpl_vector_get_data(x); 16919 ydata = cpl_vector_get_data(y); 16920 16921 smoothed = cpl_image_duplicate(image); 16922 sdata = cpl_image_get_data_float(smoothed); 16923 16924 for (i = 0; i < npoints; i++) { 16925 xdata[i] = i; 16926 ydata[i] = sdata[i]; 16927 } 16928 16929 ysmooth = cpl_vector_filter_median_create(y, hw); 16930 cpl_vector_delete(y); 16931 16932 poly = cpl_polynomial_fit_1d_create(x, ysmooth, order, NULL); 16933 cpl_vector_delete(x); 16934 cpl_vector_delete(ysmooth); 16935 16936 if (poly) { 16937 for (i = 0; i < npoints; i++) 16938 sdata[i] = cpl_polynomial_eval_1d(poly, i, NULL); 16939 16940 cpl_polynomial_delete(poly); 16941 } 16942 else { 16943 cpl_image_delete(smoothed); 16944 return NULL; 16945 } 16946 16947 return smoothed; 16948 } 16949 16950 #undef cleanup 16951 #define cleanup \ 16952 do { \ 16953 cpl_image_delete(spectrum); \ 16954 cpl_image_delete(flux); \ 16955 cpl_image_delete(efficiency); \ 16956 cpl_image_delete(smo_efficiency); \ 16957 cpl_image_delete(extinction); \ 16958 cpl_image_delete(response); \ 16959 cpl_image_delete(smo_response); \ 16960 cpl_image_delete(physical); \ 16961 } while (0) 16962 16986 cpl_table *mos_photometric_calibration(cpl_image *spectra, double startwave, 16987 double dispersion, double gain, 16988 double exptime, cpl_table *ext_table, 16989 double airmass, cpl_table *flux_table, 16990 int order) 16991 { 16992 16993 cpl_image *spectrum = NULL; // Extracted standard star spectrum 16994 float *data; 16995 cpl_image *extinction = NULL; // Extinction binned as "spectrum" 16996 float *ext_data; 16997 cpl_image *flux = NULL; // Standard star flux binned as "spectrum" 16998 float *flux_data; 16999 cpl_image *physical = NULL; // Physical units of above 17000 float *phys_data; 17001 cpl_image *efficiency = NULL; // Raw efficiency curve 17002 float *eff_data; 17003 cpl_image *smo_efficiency = NULL; // Smoothed efficiency curve 17004 float *smo_eff_data; 17005 cpl_image *response = NULL; // Raw response curve 17006 float *res_data; 17007 cpl_image *smo_response = NULL; // Smoothed response curve 17008 float *smo_res_data; 17009 cpl_image *image; 17010 cpl_image *smo_image; 17011 cpl_table *table; 17012 float lambda; 17013 int nx, ny; 17014 int ext_count, ext_pos; 17015 int eff_count, eff_pos; 17016 int flux_count, flux_pos; 17017 int start, end; 17018 int i; 17019 17020 17021 if (spectra == NULL || ext_table == NULL || flux_table == NULL) { 17022 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17023 return NULL; 17024 } 17025 17026 if (!cpl_table_has_column(ext_table, "WAVE")) { 17027 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17028 "Column WAVE in atmospheric extinction table"); 17029 return NULL; 17030 } 17031 17032 if (!cpl_table_has_column(ext_table, "EXTINCTION")) { 17033 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17034 "Column EXTINCTION in atmospheric extinction table"); 17035 return NULL; 17036 } 17037 17038 if (!cpl_table_has_column(flux_table, "WAVE")) { 17039 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17040 "Column WAVE in standard star flux table"); 17041 return NULL; 17042 } 17043 17044 if (!cpl_table_has_column(flux_table, "FLUX")) { 17045 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, 17046 "Column FLUX in standard star flux table"); 17047 return NULL; 17048 } 17049 17050 if (gain < 0.1) { 17051 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17052 "Invalid gain factor (%.2f)", gain); 17053 return NULL; 17054 } 17055 17056 if (exptime < 0.001) { 17057 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17058 "Invalid exposure time (%.2f)", exptime); 17059 return NULL; 17060 } 17061 17062 if (dispersion < 0.001) { 17063 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17064 "Invalid dispersion (%.2f)", dispersion); 17065 return NULL; 17066 } 17067 17068 if (order < 2) { 17069 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT, 17070 "Order of the polynomial fitting the " 17071 "instrument response must be at least 2"); 17072 return NULL; 17073 } 17074 17075 nx = cpl_image_get_size_x(spectra); 17076 ny = cpl_image_get_size_y(spectra); 17077 17078 17079 /* 17080 * Find brightest spectrum and duplicate it. 17081 */ 17082 17083 if (ny == 1) { 17084 spectrum = cpl_image_duplicate(spectra); 17085 } 17086 else { 17087 cpl_size x, y; 17088 cpl_image *brights = cpl_image_collapse_create(spectra, 1); 17089 17090 cpl_image_get_maxpos(brights, &x, &y); 17091 cpl_image_delete(brights); 17092 spectrum = cpl_image_extract(spectra, 1, y, nx, y); 17093 } 17094 17095 17096 /* 17097 * Convert standard star spectrum in electrons per second per Angstrom. 17098 */ 17099 17100 cpl_image_multiply_scalar(spectrum, gain / exptime / dispersion); 17101 17102 17103 /* 17104 * Map the atmospheric extinction factors to the same lambda sampling 17105 * of the extracted spectrum. 17106 */ 17107 17108 extinction = cpl_image_duplicate(spectrum); 17109 map_table(extinction, startwave + dispersion/2, dispersion, 17110 ext_table, "WAVE", "EXTINCTION"); 17111 17112 17113 /* 17114 * Convert from magnitudes to actual flux loss. 17115 */ 17116 17117 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17118 cpl_image_exponential(extinction, 10.); 17119 17120 17121 /* 17122 * Correct the scientific spectrum to airmass 0 17123 */ 17124 17125 cpl_image_multiply(spectrum, extinction); 17126 17127 17128 /* 17129 * Find in what pixel interval (start at "ext_pos", for "ext_count" 17130 * pixels) the atmospheric extinction is available. 17131 */ 17132 17133 ext_data = cpl_image_get_data_float(extinction); 17134 17135 ext_count = 0; 17136 ext_pos = 0; 17137 for (i = 0; i < nx; i++) { 17138 if (ext_data[i] > 0.0) { 17139 if (ext_count == 0) { 17140 ext_pos = i; 17141 } 17142 ext_count++; 17143 } 17144 else { 17145 if (ext_count) { 17146 break; 17147 } 17148 } 17149 } 17150 17151 cpl_image_delete(extinction); extinction = NULL; 17152 17153 17154 /* 17155 * Map the standard star catalog flux to the same lambda sampling 17156 * of the extracted spectrum. 17157 */ 17158 17159 flux = cpl_image_duplicate(spectrum); 17160 map_table(flux, startwave + dispersion/2, dispersion, 17161 flux_table, "WAVE", "FLUX"); 17162 17163 17164 /* 17165 * Find in what pixel interval (start at "flux_pos", for "flux_count" 17166 * pixels) the standard star flux is available. 17167 */ 17168 17169 flux_data = cpl_image_get_data_float(flux); 17170 17171 flux_count = 0; 17172 flux_pos = 0; 17173 for (i = 0; i < nx; i++) { 17174 if (flux_data[i] > 0.0) { 17175 if (flux_count == 0) { 17176 flux_pos = i; 17177 } 17178 flux_count++; 17179 } 17180 else { 17181 if (flux_count) { 17182 break; 17183 } 17184 } 17185 } 17186 17187 17188 /* 17189 * Intersection with previous selection 17190 */ 17191 17192 start = ext_pos > flux_pos ? ext_pos : flux_pos; 17193 end = (ext_pos + ext_count) < (flux_pos + flux_count) ? 17194 (ext_pos + ext_count) : (flux_pos + flux_count); 17195 flux_pos = start; 17196 flux_count = end - start; 17197 17198 17199 /* 17200 * Convert the flux to photons (per second per Angstrom). 17201 * std_flux is in units of erg / cm^2 / s / Angstrom. This 17202 * must be multiplied by the efficient area of the telescope, 17203 * 5.18E+5 cm^2, and divided by hv (v = frequency). With 17204 * hc = 1.98E-8 erg*Angstrom one obtains the following: 17205 */ 17206 17207 physical = cpl_image_duplicate(spectrum); 17208 phys_data = cpl_image_get_data_float(physical); 17209 17210 for (i = 0; i < nx; i++) { 17211 lambda = startwave + dispersion * (i + 0.5); 17212 phys_data[i] = 0.0026 * lambda * flux_data[i]; 17213 } 17214 17215 efficiency = cpl_image_duplicate(spectrum); 17216 eff_data = cpl_image_get_data_float(efficiency); 17217 data = cpl_image_get_data_float(spectrum); 17218 17219 for (i = 0; i < nx; i++) { 17220 if (phys_data[i] > 0.0) 17221 eff_data[i] = data[i] / phys_data[i]; 17222 else 17223 eff_data[i] = 0.0; 17224 } 17225 17226 cpl_image_delete(physical); physical = NULL; 17227 17228 17229 /* 17230 * Find interval (longer than 300 pixels) where efficiency is 17231 * greater than 1% 17232 */ 17233 17234 eff_count = 0; 17235 eff_pos = 0; 17236 for (i = 0; i < nx; i++) { 17237 if (eff_data[i] > 0.01) { 17238 if (eff_count == 0) { 17239 eff_pos = i; 17240 } 17241 eff_count++; 17242 } 17243 else { 17244 if (eff_count > 300) { 17245 break; 17246 } 17247 } 17248 } 17249 17250 17251 /* 17252 * Intersection with previous selection 17253 */ 17254 17255 start = eff_pos > flux_pos ? eff_pos : flux_pos; 17256 end = (eff_pos + eff_count) < (flux_pos + flux_count) ? 17257 (eff_pos + eff_count) : (flux_pos + flux_count); 17258 eff_pos = start; 17259 eff_count = end - start; 17260 17261 if (eff_count < 1) { 17262 cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT, 17263 "No overlap between catalog and spectrum"); 17264 cleanup; 17265 return NULL; 17266 } 17267 17268 17269 /* 17270 * Extract only data to fit, i.e., where the efficiency is available. 17271 */ 17272 17273 image = cpl_image_extract(efficiency, eff_pos + 1, 1, 17274 eff_pos + eff_count, 1); 17275 17276 smo_image = polysmooth(image, order, 50); 17277 cpl_image_delete(image); 17278 17279 smo_efficiency = cpl_image_duplicate(efficiency); 17280 smo_eff_data = cpl_image_get_data_float(smo_efficiency); 17281 cpl_image_copy(smo_efficiency, smo_image, eff_pos + 1, 1); 17282 17283 cpl_image_delete(smo_image); 17284 17285 17286 /* 17287 * Compute instrument response as the ratio between the catalog 17288 * spectrum and the observed spectrum (converted in physical units). 17289 * The polynomial smoothing, however, is performed on the inverse 17290 * of this ration, for obvious reasons (i.e., no divergence at zero 17291 * efficiency). 17292 */ 17293 17294 response = cpl_image_duplicate(spectrum); 17295 res_data = cpl_image_get_data_float(response); 17296 17297 for (i = 0; i < nx; i++) { 17298 if (eff_data[i] > 0.01 && flux_data[i] > 0.0) 17299 res_data[i] = data[i] / flux_data[i]; 17300 else 17301 res_data[i] = 0.0; 17302 } 17303 17304 17305 /* 17306 * Extract only data to fit, i.e., where the response is available. 17307 */ 17308 17309 image = cpl_image_extract(response, eff_pos + 1, 1, eff_pos + eff_count, 1); 17310 17311 smo_image = polysmooth(image, order, 50); 17312 cpl_image_delete(image); 17313 17314 smo_response = cpl_image_duplicate(response); 17315 smo_res_data = cpl_image_get_data_float(smo_response); 17316 cpl_image_copy(smo_response, smo_image, eff_pos + 1, 1); 17317 17318 cpl_image_delete(smo_image); 17319 17320 for (i = 0; i < nx; i++) { 17321 if (eff_data[i] > 0.01) { 17322 res_data[i] = 1 / res_data[i]; 17323 smo_res_data[i] = 1 / smo_res_data[i]; 17324 } 17325 else { 17326 res_data[i] = 0.0; 17327 smo_res_data[i] = 0.0; 17328 } 17329 } 17330 17331 17332 /* 17333 * Assemble the product spectrophotometric table. 17334 */ 17335 17336 table = cpl_table_new(nx); 17337 17338 cpl_table_new_column(table, "WAVE", CPL_TYPE_FLOAT); 17339 cpl_table_set_column_unit(table, "WAVE", "Angstrom"); 17340 17341 for (i = 0; i < nx; i++) 17342 cpl_table_set_float(table, "WAVE", i, startwave + dispersion*(i+0.5)); 17343 17344 cpl_table_new_column(table, "STD_FLUX", CPL_TYPE_FLOAT); 17345 cpl_table_set_column_unit(table, "STD_FLUX", 17346 "10^(-16) erg/(cm^2 s Angstrom)"); 17347 cpl_table_copy_data_float(table, "STD_FLUX", flux_data); 17348 cpl_image_delete(flux); flux = NULL; 17349 17350 cpl_table_new_column(table, "OBS_FLUX", CPL_TYPE_FLOAT); 17351 cpl_table_set_column_unit(table, "OBS_FLUX", "electron/(s Angstrom)"); 17352 cpl_table_copy_data_float(table, "OBS_FLUX", data); 17353 cpl_image_delete(spectrum); spectrum = NULL; 17354 17355 cpl_table_new_column(table, "RAW_EFFICIENCY", CPL_TYPE_FLOAT); 17356 cpl_table_set_column_unit(table, "RAW_EFFICIENCY", "electron/photon"); 17357 cpl_table_copy_data_float(table, "RAW_EFFICIENCY", eff_data); 17358 cpl_image_delete(efficiency); efficiency = NULL; 17359 17360 cpl_table_new_column(table, "EFFICIENCY", CPL_TYPE_FLOAT); 17361 cpl_table_set_column_unit(table, "EFFICIENCY", "electron/photon"); 17362 cpl_table_copy_data_float(table, "EFFICIENCY", smo_eff_data); 17363 cpl_image_delete(smo_efficiency); smo_efficiency = NULL; 17364 17365 cpl_table_new_column(table, "RAW_RESPONSE", CPL_TYPE_FLOAT); 17366 cpl_table_set_column_unit(table, "RAW_RESPONSE", 17367 "10^(-16) erg/(cm^2 electron)"); 17368 cpl_table_copy_data_float(table, "RAW_RESPONSE", res_data); 17369 cpl_image_delete(response); response = NULL; 17370 17371 cpl_table_new_column(table, "RESPONSE", CPL_TYPE_FLOAT); 17372 cpl_table_set_column_unit(table, 17373 "RESPONSE", "10^(-16) erg/(cm^2 electron)"); 17374 cpl_table_copy_data_float(table, "RESPONSE", smo_res_data); 17375 cpl_image_delete(smo_response); smo_response = NULL; 17376 17377 cleanup; 17378 17379 return table; 17380 } 17381 17382 static double ksigma_vector(cpl_vector *values, 17383 double klow, double khigh, int kiter, int *good) 17384 { 17385 cpl_vector *accepted; 17386 double mean = 0.0; 17387 double sigma = 0.0; 17388 double *data = cpl_vector_get_data(values); 17389 int n = cpl_vector_get_size(values); 17390 int ngood = n; 17391 int count = 0; 17392 int i; 17393 17394 17395 /* 17396 * At first iteration the mean is taken as the median, and the 17397 * standard deviation relative to this value is computed. 17398 */ 17399 17400 mean = cpl_vector_get_median(values); 17401 17402 for (i = 0; i < n; i++) 17403 sigma += (mean - data[i]) * (mean - data[i]); 17404 17405 sigma = sqrt(sigma / (n - 1)); 17406 17407 while (kiter) { 17408 count = 0; 17409 for (i = 0; i < ngood; i++) { 17410 if (data[i]-mean < khigh*sigma && mean-data[i] < klow*sigma) { 17411 data[count] = data[i]; 17412 ++count; 17413 } 17414 } 17415 17416 if (count == 0) // This cannot happen at first iteration. 17417 break; // So we can break: we have already computed a mean. 17418 17419 /* 17420 * The mean must be computed even if no element was rejected 17421 * (count == ngood), because at first iteration median instead 17422 * of mean was computed. 17423 */ 17424 17425 accepted = cpl_vector_wrap(count, data); 17426 mean = cpl_vector_get_mean(accepted); 17427 if (count > 1) 17428 sigma = cpl_vector_get_stdev(accepted); 17429 cpl_vector_unwrap(accepted); 17430 17431 if (count == ngood || count == 1) 17432 break; 17433 17434 ngood = count; 17435 --kiter; 17436 } 17437 17438 if (good) 17439 *good = ngood; 17440 17441 return mean; 17442 } 17443 17444 17463 cpl_image *mos_ksigma_stack(cpl_imagelist *imlist, 17464 double klow, double khigh, int kiter, 17465 cpl_image **good) 17466 { 17467 int ni, nx, ny, npix; 17468 cpl_image *out_ima; 17469 float *pout_ima; 17470 float *good_ima; 17471 cpl_image *image; 17472 float **data; 17473 cpl_vector *time_line; 17474 double *ptime_line; 17475 int ngood; 17476 int i, j; 17477 17478 17479 ni = cpl_imagelist_get_size(imlist); 17480 17481 image = cpl_imagelist_get(imlist, 0); 17482 nx = cpl_image_get_size_x(image); 17483 ny = cpl_image_get_size_y(image); 17484 npix = nx * ny; 17485 17486 out_ima = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 17487 pout_ima = cpl_image_get_data_float(out_ima); 17488 17489 if (good) { 17490 *good = cpl_image_new(nx, ny, CPL_TYPE_FLOAT); 17491 good_ima = cpl_image_get_data_float(*good); 17492 } 17493 17494 time_line = cpl_vector_new(ni); 17495 ptime_line = cpl_vector_get_data(time_line); 17496 17497 data = cpl_calloc(sizeof(float *), ni); 17498 17499 for (i = 0; i < ni; i++) { 17500 image = cpl_imagelist_get(imlist, i); 17501 data[i] = cpl_image_get_data_float(image); 17502 } 17503 17504 for (i = 0; i < npix; i++) { 17505 for (j = 0; j < ni; j++) { 17506 ptime_line[j] = data[j][i]; 17507 } 17508 pout_ima[i] = ksigma_vector(time_line, klow, khigh, kiter, &ngood); 17509 if (good) { 17510 good_ima[i] = ngood; 17511 } 17512 } 17513 17514 cpl_free(data); 17515 cpl_vector_delete(time_line); 17516 17517 return out_ima; 17518 17519 } 17520 17521 17538 cpl_image *mos_apply_photometry(cpl_image *spectra, cpl_table *response, 17539 cpl_table *ext_table, double startwave, 17540 double dispersion, double gain, 17541 double exptime, double airmass) 17542 { 17543 cpl_image *extinction; 17544 cpl_image *outspectra; 17545 cpl_image *mapresponse; 17546 float *res_data; 17547 float *out_data; 17548 float *ext_data; 17549 int tlength, xlength, ylength; 17550 int i, j, k; 17551 17552 17553 if (spectra == NULL || ext_table == NULL || response == NULL) { 17554 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17555 return NULL; 17556 } 17557 17558 res_data = cpl_table_get_data_float(response, "RESPONSE"); 17559 17560 if (res_data == NULL) { 17561 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17562 return NULL; 17563 } 17564 17565 tlength = cpl_table_get_nrow(response); 17566 xlength = cpl_image_get_size_x(spectra); 17567 ylength = cpl_image_get_size_y(spectra); 17568 17569 if (xlength != tlength) { 17570 mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17571 map_table(mapresponse, startwave + dispersion/2, dispersion, 17572 response, "WAVE", "RESPONSE"); 17573 res_data = cpl_image_get_data_float(mapresponse); 17574 } 17575 17576 /* 17577 * Map the atmospheric extinction factors to the same lambda sampling 17578 * of the extracted spectrum. 17579 */ 17580 17581 extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17582 map_table(extinction, startwave + dispersion/2, dispersion, 17583 ext_table, "WAVE", "EXTINCTION"); 17584 17585 17586 /* 17587 * Convert from magnitudes to actual flux loss. 17588 */ 17589 17590 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17591 cpl_image_exponential(extinction, 10.); 17592 17593 outspectra = cpl_image_duplicate(spectra); 17594 17595 ext_data = cpl_image_get_data_float(extinction); 17596 out_data = cpl_image_get_data_float(outspectra); 17597 17598 for (k = 0, i = 0; i < ylength; i++) { 17599 for (j = 0; j < xlength; j++, k++) { 17600 out_data[k] *= ext_data[j] * res_data[j]; 17601 } 17602 } 17603 17604 cpl_image_delete(extinction); 17605 if (xlength != tlength) { 17606 cpl_image_delete(mapresponse); 17607 } 17608 17609 cpl_image_multiply_scalar(outspectra, gain / exptime / dispersion); 17610 17611 return outspectra; 17612 } 17613 17614 17631 cpl_image *mos_propagate_photometry_error(cpl_image *spectra, 17632 cpl_image *errors, 17633 cpl_table *response, 17634 cpl_table *ext_table, 17635 double startwave, 17636 double dispersion, double gain, 17637 double exptime, double airmass) 17638 { 17639 cpl_image *extinction; 17640 cpl_image *outerrors; 17641 cpl_image *mapresponse; 17642 cpl_image *maperror; 17643 float *err_data; 17644 float *out_data; 17645 float *ext_data; 17646 float *res_data; 17647 float *spe_data; 17648 int tlength, xlength, ylength; 17649 int i, j, k; 17650 17651 17652 if (errors == NULL || ext_table == NULL || response == NULL) { 17653 cpl_error_set(cpl_func, CPL_ERROR_NULL_INPUT); 17654 return NULL; 17655 } 17656 17657 if (!cpl_table_has_column(response, "ERROR")) { 17658 return mos_apply_photometry(errors, response, ext_table, startwave, 17659 dispersion, gain, exptime, airmass); 17660 } 17661 17662 res_data = cpl_table_get_data_float(response, "RESPONSE"); 17663 17664 if (res_data == NULL) { 17665 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17666 return NULL; 17667 } 17668 17669 err_data = cpl_table_get_data_float(response, "ERROR"); 17670 17671 if (err_data == NULL) { 17672 cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND); 17673 return NULL; 17674 } 17675 17676 tlength = cpl_table_get_nrow(response); 17677 xlength = cpl_image_get_size_x(errors); 17678 ylength = cpl_image_get_size_y(errors); 17679 17680 if (xlength != tlength) { 17681 mapresponse = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17682 map_table(mapresponse, startwave + dispersion/2, dispersion, 17683 response, "WAVE", "RESPONSE"); 17684 res_data = cpl_image_get_data_float(mapresponse); 17685 17686 maperror = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17687 map_table(maperror, startwave + dispersion/2, dispersion, 17688 response, "WAVE", "ERROR"); 17689 err_data = cpl_image_get_data_float(maperror); 17690 } 17691 17692 /* 17693 * Map the atmospheric extinction factors to the same lambda sampling 17694 * of the extracted spectrum. 17695 */ 17696 17697 extinction = cpl_image_new(xlength, 1, CPL_TYPE_FLOAT); 17698 map_table(extinction, startwave + dispersion/2, dispersion, 17699 ext_table, "WAVE", "EXTINCTION"); 17700 17701 17702 /* 17703 * Convert from magnitudes to actual flux loss. 17704 */ 17705 17706 cpl_image_multiply_scalar(extinction, 0.4 * airmass); 17707 cpl_image_exponential(extinction, 10.); 17708 17709 outerrors = cpl_image_duplicate(errors); 17710 17711 ext_data = cpl_image_get_data_float(extinction); 17712 out_data = cpl_image_get_data_float(outerrors); 17713 spe_data = cpl_image_get_data_float(spectra); 17714 17715 for (k = 0, i = 0; i < ylength; i++) { 17716 for (j = 0; j < xlength; j++, k++) { 17717 out_data[k] = ext_data[j] * 17718 sqrt(err_data[j] * err_data[j] * spe_data[k] * spe_data[k] + 17719 res_data[j] * res_data[j] * out_data[k] * out_data[k]); 17720 } 17721 } 17722 17723 cpl_image_delete(extinction); 17724 if (xlength != tlength) { 17725 cpl_image_delete(maperror); 17726 } 17727 17728 cpl_image_multiply_scalar(outerrors, gain / exptime / dispersion); 17729 17730 return outerrors; 17731 } 17732 17733 17809 int mos_check_polarisation(cpl_image *q_image, cpl_image *q_error, 17810 cpl_image *u_image, cpl_image *u_error, 17811 double startwave, double dispersion, 17812 double band, cpl_table *pol_sta, 17813 double ra, double dec, char *filter, 17814 int *polarisation, 17815 double *p_offset, double *p_error, 17816 double *a_offset, double *a_error) 17817 { 17818 cpl_table *standard; 17819 cpl_image *q_noise; 17820 cpl_image *q_signal; 17821 cpl_image *u_noise; 17822 cpl_image *u_signal; 17823 cpl_image *noise; 17824 double *q_ndata; 17825 double *q_sdata; 17826 double *u_ndata; 17827 double *u_sdata; 17828 double arctol = 0.5; /* Arc tolerance in degrees */ 17829 double mindist; 17830 double cwave; 17831 double bwave[] = {3650., 4450., 5510., 6580., 8060}; 17832 char *bands = "UBVRI"; 17833 char p_label[] = {' ', 'p', '\0'}; 17834 char dp_label[] = {' ', 'd', 'p', '\0'}; 17835 char a_label[] = {' ', 'a', '\0'}; 17836 char da_label[] = {' ', 'd', 'a', '\0'}; 17837 int nbands = strlen(bands); 17838 int selected; 17839 int first, last, count, center; 17840 int nx; 17841 cpl_size col, row; 17842 int i, found, closest; 17843 int pband; 17844 int polarised; 17845 double q_obs; 17846 double q_err; 17847 double u_obs; 17848 double u_err; 17849 double p_obs; 17850 double p_err; 17851 double p_ref; 17852 double dp_ref; 17853 double a_obs; 17854 double a_err; 17855 double a_ref; 17856 double da_ref; 17857 17858 17859 *filter = '\0'; 17860 *polarisation = 0; 17861 *p_offset = 0.0; 17862 *p_error = 0.0; 17863 *a_offset = 0.0; 17864 *a_error = 0.0; 17865 17866 /* 17867 * Select reference standard star 17868 */ 17869 17870 cpl_table_select_all(pol_sta); 17871 cpl_table_and_selected_double(pol_sta, "RA", CPL_GREATER_THAN, ra-arctol); 17872 cpl_table_and_selected_double(pol_sta, "RA", CPL_LESS_THAN, ra+arctol); 17873 cpl_table_and_selected_double(pol_sta, "DEC", CPL_GREATER_THAN, dec-arctol); 17874 selected = 17875 cpl_table_and_selected_double(pol_sta, "DEC", CPL_LESS_THAN, dec+arctol); 17876 17877 if (selected == 0) { 17878 cpl_msg_warning(cpl_func, "No standard star found in FOV"); 17879 return 1; 17880 } 17881 17882 if (selected > 1) { 17883 cpl_msg_warning(cpl_func, 17884 "Ambiguity: %d standard stars found in FOV", selected); 17885 return 1; 17886 } 17887 17888 standard = cpl_table_extract_selected(pol_sta); 17889 17890 cpl_msg_info(cpl_func, "Standard star: %s", 17891 cpl_table_get_string(standard, "name", 0)); 17892 17893 /* 17894 * Check whether the star is polarised or not 17895 */ 17896 17897 polarised = cpl_table_get_int(standard, "polarised", 0, NULL); 17898 17899 cpl_msg_info(cpl_func, "This star is%sexpected to be polarised", 17900 polarised ? " " : " not "); 17901 17902 17903 /* 17904 * Determine the image row with the smallest median noise: this 17905 * row is assumed to refer to the standard star. 17906 * (note: the higher the S/N ratio of the original spectra, the 17907 * smaller the noise of the Stokes parameters Q and U). 17908 */ 17909 17910 nx = cpl_image_get_size_x(q_error); 17911 17912 noise = cpl_image_collapse_median_create(q_error, 1, 0, 0); 17913 cpl_image_get_minpos(noise, &col, &row); 17914 17915 cpl_image_delete(noise); 17916 17917 if (col != 1) { 17918 cpl_table_delete(standard); 17919 cpl_msg_error(cpl_func, 17920 "Assertion failure!!! col = %"CPL_SIZE_FORMAT" (it should be 1)", col); 17921 return 1; 17922 } 17923 17924 q_signal = cpl_image_extract(q_image, 1, row, nx, row); 17925 q_noise = cpl_image_extract(q_error, 1, row, nx, row); 17926 u_signal = cpl_image_extract(u_image, 1, row, nx, row); 17927 u_noise = cpl_image_extract(u_error, 1, row, nx, row); 17928 17929 q_sdata = cpl_image_get_data_double(q_signal); 17930 q_ndata = cpl_image_get_data_double(q_noise); 17931 u_sdata = cpl_image_get_data_double(u_signal); 17932 u_ndata = cpl_image_get_data_double(u_noise); 17933 17934 17935 /* 17936 * Determine valid interval in input images (where error is positive). 17937 */ 17938 17939 first = -1; 17940 last = nx = cpl_image_get_size_x(q_signal); 17941 for (i = 0; i < nx; i++) { 17942 if (first < 0) { 17943 if (q_ndata[i] > 0.0) { 17944 first = i; 17945 } 17946 } 17947 else { 17948 if (q_ndata[i] <= 0.0) { 17949 last = i - 1; 17950 break; 17951 } 17952 } 17953 } 17954 17955 count = last - first + 1; 17956 17957 if (first < 0 || count < band) { 17958 cpl_table_delete(standard); 17959 cpl_image_delete(q_signal); 17960 cpl_image_delete(q_noise); 17961 cpl_image_delete(u_signal); 17962 cpl_image_delete(u_noise); 17963 cpl_msg_warning(cpl_func, "Too short spectrum (%d pixels)", count); 17964 return 1; 17965 } 17966 17967 center = (first + last) / 2; // Center of valid spectrum 17968 cwave = startwave + dispersion * center; // Corresponding wavelength 17969 17970 17971 /* 17972 * Find the band UBVRI closest to the central wavelength. 17973 */ 17974 17975 found = 0; 17976 for (i = 0; i < nbands; i++) { 17977 p_label[0] = bands[i]; 17978 if (cpl_table_is_valid(standard, p_label, 0)) { 17979 if (found == 0) { 17980 found = 1; 17981 mindist = fabs(bwave[i] - cwave); 17982 closest = i; 17983 } 17984 else if (mindist > fabs(bwave[i] - cwave)) { 17985 mindist = fabs(bwave[i] - cwave); 17986 closest = i; 17987 } 17988 } 17989 } 17990 17991 if (!found) { 17992 cpl_table_delete(standard); 17993 cpl_image_delete(q_signal); 17994 cpl_image_delete(q_noise); 17995 cpl_image_delete(u_signal); 17996 cpl_image_delete(u_noise); 17997 cpl_msg_warning(cpl_func, "No reference value available"); 17998 return 1; 17999 } 18000 18001 center = (bwave[closest] - startwave) / dispersion; // Center of band (pix) 18002 cwave = bwave[closest]; // Wavelength of band 18003 18004 18005 /* 18006 * Check that the integration interval is entirely contained 18007 * in the valid interval, or give it up. 18008 */ 18009 18010 pband = floor(band / dispersion); // Band width in pixels 18011 18012 if (center - pband/2 < first || center + pband/2 > last) { 18013 cpl_table_delete(standard); 18014 cpl_image_delete(q_signal); 18015 cpl_image_delete(q_noise); 18016 cpl_image_delete(u_signal); 18017 cpl_image_delete(u_noise); 18018 cpl_msg_warning(cpl_func, "No reference value available"); 18019 return 1; 18020 } 18021 18022 first = center - pband/2; 18023 last = center + pband/2; 18024 18025 /* 18026 * Collect reference values. Note that if angle info is not available, 18027 * angle stuff is set automaticaly to zero. 18028 */ 18029 18030 p_label[0] = bands[closest]; 18031 dp_label[0] = bands[closest]; 18032 a_label[0] = bands[closest]; 18033 da_label[0] = bands[closest]; 18034 18035 p_ref = cpl_table_get(standard, p_label, 0, NULL); 18036 dp_ref = cpl_table_get(standard, dp_label, 0, NULL); 18037 a_ref = cpl_table_get(standard, a_label, 0, NULL); 18038 da_ref = cpl_table_get(standard, da_label, 0, NULL); 18039 18040 cpl_msg_info(cpl_func, 18041 "The expected polarisation is %.2f +- %.2f %%", 18042 p_ref, dp_ref); 18043 18044 if (polarised) { 18045 cpl_msg_info(cpl_func, 18046 "The expected polarisation angle is %.2f +- %.2f degrees", 18047 a_ref, da_ref); 18048 } 18049 18050 /* 18051 * Find median signal and median error. 18052 */ 18053 18054 q_obs = cpl_image_get_median_window(q_image, first, 1, last, 1); 18055 q_err = cpl_image_get_median_window(q_error, first, 1, last, 1); 18056 u_obs = cpl_image_get_median_window(u_image, first, 1, last, 1); 18057 u_err = cpl_image_get_median_window(u_error, first, 1, last, 1); 18058 18059 /* 18060 * Measured linear polarisation and its error 18061 */ 18062 18063 p_obs = sqrt(q_obs * q_obs + u_obs * u_obs); 18064 p_err = CPL_MATH_SQRT1_2 * 0.5 * (q_err + u_err); 18065 18066 /* 18067 * Measured polarisation angle 18068 */ 18069 18070 a_obs = 0.0; 18071 if (polarised) { 18072 if (fabs(q_obs) < 0.00001) { 18073 if (u_obs > 0.0) { 18074 a_obs = 45.0; 18075 } 18076 else { 18077 a_obs = 135.0; 18078 } 18079 } 18080 else { 18081 a_obs = 0.5 * atan(u_obs / q_obs) * 180 / CPL_MATH_PI; 18082 if (q_obs > 0.0) { 18083 if (u_obs < 0.0) { 18084 a_obs += 180.; 18085 } 18086 } 18087 else { 18088 a_obs += 90.; 18089 } 18090 } 18091 } 18092 18093 /* 18094 * Error on polarisation angle 18095 */ 18096 18097 a_err = 0.0; 18098 if (polarised) { 18099 a_err = sqrt(q_obs*q_obs*u_err*u_err + u_obs*u_obs*q_err*q_err) 18100 / (p_obs * p_obs) 18101 * 90 / CPL_MATH_PI; 18102 } 18103 18104 p_obs *= 100; 18105 p_err *= 100; 18106 cpl_msg_info(cpl_func, 18107 "The measured polarisation is %.2f +- %.2f %%", 18108 p_obs, p_err); 18109 18110 if (polarised) { 18111 cpl_msg_info(cpl_func, 18112 "The measured polarisation angle is %.2f +- %.2f degrees", 18113 a_obs, a_err); 18114 } 18115 18116 *filter = bands[closest]; 18117 *polarisation = polarised; 18118 18119 if (polarised) { 18120 *p_offset = (p_obs - p_ref) / p_ref; 18121 *p_error = sqrt(p_err * p_err + dp_ref * dp_ref) / p_ref; 18122 } 18123 else { 18124 *p_offset = p_obs - p_ref; 18125 *p_error = sqrt(p_err * p_err + dp_ref * dp_ref); 18126 } 18127 18128 *a_offset = a_obs - a_ref; 18129 *a_error = sqrt(a_err*a_err + da_ref*da_ref); 18130 18131 return 0; 18132 18133 } 18134 18135 18167 int mos_compute_offset(cpl_table *reference, cpl_table *objects, double *offset) 18168 { 18169 cpl_array *offsets; 18170 int noffset; 18171 int nslits = cpl_table_get_nrow(reference); 18172 int *nref; 18173 int *nobj; 18174 int corr, maxcorr; 18175 int shift, best_shift; 18176 int i, j, k; 18177 18178 cpl_error_code status = CPL_ERROR_NONE; 18179 18180 18181 *offset = 0.0; 18182 18183 if (nslits != cpl_table_get_nrow(objects)) 18184 return CPL_ERROR_INCOMPATIBLE_INPUT; 18185 18186 nref = fors_get_nobjs_perslit(reference); 18187 nobj = fors_get_nobjs_perslit(objects); 18188 18189 noffset = 0; 18190 for (i = 0; i < nslits; i++) 18191 noffset += nobj[i]; 18192 18193 if (noffset == 0) { 18194 cpl_free(nref); 18195 cpl_free(nobj); 18196 return CPL_ERROR_DATA_NOT_FOUND; 18197 } 18198 18199 noffset = 0; 18200 for (i = 0; i < nslits; i++) 18201 noffset += nref[i]; 18202 18203 if (noffset == 0) { 18204 cpl_free(nref); 18205 cpl_free(nobj); 18206 return CPL_ERROR_DATA_NOT_FOUND; 18207 } 18208 18209 offsets = cpl_array_new(noffset, CPL_TYPE_DOUBLE); 18210 18211 noffset = 0; // The real number of offsets found will be counted. 18212 18213 for (i = 0; i < nslits; i++) { 18214 if (nref[i] > 0 && nobj[i] > 0) { 18215 double shift; 18216 int length = cpl_table_get_int(objects, "length", i, NULL); 18217 double ytop = cpl_table_get_double(objects, "xtop", i, NULL); 18218 double ybottom = cpl_table_get_double(objects, "xbottom", i, NULL); 18219 int *aref = cpl_calloc(length, sizeof(int)); 18220 int *aobj = cpl_calloc(length, sizeof(int)); 18221 float *pref = cpl_calloc(nref[i], sizeof(float)); 18222 float *pobj = cpl_calloc(nobj[i], sizeof(float)); 18223 18224 for (j = 0; j < nref[i]; j++) { 18225 pref[j] = fors_get_object_position(reference, i, j + 1); 18226 aref[(int)pref[j]] = 1; 18227 } 18228 18229 for (j = 0; j < nobj[i]; j++) { 18230 pobj[j] = fors_get_object_position(objects, i, j + 1); 18231 aobj[(int)pobj[j]] = 1; 18232 } 18233 18234 /* 18235 * Do not consider objects at border 18236 */ 18237 18238 aref[0] = 0; 18239 aref[length - 1] = 0; 18240 aobj[0] = 0; 18241 aobj[length - 1] = 0; 18242 18243 //for (j = 0; j < nref[i]; j++) 18244 //printf("references: %f, ", pref[j]); 18245 //printf("\n"); 18246 //for (j = 0; j < nref[i]; j++) 18247 //printf("objects : %f, ", pobj[j]); 18248 //printf("\n"); 18249 //for (j = 0; j < length; j++) 18250 //printf("%d", aref[j]); 18251 //printf("\n"); 18252 //for (j = 0; j < length; j++) 18253 //printf("%d", aobj[j]); 18254 //printf("\n"); 18255 18256 /* 18257 * Object matching by correlation 18258 */ 18259 18260 maxcorr = 0; 18261 best_shift = length; 18262 18263 for (shift = length/2, j = 0; j <= length; shift--, j++) { 18264 int rstart, ostart, count; 18265 18266 if (shift > 0) { 18267 rstart = shift; 18268 ostart = 0; 18269 count = length - shift; 18270 } 18271 else { 18272 rstart = 0; 18273 ostart = -shift; 18274 count = length + shift; 18275 } 18276 18277 corr = 0; 18278 for (k = 0; k < count; k++) { 18279 corr += aref[rstart + k] * aobj[ostart + k]; 18280 } 18281 18282 if (maxcorr < corr) { 18283 maxcorr = corr; 18284 best_shift = shift; 18285 } 18286 } 18287 18288 if (best_shift == length) { // No shift found 18289 //printf("%d: No shift found\n", i); 18290 cpl_free(aref); 18291 cpl_free(aobj); 18292 cpl_free(pref); 18293 cpl_free(pobj); 18294 continue; 18295 } 18296 //printf("%d: Integer shift found = %d\n", i, best_shift); 18297 18298 for (j = 0; j < nref[i]; j++) { 18299 for (k = 0; k < nobj[i]; k++) { 18300 if (fabs(pref[j] - pobj[k] - best_shift) < 2) { 18301 double ccd_offset = (pref[j] - pobj[k]) 18302 * (ytop - ybottom) 18303 / length; 18304 18305 //printf("%d: Match found: %f\n", i, ccd_offset); 18306 /* 18307 * The matching object is found, store the 18308 * corresponding offset 18309 */ 18310 18311 cpl_array_set(offsets, noffset, ccd_offset); 18312 noffset++; 18313 break; 18314 } 18315 } 18316 } 18317 18318 cpl_free(aref); 18319 cpl_free(aobj); 18320 cpl_free(pref); 18321 cpl_free(pobj); 18322 } 18323 //else 18324 //printf("%d: No object found\n", i); 18325 } 18326 18327 cpl_free(nref); 18328 cpl_free(nobj); 18329 18330 //printf("%d offsets found in total\n", noffset); 18331 if (noffset > 0) { 18332 if (noffset % 2) { 18333 *offset = cpl_array_get_median(offsets); 18334 } 18335 else { 18336 double *a = cpl_malloc(sizeof(double) * noffset); 18337 for (i = 0; i < noffset; i++) { 18338 a[i] = cpl_array_get_double(offsets, i, NULL); 18339 } 18340 *offset = (fors_tools_get_kth_double(a, noffset, (noffset-1)/2) + 18341 fors_tools_get_kth_double(a, noffset, (noffset/2))) / 2.0; 18342 cpl_free(a); 18343 } 18344 } 18345 else 18346 status = CPL_ERROR_DATA_NOT_FOUND; 18347 //printf("Median offset: %f\n", *offset); 18348 18349 cpl_array_delete(offsets); 18350 18351 return status; 18352 18353 } 18354 18355 18367 cpl_error_code mos_image_shift(cpl_image *image, double dx, double dy) 18368 { 18369 cpl_image *source; 18370 int nx = cpl_image_get_size_x(image); 18371 int ny = cpl_image_get_size_y(image); 18372 float *idata; 18373 float *sdata; 18374 int i, j, pos; 18375 double xpos, ypos, xfrac, yfrac; 18376 int xint, yint; 18377 18378 18379 if (fabs(dx) >= nx || fabs(dy) >= ny) 18380 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18381 18382 source = cpl_image_duplicate(image); 18383 idata = cpl_image_get_data_float(image); 18384 sdata = cpl_image_get_data_float(source); 18385 18386 /* 18387 * Shift in y 18388 */ 18389 18390 yfrac = - dy - floor(- dy); 18391 xfrac = - dx - floor(- dx); 18392 18393 for (pos = 0, j = 0; j < ny; j++) { 18394 ypos = j - dy; 18395 yint = floor(ypos); 18396 for (i = 0; i < nx; i++) { 18397 xpos = i - dx; 18398 xint = floor(xpos); 18399 if (xint < 0 || yint < 0 || xint > nx - 2 || yint > ny - 2) { 18400 idata[pos] = 0.0; 18401 } 18402 else { 18403 idata[pos] = sdata[xint + nx*yint] * (1 - xfrac) * (1 - yfrac) 18404 + sdata[xint + 1 + nx*yint] * xfrac * (1 - yfrac) 18405 + sdata[xint + nx*(yint + 1)] * (1 - xfrac) * yfrac 18406 + sdata[xint + 1 + nx*(yint + 1)] * xfrac * yfrac; 18407 } 18408 pos++; 18409 } 18410 } 18411 18412 cpl_image_delete(source); 18413 18414 return CPL_ERROR_NONE; 18415 } 18416 18428 int mos_slit_closest_to_center(cpl_table *slits, int nx, int ny) 18429 { 18430 #ifdef CPL_SIZE_FORMAT 18431 cpl_size row; 18432 #else 18433 int row; 18434 #endif 18435 18436 cpl_table_duplicate_column(slits, "x", slits, "xtop"); 18437 cpl_table_add_columns(slits, "x", "xbottom"); 18438 cpl_table_divide_scalar(slits, "x", 2); // Mean x position 18439 cpl_table_subtract_scalar(slits, "x", nx/2); // Relative to CCD center 18440 cpl_table_multiply_columns(slits, "x", "x"); // Squared 18441 18442 cpl_table_duplicate_column(slits, "y", slits, "ytop"); 18443 cpl_table_add_columns(slits, "y", "ybottom"); 18444 cpl_table_divide_scalar(slits, "y", 2); // Mean y position 18445 cpl_table_subtract_scalar(slits, "y", ny/2); // Relative to CCD center 18446 cpl_table_multiply_columns(slits, "y", "y"); // Squared 18447 18448 cpl_table_add_columns(slits, "x", "y"); // Distance from center 18449 cpl_table_get_column_minpos(slits, "x", &row); // Min distance from center 18450 18451 cpl_table_erase_column(slits, "x"); 18452 cpl_table_erase_column(slits, "y"); 18453 18454 return row; 18455 } 18456 18476 cpl_error_code mos_extract_flux(cpl_image *image, cpl_table *slits, 18477 double xwidth, double ywidth, 18478 int dx, double gain, double *o_flux, double *o_err) 18479 { 18480 int nx = cpl_image_get_size_x(image); 18481 int ny = cpl_image_get_size_y(image); 18482 int slit = mos_slit_closest_to_center(slits, nx, ny); 18483 int ytop = (int)cpl_table_get(slits, "ytop", slit, NULL); 18484 int ybottom = (int)cpl_table_get(slits, "ybottom", slit, NULL); 18485 int dy = ytop - ybottom; 18486 int xcenter = (int)((cpl_table_get(slits, "xtop", slit, NULL) + 18487 cpl_table_get(slits, "xbottom", slit, NULL)) / 2); 18488 int xleft = xcenter - dx; 18489 int xright = xcenter + dx + 1; 18490 double area = xwidth * ywidth; // squared mm 18491 int npix = (2*dx + 1) * dy; 18492 int count = 0; 18493 float *data = cpl_image_get_data_float(image); 18494 double flux = 0.0; 18495 double error = 0.0; 18496 int satur = 60000; 18497 int x, y; 18498 18499 18500 if (cpl_table_has_column(slits, "ywidth")) { 18501 area = cpl_table_get(slits, "xwidth", slit, NULL) 18502 * cpl_table_get(slits, "ywidth", slit, NULL); 18503 } 18504 18505 *o_flux = 0.0; 18506 *o_err = 0.0; 18507 18508 if (xleft < 0) 18509 xleft = 0; 18510 18511 if (xleft > nx) 18512 xleft = nx; 18513 18514 if (xright < 0) 18515 xright = 0; 18516 18517 if (xright > nx) 18518 xright = nx; 18519 18520 if (ytop < 0) 18521 ytop = 0; 18522 18523 if (ytop > ny) 18524 ytop = ny; 18525 18526 if (ybottom < 0) 18527 ybottom = 0; 18528 18529 if (ybottom > ny) 18530 ybottom = ny; 18531 18532 count = (xright - xleft) * (ytop - ybottom); 18533 18534 if (count == 0) 18535 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18536 18537 count = 0; 18538 18539 for (y = ybottom; y < ytop; y++) { 18540 for (x = xleft; x < xright; x++) { 18541 double value = data[x + y * nx]; 18542 if (value < satur) { 18543 flux += value; 18544 count++; 18545 } 18546 } 18547 } 18548 18549 if (count == 0) 18550 return CPL_ERROR_DIVISION_BY_ZERO; 18551 18552 error = sqrt(flux/gain); 18553 18554 /* 18555 * Flux correction for lost pixels 18556 */ 18557 18558 flux *= (float)npix / count; 18559 error *= (float)npix / count; 18560 18561 flux /= area; 18562 error /= area; 18563 18564 *o_flux = flux; 18565 *o_err = error; 18566 18567 return CPL_ERROR_NONE; 18568 } 18569 18570 18593 cpl_error_code mos_extract_flux_mapped(cpl_image *image, cpl_table *slits, 18594 double xwidth, double ywidth, 18595 double lambda, double startwave, 18596 double dispersion, int dx, double gain, 18597 double *o_flux, double *o_err) 18598 { 18599 int nx = cpl_image_get_size_x(image); 18600 int ny = cpl_image_get_size_y(image); 18601 int slit = mos_slit_closest_to_center(slits, nx, ny); 18602 int dy = (int)cpl_table_get(slits, "length", slit, NULL); 18603 int ybottom = (int)cpl_table_get(slits, "position", slit, NULL); 18604 int ytop = ybottom + dy; 18605 int xcenter = (int)floor((lambda - startwave) / dispersion + 0.5); 18606 int xleft = xcenter - dx; 18607 int xright = xcenter + dx + 1; 18608 double area = xwidth * ywidth; 18609 int npix = (2*dx + 1) * dy; 18610 int count = 0; 18611 float *data = cpl_image_get_data_float(image); 18612 double flux = 0.0; 18613 double error = 0.0; 18614 int satur = 60000; 18615 int x, y; 18616 18617 18618 if (cpl_table_has_column(slits, "ywidth")) { 18619 area = cpl_table_get(slits, "xwidth", slit, NULL) 18620 * cpl_table_get(slits, "ywidth", slit, NULL); 18621 } 18622 18623 *o_flux = 0.0; 18624 *o_err = 0.0; 18625 18626 if (xleft < 0) 18627 xleft = 0; 18628 18629 if (xleft > nx) 18630 xleft = nx; 18631 18632 if (xright < 0) 18633 xright = 0; 18634 18635 if (xright > nx) 18636 xright = nx; 18637 18638 if (ytop < 0) 18639 ytop = 0; 18640 18641 if (ytop > ny) 18642 ytop = ny; 18643 18644 if (ybottom < 0) 18645 ybottom = 0; 18646 18647 if (ybottom > ny) 18648 ybottom = ny; 18649 18650 count = (xright - xleft) * (ytop - ybottom); 18651 18652 if (count == 0) 18653 return CPL_ERROR_ACCESS_OUT_OF_RANGE; 18654 18655 count = 0; 18656 18657 for (y = ybottom; y < ytop; y++) { 18658 for (x = xleft; x < xright; x++) { 18659 double value = data[x + y * nx]; 18660 if (value < satur) { 18661 flux += value; 18662 count++; 18663 } 18664 } 18665 } 18666 18667 if (count == 0) 18668 return CPL_ERROR_DIVISION_BY_ZERO; 18669 18670 error = sqrt(flux/gain); 18671 18672 /* 18673 * Flux correction for lost pixels 18674 */ 18675 18676 flux *= (float)npix / count; 18677 error *= (float)npix / count; 18678 18679 flux /= area; 18680 error /= area; 18681 18682 *o_flux = flux; 18683 *o_err = error; 18684 18685 return CPL_ERROR_NONE; 18686 18687 } 18688 18689 18703 int mos_median_in_slit(cpl_table *table, cpl_table *slits, int slit, 18704 char *label, double *mvalue) 18705 { 18706 int position = cpl_table_get_int(slits, "position", slit, NULL); 18707 int length = cpl_table_get_int(slits, "length", slit, NULL); 18708 cpl_table *tmp = cpl_table_extract(table, position, length); 18709 18710 *mvalue = cpl_table_get_column_median(tmp, label); 18711 cpl_table_delete(tmp); 18712 18713 if (cpl_error_get_code() != CPL_ERROR_NONE) 18714 return 1; 18715 18716 return 0; 18717 } 18718 18719 18731 cpl_image *mos_image_filter_median(cpl_image *image, int nx, int ny) 18732 { 18733 cpl_mask *kernel = cpl_mask_new(nx, ny); 18734 cpl_image *filtered = cpl_image_new(cpl_image_get_size_x(image), 18735 cpl_image_get_size_y(image), 18736 cpl_image_get_type(image)); 18737 18738 cpl_mask_not(kernel); 18739 cpl_image_filter_mask(filtered, image, kernel, 18740 CPL_FILTER_MEDIAN, CPL_BORDER_FILTER); 18741 cpl_mask_delete(kernel); 18742 18743 return filtered; 18744 } 18745 18746 int fors_mos_is_lss_like(cpl_table *maskslits, int nslits_out_det) 18747 { 18748 int treat_as_lss = 1; 18749 double mxpos = cpl_table_get_column_median(maskslits, "xtop"); 18750 double * slit_xpos = cpl_table_get_data_double(maskslits, "xtop"); 18751 cpl_size nslits = cpl_table_get_nrow(maskslits); 18752 18753 //If not all the slits are illuminated, then we cannot say that 18754 //it is lss-like (PIPE-4380) 18755 if(nslits_out_det != 0) 18756 return 0; 18757 18758 cpl_msg_warning(cpl_func, "Number of slits %"CPL_SIZE_FORMAT, nslits); 18759 for (cpl_size i = 0; i < nslits; i++) { 18760 if (fabs(mxpos-slit_xpos[i]) > 0.01) { 18761 treat_as_lss = 0; 18762 break; 18763 } 18764 } 18765 return treat_as_lss; 18766 }