FORS Pipeline Reference Manual 4.9.9
fors_zeropoint_impl.c
00001 /* $Id: fors_zeropoint_impl.c,v 1.92 2011/10/24 13:07:46 cgarcia Exp $
00002  *
00003  * This file is part of the FORS Library
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2011/10/24 13:07:46 $
00024  * $Revision: 1.92 $
00025  * $Name: fors-4_9_9 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <fors_zeropoint_impl.h>
00033 
00034 #include <fors_extract.h>
00035 #include <fors_identify.h>
00036 #include <fors_tools.h>
00037 #include <fors_star.h>
00038 #include <fors_std_cat.h>
00039 #include <fors_std_star.h>
00040 #include <fors_image.h>
00041 #include <fors_instrument.h>
00042 #include <fors_data.h>
00043 #include <fors_setting.h>
00044 #include <fors_dfs.h>
00045 #include <fors_pfits.h>
00046 #include <fors_utils.h>
00047 
00048 #include <cpl.h>
00049 
00050 #include <math.h>
00051 
00058 const char *const fors_zeropoint_name = "fors_zeropoint";
00059 const char *const fors_zeropoint_description_short = "Compute zeropoint";
00060 const char *const fors_zeropoint_author = "Jonas M. Larsen";
00061 const char *const fors_zeropoint_email = PACKAGE_BUGREPORT;
00062 const char *const fors_zeropoint_description = 
00063 "Input files:\n"
00064 "  DO category:               Type:       Explanation:              Number:\n"
00065 "  STANDARD_IMG               FITS image  Phot. standard field        1\n"
00066 "  MASTER_BIAS                FITS image  Master bias                 1\n"
00067 "  MASTER_SKY_FLAT_IMG        FITS image  Master sky flatfield        1\n"
00068 "  FLX_STD_IMG                FITS table  Standard star catalog       1+\n"
00069 "  PHOT_TABLE                 FITS table  Filter ext. coeff, color    1\n"
00070 "\n"
00071 "Output files:\n"
00072 "  DO category:               Data type:  Explanation:\n"
00073 "  SOURCES_STD_IMG            FITS image  Unfiltered SExtractor output\n"
00074 "  ALIGNED_PHOT               FITS table\n"
00075 "  PHOT_BACKGROUND_STD_IMG    FITS image  Reduced science image background\n"
00076 "  STANDARD_REDUCED_IMG       FITS image  Reduced std image\n";
00077 
00078 static double
00079 get_zeropoint(fors_star_list *stars, 
00080               double cutoffE,
00081               double cutoffk,
00082               double dext_coeff,
00083               double dcolor_term,
00084               double avg_airmass,
00085               double *dzeropoint,
00086               int *n);
00087 
00088 static cpl_error_code
00089 fors_zeropoint_astrometry(                  const cpl_frameset  *std_cat_frames,
00090                                             char                filter_band,
00091                                             double              color_correct,
00092                                             double              dcolor_correct,
00093                                             const identify_method
00094                                                                 *id_method,
00095                                             fors_star_list      *extracted,
00096                                             cpl_propertylist    *wcs_header,
00097                                             fors_std_star_list  **std_cat,
00098                                             cpl_image           **histogram);
00099 
00100 static cpl_error_code
00101 fors_zeropoint_astrometry_get_wcs_shift_px( const fors_star_list    *stars,
00102                                             double                  *dx,
00103                                             double                  *dy);
00104 
00105 static cpl_error_code
00106 fors_zeropoint_astrometry_shift_wcs_origin( cpl_propertylist    *header,
00107                                             double  dx,
00108                                             double  dy);
00109 
00110 static cpl_error_code
00111 fors_zeropoint_astrometry_apply_unidentified_xy2radec(
00112                                             fors_star_list          *stars,
00113                                             const cpl_propertylist  *header);
00114 
00115 void
00116 fors_zeropoint_errorstate_dump_as_warning(  unsigned self,
00117                                             unsigned first,
00118                                             unsigned last);
00119 
00124 void fors_zeropoint_define_parameters(cpl_parameterlist *parameters)
00125 {
00126     const char *context = cpl_sprintf("fors.%s", fors_zeropoint_name);
00127     
00128     fors_extract_define_parameters(parameters, context);
00129 
00130     fors_identify_define_parameters(parameters, context);
00131     
00132     const char *name, *full_name;
00133     cpl_parameter *p;
00134 
00135     name = "magcutE";
00136     full_name = cpl_sprintf("%s.%s", context, name);
00137     p = cpl_parameter_new_value(full_name,
00138                                 CPL_TYPE_DOUBLE,
00139                                 "Zeropoint absolute cutoff (magnitude)",
00140                                 context,
00141                                 1.0);
00142     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00143     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00144     cpl_parameterlist_append(parameters, p);
00145     cpl_free((void *)full_name); full_name = NULL;
00146 
00147     name = "magcutk";
00148     full_name = cpl_sprintf("%s.%s", context, name);
00149     p = cpl_parameter_new_value(full_name,
00150                                 CPL_TYPE_DOUBLE,
00151                                 "Zeropoint kappa rejection parameter",
00152                                 context,
00153                                 5.0);
00154     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00155     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00156     cpl_parameterlist_append(parameters, p);
00157     cpl_free((void *)full_name); full_name = NULL;
00158 
00159     name = "magsyserr";
00160     full_name = cpl_sprintf("%s.%s", context, name);
00161     p = cpl_parameter_new_value(full_name,
00162                                 CPL_TYPE_DOUBLE,
00163                                 "Systematic error in magnitude",
00164                                 context,
00165                                 0.01);
00166     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00167     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00168     cpl_parameterlist_append(parameters, p);
00169     cpl_free((void *)full_name); full_name = NULL;
00170 
00171 
00172     cpl_free((void *)context);
00173 
00174     return;
00175 }
00176 
00177 #undef cleanup
00178 #define cleanup \
00179 do { \
00180     cpl_frameset_delete(std_frame); \
00181     cpl_frameset_delete(master_bias_frame); \
00182     cpl_frameset_delete(master_flat_frame); \
00183     cpl_frameset_delete(std_cat_frames); \
00184     cpl_frameset_delete(phot_table); \
00185     fors_image_delete(&std); \
00186     fors_image_delete_const(&master_bias); \
00187     fors_image_delete(&master_flat); \
00188     cpl_table_delete(aligned_phot); \
00189     cpl_image_delete(background); \
00190     cpl_table_delete(sources); \
00191     fors_extract_method_delete(&em); \
00192     fors_identify_method_delete(&im); \
00193     fors_std_star_list_delete(&cat, fors_std_star_delete); \
00194     /* All std-stars (and non-std-stars) are linked by the respective star */ \
00195     /* objects in "stars", without being referenced by a std-star-list */ \
00196     /* object. So they are deleted together with the function */ \
00197     /* fors_star_delete() while deleting the list "stars". */ \
00198     fors_star_list_delete(&stars, fors_star_delete); \
00199     cpl_free((void *)context); \
00200     fors_setting_delete(&setting); \
00201     cpl_propertylist_delete(qc_phot); \
00202     cpl_propertylist_delete(qc_sources); \
00203     cpl_propertylist_delete(product_header); \
00204     cpl_propertylist_delete(raw_header); \
00205 } while (0)
00206 
00215 void fors_zeropoint(cpl_frameset *frames, const cpl_parameterlist *parameters)
00216 {
00217     /* Raw */
00218     cpl_frameset *std_frame      = NULL;
00219     fors_image *std              = NULL;
00220 
00221     /* Calibration */
00222     cpl_frameset *master_bias_frame = NULL;
00223     const fors_image *master_bias   = NULL; 
00224 
00225     cpl_frameset *master_flat_frame = NULL;
00226     fors_image *master_flat         = NULL; 
00227 
00228     cpl_frameset *std_cat_frames    = NULL;
00229     fors_std_star_list *cat         = NULL;
00230 
00231     cpl_frameset *phot_table        = NULL;
00232     double color_term, dcolor_term;
00233     double ext_coeff, dext_coeff;
00234     double expected_zeropoint, dexpected_zeropoint;
00235     
00236     
00237     cpl_propertylist *raw_header  = NULL;
00238     
00239     /* Products */
00240     int nzeropoint = -1;             /* Suppress warning */
00241     cpl_table *aligned_phot = NULL;
00242     cpl_propertylist *qc_phot = NULL;
00243     cpl_propertylist *qc_sources = NULL;
00244     double zeropoint, dzeropoint;
00245     fors_extract_sky_stats sky_stats;
00246     cpl_image *background = NULL;
00247     cpl_table *sources    = NULL;
00248     cpl_propertylist *product_header = NULL;
00249     cpl_image *histogram = NULL;
00250         
00251     /* Parameters */
00252     extract_method  *em = NULL;
00253     identify_method *im = NULL;
00254     double cutoffE, cutoffk;
00255     double magsyserr;
00256 
00257     /* Other */
00258     const char *context   = NULL;
00259     fors_star_list *stars = NULL;
00260     fors_setting *setting = NULL;
00261     double avg_airmass = 0.0;
00262     const char *target_name = NULL; 
00263     
00264     qc_phot = cpl_propertylist_new();
00265     qc_sources = cpl_propertylist_new();
00266     product_header = cpl_propertylist_new();
00267     context = cpl_sprintf("fors.%s", fors_zeropoint_name);
00268     
00269     /* Get parameters */
00270     em = fors_extract_method_new(parameters, context);
00271     assure( !cpl_error_get_code(), return, 
00272             "Could not get extraction parameters" );
00273     
00274     im = fors_identify_method_new(parameters, context);
00275     assure( !cpl_error_get_code(), return, 
00276             "Could not get identification parameters" );
00277 
00278 
00279     cpl_msg_indent_more();
00280     const char *name = cpl_sprintf("%s.%s", context, "magcutE");
00281     cutoffE = dfs_get_parameter_double_const(parameters, 
00282                                             name);
00283     cpl_free((void *)name); name = NULL;
00284     cpl_msg_indent_less();
00285     assure( !cpl_error_get_code(), return, NULL );
00286         
00287     cpl_msg_indent_more();
00288     name = cpl_sprintf("%s.%s", context, "magcutk");
00289     cutoffk = dfs_get_parameter_double_const(parameters, 
00290                                              name);
00291     cpl_free((void *)name); name = NULL;
00292     cpl_msg_indent_less();
00293     assure( !cpl_error_get_code(), return, NULL );
00294         
00295     cpl_msg_indent_more();
00296     name = cpl_sprintf("%s.%s", context, "magsyserr");
00297     magsyserr = dfs_get_parameter_double_const(parameters, 
00298                                                name);
00299     cpl_free((void *)name); name = NULL;
00300     cpl_msg_indent_less();
00301     assure( !cpl_error_get_code(), return, NULL );
00302     assure( magsyserr >= 0, return, 
00303             "Input systematic error (magsyserr=%f) cannot be negative",
00304             magsyserr);
00305         
00306     /* Find raw */
00307     std_frame = fors_frameset_extract(frames, STANDARD_IMG);
00308     assure( cpl_frameset_get_size(std_frame) == 1, return, 
00309             "Exactly 1 %s required. %d found", 
00310             STANDARD_IMG, cpl_frameset_get_size(std_frame) );
00311 
00312     /* Find calibration */
00313     master_bias_frame = fors_frameset_extract(frames, MASTER_BIAS);
00314     assure( cpl_frameset_get_size(master_bias_frame) == 1, return, 
00315             "One %s required. %d found", 
00316             MASTER_BIAS, cpl_frameset_get_size(master_bias_frame) );
00317 
00318     master_flat_frame = fors_frameset_extract(frames, MASTER_SKY_FLAT_IMG);
00319     assure( cpl_frameset_get_size(master_flat_frame) == 1, return, 
00320             "One %s required. %d found", 
00321             MASTER_SKY_FLAT_IMG, cpl_frameset_get_size(master_flat_frame) );
00322 
00323     std_cat_frames = fors_frameset_extract(frames, FLX_STD_IMG);
00324     assure( cpl_frameset_get_size(std_cat_frames) >= 1, return, 
00325             "One or more %s required. %d found",
00326             FLX_STD_IMG, cpl_frameset_get_size(std_cat_frames));
00327 
00328     phot_table = fors_frameset_extract(frames, PHOT_TABLE);
00329     assure( cpl_frameset_get_size(phot_table) == 1, return, 
00330             "One %s required. %d found",
00331             PHOT_TABLE, cpl_frameset_get_size(phot_table));
00332 
00333     /* Done finding frames */
00334 
00335     /* Get setting */
00336     setting = fors_setting_new(cpl_frameset_get_first(std_frame));
00337     assure( !cpl_error_get_code(), return, "Could not get instrument setting" );
00338     
00339     /* Load std raw frame header */
00340     raw_header = cpl_propertylist_load(
00341                     cpl_frame_get_filename(
00342                         cpl_frameset_get_first(std_frame)), 0);
00343     if (raw_header == NULL) {
00344         cpl_msg_error(cpl_func, "Failed to load raw header");
00345         cleanup;
00346         return;
00347     }
00348 
00349     /* Getting info from std header */
00350     avg_airmass = fors_get_airmass(raw_header);
00351     target_name = cpl_propertylist_get_string(raw_header, FORS_PFITS_TARG_NAME);
00352     cpl_msg_info(cpl_func, "Target name: %s", target_name);
00353     
00354     /* Load master bias */
00355     master_bias = fors_image_load(cpl_frameset_get_first(master_bias_frame), 
00356                                   NULL, setting, NULL);
00357     assure( !cpl_error_get_code(), return, 
00358             "Could not load master bias");
00359 
00360     /* Load raw frames, subtract bias */
00361     std = fors_image_load(cpl_frameset_get_first(std_frame), 
00362                           master_bias, setting, NULL);
00363     assure( !cpl_error_get_code(), return, "Could not load standard image");
00364     fors_image_delete_const(&master_bias);
00365 
00366     /* Load master flat */
00367     master_flat = fors_image_load(cpl_frameset_get_first(master_flat_frame), 
00368                                   NULL, setting, NULL);
00369     assure( !cpl_error_get_code(), return, "Could not load master flat");
00370     
00371     /* Divide by flat */
00372     fors_image_divide_scalar(master_flat,
00373                              fors_image_get_median(master_flat, NULL), -1.0);
00374     fors_image_divide(std, master_flat);
00375     assure( !cpl_error_get_code(), return, "Could not divide by master flat");
00376     fors_image_delete(&master_flat);
00377 
00378     /* Extract sources */
00379     stars = fors_extract(std, setting, em, magsyserr,
00380                          &sky_stats, &background, &sources);
00381     assure( !cpl_error_get_code(), return, "Could not extract objects");
00382 
00383 
00384     if (setting->filter_name != NULL)
00385     {
00386         char            filter_band;
00387         cpl_errorstate  local_ers = cpl_errorstate_get();
00388         
00389         /* load raw frame header */
00390 
00391 /* Moved outside by Carlo - start
00392 
00393         raw_header = cpl_propertylist_load(
00394                         cpl_frame_get_filename(
00395                             cpl_frameset_get_first(std_frame)), 0);
00396         assure(                             cpl_errorstate_is_equal(local_ers),
00397                                             return,
00398                                             "Failed to load raw header");
00399 Moved outside by Carlo - end */
00400         
00401         /* Load filter coefficients */
00402         fors_phot_table_load(cpl_frameset_get_first(phot_table), setting,
00403                              &color_term, &dcolor_term,
00404                              &ext_coeff, &dext_coeff,
00405                              &expected_zeropoint, &dexpected_zeropoint);
00406         assure(                             cpl_errorstate_is_equal(local_ers),
00407                                             return,
00408                                             "Could not load photometry table" );
00409 
00410         filter_band = fors_instrument_filterband_get_by_setting(setting);
00411         
00412         /* Do the whole astrometry:
00413          * load catalogue, apply wcs, do pattern-matching, correct wcs
00414          * (treat errors only as warnings)
00415          */
00416         cpl_msg_info(cpl_func, "Astrometry:");
00417         cpl_msg_indent_more();
00418         fors_zeropoint_astrometry(          std_cat_frames,
00419                                             filter_band,
00420                                             color_term,
00421                                             dcolor_term,
00422                                             im,
00423                                             stars,      /* sources */
00424                                             raw_header, /* wcs */
00425                                             &cat,       /* to draw debug-img */
00426                                             &histogram);
00427         cpl_msg_indent_less();
00428         if (!cpl_errorstate_is_equal(local_ers))
00429         {
00430             cpl_msg_warning(cpl_func, "Astrometric calibration failed:");
00431             cpl_msg_indent_more();
00432             cpl_errorstate_dump(local_ers,
00433                                 CPL_FALSE,
00434                                 fors_zeropoint_errorstate_dump_as_warning);
00435             cpl_msg_indent_less();
00436             /* reset error */
00437             cpl_errorstate_set(local_ers);
00438         }
00439         /* The astrometric calibration could fail but nonetheless have
00440          * succeeded in identifying some standard stars. So continue trying to
00441          * get the zeropoint anyway. */
00442 
00443         /* Correct for atmospheric extinction, gain, exposure time */
00444         /* FIXME: FAP: use WCS corrected header */
00445         avg_airmass = fors_star_ext_corr(   stars,
00446                                             setting,
00447                                             ext_coeff,
00448                                             dext_coeff,
00449                                             cpl_frameset_get_first(std_frame));
00450         /* Get zeropoint. */
00451         if (cpl_errorstate_is_equal(local_ers))
00452         {
00453             zeropoint = get_zeropoint(      stars,
00454                                             cutoffE,
00455                                             cutoffk,
00456                                             dext_coeff,
00457                                             dcolor_term,
00458                                             avg_airmass,
00459                                             &dzeropoint,
00460                                             &nzeropoint);
00461         }
00462         
00463         if (!cpl_errorstate_is_equal(local_ers))
00464         {
00465             cpl_msg_warning(cpl_func, "Failed to get zeropoint");
00466             cpl_msg_indent_more();
00467             cpl_errorstate_dump(local_ers,
00468                                 CPL_FALSE,
00469                                 fors_zeropoint_errorstate_dump_as_warning);
00470             cpl_msg_indent_less();
00471             /* reset error */
00472             cpl_errorstate_set(local_ers);
00473         }
00474     }
00475     else {
00476        cpl_msg_warning(cpl_func, "Zeropoint computation is not supported "
00477                        "for non-standard filters");
00478        color_term = 0.0;
00479        dcolor_term = 9999.0;
00480        ext_coeff = 0.0;
00481        dext_coeff = 9999.0;
00482        expected_zeropoint = 0.0;
00483        dexpected_zeropoint = 9999.0;
00484        zeropoint = 0.0;
00485        dzeropoint = 0.0;
00486        nzeropoint = 0;
00487     }
00488 
00489     /* QC */
00490     
00491     cpl_msg_info(cpl_func,"Frame zeropoint = %f mag", zeropoint);
00492     cpl_msg_info(cpl_func,"Frame zeropoint uncertainty = %f mag", dzeropoint);
00493     cpl_msg_info(cpl_func,"Number of stars used for zeropoint computation = %d",
00494                  nzeropoint);
00495     
00496     cpl_propertylist_append_double(qc_phot, "ESO QC ZPOINT", zeropoint);
00497     cpl_propertylist_set_comment(qc_phot, "ESO QC ZPOINT", "Frame zeropoint ");
00498 
00499     cpl_propertylist_append_double(qc_phot, "ESO QC ZPOINTRMS", dzeropoint);
00500     cpl_propertylist_set_comment(qc_phot, "ESO QC ZPOINTRMS", 
00501                                  "Uncertainty of frame zeropoint [mag]");
00502 
00503     cpl_propertylist_append_int(qc_phot, "ESO QC ZPOINT NSTARS", nzeropoint);
00504     cpl_propertylist_set_comment(qc_phot, "ESO QC ZPOINT NSTARS", 
00505                              "Number of stars used for zeropoint computation");
00506     
00507     double derived_ext_coeff, derived_ext_coeff_err;
00508 
00509     if (setting->filter_name != NULL) {
00510         cpl_msg_info(cpl_func,
00511                      "Computing extinction "
00512                      "(assuming zeropoint = %.3f +- %.3f mag)",
00513                      expected_zeropoint,
00514                      dexpected_zeropoint);
00515 
00516         cpl_msg_indent_more();
00517 
00518         if (nzeropoint > 0) {
00519             derived_ext_coeff = ext_coeff +
00520                 (expected_zeropoint - zeropoint) / avg_airmass;
00521             /* Things are very correlated here
00522                (e.g. ext_coeff was used to compute zeropoint).
00523            
00524                The final error on the ext.coeff. depends only
00525                on the reference and computed zeropoints' errors. 
00526                The airmass is assumed errorless.
00527 
00528                We assume the 2 zeropoints' errors are not correlated and
00529                add in quadrature.
00530 
00531                The derived extinction's error does not suffer from the part
00532                of dzeropoint which was due to the error of the assumed 
00533                extinction.
00534             */
00535             derived_ext_coeff_err = sqrt(
00536                 dexpected_zeropoint*dexpected_zeropoint +
00537                 dzeropoint*dzeropoint) / avg_airmass - dext_coeff*dext_coeff;
00538     
00539             cpl_msg_info(cpl_func, "Atmospheric extinction = "
00540                          "%f +- %f mag/airmass", derived_ext_coeff,
00541                          derived_ext_coeff_err);
00542         }
00543         else {
00544             cpl_msg_warning(cpl_func, "Too few stars available, "
00545                             "setting extinction to zero");
00546             derived_ext_coeff = 0;
00547             derived_ext_coeff_err = 9999;
00548         }
00549     }
00550     else {
00551         derived_ext_coeff = 0;
00552         derived_ext_coeff_err = 9999;
00553     }
00554 
00555     cpl_msg_info(cpl_func,"Atmospheric extinction = %f mag/airmass", 
00556                  derived_ext_coeff);
00557     cpl_msg_info(cpl_func,"Error in atmospheric extinction = %f mag/airmass", 
00558                  derived_ext_coeff_err);
00559 
00560     cpl_propertylist_append_double(qc_phot, "ESO QC EXTCOEFF",
00561                                    derived_ext_coeff);
00562     cpl_propertylist_set_comment(qc_phot, "ESO QC EXTCOEFF", 
00563                                  "Atmospheric extinction [mag/airmass]");
00564 
00565     cpl_propertylist_append_double(qc_phot, "ESO QC EXTCOEFFERR", 
00566                                    derived_ext_coeff_err);
00567     cpl_propertylist_set_comment(qc_phot, "ESO QC EXTCOEFFERR", 
00568                               "Error on atmospheric extinction [mag/airmass]");
00569 
00570     cpl_propertylist_append_double(qc_sources, "ESO QC ZPOINT NSRCEXTRACT", 
00571                                    cpl_table_get_nrow(sources));
00572     cpl_propertylist_set_comment(qc_sources, "ESO QC ZPOINT NSRCEXTRACT", 
00573                            "Number of sources extracted from the zpoint image");
00574 
00575     cpl_msg_indent_less();
00576 
00577 
00578     /* Convert to CPL table */
00579     aligned_phot = fors_create_sources_table(stars);
00580     assure( !cpl_error_get_code(), return,
00581             "Failed to create aligned photometry table");
00582 
00583     /* Save products */
00584     /* FIXME: FAP: use WCS corrected header */
00585     fors_dfs_save_table(frames, sources, SOURCES_STD,
00586                         qc_sources, parameters, fors_zeropoint_name, 
00587                         cpl_frameset_get_first(std_frame));
00588     assure( !cpl_error_get_code(), return, "Saving %s failed",
00589             SOURCES_STD);
00590     cpl_propertylist_delete(qc_sources); qc_sources = NULL;
00591 
00592     /* Add keywords necessary for fors_photometry 
00593        (just reuse/overload to the qc propertylist variable)
00594     */
00595     cpl_propertylist_update_double(qc_phot, FORS_PFITS_EXPOSURE_TIME, 
00596                                    setting->exposure_time);
00597     cpl_propertylist_update_double(qc_phot, "AIRMASS", avg_airmass);
00598     
00599     cpl_table_fill_invalid_int(aligned_phot, "USE_CAT", 2);
00600     /* FIXME: FAP: use WCS corrected header */
00601     fors_dfs_save_table(frames, aligned_phot, ALIGNED_PHOT,
00602                         qc_phot, parameters, fors_zeropoint_name, 
00603                         cpl_frameset_get_first(std_frame));
00604     assure( !cpl_error_get_code(), return, "Saving %s failed",
00605             ALIGNED_PHOT);
00606     
00607     cpl_propertylist_delete(qc_phot); qc_phot = NULL;
00608 
00609     /* FIXME: FAP: use WCS corrected header */
00610     fors_dfs_add_wcs(product_header,
00611                      cpl_frameset_get_first(std_frame), setting);
00612     /* FIXME: FAP: use WCS corrected header */
00613     fors_dfs_add_exptime(product_header, 
00614                          cpl_frameset_get_first(std_frame), 0.);
00615 
00616     cpl_propertylist_update_double(product_header, "AIRMASS", avg_airmass);
00617         
00618     dfs_save_image(frames, background, PHOT_BACKGROUND_STD_IMG,
00619                    product_header, parameters, fors_zeropoint_name, 
00620                    setting->version);
00621     assure( !cpl_error_get_code(), return, "Saving %s failed",
00622             PHOT_BACKGROUND_STD_IMG);
00623         
00624     cpl_image_delete(background); background = NULL;
00625     
00626     /* FIXME: FAP: use WCS corrected header */
00627     fors_dfs_save_image(frames, std, STANDARD_REDUCED_IMG,
00628                         product_header, parameters, fors_zeropoint_name, 
00629                         cpl_frameset_get_first(std_frame));
00630     assure( !cpl_error_get_code(), return, "Saving %s failed",
00631             STANDARD_REDUCED_IMG);
00632 
00633     if(histogram != NULL)
00634     {
00635         dfs_save_image(frames, histogram, OFFSET_HISTOGRAM,
00636                 NULL, parameters, fors_zeropoint_name, 
00637                 setting->version);
00638         assure( !cpl_error_get_code(), return, "Saving %s failed",
00639                 OFFSET_HISTOGRAM);
00640 
00641         cpl_image_delete(histogram); histogram = NULL;
00642     }
00643     else
00644     {
00645         cpl_msg_info(cpl_func,"Offset histogram not computed, not saving");
00646     }
00647         
00648     if (setting->filter_name == NULL) {
00649 
00650         /* No debug image can be created */
00651 
00652         cleanup;
00653         return;
00654     }
00655 
00656     /* Create debugging image
00657 
00658        Legend:
00659 
00660         ------  (horiz. line):   detected source
00661 
00662           |
00663           |    (vert. line):   catalog position
00664           |
00665 
00666           _
00667          / \     (circle) :   identified
00668          \_/
00669     */
00670 
00671     double color = fors_image_get_min(std);
00672     if (stars != NULL)
00673     {
00674         fors_star *s;
00675         for (s = fors_star_list_first(stars);
00676              s != NULL; 
00677              s = fors_star_list_next(stars)) {
00678             fors_image_draw(std, 0,
00679                             s->pixel->x,
00680                             s->pixel->y,
00681                             10, color);
00682 
00683             if (s->id != NULL && s->id->trusted) {
00684                 fors_image_draw(std, 2,
00685                                 s->pixel->x,
00686                                 s->pixel->y,
00687                                 10, color);
00688             }
00689         }
00690     }
00691     if (cat != NULL)
00692     {
00693         fors_std_star *s;
00694         for (s = fors_std_star_list_first(cat);
00695              s != NULL; 
00696              s = fors_std_star_list_next(cat))
00697         {
00698             if (s->trusted)
00699                 fors_image_draw(std, 1,
00700                             s->pixel->x,
00701                             s->pixel->y,
00702                             10, color);
00703         }
00704         /* FIXME: FAP: use WCS corrected header */
00705         fors_dfs_save_image(frames, std, "DEBUG",
00706                             product_header, parameters, fors_zeropoint_name, 
00707                             cpl_frameset_get_first(std_frame));
00708         assure( !cpl_error_get_code(), return, "Saving %s failed",
00709                 "DEBUG");
00710     }
00711 
00712     cleanup;
00713     return;
00714 }
00715 
00724 static bool
00725 zeropoint_inside(const fors_star *s,
00726                  void *data) 
00727 {
00728     struct {
00729         double hi, lo;   /* magnitude */
00730         double z, kappa; /* avg zeropoint, kappa */
00731     } *cuts = data;
00732     
00733     double z  = fors_star_get_zeropoint(s, NULL);
00734     double dz = fors_star_get_zeropoint_err(s, NULL);
00735 
00736     return
00737         (cuts->lo                 <= z && z <= cuts->hi) ||
00738         (cuts->z - cuts->kappa*dz <= z && z <= cuts->z + cuts->kappa*dz);
00739 }
00740 
00741 #undef cleanup
00742 #define cleanup \
00743 do { \
00744     fors_star_list_delete(&subset, fors_star_delete); \
00745     fors_star_list_delete(&identified, fors_star_delete); \
00746 } while(0)
00747 
00759 static double
00760 get_zeropoint(fors_star_list *stars, 
00761               double cutoffE,
00762               double cutoffk,
00763               double dext_coeff,
00764               double dcolor_term,
00765               double avg_airmass,
00766               double *dzeropoint,
00767               int *n)
00768 {
00769     fors_star_list *subset = NULL;
00770     fors_star_list *identified = 
00771         fors_star_list_extract(stars, fors_star_duplicate,
00772                                fors_star_is_identified, NULL);
00773 
00774     assure( stars != NULL, return 0, NULL );
00775     assure( dzeropoint != NULL, return 0, NULL );
00776     assure( n != NULL, return 0, NULL );
00777 
00778     if ( fors_star_list_size(identified) == 0 ) {
00779         cpl_msg_warning(cpl_func, 
00780                         "No identified stars for zeropoint computation");
00781         *n = 0;
00782         *dzeropoint = 0;
00783         cleanup;
00784         return 0;
00785     }
00786     
00787     cpl_msg_info(cpl_func, "Computing zeropoint (assuming extinction)");
00788     cpl_msg_indent_more();
00789 
00790     double zeropoint;
00791     double red_chisq = -1.0;
00792 
00793     /* This method does not take into account that the error bars are
00794        correlated, and therefore computes an unrealistically low
00795        dzeropoint */
00796     zeropoint = fors_star_list_mean_optimal(identified,
00797                                             fors_star_get_zeropoint, NULL,
00798                                             fors_star_get_zeropoint_err, NULL,
00799                                             dzeropoint,
00800                                             fors_star_list_size(identified) >= 2 ? &red_chisq : NULL);
00801 
00802     cpl_msg_info(cpl_func, "Optimal zeropoint (no rejection, %d stars) = %f mag",
00803                  fors_star_list_size(identified), zeropoint);  
00804     
00805     /* Reject stars that are absolute (0.3 mag) outliers and
00806        kappa sigma outliers. For robustness (against error in the initial
00807        zeropoint estimates) apply the absolute cut in two steps
00808        and update the estimated zeropoint after the first step.
00809     */
00810     struct {
00811         double hi, lo;   /* magnitude */
00812         double z, kappa; /* avg zeropoint, kappa */
00813     } cuts;
00814     cuts.hi = zeropoint + 5*cutoffE;
00815     cuts.lo = zeropoint - 5*cutoffE;
00816     cuts.z = zeropoint;
00817     cuts.kappa = cutoffk;
00818     
00819     subset = fors_star_list_extract(identified, fors_star_duplicate,
00820                                     zeropoint_inside, &cuts);
00821 
00822 
00823     if ( fors_star_list_size(subset) == 0 ) {
00824         cpl_msg_warning(cpl_func, 
00825                         "All stars rejected (%f mag). Cannot "
00826                         "compute zeropoint", 5*cutoffE);
00827         *n = 0;
00828         *dzeropoint = 0;
00829         cleanup;
00830         return 0;
00831     }
00832 
00833     zeropoint = fors_star_list_mean_optimal(subset,
00834                                             fors_star_get_zeropoint, NULL,
00835                                             fors_star_get_zeropoint_err, NULL,
00836                                             dzeropoint,
00837                                             fors_star_list_size(subset) >= 2 ? &red_chisq : NULL);
00838 
00839     cpl_msg_debug(cpl_func, "Optimal zeropoint (%.2f mag, %.2f sigma rejection) = %f mag",
00840                   5*cutoffE, cutoffk, zeropoint);  
00841     
00842     cuts.hi = zeropoint + cutoffE;
00843     cuts.lo = zeropoint - cutoffE;
00844     cuts.z = zeropoint;
00845     cuts.kappa = cutoffk;
00846     
00847     {
00848         fors_star_list *tmp = fors_star_list_duplicate(subset, fors_star_duplicate);
00849         fors_star_list_delete(&subset, fors_star_delete);
00850 
00851         subset = fors_star_list_extract(tmp, fors_star_duplicate,
00852                                         zeropoint_inside, &cuts);
00853 
00854         if ( fors_star_list_size(subset) == 0 ) {
00855             cpl_msg_warning(cpl_func, 
00856                             "All stars rejected (%f mag, %f sigma). Cannot "
00857                             "compute zeropoint", cutoffE, cutoffk);
00858             *n = 0;
00859             *dzeropoint = 0;
00860             cleanup;
00861             return 0;
00862         }
00863 
00864         fors_star_list_delete(&tmp, fors_star_delete);
00865     }
00866 
00867     zeropoint = fors_star_list_mean_optimal(subset,
00868                                             fors_star_get_zeropoint, NULL,
00869                                             fors_star_get_zeropoint_err, NULL,
00870                                             dzeropoint,
00871                                             fors_star_list_size(subset) >= 2 ? &red_chisq : NULL);
00872     
00873     cpl_msg_info(cpl_func, "Optimal zeropoint (%.2f mag, %.2f sigma rejection) = %f mag",
00874                  cutoffE, cutoffk, zeropoint);  
00875     
00876     *n = fors_star_list_size(subset);
00877     {
00878         int outliers = 
00879             fors_star_list_size(identified) - fors_star_list_size(subset);
00880         cpl_msg_info(cpl_func, "%d outlier%s rejected, %d non-outliers",
00881                      outliers, outliers == 1 ? "" : "s",
00882                      *n);
00883     }
00884 
00885     if ( *n == 0 ) {
00886         cpl_msg_warning(cpl_func, 
00887                         "All stars were rejected during zeropoint computation" );
00888         *dzeropoint = 0;
00889         cleanup;
00890         return 0;
00891     }
00892 
00893 
00894 
00895 
00896 
00897 
00898 
00899     /*
00900       Build zeropoint covariance matrix.
00901       We have already the variances from fors_star_get_zeropoint_err().
00902       Non-diagonal terms are
00903          Cij = airmass^2 * Variance(ext.coeff) + color_i * color_j * Variance(color.coeff)
00904 
00905       It was considered and tried to subtract the term
00906                airmass^2 * Variance(ext.coeff)
00907       from every Cij. This has no effect on the relative weights, only the weight's overall
00908       normalization. Since we use the normalization for computing the zeropoint error this
00909       term is kept.
00910 
00911     */
00912     cpl_matrix *covariance = cpl_matrix_new(*n,
00913                                             *n);
00914 
00915     /* Duplicate the list to allow simultaneous iterations */
00916     fors_star_list *ident_dup = fors_star_list_duplicate(subset, fors_star_duplicate);
00917     {
00918       
00919       fors_star *s, *t;
00920       int i, j;
00921       for (s = fors_star_list_first(subset), i = 0;
00922            s != NULL;
00923            s = fors_star_list_next(subset), i++) {
00924 
00925         for (t = fors_star_list_first(ident_dup), j = 0;
00926              t != NULL;
00927              t = fors_star_list_next(ident_dup), j++) {
00928           
00929           double cij;
00930 
00931           if (fors_star_equal(s, t)) {
00932               cij = fors_star_get_zeropoint_err(s, NULL)*fors_star_get_zeropoint_err(s, NULL);
00933               /*  -avg_airmass*avg_airmass*dext_coeff*dext_coeff */
00934           }
00935           else {
00936               cij = s->id->color * t->id->color * dcolor_term*dcolor_term
00937                   + avg_airmass*avg_airmass*dext_coeff*dext_coeff;
00938           }
00939           
00940           cpl_matrix_set(covariance, i, j, cij);
00941         }
00942       }
00943     }
00944     /* cpl_matrix_dump(covariance, stdout); */
00945     /* cpl_matrix_dump(cpl_matrix_invert_create(covariance), stdout); */
00946 
00947     /*
00948       Compute optimal weights, w, as
00949       
00950       w = C^-1 * const
00951 
00952       C    : nxn covariance matrix
00953       const: nx1 constant vector with all elements equal to 1
00954      */
00955 
00956     cpl_matrix *covariance_inverse = cpl_matrix_invert_create(covariance);
00957 
00958     assure( !cpl_error_get_code(), return 0,
00959             "Could not invert zeropoints covariance matrix");
00960 
00961     /*  cpl_matrix_dump(cpl_matrix_product_create(covariance_inverse, covariance), stdout); */
00962 
00963     /* fprintf(stderr, "is_identity = %d\n", cpl_matrix_is_identity(cpl_matrix_product_create(covariance_inverse, covariance),1e-10)); */
00964 
00965     cpl_matrix_delete(covariance); covariance = NULL;
00966 
00967     cpl_matrix *const_vector = cpl_matrix_new(*n, 1);
00968     cpl_matrix_fill(const_vector, 1.0);
00969     
00970     cpl_matrix *weights = cpl_matrix_product_create(covariance_inverse, const_vector);
00971 
00972     cpl_matrix_delete(const_vector); const_vector = NULL;
00973 
00974     /* cpl_matrix_dump(weights, stdout); */
00975     
00976     
00977     {
00978         double wz = 0;
00979         double w = 0;
00980         
00981         int i;
00982         fors_star *s;
00983         for (i = 0, s = fors_star_list_first(subset);
00984              s != NULL;
00985              s = fors_star_list_next(subset), i++) {
00986             
00987             double weight = cpl_matrix_get(weights, i, 0);
00988 
00989             cpl_msg_debug(cpl_func, "Weight_%d = %f", i, weight);
00990             
00991             wz += weight * fors_star_get_zeropoint(s, NULL);
00992             w += weight;
00993 
00994             /* Loop through original list to record the weight of this star */
00995             {
00996                 fors_star *t;
00997                 
00998                 for (t = fors_star_list_first(stars);
00999                      t != NULL;
01000                      t = fors_star_list_next(stars)) {
01001                     
01002                     if (fors_star_equal(s, t)) {
01003                         t->weight = weight;
01004                     }
01005                 }
01006             }
01007         }
01008 
01009         cpl_matrix_delete(weights); weights = NULL;
01010 
01011         cpl_msg_debug(cpl_func, "Sum of weights = %f", w);
01012 
01013         /* 
01014            C is positive definite (because all eigenvalues are positive). Therefore
01015            C^-1 is also positive definite, a property of positive definite matrices.
01016            
01017            Positive definite matrices always have the property that
01018                  z* C z > 0     for any non-zero (complex) vector z
01019 
01020            The sum of the weights is just
01021                 const.* w = const.* C^-1 const.
01022            where const. is our constant vector filled with 1. Therefore the sum of
01023            the weights should always be positive. But make the paranoia check anyway:
01024          */
01025 
01026         assure( w != 0, return 0, "Sum of optimal weights is zero!" );
01027         assure( sqrt(w) != 0, return 0, "Square root of sum of optimal weights is zero!" );
01028         
01029         zeropoint = wz / w;
01030         *dzeropoint = 1 / sqrt(w);
01031     }
01032 
01033     /* Previous code: weighted average, uncorrelated errors
01034 
01035     zeropoint = fors_star_list_mean_optimal(
01036         subset, 
01037         fors_star_get_zeropoint, NULL,
01038         fors_star_get_zeropoint_err, NULL,
01039         dzeropoint,
01040         *n >= 2 ? &red_chisq : NULL);
01041     */
01042     
01043     cpl_msg_info(cpl_func, "Optimal zeropoint = %f +- %f mag", 
01044                  zeropoint, *dzeropoint);
01045 
01046     if (*n >= 2) {
01047         fors_star *s, *t;
01048         int i, j;
01049 
01050         red_chisq = 0;
01051 
01052         for (s = fors_star_list_first(subset), i = 0;
01053              s != NULL;
01054              s = fors_star_list_next(subset), i++) {
01055             
01056             for (t = fors_star_list_first(ident_dup), j = 0;
01057                  t != NULL;
01058                  t = fors_star_list_next(ident_dup), j++) {
01059                 
01060                 red_chisq += 
01061                     (fors_star_get_zeropoint(s, NULL) - zeropoint) *
01062                     (fors_star_get_zeropoint(t, NULL) - zeropoint) *
01063                     cpl_matrix_get(covariance_inverse, i, j);
01064             }
01065         }
01066         red_chisq /= (*n - 1);
01067     }
01068 
01069     cpl_matrix_delete(covariance_inverse); covariance_inverse = NULL;
01070 
01071     fors_star_list_delete(&ident_dup, fors_star_delete);
01072     
01073     cpl_msg_info(cpl_func, "Reduced chi square = %f", red_chisq);
01074     
01075     cpl_msg_indent_less();
01076 
01077     cleanup;
01078     return zeropoint;
01079 }
01080 
01081 #undef cleanup
01082 #define cleanup \
01083 do { \
01084     fors_std_star_list_delete(&internal_cat, fors_std_star_delete); \
01085 } while (0)
01086 
01106 static cpl_error_code
01107 fors_zeropoint_astrometry(                  const cpl_frameset  *std_cat_frames,
01108                                             char                filter_band,
01109                                             double              color_correct,
01110                                             double              dcolor_correct,
01111                                             const identify_method
01112                                                                 *id_method,
01113                                             fors_star_list      *extracted,
01114                                             cpl_propertylist    *wcs_header,
01115                                             fors_std_star_list  **std_cat,
01116                                             cpl_image           **histogram)
01117 {
01118     double              dcrpix_x = 0,
01119                         dcrpix_y = 0;
01120     fors_std_star_list  *internal_cat = NULL,
01121                         *used_cat;
01122     cpl_errorstate      errstat = cpl_errorstate_get();
01123 
01124     
01125     if (std_cat != NULL)
01126         *std_cat = NULL;
01127     
01128     cassure_automsg(                        std_cat_frames != NULL,
01129                                             CPL_ERROR_NULL_INPUT,
01130                                             return cpl_error_get_code());
01131     cassure_automsg(                        id_method != NULL,
01132                                             CPL_ERROR_NULL_INPUT,
01133                                             return cpl_error_get_code());
01134     cassure_automsg(                        extracted != NULL,
01135                                             CPL_ERROR_NULL_INPUT,
01136                                             return cpl_error_get_code());
01137     cassure_automsg(                        wcs_header != NULL,
01138                                             CPL_ERROR_NULL_INPUT,
01139                                             return cpl_error_get_code());
01140 
01141     cpl_msg_info(cpl_func, "Loading standard star catalogue(s):");
01142     cpl_msg_indent_more();
01143     /*
01144     used_cat = internal_cat = fors_std_cat_load_old(
01145                                             std_cat_frames,
01146                                             filter_band,
01147                                             color_term,
01148                                             dcolor_term);
01149     */
01150     used_cat = internal_cat = fors_std_cat_load(
01151                                             std_cat_frames,
01152                                             filter_band,
01153                                             0,  /* don't require all frames */
01154                                             color_correct,
01155                                             dcolor_correct);
01156     cpl_msg_indent_less();
01157     assure(                                 cpl_errorstate_is_equal(errstat),
01158                                             return cpl_error_get_code(),
01159                                             "Std catalogue loading failed");
01160     
01161     /* keep catalogue for output if desired */
01162     if (std_cat != NULL)
01163     {
01164         *std_cat = used_cat;
01165         internal_cat = NULL;
01166     }
01167     if (0) if (used_cat) fors_std_star_print_list(CPL_MSG_DEBUG, used_cat);
01168 
01169     /* get (x,y) std star positions */
01170     fors_std_star_list_apply_wcs(           used_cat, wcs_header);
01171     assure(                                 cpl_errorstate_is_equal(errstat),
01172                                             return cpl_error_get_code(),
01173                                             "Failed to apply WCS to catalogue");
01174 
01175     /* Identify (std stars are duplicated and linked to stars) */
01176     fors_identify(extracted, used_cat, id_method, histogram);
01177     assure(                                 cpl_errorstate_is_equal(errstat),
01178                                             return cpl_error_get_code(),
01179                                             "Failed to identify sources");
01180 
01181     /* correct the wcs */
01182     fors_zeropoint_astrometry_get_wcs_shift_px(
01183                                             extracted,
01184                                             &dcrpix_x,
01185                                             &dcrpix_y);
01186     assure(                                 cpl_errorstate_is_equal(errstat),
01187                                             return cpl_error_get_code(),
01188                                             "Failed to determine WCS correction"
01189                                             );
01190 
01191     cpl_msg_info(                           cpl_func,
01192                                             "Correcting the WCS origin by "
01193                                             "(%f, %f)",
01194                                             -dcrpix_x,
01195                                             -dcrpix_y);
01196 
01197     fors_zeropoint_astrometry_shift_wcs_origin(
01198                                             wcs_header,
01199                                             -dcrpix_x,
01200                                             -dcrpix_y);
01201     assure(                                 cpl_errorstate_is_equal(errstat),
01202                                             return cpl_error_get_code(),
01203                                             "Failed to correct WCS origin");
01204 
01205     /* re-apply (x,y) std star positions */
01206     {
01207         /* create a list of the identified std stars */
01208         fors_star   *s;
01209         fors_std_star_list  *identified;
01210         identified = fors_std_star_list_new();
01211 
01212         for (   s = fors_star_list_first(extracted);
01213                 s != NULL;
01214                 s = fors_star_list_next(extracted))
01215         {
01216             if (s->id != NULL)
01217                 fors_std_star_list_insert(  identified,
01218                                             (fors_std_star*)s->id); /* !const */
01219         }
01220 
01221         if (fors_std_star_list_size(identified) > 0)
01222             fors_std_star_list_apply_wcs(identified, wcs_header);
01223 
01224         /* delete the list object but not the std stars */
01225         fors_std_star_list_delete(&identified, NULL);
01226     }
01227     assure(                                 cpl_errorstate_is_equal(errstat),
01228                                             return cpl_error_get_code(),
01229                                             "Failed to correct std (x,y)");
01230 
01231     /* ATTENTION: the following code links fors_std_star* objects to
01232      * the stars, which represent non-std stars (trusted-flag=false).
01233      * make sure later invoked functions respect this flag!
01234      *
01235      * Non-std stars are created and linked to stars.
01236      * Btw., those stars can be any sextracted celestial object. */
01237     cpl_msg_info(cpl_func, "Assigning RA & DEC to non-standard stars");
01238     cpl_msg_indent_more();
01239     fors_zeropoint_astrometry_apply_unidentified_xy2radec(
01240                                             extracted,
01241                                             wcs_header);
01242     cpl_msg_indent_less();
01243     assure(                                 cpl_errorstate_is_equal(errstat),
01244                                             return cpl_error_get_code(),
01245                                             "Failed to apply WCS to non-"
01246                                             "identified stars");
01247 
01248     cleanup;
01249     return (    cpl_errorstate_is_equal(errstat) ?
01250                 CPL_ERROR_NONE :
01251                 cpl_error_get_code());
01252 }
01253 
01254 #undef cleanup
01255 #define cleanup \
01256 do { \
01257     cpl_vector_delete(vdx_long); vdx_long = NULL; \
01258     cpl_vector_delete(vdy_long); vdy_long = NULL; \
01259     cpl_vector_unwrap(vdx_only_std); vdx_only_std = NULL; \
01260     cpl_vector_unwrap(vdy_only_std); vdy_only_std = NULL; \
01261 } while (0)
01262 
01269 static cpl_error_code
01270 fors_zeropoint_astrometry_get_wcs_shift_px( const fors_star_list    *stars,
01271                                             double                  *dx,
01272                                             double                  *dy)
01273 {
01274     const fors_star *s;
01275     int             n_stars,
01276                     n_std_stars = 0;
01277     cpl_vector      *vdx_long = NULL,
01278                     *vdx_only_std = NULL,
01279                     *vdy_long = NULL,
01280                     *vdy_only_std = NULL;
01281     cpl_errorstate  errstat = cpl_errorstate_get();
01282     
01283     /* init output */
01284     if (dx != NULL)
01285         *dx = 0;
01286     if (dy != NULL)
01287         *dy = 0;
01288     
01289     cassure_automsg(                        stars != NULL,
01290                                             CPL_ERROR_NULL_INPUT,
01291                                             return cpl_error_get_code());
01292     cassure_automsg(                        dx != NULL,
01293                                             CPL_ERROR_NULL_INPUT,
01294                                             return cpl_error_get_code());
01295     cassure_automsg(                        dy != NULL,
01296                                             CPL_ERROR_NULL_INPUT,
01297                                             return cpl_error_get_code());
01298     
01299     n_stars = fors_star_list_size(stars);
01300     
01301     cassure_automsg(                        n_stars > 0,
01302                                             CPL_ERROR_DATA_NOT_FOUND,
01303                                             return cpl_error_get_code());
01304     
01305     vdx_long = cpl_vector_new(n_stars);
01306     vdy_long = cpl_vector_new(n_stars);
01307     
01308     for (   s = fors_star_list_first_const(stars);
01309             s != NULL;
01310             s = fors_star_list_next_const(stars))
01311     {
01312         const fors_std_star *id = s->id;
01313         if (id != NULL)
01314         {
01315             cpl_vector_set(vdx_long, n_std_stars, id->pixel->x - s->pixel->x);
01316             cpl_vector_set(vdy_long, n_std_stars, id->pixel->y - s->pixel->y);
01317             n_std_stars++;
01318         }
01319     }
01320     
01321     passure(                                cpl_errorstate_is_equal(errstat),
01322                                             return cpl_error_get_code());
01323     cassure_automsg(                        n_std_stars > 0,
01324                                             CPL_ERROR_DATA_NOT_FOUND,
01325                                             return cpl_error_get_code());
01326     
01327     vdx_only_std = cpl_vector_wrap(n_std_stars, cpl_vector_get_data(vdx_long));
01328     vdy_only_std = cpl_vector_wrap(n_std_stars, cpl_vector_get_data(vdy_long));
01329     
01330     *dx = cpl_vector_get_median(vdx_only_std);
01331     *dy = cpl_vector_get_median(vdy_only_std);
01332     
01333     passure(                                cpl_errorstate_is_equal(errstat),
01334                                             return cpl_error_get_code());
01335     
01336     cleanup;
01337     
01338     return CPL_ERROR_NONE;
01339 }
01340 
01341 #undef cleanup
01342 #define cleanup
01343 
01350 static cpl_error_code
01351 fors_zeropoint_astrometry_shift_wcs_origin( cpl_propertylist    *header,
01352                                             double  dx,
01353                                             double  dy)
01354 {
01355     double          crpix_x,
01356                     crpix_y;
01357     cpl_errorstate  errstat = cpl_errorstate_get();
01358     
01359     cassure_automsg(                        header != NULL,
01360                                             CPL_ERROR_NULL_INPUT,
01361                                             return cpl_error_get_code());
01362     
01363     /* FIXME: wanted: creating wcs from header, modifying wcs, storing
01364      * back wcs into header. This was not possible due to the lack
01365      * of a function to store a wcs in a property list. */
01366     
01367     if (!cpl_propertylist_has(header, "CRPIX1"))
01368     {
01369         cpl_error_set_message(              cpl_func,
01370                                             CPL_ERROR_DATA_NOT_FOUND,
01371                                             "no WCS found in header");
01372         cleanup;
01373         return cpl_error_get_code();
01374     }
01375     if (!cpl_propertylist_has(header, "CRPIX2")
01376         || cpl_propertylist_has(header, "CRPIX3"))
01377     {
01378         cpl_error_set_message(              cpl_func,
01379                                             CPL_ERROR_INCOMPATIBLE_INPUT,
01380                                             "WCS in header is not "
01381                                             "2-dimensional");
01382         cleanup;
01383         return cpl_error_get_code();
01384     }
01385     
01386     
01387     crpix_x = cpl_propertylist_get_double(header, "CRPIX1");
01388     crpix_y = cpl_propertylist_get_double(header, "CRPIX2");
01389     assure(                                 cpl_errorstate_is_equal(errstat),
01390                                             return cpl_error_get_code(),
01391                                             "CRPIXn keywords must be of type "
01392                                             "double");
01393     crpix_x += dx;
01394     crpix_y += dy;
01395     cpl_propertylist_set_double(header, "CRPIX1", crpix_x);
01396     cpl_propertylist_set_double(header, "CRPIX2", crpix_y);
01397     
01398     return (    cpl_errorstate_is_equal(errstat) ?
01399                 CPL_ERROR_NONE :
01400                 cpl_error_get_code());
01401 }
01402 
01403 #undef cleanup
01404 #define cleanup \
01405 do { \
01406     cpl_wcs_delete(wcs); wcs = NULL; \
01407     cpl_matrix_delete(mradec); mradec = NULL; \
01408     cpl_matrix_delete(mxy); mxy = NULL; \
01409     cpl_array_delete(wcs_conversion_status); wcs_conversion_status = NULL; \
01410     fors_std_star_delete(&unknown); \
01411 } while (0)
01412 
01422 static cpl_error_code
01423 fors_zeropoint_astrometry_apply_unidentified_xy2radec(
01424                                             fors_star_list          *stars,
01425                                             const cpl_propertylist  *header)
01426 {
01427     cpl_wcs             *wcs = NULL;
01428     cpl_matrix          *mradec = NULL,
01429                         *mxy = NULL;
01430     cpl_array           *wcs_conversion_status = NULL;
01431     fors_star           *star = NULL;
01432     fors_std_star       *unknown = NULL;
01433     int                 n_unknowns = 0,
01434                         n_wcs_successes = 0,
01435                         n;
01436     int                 *sdat;
01437     union _nant {
01438         unsigned char   bytes[8];
01439         double          val;
01440     } nan;
01441     cpl_errorstate      errstat = cpl_errorstate_get();
01442     
01443     cassure_automsg(                        stars != NULL,
01444                                             CPL_ERROR_NULL_INPUT,
01445                                             return cpl_error_get_code());
01446     cassure_automsg(                        header != NULL,
01447                                             CPL_ERROR_NULL_INPUT,
01448                                             return cpl_error_get_code());
01449     
01450     wcs = cpl_wcs_new_from_propertylist(    header);
01451     assure(cpl_errorstate_is_equal(errstat), return cpl_error_get_code(), NULL);
01452     
01453     {
01454         /* create NaN */
01455         int n;
01456         for (n = 0; n < 8; n++)
01457             nan.bytes[n] = (unsigned char)255;
01458     }
01459     
01460     /* assuming that cpl_wcs_convert has coordinates in rows */
01461     /* count unknowns */
01462     for (   star = fors_star_list_first(stars);
01463             star != NULL;
01464             star = fors_star_list_next(stars))
01465     {
01466         if (star->id == NULL)
01467             n_unknowns++;
01468     }
01469     passure(cpl_errorstate_is_equal(errstat), return cpl_error_get_code());
01470 
01471     if (n_unknowns == 0)
01472         return cpl_error_get_code();
01473             
01474     mxy = cpl_matrix_new(n_unknowns, 2);
01475     passure(cpl_errorstate_is_equal(errstat), return cpl_error_get_code());
01476     
01477     /* copy x,y */
01478     for (   star = fors_star_list_first(stars), n = 0;
01479             star != NULL;
01480             star = fors_star_list_next(stars))
01481     {
01482         if (star->id == NULL)
01483         {
01484             cpl_matrix_set(mxy, n, 0, star->pixel->x);
01485             cpl_matrix_set(mxy, n, 1, star->pixel->y);
01486             n++;
01487         }
01488     }
01489     passure(cpl_errorstate_is_equal(errstat), return cpl_error_get_code());
01490     
01491     cpl_wcs_convert(                        wcs,
01492                                             mxy,
01493                                             &mradec,
01494                                             &wcs_conversion_status,
01495                                             CPL_WCS_PHYS2WORLD);
01496     assure(cpl_errorstate_is_equal(errstat), return cpl_error_get_code(), NULL);
01497     cpl_matrix_delete(mxy); mxy = NULL;
01498     
01499     passure(n_unknowns == cpl_array_get_size(wcs_conversion_status),
01500             return cpl_error_get_code());
01501     
01502     /* FIXME: we have to operate on the data array of wcs_conversion_status,
01503      * because currently cpl_wcs_convert() does the same and thus ignores
01504      * the "valid" flags */
01505     sdat = cpl_array_get_data_int(wcs_conversion_status);
01506     
01507     for (   star = fors_star_list_first(stars), n = 0;
01508             star != NULL;
01509             star = fors_star_list_next(stars))
01510     {
01511         if (star->id == NULL)
01512         {
01513             if (sdat[n] == 0)
01514             {
01515                 double  ra,
01516                         dec;
01517                 n_wcs_successes++;
01518                 
01519                 ra = cpl_matrix_get(mradec, n, 0);
01520                 dec = cpl_matrix_get(mradec, n, 1);
01521                 unknown = fors_std_star_new(ra, dec,
01522                                             nan.val, nan.val,
01523                                             nan.val, nan.val,
01524                                             nan.val, nan.val,
01525                                             nan.val,
01526                                             NULL);  /* no name */
01527                 passure(                    cpl_errorstate_is_equal(errstat),
01528                                             return cpl_error_get_code());
01529                 
01530                 unknown->trusted = false;   /* this star has no catalogue mag*/
01531                 unknown->pixel->x = star->pixel->x;
01532                 unknown->pixel->y = star->pixel->y;
01533                 
01534                 star->id = unknown;
01535                 unknown = NULL;
01536             }
01537             n++;
01538         }
01539     }
01540     
01541     cpl_msg_info(                           cpl_func,
01542                                             "Assigned RA & DEC to %d unknown "
01543                                             "stars",
01544                                             n_wcs_successes);
01545     if (n_wcs_successes < n_unknowns)
01546         cpl_msg_warning(                    cpl_func,
01547                                             "%d WCS conversions failed",
01548                                             n_unknowns - n_wcs_successes);
01549     
01550     cleanup;
01551     
01552     return (    cpl_errorstate_is_equal(errstat) ?
01553                 CPL_ERROR_NONE :
01554                 cpl_error_get_code());
01555 }
01556 
01557 #undef cleanup
01558 #define cleanup
01559 
01566 void
01567 fors_zeropoint_errorstate_dump_as_warning(  unsigned self,
01568                                             unsigned first,
01569                                             unsigned last)
01570 {
01571     const cpl_boolean is_reverse = first > last ? CPL_TRUE : CPL_FALSE;
01572     const unsigned    newest     = is_reverse ? first : last;
01573     /*const unsigned    oldest     = is_reverse ? last : first;*/
01574     self = self;
01575     first = first;
01576     last = last;
01577     /*assert( oldest <= self );
01578     assert( newest >= self );*/
01579     
01580     if (newest == 0)
01581     {
01582         /*assert( oldest == 0);*/
01583         cpl_msg_info(cpl_func, "Success");
01584     }
01585     else
01586     {
01587         /*assert( oldest > 0);*/
01588         cpl_msg_warning(                    cpl_func,
01589                                             "- %s (%s(), %s: %d)",
01590                                             cpl_error_get_message(),
01591                                             cpl_error_get_function(),
01592                                             cpl_error_get_file(),
01593                                             cpl_error_get_line());
01594         /*if (self == last)
01595             cpl_msg_indent_less();*/
01596     }
01597 }
01598