IIINSTRUMENT Pipeline Reference Manual 1.5.16
sofi_wavelength.c
1/* $Id: sofi_wavelength.c,v 1.52 2013-03-12 08:04:54 llundin Exp $
2 *
3 * This file is part of the SOFI Pipeline
4 * Copyright (C) 2002,2003 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: llundin $
23 * $Date: 2013-03-12 08:04:54 $
24 * $Revision: 1.52 $
25 * $Name: not supported by cvs2svn $
26 */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include "sofi_wavelength.h"
37#include "sofi_pfits.h"
38#include "sofi_utils.h"
39#include "sofi_dfs.h"
40
41#include "irplib_wavecal.h"
42#include "irplib_utils.h"
43#include "irplib_wlxcorr.h"
44#include "irplib_ppm.h"
45
46#include <math.h>
47#include <string.h>
48#include <float.h>
49
50/*-----------------------------------------------------------------------------
51 Includes
52 -----------------------------------------------------------------------------*/
53
54static cpl_bivector * sofi_wavelength_load_catalog(const char *, const char *,
55 const char *, const char *);
56static cpl_vector * sofi_wavelength_extract_spectrum(const cpl_image *, double);
57static cpl_polynomial * sofi_wavelength_xc_engine(const cpl_vector *,
58 const cpl_bivector *, int, const cpl_polynomial *, double, int,
59 double, int, double *);
60
61
62/*-----------------------------------------------------------------------------
63 Defines
64 -----------------------------------------------------------------------------*/
65
66#ifndef SOFI_WFWHM
67/* FIXME: Find a better value ? */
68#define SOFI_WFWHM 4.0
69#endif
70
71#ifndef SOFI_PIXSTEP
72/* FIXME: Parameterize (and find a better value ?) */
73#define SOFI_PIXSTEP 0.5
74#endif
75
76#ifndef SOFI_PIXTOL
77/* FIXME: Parameterize (and find a better value ?) */
78#define SOFI_PIXTOL 1e-5
79#endif
80
81#ifndef SOFI_OFFSET
82/* FIXME: Parameterize (and find a better value ?) */
83#define SOFI_OFFSET 50
84#endif
85
86#ifndef SOFI_DEGREE
87/* FIXME */
88#define SOFI_DEGREE 3
89#endif
90
91#ifndef SOFI_MAXFAIL
92/* FIXME */
93#define SOFI_MAXFAIL 3
94#endif
95
96#ifndef SOFI_CONTINUE
97/* FIXME */
98#define SOFI_CONTINUE 3
99#endif
100
101#define SOFI_MAX(A,B) ((A) > (B) ? (A) : (B))
102#define SOFI_MIN(A,B) ((A) < (B) ? (A) : (B))
103
104/*----------------------------------------------------------------------------*/
108/*----------------------------------------------------------------------------*/
109
110/*-----------------------------------------------------------------------------
111 Function codes
112 -----------------------------------------------------------------------------*/
113
115
116/*----------------------------------------------------------------------------*/
149/*----------------------------------------------------------------------------*/
150cpl_polynomial * sofi_wavelength_engine(
151 const cpl_image * in,
152 const char * table_name,
153 const char * oh,
154 const char * xe,
155 const char * ne,
156 const cpl_polynomial * phdisprel,
157 double slit_width,
158 int degree,
159 double wl_err,
160 int nsamples,
161 int use_ppm,
162 int plot,
163 double * xcorr)
164{
165 cpl_polynomial * wl_poly_xc;
166 cpl_polynomial * wl_poly_ppm;
167 double xc;
168 cpl_bivector * spec_cat;
169 cpl_vector * spectrum;
170 cpl_vector * filtered;
171 cpl_vector * clean;
172 double * pclean;
173 int filt_size = 9;
174 double stdev, median, threshold;
175 int i;
176
177 /* Check inputs */
178 if (in==NULL || table_name==NULL || phdisprel == NULL) return NULL;
179 if (degree > 3 || degree < 1) return NULL;
180
181 degree = SOFI_DEGREE; /* FIXME */
182
183 /* Load the catalog */
184 if ((spec_cat=sofi_wavelength_load_catalog(table_name, oh, xe, ne))==NULL) {
185 cpl_msg_error(cpl_func, "Cannot load the catalog");
186 return NULL;
187 }
188
189 /* Get the spectrum from the image */
190 if ((spectrum = sofi_wavelength_extract_spectrum(in, slit_width)) == NULL) {
191 cpl_msg_error(cpl_func, "Cannot extract spectrum");
192 cpl_bivector_delete(spec_cat);
193 return NULL;
194 }
195
196 /* Wavelength calibration with the cross correlation */
197 cpl_msg_info(cpl_func, "Apply the wavelength calibration with XC");
198 cpl_msg_indent_more();
199 if ((wl_poly_xc = sofi_wavelength_xc_engine(spectrum, spec_cat,
200 degree, phdisprel, wl_err, nsamples, slit_width,
201 plot, &xc)) == NULL) {
202 cpl_msg_warning(cpl_func, "Cannot calibrate");
203 wl_poly_xc = cpl_polynomial_duplicate(phdisprel);
204 *xcorr = -1;
205 } else {
206 *xcorr = xc;
207 }
208 cpl_msg_indent_less();
209
210 /* Use Point pattern matching if requested */
211 if (use_ppm) {
212 /* Remove the low frequency part */
213 if ((filtered=cpl_vector_filter_median_create(spectrum,
214 filt_size))==NULL) {
215 cpl_polynomial_delete(wl_poly_xc);
216 cpl_bivector_delete(spec_cat);
217 cpl_vector_delete(spectrum);
218 cpl_msg_error(cpl_func, "Cannot filter the spectrum");
219 return NULL;
220 }
221 clean = cpl_vector_duplicate(spectrum);
222 cpl_vector_subtract(clean, filtered);
223 cpl_vector_delete(filtered);
224
225 /* Threshold the spectrum */
226 stdev = cpl_vector_get_stdev(clean);
227 median = cpl_vector_get_median_const(clean);
228 threshold = median + stdev;
229 pclean = cpl_vector_get_data(clean);
230 for (i=0; i<cpl_vector_get_size(clean); i++) {
231 if (pclean[i] < threshold) pclean[i] = 0.0;
232 }
233
234 /* Wavelength calibration with the Point-Pattern matching */
235 cpl_msg_info(cpl_func, "Apply the wavelength calibration with PPM");
236 cpl_msg_indent_more();
237 if ((wl_poly_ppm = irplib_ppm_engine(clean, spec_cat, wl_poly_xc,
238 slit_width, 2, 0.2, degree, plot, NULL)) == NULL) {
239 cpl_msg_warning(cpl_func, "Cannot calibrate");
240 cpl_msg_indent_less();
241 cpl_vector_delete(clean);
242 cpl_bivector_delete(spec_cat);
243 return wl_poly_xc;
244 }
245 cpl_polynomial_delete(wl_poly_xc);
246 wl_poly_xc = wl_poly_ppm;
247 cpl_msg_indent_less();
248 cpl_vector_delete(clean);
249 }
250 cpl_bivector_delete(spec_cat);
251 cpl_vector_delete(spectrum);
252
253 return wl_poly_xc;
254}
255
256/*----------------------------------------------------------------------------*/
262/*----------------------------------------------------------------------------*/
263double sofi_get_slitwidth(const char * filename)
264{
265 const char * sval;
266 double slit_width;
267 double pscale;
268 cpl_propertylist * plist;
269
270 /* Initialize */
271 plist=cpl_propertylist_load(filename, 0);
272
273 /* Get the slit name used */
274 if ((sval = sofi_pfits_get_opti1_id(plist)) == NULL) {
275 cpl_msg_error(cpl_func, "cannot get slit used");
276 cpl_propertylist_delete(plist);
277 return -1.0;
278 }
279
280 /* Get the slit width in arcseconds */
281 if (!strcmp(sval, "long_slit_1")) slit_width = 1;
282 else if (!strcmp(sval, "long_slit_2")) slit_width = 2;
283 else if (!strcmp(sval, "long_slit_0.6")) slit_width = 0.6;
284 else {
285 cpl_msg_error(cpl_func, "unrecognized slit");
286 cpl_propertylist_delete(plist);
287 return -1.0;
288 }
289
290 /* Get the pixelscale and convert arsec -> pixels */
291 pscale = sofi_pfits_get_pixscale(plist);
292 if (cpl_error_get_code()) {
293 cpl_msg_error(cpl_func, "cannot get pixscale");
294 cpl_propertylist_delete(plist);
295 return -1.0;
296 }
297 cpl_propertylist_delete(plist);
298
299 slit_width /= pscale;
300
301 /* Return */
302 return slit_width;
303}
304
305/*----------------------------------------------------------------------------*/
312/*----------------------------------------------------------------------------*/
313cpl_polynomial * sofi_get_disprel_estimate(const char * filename,
314 int poly_deg)
315{
316 cpl_polynomial * self;
317 cpl_propertylist * plist;
318 int npix;
319 const char * sval;
320 double wmin = FLT_MAX; /* (False) uninit warning */
321 double wmax = FLT_MAX; /* (False) uninit warning */
322 cpl_size i;
323
324 /* Test entries */
325 cpl_ensure(filename != NULL, CPL_ERROR_NULL_INPUT, NULL);
326 cpl_ensure(poly_deg >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
327
328 plist = cpl_propertylist_load_regexp(filename, 0, "ESO INS MODE$|^NAXIS2$",
329 0);
330 cpl_ensure(plist != NULL, cpl_error_get_code(), NULL);
331
332 /* Get naxis2 */
333 npix = sofi_pfits_get_naxis2(plist);
334
335 /* Get the grism */
336 sval = sofi_pfits_get_mode(plist);
337
338 if (sval == NULL) {
339 (void)cpl_error_set_message(cpl_func, cpl_error_get_code(),
340 "Could not get grism from [%s]", filename);
341 } else if (!strcmp(sval, "LONG_SLIT_BLUE")) {
342 wmin = 9500;
343 wmax = 16400;
344 } else if (!strcmp(sval, "LONG_SLIT_RED")) {
345 wmin = 15300;
346 wmax = 25200;
347 } else if (!strcmp(sval, "LONG_SLIT_Z")) {
348 wmin = 7900;
349 wmax = 9500;
350 } else if (!strcmp(sval, "LONG_SLIT_J")) {
351 wmin = 12000;
352 wmax = 14500;
353 } else if (!strcmp(sval, "LONG_SLIT_H")) {
354 wmin = 14900;
355 wmax = 18100;
356 } else if (!strcmp(sval, "LONG_SLIT_K")) {
357 wmin = 19500;
358 wmax = 24500;
359 } else {
360 (void)cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
361 "grism ('ESO INS MODE') in '%s' is "
362 "unsupported: %s", filename, sval);
363 }
364
365 cpl_propertylist_delete(plist);
366
367 if (cpl_error_get_code()) return NULL;
368
369 if (npix < 2) {
370 (void)cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
371 "In '%s' NAXIS2 must be at least 2, not %d",
372 filename, npix);
373 return NULL;
374 }
375
376 /* P(0.5) = wmin */
377 /* P(0.5 + npix) = wmax */
378 /* P(x) = a * x + b */
379 /* a = (wmax - wmin) / npix */
380 /* b = wmin - 0.5 * a = wmin - 0.5 * (wmax - wmin) / npix */
381
382 self = cpl_polynomial_new(1);
383 i = 1;
384 cpl_polynomial_set_coeff(self, &i, (wmax - wmin) / npix);
385 i = 0;
386 cpl_polynomial_set_coeff(self, &i, wmin - 0.5 * (wmax - wmin) / npix);
387
388 return self;
389}
390
392
393/*----------------------------------------------------------------------------*/
402/*----------------------------------------------------------------------------*/
403static cpl_bivector * sofi_wavelength_load_catalog(
404 const char * name,
405 const char * oh,
406 const char * xe,
407 const char * ne)
408{
409 cpl_bivector * sig;
410 double * psigx;
411 double * psigy;
412 double epsilon;
413 int nlines;
414 cpl_table * cat_tab1;
415 cpl_table * cat_tab2;
416 cpl_propertylist * reflist;
417 int i;
418
419 /* Check entries */
420 if (name == NULL) return NULL;
421
422 /* Initialise */
423 epsilon = 0.01;
424
425 /* Handle the table names and load the tables */
426 if (!strcmp(name, "oh")) {
427 /* Check if the table is there */
428 if (oh == NULL) {
429 cpl_msg_error(cpl_func, "Please provide the OH lines catalog");
430 return NULL;
431 }
432 /* Load the table */
433 if ((cat_tab1 = cpl_table_load(oh, 1, 0)) == NULL) {
434 cpl_msg_error(cpl_func, "Cannot load the catalog");
435 return NULL;
436 }
437 } else if (!strcmp(name, "Xe")) {
438 /* Check if the table is there */
439 if (xe == NULL) {
440 cpl_msg_error(cpl_func, "Please provide the XE lines catalog");
441 return NULL;
442 }
443 /* Load the table */
444 if ((cat_tab1 = cpl_table_load(xe, 1, 0)) == NULL) {
445 cpl_msg_error(cpl_func, "Cannot load the XE catalog");
446 return NULL;
447 }
448 } else if (!strcmp(name, "Ne")) {
449 /* Check if the table is there */
450 if (ne == NULL) {
451 cpl_msg_error(cpl_func, "Please provide the NE lines catalog");
452 return NULL;
453 }
454 /* Load the table */
455 if ((cat_tab1 = cpl_table_load(ne, 1, 0)) == NULL) {
456 cpl_msg_error(cpl_func, "Cannot load the NE catalog");
457 return NULL;
458 }
459 } else if (!strcmp(name, "Xe+Ne")) {
460 /* Check if the table is there */
461 if (xe == NULL) {
462 cpl_msg_error(cpl_func, "Please provide the XE lines catalog");
463 return NULL;
464 }
465 /* Load the table */
466 if ((cat_tab1 = cpl_table_load(xe, 1, 0)) == NULL) {
467 cpl_msg_error(cpl_func, "Cannot load the XE catalog");
468 return NULL;
469 }
470 /* Check if the table is there */
471 if (ne == NULL) {
472 cpl_msg_error(cpl_func, "Please provide the NE lines catalog");
473 return NULL;
474 }
475 /* Load the table */
476 if ((cat_tab2 = cpl_table_load(ne, 1, 0)) == NULL) {
477 cpl_msg_error(cpl_func, "Cannot load the NE catalog");
478 cpl_table_delete(cat_tab1);
479 return NULL;
480 }
481 /* Merge the tables */
482 if (cpl_table_insert(cat_tab1, cat_tab2,
483 cpl_table_get_nrow(cat_tab1)) != CPL_ERROR_NONE) {
484 cpl_msg_error(cpl_func, "Cannot merge tables");
485 cpl_table_delete(cat_tab1);
486 cpl_table_delete(cat_tab2);
487 return NULL;
488 }
489 cpl_table_delete(cat_tab2);
490 } else return NULL;
491
492 /* Sort the table */
493 reflist = cpl_propertylist_new();
494 cpl_propertylist_append_bool(reflist, SOFI_COL_WAVELENGTH, 0);
495 cpl_table_sort(cat_tab1, reflist);
496 cpl_propertylist_delete(reflist);
497
498 /* Create the bivector */
499#ifdef HAVE_GSL
500 nlines = cpl_table_get_nrow(cat_tab1);
501 sig = cpl_bivector_new(nlines);
502 psigx = cpl_bivector_get_x_data(sig);
503 psigy = cpl_bivector_get_y_data(sig);
504 for (i=0; i < nlines; i++) {
505 const double wl
506 = cpl_table_get_double(cat_tab1, SOFI_COL_WAVELENGTH, i, NULL);
507 const double intens
508 = cpl_table_get_double(cat_tab1, SOFI_COL_EMISSION, i, NULL);
509
510 psigx[i] = wl;
511 psigy[i] = intens;
512 }
513#else
514 nlines = 3 * cpl_table_get_nrow(cat_tab1);
515 sig = cpl_bivector_new(nlines);
516 psigx = cpl_bivector_get_x_data(sig);
517 psigy = cpl_bivector_get_y_data(sig);
518 for (i=0; i<cpl_table_get_nrow(cat_tab1); i++) {
519 const double
520 wl = cpl_table_get_double(cat_tab1, SOFI_COL_WAVELENGTH, i, NULL);
521 const double
522 intens = cpl_table_get_double(cat_tab1, SOFI_COL_EMISSION, i, NULL);
523 psigx[3*i] = wl - epsilon;
524 psigy[3*i] = 0.0;
525 psigx[3*i+1] = wl;
526 psigy[3*i+1] = intens;
527 psigx[3*i+2] = wl + epsilon;
528 psigy[3*i+2] = 0.0;
529 }
530#endif
531 cpl_table_delete(cat_tab1);
532 return sig;
533}
534
535/*----------------------------------------------------------------------------*/
549/*----------------------------------------------------------------------------*/
550static cpl_polynomial * sofi_wavelength_xc_engine(
551 const cpl_vector * spec,
552 const cpl_bivector * spec_cat,
553 int degree,
554 const cpl_polynomial * phdisprel,
555 double wl_err,
556 int nsamples,
557 double slit_width,
558 int display,
559 double * xcorr)
560{
561#ifdef HAVE_GSL
562
563 irplib_line_spectrum_model model;
564 const int spec_size = cpl_vector_get_size(spec);
565
566 const double phwl_min = cpl_polynomial_eval_1d(phdisprel, 0.5, NULL);
567 const double phwl_max = cpl_polynomial_eval_1d(phdisprel, 0.5 + spec_size,
568 NULL);
569 const int emil = irplib_bivector_count_positive(spec_cat, phwl_min,
570 phwl_max);
571
572 const double wfwhm = SOFI_WFWHM;
573 const double slitw = slit_width;
574 const double xtrunc = 0.5 * slitw + 5.0 * wfwhm * CPL_MATH_SIG_FWHM;
575 const int max_offset = SOFI_OFFSET;
576
577 /* One practical limitation on hshiftmax is that it may not be so big,
578 as to cause phdisp to be evaluated outside its range of monotony. */
579 const int hshiftmax = SOFI_MIN(spec_size/4, max_offset);
580
581 /* Initialize to zero */
582 cpl_vector * linepix
583 = cpl_vector_wrap(cpl_bivector_get_size(spec_cat),
584 cpl_calloc(cpl_bivector_get_size(spec_cat),
585 sizeof(double)));
586 cpl_polynomial * self;
587 cpl_error_code error;
588 const int fitdeg = emil > degree ? degree : emil-1;
589 const int maxite = fitdeg * 200;
590 const int linelim =
591#ifdef SOFI_USE_LINELIM
592 xtrunc*(spec_size + 2 * hshiftmax);
593#else
594 0;
595#endif
596
597 const cpl_boolean doplot /* FIXME: Function parameter */
598 = display || getenv("SOFI_DOPLOT") ? CPL_TRUE : CPL_FALSE;
599 double xc;
600
601
602 memset(&model, 0, sizeof(model));
603 model.wslit = slitw;
604 model.wfwhm = wfwhm;
605 model.xtrunc = xtrunc;
606 model.lines = spec_cat;
607 model.linepix = linepix;
608 model.cost = 0;
609 model.xcost = 0;
610
611 self = cpl_polynomial_duplicate(phdisprel);
612
613 error = irplib_polynomial_find_1d_from_correlation_all
614 (self, fitdeg, spec, 0, linelim, (irplib_base_spectrum_model*)&model,
615 irplib_vector_fill_logline_spectrum, SOFI_PIXTOL, SOFI_PIXSTEP,
616 hshiftmax, maxite, SOFI_MAXFAIL, SOFI_CONTINUE, doplot, &xc);
617
618 cpl_vector_delete(linepix);
619
620 if (!error) {
621 cpl_msg_info(cpl_func, "XC: %g (cost=%u:%u. lines=%u)", xc,
622 model.cost, model.xcost, model.ulines);
623 *xcorr = xc;
624 } else {
625 cpl_error_set_where(cpl_func);
626 cpl_polynomial_delete(self);
627 self = NULL;
628 }
629
630 return self;
631
632#else
633 cpl_vector * wl_errors;
634 int nsamp;
635 double wl_start, wl_stop, wl_error, xc;
636 cpl_bivector * catal_loc;
637 cpl_polynomial * xcdisp;
638 cpl_polynomial * xcdisp_tmp;
639 cpl_table * spc_tab;
640
641 /* Test entries */
642 if (spec == NULL || spec_cat == NULL || phdisprel == NULL) return NULL;
643 if (degree < 1 || degree > 3) return NULL;
644
645 /* Initialise */
646 wl_error = wl_err;
647 nsamp = nsamples;
648
649 /* Extract the interesting part of the catalog */
650 wl_start = cpl_polynomial_eval_1d(phdisprel, (double)1, NULL) - wl_err;
651 wl_stop = cpl_polynomial_eval_1d(phdisprel,
652 (double)cpl_vector_get_size(spec), NULL) + wl_err;
653 catal_loc = irplib_wlxcorr_cat_extract(spec_cat, wl_start, wl_stop);
654 if (catal_loc == NULL) {
655 cpl_msg_error(cpl_func, "Cannot extract sub-catalog [%g %g]",
656 wl_start, wl_stop);
657 return NULL;
658 }
659
660 /* Plot inputs */
661 if (display) {
662 irplib_wlxcorr_catalog_plot(catal_loc,
663 cpl_polynomial_eval_1d(phdisprel, 1, NULL),
664 cpl_polynomial_eval_1d(phdisprel, cpl_vector_get_size(spec),
665 NULL));
666 cpl_plot_vector(
667 "set grid;set xlabel 'Position (Pixel)';set ylabel 'Intensity (ADU/sec)';",
668 "t 'Extracted spectrum' w lines", "", spec);
669 }
670
671 /* Calibrate the spectrum with the catalog spectrum */
672 /* DEGREE = 1 */
673 wl_errors = cpl_vector_new(2);
674 cpl_vector_fill(wl_errors, wl_error);
675 xcdisp=irplib_wlxcorr_best_poly(spec, catal_loc, 1, phdisprel,
676 wl_errors, nsamp, slit_width, 2, &xc, NULL, NULL);
677
678 /* Higher degree --> less samples */
679 nsamp /= 10;
680 cpl_vector_delete(wl_errors);
681 wl_errors = cpl_vector_new(3);
682 cpl_vector_fill(wl_errors, wl_error);
683 xcdisp_tmp=irplib_wlxcorr_best_poly(spec, catal_loc, 2, xcdisp,
684 wl_errors, nsamp, slit_width, 2, &xc, NULL, NULL);
685 cpl_polynomial_delete(xcdisp);
686 xcdisp = xcdisp_tmp;
687
688 /* Higher Precision */
689 cpl_vector_divide_scalar(wl_errors, 10.0);
690 xcdisp_tmp=irplib_wlxcorr_best_poly(spec, catal_loc, 2, xcdisp,
691 wl_errors, nsamp, slit_width, 2, &xc, &spc_tab, NULL);
692 cpl_polynomial_delete(xcdisp);
693 cpl_bivector_delete(catal_loc);
694 cpl_vector_delete(wl_errors);
695 xcdisp = xcdisp_tmp;
696
697 /* Plot result */
698 if (display) {
699 irplib_wlxcorr_plot_spc_table(spc_tab, "XC", 0, 0);
700 }
701 cpl_table_delete(spc_tab);
702
703 *xcorr = xc;
704 return xcdisp;
705#endif
706}
707
708/*----------------------------------------------------------------------------*/
714/*----------------------------------------------------------------------------*/
715static cpl_vector * sofi_wavelength_extract_spectrum(
716 const cpl_image * in,
717 double slit_width)
718{
719 cpl_image * tmp;
720 cpl_image * collapsed;
721 cpl_vector * spec_init;
722 cpl_vector * spec_bgclean;
723 cpl_vector * spec_bg;
724 double * pspec_bgclean;
725 cpl_vector * spec_bgclean_log;
726 double * pspec_bgclean_log;
727 int i;
728
729 /* Check entries */
730 if (in == NULL) return NULL;
731
732 /* Local copy */
733 tmp = cpl_image_duplicate(in);
734
735 /* Flip because the wl goes from top to bottom */
736 cpl_image_flip(tmp, 0);
737
738 /* Collapse */
739 if ((collapsed = cpl_image_collapse_median_create(tmp, 1, 20, 20)) == NULL){
740 cpl_msg_error(cpl_func, "Collapsing input image");
741 cpl_image_delete(tmp);
742 return NULL;
743 }
744 cpl_image_delete(tmp);
745
746 /* Put the spectrum in a vector */
747 spec_init = cpl_vector_new_from_image_column(collapsed, 1);
748 cpl_image_delete(collapsed);
749
750 /* Remove thermal background contributions */
751 spec_bgclean = cpl_vector_duplicate(spec_init);
752 /* Filter away very wide (8 * slit_width) features */
753 if ((spec_bg = cpl_vector_filter_median_create(spec_init,
754 (int)((0.5+8*slit_width)/2))) == NULL) {
755 cpl_msg_error(cpl_func, "sub_lowpass failed");
756 cpl_vector_delete(spec_init);
757 cpl_vector_delete(spec_bgclean);
758 return NULL;
759 }
760 cpl_vector_subtract(spec_bgclean, spec_bg);
761 cpl_vector_delete(spec_bg);
762 cpl_vector_delete(spec_init);
763
764 /* Threshold negative intensities */
765 pspec_bgclean = cpl_vector_get_data(spec_bgclean);
766 for (i=0; i<cpl_vector_get_size(spec_bgclean); i++)
767 if (pspec_bgclean[i] < 0) pspec_bgclean[i] = 0.0;
768
769 /* Put less weight on the intensity by taking the logarithm */
770 /* Add 1 to ensure continuity around zero */
771 spec_bgclean_log = cpl_vector_duplicate(spec_bgclean);
772 pspec_bgclean = cpl_vector_get_data(spec_bgclean);
773 pspec_bgclean_log = cpl_vector_get_data(spec_bgclean_log);
774#if 1
775 for (i=0; i<cpl_vector_get_size(spec_bgclean_log); i++)
776 pspec_bgclean_log[i] = pspec_bgclean[i] > 0.0 ?
777 log1p(pspec_bgclean[i] ) : 0.0;
778#endif
779
780 cpl_vector_delete(spec_bgclean);
781 return spec_bgclean_log;
782}
const char * sofi_pfits_get_opti1_id(const cpl_propertylist *plist)
find out the OPTI1.ID key
Definition sofi_pfits.c:424
const char * sofi_pfits_get_mode(const cpl_propertylist *plist)
find out the instrument mode
Definition sofi_pfits.c:351
int sofi_pfits_get_naxis2(const cpl_propertylist *plist)
find out the NAXIS2 keyword
Definition sofi_pfits.c:363
double sofi_pfits_get_pixscale(const cpl_propertylist *plist)
find out the pixel scale
Definition sofi_pfits.c:449
cpl_polynomial * sofi_get_disprel_estimate(const char *filename, int poly_deg)
Estimate the instrument dispersion relation.
double sofi_get_slitwidth(const char *filename)
Find out the slit width.
cpl_polynomial * sofi_wavelength_engine(const cpl_image *in, const char *table_name, const char *oh, const char *xe, const char *ne, const cpl_polynomial *phdisprel, double slit_width, int degree, double wl_err, int nsamples, int use_ppm, int plot, double *xcorr)
Compute a dispersion relation.