36 #include "sofi_wavelength.h"
37 #include "sofi_pfits.h"
38 #include "sofi_utils.h"
41 #include "irplib_wavecal.h"
42 #include "irplib_utils.h"
43 #include "irplib_wlxcorr.h"
44 #include "irplib_ppm.h"
54 static cpl_bivector * sofi_wavelength_load_catalog(
const char *,
const char *,
55 const char *,
const char *);
56 static cpl_vector * sofi_wavelength_extract_spectrum(
const cpl_image *,
double);
57 static cpl_polynomial * sofi_wavelength_xc_engine(
const cpl_vector *,
58 const cpl_bivector *,
int,
const cpl_polynomial *,
double,
int,
59 double,
int,
double *);
68 #define SOFI_WFWHM 4.0
73 #define SOFI_PIXSTEP 0.5
78 #define SOFI_PIXTOL 1e-5
83 #define SOFI_OFFSET 50
93 #define SOFI_MAXFAIL 3
98 #define SOFI_CONTINUE 3
101 #define SOFI_MAX(A,B) ((A) > (B) ? (A) : (B))
102 #define SOFI_MIN(A,B) ((A) < (B) ? (A) : (B))
151 const cpl_image * in,
152 const char * table_name,
156 const cpl_polynomial * phdisprel,
165 cpl_polynomial * wl_poly_xc;
166 cpl_polynomial * wl_poly_ppm;
168 cpl_bivector * spec_cat;
169 cpl_vector * spectrum;
170 cpl_vector * filtered;
174 double stdev, median, threshold;
178 if (in==NULL || table_name==NULL || phdisprel == NULL)
return NULL;
179 if (degree > 3 || degree < 1)
return NULL;
181 degree = SOFI_DEGREE;
184 if ((spec_cat=sofi_wavelength_load_catalog(table_name, oh, xe, ne))==NULL) {
185 cpl_msg_error(cpl_func,
"Cannot load the catalog");
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);
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);
208 cpl_msg_indent_less();
213 if ((filtered=cpl_vector_filter_median_create(spectrum,
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");
221 clean = cpl_vector_duplicate(spectrum);
222 cpl_vector_subtract(clean, filtered);
223 cpl_vector_delete(filtered);
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;
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);
245 cpl_polynomial_delete(wl_poly_xc);
246 wl_poly_xc = wl_poly_ppm;
247 cpl_msg_indent_less();
248 cpl_vector_delete(clean);
250 cpl_bivector_delete(spec_cat);
251 cpl_vector_delete(spectrum);
268 cpl_propertylist * plist;
271 plist=cpl_propertylist_load(filename, 0);
275 cpl_msg_error(cpl_func,
"cannot get slit used");
276 cpl_propertylist_delete(plist);
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;
285 cpl_msg_error(cpl_func,
"unrecognized slit");
286 cpl_propertylist_delete(plist);
292 if (cpl_error_get_code()) {
293 cpl_msg_error(cpl_func,
"cannot get pixscale");
294 cpl_propertylist_delete(plist);
297 cpl_propertylist_delete(plist);
299 slit_width /= pscale;
316 cpl_polynomial *
self;
317 cpl_propertylist * plist;
320 double wmin = FLT_MAX;
321 double wmax = FLT_MAX;
325 cpl_ensure(filename != NULL, CPL_ERROR_NULL_INPUT, NULL);
326 cpl_ensure(poly_deg >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
328 plist = cpl_propertylist_load_regexp(filename, 0,
"ESO INS MODE$|^NAXIS2$",
330 cpl_ensure(plist != NULL, cpl_error_get_code(), 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")) {
344 }
else if (!strcmp(sval,
"LONG_SLIT_RED")) {
347 }
else if (!strcmp(sval,
"LONG_SLIT_Z")) {
350 }
else if (!strcmp(sval,
"LONG_SLIT_J")) {
353 }
else if (!strcmp(sval,
"LONG_SLIT_H")) {
356 }
else if (!strcmp(sval,
"LONG_SLIT_K")) {
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);
365 cpl_propertylist_delete(plist);
367 if (cpl_error_get_code())
return NULL;
370 (void)cpl_error_set_message(cpl_func, CPL_ERROR_BAD_FILE_FORMAT,
371 "In '%s' NAXIS2 must be at least 2, not %d",
382 self = cpl_polynomial_new(1);
384 cpl_polynomial_set_coeff(
self, &i, (wmax - wmin) / npix);
386 cpl_polynomial_set_coeff(
self, &i, wmin - 0.5 * (wmax - wmin) / npix);
403 static cpl_bivector * sofi_wavelength_load_catalog(
414 cpl_table * cat_tab1;
415 cpl_table * cat_tab2;
416 cpl_propertylist * reflist;
420 if (name == NULL)
return NULL;
426 if (!strcmp(name,
"oh")) {
429 cpl_msg_error(cpl_func,
"Please provide the OH lines catalog");
433 if ((cat_tab1 = cpl_table_load(oh, 1, 0)) == NULL) {
434 cpl_msg_error(cpl_func,
"Cannot load the catalog");
437 }
else if (!strcmp(name,
"Xe")) {
440 cpl_msg_error(cpl_func,
"Please provide the XE lines catalog");
444 if ((cat_tab1 = cpl_table_load(xe, 1, 0)) == NULL) {
445 cpl_msg_error(cpl_func,
"Cannot load the XE catalog");
448 }
else if (!strcmp(name,
"Ne")) {
451 cpl_msg_error(cpl_func,
"Please provide the NE lines catalog");
455 if ((cat_tab1 = cpl_table_load(ne, 1, 0)) == NULL) {
456 cpl_msg_error(cpl_func,
"Cannot load the NE catalog");
459 }
else if (!strcmp(name,
"Xe+Ne")) {
462 cpl_msg_error(cpl_func,
"Please provide the XE lines catalog");
466 if ((cat_tab1 = cpl_table_load(xe, 1, 0)) == NULL) {
467 cpl_msg_error(cpl_func,
"Cannot load the XE catalog");
472 cpl_msg_error(cpl_func,
"Please provide the NE lines catalog");
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);
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);
489 cpl_table_delete(cat_tab2);
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);
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++) {
506 = cpl_table_get_double(cat_tab1, SOFI_COL_WAVELENGTH, i, NULL);
508 = cpl_table_get_double(cat_tab1, SOFI_COL_EMISSION, i, NULL);
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++) {
520 wl = cpl_table_get_double(cat_tab1, SOFI_COL_WAVELENGTH, i, NULL);
522 intens = cpl_table_get_double(cat_tab1, SOFI_COL_EMISSION, i, NULL);
523 psigx[3*i] = wl - epsilon;
526 psigy[3*i+1] = intens;
527 psigx[3*i+2] = wl + epsilon;
531 cpl_table_delete(cat_tab1);
550 static cpl_polynomial * sofi_wavelength_xc_engine(
551 const cpl_vector * spec,
552 const cpl_bivector * spec_cat,
554 const cpl_polynomial * phdisprel,
563 irplib_line_spectrum_model model;
564 const int spec_size = cpl_vector_get_size(spec);
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,
569 const int emil = irplib_bivector_count_positive(spec_cat, phwl_min,
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;
579 const int hshiftmax = SOFI_MIN(spec_size/4, max_offset);
583 = cpl_vector_wrap(cpl_bivector_get_size(spec_cat),
584 cpl_calloc(cpl_bivector_get_size(spec_cat),
586 cpl_polynomial *
self;
587 cpl_error_code error;
588 const int fitdeg = emil > degree ? degree : emil-1;
589 const int maxite = fitdeg * 200;
591 #ifdef SOFI_USE_LINELIM
592 xtrunc*(spec_size + 2 * hshiftmax);
597 const cpl_boolean doplot
598 = display || getenv(
"SOFI_DOPLOT") ? CPL_TRUE : CPL_FALSE;
602 memset(&model, 0,
sizeof(model));
605 model.xtrunc = xtrunc;
606 model.lines = spec_cat;
607 model.linepix = linepix;
611 self = cpl_polynomial_duplicate(phdisprel);
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);
618 cpl_vector_delete(linepix);
621 cpl_msg_info(cpl_func,
"XC: %g (cost=%u:%u. lines=%u)", xc,
622 model.cost, model.xcost, model.ulines);
625 cpl_error_set_where(cpl_func);
626 cpl_polynomial_delete(
self);
633 cpl_vector * wl_errors;
635 double wl_start, wl_stop, wl_error, xc;
636 cpl_bivector * catal_loc;
637 cpl_polynomial * xcdisp;
638 cpl_polynomial * xcdisp_tmp;
642 if (spec == NULL || spec_cat == NULL || phdisprel == NULL)
return NULL;
643 if (degree < 1 || degree > 3)
return NULL;
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]",
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),
667 "set grid;set xlabel 'Position (Pixel)';set ylabel 'Intensity (ADU/sec)';",
668 "t 'Extracted spectrum' w lines",
"", spec);
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);
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);
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);
699 irplib_wlxcorr_plot_spc_table(spc_tab,
"XC", 0, 0);
701 cpl_table_delete(spc_tab);
715 static cpl_vector * sofi_wavelength_extract_spectrum(
716 const cpl_image * in,
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;
730 if (in == NULL)
return NULL;
733 tmp = cpl_image_duplicate(in);
736 cpl_image_flip(tmp, 0);
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);
744 cpl_image_delete(tmp);
747 spec_init = cpl_vector_new_from_image_column(collapsed, 1);
748 cpl_image_delete(collapsed);
751 spec_bgclean = cpl_vector_duplicate(spec_init);
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);
760 cpl_vector_subtract(spec_bgclean, spec_bg);
761 cpl_vector_delete(spec_bg);
762 cpl_vector_delete(spec_init);
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;
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);
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;
780 cpl_vector_delete(spec_bgclean);
781 return spec_bgclean_log;