VISIR Pipeline Reference Manual  4.1.0
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  }
467 
468  end_skip;
469 
470  return cpl_error_get_code();
471 }
472 
473 
474 /*----------------------------------------------------------------------------*/
483 /*----------------------------------------------------------------------------*/
484 cpl_parameter * visir_parameter_duplicate(const cpl_parameter * p)
485 {
486  cpl_parameter * np = NULL;
487 
488  cpl_ensure(p != NULL, CPL_ERROR_NULL_INPUT, NULL);
489  cpl_ensure(cpl_parameter_get_class(p) == CPL_PARAMETER_CLASS_VALUE,
490  CPL_ERROR_UNSUPPORTED_MODE, NULL);
491 
492  switch (cpl_parameter_get_type(p)) {
493  case CPL_TYPE_BOOL:
494  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
495  cpl_parameter_get_type(p),
496  cpl_parameter_get_help(p),
497  cpl_parameter_get_context(p),
498  cpl_parameter_get_default_bool(p));
499  cpl_parameter_set_bool(np, cpl_parameter_get_bool(p));
500  break;
501 
502  case CPL_TYPE_INT:
503  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
504  cpl_parameter_get_type(p),
505  cpl_parameter_get_help(p),
506  cpl_parameter_get_context(p),
507  cpl_parameter_get_default_int(p));
508  cpl_parameter_set_int(np, cpl_parameter_get_int(p));
509  break;
510 
511  case CPL_TYPE_DOUBLE:
512  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
513  cpl_parameter_get_type(p),
514  cpl_parameter_get_help(p),
515  cpl_parameter_get_context(p),
516  cpl_parameter_get_default_double(p));
517  cpl_parameter_set_double(np, cpl_parameter_get_double(p));
518  break;
519 
520  case CPL_TYPE_STRING:
521  np = cpl_parameter_new_value(cpl_parameter_get_name(p),
522  cpl_parameter_get_type(p),
523  cpl_parameter_get_help(p),
524  cpl_parameter_get_context(p),
525  cpl_parameter_get_default_string(p));
526  cpl_parameter_set_string(np, cpl_parameter_get_string(p));
527  break;
528 
529  default:
530  cpl_error_set_message(cpl_func, CPL_ERROR_UNSUPPORTED_MODE,
531  "Parameter has unknown type");
532  break;
533  }
534 
535  if (np == NULL)
536  return NULL;
537 
538  if (cpl_parameter_get_tag(p))
539  cpl_parameter_set_tag(np, cpl_parameter_get_tag(p));
540 
541  {
542  cpl_parameter_mode modes[] = {CPL_PARAMETER_MODE_CLI,
543  CPL_PARAMETER_MODE_CFG, CPL_PARAMETER_MODE_ENV};
544 
545  for (size_t i = 0; i < ARRAY_LEN(modes); i++) {
546  cpl_parameter_set_alias(np, modes[i],
547  cpl_parameter_get_alias(p, modes[i]));
548  if (!cpl_parameter_is_enabled(p, modes[i]))
549  cpl_parameter_disable(np, modes[i]);
550  }
551  }
552 
553  return np;
554 }
555 
556 
557 /*----------------------------------------------------------------------------*/
568 /*----------------------------------------------------------------------------*/
569 cpl_error_code visir_copy_parameters(cpl_parameterlist * dest,
570  const cpl_parameterlist * src)
571 {
572  for (const cpl_parameter * p = cpl_parameterlist_get_first_const(src);
573  p != NULL; p = cpl_parameterlist_get_next_const(src)) {
574  cpl_parameter * par =
575  cpl_parameterlist_find(dest, cpl_parameter_get_name(p));
576 
577  if (!par)
578  continue;
579 
580  cpl_type t = cpl_parameter_get_type(par);
581  if (t == CPL_TYPE_BOOL)
582  cpl_parameter_set_bool(par, cpl_parameter_get_bool(p));
583  else if (t == CPL_TYPE_INT)
584  cpl_parameter_set_int(par, cpl_parameter_get_int(p));
585  else if (t == CPL_TYPE_DOUBLE)
586  cpl_parameter_set_double(par, cpl_parameter_get_double(p));
587  else if (t == CPL_TYPE_STRING)
588  cpl_parameter_set_string(par, cpl_parameter_get_string(p));
589  else
590  bug_if(1);
591  }
592 
593  end_skip;
594 
595  return cpl_error_get_code();
596 }
597 
598 
599 /*----------------------------------------------------------------------------*/
608 /*----------------------------------------------------------------------------*/
609 cpl_recipe *
610 visir_init_recipe(const char * name, int (*get_info)(cpl_pluginlist *),
611  cpl_pluginlist * plugins)
612 {
613  cpl_recipe * recipe = cpl_calloc(1, sizeof(cpl_recipe));
614  get_info(plugins);
615  cpl_plugin * pl = cpl_pluginlist_find (plugins, name);
616  cpl_plugin_copy ((cpl_plugin *) & recipe->interface, pl);
617  return recipe;
618 }
619 
620 
621 /*----------------------------------------------------------------------------*/
632 /*----------------------------------------------------------------------------*/
633 cpl_error_code
634 visir_run_recipe(cpl_recipe * recipe,
635  cpl_frameset * framelist, const cpl_parameterlist * parlist,
636  cpl_error_code (*set_parlist)(cpl_parameterlist *,
637  const cpl_parameterlist *))
638 {
639  cpl_plugin * pl = &recipe->interface;
640  cpl_plugin_func plugin_func_init = cpl_plugin_get_init(pl);
641  cpl_plugin_func plugin_func_exec = cpl_plugin_get_exec(pl);
642  cpl_plugin_func plugin_func_deinit = cpl_plugin_get_deinit(pl);
643 
644  skip_if(0);
645 
646  recipe->frames = framelist;
647 
648  plugin_func_init(&recipe->interface);
649  if (set_parlist)
650  set_parlist(recipe->parameters, parlist);
651  plugin_func_exec(&recipe->interface);
652  plugin_func_deinit(&recipe->interface);
653 
654  end_skip;
655  return cpl_error_get_code();
656 }
657 
658 
659 /*----------------------------------------------------------------------------*/
675 /*----------------------------------------------------------------------------*/
676 cpl_frameset *
677 visir_prepare_frameset(const cpl_frameset * frameset,
678  const char ** tagmap, size_t ntags,
679  cpl_boolean reverse)
680 {
681  cpl_frameset * nlist = cpl_frameset_new();
682  cx_list * _nlist = cx_list_new();
683  cpl_ensure(ntags % 2 == 0, CPL_ERROR_ILLEGAL_INPUT, nlist);
684 
685  FOR_EACH_FRAMESET_C(frame, frameset) {
686  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_PRODUCT) {
687  cpl_frame * frm = cpl_frame_duplicate(frame);
688  cpl_frame_set_group(frm, CPL_FRAME_GROUP_RAW);
689  cpl_frame_set_level(frm, CPL_FRAME_LEVEL_NONE);
690  for (size_t i = 0; i < ntags; i += 2)
691  if (strcmp(tagmap[i], cpl_frame_get_tag(frm)) == 0)
692  cpl_frame_set_tag(frm, tagmap[i + 1]);
693 
694  cx_list_push_back(_nlist, frm);
695  }
696  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_CALIB)
697  cx_list_push_back(_nlist, cpl_frame_duplicate(frame));
698  }
699 
700  cx_list_sort(_nlist, (cx_compare_func)visir_cmp_frm_fn);
701  if (reverse) {
702  cx_list_reverse(_nlist);
703  }
704 
705  FOR_EACH(it, _nlist)
706  cpl_frameset_insert(nlist, cx_list_get(_nlist, it));
707  cx_list_delete(_nlist);
708 
709  return nlist;
710 }
711 
712 /* ---------------------------------------------------------------------------*/
721 /* ---------------------------------------------------------------------------*/
722 cpl_frameset * visir_remove_modified_calib(cpl_frameset * set)
723 {
724  cpl_frameset * cleanset = cpl_frameset_new();
725  FOR_EACH_FRAMESET(frame, set) {
726  if (cpl_frame_get_group(frame) == CPL_FRAME_GROUP_CALIB &&
727  strcmp(cpl_frame_get_tag(frame),
728  "STATIC_MASK") == 0) {
729  continue;
730  }
731  cpl_frameset_insert(cleanset, cpl_frame_duplicate(frame));
732  }
733  cpl_frameset_delete(set);
734 
735  return cleanset;
736 }
737 
738 
739 /*----------------------------------------------------------------------------*/
748 /*----------------------------------------------------------------------------*/
749 static cpl_frameset *
750 visir_wait_for_child(pid_t pid, FILE * rpipe)
751 {
752  char * rbuffer = NULL;
753  int status;
754  cpl_error_code err;
755  size_t size;
756  cpl_frameset * result = NULL;
757 
758  /* we must read before waiting for the child else the writes may block
759  * use stream to save us the EINTR and partial read loops
760  * pipes are all closed on error so fread will fail if child fails */
761  cpl_ensure(rpipe != NULL, CPL_ERROR_NULL_INPUT, NULL);
762  skip_if(fread(&err, sizeof(err), 1, rpipe) != 1);
763  cpl_error_set(cpl_func, err);
764 
765  skip_if(fread(&size, sizeof(size), 1, rpipe) != 1);
766 
767  rbuffer = cpl_malloc(size);
768  skip_if(fread(rbuffer, size, 1, rpipe) != 1);
769 
770  result = visir_frameset_deserialize(rbuffer, NULL);
771 
772  end_skip;
773 
774  /* wait for child to end */
775  if (waitpid(pid, &status, 0) != pid) {
776  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
777  "%s", strerror(errno));
778  }
779  else if (WIFSIGNALED(status)) {
780  cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
781  "Process killed by signal %d", WTERMSIG(status));
782  }
783  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
784  cpl_error_set_message(cpl_func, CPL_ERROR_UNSPECIFIED,
785  "Process failed with code %d",
786  WEXITSTATUS(status));
787  }
788  else
789  assert(1);
790 
791  cpl_free(rbuffer);
792 
793  return result;
794 }
795 
796 /* ---------------------------------------------------------------------------*/
804 /* ---------------------------------------------------------------------------*/
805 static void
806 remove_tempdir(const cpl_parameterlist * parlist, const char* recipename,
807  const char * tmpdir)
808 {
809  const cpl_boolean delete =
810  irplib_parameterlist_get_bool(parlist, PACKAGE,
811  recipename, "delete-temp");
812 
813  if (delete) {
814  char * cmd = cpl_sprintf("rm -rf \"%s\"", tmpdir);
815  cpl_msg_info(cpl_func, "Removing temporary directory: %s", tmpdir);
816  if (WEXITSTATUS(system(cmd)) != 0)
817  cpl_msg_warning(cpl_func, "Removing temporary "
818  "directory %s failed", tmpdir);
819  cpl_free(cmd);
820  }
821  else
822  cpl_msg_info(cpl_func, "Keeping temporary directory: %s", tmpdir);
823 }
824 
825 /*----------------------------------------------------------------------------*/
834 /*----------------------------------------------------------------------------*/
835 cpl_error_code
836 visir_tmpdir_exec(const char * recipename, cpl_plugin * plugin,
837  int (*function)(cpl_frameset *, const cpl_parameterlist *))
838 {
839  char tmpdir[strlen(recipename) + 8];
840  cpl_boolean have_tmpdir = CPL_FALSE;
841  cpl_errorstate cleanstate = cpl_errorstate_get();
842  cpl_recipe * recipe = (cpl_recipe *)plugin;
843  sprintf(tmpdir, "%s_XXXXXX", recipename);
844 
845  have_tmpdir = visir_get_tempdir(tmpdir);
846  skip_if(have_tmpdir != CPL_TRUE);
847 
848  cpl_msg_info(cpl_func, "Working in temporary directory: %s", tmpdir);
849 
850  /* go to tmpdir */
851  if (chdir(tmpdir) != 0) {
852  return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
853  "Could not change to temporary directory %s", tmpdir);
854  }
855 
856  /* correct relative paths for the change to the tmpdir */
857  FOR_EACH_FRAMESET(frm, recipe->frames) {
858  if (cpl_frame_get_filename(frm)[0] != '/') {
859  char * buf = cpl_sprintf("../%s", cpl_frame_get_filename(frm));
860  cpl_frame_set_filename(frm, buf);
861  cpl_free(buf);
862  }
863  }
864 
865  cpl_error_code err = cpl_recipedefine_exec(plugin, function)
866  ? (int)cpl_error_set_where(cpl_func) : 0;
867 
868  if (chdir("..") != 0) {
869  return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_IO,
870  "Could not change back to base directory");
871  }
872 
873  /* move end products from tmp to cwd */
874  skip_if(visir_move_products(recipe->frames, ".", tmpdir));
875 
876  end_skip;
877 
878  if (have_tmpdir) {
879  remove_tempdir(recipe->parameters, recipename, tmpdir);
880  }
881 
882  if (!cpl_errorstate_is_equal(cleanstate))
883  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
884 
885  return cpl_error_get_code();
886 }
887 
888 
889 /*----------------------------------------------------------------------------*/
898 /*----------------------------------------------------------------------------*/
899 cpl_error_code
900 visir_forking_exec(const char * recipename, cpl_plugin * plugin,
901  int (*function)(cpl_frameset *, const cpl_parameterlist *))
902 {
903  char tmpdir[strlen(recipename) + 8];
904  cpl_boolean have_tmpdir = CPL_FALSE;
905  cpl_errorstate cleanstate = cpl_errorstate_get();
906  cpl_recipe * recipe = (cpl_recipe *)plugin;
907  FILE * pipe_stream = NULL;
908  int pipe_fd[2];
909  static const int READ = 0, WRITE = 1;
910  sprintf(tmpdir, "%s_XXXXXX", recipename);
911 
912  /* ignore sigint in parent to ensure cleanup */
913  struct sigaction sa, sa_orig;
914  sa.sa_handler = SIG_IGN;
915  sigemptyset(&sa.sa_mask);
916  sa.sa_flags = 0;
917  skip_if(sigaction(SIGINT, &sa, &sa_orig) != 0);
918 
919  have_tmpdir = visir_get_tempdir(tmpdir);
920  skip_if(have_tmpdir != CPL_TRUE);
921 
922  cpl_msg_info(cpl_func, "Working in temporary directory: %s", tmpdir);
923 
924  skip_if(pipe(pipe_fd) != 0);
925 
926  pid_t pid = fork();
927  if (pid == -1) {
928  close(pipe_fd[READ]);
929  close(pipe_fd[WRITE]);
930  cpl_error_set_message(cpl_func, CPL_ERROR_UNSPECIFIED,
931  "fork failed: %s", strerror(errno));
932  skip_if(0);
933  }
934  else if (pid == 0) {
935  /* restore default signal handler */
936  sigaction(SIGINT, &sa_orig, NULL);
937 
938  /* close unused read and convert write to stream for convinience */
939  close(pipe_fd[READ]);
940  pipe_stream = fdopen(pipe_fd[WRITE], "w");
941 
942  /* go to tmpdir */
943  if (!pipe_stream || chdir(tmpdir) != 0) {
944  close(pipe_fd[WRITE]);
945  _exit(EXIT_FAILURE);
946  }
947 
948  /* correct relative paths for the change to the tmpdir */
949  FOR_EACH_FRAMESET(frm, recipe->frames) {
950  if (cpl_frame_get_filename(frm)[0] != '/') {
951  char * buf = cpl_sprintf("../%s", cpl_frame_get_filename(frm));
952  cpl_frame_set_filename(frm, buf);
953  cpl_free(buf);
954  }
955  }
956 
957  cpl_error_code err = cpl_recipedefine_exec(plugin, function)
958  ? (int)cpl_error_set_where(cpl_func) : 0;
959 
960  if (err == CPL_ERROR_NONE) {
961  assert((void*)recipe == (void*)plugin);
962  err = visir_send_frameset(pipe_stream, recipe->frames);
963  }
964  fclose(pipe_stream);
965  _exit(err);
966  }
967  else {
968  /* close unused write and convert read to stream for convinience */
969  close(pipe_fd[WRITE]);
970  pipe_stream = fdopen(pipe_fd[READ], "r");
971  skip_if(pipe_stream == NULL);
972 
973  recipe->frames = visir_wait_for_child(pid, pipe_stream);
974  fclose(pipe_stream);
975  skip_if(recipe->frames == NULL);
976 
977  /* move end products from tmp to cwd */
978  skip_if(visir_move_products(recipe->frames, ".", tmpdir));
979  }
980 
981  end_skip;
982 
983  if (have_tmpdir) {
984  remove_tempdir(recipe->parameters, recipename, tmpdir);
985  }
986 
987  if (!cpl_errorstate_is_equal(cleanstate))
988  cpl_errorstate_dump(cleanstate, CPL_FALSE, NULL);
989 
990  return cpl_error_get_code();
991 }
992 
993 /*----------------------------------------------------------------------------*/
1001 /*----------------------------------------------------------------------------*/
1002 double visir_utils_get_exptime(const int nnod, const cpl_propertylist * plist)
1003 {
1004  /* fits EXPTIME key is equal to DIT * NAVRG with NGC */
1005  /* DIT */
1006  const double dit = visir_pfits_get_dit(plist);
1007  /* NDIT */
1008  const int ndit = visir_pfits_get_ndit(plist);
1009  /* NAVRG */
1010  const int navrg = visir_pfits_get_navrg(plist);
1011  /* Number of chopping cycles */
1012  const int ncycles = visir_pfits_get_chop_ncycles(plist);
1013 
1014  /* exptime *= 2 because of chopping */
1015  const double value = 2 * dit * ndit * nnod * ncycles * navrg;
1016 
1017  if (value <= 0) {
1018  cpl_msg_error(cpl_func, "Illegal exposure time "
1019  "(dit=%g:ndit=%d:ncycles=%d:nnod=%d): %g",
1020  dit, ndit, ncycles, nnod, value);
1021  skip_if(1);
1022  }
1023 
1024  end_skip;
1025  return value;
1026 }
1027 
1028 /*----------------------------------------------------------------------------*/
1037 /*----------------------------------------------------------------------------*/
1038 double * visir_utils_get_wls(const irplib_framelist * self)
1039 {
1040  /* Get the number of files */
1041  const int size = irplib_framelist_get_size(self);
1042  double * wls = NULL;
1043  int i;
1044 
1045 
1046  skip_if (0);
1047 
1048  skip_if(irplib_framelist_contains(self, VISIR_PFITS_DOUBLE_MONOC_POS,
1049  CPL_TYPE_DOUBLE, CPL_FALSE, 0.0));
1050 
1051  /* Allocate the output array */
1052  wls = cpl_malloc(size * sizeof(double));
1053 
1054  /* Get the wavelengths */
1055  for (i=0; i < size; i++) {
1056  const cpl_propertylist * plist
1058 
1059  wls[i] = visir_pfits_get_monoc_pos(plist);
1060 
1061  skip_if (0);
1062  }
1063 
1064  end_skip;
1065 
1066  if (cpl_error_get_code()) {
1067  cpl_free(wls);
1068  wls = NULL;
1069  }
1070 
1071  return wls;
1072 }
1073 
1074 /*----------------------------------------------------------------------------*/
1084 /*----------------------------------------------------------------------------*/
1085 cpl_image * visir_create_disk_intimage(
1086  int nx,
1087  int ny,
1088  int x_pos,
1089  int y_pos,
1090  int radius)
1091 {
1092  cpl_image * intima;
1093  int * pintima;
1094  double dist;
1095  int i, j;
1096 
1097  /* Create the empty output image */
1098  intima = cpl_image_new(nx, ny, CPL_TYPE_INT);
1099  pintima = cpl_image_get_data_int(intima);
1100 
1101  /* Loop on the pixels */
1102  for (j=0;j<ny ; j++) {
1103  for (i=0;i<nx ; i++) {
1104  dist = (i+1-x_pos)*(i+1-x_pos)+(j+1-y_pos)*(j+1-y_pos);
1105  if (dist < radius*radius) {
1106  pintima[i+j*nx] = 1;
1107  } else {
1108  pintima[i+j*nx] = 0;
1109  }
1110  }
1111  }
1112  return intima;
1113 }
1114 
1115 /*----------------------------------------------------------------------------*/
1126 /*----------------------------------------------------------------------------*/
1127 cpl_image * visir_create_ring_intimage(
1128  int nx,
1129  int ny,
1130  int x_pos,
1131  int y_pos,
1132  int radius1,
1133  int radius2)
1134 {
1135  cpl_image * intima;
1136  int * pintima;
1137  double dist;
1138  if (radius1 >= radius2) {
1139  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1140  "Small ring radius %d larger than big "
1141  "ring radius %d", radius1, radius2);
1142  return NULL;
1143  }
1144  if ((nx - x_pos) < radius2 || x_pos < radius2 ||
1145  (ny - y_pos) < radius2 || y_pos < radius2) {
1146  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1147  "Image of size [%d,%d] with object at "
1148  "[%d,%d] too small to create ring mask of "
1149  "radius %d", nx, ny, x_pos, y_pos, radius2);
1150  return NULL;
1151  }
1152 
1153  /* Create the empty output image */
1154  intima = cpl_image_new(nx, ny, CPL_TYPE_INT);
1155  pintima = cpl_image_get_data_int(intima);
1156 
1157  /* Loop on the pixels */
1158  for (int j=0;j<ny ; j++) {
1159  for (int i=0;i<nx ; i++) {
1160  dist = (i+1-x_pos)*(i+1-x_pos)+(j+1-y_pos)*(j+1-y_pos);
1161  if ((dist < radius2*radius2) && (dist > radius1*radius1)) {
1162  pintima[i+j*nx] = 1;
1163  } else {
1164  pintima[i+j*nx] = 0;
1165  }
1166  }
1167  }
1168  return intima;
1169 }
1170 
1171 /*----------------------------------------------------------------------------*/
1182 /*----------------------------------------------------------------------------*/
1183 double visir_image_sigma_clip(const cpl_image * self, double * pstdev)
1184 {
1185  const int dimx = cpl_image_get_size_x(self);
1186  const int dimy = cpl_image_get_size_y(self);
1187  const cpl_type type = cpl_image_get_type(self);
1188  /* Apply a 3x3 median filter to the input image */
1189  /* FIXME: A wrap (around float/double) would be sufficient */
1190  cpl_image * noise = cpl_image_new(dimx, dimy, type);
1191  cpl_mask * bpm = NULL;
1192  cpl_mask * kernel = cpl_mask_new(3, 3);
1193  const int niterations = 5;
1194  const double sigma = 3.0;
1195  const double median_corr = 0.94;
1196  double bgnoise = -1;
1197  int i = -1;
1198 
1199 
1200  bug_if (0);
1201 
1202  bug_if(cpl_mask_not(kernel));
1203  bug_if(cpl_image_filter_mask(noise, self, kernel, CPL_FILTER_MEDIAN,
1204  CPL_BORDER_FILTER));
1205 
1206  /* (Inverted) Noise image */
1207  bug_if (cpl_image_subtract(noise, self));
1208 
1209  for (i=0; i < niterations ; i++) {
1210  /* Compute mean and stdev */
1211  cpl_stats * stats =
1212  cpl_stats_new_from_image(noise, CPL_STATS_MEAN | CPL_STATS_STDEV);
1213 
1214  const double mean = cpl_stats_get_mean(stats);
1215  const double stdev = cpl_stats_get_stdev(stats);
1216 
1217  /* Set the thresholds */
1218  const double low_thresh = mean - sigma * stdev;
1219  const double high_thresh = mean + sigma * stdev;
1220 
1221 
1222  cpl_stats_delete(stats); /* :-( */
1223 
1224  /* The previous thresholding may have left too few good pixels */
1225  skip_if (0);
1226 
1227  /* Identify where mean-sigma*stdev < noise < mean+sigma*stdev */
1228  bpm = cpl_mask_threshold_image_create(noise, low_thresh, high_thresh);
1229 
1230  bug_if (0);
1231 
1232  bug_if (cpl_mask_not(bpm));
1233 
1234  bug_if (cpl_image_reject_from_mask(noise, bpm));
1235 
1236  cpl_mask_delete(bpm); /* :-( */
1237  bpm = NULL;
1238 
1239  }
1240 
1241  if (pstdev != NULL) {
1242  /* Compute the stdev of the noise and the background */
1243 
1244  cpl_stats * stats =
1245  cpl_stats_new_from_image(noise, CPL_STATS_MEAN | CPL_STATS_STDEV);
1246 
1247  /* Compute mean and stdev */
1248  bgnoise = cpl_stats_get_stdev(stats);
1249  *pstdev = cpl_image_get_median(self) - cpl_stats_get_mean(stats);
1250 
1251  cpl_stats_delete(stats);
1252  } else {
1253  /* Compute the stdev */
1254  bgnoise = cpl_image_get_stdev(noise);
1255  }
1256 
1257  bug_if(0);
1258 
1259  bgnoise /= median_corr;
1260 
1261  end_skip;
1262 
1263  if (cpl_error_get_code())
1264  cpl_msg_error(cpl_func, "Computation of background noise using sigma=%g"
1265  " failed in iteration %d of %d", sigma, i+1, niterations);
1266 
1267  cpl_mask_delete(kernel);
1268  cpl_mask_delete(bpm);
1269  cpl_image_delete(noise);
1270 
1271  return bgnoise;
1272 }
1273 
1274 /*----------------------------------------------------------------------------*/
1284 /*----------------------------------------------------------------------------*/
1285 double visir_img_phot_sigma_clip(const cpl_image * self)
1286 {
1287 
1288  const double noise = visir_image_sigma_clip(self, NULL);
1289 
1290  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), noise);
1291 
1292  return noise;
1293 
1294 }
1295 
1296 /*----------------------------------------------------------------------------*/
1314 /*----------------------------------------------------------------------------*/
1315 int visir_star_find(const cpl_vector * v_ra, const cpl_vector * v_dec,
1316  double ra, double dec, double maxdist, double * pdist)
1317 {
1318  const int nra = cpl_vector_get_size(v_ra);
1319  const int ndec = cpl_vector_get_size(v_dec);
1320  double dmin = 0.0; /* Avoid (false) uninit warning */
1321  int minind = 0;
1322  int i;
1323 
1324 
1325  /* Catch NULL input */
1326  cpl_ensure(nra > 0, cpl_error_get_code(), -2);
1327  cpl_ensure(ndec > 0, cpl_error_get_code(), -3);
1328 
1329  /* It would be natural to use a cpl_bivector for the positions,
1330  but since CPL cannot ensure that a bivector comprises two vectors
1331  of the same length this would be pointless :-( */
1332  cpl_ensure(nra == ndec, CPL_ERROR_INCOMPATIBLE_INPUT, -4);
1333 
1334  cpl_ensure(maxdist >= 0, CPL_ERROR_ILLEGAL_INPUT, -5);
1335 
1336  /* Find the index of the star closest to the given coordinate */
1337  for (i=0 ; i < nra ; i++) {
1338  const double rai = cpl_vector_get(v_ra, i);
1339  const double deci = cpl_vector_get(v_dec, i);
1340  const double gdist = visir_great_circle_dist(rai, deci, ra, dec);
1341 
1342  cpl_msg_debug(cpl_func, "DISTANCE (RAi,DECi)=(%g,%g) <=> "
1343  "(RA,DEC)=(%g,%g): %g", rai, deci, ra, dec, gdist);
1344 
1345  if (i == 0 || gdist < dmin) {
1346  minind = i;
1347  dmin = gdist;
1348  }
1349  }
1350 
1351  if (pdist != NULL) *pdist = dmin;
1352 
1353  /* Check that it is close enough */
1354  if (dmin > maxdist) {
1355  cpl_msg_error(cpl_func, "Nearest standard star (%d of %d) at (RA,DEC)="
1356  "(%g,%g) is too distant from (RA,DEC)=(%g, %g): %g > %g",
1357  1+minind, nra, cpl_vector_get(v_ra, minind),
1358  cpl_vector_get(v_dec, minind), ra, dec, dmin, maxdist);
1359  cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, -1);
1360  }
1361 
1362  return minind;
1363 }
1364 
1365 /*----------------------------------------------------------------------------*/
1384 /*----------------------------------------------------------------------------*/
1385 cpl_error_code visir_star_convert(const char * line, int ra_hh, int ra_mm,
1386  double ra_ss, char isign, int dec_hh,
1387  int dec_mm, double dec_ss,
1388  const double * jys, int njys,
1389  double * pra, double * pdec)
1390 {
1391 
1392  double sign;
1393  int i;
1394 
1395  assert( line );
1396  assert( pra );
1397  assert( pdec );
1398  assert( jys );
1399  assert( njys > 0 );
1400 
1401 
1402  if (isign == '+')
1403  sign = 1.0;
1404  else if (isign == '-')
1405  sign = -1.0;
1406  else {
1407  cpl_msg_error(cpl_func, "Line has illegal declination-sign character "
1408  "(%c): %s", isign, line);
1409  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1410  }
1411 
1412  if (ra_hh < 0) {
1413  cpl_msg_error(cpl_func, "Line has negative RA hh (%d): %s",
1414  ra_hh, line);
1415  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1416  }
1417  if (ra_mm < 0) {
1418  cpl_msg_error(cpl_func, "Line has negative RA mm (%d): %s",
1419  ra_hh, line);
1420  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1421  }
1422  if (ra_mm >= 60) {
1423  cpl_msg_error(cpl_func, "Line has too large RA mm (%d): %s ",
1424  ra_mm, line);
1425  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1426  }
1427  if (ra_ss < 0) {
1428  cpl_msg_error(cpl_func, "Line has negative RA ss (%g): %s",
1429  ra_ss, line);
1430  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1431  }
1432  if (ra_ss >= 60) {
1433  cpl_msg_error(cpl_func, "Line has too large RA ss (%g): %s ",
1434  ra_ss, line);
1435  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1436  }
1437 
1438  if (dec_hh < 0) {
1439  cpl_msg_error(cpl_func, "Line has negative DEC hh (%d): %s",
1440  dec_hh, line);
1441  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1442  }
1443  if (dec_mm < 0) {
1444  cpl_msg_error(cpl_func, "Line has negative DEC mm (%d): %s",
1445  dec_hh, line);
1446  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1447  }
1448  if (dec_mm >= 60) {
1449  cpl_msg_error(cpl_func, "Line has too large DEC mm (%d): %s ",
1450  dec_mm, line);
1451  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1452  }
1453  if (dec_ss < 0) {
1454  cpl_msg_error(cpl_func, "Line has negative DEC ss (%g): %s",
1455  dec_ss, line);
1456  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1457  }
1458  if (dec_ss >= 60) {
1459  cpl_msg_error(cpl_func, "Line has too large DEC ss (%g): %s ",
1460  dec_ss, line);
1461  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1462  }
1463 
1464  *pra = ra_hms2deg(ra_hh, ra_mm, ra_ss);
1465  if (*pra >= 360.0) {
1466  cpl_msg_error(cpl_func, "Line has too large RA (%g): %s ",
1467  *pra, line);
1468  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1469  }
1470 
1471  *pdec = sign * dec_hms2deg(dec_hh, dec_mm, dec_ss);
1472  if (*pdec > 90.0) {
1473  cpl_msg_error(cpl_func, "Line has too large RA (%g): %s ",
1474  *pdec, line);
1475  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1476  }
1477  if (*pdec < -90.0) {
1478  cpl_msg_error(cpl_func, "Line has too small RA (%g): %s ",
1479  *pdec, line);
1480  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1481  }
1482 
1483  for (i=0; i < njys; i++) if (jys[i] <= 0.0) {
1484  cpl_msg_error(cpl_func,"Line has non-positive Jy value (%g) at %d: %s ",
1485  jys[i], i+1, line);
1486  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1487  }
1488 
1489  return CPL_ERROR_NONE;
1490 
1491 }
1492 
1493 /*----------------------------------------------------------------------------*/
1506 /*----------------------------------------------------------------------------*/
1507 cpl_table * visir_table_new_xypos(const cpl_imagelist * images,
1508  const char * label)
1509 {
1510  cpl_errorstate cleanstate = cpl_errorstate_get();
1511  const int nsize = cpl_imagelist_get_size(images);
1512  double psigmas[] = {5, 2, 1, 0.5}; /* Actually not modified */
1513  cpl_vector * sigmas = NULL;
1514  cpl_table * self = NULL;
1515  const int nsigmas = sizeof(psigmas)/sizeof(double);
1516  int isflux = 0;
1517  int nfail, i;
1518 
1519  cpl_ensure(nsize > 0, cpl_error_get_code(), NULL);
1520  cpl_ensure(label, CPL_ERROR_NULL_INPUT, NULL);
1521  cpl_ensure(!strcmp(label, "FLUX") || !strcmp(label, "FWHM"),
1522  CPL_ERROR_UNSUPPORTED_MODE, NULL);
1523 
1524  self = cpl_table_new(nsize);
1525 
1526  skip_if (cpl_table_new_column(self, "X_POS", CPL_TYPE_DOUBLE));
1527  skip_if (cpl_table_new_column(self, "Y_POS", CPL_TYPE_DOUBLE));
1528 
1529  if (!strcmp(label,"FLUX")) {
1530  isflux = 1;
1531  skip_if (cpl_table_new_column(self, label, CPL_TYPE_DOUBLE));
1532  } else {
1533  skip_if (cpl_table_new_column(self, "X_FWHM", CPL_TYPE_DOUBLE));
1534  skip_if (cpl_table_new_column(self, "Y_FWHM", CPL_TYPE_DOUBLE));
1535  }
1536 
1537  sigmas = cpl_vector_wrap(4, psigmas);
1538  skip_if (sigmas == NULL);
1539 
1540  cpl_msg_info(cpl_func, "Detecting apertures using %d sigma-levels "
1541  "ranging from %g down to %g", nsigmas, psigmas[0],
1542  psigmas[nsigmas-1]);
1543 
1544  /* Object detection */
1545  nfail = 0;
1546  for (i=0 ; i < nsize ; i++) {
1547  const cpl_image * image = cpl_imagelist_get_const(images, i);
1548  cpl_apertures * apert;
1549  cpl_size isigma;
1550 
1551 
1552  double posx = -1;
1553  double posy = -1;
1554  double fwhmx = -1;
1555  double fwhmy = -1;
1556  double flux = -1;
1557  int iflux;
1558 
1559  skip_if (0);
1560 
1561  /* Find any apertures in each image */
1562  apert = cpl_apertures_extract(image, sigmas, &isigma);
1563 
1564  if (apert != NULL && cpl_error_get_code()) {
1565  /* FIX for DFS 2616 */
1566  cpl_msg_error(cpl_func, "cpl_apertures_extract() returned non-NULL "
1567  "while setting the CPL error-state to '%s' at '%s'",
1568  cpl_error_get_message(), cpl_error_get_where());
1569  cpl_msg_debug(cpl_func, "Deleting the spurious aperture list at %p:",
1570  (void*)apert);
1571  if (cpl_msg_get_level() <= CPL_MSG_DEBUG)
1572  cpl_apertures_dump(apert, stdout);
1573  cpl_apertures_delete(apert);
1574  apert = NULL;
1575  }
1576 
1577  if (apert != NULL &&
1578  !irplib_apertures_find_max_flux(apert, &iflux, 1) &&
1579  cpl_apertures_get_flux(apert, iflux) > 0) {
1580 
1581  posx = cpl_apertures_get_centroid_x(apert, iflux);
1582  posy = cpl_apertures_get_centroid_y(apert, iflux);
1583  flux = cpl_apertures_get_flux(apert, iflux);
1584  if (!isflux)
1585  cpl_image_get_fwhm(image, (int)posx, (int)posy, &fwhmx, &fwhmy);
1586 
1587  cpl_msg_info(cpl_func, "Detected an aperture with flux=%g at "
1588  "sigma=%g, at position: %g %g", flux,
1589  psigmas[isigma], posx, posy);
1590 
1591  }
1592 
1593  if (apert == NULL || cpl_error_get_code()) {
1594  visir_error_reset("Aperture detection in image %d of %d failed",
1595  i+1, nsize);
1596  nfail++;
1597  } else if (flux <= 0) {
1598  cpl_msg_warning(cpl_func, "Ignoring %d-pixel aperture %d (out of "
1599  "%d) in file %d of %d with non-positive flux: %g",
1600  (int)cpl_apertures_get_npix(apert, iflux), iflux,
1601  (int)cpl_apertures_get_size(apert), i+1, nsize,
1602  flux);
1603  nfail++;
1604  }
1605 
1606  cpl_apertures_delete(apert);
1607  apert = NULL;
1608 
1609  skip_if (cpl_table_set_double(self, "X_POS", i, posx));
1610  skip_if (cpl_table_set_double(self, "Y_POS", i, posy));
1611 
1612  if (isflux)
1613  skip_if (cpl_table_set_double(self, "FLUX", i, flux));
1614  else {
1615  skip_if (cpl_table_set_double(self, "X_FWHM", i, fwhmx));
1616  skip_if (cpl_table_set_double(self, "Y_FWHM", i, fwhmy));
1617  }
1618 
1619  }
1620 
1621  /* Check if some detections were successful */
1622  if (nfail == nsize) {
1623  cpl_msg_error(cpl_func, "Aperture detection failed in all %d images",
1624  nsize);
1625  visir_error_set(CPL_ERROR_DATA_NOT_FOUND);
1626  skip_if(1);
1627  }
1628 
1629  end_skip;
1630 
1631  cpl_vector_unwrap(sigmas);
1632 
1633  if (self && cpl_error_get_code()) {
1634  cpl_table_delete(self);
1635  self = NULL;
1636  }
1637 
1638  return self;
1639 }
1640 
1641 /*----------------------------------------------------------------------------*/
1648 /*----------------------------------------------------------------------------*/
1649 int visir_vector_minpos(const cpl_vector * v)
1650 {
1651  const double * x = cpl_vector_get_data_const(v);
1652  const int n = cpl_vector_get_size(v);
1653  int minpos = 0;
1654  int i;
1655 
1656  cpl_ensure(x, CPL_ERROR_NULL_INPUT, -1);
1657 
1658  for (i = 1; i < n; i++) if (x[i] < x[minpos]) minpos = i;
1659 
1660  return minpos;
1661 }
1662 
1663 /*----------------------------------------------------------------------------*/
1678 /*----------------------------------------------------------------------------*/
1679 cpl_error_code visir_bivector_load(cpl_bivector * self, FILE * stream)
1680 {
1681  cpl_vector * v1;
1682  cpl_vector * v2;
1683  int np = 0;
1684  int xsize, ysize;
1685  char line[1024];
1686 
1687  cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);
1688  cpl_ensure_code(stream, CPL_ERROR_NULL_INPUT);
1689 
1690  /* Create and fill the vectors */
1691  v1 = cpl_bivector_get_x(self);
1692  v2 = cpl_bivector_get_y(self);
1693 
1694  xsize = cpl_vector_get_size(v1);
1695  ysize = cpl_vector_get_size(v2);
1696 
1697  while (fgets(line, 1024, stream) != NULL) {
1698  double x, y;
1699  if (line[0] != '#' && sscanf(line, "%lg %lg", &x, &y) == 2) {
1700  /* Found new element-pair
1701  - increase vector sizes if necessary,
1702  - insert element at end and
1703  - increment size counter */
1704  if (np == xsize) {
1705  xsize *= 2;
1706  cpl_vector_set_size(v1, xsize);
1707  }
1708  if (np == ysize) {
1709  ysize *= 2;
1710  cpl_vector_set_size(v2, ysize);
1711  }
1712  cpl_vector_set(v1, np, x);
1713  cpl_vector_set(v2, np, y);
1714  np++;
1715  }
1716  }
1717 
1718  /* Check that the loop ended due to eof and not an error */
1719  cpl_ensure_code(!ferror(stream), CPL_ERROR_FILE_IO);
1720 
1721  /* Check that the file was not empty and set the size to its true value */
1722  if (np == 0 || cpl_vector_set_size(v1, np) || cpl_vector_set_size(v2, np)) {
1723  cpl_ensure_code(0, CPL_ERROR_BAD_FILE_FORMAT);
1724  }
1725 
1726  return CPL_ERROR_NONE;
1727 
1728 }
1729 
1730 /*----------------------------------------------------------------------------*/
1743 /*----------------------------------------------------------------------------*/
1744 double visir_star_dist_min(const double * pras, const double * pdecs, int nloc,
1745  int * piloc1, int * piloc2)
1746 {
1747 
1748  int i, j;
1749  double dmin = 180;
1750 
1751 
1752  assert( pras != NULL);
1753  assert( pdecs != NULL);
1754  assert( piloc1 != NULL);
1755  assert( piloc2 != NULL);
1756  assert( nloc > 0 );
1757 
1758  for (j = 0; j < nloc; j++) {
1759  for (i = 0; i < j; i++) {
1760  const double dist = visir_great_circle_dist(pras[i], pdecs[i],
1761  pras[j], pdecs[j]);
1762  if (dist < dmin) {
1763  dmin = dist;
1764  *piloc1 = i;
1765  *piloc2 = j;
1766  }
1767  if (dist < VISIR_STAR_MAX_RADIUS)
1768  cpl_msg_warning(cpl_func,"The two stars (%d,%d) have a distance"
1769  ": %g < %g", i, j, dist, VISIR_STAR_MAX_RADIUS);
1770  }
1771  }
1772 
1773  return dmin;
1774 }
1775 
1776 
1777 /*----------------------------------------------------------------------------*/
1791 /*----------------------------------------------------------------------------*/
1792 const char ** visir_framelist_set_tag(irplib_framelist * self,
1793  char * (*pftag)(const cpl_frame *,
1794  const cpl_propertylist *,
1795  int),
1796  int *pntags)
1797 {
1798 
1799  /* FIXME: Copied from NACO - move to irplib */
1800 
1801  const char ** taglist = NULL; /* Must be initialized due to realloc call */
1802  int iframe, size;
1803 
1804  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
1805  cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL);
1806  cpl_ensure(pftag != NULL, CPL_ERROR_NULL_INPUT, NULL);
1807  cpl_ensure(pntags != NULL, CPL_ERROR_NULL_INPUT, NULL);
1808 
1809  size = irplib_framelist_get_size(self);
1810 
1811  cpl_ensure(size > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);
1812 
1813  *pntags = 0;
1814 
1815  for (iframe = 0; iframe < size ; iframe++) {
1816  cpl_frame * frame = irplib_framelist_get(self, iframe);
1817  const cpl_propertylist * plist
1819  char * tag;
1820  const char * newtag;
1821  int i;
1822 
1823 
1824  /* This should really be an assert() */
1825  cpl_ensure(frame != NULL, CPL_ERROR_ILLEGAL_INPUT, NULL);
1826  cpl_ensure(plist != NULL, CPL_ERROR_ILLEGAL_INPUT, NULL);
1827 
1828  tag = (*pftag)(frame, plist, iframe);
1829 
1830  cpl_ensure(tag != NULL, cpl_error_get_code(), NULL);
1831 
1832  /* From this point on failures should not really happen */
1833 
1834  (void)cpl_frame_set_tag(frame, tag);
1835  cpl_free(tag);
1836 
1837  newtag = cpl_frame_get_tag(frame);
1838 
1839  cpl_ensure(!cpl_error_get_code(), cpl_error_get_code(), NULL);
1840 
1841  /* Compare the new tags with those of previous frames */
1842  for (i=0; i < *pntags; i++)
1843  if (strcmp(newtag, taglist[i]) == 0) break;
1844 
1845  if (i == *pntags) {
1846  /* The new tag is different from the previous ones
1847  - add it to the list */
1848  (*pntags)++;
1849  taglist = (const char **)cpl_realloc(taglist, *pntags *
1850  sizeof(const char *));
1851  taglist[i] = newtag;
1852  }
1853 
1854  }
1855 
1856  return taglist;
1857 
1858 }
1859 
1860 /*----------------------------------------------------------------------------*/
1870 /*----------------------------------------------------------------------------*/
1871 cpl_error_code visir_qc_append_background(cpl_propertylist * self,
1872  const irplib_framelist * rawframes,
1873  int icol1, int icol2)
1874 {
1875 
1876  /* Compute the background values of the HCYCLE frames */
1877  const double bg_mean = visir_hcycle_background(rawframes, icol1, icol2);
1878 
1879  skip_if (0);
1880 
1881  bug_if (cpl_propertylist_append_double(self, "ESO QC BACKGD MEAN",
1882  bg_mean));
1883 
1884  end_skip;
1885 
1886  return cpl_error_get_code();
1887 
1888 }
1889 
1890 
1891 /*----------------------------------------------------------------------------*/
1900 /*----------------------------------------------------------------------------*/
1901 cpl_error_code visir_qc_append_capa(cpl_propertylist * self,
1902  const irplib_framelist * rawframes)
1903 {
1904 
1905  cpl_errorstate cleanstate = cpl_errorstate_get();
1906  const cpl_propertylist * plist
1908  const char * capa;
1909 
1910 
1911  bug_if (0);
1912 
1913  capa = visir_get_capa(plist);
1914 
1915  if (cpl_error_get_code()) {
1916  /* ignore error as keys are not defined for aqu data */
1917  //visir_error_reset("Could not determine capa");
1918  cpl_msg_info(cpl_func, "Could not determine capa");
1919  cpl_errorstate_set(cleanstate);
1920  } else {
1921  bug_if (cpl_propertylist_append_string(self, "ESO QC CAPA", capa));
1922  }
1923 
1924  end_skip;
1925 
1926  return cpl_error_get_code();
1927 
1928 }
1929 
1930 /*----------------------------------------------------------------------------*/
1938 /*----------------------------------------------------------------------------*/
1939 cpl_error_code visir_qc_append_filter(cpl_propertylist * self,
1940  const irplib_framelist * rawframes)
1941 {
1942 
1943  const cpl_propertylist * plist
1945  const char * value = visir_pfits_get_filter(plist);
1946 
1947 
1948  skip_if (0);
1949 
1950  bug_if (cpl_propertylist_append_string(self, "ESO QC FILTER", value));
1951 
1952  end_skip;
1953 
1954  return cpl_error_get_code();
1955 
1956 }
1957 
1958 /*----------------------------------------------------------------------------*/
1966 /*----------------------------------------------------------------------------*/
1967 cpl_error_code visir_qc_append_exptime(cpl_propertylist * self,
1968  const irplib_framelist * rawframes)
1969 {
1970  const cpl_propertylist * plist
1972 
1973  /* NNOD */
1974  const int nnod = irplib_framelist_get_size(rawframes);
1975  /* Get the total exposure time */
1976  const double value = visir_utils_get_exptime(nnod, plist);
1977 
1978  skip_if (0);
1979 
1980  bug_if (cpl_propertylist_append_double(self, "ESO QC EXPTIME", value));
1981 
1982  end_skip;
1983 
1984  return cpl_error_get_code();
1985 
1986 }
1987 
1990 /*----------------------------------------------------------------------------*/
2001 /*----------------------------------------------------------------------------*/
2002 static double visir_great_circle_dist(double ra1, double dec1,
2003  double ra2, double dec2)
2004 {
2005 
2006  /* Convert all input from degrees to radian - and back for the result */
2007  const double dra = sin( CPL_MATH_RAD_DEG * (ra2 - ra1 )/2.0 );
2008  const double ddec = sin( CPL_MATH_RAD_DEG * (dec2 - dec1)/2.0 );
2009 
2010  dec1 *= CPL_MATH_RAD_DEG;
2011  dec2 *= CPL_MATH_RAD_DEG;
2012 
2013  return 2.0 * asin(sqrt( ddec*ddec + cos(dec1)*cos(dec2)*dra*dra))
2014  * CPL_MATH_DEG_RAD;
2015 
2016 }
2017 
2018 /*----------------------------------------------------------------------------*/
2031 /*----------------------------------------------------------------------------*/
2032 static double ra_hms2deg(int hh, int mm, double ss)
2033 {
2034  return 15.0 * dec_hms2deg(hh, mm, ss);
2035 }
2036 
2037 /*----------------------------------------------------------------------------*/
2049 /*----------------------------------------------------------------------------*/
2050 static double dec_hms2deg(int dd, int mm, double ss)
2051 {
2052  return ((double)ss/60.0 + (double)mm)/60.0 + dd;
2053 }
2054 
2055 /*----------------------------------------------------------------------------*/
2068 /*----------------------------------------------------------------------------*/
2069 static double visir_hcycle_background(const irplib_framelist * rawframes,
2070  int icol1, int icol2)
2071 {
2072  cpl_imagelist * iset = NULL;
2073  /* Get the number of files */
2074  const int nfiles = irplib_framelist_get_size(rawframes);
2075  double bgsum = 0;
2076  double bgmean = -1;
2077  int nsum = 0;
2078  int i, j;
2079 
2080 
2081  skip_if (nfiles < 1);
2082 
2083  if (icol1 == 0) icol1 = VISIR_BACKGD_START;
2084  if (icol2 == 0) icol2 = VISIR_BACKGD_STOP;
2085 
2086  cpl_msg_info(cpl_func, "Computing Half-cycle background level from column %d "
2087  "through %d", icol1, icol2);
2088 
2089  /* Loop on the hcycles images */
2090  for (i=0; i < nfiles; i++) {
2091 
2092  iset = visir_load_hcycle(rawframes, i);
2093 
2094  skip_if (0);
2095 
2096  for (j = 0; j < cpl_imagelist_get_size(iset) ; j++) {
2097  const double median =
2098  cpl_image_get_median_window(cpl_imagelist_get(iset, j),
2099  VISIR_BACKGD_START, icol1,
2100  VISIR_BACKGD_STOP, icol2);
2101 
2102  skip_if (0);
2103 
2104  if (median != median) {
2105  const cpl_frame * frame = irplib_framelist_get_const(rawframes,
2106  i);
2107  /* Some Comm. I data contains NaNs */
2108  cpl_msg_error(cpl_func, "Image window (%d, %d, %d, %d) "
2109  "(image %d of %d) in %s (frame %d of %d) "
2110  "has NaN median",
2111  VISIR_BACKGD_START, icol1,
2112  VISIR_BACKGD_STOP, icol2,
2113  j+1, (int)cpl_imagelist_get_size(iset),
2114  cpl_frame_get_filename(frame), i+1, nfiles);
2115  visir_error_set(CPL_ERROR_BAD_FILE_FORMAT);
2116  skip_if(1);
2117  }
2118  bgsum += median;
2119  }
2120  nsum += j;
2121  cpl_imagelist_delete(iset);
2122  iset = NULL;
2123  }
2124 
2125  /* Test if there are some HCYCLE frames */
2126  skip_if (nsum < 1);
2127 
2128  bgmean = bgsum / nsum;
2129 
2130  end_skip;
2131 
2132  cpl_imagelist_delete(iset);
2133 
2134  /* The background was requested to not include the offset correction */
2135  return bgmean - VISIR_HCYCLE_OFFSET;
2136 }
2137 
2138 
2139 /*----------------------------------------------------------------------------*/
2150 /*----------------------------------------------------------------------------*/
2151 cpl_error_code
2152 visir_get_subpixel_maxpos(const cpl_image * img, cpl_size x, cpl_size y,
2153  double * xsub, double * ysub)
2154 {
2155  int bad;
2156  const cpl_size nx = cpl_image_get_size_x(img);
2157  const cpl_size ny = cpl_image_get_size_y(img);
2158 
2159  *xsub = 0;
2160  *ysub = 0;
2161  if (x - 1 > 0 && x + 1 <= nx) {
2162  double sub[] = {
2163  cpl_image_get(img, x - 1, y, &bad),
2164  cpl_image_get(img, x - 0, y, &bad),
2165  cpl_image_get(img, x + 1, y, &bad),
2166  };
2167  if (!bad)
2168  *xsub = 0.5 * (sub[0] - sub[2])/(sub[0] - 2*sub[1] + sub[2]);
2169  }
2170  if (y - 1 > 0 && y + 1 <= ny) {
2171  double sub[] = {
2172  cpl_image_get(img, x, y - 1, &bad),
2173  cpl_image_get(img, x, y - 0, &bad),
2174  cpl_image_get(img, x, y + 1, &bad),
2175  };
2176  if (!bad)
2177  *ysub = 0.5 * (sub[0] - sub[2])/(sub[0] - 2*sub[1] + sub[2]);
2178  }
2179 
2180  return cpl_error_get_code();
2181 }
2182 
2183 static inline unsigned long get_msb(unsigned long a)
2184 {
2185  /* dumb integer msb, (63 - __builtin_clzl(a)) would be faster */
2186  unsigned long msb = 0;
2187  while (a >>= 1) {
2188  msb++;
2189  }
2190  return msb;
2191 }
2192 
2193 /* ---------------------------------------------------------------------------*/
2202 /* ---------------------------------------------------------------------------*/
2203 size_t visir_get_next_regular(size_t a)
2204 {
2205  /* fftw can also deal efficiently with factors of 7 and 13 but it needs
2206  * testing that the speed of these algorithms will not cancel out the gain
2207  * of smaller input sizes */
2208  if (a <= 6)
2209  return a;
2210  /* is already power of 2 */
2211  if ((a & (a - 1)) == 0)
2212  return a;
2213  /* overflow */
2214  if (5 > SIZE_MAX / a)
2215  return a;
2216 
2217  size_t match = SIZE_MAX;
2218  size_t p5 = 1;
2219  while(p5 < a) {
2220  size_t p35 = p5;
2221  while (p35 < a) {
2222  /* ceil division */
2223  size_t quotient = a % p35 != 0 ? a / p35 + 1 : a / p35;
2224  /* next power of two of quotient */
2225  size_t p2 = 2 << get_msb(quotient - 1);
2226 
2227  size_t n = p2 * p35;
2228  if (n == a)
2229  return n;
2230  else if (n < match)
2231  match = n;
2232 
2233  p35 *= 3;
2234  if (p35 == a)
2235  return p35;
2236  }
2237  if (p35 < match)
2238  match = p35;
2239  p5 *= 5;
2240  if (p5 == a)
2241  return p5;
2242  }
2243  if (p5 < match)
2244  match = p5;
2245  return match;
2246 }
2247 
2249  int initialized;
2250  cpl_image * template_fft;
2251  double template_stdev;
2252 };
2253 
2254 visir_fftx_cache * visir_new_fftx_cache(void)
2255 {
2256  return cpl_calloc(sizeof(visir_fftx_cache), 1);
2257 }
2258 
2259 void visir_delete_fftx_cache(visir_fftx_cache * c)
2260 {
2261  irplib_aligned_free(cpl_image_unwrap(c->template_fft));
2262  cpl_free(c);
2263 }
2264 
2265 /*----------------------------------------------------------------------------*/
2281 /*----------------------------------------------------------------------------*/
2282 cpl_error_code
2283 visir_fftxcorrelate(const cpl_image * atemplate, const cpl_image * aimg,
2284  cpl_boolean normalize, double * xshift, double * yshift,
2285  double * max_correlation, visir_fftx_cache * cache)
2286 {
2287  const cpl_size Nxi = cpl_image_get_size_x(aimg);
2288  const cpl_size Nyi = cpl_image_get_size_y(aimg);
2289  const cpl_size Nxt = cpl_image_get_size_x(atemplate);
2290  const cpl_size Nyt = cpl_image_get_size_y(atemplate);
2291  /* pad to small prime factors for better performance */
2292  const cpl_size Nxe = visir_get_next_regular(Nxi + Nxt - 1);
2293  const cpl_size Nye = visir_get_next_regular(Nyi + Nyt - 1);
2294  cpl_size txshift, tyshift;
2295  double subx = 0, suby = 0;
2296  cpl_image * img = NULL;
2297  cpl_image * zimg = NULL;
2298  cpl_image * ztemp = NULL;
2299  cpl_image * fft1 = NULL;
2300  cpl_image * fft2 = NULL;
2301  cpl_image * res = NULL;
2302  void * buffer;
2303  double template_stdev;
2304  /* measure is nondeterministic in the last ulp */
2305  cpl_fft_mode mode = getenv("VISIR_TEST_MODE") ? 0 : CPL_FFT_FIND_MEASURE;
2306 
2307  cpl_ensure_code(atemplate != NULL, CPL_ERROR_NULL_INPUT);
2308  cpl_ensure_code(aimg != NULL, CPL_ERROR_NULL_INPUT);
2309 
2310  if (cache == NULL || cache->initialized == 0) {
2311  /* prepare template */
2312  cpl_image * template = NULL;
2313  if (cpl_image_get_type(atemplate) != CPL_TYPE_FLOAT)
2314  template = cpl_image_cast(atemplate, CPL_TYPE_FLOAT);
2315  else
2316  template = cpl_image_duplicate(atemplate);
2317 
2318  cpl_image_fill_rejected(template, 0);
2319  if (normalize)
2320  skip_if(cpl_image_subtract_scalar(template,
2321  visir_image_get_mean_fast(template)));
2322  template_stdev = cpl_image_get_stdev(template);
2323 
2324  buffer = irplib_aligned_calloc(32, Nxe * Nye, sizeof(float));
2325  ztemp = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2326 
2327  /* flip we have a fft correlation instead of a convolution
2328  * due to FT(f(x))* = FT(f(-x)) for real values*/
2329  skip_if(cpl_image_flip(template, 1));
2330  skip_if(cpl_image_flip(template, 3));
2331 
2332  /* zero pad */
2333  skip_if(cpl_image_copy(ztemp, template, 1, 1));
2334  cpl_image_delete(template);
2335 
2336  buffer = irplib_aligned_malloc(32, (Nxe / 2 + 1) * Nye *
2337  sizeof(float complex));
2338  fft2 = cpl_image_wrap(Nxe / 2 + 1, Nye, CPL_TYPE_FLOAT_COMPLEX,
2339  buffer);
2340  skip_if(cpl_fft_image(fft2, ztemp, CPL_FFT_FORWARD));
2341  if (cache) {
2342  cache->template_fft = fft2;
2343  cache->template_stdev = template_stdev;
2344  cache->initialized = 1;
2345  }
2346  }
2347  else {
2348  fft2 = cache->template_fft;
2349  template_stdev = cache->template_stdev;
2350  error_if(cpl_image_get_type(fft2) != CPL_TYPE_FLOAT_COMPLEX ||
2351  cpl_image_get_size_x(fft2) != Nxe / 2 + 1 ||
2352  cpl_image_get_size_y(fft2) != Nye, CPL_ERROR_ILLEGAL_INPUT,
2353  "Invalid fourier transformed template");
2354  }
2355 
2356  /* prepare image */
2357 
2358  buffer = irplib_aligned_calloc(32, Nxe * Nye, sizeof(float));
2359  zimg = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2360  if (cpl_image_get_type(aimg) != CPL_TYPE_FLOAT)
2361  img = cpl_image_cast(aimg, CPL_TYPE_FLOAT);
2362  else
2363  img = cpl_image_duplicate(aimg);
2364 
2365  skip_if(img == NULL);
2366 
2367  cpl_image_fill_rejected(img, 0);
2368 
2369  if (normalize)
2370  skip_if(cpl_image_subtract_scalar(img,
2371  visir_image_get_mean_fast(img)));
2372 
2373  /* zero pad */
2374  skip_if(cpl_image_copy(zimg, img, 1, 1));
2375 
2376  buffer = irplib_aligned_malloc(32, (Nxe / 2 + 1) * Nye *
2377  sizeof(float complex));
2378  fft1 = cpl_image_wrap(Nxe / 2 + 1, Nye, CPL_TYPE_FLOAT_COMPLEX, buffer);
2379 
2380  skip_if(cpl_fft_image(fft1, zimg, CPL_FFT_FORWARD | mode));
2381 
2382 
2383  /* correlate, no conjugation necessary due to flipping */
2384  skip_if(visir_image_multiply_fast(fft1, fft2));
2385 
2386  buffer = irplib_aligned_malloc(32, Nxe * Nye * sizeof(float));
2387  res = cpl_image_wrap(Nxe, Nye, CPL_TYPE_FLOAT, buffer);
2388  skip_if(cpl_fft_image(res, fft1, CPL_FFT_BACKWARD | CPL_FFT_NOSCALE |
2389  mode));
2390 
2391  skip_if(cpl_image_get_maxpos_window(res, Nxt / 2 , Nyt / 2,
2392  Nxt / 2 + Nxi, Nyt / 2 + Nyi,
2393  &txshift, &tyshift));
2394 
2395  if (max_correlation != NULL) {
2396  int rej;
2397  *max_correlation = cpl_image_get(res, txshift, tyshift, &rej);
2398  // remove fftw scaling
2399  *max_correlation /= Nxe * Nye;
2400 
2401  if (normalize) {
2402  double tstd = template_stdev;
2403  int mx = txshift - Nxt;
2404  int my = tyshift - Nyt;
2405  double istd= cpl_image_get_stdev_window(zimg,
2406  CX_MAX(1, mx + 1),
2407  CX_MAX(1, my + 1),
2408  CX_MIN(Nxe, mx + Nxt),
2409  CX_MIN(Nye, my + Nyt));
2410 
2411  if (tstd * istd == 0)
2412  *max_correlation = 0;
2413  else
2414  *max_correlation /= (tstd * istd * Nxt * Nyt);
2415 
2416  skip_if(0);
2417  }
2418  }
2419  skip_if(visir_get_subpixel_maxpos(res, txshift, tyshift, &subx, &suby));
2420  if (xshift != NULL) {
2421  *xshift = txshift - Nxt + subx;
2422  }
2423  if (yshift != NULL) {
2424  *yshift = tyshift - Nyt + suby;
2425  }
2426 
2427  end_skip;
2428 
2429  cpl_image_delete(img);
2430  if (cache == NULL)
2431  irplib_aligned_free(cpl_image_unwrap(fft2));
2432  irplib_aligned_free(cpl_image_unwrap(fft1));
2433  irplib_aligned_free(cpl_image_unwrap(zimg));
2434  irplib_aligned_free(cpl_image_unwrap(ztemp));
2435  irplib_aligned_free(cpl_image_unwrap(res));
2436 
2437  return cpl_error_get_code();
2438 }
2439 
2440 
2441 /*----------------------------------------------------------------------------*/
2457 /*----------------------------------------------------------------------------*/
2458 static cx_list *
2459 get_interpolation_points(size_t x, size_t y,
2460  size_t nx_, size_t ny_, cpl_binary *bpm)
2461 {
2462  ssize_t l = -1, r = -1 , u = -1, d = -1;
2463  ssize_t xl = x;
2464  ssize_t xh = x;
2465  ssize_t yl = y;
2466  ssize_t yh = y;
2467  ssize_t nx = (ssize_t)nx_;
2468  ssize_t ny = (ssize_t)ny_;
2469  cx_list * p = cx_list_new();
2470 
2471  /* FIXME: only uses x and y axes, add diagonal?
2472  * maybe a circle around bp? */
2473  while (1) {
2474  xl--;
2475  xh++;
2476  yl--;
2477  yh++;
2478 
2479  if (l < 0 && xl >= 0 && bpm[IND(xl, y, nx)] == CPL_BINARY_0)
2480  l = xl;
2481  if (r < 0 && xh < nx && bpm[IND(xh, y, nx)] == CPL_BINARY_0)
2482  r = xh;
2483  if (d < 0 && yl >= 0 && bpm[IND(x, yl, nx)] == CPL_BINARY_0)
2484  d = yl;
2485  if (u < 0 && yh < ny && bpm[IND(x, yh, nx)] == CPL_BINARY_0)
2486  u = yh;
2487 
2488  /* stop on first pair or end of image */
2489  /* FIXME: searches too much for bp on corner */
2490  if ((l != -1 && r != -1) || (d != -1 && u != -1) ||
2491  (xl < 0 && xh >= nx && yl < 0 && yh >= ny))
2492  break;
2493  }
2494 
2495  /* save the valid points */
2496  if (r >= 0)
2497  cx_list_push_back(p, (cxcptr)(IND(r, y, nx)));
2498  if (l >= 0)
2499  cx_list_push_back(p, (cxcptr)(IND(l, y, nx)));
2500  if (u >= 0)
2501  cx_list_push_back(p, (cxcptr)(IND(x, u, nx)));
2502  if (d >= 0)
2503  cx_list_push_back(p, (cxcptr)(IND(x, d, nx)));
2504  return p;
2505 }
2506 
2507 
2508 /*----------------------------------------------------------------------------*/
2526 /*----------------------------------------------------------------------------*/
2527 cpl_error_code
2528 visir_interpolate_rejected(cpl_image * img, size_t ** ppoints, size_t * n)
2529 {
2530  cpl_mask * mask = cpl_image_get_bpm(img);
2531  float * data = cpl_image_get_data_float(img);
2532  cpl_binary * bpm = cpl_mask_get_data(mask);
2533  const size_t nx = (size_t)cpl_image_get_size_x(img);
2534  const size_t ny = (size_t)cpl_image_get_size_y(img);
2535 
2536  /* FIXME: support more image types */
2537  skip_if(data == NULL);
2538 
2539  if (ppoints == NULL || *ppoints == NULL) {
2540  size_t i = 0;
2541  cpl_binary * found = memchr(bpm, CPL_BINARY_1,
2542  sizeof(cpl_binary) * nx * ny);
2543  /* max 4 points per bp + number of points + bp index */
2544  size_t * restrict pbpm = cpl_calloc(cpl_image_count_rejected(img) * 6,
2545  sizeof(size_t));
2546 
2547  while (found != NULL) {
2548  const size_t ind = found - bpm;
2549  const size_t y = ind / nx;
2550  const size_t x = ind - y * nx;
2551  cx_list * p = get_interpolation_points(x, y, nx, ny, bpm);
2552  cx_list_iterator it = cx_list_begin(p);
2553  const size_t npix = cx_list_size(p);
2554  double sum = 0;
2555 
2556  pbpm[i++] = ind;
2557  pbpm[i++] = npix;
2558  assert(pbpm[i - 1] <= 4);
2559 
2560  while (it != cx_list_end(p)) {
2561  const size_t lind = (size_t)cx_list_get(p, it);
2562  pbpm[i++] = lind;
2563  sum += data[lind];
2564  it = cx_list_next(p, it);
2565  }
2566  data[ind] = sum / npix;
2567  cx_list_delete(p);
2568 
2569  found = memchr(found + 1, CPL_BINARY_1,
2570  sizeof(cpl_binary) * nx * ny - ind - 1);
2571  }
2572  if (ppoints && n) {
2573  *n = i;
2574  *ppoints = pbpm;
2575  }
2576  else
2577  cpl_free(pbpm);
2578  }
2579  else {
2580  const size_t n_ = *n;
2581  size_t * restrict points = *ppoints;
2582  for (size_t i = 0; i < n_;) {
2583  const size_t ind = points[i++];
2584  const size_t m = points[i++];
2585  double sum = 0;
2586  for (size_t j = 0; j < m; j++) {
2587  const size_t lind = points[i++];
2588  sum += data[lind];
2589  }
2590  data[ind] = sum / m;
2591  }
2592  }
2593 
2594  cpl_image_accept_all(img);
2595 
2596  end_skip;
2597 
2598  return cpl_error_get_code();
2599 }
2600 
2601 static const cpl_image *
2602 image_const_row_view_create(const cpl_image * img,
2603  cpl_size ly,
2604  cpl_size uy)
2605 {
2606  const size_t dsz = cpl_type_get_sizeof(cpl_image_get_type(img));
2607  const cpl_size nx = cpl_image_get_size_x(img);
2608  const char * d = cpl_image_get_data_const(img);
2609  size_t offset = (ly - 1) * nx;
2610  cpl_size nny = uy - ly + 1;
2611  cpl_image * wimg = cpl_image_wrap(nx, nny, cpl_image_get_type(img),
2612  (char*)d + offset * dsz);
2613 
2614  const cpl_mask * omask = cpl_image_get_bpm_const(img);
2615  if (omask) {
2616  cpl_mask * mask = cpl_mask_wrap(nx, nny,
2617  (cpl_binary*)cpl_mask_get_data_const(omask) + offset);
2618  cpl_mask_delete(cpl_image_set_bpm(wimg, mask));
2619  }
2620 
2621  return wimg;
2622 }
2623 
2624 static void
2625 image_const_row_view_delete(const cpl_image * img)
2626 {
2627  cpl_mask_unwrap(cpl_image_unset_bpm((cpl_image*)(img)));
2628  cpl_image_unwrap((cpl_image *)img);
2629 }
2630 
2631 /* ---------------------------------------------------------------------------*/
2638 /* ---------------------------------------------------------------------------*/
2639 cpl_image *
2640 visir_parallel_median_collapse(const cpl_imagelist * l)
2641 {
2642 #ifndef _OPENMP
2643  return cpl_imagelist_collapse_median_create(l);
2644 #else
2645  cpl_ensure(l != NULL, CPL_ERROR_NULL_INPUT, NULL);
2646  cpl_ensure(cpl_imagelist_get_size(l) > 0,
2647  CPL_ERROR_ILLEGAL_INPUT, NULL);
2648 
2649  const size_t n = cpl_imagelist_get_size(l);
2650  const cpl_image * img = cpl_imagelist_get_const(l, 0);
2651  const size_t ny = cpl_image_get_size_y(img);
2652  const size_t nx = cpl_image_get_size_x(img);
2653  const size_t nthreads = CX_MIN(visir_get_num_threads(CPL_FALSE), ny);
2654  cpl_image * res = cpl_image_new(nx, ny, cpl_image_get_type(img));
2655  /* make sure image has bpm to avoid creation races later */
2656  cpl_image_get_bpm(res);
2657 
2658  OMP_PRAGMA(omp parallel for num_threads(nthreads))
2659  for (size_t j = 0; j < nthreads; j++) {
2660  size_t ylow = j * (ny / nthreads) + 1;
2661  size_t yhigh = (j + 1) * (ny / nthreads);
2662  if (j == nthreads - 1)
2663  yhigh = ny;
2664  /* create list with images pointing to row views of the original */
2665  cpl_imagelist * view = cpl_imagelist_new();
2666  for (size_t i = 0; i < n; i++) {
2667  const cpl_image * img = cpl_imagelist_get_const(l, i);
2668  const cpl_image * iview =
2669  image_const_row_view_create(img, ylow, yhigh);
2670 IRPLIB_DIAG_PRAGMA_PUSH_IGN(-Wcast-qual);
2671  cpl_imagelist_set(view, (cpl_image *)iview, i);
2672 IRPLIB_DIAG_PRAGMA_POP;
2673  }
2674 
2675  /* collapse views and copy to full result image */
2676  cpl_image * r = cpl_imagelist_collapse_median_create(view);
2677  cpl_image_copy(res, r, 1, ylow);
2678 
2679  /* cleanup */
2680  cpl_image_delete(r);
2681  for (size_t i = 0; i < n; i++) {
2682  cpl_image * img = cpl_imagelist_get(view, i);
2683  image_const_row_view_delete(img);
2684  }
2685  cpl_imagelist_unwrap(view);
2686  }
2687 
2688  return res;
2689 #endif
2690 }
2691 
2692 
2693 /*----------------------------------------------------------------------------*/
2699 /*----------------------------------------------------------------------------*/
2700 static const char * visir_get_capa(const cpl_propertylist * plist)
2701 {
2702  const char * capa = "Pb with Capa";
2703  const char * sval;
2704  double mean;
2705 
2706 
2707  skip_if (0);
2708 
2709 
2710  /* Get the instrument mode */
2711  sval = visir_pfits_get_insmode(plist);
2712  skip_if (0);
2713 
2714  /* Identify the mode */
2715  if (!strcmp(sval, "IMG")) {
2716  /* Imaging mode */
2717  mean = visir_pfits_get_volt1dcta9(plist);
2718  mean += visir_pfits_get_volt1dctb9(plist);
2719  } else if (!strcmp(sval, "SPC") || !strcmp(sval, "SPCIMG")) {
2720  /* Spectro mode */
2721  mean = visir_pfits_get_volt2dcta9(plist);
2722  mean += visir_pfits_get_volt2dctb9(plist);
2723  } else
2724  skip_if (1);
2725 
2726  skip_if (0);
2727 
2728  mean *= 0.5;
2729 
2730  /* Compute Capa value */
2731  if (mean < 1.0) {
2732  capa = "Large Capa";
2733  } else if (mean > 4.5) {
2734  capa = "Small Capa";
2735  }
2736 
2737  end_skip;
2738 
2739  return capa;
2740 }
2741 
2742 #ifdef VISIR_MASK_HAS
2743 /*----------------------------------------------------------------------------*/
2763 /*----------------------------------------------------------------------------*/
2764 static cpl_boolean visir_mask_has(const cpl_mask * self, cpl_binary value,
2765  int ngood)
2766 {
2767  const cpl_binary * pself = cpl_mask_get_data_const(self);
2768  int size = cpl_mask_get_size_x(self)
2769  * cpl_mask_get_size_y(self);
2770  int i;
2771 
2772  cpl_ensure(self, CPL_ERROR_NULL_INPUT, CPL_FALSE);
2773  cpl_ensure(ngood >= 0, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE);
2774  cpl_ensure(ngood <= size, CPL_ERROR_ACCESS_OUT_OF_RANGE, CPL_FALSE);
2775  cpl_ensure(value == CPL_BINARY_0 || value == CPL_BINARY_1,
2776  CPL_ERROR_INCOMPATIBLE_INPUT, CPL_FALSE);
2777 
2778  for (i = 0; i < ngood; i++) {
2779  /* Assume NULL is returned if size == 0 */
2780  const cpl_binary * ppos = memchr(pself, value, (size_t)size);
2781  if (ppos == NULL) break;
2782 
2783  size -= 1 + (int)(ppos - pself);
2784  pself = 1 + ppos;
2785  }
2786 
2787  return i == ngood ? CPL_TRUE : CPL_FALSE;
2788 }
2789 #endif
2790 
2791 
2792 /* ---------------------------------------------------------------------------*/
2798 /* ---------------------------------------------------------------------------*/
2799 size_t visir_upper_bound(const cpl_vector * vec, double val)
2800 {
2801  const double * d = cpl_vector_get_data_const(vec);
2802  long count = cpl_vector_get_size(vec);
2803  long first = 0;
2804  while (count > 0)
2805  {
2806  long step = count / 2;
2807  long it = first + step;
2808  if (!(val < d[it])) {
2809  first = it + 1;
2810  count -= step + 1;
2811  }
2812  else
2813  count = step;
2814  }
2815  return first;
2816 }
2817 
2818 
2819 /* ---------------------------------------------------------------------------*/
2825 /* ---------------------------------------------------------------------------*/
2826 size_t visir_lower_bound(const cpl_vector * vec, double val)
2827 {
2828  const double * d = cpl_vector_get_data_const(vec);
2829  long count = cpl_vector_get_size(vec);
2830  long first = 0;
2831  while (count > 0)
2832  {
2833  long step = count / 2;
2834  long it = first + step;
2835  if (d[it] < val) {
2836  first = it + 1;
2837  count -= step + 1;
2838  }
2839  else
2840  count = step;
2841  }
2842  return first;
2843 }
2844 
2845 
2846 /* ---------------------------------------------------------------------------*/
2858 /* ---------------------------------------------------------------------------*/
2859 cpl_image * visir_linintp_values(const cpl_image * inp, const cpl_bivector * ref)
2860 {
2861  const double * data = cpl_image_get_data_double_const(inp);
2862  const cpl_vector * rx = cpl_bivector_get_x_const(ref);
2863  const cpl_vector * ry = cpl_bivector_get_y_const(ref);
2864  size_t nref = cpl_bivector_get_size(ref);
2865  size_t nx = cpl_image_get_size_x(inp);
2866  size_t ny = cpl_image_get_size_y(inp);
2867  cpl_image * res = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
2868  double * rdata = cpl_image_get_data_double(res);
2869  cpl_ensure(nref >= 2, CPL_ERROR_ILLEGAL_INPUT, NULL);
2870 
2871  for (size_t y = 0; y < ny; y++) {
2872  for (size_t x = 0; x < nx; x++) {
2873  double val = data[y * nx + x];
2874  intptr_t ilo = visir_lower_bound(rx, val);
2875  //double intval, rx1, rx2, ry1, ry2;
2876  //if (ilo == 0) {
2877  // /* extrapolate low */
2878  // ilo += 1;
2879  //}
2880  //else if (ilo == nref) {
2881  // /* extrapolate high */
2882  // ilo -= 1;
2883  //}
2884  //rx1 = cpl_vector_get(rx, ilo - 1);
2885  //rx2 = cpl_vector_get(rx, ilo);
2886  //ry1 = cpl_vector_get(ry, ilo - 1);
2887  //ry2 = cpl_vector_get(ry, ilo);
2888  //double grad = (ry2 - ry1) / (rx2 - rx1);
2889  //double y0 = ry1 - grad * rx1;
2890  //rdata[y * nx + x] = grad * val + y0;
2891 
2892  if (ilo == 0) {
2893  rdata[y * nx + x] = cpl_vector_get(ry, 0);
2894  cpl_image_reject(res, x + 1, y + 1);
2895  }
2896  else if (ilo == nref) {
2897  rdata[y * nx + x] = cpl_vector_get(ry, nref - 1);
2898  cpl_image_reject(res, x + 1, y + 1);
2899  }
2900  else {
2901  double rx1 = cpl_vector_get(rx, ilo - 1);
2902  double rx2 = cpl_vector_get(rx, ilo);
2903  double ry1 = cpl_vector_get(ry, ilo - 1);
2904  double ry2 = cpl_vector_get(ry, ilo);
2905  double grad = (ry2 - ry1) / (rx2 - rx1);
2906  double y0 = ry1 - grad * rx1;
2907  rdata[y * nx + x] = grad * val + y0;
2908  }
2909  }
2910  }
2911  return res;
2912 }
2913 
2914 /* ---------------------------------------------------------------------------*/
2933 /* ---------------------------------------------------------------------------*/
2934 cpl_error_code
2935 fit_2d_gauss(const cpl_image * img_, const cpl_image * weights, cpl_size x, cpl_size y,
2936  double est_fwhmx, double est_fwhmy,
2937  double * peak, double * peak_err,
2938  double * major, double * major_err,
2939  double * minor, double * minor_err,
2940  double * angle, double * angle_err)
2941 {
2942  cpl_image * img = cpl_image_cast(img_, CPL_TYPE_DOUBLE);
2943  cpl_size llx = CX_MAX(x - est_fwhmx * 3, 1);
2944  cpl_size lly = CX_MAX(y - est_fwhmy * 3, 1);
2945  cpl_size urx = CX_MIN(x + est_fwhmx * 3, cpl_image_get_size_x(img));
2946  cpl_size ury = CX_MIN(y + est_fwhmy * 3, cpl_image_get_size_y(img));
2947  cpl_array * dpar = cpl_array_new(7, CPL_TYPE_DOUBLE);
2948  cpl_array * epar = cpl_array_new(7, CPL_TYPE_DOUBLE);
2949  cpl_matrix * cov = NULL;
2950  cpl_matrix * phys_cov = NULL;
2951 
2952  cpl_array_set_double(dpar, 0, cpl_image_get_median(img));
2953  cpl_array_set_double(dpar, 1, cpl_image_get_flux_window(img, llx, lly,
2954  urx, ury));
2955  cpl_array_set_double(dpar, 2, 0.);
2956  cpl_array_set_double(dpar, 3, x);
2957  cpl_array_set_double(dpar, 4, y);
2958  cpl_array_set_double(dpar, 5, est_fwhmx / 2.355);
2959  cpl_array_set_double(dpar, 6, est_fwhmx / 2.355);
2960 
2961  cpl_image * err = cpl_image_new(cpl_image_get_size_x(img),
2962  cpl_image_get_size_y(img),
2963  CPL_TYPE_DOUBLE);
2964  cpl_image_add_scalar(err, 1.);
2965  cpl_image_divide(err, weights);
2966  cpl_image_power(err, 0.5);
2967 
2968  skip_if(cpl_fit_image_gaussian(img, err,
2969  x,
2970  y,
2971  urx - llx + 1,
2972  ury - lly + 1,
2973  dpar,
2974  epar,
2975  NULL, /* fit params */
2976  NULL,
2977  NULL,
2978  &cov,
2979  major,
2980  minor,
2981  angle,
2982  &phys_cov
2983  ));
2984 
2985  /* ignores covariance, values become nan for near circular gauss, forumula
2986  * illdefined for e.g. rho < 0 or theta = 45
2987  * nan cannot be in fits header, so set to -1 */
2988  if (peak) {
2989  double * a = cpl_array_get_data_double(dpar);
2990  *peak = a[0] + a[1] /
2991  (CPL_MATH_2PI * a[5] * a[6] * sqrt(1.0 - a[2] * a[2]));
2992  if (isnan(*peak)) {
2993  cpl_msg_warning(cpl_func, "2d gaussfit, could not determine peak");
2994  *peak = -1.;
2995  }
2996  }
2997  if (peak_err) {
2998  double * a = cpl_array_get_data_double(dpar);
2999  double * e = cpl_array_get_data_double(epar);
3000  double B = a[0];
3001  double A = a[1];
3002  double sigx = a[5];
3003  double sigy = a[6];
3004  double rho = a[2];
3005  double dB = sqrt(e[0]);
3006  double dA = sqrt(e[1]);
3007  double dsigx = sqrt(e[5]);
3008  double dsigy = sqrt(e[6]);
3009  double drho = sqrt(e[2]);
3010  double A2 = A * A;
3011  double rho2 = rho * rho;
3012  double x = CPL_MATH_PI * CPL_MATH_PI * 4 * sigx * sigx * sigy * sigy;
3013  *peak_err =
3014  sqrt(A2 * drho * drho * rho2 /(x * pow(-rho2 + 1.0, 3)) +
3015  A2 * dsigx * dsigx /(x * sigx * sigx * (-rho2 + 1.0)) +
3016  A2 * dsigy * dsigy /(x * sigy * sigy * (-rho2 + 1.0)) +
3017  dA * dA/(x * (-rho2 + 1.0)) + dB * dB);
3018  if (isnan(*peak_err)) {
3019  *peak_err = -1.;
3020  }
3021  }
3022  if (major) {
3023  *major *= CPL_MATH_FWHM_SIG;
3024  if (isnan(*major)) {
3025  cpl_msg_warning(cpl_func,
3026  "2d gaussfit, could not determine major axis");
3027  *minor = -1.;
3028  }
3029  }
3030  if (minor) {
3031  *minor *= CPL_MATH_FWHM_SIG;
3032  if (isnan(*minor)) {
3033  cpl_msg_warning(cpl_func,
3034  "2d gaussfit, could not determine minor axis");
3035  *minor = -1.;
3036  }
3037  }
3038  if (major_err) {
3039  *major_err = sqrt(cpl_matrix_get(phys_cov, 1, 1)) * CPL_MATH_FWHM_SIG;
3040  if (isnan(*major_err)) {
3041  *major_err = -1.;
3042  }
3043  }
3044  if (minor_err) {
3045  *minor_err = sqrt(cpl_matrix_get(phys_cov, 2, 2)) * CPL_MATH_FWHM_SIG;
3046  if (isnan(*minor_err)) {
3047  *minor_err = -1.;
3048  }
3049  }
3050  if (angle_err) {
3051  *angle_err = sqrt(cpl_matrix_get(phys_cov, 0, 0));
3052  if (isnan(*angle_err)) {
3053  *angle_err = -1.;
3054  }
3055  }
3056 
3057 cleanup:
3058  cpl_array_delete(dpar);
3059  cpl_array_delete(epar);
3060  cpl_image_delete(err);
3061  cpl_image_delete(img);
3062  cpl_matrix_delete(phys_cov);
3063  cpl_matrix_delete(cov);
3064 
3065  return cpl_error_get_code();
3066 }
3067 
3068 
3069 /* ---------------------------------------------------------------------------*/
3083 /* ---------------------------------------------------------------------------*/
3084 cpl_error_code
3085 fit_1d_gauss(const cpl_vector * xv, const cpl_vector * yv, cpl_vector * dyv,
3086  double * x0, double * x0_err,
3087  double * peak, double * peak_err,
3088  double * sigma_, double * sigma_err)
3089 {
3090  double sigma, area, offset;
3091  cpl_matrix * cov = NULL;
3092  skip_if(cpl_vector_fit_gaussian(xv, /*sigma_x*/NULL,
3093  yv, dyv,
3094  CPL_FIT_CENTROID | CPL_FIT_STDEV |
3095  CPL_FIT_AREA | CPL_FIT_OFFSET,
3096  x0, &sigma, &area, &offset,
3097  NULL, NULL,
3098  &cov));
3099  if (x0 && isnan(*x0)) {
3100  cpl_msg_warning(cpl_func, "1d gaussfit, could not determine mean");
3101  *x0 = -1.;
3102  }
3103  if (x0_err) {
3104  *x0_err = sqrt(cpl_matrix_get(cov, 0, 0));
3105  if (isnan(*x0_err)) {
3106  *x0_err = -1.;
3107  }
3108  }
3109  if (sigma) {
3110  *sigma_ = sigma;
3111  if (isnan(*sigma_)) {
3112  cpl_msg_warning(cpl_func,
3113  "1d gaussfit, could not determine sigma");
3114  *sigma_ = -1.;
3115  }
3116  }
3117  if (peak) {
3118  *peak = area / sqrt(2 * CPL_MATH_PI * sigma * sigma) + offset;
3119  if (isnan(*peak)) {
3120  cpl_msg_warning(cpl_func, "1d gaussfit, could not determine peak");
3121  *peak = -1.;
3122  }
3123  }
3124  if (peak_err) {
3125  double dsig = sqrt(cpl_matrix_get(cov, 1, 1));
3126  double dA = sqrt(cpl_matrix_get(cov, 2, 2));
3127  double dB = sqrt(cpl_matrix_get(cov, 3, 3));
3128  double pi2sig2 = 2 * CPL_MATH_PI * sigma * sigma;
3129  *peak_err = sqrt(dsig * dsig * area * area /
3130  (2 * CPL_MATH_PI * pi2sig2 * sigma * sigma) +
3131  dA * dA / pi2sig2 + dB * dB);
3132  if (isnan(*peak_err)) {
3133  *peak_err = -1.;
3134  }
3135  }
3136  if (sigma_err) {
3137  *sigma_err = sqrt(cpl_matrix_get(cov, 1, 1));
3138  if (isnan(*sigma_err)) {
3139  *sigma_err = -1.;
3140  }
3141  }
3142 
3143 cleanup:
3144  cpl_matrix_delete(cov);
3145 
3146  return cpl_error_get_code();
3147 }
double visir_pfits_get_volt1dcta9(const cpl_propertylist *self)
The VOLT1.DCTA9.
Definition: visir_pfits.c:761
double visir_pfits_get_volt1dctb9(const cpl_propertylist *self)
The VOLT1.DCTB9.
Definition: visir_pfits.c:773
double visir_pfits_get_volt2dcta9(const cpl_propertylist *self)
The VOLT2.DCTA9.
Definition: visir_pfits.c:785
int visir_pfits_get_navrg(const cpl_propertylist *self)
The NAVRG.
Definition: visir_pfits.c:315
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:341
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:229
int visir_pfits_get_ndit(const cpl_propertylist *self)
The NDIT keyword.
Definition: visir_pfits.c:479
double visir_pfits_get_volt2dctb9(const cpl_propertylist *self)
The VOLT2.DCTB9.
Definition: visir_pfits.c:797
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:419
double visir_pfits_get_monoc_pos(const cpl_propertylist *self)
The INS.MONOC1.POS.
Definition: visir_pfits.c:467
double visir_pfits_get_dit(const cpl_propertylist *self)
The DIT.
Definition: visir_pfits.c:301
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.