VISIR Pipeline Reference Manual  4.1.7
visir_utils.c
1 /* $Id: visir_utils.c,v 1.187 2013-09-24 10:19:57 jtaylor Exp $
2  *
3  * This file is part of the VISIR Pipeline
4  * Copyright (C) 2002,2003,2013,2014 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 /* for mkstemp */
26 #if !defined _XOPEN_SOURCE
27 #define _XOPEN_SOURCE 500
28 #endif
29 
30 /*-----------------------------------------------------------------------------
31  Includes
32  -----------------------------------------------------------------------------*/
33 
34 #include "visir_utils.h"
35 
36 #include "irplib_utils.h"
37 
38 #include "visir_inputs.h"
39 #include "visir_pfits.h"
40 #define visir_pfits_get_int(KEY) irplib_pfits_get_int(self, KEY)
41 #include "visir_spc_distortion.h"
42 #include "visir_cpl_compat.h"
43 #include <cpl.h>
44 #include <cxlist.h>
45 #include <sys/types.h> /* for waitpid */
46 #include <sys/wait.h> /* for waitpid */
47 #include <errno.h>
48 
49 #include <string.h>
50 #include <math.h>
51 #include <assert.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <libgen.h>
55 #include <stdint.h> /* for intptr_t */
56 
57 /* for mkdir, open, posix_fadvise */
58 #include <sys/stat.h>
59 #include <sys/types.h>
60 #include <fcntl.h>
61 
62 #include <signal.h> /* for sigaction, sigemptyset */
63 
64 /* make sure we have fdopen (posix 1990) */
65 IRPLIB_DIAG_PRAGMA_PUSH_ERR(-Wimplicit-function-declaration)
66 
67 #ifdef __SSE3__
68 #include <pmmintrin.h>
69 #endif
70 #ifdef __SSE2__
71 #include <xmmintrin.h>
72 #endif
73 
74 /*-----------------------------------------------------------------------------
75  Define
76  -----------------------------------------------------------------------------*/
77 
78 #define VISIR_BACKGD_START 76
79 #define VISIR_BACKGD_STOP 172
80 
81 /*----------------------------------------------------------------------------*/
87 /*----------------------------------------------------------------------------*/
88 
89 #define IND(x, y , nx) ((x) + (nx) * (y))
90 
91 /*-----------------------------------------------------------------------------
92  Private function prototypes
93  -----------------------------------------------------------------------------*/
94 
95 
96 static double visir_great_circle_dist(double, double, double, double);
97 static double ra_hms2deg(int, int, double);
98 static double dec_hms2deg(int, int, double);
99 
100 static const char * visir_get_capa(const cpl_propertylist *);
101 static double visir_hcycle_background(const irplib_framelist *, int, int);
102 
103 #ifdef VISIR_MASK_HAS
104 static cpl_boolean visir_mask_has(const cpl_mask *, cpl_binary, int);
105 #endif
106 
110 /*----------------------------------------------------------------------------*/
116 /*----------------------------------------------------------------------------*/
117 size_t visir_get_num_threads(cpl_boolean force)
118 {
119  if (force == CPL_FALSE && getenv("OMP_NUM_THREADS")) {
120  char * endptr, * str = getenv("OMP_NUM_THREADS");
121  int r = (int)strtol(str, &endptr, 10);
122  return (str == endptr || r < 1) ? 1 : r;
123  }
124 #if defined HAVE_SYSCONF && defined _SC_NPROCESSORS_ONLN
125  long ret = sysconf(_SC_NPROCESSORS_ONLN);
126  return ret < 1 ? 1 : ret;
127 #else
128  return 1;
129 #endif
130 }
131 
132 
133 /*----------------------------------------------------------------------------*/
140 /*----------------------------------------------------------------------------*/
141 cpl_boolean visir_get_tempdir(char * tmpdir_)
142 {
143  cpl_boolean have_tmpdir = CPL_FALSE;
144  char tmpdir[strlen(tmpdir_) + 1];
145 
146  /* create safe tempfile, remove it and reuse name for mkdir,
147  * mkdir is atomic and symlink safe but we need to try multiple times
148  * due to the possible race between unlink and mkdir */
149  for (int i = 0; i < 10 && !have_tmpdir; i++) {
150  strcpy(tmpdir, tmpdir_);
151  skip_if(mkstemp(tmpdir) < 0);
152  skip_if(unlink(tmpdir));
153  have_tmpdir = mkdir(tmpdir, O_RDWR | S_IRWXU) == 0;
154  }
155 
156  error_if(have_tmpdir != CPL_TRUE, CPL_ERROR_FILE_IO,
157  "Temporary directory creation failed");
158 
159  strcpy(tmpdir_, tmpdir);
160 
161  end_skip;
162 
163  return have_tmpdir;
164 }
165 
166 
167 /* ---------------------------------------------------------------------------*/
174 /* ---------------------------------------------------------------------------*/
175 void visir_drop_cache(const char * filename, off_t offset, off_t length)
176 {
177 #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
178  int fd = open(filename, O_RDONLY);
179  if (fd != -1) {
180  /* POSIX_FADV_NOREUSE is a nop on linux */
181  posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED);
182  close(fd);
183  }
184 #endif
185 }
186 
187 /* ---------------------------------------------------------------------------*/
199 /* ---------------------------------------------------------------------------*/
200 void visir_readahead(const char * filename, off_t offset, off_t length)
201 {
202 #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_WILLNEED
203  int fd = open(filename, O_RDONLY);
204  if (fd != -1) {
205  /* does the same as readahead(2) on linux */
206  posix_fadvise(fd, offset, length, POSIX_FADV_WILLNEED);
207  close(fd);
208  }
209 #endif
210 }
211 
212 /* ---------------------------------------------------------------------------*/
217 /* ---------------------------------------------------------------------------*/
218 size_t visir_get_nbytes(const cpl_image * img)
219 {
220  return cpl_image_get_size_x(img) *
221  cpl_image_get_size_y(img) *
222  cpl_type_get_sizeof(cpl_image_get_type(img));
223 }
224 
225 /* ---------------------------------------------------------------------------*/
234 /* ---------------------------------------------------------------------------*/
235 size_t visir_get_nbytes_plist(const cpl_propertylist * self)
236 {
237  int naxis = visir_pfits_get_int("NAXIS");
238  int bitpix = abs(visir_pfits_get_int("BITPIX"));
239  int pcount = 0;
240  int gcount = 1;
241  size_t naxis_bytes = 1;
242  size_t nbytes;
243  for (int i = 0; i < naxis; i++) {
244  char buffer[80];
245  sprintf(buffer, "NAXIS%d", i + 1);
246  naxis_bytes *= visir_pfits_get_int(buffer);
247  }
248  if (cpl_propertylist_has(self, "XTENSION")) {
249  pcount = visir_pfits_get_int("PCOUNT");
250  gcount = visir_pfits_get_int("GCOUNT");
251  }
252  if (cpl_error_get_code())
253  return 0;
254 
255  nbytes = (bitpix / 8) * gcount * (pcount + naxis_bytes);
256  /* approximation as there may be empty headers */
257  nbytes += cpl_propertylist_get_size(self) * 80;
258  return nbytes;
259 }
260 
261 /*----------------------------------------------------------------------------*/
272 /*----------------------------------------------------------------------------*/
273 cpl_error_code visir_image_multiply_fast(cpl_image * im1,
274  const cpl_image * im2)
275 {
276 #if defined __SSE3__ || defined __SSE2__
277  cpl_ensure_code(im1 != NULL, CPL_ERROR_NULL_INPUT);
278  cpl_ensure_code(im2 != NULL, CPL_ERROR_NULL_INPUT);
279 
280  if (cpl_image_get_type(im1) == CPL_TYPE_FLOAT_COMPLEX &&
281  cpl_image_get_type(im2) == CPL_TYPE_FLOAT_COMPLEX) {
282  const cpl_size nx1 = cpl_image_get_size_x(im1);
283  const cpl_size ny1 = cpl_image_get_size_y(im1);
284  const cpl_size nx2 = cpl_image_get_size_x(im2);
285  const cpl_size ny2 = cpl_image_get_size_y(im2);
286  const size_t Nt = nx1 * ny1;
287  const size_t n = (Nt % 2) == 1 ? Nt - 1 : Nt;
288 
289  float complex * a = cpl_image_get_data(im1);
290  const float complex * b = cpl_image_get_data_const(im2);
291 
292  cpl_ensure_code(nx1 == nx2, CPL_ERROR_INCOMPATIBLE_INPUT);
293  cpl_ensure_code(ny1 == ny2, CPL_ERROR_INCOMPATIBLE_INPUT);
294  cpl_ensure_code(n < SIZE_MAX - 1, CPL_ERROR_ACCESS_OUT_OF_RANGE);
295 
296  /* if unaligned fall back to cpl, it is vectorized since 6.1 too but
297  * checks for NaN which makes it a bit slower */
298  if (((intptr_t)a % 16) != 0 || ((intptr_t)b % 16) != 0)
299  return cpl_image_multiply(im1, im2);
300 
301 #if defined __SSE2__ && !defined __SSE3__
302  const __m128 neg = (__m128)_mm_set_epi32(0x0u, 0x80000000u, 0x0u, 0x80000000u);
303 #endif
304  for (size_t i = 0; i < n; i += 2) {
305  __m128 ua = _mm_load_ps((float *)&a[i]); /* x w */
306  __m128 ub = _mm_load_ps((const float *)&b[i]); /* y z */
307  /* optimized to SSE3 _mm_move[hl]dup_ps by gcc */
308  __m128 reala = _mm_shuffle_ps(ua, ua, _MM_SHUFFLE(2, 2, 0, 0)); /* x x */
309  __m128 imaga = _mm_shuffle_ps(ua, ua, _MM_SHUFFLE(3, 3, 1, 1)); /* w w */
310  __m128 t1 = _mm_mul_ps(reala, ub); /* x*y x*z */
311  __m128 sb = _mm_shuffle_ps(ub, ub, _MM_SHUFFLE(2, 3, 0, 1)); /* z y */
312  __m128 t2 = _mm_mul_ps(imaga, sb); /* w*z w*y */
313 #if defined __SSE2__ && !defined __SSE3__
314  t2 = _mm_xor_ps(t2, neg); /* faster than multipling with 1,-1,1,-1 */
315  __m128 res = _mm_add_ps(t1, t2);
316 #else
317  __m128 res = _mm_addsub_ps(t1, t2); /* x*y-w*z x*z+w*y*/
318 #endif
319  _mm_store_ps((float *)&a[i], res);
320  }
321 
322  if ((Nt) % 2 == 1)
323  a[Nt - 1] = a[Nt - 1] * b[Nt - 1];
324 
325  /* Handle bad pixels map */
326  const cpl_mask * bpm1 = cpl_image_get_bpm_const(im1);
327  const cpl_mask * bpm2 = cpl_image_get_bpm_const(im2);
328  if (bpm2 != NULL) {
329  if (bpm1 == NULL)
330  cpl_image_reject_from_mask(im1, bpm2);
331  else {
332  cpl_mask_or(cpl_image_get_bpm(im1), bpm2);
333  }
334  }
335 
336  } else
337 #endif
338  cpl_image_multiply(im1, im2);
339 
340  return CPL_ERROR_NONE;
341 }
342 
343 /* ---------------------------------------------------------------------------*/
354 /* ---------------------------------------------------------------------------*/
355 double visir_image_get_mean_fast(const cpl_image * im)
356 {
357  if (im == NULL || cpl_image_get_type(im) != CPL_TYPE_FLOAT) {
358  return cpl_image_get_mean(im);
359  }
360  else {
361  const size_t npix = cpl_image_get_size_x(im) *
362  cpl_image_get_size_y(im);
363  const float * data = cpl_image_get_data_float_const(im);
364  const size_t nbad = cpl_image_count_rejected(im);
365  /* sum into multiple values to break dependency cycle and improve
366  * accuracy slightly (see pairwise summation) */
367  double sum1 = 0;
368  double sum2 = 0;
369  double sum3 = 0;
370  double sum4 = 0;
371  if (nbad == 0) {
372  size_t i;
373  for (i = 0; i < npix - (npix % 4u); i+=4) {
374  sum1 += data[i];
375  sum2 += data[i + 1];
376  sum3 += data[i + 2];
377  sum4 += data[i + 3];
378  }
379  for (; i < npix; i++) {
380  sum1 += data[i];
381  }
382  }
383  else if (nbad == npix) {
384  return 0.;
385  }
386  else {
387  size_t i;
388  const cpl_binary * m =
389  cpl_mask_get_data_const(cpl_image_get_bpm_const(im));
390  for (i = 0; i < npix - (npix % 4u); i+=4) {
391  if (!m[i])
392  sum1 += data[i];
393  if (!m[i + 1])
394  sum2 += data[i + 1];
395  if (!m[i + 2])
396  sum3 += data[i + 2];
397  if (!m[i + 3])
398  sum4 += data[i + 3];
399  }
400  for (; i < npix; i++) {
401  if (!m[i])
402  sum1 += data[i];
403  }
404  }
405  return (sum1 + sum2 + sum3 + sum4) / (npix - nbad);
406  }
407 }
408 
409 
410 /*----------------------------------------------------------------------------*/
418 /*----------------------------------------------------------------------------*/
419 int visir_cmp_frm_fn(const cpl_frame * a, const cpl_frame * b)
420 {
421  const char * fna = cpl_frame_get_filename(a);
422  const char * fnb = cpl_frame_get_filename(b);
423 
424  return strcmp(fna, fnb);
425 }
426 
427 /*----------------------------------------------------------------------------*/
436 /*----------------------------------------------------------------------------*/
437 cpl_error_code visir_move_products(cpl_frameset *frames,
438  const char * dest_, const char * src_)
439 {
440  const char * dest = dest_ ? dest_ : ".";
441  const char * src = src_ ? src_ : ".";
442 
443  FOR_EACH_FRAMESET(frm, frames) {
444  if (cpl_frame_get_group(frm) == CPL_FRAME_GROUP_PRODUCT) {
445  char * buf = cpl_strdup(cpl_frame_get_filename(frm));
446  char * new_fn = cpl_sprintf("%s/%s", dest, basename(buf));
447  cpl_free(buf);
448 
449  buf = cpl_sprintf("mv \"%s/%s\" \"%s\"", src,
450  cpl_frame_get_filename(frm), new_fn);
451  if (WEXITSTATUS(system(buf)) != 0) {
452  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
453  "Could not move %s/%s to %s", src,
454  cpl_frame_get_filename(frm), new_fn);
455  cpl_free(buf);
456  cpl_free(new_fn);
457  skip_if(0);
458  }
459  /* DFS11986: new_fn is an absolute path
460  * esorex cannot deal with paths in the filename */
461  /* cpl_frame_set_filename(frm, new_fn); */
462  cpl_free(buf);
463  cpl_free(new_fn);
464  skip_if(0);
465  }
466  /* remove ../ from adapted relative paths again so esorex finds them */
467  if (cpl_frame_get_group(frm) == CPL_FRAME_GROUP_RAW ||
468  cpl_frame_get_group(frm) == CPL_FRAME_GROUP_CALIB) {
469  if (strncmp(cpl_frame_get_filename(frm), "../", 3) != 0) {
470  continue;
471  }
472  char * buf = cpl_strdup(cpl_frame_get_filename(frm));
473  cpl_frame_set_filename(frm, buf + 3);
474  cpl_free(buf);
475  }
476  }
477 
478  end_skip;
479 
480  return cpl_error_get_code();
481 }
482 
483 
484 /*----------------------------------------------------------------------------*/
493 /*----------------------------------------------------------------------------*/
494 cpl_parameter * visir_parameter_duplicate(const cpl_parameter * p)
495 {
496  cpl_parameter * np = NULL;
497 
498  cpl_ensure(p != NULL, CPL_ERROR_NULL_INPUT, NULL);
499  cpl_ensure(cpl_parameter_get_class(p) == CPL_PARAMETER_CLASS_VALUE,
500  CPL_ERROR_UNSUPPORTED_MODE, NULL);
501 
502  switch (cpl_parameter_get_type(p)) {
503  case CPL_TYPE_BOOL:
504  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
505  cpl_parameter_get_type(p),
506  cpl_parameter_get_help(p),
507  cpl_parameter_get_context(p),
508  cpl_parameter_get_default_bool(p));
509  cpl_parameter_set_bool(np, cpl_parameter_get_bool(p));
510  break;
511 
512  case CPL_TYPE_INT:
513  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
514  cpl_parameter_get_type(p),
515  cpl_parameter_get_help(p),
516  cpl_parameter_get_context(p),
517  cpl_parameter_get_default_int(p));
518  cpl_parameter_set_int(np, cpl_parameter_get_int(p));
519  break;
520 
521  case CPL_TYPE_DOUBLE:
522  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
523  cpl_parameter_get_type(p),
524  cpl_parameter_get_help(p),
525  cpl_parameter_get_context(p),
526  cpl_parameter_get_default_double(p));
527  cpl_parameter_set_double(np, cpl_parameter_get_double(p));
528  break;
529 
530  case CPL_TYPE_STRING:
531  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
532  cpl_parameter_get_type(p),
533  cpl_parameter_get_help(p),
534  cpl_parameter_get_context(p),
535  cpl_parameter_get_default_string(p));
536  cpl_parameter_set_string(np, cpl_parameter_get_string(p));
537  break;
538 
539  default:
540  cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
541  "Parameter has unknown type");
542  break;
543  }
544 
545  if (np == NULL)
546  return NULL;
547 
548  if (cpl_parameter_get_tag(p))
549  cpl_parameter_set_tag(np, cpl_parameter_get_tag(p));
550 
551  {
552  cpl_parameter_mode modes[] = {CPL_PARAMETER_MODE_CLI,
553  CPL_PARAMETER_MODE_CFG, CPL_PARAMETER_MODE_ENV};
554 
555  for (size_t i = 0; i < ARRAY_LEN(modes); i++) {
556  cpl_parameter_set_alias(np, modes[i],
557  cpl_parameter_get_alias(p, modes[i]));
558  if (!cpl_parameter_is_enabled(p, modes[i]))
559  cpl_parameter_disable(np, modes[i]);
560  }
561  }
562 
563  return np;
564 }
565 
566 
567 /*----------------------------------------------------------------------------*/
578 /*----------------------------------------------------------------------------*/
579 cpl_error_code visir_copy_parameters(cpl_parameterlist * dest,
580  const cpl_parameterlist * src)
581 {
582  for (const cpl_parameter * p = cpl_parameterlist_get_first_const(src);
583  p != NULL; p = cpl_parameterlist_get_next_const(src)) {
584  cpl_parameter * par =
585  cpl_parameterlist_find(dest, cpl_parameter_get_name(p));
586 
587  if (!par)
588  continue;
589 
590  cpl_type t = cpl_parameter_get_type(par);
591  if (t == CPL_TYPE_BOOL)
592  cpl_parameter_set_bool(par, cpl_parameter_get_bool(p));
593  else if (t == CPL_TYPE_INT)
594  cpl_parameter_set_int(par, cpl_parameter_get_int(p));
595  else if (t == CPL_TYPE_DOUBLE)
596  cpl_parameter_set_double(par, cpl_parameter_get_double(p));
597  else if (t == CPL_TYPE_STRING)
598  cpl_parameter_set_string(par, cpl_parameter_get_string(p));
599  else
600  bug_if(1);
601  }
602 
603  end_skip;
604 
605  return cpl_error_get_code();
606 }
607 
608 
609 /*----------------------------------------------------------------------------*/
618 /*----------------------------------------------------------------------------*/
619 cpl_recipe *
620 visir_init_recipe(const char * name, int (*get_info)(cpl_pluginlist *),
621  cpl_pluginlist * plugins)
622 {
623  cpl_recipe * recipe = cpl_calloc(1, sizeof(cpl_recipe));
624  get_info(plugins);
625  cpl_plugin * pl = cpl_pluginlist_find (plugins, name);
626  cpl_plugin_copy ((cpl_plugin *) & recipe->interface, pl);
627  return recipe;
628 }
629 
630 
631 /*----------------------------------------------------------------------------*/
642 /*----------------------------------------------------------------------------*/
643 cpl_error_code
644 visir_run_recipe(cpl_recipe * recipe,
645  cpl_frameset * framelist, const cpl_parameterlist * parlist,
646  cpl_error_code (*set_parlist)(cpl_parameterlist *,
647  const cpl_parameterlist *))
648 {
649  cpl_plugin * pl = &recipe->interface;
650  cpl_plugin_func plugin_func_init = cpl_plugin_get_init(pl);
651  cpl_plugin_func plugin_func_exec = cpl_plugin_get_exec(pl);
652  cpl_plugin_func plugin_func_deinit = cpl_plugin_get_deinit(pl);
653 
654  skip_if(0);
655 
656  recipe->frames = framelist;
657 
658  plugin_func_init(&recipe->interface);
659  if (set_parlist)
660  set_parlist(recipe->parameters, parlist);
661  plugin_func_exec(&recipe->interface);
662  plugin_func_deinit(&recipe->interface);
663 
664  end_skip;
665  return cpl_error_get_code();
666 }
667 
668 
669 /*----------------------------------------------------------------------------*/
685 /*----------------------------------------------------------------------------*/
686 cpl_frameset *
687 visir_prepare_frameset(const cpl_frameset * frameset,
688  const char ** tagmap, size_t ntags,
689  cpl_boolean reverse)
690 {
691  cpl_frameset * nlist = cpl_frameset_new();
692  cx_list * _nlist = cx_list_new();
693  cpl_ensure(ntags % 2 == 0, CPL_ERROR_ILLEGAL_INPUT, nlist);
694 
695  FOR_EACH_FRAMESET_C(frame, frameset) {
696  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_PRODUCT) {
697  cpl_frame * frm = cpl_frame_duplicate(frame);
698  cpl_frame_set_group(frm, CPL_FRAME_GROUP_RAW);
699  cpl_frame_set_level(frm, CPL_FRAME_LEVEL_NONE);
700  for (size_t i = 0; i < ntags; i += 2)
701  if (strcmp(tagmap[i], cpl_frame_get_tag(frm)) == 0)
702  cpl_frame_set_tag(frm, tagmap[i + 1]);
703 
704  cx_list_push_back(_nlist, frm);
705  }
706  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_CALIB)
707  cx_list_push_back(_nlist, cpl_frame_duplicate(frame));
708  }
709 
710  cx_list_sort(_nlist, (cx_compare_func)visir_cmp_frm_fn);
711  if (reverse) {
712  cx_list_reverse(_nlist);
713  }
714 
715  FOR_EACH(it, _nlist)
716  cpl_frameset_insert(nlist, cx_list_get(_nlist, it));
717  cx_list_delete(_nlist);
718 
719  return nlist;
720 }
721 
722 /* ---------------------------------------------------------------------------*/
731 /* ---------------------------------------------------------------------------*/
732 cpl_frameset * visir_remove_modified_calib(cpl_frameset * set)
733 {
734  cpl_frameset * cleanset = cpl_frameset_new();
735  FOR_EACH_FRAMESET(frame, set) {
736  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_CALIB &&
737  strcmp(cpl_frame_get_tag(frame),
738  "STATIC_MASK") == 0) {
739  continue;
740  }
741  cpl_frameset_insert(cleanset, cpl_frame_duplicate(frame));
742  }
743  cpl_frameset_delete(set);
744 
745  return cleanset;
746 }
747 
748 
749 /*----------------------------------------------------------------------------*/
758 /*----------------------------------------------------------------------------*/
759 static cpl_frameset *
760 visir_wait_for_child(pid_t pid, FILE * rpipe)
761 {
762  char * rbuffer = NULL;
763  int status;
764  cpl_error_code err;
765  size_t size;
766  cpl_frameset * result = NULL;
767 
768  /* we must read before waiting for the child else the writes may block
769  * use stream to save us the EINTR and partial read loops
770  * pipes are all closed on error so fread will fail if child fails */
771  cpl_ensure(rpipe != NULL, CPL_ERROR_NULL_INPUT, NULL);
772  skip_if(fread(&err, sizeof(err), 1, rpipe) != 1);
773  cpl_error_set(cpl_func, err);
774 
775  skip_if(fread(&size, sizeof(size), 1, rpipe) != 1);
776 
777  rbuffer = cpl_malloc(size);
778  skip_if(fread(rbuffer, size, 1, rpipe) != 1);
779 
780  result = visir_frameset_deserialize(rbuffer, NULL);
781 
782  end_skip;
783 
784  /* wait for child to end */
785  if (waitpid(pid, &status, 0) != pid) {
786  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
787  "%s", strerror(errno));
788  }
789  else if (WIFSIGNALED(status)) {
790  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
791  "Process killed by signal %d", WTERMSIG(status));
792  }
793  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
794  cpl_error_set_message(cpl_func, CPL_ERROR_UNSPECIFIED,
795  "Process failed with code %d",
796  WEXITSTATUS(status));
797  }
798  else
799  assert(1);
800 
801  cpl_free(rbuffer);
802 
803  return result;
804 }
805 
806 /* ---------------------------------------------------------------------------*/
814 /* ---------------------------------------------------------------------------*/
815 static void
816 remove_tempdir(const cpl_parameterlist * parlist, const char* recipename,
817  const char * tmpdir)
818 {
819  const cpl_boolean delete =
820  irplib_parameterlist_get_bool(parlist, PACKAGE,
821  recipename, "delete-temp");
822 
823  if (delete) {
824  char * cmd = cpl_sprintf("rm -rf \"%s\"", tmpdir);
825  cpl_msg_info(cpl_func, "Removing temporary directory: %s", tmpdir);
826  if (WEXITSTATUS(system(cmd)) != 0)
827  cpl_msg_warning(cpl_func, "Removing temporary "
828  "directory %s failed", tmpdir);
829  cpl_free(cmd);
830  }
831  else
832  cpl_msg_info(cpl_func, "Keeping temporary directory: %s", tmpdir);
833 }
834 
835 /*----------------------------------------------------------------------------*/
844 /*----------------------------------------------------------------------------*/
845 cpl_error_code
846 visir_tmpdir_exec(const char * recipename, cpl_plugin * plugin,
847  int (*function)(cpl_frameset *, const cpl_parameterlist *))
848 {
849  char tmpdir[strlen(recipename) + 8];
850  cpl_boolean have_tmpdir = CPL_FALSE;
851  cpl_errorstate cleanstate = cpl_errorstate_get();
852  cpl_recipe * recipe = (cpl_recipe *)plugin;
853  sprintf(tmpdir, "%s_XXXXXX", recipename);
854 
855  have_tmpdir = visir_get_tempdir(tmpdir);
856  skip_if(have_tmpdir != CPL_TRUE);
857 
858  cpl_msg_info(cpl_func, "Working in temporary directory: %s", tmpdir);
859 
860  /* go to tmpdir */
861  if (chdir(tmpdir) != 0) {
862  return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
863  "Could not change to temporary directory %s", tmpdir);
864  }
865 
866  /* correct relative paths for the change to the tmpdir */
867  FOR_EACH_FRAMESET(frm, recipe->frames) {
868  if (cpl_frame_get_filename(frm)[0] != '/') {
869  char * buf = cpl_sprintf("../%s", cpl_frame_get_filename(frm));
870  cpl_frame_set_filename(frm, buf);
871  cpl_free(buf);
872  }
873  }
874 
875  cpl_error_code err = cpl_recipedefine_exec(plugin, function)
876  ? (int)cpl_error_set_where(cpl_func) : 0;
877 
878  if (chdir("..") != 0) {
879  return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
880  "Could not change back to base directory");
881  }
882 
883  /* move end products from tmp to cwd */
884  skip_if(visir_move_products(recipe->frames, ".", tmpdir));
885 
886  end_skip;
887 
888  if (have_tmpdir) {
889  remove_tempdir(recipe->parameters, recipename, tmpdir);
890  }
891 
892  if (!cpl_errorstate_is_equal(cleanstate))
893  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
894 
895  return cpl_error_get_code();
896 }
897 
898 
899 /*----------------------------------------------------------------------------*/
908 /*----------------------------------------------------------------------------*/
909 cpl_error_code
910 visir_forking_exec(const char * recipename, cpl_plugin * plugin,
911  int (*function)(cpl_frameset *, const cpl_parameterlist *))
912 {
913  char tmpdir[strlen(recipename) + 8];
914  cpl_boolean have_tmpdir = CPL_FALSE;
915  cpl_errorstate cleanstate = cpl_errorstate_get();
916  cpl_recipe * recipe = (cpl_recipe *)plugin;
917  FILE * pipe_stream = NULL;
918  int pipe_fd[2];
919  static const int READ = 0, WRITE = 1;
920  sprintf(tmpdir, "%s_XXXXXX", recipename);
921 
922  /* ignore sigint in parent to ensure cleanup */
923  struct sigaction sa, sa_orig;
924  sa.sa_handler = SIG_IGN;
925  sigemptyset(&sa.sa_mask);
926  sa.sa_flags = 0;
927  skip_if(sigaction(SIGINT, &sa, &sa_orig) != 0);
928 
929  have_tmpdir = visir_get_tempdir(tmpdir);
930  skip_if(have_tmpdir != CPL_TRUE);
931 
932  cpl_msg_info(cpl_func, "Working in temporary directory: %s", tmpdir);
933 
934  skip_if(pipe(pipe_fd) != 0);
935 
936  pid_t pid = fork();
937  if (pid == -1) {
938  close(pipe_fd[READ]);
939  close(pipe_fd[WRITE]);
940  cpl_error_set_message(cpl_func, CPL_ERROR_UNSPECIFIED,
941  "fork failed: %s", strerror(errno));
942  skip_if(0);
943  }
944  else if (pid == 0) {
945  /* restore default signal handler */
946  sigaction(SIGINT, &sa_orig, NULL);
947 
948  /* close unused read and convert write to stream for convinience */
949  close(pipe_fd[READ]);
950  pipe_stream = fdopen(pipe_fd[WRITE], "w");
951 
952  /* go to tmpdir */
953  if (!pipe_stream || chdir(tmpdir) != 0) {
954  close(pipe_fd[WRITE]);
955  _exit(EXIT_FAILURE);
956  }
957 
958  /* correct relative paths for the change to the tmpdir */
959  FOR_EACH_FRAMESET(frm, recipe->frames) {
960  if (cpl_frame_get_filename(frm)[0] != '/') {
961  char * buf = cpl_sprintf("../%s", cpl_frame_get_filename(frm));
962  cpl_frame_set_filename(frm, buf);
963  cpl_free(buf);
964  }
965  }
966 
967  cpl_error_code err = cpl_recipedefine_exec(plugin, function)
968  ? (int)cpl_error_set_where(cpl_func) : 0;
969 
970  if (err == CPL_ERROR_NONE) {
971  assert((void*)recipe == (void*)plugin);
972  err = visir_send_frameset(pipe_stream, recipe->frames);
973  }
974  fclose(pipe_stream);
975  _exit(err);
976  }
977  else {
978  /* close unused write and convert read to stream for convinience */
979  close(pipe_fd[WRITE]);
980  pipe_stream = fdopen(pipe_fd[READ], "r");
981  skip_if(pipe_stream == NULL);
982 
983  recipe->frames = visir_wait_for_child(pid, pipe_stream);
984  fclose(pipe_stream);
985  skip_if(recipe->frames == NULL);
986 
987  /* move end products from tmp to cwd */
988  skip_if(visir_move_products(recipe->frames, ".", tmpdir));
989  }
990 
991  end_skip;
992 
993  if (have_tmpdir) {
994  remove_tempdir(recipe->parameters, recipename, tmpdir);
995  }
996 
997  if (!cpl_errorstate_is_equal(cleanstate))
998  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
999 
1000  return cpl_error_get_code();
1001 }
1002 
1003 /*----------------------------------------------------------------------------*/
1011 /*----------------------------------------------------------------------------*/
1012 double visir_utils_get_exptime(const int nnod, const cpl_propertylist * plist)
1013 {
1014  /* fits EXPTIME key is equal to DIT * NAVRG with NGC */
1015  /* DIT */
1016  const double dit = visir_pfits_get_dit(plist);
1017  /* NDIT */
1018  const int ndit = visir_pfits_get_ndit(plist);
1019  /* NAVRG */
1020  const int navrg = visir_pfits_get_navrg(plist);
1021  /* Number of chopping cycles */
1022  const int ncycles = visir_pfits_get_chop_ncycles(plist);
1023 
1024  /* exptime *= 2 because of chopping */
1025  const double value = 2 * dit * ndit * nnod * ncycles * navrg;
1026 
1027  if (value <= 0) {
1028  cpl_msg_error(cpl_func, "Illegal exposure time "
1029  "(dit=%g:ndit=%d:ncycles=%d:nnod=%d): %g",
1030  dit, ndit, ncycles, nnod, value);
1031  skip_if(1);
1032  }
1033 
1034  end_skip;
1035  return value;
1036 }
1037 
1038 /*----------------------------------------------------------------------------*/
1047 /*----------------------------------------------------------------------------*/
1048 double * visir_utils_get_wls(const irplib_framelist * self)
1049 {
1050  /* Get the number of files */
1051  const int size = irplib_framelist_get_size(self);
1052  double * wls = NULL;
1053  int i;
1054 
1055 
1056  skip_if (0);
1057 
1058  skip_if(irplib_framelist_contains(self, VISIR_PFITS_DOUBLE_MONOC_POS,
1059  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
1060 
1061  /* Allocate the output array */
1062  wls = cpl_malloc(size * sizeof(double));
1063 
1064  /* Get the wavelengths */
1065  for (i=0; i < size; i++) {
1066  const cpl_propertylist * plist
1068 
1069  wls[i] = visir_pfits_get_monoc_pos(plist);
1070 
1071  skip_if (0);
1072  }
1073 
1074  end_skip;
1075 
1076  if (cpl_error_get_code()) {
1077  cpl_free(wls);
1078  wls = NULL;
1079  }
1080 
1081  return wls;
1082 }
1083 
1084 /*----------------------------------------------------------------------------*/
1094 /*----------------------------------------------------------------------------*/
1095 cpl_image * visir_create_disk_intimage(
1096  int nx,
1097  int ny,
1098  int x_pos,
1099  int y_pos,
1100  int radius)
1101 {
1102  cpl_image * intima;
1103  int * pintima;
1104  double dist;
1105  int i, j;
1106 
1107  /* Create the empty output image */
1108  intima = cpl_image_new(nx, ny, CPL_TYPE_INT);
1109  pintima = cpl_image_get_data_int(intima);
1110 
1111  /* Loop on the pixels */
1112  for (j=0;j<ny ; j++) {
1113  for (i=0;i<nx ; i++) {
1114  dist = (i+1-x_pos)*(i+1-x_pos)+(j+1-y_pos)*(j+1-y_pos);
1115  if (dist < radius*radius) {
1116  pintima[i+j*nx] = 1;
1117  } else {
1118  pintima[i+j*nx] = 0;
1119  }
1120  }
1121  }
1122  return intima;
1123 }
1124 
1125 /*----------------------------------------------------------------------------*/
1136 /*----------------------------------------------------------------------------*/
1137 cpl_image * visir_create_ring_intimage(
1138  int nx,
1139  int ny,
1140  int x_pos,
1141  int y_pos,
1142  int radius1,
1143  int radius2)
1144 {
1145  cpl_image * intima;
1146  int * pintima;
1147  double dist;
1148  if (radius1 >= radius2) {
1149  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1150  "Small ring radius %d larger than big "
1151  "ring radius %d", radius1, radius2);
1152  return NULL;
1153  }
1154  if ((nx - x_pos) < radius2 || x_pos < radius2 ||
1155  (ny - y_pos) < radius2 || y_pos < radius2) {
1156  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1157  "Image of size [%d,%d] with object at "
1158  "[%d,%d] too small to create ring mask of "
1159  "radius %d", nx, ny, x_pos, y_pos, radius2);
1160  return NULL;
1161  }
1162 
1163  /* Create the empty output image */
1164  intima = cpl_image_new(nx, ny, CPL_TYPE_INT);
1165  pintima = cpl_image_get_data_int(intima);
1166 
1167  /* Loop on the pixels */
1168  for (int j=0;j<ny ; j++) {
1169  for (int i=0;i<nx ; i++) {
1170  dist = (i+1-x_pos)*(i+1-x_pos)+(j+1-y_pos)*(j+1-y_pos);
1171  if ((dist < radius2*radius2) && (dist > radius1*radius1)) {
1172  pintima[i+j*nx] = 1;
1173  } else {
1174  pintima[i+j*nx] = 0;
1175  }
1176  }
1177  }
1178  return intima;
1179 }
1180 
1181 /*----------------------------------------------------------------------------*/
1192 /*----------------------------------------------------------------------------*/
1193 double visir_image_sigma_clip(const cpl_image * self, double * pstdev)
1194 {
1195  const int dimx = cpl_image_get_size_x(self);
1196  const int dimy = cpl_image_get_size_y(self);
1197  const cpl_type type = cpl_image_get_type(self);
1198  /* Apply a 3x3 median filter to the input image */
1199  /* FIXME: A wrap (around float/double) would be sufficient */
1200  cpl_image * noise = cpl_image_new(dimx, dimy, type);
1201  cpl_mask * bpm = NULL;
1202  cpl_mask * kernel = cpl_mask_new(3, 3);
1203  const int niterations = 5;
1204  const double sigma = 3.0;
1205  const double median_corr = 0.94;
1206  double bgnoise = -1;
1207  int i = -1;
1208 
1209 
1210  bug_if (0);
1211 
1212  bug_if(cpl_mask_not(kernel));
1213  bug_if(cpl_image_filter_mask(noise, self, kernel, CPL_FILTER_MEDIAN,
1214  CPL_BORDER_FILTER));
1215 
1216  /* (Inverted) Noise image */
1217  bug_if (cpl_image_subtract(noise, self));
1218 
1219  for (i=0; i < niterations ; i++) {
1220  /* Compute mean and stdev */
1221  cpl_stats * stats =
1222  cpl_stats_new_from_image(noise, CPL_STATS_MEAN | CPL_STATS_STDEV);
1223 
1224  const double mean = cpl_stats_get_mean(stats);
1225  const double stdev = cpl_stats_get_stdev(stats);
1226 
1227  /* Set the thresholds */
1228  const double low_thresh = mean - sigma * stdev;
1229  const double high_thresh = mean + sigma * stdev;
1230 
1231 
1232  cpl_stats_delete(stats); /* :-( */
1233 
1234  /* The previous thresholding may have left too few good pixels */
1235  skip_if (0);
1236 
1237  /* Identify where mean-sigma*stdev < noise < mean+sigma*stdev */
1238  bpm = cpl_mask_threshold_image_create(noise, low_thresh, high_thresh);
1239 
1240  bug_if (0);
1241 
1242  bug_if (cpl_mask_not(bpm));
1243 
1244  bug_if (cpl_image_reject_from_mask(noise, bpm));
1245 
1246  cpl_mask_delete(bpm); /* :-( */
1247  bpm = NULL;
1248 
1249  }
1250 
1251  if (pstdev != NULL) {
1252  /* Compute the stdev of the noise and the background */
1253 
1254  cpl_stats * stats =
1255  cpl_stats_new_from_image(noise, CPL_STATS_MEAN | CPL_STATS_STDEV);
1256 
1257  /* Compute mean and stdev */
1258  bgnoise = cpl_stats_get_stdev(stats);
1259  *pstdev = cpl_image_get_median(self) - cpl_stats_get_mean(stats);
1260 
1261  cpl_stats_delete(stats);
1262  } else {
1263  /* Compute the stdev */
1264  bgnoise = cpl_image_get_stdev(noise);
1265  }
1266 
1267  bug_if(0);
1268 
1269  bgnoise /= median_corr;
1270 
1271  end_skip;
1272 
1273  if (cpl_error_get_code())
1274  cpl_msg_error(cpl_func, "Computation of background noise using sigma=%g"
1275  " failed in iteration %d of %d", sigma, i+1, niterations);
1276 
1277  cpl_mask_delete(kernel);
1278  cpl_mask_delete(bpm);
1279  cpl_image_delete(noise);
1280 
1281  return bgnoise;
1282 }
1283 
1284 /*----------------------------------------------------------------------------*/
1294 /*----------------------------------------------------------------------------*/
1295 double visir_img_phot_sigma_clip(const cpl_image * self)
1296 {
1297 
1298  const double noise = visir_image_sigma_clip(self, NULL);
1299 
1300  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), noise);
1301 
1302  return noise;
1303 
1304 }
1305 
1306 /*----------------------------------------------------------------------------*/
1324 /*----------------------------------------------------------------------------*/
1325 int visir_star_find(const cpl_vector * v_ra, const cpl_vector * v_dec,
1326  double ra, double dec, double maxdist, double * pdist)
1327 {
1328  const int nra = cpl_vector_get_size(v_ra);
1329  const int ndec = cpl_vector_get_size(v_dec);
1330  double dmin = 0.0; /* Avoid (false) uninit warning */
1331  int minind = 0;
1332  int i;
1333 
1334 
1335  /* Catch NULL input */
1336  cpl_ensure(nra > 0, cpl_error_get_code(), -2);
1337  cpl_ensure(ndec > 0, cpl_error_get_code(), -3);
1338 
1339  /* It would be natural to use a cpl_bivector for the positions,
1340  but since CPL cannot ensure that a bivector comprises two vectors
1341  of the same length this would be pointless :-( */
1342  cpl_ensure(nra == ndec, CPL_ERROR_INCOMPATIBLE_INPUT, -4);
1343 
1344  cpl_ensure(maxdist >= 0, CPL_ERROR_ILLEGAL_INPUT, -5);
1345 
1346  /* Find the index of the star closest to the given coordinate */
1347  for (i=0 ; i < nra ; i++) {
1348  const double rai = cpl_vector_get(v_ra, i);
1349  const double deci = cpl_vector_get(v_dec, i);
1350  const double gdist = visir_great_circle_dist(rai, deci, ra, dec);
1351 
1352  cpl_msg_debug(cpl_func, "DISTANCE (RAi,DECi)=(%g,%g) <=> "
1353  "(RA,DEC)=(%g,%g): %g", rai, deci, ra, dec, gdist);
1354 
1355  if (i == 0 || gdist < dmin) {
1356  minind = i;
1357  dmin = gdist;
1358  }
1359  }
1360 
1361  if (pdist != NULL) *pdist = dmin;
1362 
1363  /* Check that it is close enough */
1364  if (dmin > maxdist) {
1365  cpl_msg_error(cpl_func, "Nearest standard star (%d of %d) at (RA,DEC)="
1366  "(%g,%g) is too distant from (RA,DEC)=(%g, %g): %g > %g",
1367  1+minind, nra, cpl_vector_get(v_ra, minind),
1368  cpl_vector_get(v_dec, minind), ra, dec, dmin, maxdist);
1369  cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, -1);
1370  }
1371 
1372  return minind;
1373 }
1374 
1375 /*----------------------------------------------------------------------------*/
1394 /*----------------------------------------------------------------------------*/
1395 cpl_error_code visir_star_convert(const char * line, int ra_hh, int ra_mm,
1396  double ra_ss, char isign, int dec_hh,
1397  int dec_mm, double dec_ss,
1398  const double * jys, int njys,
1399  double * pra, double * pdec)
1400 {
1401 
1402  double sign;
1403  int i;
1404 
1405  assert( line );
1406  assert( pra );
1407  assert( pdec );
1408  assert( jys );
1409  assert( njys > 0 );
1410 
1411 
1412  if (isign == '+')
1413  sign = 1.0;
1414  else if (isign == '-')
1415  sign = -1.0;
1416  else {
1417  cpl_msg_error(cpl_func, "Line has illegal declination-sign character "
1418  "(%c): %s", isign, line);
1419  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1420  }
1421 
1422  if (ra_hh < 0) {
1423  cpl_msg_error(cpl_func, "Line has negative RA hh (%d): %s",
1424  ra_hh, line);
1425  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1426  }
1427  if (ra_mm < 0) {
1428  cpl_msg_error(cpl_func, "Line has negative RA mm (%d): %s",
1429  ra_hh, line);
1430  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1431  }
1432  if (ra_mm >= 60) {
1433  cpl_msg_error(cpl_func, "Line has too large RA mm (%d): %s ",
1434  ra_mm, line);
1435  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1436  }
1437  if (ra_ss < 0) {
1438  cpl_msg_error(cpl_func, "Line has negative RA ss (%g): %s",
1439  ra_ss, line);
1440  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1441  }
1442  if (ra_ss >= 60) {
1443  cpl_msg_error(cpl_func, "Line has too large RA ss (%g): %s ",
1444  ra_ss, line);
1445  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1446  }
1447 
1448  if (dec_hh < 0) {
1449  cpl_msg_error(cpl_func, "Line has negative DEC hh (%d): %s",
1450  dec_hh, line);
1451  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1452  }
1453  if (dec_mm < 0) {
1454  cpl_msg_error(cpl_func, "Line has negative DEC mm (%d): %s",
1455  dec_hh, line);
1456  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1457  }
1458  if (dec_mm >= 60) {
1459  cpl_msg_error(cpl_func, "Line has too large DEC mm (%d): %s ",
1460  dec_mm, line);
1461  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1462  }
1463  if (dec_ss < 0) {
1464  cpl_msg_error(cpl_func, "Line has negative DEC ss (%g): %s",
1465  dec_ss, line);
1466  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1467  }
1468  if (dec_ss >= 60) {
1469  cpl_msg_error(cpl_func, "Line has too large DEC ss (%g): %s ",
1470  dec_ss, line);
1471  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1472  }
1473 
1474  *pra = ra_hms2deg(ra_hh, ra_mm, ra_ss);
1475  if (*pra >= 360.0) {
1476  cpl_msg_error(cpl_func, "Line has too large RA (%g): %s ",
1477  *pra, line);
1478  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1479  }
1480 
1481  *pdec = sign * dec_hms2deg(dec_hh, dec_mm, dec_ss);
1482  if (*pdec > 90.0) {
1483  cpl_msg_error(cpl_func, "Line has too large RA (%g): %s ",
1484  *pdec, line);
1485  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1486  }
1487  if (*pdec < -90.0) {
1488  cpl_msg_error(cpl_func, "Line has too small RA (%g): %s ",
1489  *pdec, line);
1490  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1491  }
1492 
1493  for (i=0; i < njys; i++) if (jys[i] <= 0.0) {
1494  cpl_msg_error(cpl_func,"Line has non-positive Jy value (%g) at %d: %s ",
1495  jys[i], i+1, line);
1496  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1497  }
1498 
1499  return CPL_ERROR_NONE;
1500 
1501 }
1502 
1503 /*----------------------------------------------------------------------------*/
1516 /*----------------------------------------------------------------------------*/
1517 cpl_table * visir_table_new_xypos(const cpl_imagelist * images,
1518  const char * label)
1519 {
1520  cpl_errorstate cleanstate = cpl_errorstate_get();
1521  const int nsize = cpl_imagelist_get_size(images);
1522  double psigmas[] = {5, 2, 1, 0.5}; /* Actually not modified */
1523  cpl_vector * sigmas = NULL;
1524  cpl_table * self = NULL;
1525  const int nsigmas = sizeof(psigmas)/sizeof(double);
1526  int isflux = 0;
1527  int nfail, i;
1528 
1529  cpl_ensure(nsize > 0, cpl_error_get_code(), NULL);
1530  cpl_ensure(label, CPL_ERROR_NULL_INPUT, NULL);
1531  cpl_ensure(!strcmp(label, "FLUX") || !strcmp(label, "FWHM"),
1532  CPL_ERROR_UNSUPPORTED_MODE, NULL);
1533 
1534  self = cpl_table_new(nsize);
1535 
1536  skip_if (cpl_table_new_column(self, "X_POS", CPL_TYPE_DOUBLE));
1537  skip_if (cpl_table_new_column(self, "Y_POS", CPL_TYPE_DOUBLE));
1538 
1539  if (!strcmp(label,"FLUX")) {
1540  isflux = 1;
1541  skip_if (cpl_table_new_column(self, label, CPL_TYPE_DOUBLE));
1542  } else {
1543  skip_if (cpl_table_new_column(self, "X_FWHM", CPL_TYPE_DOUBLE));
1544  skip_if (cpl_table_new_column(self, "Y_FWHM", CPL_TYPE_DOUBLE));
1545  }
1546 
1547  sigmas = cpl_vector_wrap(4, psigmas);
1548  skip_if (sigmas == NULL);
1549 
1550  cpl_msg_info(cpl_func, "Detecting apertures using %d sigma-levels "
1551  "ranging from %g down to %g", nsigmas, psigmas[0],
1552  psigmas[nsigmas-1]);
1553 
1554  /* Object detection */
1555  nfail = 0;
1556  for (i=0 ; i < nsize ; i++) {
1557  const cpl_image * image = cpl_imagelist_get_const(images, i);
1558  cpl_apertures * apert;
1559  cpl_size isigma;
1560 
1561 
1562  double posx = -1;
1563  double posy = -1;
1564  double fwhmx = -1;
1565  double fwhmy = -1;
1566  double flux = -1;
1567  int iflux;
1568 
1569  skip_if (0);
1570 
1571  /* Find any apertures in each image */
1572  apert = cpl_apertures_extract(image, sigmas, &isigma);
1573 
1574  if (apert != NULL && cpl_error_get_code()) {
1575  /* FIX for DFS 2616 */
1576  cpl_msg_error(cpl_func, "cpl_apertures_extract() returned non-NULL "
1577  "while setting the CPL error-state to '%s' at '%s'",
1578  cpl_error_get_message(), cpl_error_get_where());
1579  cpl_msg_debug(cpl_func, "Deleting the spurious aperture list at %p:",
1580  (void*)apert);
1581  if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
1582  cpl_apertures_dump(apert, stdout);
1583  cpl_apertures_delete(apert);
1584  apert = NULL;
1585  }
1586 
1587  if (apert != NULL &&
1588  !irplib_apertures_find_max_flux(apert, &iflux, 1) &&
1589  cpl_apertures_get_flux(apert, iflux) > 0) {
1590 
1591  posx = cpl_apertures_get_centroid_x(apert, iflux);
1592  posy = cpl_apertures_get_centroid_y(apert, iflux);
1593  flux = cpl_apertures_get_flux(apert, iflux);
1594  if (!isflux)
1595  cpl_image_get_fwhm(image, (int)posx, (int)posy, &fwhmx, &fwhmy);
1596 
1597  cpl_msg_info(cpl_func, "Detected an aperture with flux=%g at "
1598  "sigma=%g, at position: %g %g", flux,
1599  psigmas[isigma], posx, posy);
1600 
1601  }
1602 
1603  if (apert == NULL || cpl_error_get_code()) {
1604  visir_error_reset("Aperture detection in image %d of %d failed",
1605  i+1, nsize);
1606  nfail++;
1607  } else if (flux <= 0) {
1608  cpl_msg_warning(cpl_func, "Ignoring %d-pixel aperture %d (out of "
1609  "%d) in file %d of %d with non-positive flux: %g",
1610  (int)cpl_apertures_get_npix(apert, iflux), iflux,
1611  (int)cpl_apertures_get_size(apert), i+1, nsize,
1612  flux);
1613  nfail++;
1614  }
1615 
1616  cpl_apertures_delete(apert);
1617  apert = NULL;
1618 
1619  skip_if (cpl_table_set_double(self, "X_POS", i, posx));
1620  skip_if (cpl_table_set_double(self, "Y_POS", i, posy));
1621 
1622  if (isflux)
1623  skip_if (cpl_table_set_double(self, "FLUX", i, flux));
1624  else {
1625  skip_if (cpl_table_set_double(self, "X_FWHM", i, fwhmx));
1626  skip_if (cpl_table_set_double(self, "Y_FWHM", i, fwhmy));
1627  }
1628 
1629  }
1630 
1631  /* Check if some detections were successful */
1632  if (nfail == nsize) {
1633  cpl_msg_error(cpl_func, "Aperture detection failed in all %d images",
1634  nsize);
1635  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
1636  skip_if(1);
1637  }
1638 
1639  end_skip;
1640 
1641  cpl_vector_unwrap(sigmas);
1642 
1643  if (self && cpl_error_get_code()) {
1644  cpl_table_delete(self);
1645  self = NULL;
1646  }
1647 
1648  return self;
1649 }
1650 
1651 /*----------------------------------------------------------------------------*/
1658 /*----------------------------------------------------------------------------*/
1659 int visir_vector_minpos(const cpl_vector * v)
1660 {
1661  const double * x = cpl_vector_get_data_const(v);
1662  const int n = cpl_vector_get_size(v);
1663  int minpos = 0;
1664  int i;
1665 
1666  cpl_ensure(x, CPL_ERROR_NULL_INPUT, -1);
1667 
1668  for (i = 1; i < n; i++) if (x[i] < x[minpos]) minpos = i;
1669 
1670  return minpos;
1671 }
1672 
1673 /*----------------------------------------------------------------------------*/
1688 /*----------------------------------------------------------------------------*/
1689 cpl_error_code visir_bivector_load(cpl_bivector * self, FILE * stream)
1690 {
1691  cpl_vector * v1;
1692  cpl_vector * v2;
1693  int np = 0;
1694  int xsize, ysize;
1695  char line[1024];
1696 
1697  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
1698  cpl_ensure_code(stream, CPL_ERROR_NULL_INPUT);
1699 
1700  /* Create and fill the vectors */
1701  v1 = cpl_bivector_get_x(self);
1702  v2 = cpl_bivector_get_y(self);
1703 
1704  xsize = cpl_vector_get_size(v1);
1705  ysize = cpl_vector_get_size(v2);
1706 
1707  while (fgets(line, 1024, stream) != NULL) {
1708  double x, y;
1709  if (line[0] != '#' && sscanf(line, "%lg %lg", &x, &y) == 2) {
1710  /* Found new element-pair
1711  - increase vector sizes if necessary,
1712  - insert element at end and
1713  - increment size counter */
1714  if (np == xsize) {
1715  xsize *= 2;
1716  cpl_vector_set_size(v1, xsize);
1717  }
1718  if (np == ysize) {
1719  ysize *= 2;
1720  cpl_vector_set_size(v2, ysize);
1721  }
1722  cpl_vector_set(v1, np, x);
1723  cpl_vector_set(v2, np, y);
1724  np++;
1725  }
1726  }
1727 
1728  /* Check that the loop ended due to eof and not an error */
1729  cpl_ensure_code(!ferror(stream), CPL_ERROR_FILE_IO);
1730 
1731  /* Check that the file was not empty and set the size to its true value */
1732  if (np == 0 || cpl_vector_set_size(v1, np) || cpl_vector_set_size(v2, np)) {
1733  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1734  }
1735 
1736  return CPL_ERROR_NONE;
1737 
1738 }
1739 
1740 /*----------------------------------------------------------------------------*/
1753 /*----------------------------------------------------------------------------*/
1754 double visir_star_dist_min(const double * pras, const double * pdecs, int nloc,
1755  int * piloc1, int * piloc2)
1756 {
1757 
1758  int i, j;
1759  double dmin = 180;
1760 
1761 
1762  assert( pras != NULL);
1763  assert( pdecs != NULL);
1764  assert( piloc1 != NULL);
1765  assert( piloc2 != NULL);
1766  assert( nloc > 0 );
1767 
1768  for (j = 0; j < nloc; j++) {
1769  for (i = 0; i < j; i++) {
1770  const double dist = visir_great_circle_dist(pras[i], pdecs[i],
1771  pras[j], pdecs[j]);
1772  if (dist < dmin) {
1773  dmin = dist;
1774  *piloc1 = i;
1775  *piloc2 = j;
1776  }
1777  if (dist < VISIR_STAR_MAX_RADIUS)
1778  cpl_msg_warning(cpl_func,"The two stars (%d,%d) have a distance"
1779  ": %g < %g", i, j, dist, VISIR_STAR_MAX_RADIUS);
1780  }
1781  }
1782 
1783  return dmin;
1784 }
1785 
1786 
1787 /*----------------------------------------------------------------------------*/
1801 /*----------------------------------------------------------------------------*/
1802 const char ** visir_framelist_set_tag(irplib_framelist * self,
1803  char * (*pftag)(const cpl_frame *,
1804  const cpl_propertylist *,
1805  int),
1806  int *pntags)
1807 {
1808 
1809  /* FIXME: Copied from NACO - move to irplib */
1810 
1811  const char ** taglist = NULL; /* Must be initialized due to realloc call */
1812  int iframe, size;
1813 
1814  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
1815  cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
1816  cpl_ensure(pftag != NULL, CPL_ERROR_NULL_INPUT, NULL);
1817  cpl_ensure(pntags != NULL, CPL_ERROR_NULL_INPUT, NULL);
1818 
1819  size = irplib_framelist_get_size(self);
1820 
1821  cpl_ensure(size > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);
1822 
1823  *pntags = 0;
1824 
1825  for (iframe = 0; iframe < size ; iframe++) {
1826  cpl_frame * frame = irplib_framelist_get(self, iframe);
1827  const cpl_propertylist * plist
1829  char * tag;
1830  const char * newtag;
1831  int i;
1832 
1833 
1834  /* This should really be an assert() */
1835  cpl_ensure(frame != NULL, CPL_ERROR_ILLEGAL_INPUT, NULL);
1836  cpl_ensure(plist != NULL, CPL_ERROR_ILLEGAL_INPUT, NULL);
1837 
1838  tag = (*pftag)(frame, plist, iframe);
1839 
1840  cpl_ensure(tag != NULL, cpl_error_get_code(), NULL);
1841 
1842  /* From this point on failures should not really happen */
1843 
1844  (void)cpl_frame_set_tag(frame, tag);
1845  cpl_free(tag);
1846 
1847  newtag = cpl_frame_get_tag(frame);
1848 
1849  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
1850 
1851  /* Compare the new tags with those of previous frames */
1852  for (i=0; i < *pntags; i++)
1853  if (strcmp(newtag, taglist[i]) == 0) break;
1854 
1855  if (i == *pntags) {
1856  /* The new tag is different from the previous ones
1857  - add it to the list */
1858  (*pntags)++;
1859  taglist = (const char **)cpl_realloc(taglist, *pntags *
1860  sizeof(const char *));
1861  taglist[i] = newtag;
1862  }
1863 
1864  }
1865 
1866  return taglist;
1867 
1868 }
1869 
1870 /*----------------------------------------------------------------------------*/
1880 /*----------------------------------------------------------------------------*/
1881 cpl_error_code visir_qc_append_background(cpl_propertylist * self,
1882  const irplib_framelist * rawframes,
1883  int icol1, int icol2)
1884 {
1885 
1886  /* Compute the background values of the HCYCLE frames */
1887  const double bg_mean = visir_hcycle_background(rawframes, icol1, icol2);
1888 
1889  skip_if (0);
1890 
1891  bug_if (cpl_propertylist_append_double(self, "ESO QC BACKGD MEAN",
1892  bg_mean));
1893 
1894  end_skip;
1895 
1896  return cpl_error_get_code();
1897 
1898 }
1899 
1900 
1901 /*----------------------------------------------------------------------------*/
1910 /*----------------------------------------------------------------------------*/
1911 cpl_error_code visir_qc_append_capa(cpl_propertylist * self,
1912  const irplib_framelist * rawframes)
1913 {
1914 
1915  cpl_errorstate cleanstate = cpl_errorstate_get();
1916  const cpl_propertylist * plist
1918  const char * capa;
1919 
1920 
1921  bug_if (0);
1922 
1923  capa = visir_get_capa(plist);
1924 
1925  if (cpl_error_get_code()) {
1926  /* ignore error as keys are not defined for aqu data */
1927  //visir_error_reset("Could not determine capa");
1928  cpl_msg_info(cpl_func, "Could not determine capa");
1929  cpl_errorstate_set(cleanstate);
1930  } else {
1931  bug_if (cpl_propertylist_append_string(self, "ESO QC CAPA", capa));
1932  }
1933 
1934  end_skip;
1935 
1936  return cpl_error_get_code();
1937 
1938 }
1939 
1940 /*----------------------------------------------------------------------------*/
1948 /*----------------------------------------------------------------------------*/
1949 cpl_error_code visir_qc_append_filter(cpl_propertylist * self,
1950  const irplib_framelist * rawframes)
1951 {
1952 
1953  const cpl_propertylist * plist
1955  const char * value = visir_pfits_get_filter(plist);
1956 
1957 
1958  skip_if (0);
1959 
1960  bug_if (cpl_propertylist_append_string(self, "ESO QC FILTER", value));
1961 
1962  end_skip;
1963 
1964  return cpl_error_get_code();
1965 
1966 }
1967 
1968 /*----------------------------------------------------------------------------*/
1976 /*----------------------------------------------------------------------------*/
1977 cpl_error_code visir_qc_append_exptime(cpl_propertylist * self,
1978  const irplib_framelist * rawframes)
1979 {
1980  const cpl_propertylist * plist
1982 
1983  /* NNOD */
1984  const int nnod = irplib_framelist_get_size(rawframes);
1985  /* Get the total exposure time */
1986  const double value = visir_utils_get_exptime(nnod, plist);
1987 
1988  skip_if (0);
1989 
1990  bug_if (cpl_propertylist_append_double(self, "ESO QC EXPTIME", value));
1991 
1992  end_skip;
1993 
1994  return cpl_error_get_code();
1995 
1996 }
1997 
2000 /*----------------------------------------------------------------------------*/
2011 /*----------------------------------------------------------------------------*/
2012 static double visir_great_circle_dist(double ra1, double dec1,
2013  double ra2, double dec2)
2014 {
2015 
2016  /* Convert all input from degrees to radian - and back for the result */
2017  const double dra = sin( CPL_MATH_RAD_DEG * (ra2 - ra1 )/2.0 );
2018  const double ddec = sin( CPL_MATH_RAD_DEG * (dec2 - dec1)/2.0 );
2019 
2020  dec1 *= CPL_MATH_RAD_DEG;
2021  dec2 *= CPL_MATH_RAD_DEG;
2022 
2023  return 2.0 * asin(sqrt( ddec*ddec + cos(dec1)*cos(dec2)*dra*dra))
2024  * CPL_MATH_DEG_RAD;
2025 
2026 }
2027 
2028 /*----------------------------------------------------------------------------*/
2041 /*----------------------------------------------------------------------------*/
2042 static double ra_hms2deg(int hh, int mm, double ss)
2043 {
2044  return 15.0 * dec_hms2deg(hh, mm, ss);
2045 }
2046 
2047 /*----------------------------------------------------------------------------*/
2059 /*----------------------------------------------------------------------------*/
2060 static double dec_hms2deg(int dd, int mm, double ss)
2061 {
2062  return ((double)ss/60.0 + (double)mm)/60.0 + dd;
2063 }
2064 
2065 /*----------------------------------------------------------------------------*/
2078 /*----------------------------------------------------------------------------*/
2079 static double visir_hcycle_background(const irplib_framelist * rawframes,
2080  int icol1, int icol2)
2081 {
2082  cpl_imagelist * iset = NULL;
2083  /* Get the number of files */
2084  const int nfiles = irplib_framelist_get_size(rawframes);
2085  double bgsum = 0;
2086  double bgmean = -1;
2087  int nsum = 0;
2088  int i, j;
2089 
2090 
2091  skip_if (nfiles < 1);
2092 
2093  if (icol1 == 0) icol1 = VISIR_BACKGD_START;
2094  if (icol2 == 0) icol2 = VISIR_BACKGD_STOP;
2095 
2096  cpl_msg_info(cpl_func, "Computing Half-cycle background level from column %d "
2097  "through %d", icol1, icol2);
2098 
2099  /* Loop on the hcycles images */
2100  for (i=0; i < nfiles; i++) {
2101 
2102  iset = visir_load_hcycle(rawframes, i);
2103 
2104  skip_if (0);
2105 
2106  for (j = 0; j < cpl_imagelist_get_size(iset) ; j++) {
2107  const double median =
2108  cpl_image_get_median_window(cpl_imagelist_get(iset, j),
2109  VISIR_BACKGD_START, icol1,
2110  VISIR_BACKGD_STOP, icol2);
2111 
2112  skip_if (0);
2113 
2114  if (median != median) {
2115  const cpl_frame * frame = irplib_framelist_get_const(rawframes,
2116  i);
2117  /* Some Comm. I data contains NaNs */
2118  cpl_msg_error(cpl_func, "Image window (%d, %d, %d, %d) "
2119  "(image %d of %d) in %s (frame %d of %d) "
2120  "has NaN median",
2121  VISIR_BACKGD_START, icol1,
2122  VISIR_BACKGD_STOP, icol2,
2123  j+1, (int)cpl_imagelist_get_size(iset),
2124  cpl_frame_get_filename(frame), i+1, nfiles);
2125  visir_error_set(CPL_ERROR_BAD_FILE_FORMAT);
2126  skip_if(1);
2127  }
2128  bgsum += median;
2129  }
2130  nsum += j;
2131  cpl_imagelist_delete(iset);
2132  iset = NULL;
2133  }
2134 
2135  /* Test if there are some HCYCLE frames */
2136  skip_if (nsum < 1);
2137 
2138  bgmean = bgsum / nsum;
2139 
2140  end_skip;
2141 
2142  cpl_imagelist_delete(iset);
2143 
2144  /* The background was requested to not include the offset correction */
2145  return bgmean - VISIR_HCYCLE_OFFSET;
2146 }
2147 
2148 
2149 /*----------------------------------------------------------------------------*/
2160 /*----------------------------------------------------------------------------*/
2161 cpl_error_code
2162 visir_get_subpixel_maxpos(const cpl_image * img, cpl_size x, cpl_size y,
2163  double * xsub, double * ysub)
2164 {
2165  int bad;
2166  const cpl_size nx = cpl_image_get_size_x(img);
2167  const cpl_size ny = cpl_image_get_size_y(img);
2168 
2169  *xsub = 0;
2170  *ysub = 0;
2171  if (x - 1 > 0 && x + 1 <= nx) {
2172  double sub[] = {
2173  cpl_image_get(img, x - 1, y, &bad),
2174  cpl_image_get(img, x - 0, y, &bad),
2175  cpl_image_get(img, x + 1, y, &bad),
2176  };
2177  if (!bad)
2178  *xsub = 0.5 * (sub[0] - sub[2])/(sub[0] - 2*sub[1] + sub[2]);
2179  }
2180  if (y - 1 > 0 && y + 1 <= ny) {
2181  double sub[] = {
2182  cpl_image_get(img, x, y - 1, &bad),
2183  cpl_image_get(img, x, y - 0, &bad),
2184  cpl_image_get(img, x, y + 1, &bad),
2185  };
2186  if (!bad)
2187  *ysub = 0.5 * (sub[0] - sub[2])/(sub[0] - 2*sub[1] + sub[2]);
2188  }
2189 
2190  return cpl_error_get_code();
2191 }
2192 
2193 static inline unsigned long get_msb(unsigned long a)
2194 {
2195  /* dumb integer msb, (63 - __builtin_clzl(a)) would be faster */
2196  unsigned long msb = 0;
2197  while (a >>= 1) {
2198  msb++;
2199  }
2200  return msb;
2201 }
2202 
2203 /* ---------------------------------------------------------------------------*/
2212 /* ---------------------------------------------------------------------------*/
2213 size_t visir_get_next_regular(size_t a)
2214 {
2215  /* fftw can also deal efficiently with factors of 7 and 13 but it needs
2216  * testing that the speed of these algorithms will not cancel out the gain
2217  * of smaller input sizes */
2218  if (a <= 6)
2219  return a;
2220  /* is already power of 2 */
2221  if ((a & (a - 1)) == 0)
2222  return a;
2223  /* overflow */
2224  if (5 > SIZE_MAX / a)
2225  return a;
2226 
2227  size_t match = SIZE_MAX;
2228  size_t p5 = 1;
2229  while(p5 < a) {
2230  size_t p35 = p5;
2231  while (p35 < a) {
2232  /* ceil division */
2233  size_t quotient = a % p35 != 0 ? a / p35 + 1 : a / p35;
2234  /* next power of two of quotient */
2235  size_t p2 = 2 << get_msb(quotient - 1);
2236 
2237  size_t n = p2 * p35;
2238  if (n == a)
2239  return n;
2240  else if (n < match)
2241  match = n;
2242 
2243  p35 *= 3;
2244  if (p35 == a)
2245  return p35;
2246  }
2247  if (p35 < match)
2248  match = p35;
2249  p5 *= 5;
2250  if (p5 == a)
2251  return p5;
2252  }
2253  if (p5 < match)
2254  match = p5;
2255  return match;
2256 }
2257 
2259  int initialized;
2260  cpl_image * template_fft;
2261  double template_stdev;
2262 };
2263 
2264 visir_fftx_cache * visir_new_fftx_cache(void)
2265 {
2266  return cpl_calloc(sizeof(visir_fftx_cache), 1);
2267 }
2268 
2269 void visir_delete_fftx_cache(visir_fftx_cache * c)
2270 {
2271  irplib_aligned_free(cpl_image_unwrap(c->template_fft));
2272  cpl_free(c);
2273 }
2274 
2275 /*----------------------------------------------------------------------------*/
2291 /*----------------------------------------------------------------------------*/
2292 cpl_error_code
2293 visir_fftxcorrelate(const cpl_image * atemplate, const cpl_image * aimg,
2294  cpl_boolean normalize, double * xshift, double * yshift,
2295  double * max_correlation, visir_fftx_cache * cache)
2296 {
2297  const cpl_size Nxi = cpl_image_get_size_x(aimg);
2298  const cpl_size Nyi = cpl_image_get_size_y(aimg);
2299  const cpl_size Nxt = cpl_image_get_size_x(atemplate);
2300  const cpl_size Nyt = cpl_image_get_size_y(atemplate);
2301  /* pad to small prime factors for better performance */
2302  const cpl_size Nxe = visir_get_next_regular(Nxi + Nxt - 1);
2303  const cpl_size Nye = visir_get_next_regular(Nyi + Nyt - 1);
2304  cpl_size txshift, tyshift;
2305  double subx = 0, suby = 0;
2306  cpl_image * img = NULL;
2307  cpl_image * zimg = NULL;
2308  cpl_image * ztemp = NULL;
2309  cpl_image * fft1 = NULL;
2310  cpl_image * fft2 = NULL;
2311  cpl_image * res = NULL;
2312  void * buffer;
2313  double template_stdev;
2314  /* measure is nondeterministic in the last ulp */
2315  cpl_fft_mode mode = getenv("VISIR_TEST_MODE") ? 0 : CPL_FFT_FIND_MEASURE;
2316 
2317  cpl_ensure_code(atemplate != NULL, CPL_ERROR_NULL_INPUT);
2318  cpl_ensure_code(aimg != NULL, CPL_ERROR_NULL_INPUT);
2319 
2320  if (cache == NULL || cache->initialized == 0) {
2321  /* prepare template */
2322  cpl_image * template = NULL;
2323  if (cpl_image_get_type(atemplate) != CPL_TYPE_FLOAT)
2324  template = cpl_image_cast(atemplate, CPL_TYPE_FLOAT);
2325  else
2326  template = cpl_image_duplicate(atemplate);
2327 
2328  cpl_image_fill_rejected(template, 0);
2329  if (normalize)
2330  skip_if(cpl_image_subtract_scalar(template,
2331  visir_image_get_mean_fast(template)));
2332  template_stdev = cpl_image_get_stdev(template);
2333 
2334  buffer = irplib_aligned_calloc(32, Nxe * Nye, sizeof(float));
2335  ztemp = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2336 
2337  /* flip we have a fft correlation instead of a convolution
2338  * due to FT(f(x))* = FT(f(-x)) for real values*/
2339  skip_if(cpl_image_flip(template, 1));
2340  skip_if(cpl_image_flip(template, 3));
2341 
2342  /* zero pad */
2343  skip_if(cpl_image_copy(ztemp, template, 1, 1));
2344  cpl_image_delete(template);
2345 
2346  buffer = irplib_aligned_malloc(32, (Nxe / 2 + 1) * Nye *
2347  sizeof(float complex));
2348  fft2 = cpl_image_wrap(Nxe / 2 + 1, Nye, CPL_TYPE_FLOAT_COMPLEX,
2349  buffer);
2350  skip_if(cpl_fft_image(fft2, ztemp, CPL_FFT_FORWARD));
2351  if (cache) {
2352  cache->template_fft = fft2;
2353  cache->template_stdev = template_stdev;
2354  cache->initialized = 1;
2355  }
2356  }
2357  else {
2358  fft2 = cache->template_fft;
2359  template_stdev = cache->template_stdev;
2360  error_if(cpl_image_get_type(fft2) != CPL_TYPE_FLOAT_COMPLEX ||
2361  cpl_image_get_size_x(fft2) != Nxe / 2 + 1 ||
2362  cpl_image_get_size_y(fft2) != Nye, CPL_ERROR_ILLEGAL_INPUT,
2363  "Invalid fourier transformed template");
2364  }
2365 
2366  /* prepare image */
2367 
2368  buffer = irplib_aligned_calloc(32, Nxe * Nye, sizeof(float));
2369  zimg = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2370  if (cpl_image_get_type(aimg) != CPL_TYPE_FLOAT)
2371  img = cpl_image_cast(aimg, CPL_TYPE_FLOAT);
2372  else
2373  img = cpl_image_duplicate(aimg);
2374 
2375  skip_if(img == NULL);
2376 
2377  cpl_image_fill_rejected(img, 0);
2378 
2379  if (normalize)
2380  skip_if(cpl_image_subtract_scalar(img,
2381  visir_image_get_mean_fast(img)));
2382 
2383  /* zero pad */
2384  skip_if(cpl_image_copy(zimg, img, 1, 1));
2385 
2386  buffer = irplib_aligned_malloc(32, (Nxe / 2 + 1) * Nye *
2387  sizeof(float complex));
2388  fft1 = cpl_image_wrap(Nxe / 2 + 1, Nye, CPL_TYPE_FLOAT_COMPLEX, buffer);
2389 
2390  skip_if(cpl_fft_image(fft1, zimg, CPL_FFT_FORWARD | mode));
2391 
2392 
2393  /* correlate, no conjugation necessary due to flipping */
2394  skip_if(visir_image_multiply_fast(fft1, fft2));
2395 
2396  buffer = irplib_aligned_malloc(32, Nxe * Nye * sizeof(float));
2397  res = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2398  skip_if(cpl_fft_image(res, fft1, CPL_FFT_BACKWARD | CPL_FFT_NOSCALE |
2399  mode));
2400 
2401  skip_if(cpl_image_get_maxpos_window(res, Nxt / 2 , Nyt / 2,
2402  Nxt / 2 + Nxi, Nyt / 2 + Nyi,
2403  &txshift, &tyshift));
2404 
2405  if (max_correlation != NULL) {
2406  int rej;
2407  *max_correlation = cpl_image_get(res, txshift, tyshift, &rej);
2408  // remove fftw scaling
2409  *max_correlation /= Nxe * Nye;
2410 
2411  if (normalize) {
2412  double tstd = template_stdev;
2413  int mx = txshift - Nxt;
2414  int my = tyshift - Nyt;
2415  double istd= cpl_image_get_stdev_window(zimg,
2416  CX_MAX(1, mx + 1),
2417  CX_MAX(1, my + 1),
2418  CX_MIN(Nxe, mx + Nxt),
2419  CX_MIN(Nye, my + Nyt));
2420 
2421  if (tstd * istd == 0)
2422  *max_correlation = 0;
2423  else
2424  *max_correlation /= (tstd * istd * Nxt * Nyt);
2425 
2426  skip_if(0);
2427  }
2428  }
2429  skip_if(visir_get_subpixel_maxpos(res, txshift, tyshift, &subx, &suby));
2430  if (xshift != NULL) {
2431  *xshift = txshift - Nxt + subx;
2432  }
2433  if (yshift != NULL) {
2434  *yshift = tyshift - Nyt + suby;
2435  }
2436 
2437  end_skip;
2438 
2439  cpl_image_delete(img);
2440  if (cache == NULL)
2441  irplib_aligned_free(cpl_image_unwrap(fft2));
2442  irplib_aligned_free(cpl_image_unwrap(fft1));
2443  irplib_aligned_free(cpl_image_unwrap(zimg));
2444  irplib_aligned_free(cpl_image_unwrap(ztemp));
2445  irplib_aligned_free(cpl_image_unwrap(res));
2446 
2447  return cpl_error_get_code();
2448 }
2449 
2450 
2451 /*----------------------------------------------------------------------------*/
2467 /*----------------------------------------------------------------------------*/
2468 static cx_list *
2469 get_interpolation_points(size_t x, size_t y,
2470  size_t nx_, size_t ny_, cpl_binary *bpm)
2471 {
2472  ssize_t l = -1, r = -1 , u = -1, d = -1;
2473  ssize_t xl = x;
2474  ssize_t xh = x;
2475  ssize_t yl = y;
2476  ssize_t yh = y;
2477  ssize_t nx = (ssize_t)nx_;
2478  ssize_t ny = (ssize_t)ny_;
2479  cx_list * p = cx_list_new();
2480 
2481  /* FIXME: only uses x and y axes, add diagonal?
2482  * maybe a circle around bp? */
2483  while (1) {
2484  xl--;
2485  xh++;
2486  yl--;
2487  yh++;
2488 
2489  if (l < 0 && xl >= 0 && bpm[IND(xl, y, nx)] == CPL_BINARY_0)
2490  l = xl;
2491  if (r < 0 && xh < nx && bpm[IND(xh, y, nx)] == CPL_BINARY_0)
2492  r = xh;
2493  if (d < 0 && yl >= 0 && bpm[IND(x, yl, nx)] == CPL_BINARY_0)
2494  d = yl;
2495  if (u < 0 && yh < ny && bpm[IND(x, yh, nx)] == CPL_BINARY_0)
2496  u = yh;
2497 
2498  /* stop on first pair or end of image */
2499  /* FIXME: searches too much for bp on corner */
2500  if ((l != -1 && r != -1) || (d != -1 && u != -1) ||
2501  (xl < 0 && xh >= nx && yl < 0 && yh >= ny))
2502  break;
2503  }
2504 
2505  /* save the valid points */
2506  if (r >= 0)
2507  cx_list_push_back(p, (cxcptr)(IND(r, y, nx)));
2508  if (l >= 0)
2509  cx_list_push_back(p, (cxcptr)(IND(l, y, nx)));
2510  if (u >= 0)
2511  cx_list_push_back(p, (cxcptr)(IND(x, u, nx)));
2512  if (d >= 0)
2513  cx_list_push_back(p, (cxcptr)(IND(x, d, nx)));
2514  return p;
2515 }
2516 
2517 
2518 /*----------------------------------------------------------------------------*/
2536 /*----------------------------------------------------------------------------*/
2537 cpl_error_code
2538 visir_interpolate_rejected(cpl_image * img, size_t ** ppoints, size_t * n)
2539 {
2540  cpl_mask * mask = cpl_image_get_bpm(img);
2541  float * data = cpl_image_get_data_float(img);
2542  cpl_binary * bpm = cpl_mask_get_data(mask);
2543  const size_t nx = (size_t)cpl_image_get_size_x(img);
2544  const size_t ny = (size_t)cpl_image_get_size_y(img);
2545 
2546  /* FIXME: support more image types */
2547  skip_if(data == NULL);
2548 
2549  if (ppoints == NULL || *ppoints == NULL) {
2550  size_t i = 0;
2551  cpl_binary * found = memchr(bpm, CPL_BINARY_1,
2552  sizeof(cpl_binary) * nx * ny);
2553  /* max 4 points per bp + number of points + bp index */
2554  size_t * restrict pbpm = cpl_calloc(cpl_image_count_rejected(img) * 6,
2555  sizeof(size_t));
2556 
2557  while (found != NULL) {
2558  const size_t ind = found - bpm;
2559  const size_t y = ind / nx;
2560  const size_t x = ind - y * nx;
2561  cx_list * p = get_interpolation_points(x, y, nx, ny, bpm);
2562  cx_list_iterator it = cx_list_begin(p);
2563  const size_t npix = cx_list_size(p);
2564  double sum = 0;
2565 
2566  pbpm[i++] = ind;
2567  pbpm[i++] = npix;
2568  assert(pbpm[i - 1] <= 4);
2569 
2570  while (it != cx_list_end(p)) {
2571  const size_t lind = (size_t)cx_list_get(p, it);
2572  pbpm[i++] = lind;
2573  sum += data[lind];
2574  it = cx_list_next(p, it);
2575  }
2576  data[ind] = sum / npix;
2577  cx_list_delete(p);
2578 
2579  found = memchr(found + 1, CPL_BINARY_1,
2580  sizeof(cpl_binary) * nx * ny - ind - 1);
2581  }
2582  if (ppoints && n) {
2583  *n = i;
2584  *ppoints = pbpm;
2585  }
2586  else
2587  cpl_free(pbpm);
2588  }
2589  else {
2590  const size_t n_ = *n;
2591  size_t * restrict points = *ppoints;
2592  for (size_t i = 0; i < n_;) {
2593  const size_t ind = points[i++];
2594  const size_t m = points[i++];
2595  double sum = 0;
2596  for (size_t j = 0; j < m; j++) {
2597  const size_t lind = points[i++];
2598  sum += data[lind];
2599  }
2600  data[ind] = sum / m;
2601  }
2602  }
2603 
2604  cpl_image_accept_all(img);
2605 
2606  end_skip;
2607 
2608  return cpl_error_get_code();
2609 }
2610 
2611 static const cpl_image *
2612 image_const_row_view_create(const cpl_image * img,
2613  cpl_size ly,
2614  cpl_size uy)
2615 {
2616  const size_t dsz = cpl_type_get_sizeof(cpl_image_get_type(img));
2617  const cpl_size nx = cpl_image_get_size_x(img);
2618  const char * d = cpl_image_get_data_const(img);
2619  size_t offset = (ly - 1) * nx;
2620  cpl_size nny = uy - ly + 1;
2621  cpl_image * wimg = cpl_image_wrap(nx, nny, cpl_image_get_type(img),
2622  (char*)d + offset * dsz);
2623 
2624  const cpl_mask * omask = cpl_image_get_bpm_const(img);
2625  if (omask) {
2626  cpl_mask * mask = cpl_mask_wrap(nx, nny,
2627  (cpl_binary*)cpl_mask_get_data_const(omask) + offset);
2628  cpl_mask_delete(cpl_image_set_bpm(wimg, mask));
2629  }
2630 
2631  return wimg;
2632 }
2633 
2634 static void
2635 image_const_row_view_delete(const cpl_image * img)
2636 {
2637  cpl_mask_unwrap(cpl_image_unset_bpm((cpl_image*)(img)));
2638  cpl_image_unwrap((cpl_image *)img);
2639 }
2640 
2641 /* ---------------------------------------------------------------------------*/
2648 /* ---------------------------------------------------------------------------*/
2649 cpl_image *
2650 visir_parallel_median_collapse(const cpl_imagelist * l)
2651 {
2652 #ifndef _OPENMP
2653  return cpl_imagelist_collapse_median_create(l);
2654 #else
2655  cpl_ensure(l != NULL, CPL_ERROR_NULL_INPUT, NULL);
2656  cpl_ensure(cpl_imagelist_get_size(l) > 0,
2657  CPL_ERROR_ILLEGAL_INPUT, NULL);
2658 
2659  const size_t n = cpl_imagelist_get_size(l);
2660  const cpl_image * img = cpl_imagelist_get_const(l, 0);
2661  const size_t ny = cpl_image_get_size_y(img);
2662  const size_t nx = cpl_image_get_size_x(img);
2663  const size_t nthreads = CX_MIN(visir_get_num_threads(CPL_FALSE), ny);
2664  cpl_image * res = cpl_image_new(nx, ny, cpl_image_get_type(img));
2665  /* make sure image has bpm to avoid creation races later */
2666  cpl_image_get_bpm(res);
2667 
2668  OMP_PRAGMA(omp parallel for num_threads(nthreads))
2669  for (size_t j = 0; j < nthreads; j++) {
2670  size_t ylow = j * (ny / nthreads) + 1;
2671  size_t yhigh = (j + 1) * (ny / nthreads);
2672  if (j == nthreads - 1)
2673  yhigh = ny;
2674  /* create list with images pointing to row views of the original */
2675  cpl_imagelist * view = cpl_imagelist_new();
2676  for (size_t i = 0; i < n; i++) {
2677  const cpl_image * img = cpl_imagelist_get_const(l, i);
2678  const cpl_image * iview =
2679  image_const_row_view_create(img, ylow, yhigh);
2680 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2681  cpl_imagelist_set(view, (cpl_image *)iview, i);
2682 IRPLIB_DIAG_PRAGMA_POP;
2683  }
2684 
2685  /* collapse views and copy to full result image */
2686  cpl_image * r = cpl_imagelist_collapse_median_create(view);
2687  cpl_image_copy(res, r, 1, ylow);
2688 
2689  /* cleanup */
2690  cpl_image_delete(r);
2691  for (size_t i = 0; i < n; i++) {
2692  cpl_image * img = cpl_imagelist_get(view, i);
2693  image_const_row_view_delete(img);
2694  }
2695  cpl_imagelist_unwrap(view);
2696  }
2697 
2698  return res;
2699 #endif
2700 }
2701 
2702 
2703 /*----------------------------------------------------------------------------*/
2709 /*----------------------------------------------------------------------------*/
2710 static const char * visir_get_capa(const cpl_propertylist * plist)
2711 {
2712  const char * capa = "Pb with Capa";
2713  const char * sval;
2714  double mean;
2715 
2716 
2717  skip_if (0);
2718 
2719 
2720  /* Get the instrument mode */
2721  sval = visir_pfits_get_insmode(plist);
2722  skip_if (0);
2723 
2724  /* Identify the mode */
2725  if (!strcmp(sval, "IMG")) {
2726  /* Imaging mode */
2727  mean = visir_pfits_get_volt1dcta9(plist);
2728  mean += visir_pfits_get_volt1dctb9(plist);
2729  } else if (!strcmp(sval, "SPC") || !strcmp(sval, "SPCIMG")) {
2730  /* Spectro mode */
2731  mean = visir_pfits_get_volt2dcta9(plist);
2732  mean += visir_pfits_get_volt2dctb9(plist);
2733  } else
2734  skip_if (1);
2735 
2736  skip_if (0);
2737 
2738  mean *= 0.5;
2739 
2740  /* Compute Capa value */
2741  if (mean < 1.0) {
2742  capa = "Large Capa";
2743  } else if (mean > 4.5) {
2744  capa = "Small Capa";
2745  }
2746 
2747  end_skip;
2748 
2749  return capa;
2750 }
2751 
2752 #ifdef VISIR_MASK_HAS
2753 /*----------------------------------------------------------------------------*/
2773 /*----------------------------------------------------------------------------*/
2774 static cpl_boolean visir_mask_has(const cpl_mask * self, cpl_binary value,
2775  int ngood)
2776 {
2777  const cpl_binary * pself = cpl_mask_get_data_const(self);
2778  int size = cpl_mask_get_size_x(self)
2779  * cpl_mask_get_size_y(self);
2780  int i;
2781 
2782  cpl_ensure(self, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2783  cpl_ensure(ngood >= 0, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
2784  cpl_ensure(ngood <= size, CPL_ERROR_ACCESS_OUT_OF_RANGE, CPL_FALSE);
2785  cpl_ensure(value == CPL_BINARY_0 || value == CPL_BINARY_1,
2786  CPL_ERROR_INCOMPATIBLE_INPUT, CPL_FALSE);
2787 
2788  for (i = 0; i < ngood; i++) {
2789  /* Assume NULL is returned if size == 0 */
2790  const cpl_binary * ppos = memchr(pself, value, (size_t)size);
2791  if (ppos == NULL) break;
2792 
2793  size -= 1 + (int)(ppos - pself);
2794  pself = 1 + ppos;
2795  }
2796 
2797  return i == ngood ? CPL_TRUE : CPL_FALSE;
2798 }
2799 #endif
2800 
2801 
2802 /* ---------------------------------------------------------------------------*/
2808 /* ---------------------------------------------------------------------------*/
2809 size_t visir_upper_bound(const cpl_vector * vec, double val)
2810 {
2811  const double * d = cpl_vector_get_data_const(vec);
2812  long count = cpl_vector_get_size(vec);
2813  long first = 0;
2814  while (count > 0)
2815  {
2816  long step = count / 2;
2817  long it = first + step;
2818  if (!(val < d[it])) {
2819  first = it + 1;
2820  count -= step + 1;
2821  }
2822  else
2823  count = step;
2824  }
2825  return first;
2826 }
2827 
2828 
2829 /* ---------------------------------------------------------------------------*/
2835 /* ---------------------------------------------------------------------------*/
2836 size_t visir_lower_bound(const cpl_vector * vec, double val)
2837 {
2838  const double * d = cpl_vector_get_data_const(vec);
2839  long count = cpl_vector_get_size(vec);
2840  long first = 0;
2841  while (count > 0)
2842  {
2843  long step = count / 2;
2844  long it = first + step;
2845  if (d[it] < val) {
2846  first = it + 1;
2847  count -= step + 1;
2848  }
2849  else
2850  count = step;
2851  }
2852  return first;
2853 }
2854 
2855 
2856 /* ---------------------------------------------------------------------------*/
2868 /* ---------------------------------------------------------------------------*/
2869 cpl_image * visir_linintp_values(const cpl_image * inp, const cpl_bivector * ref)
2870 {
2871  const double * data = cpl_image_get_data_double_const(inp);
2872  const cpl_vector * rx = cpl_bivector_get_x_const(ref);
2873  const cpl_vector * ry = cpl_bivector_get_y_const(ref);
2874  size_t nref = cpl_bivector_get_size(ref);
2875  size_t nx = cpl_image_get_size_x(inp);
2876  size_t ny = cpl_image_get_size_y(inp);
2877  cpl_image * res = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
2878  double * rdata = cpl_image_get_data_double(res);
2879  cpl_ensure(nref >= 2, CPL_ERROR_ILLEGAL_INPUT, NULL);
2880 
2881  for (size_t y = 0; y < ny; y++) {
2882  for (size_t x = 0; x < nx; x++) {
2883  double val = data[y * nx + x];
2884  intptr_t ilo = visir_lower_bound(rx, val);
2885  //double intval, rx1, rx2, ry1, ry2;
2886  //if (ilo == 0) {
2887  // /* extrapolate low */
2888  // ilo += 1;
2889  //}
2890  //else if (ilo == nref) {
2891  // /* extrapolate high */
2892  // ilo -= 1;
2893  //}
2894  //rx1 = cpl_vector_get(rx, ilo - 1);
2895  //rx2 = cpl_vector_get(rx, ilo);
2896  //ry1 = cpl_vector_get(ry, ilo - 1);
2897  //ry2 = cpl_vector_get(ry, ilo);
2898  //double grad = (ry2 - ry1) / (rx2 - rx1);
2899  //double y0 = ry1 - grad * rx1;
2900  //rdata[y * nx + x] = grad * val + y0;
2901 
2902  if (ilo == 0) {
2903  rdata[y * nx + x] = cpl_vector_get(ry, 0);
2904  cpl_image_reject(res, x + 1, y + 1);
2905  }
2906  else if (ilo == nref) {
2907  rdata[y * nx + x] = cpl_vector_get(ry, nref - 1);
2908  cpl_image_reject(res, x + 1, y + 1);
2909  }
2910  else {
2911  double rx1 = cpl_vector_get(rx, ilo - 1);
2912  double rx2 = cpl_vector_get(rx, ilo);
2913  double ry1 = cpl_vector_get(ry, ilo - 1);
2914  double ry2 = cpl_vector_get(ry, ilo);
2915  double grad = (ry2 - ry1) / (rx2 - rx1);
2916  double y0 = ry1 - grad * rx1;
2917  rdata[y * nx + x] = grad * val + y0;
2918  }
2919  }
2920  }
2921  return res;
2922 }
2923 
2924 /* ---------------------------------------------------------------------------*/
2943 /* ---------------------------------------------------------------------------*/
2944 cpl_error_code
2945 fit_2d_gauss(const cpl_image * img_, const cpl_image * weights, cpl_size x, cpl_size y,
2946  double est_fwhmx, double est_fwhmy,
2947  double * peak, double * peak_err,
2948  double * major, double * major_err,
2949  double * minor, double * minor_err,
2950  double * angle, double * angle_err)
2951 {
2952  cpl_image * img = cpl_image_cast(img_, CPL_TYPE_DOUBLE);
2953  cpl_size llx = CX_MAX(x - est_fwhmx * 3, 1);
2954  cpl_size lly = CX_MAX(y - est_fwhmy * 3, 1);
2955  cpl_size urx = CX_MIN(x + est_fwhmx * 3, cpl_image_get_size_x(img));
2956  cpl_size ury = CX_MIN(y + est_fwhmy * 3, cpl_image_get_size_y(img));
2957  cpl_array * dpar = cpl_array_new(7, CPL_TYPE_DOUBLE);
2958  cpl_array * epar = cpl_array_new(7, CPL_TYPE_DOUBLE);
2959  cpl_matrix * cov = NULL;
2960  cpl_matrix * phys_cov = NULL;
2961 
2962  cpl_array_set_double(dpar, 0, cpl_image_get_median(img));
2963  cpl_array_set_double(dpar, 1, cpl_image_get_flux_window(img, llx, lly,
2964  urx, ury));
2965  cpl_array_set_double(dpar, 2, 0.);
2966  cpl_array_set_double(dpar, 3, x);
2967  cpl_array_set_double(dpar, 4, y);
2968  cpl_array_set_double(dpar, 5, est_fwhmx / 2.355);
2969  cpl_array_set_double(dpar, 6, est_fwhmx / 2.355);
2970 
2971  cpl_image * err = cpl_image_new(cpl_image_get_size_x(img),
2972  cpl_image_get_size_y(img),
2973  CPL_TYPE_DOUBLE);
2974  cpl_image_add_scalar(err, 1.);
2975  cpl_image_divide(err, weights);
2976  cpl_image_power(err, 0.5);
2977 
2978  skip_if(cpl_fit_image_gaussian(img, err,
2979  x,
2980  y,
2981  urx - llx + 1,
2982  ury - lly + 1,
2983  dpar,
2984  epar,
2985  NULL, /* fit params */
2986  NULL,
2987  NULL,
2988  &cov,
2989  major,
2990  minor,
2991  angle,
2992  &phys_cov
2993  ));
2994 
2995  /* ignores covariance, values become nan for near circular gauss, forumula
2996  * illdefined for e.g. rho < 0 or theta = 45
2997  * nan cannot be in fits header, so set to -1 */
2998  if (peak) {
2999  double * a = cpl_array_get_data_double(dpar);
3000  *peak = a[0] + a[1] /
3001  (CPL_MATH_2PI * a[5] * a[6] * sqrt(1.0 - a[2] * a[2]));
3002  if (isnan(*peak)) {
3003  cpl_msg_warning(cpl_func, "2d gaussfit, could not determine peak");
3004  *peak = -1.;
3005  }
3006  }
3007  if (peak_err) {
3008  double * a = cpl_array_get_data_double(dpar);
3009  double * e = cpl_array_get_data_double(epar);
3010  double B = a[0];
3011  double A = a[1];
3012  double sigx = a[5];
3013  double sigy = a[6];
3014  double rho = a[2];
3015  double dB = sqrt(e[0]);
3016  double dA = sqrt(e[1]);
3017  double dsigx = sqrt(e[5]);
3018  double dsigy = sqrt(e[6]);
3019  double drho = sqrt(e[2]);
3020  double A2 = A * A;
3021  double rho2 = rho * rho;
3022  double x = CPL_MATH_PI * CPL_MATH_PI * 4 * sigx * sigx * sigy * sigy;
3023  *peak_err =
3024  sqrt(A2 * drho * drho * rho2 /(x * pow(-rho2 + 1.0, 3)) +
3025  A2 * dsigx * dsigx /(x * sigx * sigx * (-rho2 + 1.0)) +
3026  A2 * dsigy * dsigy /(x * sigy * sigy * (-rho2 + 1.0)) +
3027  dA * dA/(x * (-rho2 + 1.0)) + dB * dB);
3028  if (isnan(*peak_err)) {
3029  *peak_err = -1.;
3030  }
3031  }
3032  if (major) {
3033  *major *= CPL_MATH_FWHM_SIG;
3034  if (isnan(*major)) {
3035  cpl_msg_warning(cpl_func,
3036  "2d gaussfit, could not determine major axis");
3037  *minor = -1.;
3038  }
3039  }
3040  if (minor) {
3041  *minor *= CPL_MATH_FWHM_SIG;
3042  if (isnan(*minor)) {
3043  cpl_msg_warning(cpl_func,
3044  "2d gaussfit, could not determine minor axis");
3045  *minor = -1.;
3046  }
3047  }
3048  if (major_err) {
3049  *major_err = sqrt(cpl_matrix_get(phys_cov, 1, 1)) * CPL_MATH_FWHM_SIG;
3050  if (isnan(*major_err)) {
3051  *major_err = -1.;
3052  }
3053  }
3054  if (minor_err) {
3055  *minor_err = sqrt(cpl_matrix_get(phys_cov, 2, 2)) * CPL_MATH_FWHM_SIG;
3056  if (isnan(*minor_err)) {
3057  *minor_err = -1.;
3058  }
3059  }
3060  if (angle_err) {
3061  *angle_err = sqrt(cpl_matrix_get(phys_cov, 0, 0));
3062  if (isnan(*angle_err)) {
3063  *angle_err = -1.;
3064  }
3065  }
3066 
3067 cleanup:
3068  cpl_array_delete(dpar);
3069  cpl_array_delete(epar);
3070  cpl_image_delete(err);
3071  cpl_image_delete(img);
3072  cpl_matrix_delete(phys_cov);
3073  cpl_matrix_delete(cov);
3074 
3075  return cpl_error_get_code();
3076 }
3077 
3078 
3079 /* ---------------------------------------------------------------------------*/
3093 /* ---------------------------------------------------------------------------*/
3094 cpl_error_code
3095 fit_1d_gauss(const cpl_vector * xv, const cpl_vector * yv, cpl_vector * dyv,
3096  double * x0, double * x0_err,
3097  double * peak, double * peak_err,
3098  double * sigma_, double * sigma_err)
3099 {
3100  double sigma, area, offset;
3101  cpl_matrix * cov = NULL;
3102  skip_if(cpl_vector_fit_gaussian(xv, /*sigma_x*/NULL,
3103  yv, dyv,
3104  CPL_FIT_CENTROID | CPL_FIT_STDEV |
3105  CPL_FIT_AREA | CPL_FIT_OFFSET,
3106  x0, &sigma, &area, &offset,
3107  NULL, NULL,
3108  &cov));
3109  if (x0 && isnan(*x0)) {
3110  cpl_msg_warning(cpl_func, "1d gaussfit, could not determine mean");
3111  *x0 = -1.;
3112  }
3113  if (x0_err) {
3114  *x0_err = sqrt(cpl_matrix_get(cov, 0, 0));
3115  if (isnan(*x0_err)) {
3116  *x0_err = -1.;
3117  }
3118  }
3119  if (sigma) {
3120  *sigma_ = sigma;
3121  if (isnan(*sigma_)) {
3122  cpl_msg_warning(cpl_func,
3123  "1d gaussfit, could not determine sigma");
3124  *sigma_ = -1.;
3125  }
3126  }
3127  if (peak) {
3128  *peak = area / sqrt(2 * CPL_MATH_PI * sigma * sigma) + offset;
3129  if (isnan(*peak)) {
3130  cpl_msg_warning(cpl_func, "1d gaussfit, could not determine peak");
3131  *peak = -1.;
3132  }
3133  }
3134  if (peak_err) {
3135  double dsig = sqrt(cpl_matrix_get(cov, 1, 1));
3136  double dA = sqrt(cpl_matrix_get(cov, 2, 2));
3137  double dB = sqrt(cpl_matrix_get(cov, 3, 3));
3138  double pi2sig2 = 2 * CPL_MATH_PI * sigma * sigma;
3139  *peak_err = sqrt(dsig * dsig * area * area /
3140  (2 * CPL_MATH_PI * pi2sig2 * sigma * sigma) +
3141  dA * dA / pi2sig2 + dB * dB);
3142  if (isnan(*peak_err)) {
3143  *peak_err = -1.;
3144  }
3145  }
3146  if (sigma_err) {
3147  *sigma_err = sqrt(cpl_matrix_get(cov, 1, 1));
3148  if (isnan(*sigma_err)) {
3149  *sigma_err = -1.;
3150  }
3151  }
3152 
3153 cleanup:
3154  cpl_matrix_delete(cov);
3155 
3156  return cpl_error_get_code();
3157 }
double visir_pfits_get_volt1dcta9(const cpl_propertylist *self)
The VOLT1.DCTA9.
Definition: visir_pfits.c:811
double visir_pfits_get_volt1dctb9(const cpl_propertylist *self)
The VOLT1.DCTB9.
Definition: visir_pfits.c:823
double visir_pfits_get_volt2dcta9(const cpl_propertylist *self)
The VOLT2.DCTA9.
Definition: visir_pfits.c:835
int visir_pfits_get_navrg(const cpl_propertylist *self)
The NAVRG.
Definition: visir_pfits.c:323
cpl_error_code irplib_apertures_find_max_flux(const cpl_apertures *self, int *ind, int nfind)
Find the aperture(s) with the greatest flux.
const char * visir_pfits_get_filter(const cpl_propertylist *self)
The filter.
Definition: visir_pfits.c:349
const cpl_propertylist * irplib_framelist_get_propertylist_const(const irplib_framelist *self, int pos)
Get the propertylist of the specified frame in the framelist.
int visir_pfits_get_chop_ncycles(const cpl_propertylist *self)
The number of chopping cycles.
Definition: visir_pfits.c:237
int visir_pfits_get_ndit(const cpl_propertylist *self)
The NDIT keyword.
Definition: visir_pfits.c:502
double visir_pfits_get_volt2dctb9(const cpl_propertylist *self)
The VOLT2.DCTB9.
Definition: visir_pfits.c:847
cpl_frame * irplib_framelist_get(irplib_framelist *self, int pos)
Get the specified frame from the framelist.
const char * visir_pfits_get_insmode(const cpl_propertylist *self)
The mode.
Definition: visir_pfits.c:442
double visir_pfits_get_monoc_pos(const cpl_propertylist *self)
The INS.MONOC1.POS.
Definition: visir_pfits.c:490
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:309
const cpl_frame * irplib_framelist_get_const(const irplib_framelist *self, int pos)
Get the specified frame from the framelist.
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.
cpl_imagelist * visir_load_hcycle(const irplib_framelist *rawframes, int pos)
Load the HCYCLE images from a VISIR file.
int irplib_framelist_get_size(const irplib_framelist *self)
Get the size of a framelist.