DETMON Pipeline Reference Manual  1.2.8
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
39 static cpl_vector * irplib_ppm_convolve_line(const cpl_vector *, double,double);
40 static cpl_vector * irplib_ppm_detect_lines(const cpl_vector *, double) ;
41 #endif
42 
43 /*----------------------------------------------------------------------------*/
47 /*----------------------------------------------------------------------------*/
48 
51 /*----------------------------------------------------------------------------*/
68 /*----------------------------------------------------------------------------*/
69 cpl_polynomial * irplib_ppm_engine(
70  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  double * pdet_lines ;
86  cpl_vector * cat_lines ;
87  double * pcat_lines ;
88  double wmin, wmax ;
89  double disp_min, disp_max, disp ;
90  int nlines_cat, nlines ;
91  const double * plines_catalog_x ;
92  const double * plines_catalog_y ;
93  cpl_bivector * matched ;
94  cpl_matrix * matchedx;
95  int match_sz;
96  cpl_polynomial * fitted ;
97  cpl_table * spc_table ;
98  const cpl_vector* vectors_plot[3];
99  cpl_vector * plot_y ;
100  int wl_ind, start_ind, stop_ind ;
101  double fill_val ;
102  cpl_size deg_loc ;
103  int i ;
104  cpl_error_code error;
105 
106  /* Check entries */
107  if (spectrum == NULL) return NULL ;
108  if (lines_catalog == NULL) return NULL ;
109  if (poly_init == NULL) return NULL ;
110 
111  /* Initialise */
112  spec_sz = cpl_vector_get_size(spectrum) ;
113  deg_loc = (cpl_size)degree ;
114 
115 #ifdef IRPLIB_PPM_USE_METHOD2
116  /* METHOD 2 */
117  /* Correlate the spectrum with the line profile */
118  if ((spec_conv = irplib_ppm_convolve_line(spectrum, slitw, fwhm)) == NULL) {
119  cpl_msg_error(cpl_func, "Cannot convolve the signal") ;
120  return NULL ;
121  }
122 
123  /* Apply the lines detection */
124  if ((det_lines = irplib_ppm_detect_lines(spec_conv, 0.9)) == NULL) {
125  cpl_msg_error(cpl_func, "Cannot detect lines") ;
126  cpl_vector_delete(spec_conv) ;
127  return NULL ;
128  }
129  cpl_vector_delete(spec_conv) ;
130 #else
131  /* METHOD 1 */
132  if ((det_lines = irplib_spectrum_detect_peaks(spectrum, fwhm,
133  thresh, 0, NULL, NULL)) == NULL) {
134  cpl_msg_error(cpl_func, "Cannot convolve the signal") ;
135  return NULL ;
136  }
137 #endif
138  cpl_msg_info(cpl_func, "Detected %"CPL_SIZE_FORMAT" lines",
139  cpl_vector_get_size(det_lines));
140 
141  /* Get the catalog lines */
142  wmin = cpl_polynomial_eval_1d(poly_init, 1.0, NULL) ;
143  wmax = cpl_polynomial_eval_1d(poly_init, spec_sz, NULL) ;
144  plines_catalog_x = cpl_bivector_get_x_data_const(lines_catalog) ;
145  plines_catalog_y = cpl_bivector_get_y_data_const(lines_catalog) ;
146  nlines = cpl_bivector_get_size(lines_catalog) ;
147  nlines_cat = 0 ;
148  start_ind = stop_ind = -1 ;
149  for (i=0 ; i<nlines ; i++) {
150  if (plines_catalog_x[i] > wmin && plines_catalog_x[i] < wmax &&
151  plines_catalog_y[i] > 0.0) {
152  nlines_cat++ ;
153  if (start_ind<0) start_ind = i ;
154  stop_ind = i ;
155  }
156  }
157  if (nlines_cat == 0) {
158  cpl_msg_error(cpl_func, "No lines in catalog") ;
159  cpl_vector_delete(det_lines) ;
160  return NULL ;
161  }
162  cat_lines = cpl_vector_new(nlines_cat) ;
163  pcat_lines = cpl_vector_get_data(cat_lines) ;
164  nlines_cat = 0 ;
165  for (i=0 ; i<nlines ; i++) {
166  if (plines_catalog_x[i] > wmin && plines_catalog_x[i] < wmax &&
167  plines_catalog_y[i] > 0.0) {
168  pcat_lines[nlines_cat] = plines_catalog_x[i] ;
169  nlines_cat++ ;
170  }
171  }
172 
173  /* Plot inputs */
174  if (doplot) {
175  /* Catalog */
176  irplib_wlxcorr_catalog_plot(lines_catalog, wmin, wmax) ;
177 
178  /* Spectrum with detected lines */
179  fill_val = cpl_vector_get_max(spectrum) ;
180  plot_y = cpl_vector_new(spec_sz);
181  cpl_vector_fill(plot_y, 0.0) ;
182  pdet_lines = cpl_vector_get_data(det_lines) ;
183  for (i=0 ; i<cpl_vector_get_size(det_lines) ; i++) {
184  cpl_vector_set(plot_y, (int)pdet_lines[i], fill_val) ;
185  }
186  vectors_plot[0] = NULL ;
187  vectors_plot[1] = spectrum ;
188  vectors_plot[2] = plot_y ;
189 
190  cpl_plot_vectors("set grid;set xlabel 'Position (Pixel)';set ylabel "
191  "'Intensity (ADU/sec)';",
192  "t 'Spectrum with detected lines' w lines", "",
193  vectors_plot, 3);
194  cpl_vector_delete(plot_y) ;
195  }
196 
197  /* Apply the point pattern matching */
198  disp = (wmax-wmin) / spec_sz ;
199  disp_min = disp - (disp/10) ;
200  disp_max = disp + (disp/10) ;
201  matched = cpl_ppm_match_positions(det_lines, cat_lines, disp_min,
202  disp_max, 0.05, NULL, NULL);
203  cpl_vector_delete(det_lines) ;
204  cpl_vector_delete(cat_lines) ;
205 
206  if (matched == NULL) {
207  cpl_msg_error(cpl_func, "Cannot apply the point pattern matching") ;
208  return NULL ;
209  }
210 
211  match_sz = cpl_bivector_get_size(matched);
212 
213  cpl_msg_info(cpl_func, "Matched %d lines", match_sz) ;
214 
215  if (match_sz <= deg_loc) {
216  cpl_msg_error(cpl_func, "Not enough match for the fit") ;
217  cpl_bivector_delete(matched) ;
218  return NULL ;
219  }
220 
221  /* Plot if requested */
222  if (doplot) {
223  const double * pmatched ;
224  cpl_bivector * biplot ;
225  cpl_vector * plot_cat_x ;
226  cpl_vector * plot_cat_y ;
227  /* Spectrum with matched lines */
228  fill_val = cpl_vector_get_max(spectrum) ;
229  plot_y = cpl_vector_new(spec_sz);
230  cpl_vector_fill(plot_y, 0.0) ;
231  pmatched = cpl_bivector_get_x_data_const(matched) ;
232  for (i=0 ; i < match_sz; i++) {
233  cpl_vector_set(plot_y, (int)pmatched[i], fill_val) ;
234  }
235  vectors_plot[0] = NULL ;
236  vectors_plot[1] = spectrum ;
237  vectors_plot[2] = plot_y ;
238 
239  cpl_plot_vectors("set grid;set xlabel 'Position (Pixel)';set ylabel "
240  "'Intensity (ADU/sec)';",
241  "t 'Spectrum with matched lines' w lines", "",
242  vectors_plot, 3);
243  cpl_vector_delete(plot_y) ;
244 
245  /* Catalog with matched lines */
246  plot_cat_x=cpl_vector_extract(cpl_bivector_get_x_const(lines_catalog),
247  start_ind, stop_ind, 1) ;
248  plot_cat_y=cpl_vector_extract(cpl_bivector_get_y_const(lines_catalog),
249  start_ind, stop_ind, 1) ;
250  biplot = cpl_bivector_wrap_vectors(plot_cat_x, plot_cat_y) ;
251  cpl_plot_bivector("set grid;set xlabel 'Wavelength';set ylabel "
252  "'Emission';", "t 'Catalog' w impulses", "",
253  biplot);
254  cpl_bivector_unwrap_vectors(biplot) ;
255 
256  plot_y = cpl_vector_duplicate(plot_cat_y) ;
257  cpl_vector_fill(plot_y, 0.0) ;
258  pmatched = cpl_bivector_get_y_data_const(matched) ;
259  fill_val=cpl_vector_get_mean(plot_cat_y) ;
260  for (i=0 ; i < match_sz; i++) {
261  wl_ind = 0 ;
262  while (pmatched[i] > cpl_vector_get(plot_cat_x, wl_ind)
263  && wl_ind < spec_sz) wl_ind++ ;
264  if (wl_ind < spec_sz) cpl_vector_set(plot_y, wl_ind, fill_val) ;
265  }
266  biplot = cpl_bivector_wrap_vectors(plot_cat_x, plot_y) ;
267  cpl_plot_bivector("set grid;set xlabel 'Wavelength';set ylabel "
268  "'Emission';", "t 'Catalog (matched lines)' w "
269  "impulses", "", biplot) ;
270  cpl_bivector_unwrap_vectors(biplot) ;
271  cpl_vector_delete(plot_cat_x) ;
272  cpl_vector_delete(plot_cat_y) ;
273  cpl_vector_delete(plot_y) ;
274  }
275 
276  /* Apply the fit */
277  matchedx = cpl_matrix_wrap(1, match_sz, cpl_bivector_get_x_data(matched));
278  fitted = cpl_polynomial_new(1);
279  error = cpl_polynomial_fit(fitted, matchedx, NULL,
280  cpl_bivector_get_y_const(matched), NULL,
281  CPL_FALSE, NULL, &deg_loc);
282  cpl_bivector_delete(matched);
283  (void)cpl_matrix_unwrap(matchedx);
284  if (error) {
285  cpl_msg_error(cpl_func, "Cannot fit the polynomial") ;
286  cpl_polynomial_delete(fitted);
287  return NULL ;
288  }
289 
290  /* Create the infos table */
291  if ((spc_table = irplib_wlxcorr_gen_spc_table(spectrum,
292  lines_catalog, slitw, fwhm, poly_init, fitted)) == NULL) {
293  cpl_msg_error(cpl_func, "Cannot generate the infos table") ;
294  cpl_polynomial_delete(fitted) ;
295  return NULL ;
296  }
297  if (tab_infos != NULL) *tab_infos = spc_table ;
298  else cpl_table_delete(spc_table) ;
299  return fitted ;
300 }
301 
304 #ifdef IRPLIB_PPM_USE_METHOD2
305 /*----------------------------------------------------------------------------*/
316 /*----------------------------------------------------------------------------*/
317 static cpl_vector * irplib_ppm_convolve_line(
318  const cpl_vector * spectrum,
319  double slitw,
320  double fwhm)
321 {
322  cpl_vector * conv_kernel ;
323  cpl_vector * line_profile ;
324  cpl_vector * xcorrs ;
325  cpl_vector * spec_ext ;
326  cpl_vector * xc_single ;
327  int hs, line_sz, sp_sz ;
328  int i ;
329 
330  /* Test entries */
331  if (spectrum == NULL) return NULL ;
332 
333  /* Create the convolution kernel */
334  if ((conv_kernel = irplib_wlxcorr_convolve_create_kernel(slitw,
335  fwhm)) == NULL) {
336  cpl_msg_error(cpl_func, "Cannot create kernel") ;
337  return NULL ;
338  }
339  hs = cpl_vector_get_size(conv_kernel) ;
340  line_sz = 2 * hs + 1 ;
341 
342  /* Create the line profile */
343  line_profile = cpl_vector_new(line_sz) ;
344  cpl_vector_fill(line_profile, 0.0) ;
345  cpl_vector_set(line_profile, hs, 1.0) ;
346  if (irplib_wlxcorr_convolve(line_profile, conv_kernel) != 0) {
347  cpl_msg_error(cpl_func, "Cannot create line profile") ;
348  cpl_vector_delete(line_profile) ;
349  cpl_vector_delete(conv_kernel) ;
350  return NULL ;
351  }
352  cpl_vector_delete(conv_kernel) ;
353 
354  /* Create the correlations values vector */
355  sp_sz = cpl_vector_get_size(spectrum) ;
356  xcorrs = cpl_vector_new(sp_sz) ;
357  cpl_vector_fill(xcorrs, 0.0) ;
358  xc_single = cpl_vector_new(1) ;
359 
360  /* Loop on the pixels of the spectrum */
361  for (i=hs ; i<sp_sz-hs ; i++) {
362  /* Extract the current spectrum part */
363  if ((spec_ext = cpl_vector_extract(spectrum, i-hs, i+hs, 1)) == NULL) {
364  cpl_msg_error(cpl_func, "Cannot extract spectrum") ;
365  cpl_vector_delete(xc_single) ;
366  cpl_vector_delete(line_profile) ;
367  return NULL ;
368  }
369  if (cpl_vector_correlate(xc_single, spec_ext, line_profile) < 0) {
370  cpl_msg_error(cpl_func, "Cannot correlate") ;
371  cpl_vector_delete(xc_single) ;
372  cpl_vector_delete(line_profile) ;
373  cpl_vector_delete(spec_ext) ;
374  return NULL ;
375  }
376  cpl_vector_set(xcorrs, i, cpl_vector_get(xc_single, 0)) ;
377  cpl_vector_delete(spec_ext) ;
378  }
379  cpl_vector_delete(xc_single) ;
380  cpl_vector_delete(line_profile) ;
381 
382  return xcorrs ;
383 }
384 
385 /*----------------------------------------------------------------------------*/
394 /*----------------------------------------------------------------------------*/
395 static cpl_vector * irplib_ppm_detect_lines(
396  const cpl_vector * spec,
397  double threshold)
398 {
399  cpl_vector * spec_loc ;
400  double * pspec_loc ;
401  cpl_vector * lines ;
402  double * plines ;
403  int spec_loc_sz, max_ind, nlines ;
404  double max ;
405  int i ;
406 
407  /* Test inputs */
408  if (spec == NULL) return NULL ;
409 
410  /* Local spectrum */
411  spec_loc = cpl_vector_duplicate(spec) ;
412  pspec_loc = cpl_vector_get_data(spec_loc) ;
413  spec_loc_sz = cpl_vector_get_size(spec_loc) ;
414 
415  /* Threshold the local spectrum */
416  for (i=0 ; i<spec_loc_sz ; i++)
417  if (pspec_loc[i] < threshold) pspec_loc[i] = 0.0 ;
418 
419  /* Allocate lines container */
420  lines = cpl_vector_new(spec_loc_sz) ;
421  plines = cpl_vector_get_data(lines) ;
422  nlines = 0 ;
423 
424  /* Loop as long as there are lines */
425  while ((max = cpl_vector_get_max(spec_loc)) > threshold) {
426  /* Find the max position */
427  max_ind = 0 ;
428  while (max_ind < spec_loc_sz && pspec_loc[max_ind] < max) max_ind++ ;
429  if (max_ind == spec_loc_sz) {
430  cpl_msg_error(cpl_func, "Cannot find maximum") ;
431  cpl_vector_delete(spec_loc) ;
432  cpl_vector_delete(lines) ;
433  return NULL ;
434  }
435  if (max_ind == 0 || max_ind == spec_loc_sz-1) {
436  pspec_loc[max_ind] = 0 ;
437  continue ;
438  }
439 
440  /* Get the precise position from the neighbours values */
441  plines[nlines] = pspec_loc[max_ind] * max_ind +
442  pspec_loc[max_ind-1] * (max_ind-1) +
443  pspec_loc[max_ind+1] * (max_ind+1) ;
444  plines[nlines] /= pspec_loc[max_ind] + pspec_loc[max_ind+1] +
445  pspec_loc[max_ind-1] ;
446  plines[nlines] ++ ;
447  nlines ++ ;
448 
449  /* Clean the line */
450  i = max_ind ;
451  while (i>=0 && pspec_loc[i] > threshold) {
452  pspec_loc[i] = 0.0 ;
453  i-- ;
454  }
455  i = max_ind+1 ;
456  while (i<spec_loc_sz && pspec_loc[i] > threshold) {
457  pspec_loc[i] = 0.0 ;
458  i++ ;
459  }
460  }
461  cpl_vector_delete(spec_loc) ;
462 
463  /* Check if there are lines */
464  if (nlines == 0) {
465  cpl_msg_error(cpl_func, "Cannot detect any line") ;
466  cpl_vector_delete(lines) ;
467  return NULL ;
468  }
469 
470  /* Resize the vector */
471  cpl_vector_set_size(lines, nlines) ;
472 
473  /* Sort the lines */
474  cpl_vector_sort(lines, 1) ;
475 
476  return lines ;
477 }
478 
479 #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:69
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.