28#include "irplib_wavecal_impl.h"
31#include "irplib_utils.h"
37#include <gsl/gsl_multimin.h>
48 const cpl_vector *observed;
49 cpl_polynomial *disp1d;
51 irplib_base_spectrum_model *param;
52 cpl_error_code (*filler)(cpl_vector *,
53 const cpl_polynomial *,
54 irplib_base_spectrum_model *);
59 cpl_polynomial *mdisp;
74#define IRPLIB_MAX(A, B) ((A) > (B) ? (A) : (B))
75#define IRPLIB_MIN(A, B) ((A) < (B) ? (A) : (B))
82static double irplib_gsl_correlation(
const gsl_vector *,
void *);
85static cpl_error_code irplib_polynomial_find_1d_from_correlation_(
89 irplib_base_spectrum_model *,
90 cpl_error_code (*)(cpl_vector *,
91 const cpl_polynomial *,
92 irplib_base_spectrum_model *),
124 const int nself = cpl_bivector_get_size(self);
125 const double *px = cpl_bivector_get_x_data_const(self);
126 const double *py = cpl_bivector_get_y_data_const(self);
130 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, -1);
131 cpl_ensure(x_min <= x_max, CPL_ERROR_ILLEGAL_INPUT, -2);
134 while (i < nself && px[i] < x_min)
136 while (i < nself && px[i] < x_max)
156 const cpl_image *imgwave,
160 const int nx = cpl_image_get_size_x(imgwave);
161 const int ny = cpl_image_get_size_y(imgwave);
162 const int nbad = cpl_image_count_rejected(imgwave);
163 const int nsamp = nx * ny - nbad;
169 const cpl_size nfitdeg = (cpl_size)fitdeg;
173 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
174 cpl_ensure_code(imgwave != NULL, CPL_ERROR_NULL_INPUT);
175 cpl_ensure_code(presid != NULL, CPL_ERROR_NULL_INPUT);
176 cpl_ensure_code(fitdeg > 0, CPL_ERROR_ILLEGAL_INPUT);
178 cpl_ensure_code(cpl_polynomial_get_dimension(self) == 2,
179 CPL_ERROR_ILLEGAL_INPUT);
181 xy_pos = cpl_matrix_new(2, nsamp);
182 xdata = cpl_matrix_get_data(xy_pos);
183 ydata = xdata + nsamp;
185 dwlen = (
double *)cpl_malloc(nsamp *
sizeof(
double));
186 wlen = cpl_vector_wrap(nsamp, dwlen);
188 for (i = 1; i <= nx; i++) {
189 for (j = 1; j <= ny; j++) {
191 const double value = cpl_image_get(imgwave, i, j, &is_bad);
201 cpl_msg_info(cpl_func,
202 "Fitting 2D polynomial to %d X %d image, ignoring "
203 "%d poorly calibrated pixels",
206 if (cpl_polynomial_fit(self, xy_pos, NULL, wlen, NULL, CPL_FALSE, NULL,
207 &nfitdeg) == CPL_ERROR_NONE &&
209 cpl_vector_fill_polynomial_fit_residual(wlen, wlen, NULL, self, xy_pos,
211 *presid = cpl_vector_product(wlen, wlen) / nsamp;
213 cpl_matrix_delete(xy_pos);
214 cpl_vector_delete(wlen);
216 cpl_ensure_code(k == nsamp, CPL_ERROR_UNSPECIFIED);
218 return CPL_ERROR_NONE;
243 cpl_polynomial *self,
245 const cpl_vector *obs,
246 irplib_base_spectrum_model *model,
247 cpl_error_code (*filler)(cpl_vector *,
248 const cpl_polynomial *,
249 irplib_base_spectrum_model *),
256 cpl_boolean restart = CPL_FALSE;
257 const cpl_error_code error =
258 irplib_polynomial_find_1d_from_correlation_(self, maxdeg, obs, model,
259 filler, pixtol, pixstep,
263 return error ? cpl_error_set_where(cpl_func)
264 : (restart ? cpl_error_set(cpl_func, CPL_ERROR_CONTINUE)
291irplib_polynomial_find_1d_from_correlation_(
292 cpl_polynomial *self,
294 const cpl_vector *obs,
295 irplib_base_spectrum_model *model,
296 cpl_error_code (*filler)(cpl_vector *,
297 const cpl_polynomial *,
298 irplib_base_spectrum_model *),
304 cpl_boolean *prestart)
307 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
308 gsl_multimin_fminimizer *minimizer;
309 gsl_multimin_function my_func;
310 irplib_multimin data;
312 gsl_vector *stepsize;
313 gsl_vector *dispprev;
314 int status = GSL_CONTINUE;
315 const int nobs = cpl_vector_get_size(obs);
316 const cpl_size nfit = maxdeg + 1;
317 cpl_errorstate prestate = cpl_errorstate_get();
319 const double wlstep =
320 cpl_polynomial_eval_1d_diff(self, 0.5 * (nobs + pixstep),
321 0.5 * (nobs - pixstep), NULL);
322 double wlstepi = wlstep;
328 cpl_ensure_code(prestart != NULL, CPL_ERROR_NULL_INPUT);
329 *prestart = CPL_FALSE;
330 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
331 cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
332 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
333 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
334 cpl_ensure_code(pxc != NULL, CPL_ERROR_NULL_INPUT);
336 cpl_ensure_code(cpl_polynomial_get_dimension(self) == 1,
337 CPL_ERROR_ILLEGAL_INPUT);
339 cpl_ensure_code(cpl_polynomial_get_degree(self) > 0,
340 CPL_ERROR_ILLEGAL_INPUT);
342 cpl_ensure_code(maxdeg >= 0, CPL_ERROR_ILLEGAL_INPUT);
343 cpl_ensure_code(pixtol > 0.0, CPL_ERROR_ILLEGAL_INPUT);
344 cpl_ensure_code(pixstep > 0.0, CPL_ERROR_ILLEGAL_INPUT);
345 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
346 cpl_ensure_code(maxite >= 0, CPL_ERROR_ILLEGAL_INPUT);
349 return cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
350 "GSL is not available");
353 minimizer = gsl_multimin_fminimizer_alloc(T, (
size_t)nfit);
355 cpl_ensure_code(minimizer != NULL, CPL_ERROR_ILLEGAL_OUTPUT);
357 dispgsl = gsl_vector_alloc((
size_t)nfit);
358 stepsize = gsl_vector_alloc((
size_t)nfit);
359 dispprev = gsl_vector_alloc((
size_t)nfit);
361 for (i = 0; i < nfit; i++) {
362 const double value = cpl_polynomial_get_coeff(self, &i);
363 gsl_vector_set(dispgsl, (
size_t)i, value);
364 gsl_vector_set(stepsize, (
size_t)i, wlstepi);
365 wlstepi /= (double)nobs;
369 my_func.f = &irplib_gsl_correlation;
370 my_func.params = (
void *)(&data);
374 data.spectrum = cpl_vector_new(nobs + 2 * hsize);
375 data.vxc = cpl_vector_new(1 + 2 * hsize);
378 data.filler = filler;
384 gsl_multimin_fminimizer_set(minimizer, &my_func, dispgsl, stepsize);
386 for (iter = 0; status == GSL_CONTINUE && iter < maxite; iter++) {
388 const double fprev = minimizer->fval;
390 gsl_vector_memcpy(dispprev, minimizer->x);
391 status = gsl_multimin_fminimizer_iterate(minimizer);
393 if (status || !cpl_errorstate_is_equal(prestate))
396 size = gsl_multimin_fminimizer_size(minimizer);
397 status = gsl_multimin_test_size(size, pixtol);
399 if (status == GSL_SUCCESS) {
400 cpl_msg_debug(cpl_func,
"converged to minimum at");
403 cpl_msg_debug(cpl_func,
"%5d %g df() = %g size = %g", iter,
404 gsl_vector_get(minimizer->x, 0) -
405 gsl_vector_get(dispprev, 0),
406 minimizer->fval - fprev, size);
408 else if (nfit == 1) {
409 cpl_msg_debug(cpl_func,
"%5d %g %g df() = %g size = %g", iter,
410 gsl_vector_get(minimizer->x, 0) -
411 gsl_vector_get(dispprev, 0),
412 gsl_vector_get(minimizer->x, 1) -
413 gsl_vector_get(dispprev, 1),
414 minimizer->fval - fprev, size);
417 cpl_msg_debug(cpl_func,
"%5d %g %g %g df() = %g size = %g",
419 gsl_vector_get(minimizer->x, 0) -
420 gsl_vector_get(dispprev, 0),
421 gsl_vector_get(minimizer->x, 1) -
422 gsl_vector_get(dispprev, 1),
423 gsl_vector_get(minimizer->x, 2) -
424 gsl_vector_get(dispprev, 2),
425 minimizer->fval - fprev, size);
430 if (status == GSL_SUCCESS && cpl_errorstate_is_equal(prestate)) {
431 if (data.mxc > -minimizer->fval) {
433 cpl_msg_warning(cpl_func,
"Local maximum: %g(%d) > %g", data.mxc,
434 data.ishift, -minimizer->fval);
435 cpl_polynomial_shift_1d(data.mdisp, 0, (
double)data.ishift);
436 cpl_polynomial_copy(self, data.mdisp);
437 *prestart = CPL_TRUE;
440 *pxc = -minimizer->fval;
441 for (i = 0; i < nfit; i++) {
442 const double value = gsl_vector_get(minimizer->x, i);
443 cpl_polynomial_set_coeff(self, &i, value);
448 cpl_vector_delete(data.spectrum);
449 cpl_vector_delete(data.vxc);
450 cpl_polynomial_delete(data.mdisp);
451 gsl_multimin_fminimizer_free(minimizer);
452 gsl_vector_free(dispgsl);
453 gsl_vector_free(dispprev);
454 gsl_vector_free(stepsize);
456 cpl_ensure_code(status != GSL_CONTINUE, CPL_ERROR_CONTINUE);
457 cpl_ensure_code(status == GSL_SUCCESS, CPL_ERROR_DATA_NOT_FOUND);
458 cpl_ensure_code(cpl_errorstate_is_equal(prestate), cpl_error_get_code());
460 return CPL_ERROR_NONE;
496 const cpl_polynomial *disp,
497 irplib_base_spectrum_model *lsslamp)
499 irplib_line_spectrum_model *arclamp = (irplib_line_spectrum_model *)lsslamp;
500 cpl_error_code error;
502 cpl_ensure_code(arclamp != NULL, CPL_ERROR_NULL_INPUT);
508 arclamp->erftmp, disp,
509 arclamp->lines, arclamp->wslit,
510 arclamp->wfwhm, arclamp->xtrunc,
511 0, CPL_FALSE, CPL_FALSE,
513 cpl_ensure_code(!error, error);
517 return CPL_ERROR_NONE;
536 const cpl_polynomial *disp,
537 irplib_base_spectrum_model *lsslamp)
539 irplib_line_spectrum_model *arclamp = (irplib_line_spectrum_model *)lsslamp;
540 cpl_error_code error;
542 cpl_ensure_code(arclamp != NULL, CPL_ERROR_NULL_INPUT);
548 arclamp->erftmp, disp,
549 arclamp->lines, arclamp->wslit,
550 arclamp->wfwhm, arclamp->xtrunc,
551 0, CPL_FALSE, CPL_TRUE,
553 cpl_ensure_code(!error, error);
557 return CPL_ERROR_NONE;
577 const cpl_polynomial *disp,
578 irplib_base_spectrum_model *lsslamp)
580 irplib_line_spectrum_model *arclamp = (irplib_line_spectrum_model *)lsslamp;
581 cpl_error_code error;
583 cpl_ensure_code(arclamp != NULL, CPL_ERROR_NULL_INPUT);
589 arclamp->erftmp, disp,
590 arclamp->lines, arclamp->wslit,
591 arclamp->wfwhm, arclamp->xtrunc,
592 0, CPL_TRUE, CPL_FALSE,
594 cpl_ensure_code(!error, error);
598 return CPL_ERROR_NONE;
617 const cpl_polynomial *disp,
618 irplib_base_spectrum_model *lsslamp)
620 irplib_line_spectrum_model *arclamp = (irplib_line_spectrum_model *)lsslamp;
621 cpl_error_code error;
623 cpl_ensure_code(arclamp != NULL, CPL_ERROR_NULL_INPUT);
629 arclamp->erftmp, disp,
630 arclamp->lines, arclamp->wslit,
631 arclamp->wfwhm, arclamp->xtrunc,
632 0, CPL_TRUE, CPL_TRUE,
634 cpl_ensure_code(!error, error);
638 return CPL_ERROR_NONE;
655 const cpl_vector *self,
656 const cpl_polynomial *disp1d,
657 irplib_base_spectrum_model *model,
658 cpl_error_code (*filler)(cpl_vector *,
659 const cpl_polynomial *,
660 irplib_base_spectrum_model *))
662 cpl_errorstate prestate = cpl_errorstate_get();
664 cpl_vector *spectrum;
666 const int len = cpl_vector_get_size(self);
671 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
672 cpl_ensure_code(disp1d != NULL, CPL_ERROR_NULL_INPUT);
673 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
674 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
676 cpl_ensure_code(cpl_polynomial_get_dimension(disp1d) == 1,
677 CPL_ERROR_ILLEGAL_INPUT);
679 cpl_ensure_code(cpl_polynomial_get_degree(disp1d) > 0,
680 CPL_ERROR_ILLEGAL_INPUT);
682 wl = cpl_vector_new(len);
683 spectrum = cpl_vector_new(len);
684 vxc = cpl_vector_new(1);
686 error |= (int)cpl_vector_fill_polynomial(wl, disp1d, 1.0, 1.0);
687 error |= filler(spectrum, disp1d, model);
689 ixc = cpl_vector_correlate(vxc, self, spectrum);
690 xc = cpl_vector_get(vxc, ixc);
692 maxval = cpl_vector_get_max(spectrum);
694 error |= cpl_vector_multiply_scalar(spectrum,
695 cpl_vector_get_max(self) / maxval);
697 const cpl_vector *spair[] = {wl, self, spectrum};
699 cpl_sprintf(
"set grid;set xlabel 'Wavelength (%g -> %g)'; "
700 "set ylabel 'Intensity';",
701 cpl_vector_get(wl, 0), cpl_vector_get(wl, len - 1));
702 char *title = cpl_sprintf(
"t 'Observed and modelled spectra (%d pixel "
703 "XC=%g) ' w linespoints",
706 (void)cpl_plot_vectors(pre, title,
"", spair, 3);
711 cpl_vector_delete(wl);
712 cpl_vector_delete(spectrum);
713 cpl_vector_delete(vxc);
715 cpl_errorstate_set(prestate);
717 return CPL_ERROR_NONE;
744 const cpl_polynomial *disp,
745 const cpl_vector *obs,
746 irplib_base_spectrum_model *model,
747 cpl_error_code (*filler)(cpl_vector *,
748 const cpl_polynomial *,
749 irplib_base_spectrum_model *),
754 const int nobs = cpl_vector_get_size(obs);
755 const int nmodel = 2 * hsize + nobs;
756 cpl_polynomial *shdisp;
757 cpl_vector *xself = cpl_bivector_get_x(self);
758 cpl_vector *yself = cpl_bivector_get_y(self);
761 cpl_error_code error = CPL_ERROR_NONE;
762 double xcprev, xcnext;
766 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
767 cpl_ensure_code(disp != NULL, CPL_ERROR_NULL_INPUT);
768 cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
769 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
770 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
771 cpl_ensure_code(hsize > 0, CPL_ERROR_ILLEGAL_INPUT);
773 shdisp = cpl_polynomial_duplicate(disp);
776 if (cpl_polynomial_shift_1d(shdisp, 0, -hsize)) {
777 cpl_polynomial_delete(shdisp);
778 return cpl_error_set_where(cpl_func);
781 mspec1d = cpl_vector_new(nmodel);
783 if (filler(mspec1d, shdisp, model)) {
784 cpl_vector_delete(mspec1d);
785 return cpl_error_set_where(cpl_func);
789 xcorr = cpl_vector_new(1 + 2 * hsize);
790 ixc = cpl_vector_correlate(xcorr, mspec1d, obs);
792#ifdef IRPLIB_SPC_DUMP
794 irplib_polynomial_dump_corr_step(shdisp, xcorr,
"Shift");
797 cpl_vector_delete(mspec1d);
798 cpl_polynomial_delete(shdisp);
803 xcprev = cpl_vector_get(xcorr, i);
804 xcnext = cpl_vector_get(xcorr, i + 1);
806 if (xcprev >= xcnext) {
812 cpl_vector_set(xself, 0, i - hsize);
813 cpl_vector_set(yself, 0, xcprev);
816 for (i = 1; i < 2 * hsize; i++) {
817 const double xc = xcnext;
818 xcnext = cpl_vector_get(xcorr, i + 1);
819 if (xc >= xcprev && xc >= xcnext) {
825 if (cpl_bivector_get_size(self) < imax) {
826 cpl_vector_set_size(xself, imax);
827 cpl_vector_set_size(yself, imax);
830 for (j = imax - 1; j > 0; j--) {
831 if (xc <= cpl_vector_get(yself, j - 1))
833 cpl_vector_set(xself, j, cpl_vector_get(xself, j - 1));
834 cpl_vector_set(yself, j, cpl_vector_get(yself, j - 1));
836 cpl_vector_set(xself, j, i - hsize);
837 cpl_vector_set(yself, j, xc);
844 if (xcnext >= xcprev) {
852 if (cpl_bivector_get_size(self) < imax) {
853 cpl_vector_set_size(xself, imax);
854 cpl_vector_set_size(yself, imax);
857 for (j = imax - 1; j > 0; j--) {
858 if (xcnext <= cpl_vector_get(yself, j - 1))
860 cpl_vector_set(xself, j, cpl_vector_get(xself, j - 1));
861 cpl_vector_set(yself, j, cpl_vector_get(yself, j - 1));
863 cpl_vector_set(xself, j, i - hsize);
864 cpl_vector_set(yself, j, xcnext);
869 cpl_vector *xvals = cpl_vector_new(1 + 2 * hsize);
870 cpl_bivector *bcorr = cpl_bivector_wrap_vectors(xvals, xcorr);
871 double x = (double)-hsize;
873 cpl_sprintf(
"t 'Cross-correlation of shifted %d-pixel "
874 "spectrum (XCmax=%g at %d)' w linespoints",
875 nobs, cpl_vector_get(xcorr, ixc), ixc - hsize);
877 for (i = 0; i < 1 + 2 * hsize; i++, x += 1.0) {
878 cpl_vector_set(xvals, i, x);
881 cpl_plot_bivector(
"set grid;set xlabel 'Offset [pixel]';", title,
"",
883 cpl_bivector_unwrap_vectors(bcorr);
884 cpl_vector_delete(xvals);
889 *pxc = cpl_vector_get(xcorr, hsize);
891 cpl_vector_delete(xcorr);
894 error = CPL_ERROR_DATA_NOT_FOUND;
896 else if (cpl_bivector_get_size(self) > imax) {
897 cpl_vector_set_size(xself, imax);
898 cpl_vector_set_size(yself, imax);
902 return cpl_error_set(cpl_func, error);
921 cpl_polynomial *self,
922 const cpl_vector *obs,
923 irplib_base_spectrum_model *model,
924 cpl_error_code (*filler)(cpl_vector *,
925 const cpl_polynomial *,
926 irplib_base_spectrum_model *),
931 const int nobs = cpl_vector_get_size(obs);
932 const int nmodel = 2 * hsize + nobs;
935 cpl_error_code error;
939 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
940 cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
941 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
942 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
943 cpl_ensure_code(hsize > 0, CPL_ERROR_ILLEGAL_INPUT);
946 cpl_ensure_code(!cpl_polynomial_shift_1d(self, 0, -hsize),
947 cpl_error_get_code());
949 mspec1d = cpl_vector_new(nmodel);
951 if (filler(mspec1d, self, model)) {
952 cpl_vector_delete(mspec1d);
953 return cpl_error_set_where(cpl_func);
957 xcorr = cpl_vector_new(1 + 2 * hsize);
958 ixc = cpl_vector_correlate(xcorr, mspec1d, obs);
960#ifdef IRPLIB_SPC_DUMP
962 irplib_polynomial_dump_corr_step(self, xcorr,
"Shift");
965 cpl_vector_delete(mspec1d);
967 error = cpl_polynomial_shift_1d(self, 0, (
double)ixc);
969 xc = cpl_vector_get(xcorr, ixc);
973 cpl_msg_info(cpl_func,
"Shifting %d pixels (%g < %g)", xxc,
974 cpl_vector_get(xcorr, hsize), xc);
977 cpl_vector *xvals = cpl_vector_new(1 + 2 * hsize);
978 cpl_bivector *bcorr = cpl_bivector_wrap_vectors(xvals, xcorr);
980 double x = (double)-hsize;
981 char *title = cpl_sprintf(
"t 'Cross-correlation of shifted %d-pixel "
982 "spectrum (XCmax=%g at %d)' w linespoints",
983 nobs, cpl_vector_get(xcorr, ixc), xxc);
985 for (i = 0; i < 1 + 2 * hsize; i++, x += 1.0) {
986 cpl_vector_set(xvals, i, x);
989 cpl_plot_bivector(
"set grid;set xlabel 'Offset [pixel]';", title,
"",
991 cpl_bivector_unwrap_vectors(bcorr);
992 cpl_vector_delete(xvals);
996 cpl_vector_delete(xcorr);
998 cpl_ensure_code(!error, error);
1003 return CPL_ERROR_NONE;
1031 cpl_vector *linepix,
1033 const cpl_polynomial *disp,
1034 const cpl_bivector *lines,
1043 cpl_errorstate prestate;
1044 const double sigma = wfwhm * CPL_MATH_SIG_FWHM;
1045 const cpl_vector *xlines = cpl_bivector_get_x_const(lines);
1046 const double *dxlines = cpl_vector_get_data_const(xlines);
1047 const double *dylines = cpl_bivector_get_y_data_const(lines);
1048 double *plinepix = linepix ? cpl_vector_get_data(linepix) : NULL;
1049 const int nlines = cpl_vector_get_size(xlines);
1050 const int nself = cpl_vector_get_size(self);
1051 double *dself = cpl_vector_get_data(self);
1052 cpl_polynomial *dispi;
1053 double *profile = NULL;
1054 const cpl_size i0 = 0;
1055 const double p0 = cpl_polynomial_get_coeff(disp, &i0);
1057 double xpos = (double)(1 - hsize) - xtrunc;
1058 const double xmax = (double)(nself - hsize) + xtrunc;
1059 double xderiv, xextreme;
1060 cpl_error_code error = CPL_ERROR_NONE;
1062 cpl_size ulines = 0;
1064 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
1065 cpl_ensure_code(disp != NULL, CPL_ERROR_NULL_INPUT);
1066 cpl_ensure_code(lines != NULL, CPL_ERROR_NULL_INPUT);
1068 cpl_ensure_code(wslit > 0.0, CPL_ERROR_ILLEGAL_INPUT);
1069 cpl_ensure_code(wfwhm > 0.0, CPL_ERROR_ILLEGAL_INPUT);
1070 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
1071 cpl_ensure_code(xtrunc > 0.0, CPL_ERROR_ILLEGAL_INPUT);
1072 cpl_ensure_code(nself > 2 * hsize, CPL_ERROR_ILLEGAL_INPUT);
1074 cpl_ensure_code(cpl_polynomial_get_dimension(disp) == 1,
1075 CPL_ERROR_ILLEGAL_INPUT);
1076 cpl_ensure_code(cpl_polynomial_get_degree(disp) > 0,
1077 CPL_ERROR_ILLEGAL_INPUT);
1080 wl = cpl_polynomial_eval_1d(disp, xpos, &xderiv);
1083 return cpl_error_set_message_macro(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1085 "Non-positive wavelength at x=%g: "
1086 "P(x)=%g, P'(x)=%g",
1090 return cpl_error_set_message_macro(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1092 "Non-increasing dispersion at "
1093 "x=%g: P'(x)=%g, P(x)=%g",
1097 iline = cpl_vector_find(xlines, wl);
1100 if (dxlines[iline] < wl)
1103 if (iline >= nlines)
1104 return cpl_error_set_message_macro(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1106 "The %d-line catalogue has only "
1107 "lines below P(%g)=%g > %g",
1109 dxlines[nlines - 1]);
1111 memset(dself, 0, nself *
sizeof(
double));
1113 dispi = cpl_polynomial_duplicate(disp);
1116 cpl_polynomial_derivative(dispi, 0);
1118 prestate = cpl_errorstate_get();
1120 if (cpl_polynomial_solve_1d(dispi, 0.5 * (nlines + 1), &xextreme, 1)) {
1121 cpl_errorstate_set(prestate);
1123 else if (xpos < xextreme && xextreme < xmax) {
1124 cpl_polynomial_delete(dispi);
1125 return cpl_error_set_message_macro(
1126 cpl_func, CPL_ERROR_ILLEGAL_INPUT, __FILE__, __LINE__,
1128 "dispersion at x=%g: P'(x)=0, "
1130 xextreme, cpl_polynomial_eval_1d(disp, xextreme, NULL));
1134 const int npix = 1 + (int)xtrunc;
1136 if (erftmp != NULL && cpl_vector_get_size(erftmp) == npix &&
1137 cpl_vector_get(erftmp, 0) > 0.0) {
1138 profile = cpl_vector_get_data(erftmp);
1141 const double yval = 0.5 / wslit;
1142 const double x0p = 0.5 * wslit + 0.5;
1143 const double x0n = -0.5 * wslit + 0.5;
1148 if (erftmp == NULL) {
1149 profile = (
double *)cpl_malloc(
sizeof(
double) * (
size_t)npix);
1152 cpl_vector_set_size(erftmp, npix);
1153 profile = cpl_vector_get_data(erftmp);
1156 profile[0] = 2.0 * yval * x1diff;
1158 for (ipix = 1; ipix < npix; ipix++) {
1159 const double x1 = (double)ipix;
1160 const double x1p = x1 + 0.5 * wslit + 0.5;
1161 const double x1n = x1 - 0.5 * wslit + 0.5;
1162 const double x0diff = x1diff;
1167 profile[ipix] = yval * (x1diff - x0diff);
1172 cpl_polynomial_copy(dispi, disp);
1178 xpos -= (wl - dxlines[iline]) / xderiv;
1181 for (; !error && iline < nlines; iline++) {
1184 if (dylines[iline] <= 0.0)
1188 if (plinepix != NULL && plinepix[iline] > 0.0)
1189 xpos = plinepix[iline];
1195 error = cpl_polynomial_set_coeff(dispi, &i0, p0 - dxlines[iline]) ||
1196 cpl_polynomial_solve_1d(dispi, xpos, &xpos, 1);
1201 cpl_msg_debug(cpl_func,
1202 "Stopping spectrum fill at line %d/%d "
1203 "at xpos=%g > xmax=%g",
1204 iline, nlines, xpos, xmax);
1205 cpl_errorstate_dump(prestate, CPL_FALSE,
1207 cpl_errorstate_set(prestate);
1212 if (linepix != NULL && ulines)
1213 (void)cpl_vector_fill(linepix, 0.0);
1214 (void)cpl_error_set_message_macro(cpl_func, cpl_error_get_code(),
1216 "Could not find pixel-position "
1217 "of line %d/%d at wavelength=%g."
1218 " xpos=%g, xmax=%g",
1219 iline, nlines, dxlines[iline],
1224 const double frac = fabs(xpos - floor(xpos));
1225#ifdef IRPLIB_WAVECAL_FAST_FAST
1226 const double frac0 = 1.0 - frac;
1229 const double ep1pw =
1231 const double en1pw =
1233 const double ep1nw =
1235 const double en1nw =
1237 const double frac0 =
1238 (en1nw - en1pw) / (ep1pw - en1pw - ep1nw + en1nw);
1241 const double frac1 = 1.0 - frac0;
1242 const double yval0 = frac0 * dylines[iline];
1243 const double yval1 = frac1 * dylines[iline];
1244 const int npix = 1 + (int)xtrunc;
1246 int i0n = hsize - 1 + floor(xpos);
1250 cpl_boolean didline = CPL_FALSE;
1254 if (plinepix != NULL)
1255 plinepix[iline] = xpos;
1258 (void)cpl_error_set_message_macro(cpl_func,
1259 CPL_ERROR_UNSPECIFIED,
1261 "Illegal split at x=%g: %g + "
1263 xpos, frac0, frac1);
1264#ifdef IRPLIB_WAVEVAL_DEBUG
1267 cpl_msg_warning(cpl_func,
"profile split at x=%g: %g + %g = 1",
1268 xpos, frac0, frac1);
1272 for (ipix = 0; ipix < npix; ipix++, i0n--, i0p++, i1n--, i1p++) {
1273 if (i0n >= 0 && i0n < nself) {
1274 dself[i0n] += yval0 * profile[ipix];
1277 if (i1n >= 0 && i1n < nself && ipix + 1 < npix) {
1278 dself[i1n] += yval1 * profile[ipix + 1];
1285 if (i0p >= 0 && i0p < nself) {
1286 dself[i0p] += yval0 * profile[ipix];
1289 if (i1p >= 0 && i1p < nself && ipix + 1 < npix) {
1290 dself[i1p] += yval1 * profile[ipix + 1];
1299 const double yval = 0.5 * dylines[iline] / wslit;
1301 IRPLIB_MAX((
int)(xpos - xtrunc + 0.5), 1 - hsize);
1302 const int ilast = IRPLIB_MIN((
int)(xpos + xtrunc), nself - hsize);
1304 const double x0 = (double)ifirst - xpos;
1305 const double x0p = x0 + 0.5 * wslit - 0.5;
1306 const double x0n = x0 - 0.5 * wslit - 0.5;
1311 if (plinepix != NULL)
1312 plinepix[iline] = xpos;
1314 if (ilast >= ifirst)
1317 for (ipix = ifirst; ipix <= ilast; ipix++) {
1318 const double x1 = (double)ipix - xpos;
1319 const double x1p = x1 + 0.5 * wslit + 0.5;
1320 const double x1n = x1 - 0.5 * wslit + 0.5;
1321 const double x0diff = x1diff;
1326 dself[ipix + hsize - 1] += yval * (x1diff - x0diff);
1331 cpl_polynomial_delete(dispi);
1335 cpl_ensure_code(!error, cpl_error_get_code());
1339 for (i = 0; i < nself; i++) {
1340 dself[i] = dself[i] > 0.0 ? log(1.0 + dself[i]) : 0.0;
1345 return cpl_error_set_message_macro(
1346 cpl_func, CPL_ERROR_DATA_NOT_FOUND, __FILE__, __LINE__,
1348 "catalogue has no lines in the range "
1350 nlines, wl, xmax, cpl_polynomial_eval_1d(disp, xmax, NULL));
1352 if (pulines != NULL)
1355 return CPL_ERROR_NONE;
1371 return x * erf(x / (sigma * CPL_MATH_SQRT2)) +
1372 2.0 * sigma / CPL_MATH_SQRT2PI * exp(-0.5 * x * x / (sigma * sigma));
1387irplib_gsl_correlation(
const gsl_vector *self,
void *data)
1389 irplib_multimin *mindata = (irplib_multimin *)data;
1390 cpl_errorstate prestate = cpl_errorstate_get();
1391 int nobs, nmodel, ndiff;
1394 cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, GSL_NAN);
1395 cpl_ensure(data != NULL, CPL_ERROR_NULL_INPUT, GSL_NAN);
1397 cpl_ensure(mindata->filler != NULL, CPL_ERROR_NULL_INPUT, GSL_NAN);
1398 cpl_ensure(mindata->observed != NULL, CPL_ERROR_NULL_INPUT, GSL_NAN);
1399 cpl_ensure(mindata->spectrum != NULL, CPL_ERROR_NULL_INPUT, GSL_NAN);
1401 nobs = cpl_vector_get_size(mindata->observed);
1402 nmodel = cpl_vector_get_size(mindata->spectrum);
1403 ndiff = nmodel - nobs;
1405 cpl_ensure((ndiff & 1) == 0, CPL_ERROR_ILLEGAL_INPUT, GSL_NAN);
1407 cpl_ensure(cpl_vector_get_size(mindata->vxc) == 1 + ndiff,
1408 CPL_ERROR_ILLEGAL_INPUT, GSL_NAN);
1412 for (i = 0; i < (cpl_size)self->size; i++) {
1413 const double value = gsl_vector_get(self, (
size_t)i);
1414 cpl_polynomial_set_coeff(mindata->disp1d, &i, value);
1419 cpl_ensure_code(!cpl_polynomial_shift_1d(mindata->disp1d, 0, -ndiff),
1420 cpl_error_get_code());
1422 if (mindata->filler(mindata->spectrum, mindata->disp1d, mindata->param) ||
1423 !cpl_errorstate_is_equal(prestate)) {
1427 (void)cpl_vector_fill(mindata->vxc, -1.0);
1429 mindata->maxxc = ndiff;
1431 if (!cpl_errorstate_is_equal(prestate)) {
1432 cpl_msg_debug(cpl_func,
"Spectrum fill failed:");
1433 cpl_errorstate_dump(prestate, CPL_FALSE,
1435 cpl_errorstate_set(prestate);
1439 mindata->maxxc = cpl_vector_correlate(mindata->vxc, mindata->spectrum,
1443#ifdef IRPLIB_SPC_DUMP
1445 irplib_polynomial_dump_corr_step(mindata->disp1d, mindata->vxc,
"Optimize");
1448 mindata->xc = cpl_vector_get(mindata->vxc, ndiff);
1450 if (mindata->maxxc != ndiff &&
1451 cpl_vector_get(mindata->vxc, mindata->maxxc) > mindata->mxc) {
1452 const irplib_base_spectrum_model *arclamp =
1453 (
const irplib_base_spectrum_model *)mindata->param;
1455 if (mindata->mdisp == NULL) {
1456 mindata->mdisp = cpl_polynomial_duplicate(mindata->disp1d);
1459 cpl_polynomial_copy(mindata->mdisp, mindata->disp1d);
1461 mindata->mxc = cpl_vector_get(mindata->vxc, mindata->maxxc);
1462 mindata->ishift = mindata->maxxc;
1463 cpl_msg_debug(cpl_func,
1464 "Local maximum: %g(%d) > %g(%d) (cost=%u:%u. "
1466 mindata->mxc, mindata->maxxc, mindata->xc, ndiff,
1467 (
unsigned)arclamp->cost, (
unsigned)arclamp->xcost,
1468 (
unsigned)arclamp->ulines);
1471 return -mindata->xc;
1502 cpl_polynomial *self,
1504 const cpl_vector *obs,
1507 irplib_base_spectrum_model *model,
1508 cpl_error_code (*filler)(cpl_vector *,
1509 const cpl_polynomial *,
1510 irplib_base_spectrum_model *),
1522 cpl_errorstate prestate = cpl_errorstate_get();
1523 cpl_polynomial *start;
1524 cpl_polynomial *cand;
1525 cpl_polynomial *backup;
1526 cpl_error_code error = CPL_ERROR_NONE;
1528 cpl_bivector *xtshift = cpl_bivector_new(nmaxima ? nmaxima : 1);
1529 const cpl_vector *xtshiftx = cpl_bivector_get_x_const(xtshift);
1530 const cpl_vector *xtshifty = cpl_bivector_get_y_const(xtshift);
1537 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
1538 cpl_ensure_code(obs != NULL, CPL_ERROR_NULL_INPUT);
1539 cpl_ensure_code(model != NULL, CPL_ERROR_NULL_INPUT);
1540 cpl_ensure_code(filler != NULL, CPL_ERROR_NULL_INPUT);
1541 cpl_ensure_code(pxc != NULL, CPL_ERROR_NULL_INPUT);
1543 cpl_ensure_code(cpl_polynomial_get_dimension(self) == 1,
1544 CPL_ERROR_ILLEGAL_INPUT);
1546 cpl_ensure_code(cpl_polynomial_get_degree(self) > 0,
1547 CPL_ERROR_ILLEGAL_INPUT);
1549 cpl_ensure_code(maxdeg >= 0, CPL_ERROR_ILLEGAL_INPUT);
1550 cpl_ensure_code(pixtol > 0.0, CPL_ERROR_ILLEGAL_INPUT);
1551 cpl_ensure_code(pixstep > 0.0, CPL_ERROR_ILLEGAL_INPUT);
1552 cpl_ensure_code(hsize >= 0, CPL_ERROR_ILLEGAL_INPUT);
1553 cpl_ensure_code(maxite >= 0, CPL_ERROR_ILLEGAL_INPUT);
1554 cpl_ensure_code(nmaxima >= 0, CPL_ERROR_ILLEGAL_INPUT);
1555 cpl_ensure_code(maxfail > 0, CPL_ERROR_ILLEGAL_INPUT);
1556 cpl_ensure_code(maxcont > 0, CPL_ERROR_ILLEGAL_INPUT);
1557 cpl_ensure_code(linelim >= 0, CPL_ERROR_ILLEGAL_INPUT);
1561 cpl_ensure_code(doplot == CPL_TRUE || doplot == CPL_FALSE,
1562 CPL_ERROR_ILLEGAL_INPUT);
1563 return cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
1564 "GSL is not available");
1568 filler, hsize, doplot,
1570 cpl_bivector_delete(xtshift);
1571 return cpl_error_set_where(cpl_func);
1574 if (model->ulines > (cpl_size)linelim) {
1576 const double xxc = cpl_vector_get(xtshiftx, 0);
1577 const double xc0 = cpl_vector_get(xtshifty, 0);
1579 cpl_msg_warning(cpl_func,
1580 "Doing only shift=%g pixels with lines=%u > "
1582 xxc, (
unsigned)model->ulines, linelim, xc0);
1584 cpl_polynomial_shift_1d(self, 0, xxc);
1588 cpl_bivector_delete(xtshift);
1590 return CPL_ERROR_NONE;
1593 start = cpl_polynomial_duplicate(self);
1594 cand = cpl_polynomial_new(1);
1595 backup = cpl_polynomial_new(1);
1598 nshift = cpl_bivector_get_size(xtshift);
1599 if (nmaxima == 0 || nmaxima > nshift)
1602 cpl_msg_info(cpl_func,
1603 "Optimizing %d/%d local shift-maxima "
1604 "(no-shift xc=%g. linelim=%d)",
1605 nmaxima, nshift, xc, linelim);
1606 if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
1607 cpl_bivector_dump(xtshift, stdout);
1609 for (imaxima = 0; imaxima < nmaxima; imaxima++) {
1611 const double xxc = cpl_vector_get(xtshiftx, imaxima);
1612 double xtpixstep = pixstep;
1613 double xtpixtol = pixtol;
1615 cpl_boolean ok = CPL_FALSE;
1619 cpl_polynomial_copy(cand, start);
1620 cpl_polynomial_shift_1d(cand, 0, xxc);
1621 cpl_polynomial_copy(backup, cand);
1624 for (nfail = 0; nfail < maxfail;
1625 nfail++, xtpixtol *= 2.0, xtpixstep *= 2.0) {
1626 int restart = maxcont;
1631 cpl_errorstate_dump(prestate, CPL_FALSE,
1633 cpl_errorstate_set(prestate);
1635 error = irplib_polynomial_find_1d_from_correlation_(
1636 cand, maxdeg, obs, model, filler, xtpixtol, xtpixstep, 2,
1637 maxite, &xtxc, &redo);
1639 error = CPL_ERROR_CONTINUE;
1640 }
while (((!error && redo) || error == CPL_ERROR_CONTINUE) &&
1643 if (!error && !redo) {
1644 cpl_msg_debug(cpl_func,
1645 "XC(imax=%d/%d:xtpixtol=%g): %g "
1647 1 + imaxima, nmaxima, xtpixtol, xtxc,
1648 (
unsigned)model->cost, (
unsigned)model->xcost);
1651 cpl_msg_warning(cpl_func,
1652 "Increasing xtpixtol from %g (%g, imax="
1654 xtpixtol, xtpixstep, 1 + imaxima, nmaxima);
1655 if (model->ulines > (cpl_size)linelim) {
1656 cpl_msg_warning(cpl_func,
1657 "Stopping search-refinement via "
1658 "catalogue with %u lines > %d",
1659 (
unsigned)model->ulines, linelim);
1662 cpl_polynomial_copy(cand, start);
1666 for (; !error && xtpixtol > 0.0; xtpixtol *= 0.25, xtpixstep *= 0.5) {
1667 int restart = maxcont;
1670 cpl_polynomial_copy(backup, cand);
1673 cpl_errorstate_dump(prestate, CPL_FALSE,
1675 cpl_errorstate_set(prestate);
1677 error = irplib_polynomial_find_1d_from_correlation_(
1678 cand, maxdeg, obs, model, filler, xtpixtol, xtpixstep, 2,
1679 maxite, &xtxc, &redo);
1681 error = CPL_ERROR_CONTINUE;
1682 }
while (((!error && redo) || error == CPL_ERROR_CONTINUE) &&
1689 cpl_msg_debug(cpl_func,
1690 "XC(imax=%d/%d:xtpixtol=%g): %g (cost=%u:%u"
1692 1 + imaxima, nmaxima, xtpixtol, xtxc,
1693 (
unsigned)model->cost, (
unsigned)model->xcost,
1694 (
unsigned)model->ulines);
1695 if (model->ulines > (cpl_size)linelim) {
1696 cpl_msg_info(cpl_func,
1697 "Stopping search-refinement via "
1698 "catalogue with %u lines > %u",
1699 (
unsigned)model->ulines, linelim);
1706 cpl_errorstate_dump(prestate, CPL_FALSE,
1708 cpl_errorstate_set(prestate);
1709 cpl_polynomial_copy(cand, backup);
1711 if (ok && xtxc > xc) {
1713 cpl_polynomial_copy(self, cand);
1716 cpl_msg_info(cpl_func,
1717 "XC(imax=%d/%d): %g -> %g (initial-shift=%g. "
1718 "cost=%u:%u. lines=%u)",
1719 1 + imaxima, nmaxima,
1720 cpl_vector_get(xtshifty, imaxima), xtxc,
1721 cpl_vector_get(xtshiftx, imaxima),
1722 (
unsigned)model->cost, (
unsigned)model->xcost,
1723 (
unsigned)model->ulines);
1726 cpl_msg_info(cpl_func,
1727 "xc(imax=%d/%d): %g -> %g (initial-shift=%g. "
1728 "cost=%u:%u. lines=%u)",
1729 1 + imaxima, nmaxima,
1730 cpl_vector_get(xtshifty, imaxima), xtxc,
1731 cpl_vector_get(xtshiftx, imaxima),
1732 (
unsigned)model->cost, (
unsigned)model->xcost,
1733 (
unsigned)model->ulines);
1737 cpl_polynomial_delete(start);
1738 cpl_polynomial_delete(backup);
1739 cpl_polynomial_delete(cand);
1743 const double xxc = cpl_vector_get(xtshiftx, 0);
1744 const double xc0 = cpl_vector_get(xtshifty, 0);
1746 error = cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1747 "Could not improve XC=%g over %d "
1748 "local shift-maxima, best at shift %g",
1752 cpl_msg_info(cpl_func,
1753 "Maximal XC=%g (up from %g, with initial pixel-"
1754 "shift of %g) at %d/%d local shift-maximi",
1755 xc, cpl_vector_get(xtshifty, imaximum),
1756 cpl_vector_get(xtshiftx, imaximum), 1 + imaximum, nmaxima);
1765 cpl_bivector_delete(xtshift);
void irplib_errorstate_dump_debug(unsigned self, unsigned first, unsigned last)
Dump a single CPL error at the CPL debug level.
int irplib_bivector_count_positive(const cpl_bivector *self, double x_min, double x_max)
Count the positive Y-entries in a given X-range.
cpl_error_code irplib_vector_fill_line_spectrum(cpl_vector *self, const cpl_polynomial *disp, irplib_base_spectrum_model *lsslamp)
Generate a 1D spectrum from a model and a dispersion relation.
cpl_error_code irplib_bivector_find_shift_from_correlation(cpl_bivector *self, const cpl_polynomial *disp, const cpl_vector *obs, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *), int hsize, cpl_boolean doplot, double *pxc)
Find shift(s) that maximizes (locally) the cross-correlation.
double irplib_erf_antideriv(double x, double sigma)
The antiderivative of erx(x/sigma/sqrt(2)) with respect to x.
cpl_error_code irplib_polynomial_fit_2d_dispersion(cpl_polynomial *self, const cpl_image *imgwave, int fitdeg, double *presid)
Fit a 2D-dispersion from an image of wavelengths.
cpl_error_code irplib_vector_fill_line_spectrum_fast(cpl_vector *self, const cpl_polynomial *disp, irplib_base_spectrum_model *lsslamp)
Generate a 1D spectrum from a model and a dispersion relation.
cpl_error_code irplib_vector_fill_logline_spectrum_fast(cpl_vector *self, const cpl_polynomial *disp, irplib_base_spectrum_model *lsslamp)
Generate a 1D spectrum from a model and a dispersion relation.
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.
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.
cpl_error_code irplib_vector_fill_line_spectrum_model(cpl_vector *self, cpl_vector *linepix, cpl_vector *erftmp, const cpl_polynomial *disp, const cpl_bivector *lines, double wslit, double wfwhm, double xtrunc, int hsize, cpl_boolean dofast, cpl_boolean dolog, cpl_size *pulines)
Generate a 1D spectrum from (arc) lines and a dispersion relation.
cpl_error_code irplib_polynomial_find_1d_from_correlation(cpl_polynomial *self, int maxdeg, const cpl_vector *obs, 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, double *pxc)
Modify self by maximizing the cross-correlation.
cpl_error_code irplib_vector_fill_logline_spectrum(cpl_vector *self, const cpl_polynomial *disp, irplib_base_spectrum_model *lsslamp)
Generate a 1D spectrum from a model and a dispersion relation.
cpl_error_code irplib_polynomial_shift_1d_from_correlation(cpl_polynomial *self, const cpl_vector *obs, irplib_base_spectrum_model *model, cpl_error_code(*filler)(cpl_vector *, const cpl_polynomial *, irplib_base_spectrum_model *), int hsize, cpl_boolean doplot, double *pxc)
Shift self by the amount that maximizes the cross-correlation.