36 #include "irplib_wavecal.h"
38 #include "visir_spectro.h"
40 #include "visir_utils.h"
41 #include "visir_pfits.h"
42 #include "visir_inputs.h"
43 #include "visir_parameter.h"
44 #include "visir_spc_distortion.h"
46 #include "irplib_framelist.h"
73 const cpl_vector * vsymm;
77 const cpl_bivector * lines;
79 const cpl_bivector * tqeff;
89 visir_polynomial_shift_1d_from_correlation(cpl_polynomial *,
94 const cpl_polynomial *,
96 int,
int, cpl_boolean,
99 static cpl_error_code visir_spectro_refine(cpl_polynomial *,
102 const cpl_polynomial *,
103 int, cpl_boolean,
double *,
104 cpl_boolean *,
double *);
106 static cpl_error_code visir_spectro_fill(cpl_vector *,
const cpl_polynomial *,
109 static cpl_bivector * visir_spc_extract(cpl_image *, cpl_propertylist *,
110 cpl_image **,
int,
int);
112 static cpl_error_code visir_spc_emission(cpl_bivector *,
const cpl_vector *,
113 const cpl_bivector *,
114 const cpl_bivector *,
115 const cpl_vector *,
double);
117 static cpl_polynomial * visir_spc_phys_disp(
int,
double, visir_spc_resol,
int,
119 static cpl_polynomial * visir_spc_phys_lrp(
void);
120 static double visir_spc_get_dispersion(
const cpl_polynomial *,
double);
121 static cpl_error_code visir_vector_convolve_symm(cpl_vector *,
123 static cpl_vector * cpl_spc_convolve_init(
int,
double,
double,
int);
125 static cpl_error_code visir_spectro_qclist_wcal(cpl_propertylist *,
128 const cpl_polynomial *,
129 const cpl_polynomial *);
131 static cpl_error_code visir_spectro_qclist_obs(cpl_propertylist *,
134 static const double N_upper = 13.4e-6;
135 static const double whechelle = 35.8/2;
138 #define VISIR_XC_LEN 50
140 #ifndef VISIR_XC_SUBSEARCH
141 #define VISIR_XC_SUBSEARCH 100
144 #ifndef VISIR_SPECTRO_SIGMA
145 #define VISIR_SPECTRO_SIGMA 3.0
156 visir_spc_extract_order(cpl_image ** order,
157 cpl_image ** comorder,
158 const cpl_image * combined,
159 const cpl_image * imhcycle,
162 const cpl_boolean do_ech,
169 VISIR_PARAM_REJLEFT);
171 VISIR_PARAM_REJRIGHT);
175 skip_if (visir_spc_echelle_limit(&icol1, &icol2, wlen,
176 pconfig->orderoffset,
177 1, cpl_image_get_size_y(combined),
181 icol2 = cpl_image_get_size_x(imhcycle);
186 cpl_msg_info(cpl_func,
"Ignoring %d leftmost columns from %d to %d",
187 jcol1, icol1, icol1 + jcol1);
191 cpl_msg_info(cpl_func,
"Ignoring %d rightmost columns from %d to %d",
192 jcol2, icol2 - jcol2, icol2);
197 cpl_msg_info(cpl_func,
"Ignoring %d leftmost columns", jcol1);
201 cpl_msg_info(cpl_func,
"Ignoring %d rightmost columns", jcol2);
206 if (icol1 != 1 || icol2 != cpl_image_get_size_x(imhcycle)) {
207 *order = visir_spc_column_extract(imhcycle, icol1, icol2,
211 *comorder = visir_spc_column_extract(combined, icol1, icol2,
216 *order = cpl_image_duplicate(imhcycle);
217 *comorder = cpl_image_duplicate(combined);
222 return cpl_error_get_code();
242 visir_spc_resol visir_spc_get_res_wl(
const irplib_framelist * rawframes,
243 double * pwlen,
double * pslitw,
244 double * ptemp,
double * pfwhm,
247 cpl_errorstate cleanstate = cpl_errorstate_get();
249 visir_spc_resol resol = VISIR_SPC_R_ERR;
250 char ptmp[IRPLIB_FITS_STRLEN+1];
251 double wl, spx, pfov = 0.127;
253 cpl_boolean need_temp = ptemp != NULL;
257 cpl_ensure(rawframes != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
258 cpl_ensure(pwlen != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
259 cpl_ensure(pslitw != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
260 cpl_ensure(pfwhm != NULL, CPL_ERROR_NULL_INPUT, VISIR_SPC_R_ERR);
264 cpl_ensure(n > 0, CPL_ERROR_DATA_NOT_FOUND, VISIR_SPC_R_ERR);
268 CPL_TYPE_DOUBLE, CPL_TRUE, 1e-6));
272 CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
275 CPL_TYPE_STRING, CPL_TRUE, 0.0));
278 CPL_TYPE_STRING, CPL_TRUE, 0.0));
280 for (
int i = 0; i < n; i++) {
281 const cpl_propertylist * plist;
282 const char * filename =
285 double wl_tmp, sl_tmp, spx_tmp, pfov_tmp;
288 cpl_ensure(!cpl_error_get_code(), CPL_ERROR_DATA_NOT_FOUND,
291 cpl_ensure(filename != NULL, CPL_ERROR_DATA_NOT_FOUND,
296 cpl_ensure(plist != NULL, CPL_ERROR_DATA_NOT_FOUND, VISIR_SPC_R_ERR);
299 if (wl_tmp <= 0.0 || !cpl_errorstate_is_equal(cleanstate)) {
300 irplib_error_recover(cleanstate,
"Missing or invalid FITS card");
301 wl_tmp = VISIR_SPC_LRP_CWLEN;
304 if (pfits == NULL || !cpl_errorstate_is_equal(cleanstate)) {
305 irplib_error_recover(cleanstate,
"Missing or invalid FITS card");
306 pfits = VISIR_SPC_LRP_NAME;
314 if (pfov_tmp <= 0.) {
315 cpl_errorstate_set(cleanstate);
316 cpl_msg_warning(cpl_func, VISIR_PFITS_STRING_PIXSCALE
317 " not set, falling back to 0.127");
322 cpl_ensure(!cpl_error_get_code(), CPL_ERROR_DATA_NOT_FOUND,
340 strncpy(ptmp, pfits, IRPLIB_FITS_STRLEN);
341 ptmp[IRPLIB_FITS_STRLEN] =
'\0';
343 cpl_msg_info(cpl_func,
"RESOL [" VISIR_SPC_LRP_NAME
"|LR|MR|HRS|HRG]"
344 " and WLEN [m] (%d frames): %s %g", n, ptmp, *pwlen);
347 cpl_msg_error(cpl_func,
"Pixel Spacing (%g) in %s is non-"
348 "positive", spx, filename);
349 cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, VISIR_SPC_R_ERR);
353 cpl_msg_error(cpl_func,
"Slit Width (%g) in %s is non-positive",
355 cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, VISIR_SPC_R_ERR);
358 cpl_msg_info(cpl_func,
"Slit Width [pixel] and Pixel Spacing [m]: "
359 "%g %g", *pslitw, spx);
361 if (!strcmp(VISIR_SPC_LRP_NAME, ptmp)) {
362 resol = VISIR_SPC_R_LRP;
363 }
else if (!strcmp(
"LR", ptmp)) {
364 resol = VISIR_SPC_R_LR;
365 }
else if (!strcmp(
"MR", ptmp)) {
366 resol = VISIR_SPC_R_MR;
367 }
else if (!strcmp(
"HRS", ptmp)) {
368 resol = VISIR_SPC_R_HR;
369 }
else if (!strcmp(
"HRG", ptmp)) {
370 resol = VISIR_SPC_R_GHR;
372 cpl_msg_error(cpl_func,
"Unsupported resolution (%s) in %s",
374 cpl_ensure(0, CPL_ERROR_UNSUPPORTED_MODE, VISIR_SPC_R_ERR);
377 if (resol != VISIR_SPC_R_LRP) {
380 VISIR_PFITS_DOUBLE_WLEN,
381 CPL_TYPE_DOUBLE, CPL_TRUE,
385 if (visir_spc_optmod_init(resol, *pwlen, &ins_settings, is_aqu)) {
386 cpl_msg_error(cpl_func,
"Resolution %s does not support "
387 "Central Wavelength [m]: %g", ptmp, *pwlen);
388 cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
391 cpl_msg_info(cpl_func,
"The %s-Spectral Resolution at %gm: %g",
393 visir_spc_optmod_resolution(&ins_settings));
394 cpl_msg_info(cpl_func,
"The %s-Linear Dispersion at %gm [pixel/m]: "
396 visir_spc_optmod_dispersion(&ins_settings));
398 *pfwhm = *pwlen * visir_spc_optmod_dispersion(&ins_settings)
399 / visir_spc_optmod_resolution(&ins_settings);
401 cpl_msg_info(cpl_func,
"The %s-FWHM at %gm [pixel]: %g",
402 ptmp, *pwlen, *pfwhm);
404 if (fabs(sl-sl_tmp) > 1e-3) {
405 cpl_msg_error(cpl_func,
"Inconsistent slit width (%g <=>"
406 " %g) in %s (%d of %d)",
407 sl, sl_tmp, filename, i+1, n);
408 cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
410 if (fabs(pfov-pfov_tmp) > 1e-4) {
411 cpl_msg_error(cpl_func,
"Inconsistent pfov (%g <=>"
412 " %g) in %s (%d of %d)",
413 pfov, pfov_tmp, filename, i+1, n);
414 cpl_ensure(0, CPL_ERROR_INCOMPATIBLE_INPUT, VISIR_SPC_R_ERR);
420 if (cpl_error_get_code()) {
421 visir_error_reset(
"Could not get FITS key");
422 }
else if ((-20 < temp) && (temp < 60)) {
424 need_temp = CPL_FALSE;
432 cpl_msg_warning(cpl_func,
"No FITS-files specify the M1 temperature, "
440 cpl_msg_info(cpl_func,
"The M1 temperature [Kelvin]: %g", *ptemp);
471 cpl_error_code visir_vector_resample(cpl_vector *
self,
472 const cpl_vector * xbounds,
473 const cpl_bivector * source)
476 const cpl_vector * xsource = cpl_bivector_get_x_const(source);
477 const cpl_vector * ysource = cpl_bivector_get_y_const(source);
479 const double * pxsource = cpl_vector_get_data_const(xsource);
480 const double * pysource = cpl_vector_get_data_const(ysource);
481 const double * pxbounds = cpl_vector_get_data_const(xbounds);
484 cpl_vector * ybounds = cpl_vector_new(cpl_vector_get_size(xbounds));
485 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual)
486 cpl_bivector * boundary = cpl_bivector_wrap_vectors((cpl_vector*)xbounds,
488 IRPLIB_DIAG_PRAGMA_POP
489 double * pybounds = cpl_vector_get_data(ybounds);
491 double * pself = cpl_vector_get_data(self);
492 const
int npix = cpl_vector_get_size(self);
497 cpl_ensure_code(cpl_bivector_get_size(boundary) == npix + 1,
498 CPL_ERROR_ILLEGAL_INPUT);
502 itt = cpl_vector_find(xsource, pxbounds[0]);
506 skip_if (cpl_bivector_interpolate_linear(boundary, source));
510 while (pxsource[itt] < pxbounds[0]) itt++;
512 for (i=0; i < npix; i++) {
517 double xlow = pxbounds[i];
518 double x = pxsource[itt];
520 if (x > pxbounds[i+1]) x = pxbounds[i+1];
522 pself[i] = pybounds[i] * (x - xlow);
525 while (pxsource[itt] < pxbounds[i+1]) {
526 const double xprev = x;
528 if (x > pxbounds[i+1]) x = pxbounds[i+1];
529 pself[i] += pysource[itt] * (x - xlow);
535 pself[i] += pybounds[i+1] * (pxbounds[i+1] - xlow);
539 pself[i] /= 2 * (pxbounds[i+1] - pxbounds[i]);
546 cpl_vector_delete(ybounds);
547 cpl_bivector_unwrap_vectors(boundary);
549 return cpl_error_get_code();
579 cpl_error_code visir_spc_extract_wcal(
const cpl_image * combined,
580 const cpl_image * hcycle,
581 double wlen,
double slitw,
582 double temp,
double fwhm,
583 visir_spc_resol resol,
585 const char * spc_cal_lines,
586 const char * spc_cal_qeff,
587 cpl_table ** pspc_table,
588 cpl_image ** pweight2d,
589 cpl_propertylist * qclist,
590 int doplot,
int bkgcorrect,
595 cpl_bivector * spc_n_err = NULL;
596 cpl_image * flipped = NULL;
597 const int npix = cpl_image_get_size_y(combined);
600 cpl_ensure_code(pweight2d != NULL, CPL_ERROR_NULL_INPUT);
604 cpl_ensure_code(npix > 0, CPL_ERROR_ILLEGAL_INPUT);
605 error_if(npix != cpl_image_get_size_y(hcycle), CPL_ERROR_ILLEGAL_INPUT,
606 "Sky frame does not have same size as the object frame."
607 " %d vs %d pixels", (
int)cpl_image_get_size_y(hcycle), npix);
612 skip_if (visir_spc_wavecal(hcycle, qclist, wlen, slitw, temp, fwhm, resol,
613 ioffset, spc_cal_lines, spc_cal_qeff,
614 pspc_table, doplot, is_aqu));
617 flipped = cpl_image_cast(combined, CPL_TYPE_DOUBLE);
622 spc_n_err = visir_spc_extract(flipped, qclist, pweight2d,
626 cpl_image_delete(flipped);
629 skip_if (*pspc_table == NULL);
631 skip_if (cpl_table_new_column(*pspc_table,
"SPC_EXTRACTED", CPL_TYPE_DOUBLE));
632 skip_if (cpl_table_new_column(*pspc_table,
"SPC_ERROR", CPL_TYPE_DOUBLE));
634 skip_if (cpl_table_set_column_unit(*pspc_table,
"SPC_EXTRACTED",
"ADU/s"));
635 skip_if (cpl_table_set_column_unit(*pspc_table,
"SPC_ERROR",
"ADU/s"));
637 skip_if (cpl_table_copy_data_double(*pspc_table,
"SPC_EXTRACTED",
638 cpl_bivector_get_x_data(spc_n_err)));
639 skip_if (cpl_table_copy_data_double(*pspc_table,
"SPC_ERROR",
640 cpl_bivector_get_y_data(spc_n_err)));
643 visir_table_plot(
"set grid;set xlabel 'Wavelength [m]';",
644 "t 'Extracted Spectrum' w linespoints",
645 "", *pspc_table,
"WLEN",
"SPC_EXTRACTED");
646 visir_table_plot(
"set grid;set xlabel 'Wavelength [m]';",
647 "t 'Error on Extracted Spectrum' w linespoints",
648 "", *pspc_table,
"WLEN",
"SPC_ERROR");
653 cpl_image_delete(flipped);
654 cpl_bivector_delete(spc_n_err);
656 return cpl_error_get_code();
687 cpl_error_code visir_spc_wavecal(
const cpl_image * hcycle,
688 cpl_propertylist * qclist,
689 double wlen,
double slitw,
690 double temp,
double fwhm,
691 visir_spc_resol resol,
693 const char * linefile,
694 const char * qefffile,
695 cpl_table ** pspc_table,
int doplot,
700 cpl_polynomial * phdisp = NULL;
702 cpl_polynomial * xcdisp = NULL;
705 cpl_vector * wlvals = NULL;
706 cpl_vector * spmodel = NULL;
708 cpl_bivector * emission = NULL;
709 cpl_vector * boundary = NULL;
711 cpl_bivector * temiss = NULL;
712 cpl_bivector * tqeff = NULL;
714 cpl_image * corrected = NULL;
716 cpl_image * xc_image = NULL;
717 cpl_vector * xc_vector = NULL;
719 cpl_vector * vsymm = NULL;
720 cpl_vector * vxc = NULL;
722 const int npix = cpl_image_get_size_y(hcycle);
726 double qcxc = -1.0, qcsubdelta = 0.;
728 const cpl_size i0 = 0;
729 const cpl_size i1 = 1;
730 cpl_boolean didshift = CPL_FALSE;
733 cpl_ensure_code(!cpl_error_get_code(), cpl_error_get_code());
734 cpl_ensure_code(pspc_table, CPL_ERROR_NULL_INPUT);
735 cpl_ensure_code(npix > 0, CPL_ERROR_ILLEGAL_INPUT);
739 corrected = cpl_image_cast(hcycle, CPL_TYPE_DOUBLE);
742 hc_min = cpl_image_get_min(corrected);
744 cpl_msg_info(cpl_func,
"Half-cycle image [%d X %d] has minimum intensity: %g",
745 (
int)cpl_image_get_size_x(hcycle), npix, hc_min);
747 cpl_msg_warning(cpl_func,
"Thresholding negative intensities in half-"
748 "cycle image: %g", hc_min);
749 skip_if (cpl_image_threshold(corrected, 0.0, DBL_MAX, 0.0, DBL_MAX));
750 }
else if (hc_min > 0) {
751 skip_if (cpl_image_subtract_scalar(corrected, hc_min));
754 xc_image = cpl_image_duplicate(corrected);
757 cpl_image_delete(corrected);
758 corrected = cpl_image_collapse_create(xc_image, 1);
759 cpl_image_delete(xc_image);
760 xc_image = corrected;
763 skip_if(cpl_image_divide_scalar(xc_image, npix));
765 xc_vector = cpl_vector_wrap(npix, cpl_image_get_data(xc_image));
770 phdisp = visir_spc_phys_lrp();
771 cpl_msg_info(cpl_func,
"Central Dispersion (physical model) [pixel/m]: %g",
772 1.0/visir_spc_get_dispersion(phdisp, npix/2.0 + 0.5));
773 cpl_msg_info(cpl_func,
"Central Wavelength (physical model) [m]: %g",
774 cpl_polynomial_eval_1d(phdisp, npix/2.0 + 0.5, NULL));
775 cpl_msg_info(cpl_func,
"First Wavelength (physical model) [m]: %g",
776 cpl_polynomial_eval_1d(phdisp, 1.0, NULL));
777 cpl_msg_info(cpl_func,
"Last Wavelength (physical model) [m]: %g",
778 cpl_polynomial_eval_1d(phdisp, 1024, NULL));
779 cpl_polynomial_dump(phdisp, stdout);
780 cpl_polynomial_delete(phdisp);
783 phdisp = visir_spc_phys_disp(npix, wlen, resol, ioffset, is_aqu);
786 if (cpl_polynomial_get_degree(phdisp) == 2) {
787 const cpl_size i2 = 2;
788 cpl_msg_info(cpl_func,
"Dispersion polynomial of physical model:"
789 " %gmum + ipix * %gmum/pixel + ipix^2 * (%g)mum/pixel^2 "
790 "[ipix = 1, 2, ..., %d]",
791 cpl_polynomial_get_coeff(phdisp, &i0) * 1e6,
792 cpl_polynomial_get_coeff(phdisp, &i1) * 1e6,
793 cpl_polynomial_get_coeff(phdisp, &i2) * 1e6,
797 cpl_msg_info(cpl_func,
"Dispersion polynomial of physical model:"
798 " %gmum + ipix * %gmum/pixel [ipix = 1, 2, ..., %d]",
799 cpl_polynomial_get_coeff(phdisp, &i0) * 1e6,
800 cpl_polynomial_get_coeff(phdisp, &i1) * 1e6, npix);
803 temiss = visir_bivector_load_fits(linefile,
"Wavelength",
"Emission", 1);
804 any_if (
"Could not load file with Emission Lines");
806 tqeff = visir_bivector_load_fits(qefffile,
"Wavelength",
"Efficiency",
808 any_if(
"Could not load file with Quantum-Efficiencies");
811 visir_bivector_plot(
"set grid;set xlabel 'Wavelength [m]';",
"t '"
812 "Quantum Efficiency' w linespoints",
"", tqeff);
815 vsymm = cpl_spc_convolve_init(npix, slitw, fwhm, doplot);
817 skip_if (vsymm == NULL);
819 vxc = cpl_vector_new(1);
820 xcdisp = cpl_polynomial_new(1);
822 mymodel.lines = temiss;
823 mymodel.tqeff = tqeff;
824 mymodel.vsymm = vsymm;
830 skip_if(visir_spectro_refine(xcdisp, xc_vector, &mymodel, phdisp,
831 VISIR_XC_LEN, doplot,
832 &qcxc, &didshift, &qcsubdelta));
835 if (fabs(qcsubdelta) >= VISIR_XC_LEN) {
836 cpl_msg_warning(cpl_func,
"Cross-correlation (%g pixel shift): %g",
839 cpl_msg_info(cpl_func,
"Cross-correlation (%g pixel shift): %g",
844 cpl_msg_info(cpl_func,
"Dispersion polynomial from cross-correlation: "
845 "%gm + ipix * %gm/pixel [ipix = 1, 2, ..., %d]",
846 cpl_polynomial_get_coeff(xcdisp, &i0),
847 cpl_polynomial_get_coeff(xcdisp, &i1), npix);
849 cpl_msg_info(cpl_func,
"New Central Wavelength [m]: %g",
850 cpl_polynomial_eval_1d(xcdisp, 0.5*npix+0.5, NULL));
852 *pspc_table = cpl_table_new(npix);
856 wlvals = cpl_vector_new(npix);
857 bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(wlvals),
860 skip_if (cpl_vector_fill_polynomial(wlvals, xcdisp, 1.0, 1.0));
863 spmodel = cpl_vector_new(npix);
864 bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
866 skip_if (visir_spectro_fill(spmodel, phdisp,
870 (void)cpl_vector_unwrap(spmodel);
871 spmodel = cpl_vector_new(npix);
872 bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
875 skip_if (visir_spectro_fill(spmodel, xcdisp,
878 bug_if (cpl_table_wrap_double(*pspc_table,
879 cpl_image_get_data_double(xc_image),
881 (void)cpl_image_unwrap(xc_image);
885 (void)cpl_vector_unwrap(spmodel);
886 spmodel = cpl_vector_new(npix);
887 bug_if (cpl_table_wrap_double(*pspc_table, cpl_vector_get_data(spmodel),
890 boundary = cpl_vector_new(npix + 1);
891 skip_if (cpl_vector_fill_polynomial(boundary, xcdisp, 0.5, 1.0));
892 skip_if (visir_vector_resample(spmodel, boundary, temiss));
894 bug_if (cpl_table_set_column_unit(*pspc_table,
"WLEN",
"m"));
895 bug_if (cpl_table_set_column_unit(*pspc_table,
"SPC_MODEL_PH",
897 bug_if (cpl_table_set_column_unit(*pspc_table,
"SPC_MODEL_XC",
899 bug_if (cpl_table_set_column_unit(*pspc_table,
"SPC_SKY",
"ADU/s"));
905 if (cpl_vector_get(wlvals, 0) < N_upper &&
906 N_upper < cpl_vector_get(wlvals, npix-1))
907 cpl_msg_warning(cpl_func,
"Spectrum goes above N-band (%gm). Wavelength"
908 " Calibration may be entirely inaccurate", N_upper);
910 bug_if(visir_spectro_qclist_wcal(qclist, npix, qcxc, didshift, qcsubdelta,
914 cpl_bivector * plot = cpl_bivector_wrap_vectors(wlvals, xc_vector);
916 visir_bivector_plot(
"set grid;set xlabel 'Wavelength [m]';",
"t 'Spec"
917 "trum from Half-cycle' w linespoints",
"", plot);
918 cpl_bivector_unwrap_vectors(plot);
920 visir_table_plot(
"set grid;set xlabel 'Wavelength [m]';",
921 "t 'Calibrated Model Spectrum' w linespoints",
922 "", *pspc_table,
"WLEN",
"SPC_MODEL_XC");
925 visir_table_plot(
"set grid;set xlabel 'Wavelength [m]';",
926 "t 'Physical Model Spectrum' w linespoints",
927 "", *pspc_table,
"WLEN",
"SPC_MODEL_PH");
929 if (resol != VISIR_SPC_R_LRP) {
932 emission = cpl_bivector_new(2 * npix);
934 cpl_vector_delete(boundary);
935 boundary = cpl_vector_new(2 * npix + 1);
937 cpl_vector_fill_polynomial(cpl_bivector_get_x(emission),
938 phdisp, -0.5*npix, 1);
939 cpl_vector_fill_polynomial(boundary, phdisp, -0.5*(npix+1), 1);
942 visir_spc_emission(emission, boundary, temiss, tqeff, vsymm, temp);
943 cpl_vector_delete(boundary);
946 visir_bivector_plot(
"set grid;set xlabel 'Wavelength [m]';",
947 "t 'Extended Model Spectrum' w linespoints",
954 (void)cpl_vector_unwrap(wlvals);
955 (void)cpl_vector_unwrap(spmodel);
956 cpl_polynomial_delete(phdisp);
957 cpl_polynomial_delete(xcdisp);
958 cpl_image_delete(xc_image);
959 cpl_vector_delete(vsymm);
960 cpl_image_delete(corrected);
961 cpl_bivector_delete(temiss);
962 cpl_bivector_delete(tqeff);
963 cpl_vector_delete(boundary);
964 cpl_bivector_delete(emission);
965 (void)cpl_vector_unwrap(xc_vector);
966 cpl_vector_delete(vxc);
968 return cpl_error_get_code();
990 cpl_error_code visir_spc_echelle_limit(
int * pcol1,
int * pcol2,
double wlen,
991 int ioffset,
int icolmin,
int icolmax,
1002 cpl_ensure_code(wlen > 0, CPL_ERROR_ILLEGAL_INPUT);
1003 cpl_ensure_code(pcol1, CPL_ERROR_NULL_INPUT);
1004 cpl_ensure_code(pcol2, CPL_ERROR_NULL_INPUT);
1005 cpl_ensure_code(icolmin > 0, CPL_ERROR_ILLEGAL_INPUT);
1006 cpl_ensure_code(icolmax >= icolmin, CPL_ERROR_ILLEGAL_INPUT);
1008 cpl_ensure_code(ioffset >= -4, CPL_ERROR_ILLEGAL_INPUT);
1009 cpl_ensure_code(ioffset <= 4, CPL_ERROR_ILLEGAL_INPUT);
1011 error = visir_spc_optmod_init(VISIR_SPC_R_GHR, wlen, &ins_settings, is_aqu);
1013 cpl_msg_error(cpl_func,
"HRG Optical model initialization (%p) failed: %d "
1014 "(%g)", (
void*)&ins_settings, error, wlen);
1015 cpl_ensure_code(0, CPL_ERROR_ILLEGAL_INPUT);
1017 order = ioffset + visir_spc_optmod_get_echelle_order(&ins_settings);
1020 cpl_ensure_code(order > 0, CPL_ERROR_ILLEGAL_INPUT);
1021 cpl_ensure_code(order <= 18, CPL_ERROR_ILLEGAL_INPUT);
1023 wleni = visir_spc_optmod_echelle(&ins_settings, wlen, ioffset );
1025 echpos = visir_spc_optmod_cross_dispersion(&ins_settings, wleni);
1026 if (echpos <= whechelle || echpos >= icolmax-whechelle) {
1027 cpl_msg_error(cpl_func,
"Echelle (%d) location out of range [%d;%d]: %g",
1028 order, icolmin, icolmax, echpos);
1029 cpl_ensure_code(0, CPL_ERROR_DATA_NOT_FOUND);
1032 *pcol1 = ceil(echpos - whechelle);
1033 *pcol2 = echpos + whechelle;
1035 if (*pcol1 < icolmin) *pcol1 = icolmin;
1036 if (*pcol2 > icolmax) *pcol2 = icolmax;
1038 cpl_msg_info(cpl_func,
"Echelle order %d at col %g [%d; %d]", order, echpos,
1041 return cpl_error_get_code();
1059 cpl_image * visir_spc_column_extract(
const cpl_image *
self,
int icol1,
1060 int icol2,
int doplot)
1063 cpl_image * band = NULL;
1064 cpl_image * spatial = NULL;
1065 const int nrow = cpl_image_get_size_y(
self);
1066 const int ncol = cpl_image_get_size_x(
self);
1068 cpl_ensure(
self != NULL, CPL_ERROR_NULL_INPUT, NULL);
1069 cpl_ensure(icol1 > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1070 cpl_ensure(icol2 >= icol1, CPL_ERROR_ILLEGAL_INPUT, NULL);
1072 cpl_ensure(ncol >= icol2, CPL_ERROR_ILLEGAL_INPUT, NULL);
1074 band = cpl_image_extract(
self, icol1, 1, icol2, nrow);
1078 visir_image_plot(
"",
"t 'The full-width image'",
"",
self);
1082 spatial = cpl_image_collapse_create(
self, 0);
1084 skip_if (cpl_image_divide_scalar(spatial, nrow));
1086 visir_image_row_plot(
"set grid;",
"t 'Spectral direction "
1087 "collapsed' w linespoints",
"",
1094 cpl_image_delete(spatial);
1095 if (cpl_error_get_code() && band != NULL) {
1096 cpl_image_delete(band);
1119 cpl_error_code visir_spectro_qc(cpl_propertylist * qclist,
1120 cpl_propertylist * paflist,
1121 cpl_boolean drop_wcs,
1122 const irplib_framelist * rawframes,
1123 const char * regcopy,
1124 const char * regcopypaf)
1127 const cpl_propertylist * reflist
1132 bug_if (visir_qc_append_capa(qclist, rawframes));
1134 if (regcopy != NULL)
1135 bug_if (cpl_propertylist_copy_property_regexp(qclist, reflist,
1138 if (regcopypaf != NULL)
1139 bug_if (cpl_propertylist_copy_property_regexp(paflist, reflist,
1142 bug_if (cpl_propertylist_append(paflist, qclist));
1145 cpl_propertylist * pcopy = cpl_propertylist_new();
1146 const cpl_error_code error
1147 = cpl_propertylist_copy_property_regexp(pcopy, reflist,
"^("
1148 IRPLIB_PFITS_WCS_REGEXP
1150 if (!error && cpl_propertylist_get_size(pcopy) > 0) {
1151 cpl_msg_warning(cpl_func,
"Combined image will have no WCS "
1154 cpl_propertylist_delete(pcopy);
1157 bug_if(cpl_propertylist_copy_property_regexp(qclist, reflist,
"^("
1158 IRPLIB_PFITS_WCS_REGEXP
1164 return cpl_error_get_code();
1186 static cpl_error_code visir_spectro_qclist_wcal(cpl_propertylist *
self,
1187 int npix,
double xc,
1188 cpl_boolean didshift,
1190 const cpl_polynomial * phdisp,
1191 const cpl_polynomial * xcdisp)
1194 const cpl_size phdegree = cpl_polynomial_get_degree(phdisp);
1195 const cpl_size xcdegree = cpl_polynomial_get_degree(xcdisp);
1197 const double phdisp0 = cpl_polynomial_eval_1d(phdisp, 1.0, NULL);
1198 const double xcdisp0 = cpl_polynomial_eval_1d(xcdisp, 1.0, NULL);
1200 const double xcwlen = cpl_polynomial_eval_1d(xcdisp, 0.5*(
double)npix+0.5,
1202 const double phcdisp = visir_spc_get_dispersion(phdisp, npix/2.0 + 0.5);
1203 const double xccdisp = visir_spc_get_dispersion(xcdisp, npix/2.0 + 0.5);
1208 skip_if (phdegree < 1);
1209 skip_if (xcdegree < 1);
1211 cpl_msg_info(cpl_func,
"Central Dispersion (physical model) [m/pixel]: %g",
1213 cpl_msg_info(cpl_func,
"Central Dispersion (calibrated) [m/pixel]: %g",
1216 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XC", xc));
1219 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XCSHIFT",
1222 bug_if (cpl_propertylist_append_int(
self,
"ESO QC PHDEGREE", phdegree));
1223 bug_if (cpl_propertylist_append_double(
self,
"ESO QC PHDISPX0", phdisp0));
1224 for (i = 1; i <= phdegree; i++) {
1225 const double coeff = cpl_polynomial_get_coeff(phdisp, &i);
1226 char * label = cpl_sprintf(
"ESO QC PHDISPX%d", (
int)i);
1228 bug_if (cpl_propertylist_append_double(
self, label, coeff));
1232 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XCWLEN", xcwlen));
1234 bug_if (cpl_propertylist_append_int(
self,
"ESO QC XCDEGREE", xcdegree));
1235 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XCDISPX0", xcdisp0));
1237 for (i = 1; i <= xcdegree; i++) {
1238 const double coeff = cpl_polynomial_get_coeff(xcdisp, &i);
1239 char * label = cpl_sprintf(
"ESO QC XCDISPX%d", (
int)i);
1241 bug_if (cpl_propertylist_append_double(
self, label, coeff));
1247 return cpl_error_get_code();
1266 static cpl_error_code visir_spectro_qclist_obs(cpl_propertylist *
self,
1267 double xfwhm,
double xcentro)
1273 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XFWHM", xfwhm));
1274 bug_if (cpl_propertylist_append_double(
self,
"ESO QC XCENTROI", xcentro));
1278 return cpl_error_get_code();
1296 static cpl_error_code visir_vector_convolve_symm(cpl_vector *
self,
1297 const cpl_vector * vsymm)
1300 const int npix = cpl_vector_get_size(
self);
1301 const int ihwidth = cpl_vector_get_size(vsymm) - 1;
1302 cpl_vector * raw = cpl_vector_duplicate(
self);
1303 double * pself= cpl_vector_get_data(
self);
1304 double * praw = cpl_vector_get_data(raw);
1305 const double * psymm = cpl_vector_get_data_const(vsymm);
1313 skip_if (ihwidth >= npix);
1316 for (i = 0; i < ihwidth; i++) {
1317 pself[i] = praw[i] * psymm[0];
1318 for (j = 1; j <= ihwidth; j++) {
1319 const int k = i-j < 0 ? 0 : i-j;
1320 pself[i] += (praw[k]+praw[i+j]) * psymm[j];
1325 for (i = ihwidth; i < npix-ihwidth; i++) {
1326 pself[i] = praw[i] * psymm[0];
1327 for (j = 1; j <= ihwidth; j++)
1328 pself[i] += (praw[i-j]+praw[i+j]) * psymm[j];
1331 for (i = npix-ihwidth; i < npix; i++) {
1332 pself[i] = praw[i] * psymm[0];
1333 for (j = 1; j <= ihwidth; j++) {
1334 const int k = i+j > npix-1 ? npix - 1 : i+j;
1335 pself[i] += (praw[k]+praw[i-j]) * psymm[j];
1342 cpl_vector_delete(raw);
1344 return cpl_error_get_code();
1369 cpl_image * visir_spc_flip(
const cpl_image * image,
double wlen,
1370 visir_spc_resol resol, visir_data_type dtype)
1372 cpl_image * flipped = cpl_image_cast(image, CPL_TYPE_DOUBLE);
1378 if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1379 visir_spc_optmod_init(resol, wlen, &ins_settings,
1380 visir_data_is_aqu(dtype))) {
1381 visir_error_set(CPL_ERROR_ILLEGAL_INPUT);
1388 if (visir_data_is_aqu(dtype)) {
1389 skip_if (cpl_image_turn(flipped, 1));
1390 if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1391 visir_spc_optmod_side_is_A(&ins_settings) == 0) {
1392 skip_if (cpl_image_flip(flipped, 0));
1396 else if ((resol != VISIR_SPC_R_HR && resol != VISIR_SPC_R_GHR) ||
1397 visir_spc_optmod_side_is_A(&ins_settings) > 0) {
1399 cpl_msg_info(cpl_func,
"Flipping image");
1401 skip_if (cpl_image_flip(flipped, 0));
1406 if (cpl_error_get_code() && flipped) {
1407 cpl_image_delete(flipped);
1432 static cpl_polynomial * visir_spc_phys_disp(
int npix,
double wlen,
1433 visir_spc_resol resol,
int ioffset,
1437 cpl_polynomial * phdisp = NULL;
1444 const cpl_size i1 = 1;
1445 const cpl_size i0 = 0;
1448 cpl_ensure(resol, CPL_ERROR_ILLEGAL_INPUT, NULL);
1449 cpl_ensure(wlen > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1450 cpl_ensure(npix > 1, CPL_ERROR_ILLEGAL_INPUT, NULL);
1457 cpl_ensure(!visir_spc_optmod_init(resol, wlen, &ins_settings, is_aqu),
1458 CPL_ERROR_ILLEGAL_INPUT, NULL);
1463 dwl = visir_spc_optmod_wlen(&ins_settings, &wlen0, &wlen1);
1465 cpl_ensure(dwl >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1470 if (fabs(dwl) > 2*wlen*DBL_EPSILON) cpl_msg_warning(cpl_func,
"Too large res"
1471 "idual in Scan-Angle determination [meps]: %g", dwl/DBL_EPSILON/wlen);
1473 if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1474 !visir_spc_optmod_side_is_A(&ins_settings)) {
1475 const double swap = wlen1;
1479 cpl_ensure(wlen1 > wlen0, CPL_ERROR_ILLEGAL_INPUT, NULL);
1481 if (resol == VISIR_SPC_R_LRP) {
1482 phdisp = visir_spc_phys_lrp();
1486 phdisp = cpl_polynomial_new(1);
1489 disp = (wlen1-wlen0)/(npix-1);
1493 skip_if (cpl_polynomial_set_coeff(phdisp, &i1, disp));
1495 skip_if (cpl_polynomial_set_coeff(phdisp, &i0, wlen0-disp));
1498 if ((resol == VISIR_SPC_R_HR || resol == VISIR_SPC_R_GHR) &&
1499 !visir_spc_optmod_side_is_A(&ins_settings)) {
1500 cpl_msg_info(cpl_func,
"HR B-side WLMin, WLMax, Disp: %g %g %g", wlen0,
1501 wlen1, cpl_polynomial_get_coeff(phdisp, &i1));
1503 cpl_msg_info(cpl_func,
"WLMin, WLMax, Disp: %g %g %g", wlen0, wlen1,
1504 cpl_polynomial_get_coeff(phdisp, &i1));
1507 if (resol == VISIR_SPC_R_GHR && ioffset != 0) {
1510 const double dispi = visir_spc_optmod_echelle(&ins_settings,
1511 cpl_polynomial_get_coeff(phdisp, &i1), ioffset);
1512 const double wlen0i= visir_spc_optmod_echelle(&ins_settings,
1513 cpl_polynomial_get_coeff(phdisp, &i0), ioffset);
1515 skip_if (cpl_polynomial_set_coeff(phdisp, &i1, dispi));
1517 skip_if (cpl_polynomial_set_coeff(phdisp, &i0, wlen0i));
1519 cpl_msg_info(cpl_func,
"WLc relative error(%d): %g", ioffset,
1520 (wlen0i - cpl_polynomial_eval_1d(phdisp, 1, NULL))/wlen0i);
1526 if (cpl_error_get_code() && phdisp != NULL) {
1527 cpl_polynomial_delete(phdisp);
1552 cpl_bivector * visir_bivector_load_fits(
const char * file,
1553 const char * labelx,
1554 const char * labely,
1558 cpl_bivector * result = NULL;
1559 cpl_table * table = NULL;
1560 cpl_propertylist * extlist = NULL;
1561 cpl_vector * xwrapper;
1562 cpl_vector * ywrapper;
1570 bug_if (extnum < 1);
1572 next = cpl_fits_count_extensions(file);
1573 any_if(
"Could not load FITS table from (extension %d in) file: %s",
1574 extnum, file ? file :
"<NULL>");
1576 skip_if_lt(next, extnum,
"extensions in file: %s", file);
1578 table = cpl_table_load(file, extnum, 0);
1579 any_if (
"Could not load FITS table from extension %d of %d in file: %s",
1580 extnum, next, file ? file :
"<NULL>");
1582 extlist = cpl_propertylist_load_regexp(file, extnum,
"EXTNAME", 0);
1583 if (cpl_propertylist_has(extlist,
"EXTNAME")) {
1584 const char * extname = cpl_propertylist_get_string(extlist,
"EXTNAME");
1585 sext = cpl_sprintf(
" (EXTNAME=%s)", extname);
1588 nlines = cpl_table_get_nrow(table);
1589 skip_if_lt(nlines, 2,
"rows in table from extension %d%s of %d "
1590 "in %s", extnum, sext, next, file);
1592 prowx = cpl_table_get_data_double(table, labelx);
1593 any_if(
"Table from extension %d%s of %d in %s has no column %s",
1594 extnum, sext, next, file, labelx);
1596 prowy = cpl_table_get_data_double(table, labely);
1597 any_if(
"Table from extension %d%s of %d in %s has no column %s",
1598 extnum, sext, next, file, labely);
1600 xwrapper = cpl_vector_wrap(nlines, prowx);
1601 ywrapper = cpl_vector_wrap(nlines, prowy);
1603 result = cpl_bivector_wrap_vectors(xwrapper, ywrapper);
1604 cpl_table_unwrap(table, labelx);
1605 cpl_table_unwrap(table, labely);
1607 cpl_msg_info(cpl_func,
"Read %d rows from extension %d%s of %d "
1608 "in %s [%g;%g]", nlines, extnum, sext, next, file,
1609 cpl_vector_get(xwrapper, 0),
1610 cpl_vector_get(ywrapper, nlines-1));
1615 cpl_table_delete(table);
1616 cpl_propertylist_delete(extlist);
1618 if (result && cpl_error_get_code()) {
1619 cpl_bivector_delete(result);
1656 static cpl_error_code visir_spc_emission(cpl_bivector * emission,
1657 const cpl_vector * boundary,
1658 const cpl_bivector * temiss,
1659 const cpl_bivector * tqeff,
1660 const cpl_vector * vsymm,
1663 cpl_bivector * tqeffi = NULL;
1664 cpl_vector * planck = NULL;
1665 const int npix = cpl_bivector_get_size(emission);
1668 bug_if(emission == NULL);
1669 bug_if(boundary == NULL);
1670 bug_if(temiss == NULL);
1671 bug_if(tqeff == NULL);
1676 skip_if(cpl_vector_get_size(boundary) != npix + 1);
1678 planck = cpl_vector_new(npix);
1683 cpl_photom_fill_blackbody(planck, CPL_UNIT_ENERGYRADIANCE,
1684 cpl_bivector_get_x(emission),
1685 CPL_UNIT_LENGTH, 253);
1687 skip_if (visir_vector_resample(cpl_bivector_get_y(emission),
1691 skip_if (visir_vector_convolve_symm(cpl_bivector_get_y(emission),
1694 skip_if (cpl_vector_multiply(cpl_bivector_get_y(emission), planck));
1698 cpl_photom_fill_blackbody(planck, CPL_UNIT_ENERGYRADIANCE,
1699 cpl_bivector_get_x(emission),
1700 CPL_UNIT_LENGTH, temp);
1703 skip_if (cpl_vector_multiply_scalar(planck, 0.12));
1706 skip_if (cpl_vector_add(cpl_bivector_get_y(emission), planck));
1709 tqeffi = cpl_bivector_duplicate(emission);
1710 skip_if (cpl_bivector_interpolate_linear(tqeffi, tqeff));
1712 skip_if (cpl_vector_multiply(cpl_bivector_get_y(emission),
1713 cpl_bivector_get_y(tqeffi)));
1717 cpl_bivector_delete(tqeffi);
1718 cpl_vector_delete(planck);
1720 return cpl_error_get_code();
1748 static cpl_vector * cpl_spc_convolve_init(
int maxlen,
double slitw,
1749 double fwhm,
int doplot)
1752 const double sigma = fwhm * CPL_MATH_SIG_FWHM;
1753 const int ihtophat = (int)slitw/2;
1754 const int gausshlen = 1 + 5 * sigma + ihtophat < maxlen/2
1755 ? 1 + 5 * sigma + ihtophat : maxlen/2 - 1;
1757 const int convolen = 1 + 10 * sigma + 8*ihtophat;
1758 cpl_vector *
self = cpl_vector_new(gausshlen);
1759 cpl_vector * tophat = cpl_vector_new(convolen);
1763 cpl_image * iself = cpl_image_wrap_double(gausshlen, 1,
1764 cpl_vector_get_data(
self));
1769 skip_if( slitw <= 0.0);
1770 skip_if( fwhm <= 0.0);
1771 skip_if( convolen < 2 * gausshlen);
1774 skip_if (cpl_image_fill_gaussian(iself, 1.0, 1.0, CPL_MATH_SQRT2PI,
1777 if (doplot > 2) visir_vector_plot(
"set grid;",
"t 'Right Half of Gaussian' "
1778 "w linespoints",
"",
self);
1781 skip_if( cpl_vector_fill(tophat, 0.0));
1783 for (i = convolen/2-ihtophat; i < 1+convolen/2+ihtophat; i++)
1784 skip_if (cpl_vector_set(tophat, i, 1.0/(1.0+2.0*ihtophat)));
1787 skip_if (visir_vector_convolve_symm(tophat,
self));
1789 if (doplot > 2) visir_vector_plot(
"set grid;",
"t 'Full Width Convolution' "
1790 "w linespoints",
"", tophat);
1795 memcpy(cpl_vector_get_data(
self),
1796 cpl_vector_get_data(tophat) + convolen/2,
1797 sizeof(
double)*gausshlen);
1800 for (i = 0 ; i < gausshlen; i++)
1801 skip_if (cpl_vector_set(
self, i, cpl_vector_get(tophat,
1807 cpl_msg_info(cpl_func,
"Convolving Model Spectrum, Gauss-sigma=%g, "
1808 "Tophat-width=%d, Truncation-Error=%g with width=%d", sigma,
1810 cpl_vector_get(
self,gausshlen-1)/cpl_vector_get(
self,0),
1813 if (doplot > 1) visir_vector_plot(
"set grid;",
"t 'Right Half of Convolution"
1814 "' w linespoints",
"",
self);
1818 cpl_vector_delete(tophat);
1819 cpl_image_unwrap(iself);
1821 if (cpl_error_get_code()) {
1822 cpl_vector_delete(
self);
1831 static cpl_error_code
1832 fit_gaussians(
const cpl_image * flipped,
const cpl_vector * error,
1833 cpl_size icollo, cpl_size icolhi,
1834 cpl_propertylist * qclist)
1836 cpl_size nrow = cpl_image_get_size_y(flipped);
1837 cpl_size ncol = cpl_image_get_size_x(flipped);
1838 icollo = CX_MAX(1, icollo);
1839 icolhi = CX_MIN(ncol, icolhi);
1840 cpl_errorstate cleanstate = cpl_errorstate_get();
1842 double sigs_err = 0.;
1844 double peaks_err = 0.;
1846 for (cpl_size row = 0; row < nrow; row++) {
1847 const cpl_binary * dmask = cpl_image_get_bpm_const(flipped) ?
1848 cpl_mask_get_data_const(cpl_image_get_bpm_const(flipped)) : NULL;
1849 const double *dflipped = cpl_image_get_data_double_const(flipped);
1850 double * dx = cpl_malloc(ncol *
sizeof(*dx));
1851 double * dy = cpl_malloc(ncol *
sizeof(*dy));
1852 double * dye = cpl_malloc(ncol *
sizeof(*dye));
1857 for (cpl_size i = icollo; i <= icolhi; i++) {
1858 if (dmask == NULL || !dmask[row * ncol + i]) {
1860 dy[n] = dflipped[row * ncol + (i - 1)];
1861 dye[n] = cpl_vector_get(error, (i - 1));
1866 x = cpl_vector_wrap(n, dx);
1867 y = cpl_vector_wrap(n, dy);
1868 ye = cpl_vector_wrap(n, dye);
1869 double x0, sigma, sigma_err, peak, peak_err;
1870 fit_1d_gauss(x, y, ye, &x0, NULL, &peak, &peak_err, &sigma, &sigma_err);
1871 if (cpl_error_get_code() != CPL_ERROR_NONE) {
1872 cpl_msg_debug(cpl_func,
"FIT row %lld failed", row);
1873 cpl_errorstate_set(cleanstate);
1876 sigs[nmeas] = sigma;
1877 peaks[nmeas] = peak;
1878 sigs_err += sigma * sigma;
1879 peaks_err += peak * peak;
1881 cpl_msg_debug(cpl_func,
"FIT row %lld x %g sig %g +- %g "
1883 row, x0, sigma, sigma_err, peak, peak_err);
1885 cpl_vector_delete(x);
1886 cpl_vector_delete(y);
1887 cpl_vector_delete(ye);
1895 cpl_vector * sigv = cpl_vector_wrap(nmeas, sigs);
1896 cpl_vector * peakv = cpl_vector_wrap(nmeas, peaks);
1897 double medsigma = cpl_vector_get_median(sigv);
1898 double medsigma_err = sqrt(sigs_err) * sqrt(CPL_MATH_PI_2) / nmeas;
1899 double medpeak = cpl_vector_get_median(peakv);
1900 double medpeak_err = sqrt(peaks_err) * sqrt(CPL_MATH_PI_2) / nmeas;
1901 cpl_msg_info(cpl_func,
"Median FWHM of spectrum: %g +- %g, Peak %g +- %g",
1902 medsigma, medsigma_err, medpeak, medpeak_err);
1903 cpl_propertylist_append_double(qclist,
"ESO QC GAUSSFIT FWHM",
1905 cpl_propertylist_set_comment(qclist,
"ESO QC GAUSSFIT FWHM",
"[pix]");
1906 cpl_propertylist_append_double(qclist,
"ESO QC GAUSSFIT FWHM_ERR",
1907 medsigma_err * 2.355);
1908 cpl_propertylist_append_double(qclist,
"ESO QC GAUSSFIT PEAK", medpeak);
1909 cpl_propertylist_set_comment(qclist,
"ESO QC GAUSSFIT PEAK",
"[adu/s]");
1910 cpl_propertylist_append_double(qclist,
"ESO QC GAUSSFIT PEAK_ERR",
1912 cpl_vector_unwrap(sigv);
1913 cpl_vector_unwrap(peakv);
1915 return cpl_error_get_code();
1936 static cpl_bivector * visir_spc_extract(cpl_image * flipped,
1937 cpl_propertylist * qclist,
1938 cpl_image ** pweight2d,
1939 int doplot,
int bkgcorrect)
1941 const int ncol = cpl_image_get_size_x(flipped);
1942 const int npix = cpl_image_get_size_y(flipped);
1944 cpl_bivector * result = NULL;
1945 cpl_vector * spectrum = NULL;
1946 cpl_vector * error = NULL;
1947 cpl_vector * col = NULL;
1949 cpl_image * spatial = NULL;
1950 cpl_image * iweight = NULL;
1951 cpl_vector * row = NULL;
1952 cpl_image * imrow = NULL;
1954 double * pweight = NULL;
1956 cpl_apertures * objects = NULL;
1957 cpl_mask * binary = NULL;
1958 cpl_image * locnoise = NULL;
1966 const double sigma = VISIR_SPECTRO_SIGMA;
1968 double stdev2d, min, max, yfwhm;
1969 double weight_2norm;
1971 cpl_size ifwhm, jfwhm;
1974 int ilnoise, ihnoise;
1975 const int is_echelle = ncol <= 2 * (whechelle + 1);
1978 cpl_ensure(pweight2d != NULL, CPL_ERROR_NULL_INPUT, NULL);
1980 cpl_ensure(sigma > 0.0, CPL_ERROR_UNSUPPORTED_MODE, NULL);
1990 cpl_image * imean = cpl_image_duplicate(flipped);
1991 for (
int h = 0; h < cpl_image_get_size_y(flipped); h++) {
1992 cpl_vector * n = cpl_vector_new_from_image_row(flipped, h+1);
1993 double mean = cpl_vector_get_median(n);
1994 cpl_vector_delete(n);
1995 for (
int g = 0; g < cpl_image_get_size_x(flipped); g++) {
1996 cpl_image_set(imean, g+1, h + 1, mean);
1999 cpl_image_subtract(flipped, imean);
2000 cpl_image_delete(imean);
2007 cpl_msg_info(cpl_func,
"Combined image has mean: %g",
2008 cpl_image_get_mean(flipped));
2010 col = cpl_vector_new(npix);
2014 pweight = cpl_image_get_data_double(flipped);
2015 for (j=0; j < npix; j++, pweight += ncol) {
2018 imrow = cpl_image_wrap_double(1, ncol, pweight);
2021 mean = cpl_image_get_mean(imrow);
2024 skip_if (cpl_vector_set(col, j, mean));
2026 skip_if (cpl_image_subtract_scalar(imrow, mean));
2028 (void)cpl_image_unwrap(imrow);
2033 if (doplot > 1) visir_vector_plot(
"set grid;",
"t 'Estimated Background'"
2034 " w linespoints",
"", col);
2035 cpl_vector_delete(col);
2040 stdev2d = visir_img_phot_sigma_clip(flipped)/sqrt(npix);
2043 cpl_msg_info(cpl_func,
"St.Dev. on noise in 2D-combined image: %g",
2047 spatial = cpl_image_collapse_create(flipped, 0);
2049 skip_if (cpl_image_divide_scalar(spatial, npix));
2051 iweight = cpl_image_duplicate(spatial);
2054 sp_median = cpl_image_get_median(spatial);
2055 binary = cpl_mask_threshold_image_create(spatial, sp_median - sigma * stdev2d,
2056 sp_median + sigma * stdev2d);
2058 if (cpl_mask_count(binary) == ncol) {
2059 (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
2060 "%d spatial weights too noisy. sigma=%g. "
2061 "stdev2d=%g. Spatial median=%g", ncol,
2062 sigma, stdev2d, sp_median);
2067 bug_if (cpl_image_reject_from_mask(spatial, binary));
2069 bug_if (cpl_image_get_maxpos(spatial, &ifwhm, &jfwhm));
2072 visir_image_col_plot(
"",
"t 'Most intense column' w linespoints",
2073 "", flipped, ifwhm, ifwhm, 1);
2074 visir_image_row_plot(
"set grid;",
"t 'Combined image with "
2075 "spectral direction collapsed' w linespoints",
2076 "", spatial, 1, 1, 1);
2079 max = cpl_image_get(spatial, ifwhm, 1, &is_rejected);
2080 bug_if(is_rejected);
2082 (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
2083 "Cannot compute FWHM on a collapsed "
2084 "spectrum with a non-positive maximum: %g "
2085 "(at i=%d)", max, (
int)ifwhm);
2089 skip_if (cpl_image_get_fwhm(spatial, ifwhm, 1, &xfwhm, &yfwhm));
2092 for (ilnoise = ifwhm; ilnoise > 0 &&
2093 !cpl_image_is_rejected(spatial, ilnoise, 1); ilnoise--);
2095 for (ihnoise = ifwhm; ihnoise <= ncol &&
2096 !cpl_image_is_rejected(spatial, ihnoise, 1); ihnoise++);
2099 if (!ilnoise) ilnoise = 1;
2100 if (ihnoise > ncol) ihnoise = ncol;
2102 xcentro = cpl_image_get_centroid_x_window(spatial, ilnoise, 1, ihnoise, 1);
2104 cpl_msg_info(cpl_func,
"Spatial FWHM(%d:%d:%d:%g): %g", (
int)ilnoise,
2105 (
int)ifwhm, (
int)ihnoise, xcentro, xfwhm);
2108 skip_if (cpl_image_normalise(iweight, CPL_NORM_ABSFLUX));
2110 if (doplot > 1) visir_image_row_plot(
"set grid;",
"t 'Cleaned, normalized "
2111 "combined image with spectral direction"
2112 " averaged' w linespoints",
"",
2115 weight_2norm = sqrt(cpl_image_get_sqflux(iweight));
2117 cpl_msg_info(cpl_func,
"2-norm of weights: %g", weight_2norm);
2124 int iright = ncol - 5;
2128 if (ileft > xcentro - xfwhm * 2)
2129 ileft = xcentro - xfwhm * 2;
2130 if (iright < xcentro + xfwhm * 2)
2131 iright = xcentro + xfwhm * 2;
2133 cpl_msg_info(cpl_func,
"HRG pixels of noise: [1 %d] [%d %d]", ileft,
2136 bug_if(cpl_mask_xor(binary, binary));
2138 pbin = cpl_mask_get_data(binary);
2141 for (i = 0; i < ncol; i++) pbin[i] = CPL_BINARY_0;
2142 for (i = 0; i < ileft; i++) pbin[i] = CPL_BINARY_1;
2143 for (i = iright; i < ncol; i++) pbin[i] = CPL_BINARY_1;
2148 mspix = cpl_mask_count(binary);
2149 cpl_msg_info(cpl_func,
"Pixels of noise(%g +/- %g*%g): %d",
2150 sp_median, stdev2d, sigma, mspix);
2155 cpl_msg_error(cpl_func,
"Cannot estimate spectrum noise with just %d "
2156 "pixels of noise", mspix);
2157 visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
2161 locnoise = cpl_image_new_from_mask(binary);
2162 cpl_mask_delete(binary);
2167 error = cpl_vector_new(npix);
2172 for (j=0; j < npix; j++) {
2174 double npp, stdev1d;
2177 imrow = cpl_image_extract(flipped, 1, j+1, ncol, j+1);
2181 objects = cpl_apertures_new_from_image(imrow, locnoise);
2182 cpl_image_delete(imrow);
2187 stdev1d = cpl_apertures_get_stdev(objects, 1);
2188 cpl_apertures_delete(objects);
2196 npp = weight_2norm * stdev1d;
2198 skip_if (cpl_vector_set(error, j, npp));
2205 for (i=1; i <= ncol; i++) {
2206 const double weight = cpl_image_get(iweight, i, 1, &is_rejected);
2211 visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
2216 if (weight == 0)
continue;
2218 col = cpl_vector_new_from_image_column(flipped, i);
2221 skip_if (cpl_vector_multiply_scalar(col, weight));
2223 if (spectrum == NULL) {
2226 skip_if (cpl_vector_add(spectrum, col));
2227 cpl_vector_delete(col);
2232 fit_gaussians(flipped, error, ifwhm - 20, ifwhm + 20, qclist);
2236 min = cpl_vector_get_min(spectrum);
2237 if (min <0) cpl_msg_warning(cpl_func,
"Extracted spectrum has negative "
2238 "intensity: %g", min);
2243 *pweight2d = cpl_image_new(ncol, npix, CPL_TYPE_DOUBLE);
2245 for (j=1; j <= npix; j++)
2246 skip_if (cpl_image_copy(*pweight2d, iweight, 1, j));
2248 if (doplot > 0) visir_image_plot(
"",
"t 'The weight map'",
"", *pweight2d);
2250 bug_if(visir_spectro_qclist_obs(qclist, xfwhm, xcentro));
2254 cpl_image_delete(locnoise);
2255 cpl_mask_delete(binary);
2256 cpl_image_delete(spatial);
2257 cpl_apertures_delete(objects);
2258 cpl_vector_delete(col);
2259 cpl_vector_delete(row);
2260 cpl_image_delete(imrow);
2261 cpl_image_delete(iweight);
2263 if (cpl_error_get_code()) {
2264 cpl_vector_delete(spectrum);
2265 cpl_vector_delete(error);
2268 result = cpl_bivector_wrap_vectors(spectrum, error);
2270 if (doplot > 2) visir_bivector_plot(
"",
"t 'error versus spectrum'",
2300 static cpl_error_code visir_spectro_fill(cpl_vector *
self,
2301 const cpl_polynomial * disp,
2306 const cpl_size npix = cpl_vector_get_size(
self);
2308 cpl_ensure_code(
self, CPL_ERROR_NULL_INPUT);
2309 cpl_ensure_code(model, CPL_ERROR_NULL_INPUT);
2310 cpl_ensure_code(disp, CPL_ERROR_NULL_INPUT);
2312 cpl_vector * wavelength = cpl_vector_new(npix);
2313 cpl_bivector * emission = cpl_bivector_wrap_vectors(wavelength,
self);
2314 cpl_vector * boundary = cpl_vector_new(npix + 1);
2318 skip_if (cpl_vector_fill_polynomial(cpl_bivector_get_x(emission),
2320 skip_if (cpl_vector_fill_polynomial(boundary, disp, 0.5, 1));
2323 skip_if (visir_spc_emission(emission, boundary, mymodel->lines,
2324 mymodel->tqeff, mymodel->vsymm,
2328 cpl_bivector_unwrap_vectors(emission);
2329 cpl_vector_delete(wavelength);
2330 cpl_vector_delete(boundary);
2332 return cpl_error_get_code();
2353 static cpl_error_code visir_spectro_refine(cpl_polynomial *
self,
2354 const cpl_vector * xc_vector,
2356 const cpl_polynomial * phdisp,
2357 int hsize, cpl_boolean doplot,
2359 cpl_boolean * pdidshift,
2362 const int subres = VISIR_XC_SUBSEARCH;
2363 cpl_polynomial * shifted = NULL;
2364 #ifdef VISIR_SPC_CAL_HIGH
2365 const int fitdeg = 2;
2366 double pixstep = 0.5;
2367 double pixtol = 1e-5;
2368 const int maxite = fitdeg * 200;
2371 const int clines = (int)(cpl_bivector_get_size(pmymodel->lines) *
2372 cpl_vector_get_size(xc_vector));
2373 cpl_errorstate prestate = cpl_errorstate_get();
2376 cpl_ensure_code(
self, CPL_ERROR_NULL_INPUT);
2377 cpl_ensure_code(xc_vector, CPL_ERROR_NULL_INPUT);
2378 cpl_ensure_code(pmymodel, CPL_ERROR_NULL_INPUT);
2379 cpl_ensure_code(phdisp, CPL_ERROR_NULL_INPUT);
2380 cpl_ensure_code(pxc, CPL_ERROR_NULL_INPUT);
2381 cpl_ensure_code(pdidshift, CPL_ERROR_NULL_INPUT);
2382 cpl_ensure_code(pdelta, CPL_ERROR_NULL_INPUT);
2384 skip_if(cpl_polynomial_copy(
self, phdisp));
2386 #ifdef VISIR_SPC_CAL_HIGH
2388 (
self, fitdeg, xc_vector, 1, clines,
2390 visir_spectro_fill, pixtol, pixstep,
2391 hsize, maxite, maxfail, maxcont, doplot, pxc) || *pxc <= 0.0) {
2393 irplib_error_recover(prestate,
"Could not optimize %d "
2394 "coefficients, trying shifting", fitdeg);
2395 skip_if(cpl_polynomial_copy(
self, phdisp));
2397 skip_if(visir_polynomial_shift_1d_from_correlation
2399 visir_spectro_fill, hsize, subres, doplot, pxc, pdelta));
2400 *pdidshift = CPL_TRUE;
2403 shifted = cpl_polynomial_duplicate(
self);
2406 (
self, fitdeg, xc_vector, 1, clines,
2408 visir_spectro_fill, pixtol, pixstep,
2409 hsize, maxite, maxfail, maxcont, doplot, pxc) || *pxc <= 0.0) {
2411 irplib_error_recover(prestate,
"Could not re-optimize %d "
2412 "coefficients, keeping shifted", fitdeg);
2413 skip_if(cpl_polynomial_copy(
self, shifted));
2418 skip_if(visir_polynomial_shift_1d_from_correlation
2420 visir_spectro_fill, hsize, subres, doplot, pxc, pdelta));
2421 *pdidshift = CPL_TRUE;
2424 error_if (*pxc <= 0.0, CPL_ERROR_DATA_NOT_FOUND,
"Atmospheric and Model "
2425 "Spectra have non-positive cross-correlation (%g pixel shift): "
2426 "%g", *pdelta, *pxc);
2430 cpl_polynomial_delete(shifted);
2432 return cpl_error_get_code();
2460 static cpl_error_code
2461 visir_polynomial_shift_1d_from_correlation(cpl_polynomial *
self,
2462 const cpl_vector * obs,
2464 cpl_error_code (*filler)
2466 const cpl_polynomial *,
2471 double * pxc,
double *pshift)
2473 const int nobs = cpl_vector_get_size(obs);
2474 cpl_polynomial * cand = NULL;
2475 cpl_bivector * xcplot = NULL;
2476 double * xcplotx = NULL;
2477 double * xcploty = NULL;
2478 cpl_vector * mspec1d = NULL;
2480 double bestxc = -1.0;
2481 double bestdelta = -1.0;
2485 cpl_ensure_code(
self != NULL, CPL_ERROR_NULL_INPUT);
2486 cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
2487 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
2488 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
2489 cpl_ensure_code(subres > 0, CPL_ERROR_ILLEGAL_INPUT);
2490 cpl_ensure_code(hsize > 0, CPL_ERROR_ILLEGAL_INPUT);
2492 cand = cpl_polynomial_new(1);
2493 mspec1d = cpl_vector_new(2 * hsize + nobs);
2494 vxc = cpl_vector_new(2 * hsize + 1);
2496 xcplot = cpl_bivector_new(subres * (2 * hsize + 1));
2497 xcplotx = cpl_bivector_get_x_data(xcplot);
2498 xcploty = cpl_bivector_get_y_data(xcplot);
2502 for (i = 0; i < subres; i++) {
2503 const double delta = i / (double)subres;
2507 bug_if (cpl_polynomial_copy(cand,
self));
2508 bug_if (cpl_polynomial_shift_1d(cand, 0, delta - hsize));
2510 skip_if (filler(mspec1d, cand, model));
2512 ixc = cpl_vector_correlate(vxc, mspec1d, obs);
2513 xc = cpl_vector_get(vxc, ixc);
2517 bestxxc = ixc - hsize;
2518 bestdelta = delta + bestxxc;
2519 cpl_msg_debug(cpl_func,
"Shifting %g = %d + %g pixels (XC=%g)",
2520 bestdelta, bestxxc, delta, bestxc);
2524 for (j = 0; j <= 2 * hsize; j++) {
2525 const double xcj = cpl_vector_get(vxc, j);
2526 xcplotx[i + j * subres] = (double)(j - hsize) + delta;
2527 xcploty[i + j * subres] = xcj;
2532 #ifdef IRPLIB_SPC_DUMP
2534 irplib_polynomial_dump_corr_step(
self, vxc,
"Shift");
2537 skip_if(cpl_polynomial_shift_1d(
self, 0, bestdelta));
2540 cpl_vector_set_size(vxc, 1);
2541 cpl_vector_set_size(mspec1d, nobs);
2542 skip_if (filler(mspec1d,
self, model));
2543 bug_if(cpl_vector_correlate(vxc, mspec1d, obs));
2546 char * title = cpl_sprintf(
"t 'Cross-correlation of %d-pixel spectrum "
2547 "(max=%.4g at %g pixel)' w points", nobs,
2548 cpl_vector_get(vxc, 0), bestdelta);
2550 cpl_plot_bivector(
"set grid;set xlabel 'Offset [pixel]';set ylabel "
2551 "'Cross-correlation';", title,
"", xcplot);
2557 cpl_msg_info(cpl_func,
"Shifting %g = %d + %g pixels (XC: %g <=> %g)",
2558 bestdelta, bestxxc, bestdelta - (
double)bestxxc,
2559 cpl_vector_get(vxc, 0), bestxc);
2561 if (pxc != NULL) *pxc = cpl_vector_get(vxc, 0);
2562 if (pshift != NULL) *pshift = bestdelta;
2566 cpl_vector_delete(mspec1d);
2567 cpl_polynomial_delete(cand);
2568 cpl_vector_delete(vxc);
2569 cpl_bivector_delete(xcplot);
2571 return cpl_error_get_code();
2581 static cpl_polynomial * visir_spc_phys_lrp(
void)
2584 const double xval[] = {146, 191, 223, 355, 470, 649, 669, 747, 792};
2585 const double yval[] = {8.200e-06, 8.3e-06, 9.50e-06,
2586 9.660e-06, 11.e-06, 11.7e-06,
2587 12.54e-06, 12.76e-06, 13.02e-06 };
2594 const cpl_size maxdeg1d = 2;
2596 cpl_polynomial *
self = cpl_polynomial_new(1);
2597 const cpl_boolean sampsym = CPL_FALSE;
2598 const size_t nvals =
sizeof(xval)/
sizeof(*xval);
2600 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual)
2601 cpl_matrix * xmatrix = cpl_matrix_wrap(1, nvals, (
double*)xval);
2602 cpl_vector * yvector = cpl_vector_wrap(nvals, (
double*)yval);
2603 IRPLIB_DIAG_PRAGMA_POP;
2604 cpl_vector * fitres = cpl_vector_new(nvals);
2606 const cpl_error_code error = cpl_polynomial_fit(self, xmatrix, &sampsym,
2608 CPL_FALSE, NULL, &maxdeg1d)
2609 || cpl_vector_fill_polynomial_fit_residual(fitres, yvector, NULL, self,
2612 const
double mse = cpl_vector_product(fitres, fitres) / (
double)nvals;
2614 (
void)cpl_matrix_unwrap(xmatrix);
2615 (
void)cpl_vector_unwrap(yvector);
2616 cpl_vector_delete(fitres);
2619 cpl_error_set_where(cpl_func);
2620 cpl_polynomial_delete(
self);
2623 cpl_msg_info(cpl_func,
"Fitted %d degree 1D-polynomial to %u "
2624 "wavelengths with a root mean square error [m]: %g",
2625 (
int)maxdeg1d, (
unsigned)nvals, sqrt(mse));
2639 static double visir_spc_get_dispersion(
const cpl_polynomial *
self,
double xval)
2642 cpl_errorstate prestate = cpl_errorstate_get();
2645 (void)cpl_polynomial_eval_1d(
self, xval, &disp);
2647 if (!cpl_errorstate_is_equal(prestate)) {
2648 (void)cpl_error_set_where(cpl_func);
double visir_pfits_get_slitwidth(const cpl_propertylist *self)
The slit width in Arcseconds.
const char * visir_pfits_get_resol(const cpl_propertylist *self)
The spectral resolution.
int visir_parameterlist_get_int(const cpl_parameterlist *self, const char *recipe, visir_parameter bitmask)
Retrieve the value of a VISIR integer parameter.
const cpl_propertylist * irplib_framelist_get_propertylist_const(const irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
cpl_error_code irplib_plot_spectrum_and_model(const cpl_vector *self, const cpl_polynomial *disp1d, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *))
Plot a 1D spectrum and one from a model.
double visir_pfits_get_pixspace(const cpl_propertylist *self)
The pixel spacing.
cpl_error_code irplib_polynomial_find_1d_from_correlation_all(cpl_polynomial *self, int maxdeg, const cpl_vector *obs, int nmaxima, int linelim, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *), double pixtol, double pixstep, int hsize, int maxite, int maxfail, int maxcont, cpl_boolean doplot, double *pxc)
Modify self by maximizing the cross-correlation across all maxima.
double visir_pfits_get_wlen(const cpl_propertylist *self)
The central wavelength.
const cpl_frame * irplib_framelist_get_const(const irplib_framelist *self, int pos)
Get the specified frame from the framelist.
double visir_pfits_get_pixscale(const cpl_propertylist *self)
The pixel scale.
cpl_error_code irplib_framelist_contains(const irplib_framelist *self, const char *key, cpl_type type, cpl_boolean is_equal, double fp_tol)
Verify that a property is present for all frames.
double visir_pfits_get_temp(const cpl_propertylist *self)
The telescope (M1) temperature [Celcius].
int irplib_framelist_get_size(const irplib_framelist *self)
Get the size of a framelist.