CR2RE Pipeline Reference Manual 1.7.0
irplib_ppm.c
1/*
2 * This file is part of the irplib package
3 * Copyright (C) 2002,2003,2014 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24/*-----------------------------------------------------------------------------
25 Includes
26 -----------------------------------------------------------------------------*/
27
28#include "irplib_ppm.h"
29#include "irplib_wlxcorr.h"
30#include "irplib_spectrum.h"
31
32#include <math.h>
33#include <cpl.h>
34
35/*-----------------------------------------------------------------------------
36 Private functions
37 -----------------------------------------------------------------------------*/
38#ifdef IRPLIB_PPM_USE_METHOD2
39static cpl_vector *irplib_ppm_convolve_line(const cpl_vector *, double, double);
40static cpl_vector *irplib_ppm_detect_lines(const cpl_vector *, double);
41#endif
42
43/*----------------------------------------------------------------------------*/
47/*----------------------------------------------------------------------------*/
48
50
51/*----------------------------------------------------------------------------*/
68/*----------------------------------------------------------------------------*/
69cpl_polynomial *
70irplib_ppm_engine(const cpl_vector *spectrum,
71 const cpl_bivector *lines_catalog,
72 const cpl_polynomial *poly_init,
73 double slitw,
74 double fwhm,
75 double thresh,
76 int degree,
77 int doplot,
78 cpl_table **tab_infos)
79{
80#ifdef IRPLIB_PPM_USE_METHOD2
81 cpl_vector *spec_conv;
82#endif
83 int spec_sz;
84 cpl_vector *det_lines;
85 cpl_vector *cat_lines;
86 double *pcat_lines;
87 double wmin, wmax;
88 double disp_min, disp_max, disp;
89 int nlines_cat, nlines;
90 const double *plines_catalog_x;
91 const double *plines_catalog_y;
92 cpl_bivector *matched;
93 cpl_matrix *matchedx;
94 int match_sz;
95 cpl_polynomial *fitted;
96 cpl_table *spc_table;
97 const cpl_vector *vectors_plot[3];
98 cpl_vector *plot_y;
99 int start_ind, stop_ind;
100 double fill_val;
101 cpl_size deg_loc;
102 int i;
103 cpl_error_code error;
104
105 /* Check entries */
106 if (spectrum == NULL)
107 return NULL;
108 if (lines_catalog == NULL)
109 return NULL;
110 if (poly_init == NULL)
111 return NULL;
112
113 /* Initialise */
114 spec_sz = cpl_vector_get_size(spectrum);
115 deg_loc = (cpl_size)degree;
116
117#ifdef IRPLIB_PPM_USE_METHOD2
118 /* METHOD 2 */
119 /* Correlate the spectrum with the line profile */
120 if ((spec_conv = irplib_ppm_convolve_line(spectrum, slitw, fwhm)) == NULL) {
121 cpl_msg_error(cpl_func, "Cannot convolve the signal");
122 return NULL;
123 }
124
125 /* Apply the lines detection */
126 if ((det_lines = irplib_ppm_detect_lines(spec_conv, 0.9)) == NULL) {
127 cpl_msg_error(cpl_func, "Cannot detect lines");
128 cpl_vector_delete(spec_conv);
129 return NULL;
130 }
131 cpl_vector_delete(spec_conv);
132#else
133 /* METHOD 1 */
134 if ((det_lines = irplib_spectrum_detect_peaks(spectrum, fwhm, thresh, 0,
135 NULL, NULL)) == NULL) {
136 cpl_msg_error(cpl_func, "Cannot convolve the signal");
137 return NULL;
138 }
139#endif
140 cpl_msg_info(cpl_func, "Detected %" CPL_SIZE_FORMAT " lines",
141 cpl_vector_get_size(det_lines));
142
143 /* Get the catalog lines */
144 wmin = cpl_polynomial_eval_1d(poly_init, 1.0, NULL);
145 wmax = cpl_polynomial_eval_1d(poly_init, spec_sz, NULL);
146 plines_catalog_x = cpl_bivector_get_x_data_const(lines_catalog);
147 plines_catalog_y = cpl_bivector_get_y_data_const(lines_catalog);
148 nlines = cpl_bivector_get_size(lines_catalog);
149 nlines_cat = 0;
150 start_ind = stop_ind = -1;
151 for (i = 0; i < nlines; i++) {
152 if (plines_catalog_x[i] > wmin && plines_catalog_x[i] < wmax &&
153 plines_catalog_y[i] > 0.0) {
154 nlines_cat++;
155 if (start_ind < 0)
156 start_ind = i;
157 stop_ind = i;
158 }
159 }
160 if (nlines_cat == 0) {
161 cpl_msg_error(cpl_func, "No lines in catalog");
162 cpl_vector_delete(det_lines);
163 return NULL;
164 }
165 cat_lines = cpl_vector_new(nlines_cat);
166 pcat_lines = cpl_vector_get_data(cat_lines);
167 nlines_cat = 0;
168 for (i = 0; i < nlines; i++) {
169 if (plines_catalog_x[i] > wmin && plines_catalog_x[i] < wmax &&
170 plines_catalog_y[i] > 0.0) {
171 pcat_lines[nlines_cat] = plines_catalog_x[i];
172 nlines_cat++;
173 }
174 }
175
176 /* Plot inputs */
177 if (doplot) {
178 double *pdet_lines;
179
180 /* Catalog */
181 irplib_wlxcorr_catalog_plot(lines_catalog, wmin, wmax);
182
183 /* Spectrum with detected lines */
184 fill_val = cpl_vector_get_max(spectrum);
185 plot_y = cpl_vector_new(spec_sz);
186 cpl_vector_fill(plot_y, 0.0);
187 pdet_lines = cpl_vector_get_data(det_lines);
188 for (i = 0; i < cpl_vector_get_size(det_lines); i++) {
189 cpl_vector_set(plot_y, (int)pdet_lines[i], fill_val);
190 }
191 vectors_plot[0] = NULL;
192 vectors_plot[1] = spectrum;
193 vectors_plot[2] = plot_y;
194
195 cpl_plot_vectors("set grid;set xlabel 'Position (Pixel)';set ylabel "
196 "'Intensity (ADU/sec)';",
197 "t 'Spectrum with detected lines' w lines", "",
198 vectors_plot, 3);
199 cpl_vector_delete(plot_y);
200 }
201
202 /* Apply the point pattern matching */
203 disp = (wmax - wmin) / spec_sz;
204 disp_min = disp - (disp / 10);
205 disp_max = disp + (disp / 10);
206 matched = cpl_ppm_match_positions(det_lines, cat_lines, disp_min, disp_max,
207 0.05, NULL, NULL);
208 cpl_vector_delete(det_lines);
209 cpl_vector_delete(cat_lines);
210
211 if (matched == NULL) {
212 cpl_msg_error(cpl_func, "Cannot apply the point pattern matching");
213 return NULL;
214 }
215
216 match_sz = cpl_bivector_get_size(matched);
217
218 cpl_msg_info(cpl_func, "Matched %d lines", match_sz);
219
220 if (match_sz <= deg_loc) {
221 cpl_msg_error(cpl_func, "Not enough match for the fit");
222 cpl_bivector_delete(matched);
223 return NULL;
224 }
225
226 /* Plot if requested */
227 if (doplot) {
228 const double *pmatched;
229 cpl_bivector *biplot;
230 cpl_vector *plot_cat_x;
231 cpl_vector *plot_cat_y;
232 /* Spectrum with matched lines */
233 fill_val = cpl_vector_get_max(spectrum);
234 plot_y = cpl_vector_new(spec_sz);
235 cpl_vector_fill(plot_y, 0.0);
236 pmatched = cpl_bivector_get_x_data_const(matched);
237 for (i = 0; i < match_sz; i++) {
238 cpl_vector_set(plot_y, (int)pmatched[i], fill_val);
239 }
240 vectors_plot[0] = NULL;
241 vectors_plot[1] = spectrum;
242 vectors_plot[2] = plot_y;
243
244 cpl_plot_vectors("set grid;set xlabel 'Position (Pixel)';set ylabel "
245 "'Intensity (ADU/sec)';",
246 "t 'Spectrum with matched lines' w lines", "",
247 vectors_plot, 3);
248 cpl_vector_delete(plot_y);
249
250 /* Catalog with matched lines */
251 plot_cat_x = cpl_vector_extract(cpl_bivector_get_x_const(lines_catalog),
252 start_ind, stop_ind, 1);
253 plot_cat_y = cpl_vector_extract(cpl_bivector_get_y_const(lines_catalog),
254 start_ind, stop_ind, 1);
255 biplot = cpl_bivector_wrap_vectors(plot_cat_x, plot_cat_y);
256 cpl_plot_bivector("set grid;set xlabel 'Wavelength';set ylabel "
257 "'Emission';",
258 "t 'Catalog' w impulses", "", biplot);
259 cpl_bivector_unwrap_vectors(biplot);
260
261 plot_y = cpl_vector_duplicate(plot_cat_y);
262 cpl_vector_fill(plot_y, 0.0);
263 pmatched = cpl_bivector_get_y_data_const(matched);
264 fill_val = cpl_vector_get_mean(plot_cat_y);
265 for (i = 0; i < match_sz; i++) {
266 int wl_ind = 0;
267 while (pmatched[i] > cpl_vector_get(plot_cat_x, wl_ind) &&
268 wl_ind < spec_sz)
269 wl_ind++;
270 if (wl_ind < spec_sz)
271 cpl_vector_set(plot_y, wl_ind, fill_val);
272 }
273 biplot = cpl_bivector_wrap_vectors(plot_cat_x, plot_y);
274 cpl_plot_bivector("set grid;set xlabel 'Wavelength';set ylabel "
275 "'Emission';",
276 "t 'Catalog (matched lines)' w "
277 "impulses",
278 "", biplot);
279 cpl_bivector_unwrap_vectors(biplot);
280 cpl_vector_delete(plot_cat_x);
281 cpl_vector_delete(plot_cat_y);
282 cpl_vector_delete(plot_y);
283 }
284
285 /* Apply the fit */
286 matchedx = cpl_matrix_wrap(1, match_sz, cpl_bivector_get_x_data(matched));
287 fitted = cpl_polynomial_new(1);
288 error = cpl_polynomial_fit(fitted, matchedx, NULL,
289 cpl_bivector_get_y_const(matched), NULL,
290 CPL_FALSE, NULL, &deg_loc);
291 cpl_bivector_delete(matched);
292 (void)cpl_matrix_unwrap(matchedx);
293 if (error) {
294 cpl_msg_error(cpl_func, "Cannot fit the polynomial");
295 cpl_polynomial_delete(fitted);
296 return NULL;
297 }
298
299 /* Create the infos table */
300 if ((spc_table =
301 irplib_wlxcorr_gen_spc_table(spectrum, lines_catalog, slitw, fwhm,
302 poly_init, fitted)) == NULL) {
303 cpl_msg_error(cpl_func, "Cannot generate the infos table");
304 cpl_polynomial_delete(fitted);
305 return NULL;
306 }
307 if (tab_infos != NULL)
308 *tab_infos = spc_table;
309 else
310 cpl_table_delete(spc_table);
311 return fitted;
312}
313
315
316#ifdef IRPLIB_PPM_USE_METHOD2
317/*----------------------------------------------------------------------------*/
328/*----------------------------------------------------------------------------*/
329static cpl_vector *
330irplib_ppm_convolve_line(const cpl_vector *spectrum, double slitw, double fwhm)
331{
332 cpl_vector *conv_kernel;
333 cpl_vector *line_profile;
334 cpl_vector *xcorrs;
335 cpl_vector *xc_single;
336 int hs, line_sz, sp_sz;
337 int i;
338
339 /* Test entries */
340 if (spectrum == NULL)
341 return NULL;
342
343 /* Create the convolution kernel */
344 if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(slitw, fwhm)) ==
345 NULL) {
346 cpl_msg_error(cpl_func, "Cannot create kernel");
347 return NULL;
348 }
349 hs = cpl_vector_get_size(conv_kernel);
350 line_sz = 2 * hs + 1;
351
352 /* Create the line profile */
353 line_profile = cpl_vector_new(line_sz);
354 cpl_vector_fill(line_profile, 0.0);
355 cpl_vector_set(line_profile, hs, 1.0);
356 if (irplib_wlxcorr_convolve(line_profile, conv_kernel) != 0) {
357 cpl_msg_error(cpl_func, "Cannot create line profile");
358 cpl_vector_delete(line_profile);
359 cpl_vector_delete(conv_kernel);
360 return NULL;
361 }
362 cpl_vector_delete(conv_kernel);
363
364 /* Create the correlations values vector */
365 sp_sz = cpl_vector_get_size(spectrum);
366 xcorrs = cpl_vector_new(sp_sz);
367 cpl_vector_fill(xcorrs, 0.0);
368 xc_single = cpl_vector_new(1);
369
370 /* Loop on the pixels of the spectrum */
371 for (i = hs; i < sp_sz - hs; i++) {
372 cpl_vector *spec_ext;
373 /* Extract the current spectrum part */
374 if ((spec_ext = cpl_vector_extract(spectrum, i - hs, i + hs, 1)) ==
375 NULL) {
376 cpl_msg_error(cpl_func, "Cannot extract spectrum");
377 cpl_vector_delete(xc_single);
378 cpl_vector_delete(line_profile);
379 return NULL;
380 }
381 if (cpl_vector_correlate(xc_single, spec_ext, line_profile) < 0) {
382 cpl_msg_error(cpl_func, "Cannot correlate");
383 cpl_vector_delete(xc_single);
384 cpl_vector_delete(line_profile);
385 cpl_vector_delete(spec_ext);
386 return NULL;
387 }
388 cpl_vector_set(xcorrs, i, cpl_vector_get(xc_single, 0));
389 cpl_vector_delete(spec_ext);
390 }
391 cpl_vector_delete(xc_single);
392 cpl_vector_delete(line_profile);
393
394 return xcorrs;
395}
396
397/*----------------------------------------------------------------------------*/
406/*----------------------------------------------------------------------------*/
407static cpl_vector *
408irplib_ppm_detect_lines(const cpl_vector *spec, double threshold)
409{
410 cpl_vector *spec_loc;
411 double *pspec_loc;
412 cpl_vector *lines;
413 double *plines;
414 int spec_loc_sz, nlines;
415 double max;
416 int i;
417
418 /* Test inputs */
419 if (spec == NULL)
420 return NULL;
421
422 /* Local spectrum */
423 spec_loc = cpl_vector_duplicate(spec);
424 pspec_loc = cpl_vector_get_data(spec_loc);
425 spec_loc_sz = cpl_vector_get_size(spec_loc);
426
427 /* Threshold the local spectrum */
428 for (i = 0; i < spec_loc_sz; i++)
429 if (pspec_loc[i] < threshold)
430 pspec_loc[i] = 0.0;
431
432 /* Allocate lines container */
433 lines = cpl_vector_new(spec_loc_sz);
434 plines = cpl_vector_get_data(lines);
435 nlines = 0;
436
437 /* Loop as long as there are lines */
438 while ((max = cpl_vector_get_max(spec_loc)) > threshold) {
439 /* Find the max position */
440 int max_ind = 0;
441 while (max_ind < spec_loc_sz && pspec_loc[max_ind] < max)
442 max_ind++;
443 if (max_ind == spec_loc_sz) {
444 cpl_msg_error(cpl_func, "Cannot find maximum");
445 cpl_vector_delete(spec_loc);
446 cpl_vector_delete(lines);
447 return NULL;
448 }
449 if (max_ind == 0 || max_ind == spec_loc_sz - 1) {
450 pspec_loc[max_ind] = 0;
451 continue;
452 }
453
454 /* Get the precise position from the neighbours values */
455 plines[nlines] = pspec_loc[max_ind] * max_ind +
456 pspec_loc[max_ind - 1] * (max_ind - 1) +
457 pspec_loc[max_ind + 1] * (max_ind + 1);
458 plines[nlines] /= pspec_loc[max_ind] + pspec_loc[max_ind + 1] +
459 pspec_loc[max_ind - 1];
460 plines[nlines]++;
461 nlines++;
462
463 /* Clean the line */
464 i = max_ind;
465 while (i >= 0 && pspec_loc[i] > threshold) {
466 pspec_loc[i] = 0.0;
467 i--;
468 }
469 i = max_ind + 1;
470 while (i < spec_loc_sz && pspec_loc[i] > threshold) {
471 pspec_loc[i] = 0.0;
472 i++;
473 }
474 }
475 cpl_vector_delete(spec_loc);
476
477 /* Check if there are lines */
478 if (nlines == 0) {
479 cpl_msg_error(cpl_func, "Cannot detect any line");
480 cpl_vector_delete(lines);
481 return NULL;
482 }
483
484 /* Resize the vector */
485 cpl_vector_set_size(lines, nlines);
486
487 /* Sort the lines */
488 cpl_vector_sort(lines, 1);
489
490 return lines;
491}
492
493#endif
cpl_polynomial * irplib_ppm_engine(const cpl_vector *spectrum, const cpl_bivector *lines_catalog, const cpl_polynomial *poly_init, double slitw, double fwhm, double thresh, int degree, int doplot, cpl_table **tab_infos)
The Wavelength Calibration using PPM.
Definition irplib_ppm.c:70
cpl_vector * irplib_spectrum_detect_peaks(const cpl_vector *in, int fwhm, double sigma, int display, cpl_vector **fwhms_out, cpl_vector **areas_out)
Detect the brightest features in a spectrum.