fors_identify.c

00001 /* $Id: fors_identify.c,v 1.45 2010/09/14 07:49:30 cizzo 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: cizzo $
00023  * $Date: 2010/09/14 07:49:30 $
00024  * $Revision: 1.45 $
00025  * $Name: fors-4_8_6 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <fors_identify.h>
00033 
00034 #include <fors_image.h>
00035 #include <fors_pattern.h>
00036 #include <fors_point.h>
00037 #include <fors_dfs.h>
00038 #include <fors_utils.h>
00039 #include <fors_double.h>
00040 
00041 #include <cpl.h>
00042 
00043 #include <math.h>
00044 #include <assert.h>
00045 
00052 struct _identify_method
00053 {
00054     int ncat;
00055     double nsource;
00056     double kappa;
00057     double search;
00058     double max_search;
00059     double max_offset;
00060 };
00061 
00062 static bool
00063 inside_region(const fors_std_star *std,
00064               void *reg);
00065 
00066 static void
00067 match_patterns(const fors_star_list *stars, 
00068                const fors_std_star_list *std,
00069                double kappa,
00070                double *sx_00,
00071                double *sy_00,
00072                double *med_scale,
00073                double *med_angle,
00074                int    *status);
00075 
00081 void 
00082 fors_identify_define_parameters(cpl_parameterlist *parameters, 
00083                                 const char *context)
00084 {
00085     cpl_parameter *p;
00086     const char *full_name = NULL;
00087     const char *name;
00088 
00089     name = "ncat";
00090     full_name = cpl_sprintf("%s.%s", context, name);
00091     p = cpl_parameter_new_value(full_name,
00092                                 CPL_TYPE_INT,
00093                                 "Number of catalog stars to use in "
00094                                 "pattern matching",
00095                                 context,
00096                                 10);
00097                                          
00098     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00099     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00100     cpl_parameterlist_append(parameters, p);
00101     cpl_free((void *)full_name);
00102 
00103 
00104     name = "nsource";
00105     full_name = cpl_sprintf("%s.%s", context, name);
00106     p = cpl_parameter_new_value(full_name,
00107                                 CPL_TYPE_DOUBLE,
00108                                 "Number of sources to use in "
00109                                 "pattern matching, pr. catalog star",
00110                                 context,
00111                                 15.0);
00112                                          
00113     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00114     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00115     cpl_parameterlist_append(parameters, p);
00116     cpl_free((void *)full_name);
00117 
00118 
00119     name = "kappa";
00120     full_name = cpl_sprintf("%s.%s", context, name);
00121     p = cpl_parameter_new_value(full_name,
00122                                 CPL_TYPE_DOUBLE,
00123                                 "Rejection parameter (scale, angle) used "
00124                                 "in pattern matching ",
00125                                 context,
00126                                 5.0);
00127     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00128     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00129     cpl_parameterlist_append(parameters, p);
00130     cpl_free((void *)full_name);
00131     
00132 
00133     name = "search";
00134     full_name = cpl_sprintf("%s.%s", context, name);
00135     p = cpl_parameter_new_value(full_name,
00136                                 CPL_TYPE_DOUBLE,
00137                                 "Search radius (pixels)",
00138                                 context,
00139                                 20.0);
00140     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00141     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00142     cpl_parameterlist_append(parameters, p);
00143     cpl_free((void *)full_name);
00144     
00145     name = "maxsearch";
00146     full_name = cpl_sprintf("%s.%s", context, name);
00147     p = cpl_parameter_new_value(full_name,
00148                                 CPL_TYPE_DOUBLE,
00149                                 "Maximum search radius (pixels)",
00150                                 context,
00151                                 20.0);
00152     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00153     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00154     cpl_parameterlist_append(parameters, p);
00155     cpl_free((void *)full_name);
00156 
00157     name = "maxoffset";
00158     full_name = cpl_sprintf("%s.%s", context, name);
00159     p = cpl_parameter_new_value(full_name,
00160                                 CPL_TYPE_DOUBLE,
00161     "Maximum accepted offset between WCS and pattern matching result (pixels)",
00162                                 context,
00163                                 24.0);
00164     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00165     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00166     cpl_parameterlist_append(parameters, p);
00167     cpl_free((void *)full_name);
00168     
00169     return;
00170 }
00171 
00172 #undef cleanup
00173 #define cleanup \
00174 do { \
00175     cpl_free((void *)name); \
00176 } while (0)
00177 
00186 identify_method *
00187 fors_identify_method_new(const cpl_parameterlist *parameters, const char *context)
00188 {
00189     identify_method *im = cpl_malloc(sizeof(*im));
00190     const char *name = NULL;
00191 
00192     cpl_msg_info(cpl_func, "Identification parameters:");
00193        
00194     cpl_msg_indent_more();
00195     name = cpl_sprintf("%s.%s", context, "ncat");
00196     im->ncat = dfs_get_parameter_int_const(parameters, name);
00197     cpl_free((void *)name); name = NULL;
00198     cpl_msg_indent_less();
00199 
00200        
00201     cpl_msg_indent_more();
00202     name = cpl_sprintf("%s.%s", context, "nsource");
00203     im->nsource = dfs_get_parameter_double_const(parameters, name);
00204     cpl_free((void *)name); name = NULL;
00205     cpl_msg_indent_less();
00206 
00207        
00208     cpl_msg_indent_more();
00209     name = cpl_sprintf("%s.%s", context, "kappa");
00210     im->kappa = dfs_get_parameter_double_const(parameters, name);
00211     cpl_free((void *)name); name = NULL;
00212     cpl_msg_indent_less();
00213     
00214 
00215     cpl_msg_indent_more();
00216     name = cpl_sprintf("%s.%s", context, "search");
00217     im->search = dfs_get_parameter_double_const(parameters, name);
00218     cpl_free((void *)name); name = NULL;
00219     cpl_msg_indent_less();
00220     
00221    
00222     cpl_msg_indent_more();
00223     name = cpl_sprintf("%s.%s", context, "maxsearch");
00224     im->max_search = dfs_get_parameter_double_const(parameters, name);
00225     cpl_free((void *)name); name = NULL;
00226     cpl_msg_indent_less();
00227 
00228 
00229     cpl_msg_indent_more();
00230     name = cpl_sprintf("%s.%s", context, "maxoffset");
00231     im->max_offset = dfs_get_parameter_double_const(parameters, name);
00232     cpl_free((void *)name); name = NULL;
00233     cpl_msg_indent_less();
00234     
00235 
00236     assure( !cpl_error_get_code(), return NULL, NULL );
00237     
00238     return im;
00239 }
00240 
00244 void
00245 fors_identify_method_delete(identify_method **em)
00246 {
00247     if (em && *em) {
00248         cpl_free(*em); *em = NULL;
00249     }
00250     return;
00251 }
00252 
00262 static bool
00263 std_brighter_than(const fors_std_star *s1,
00264                   void *s2)
00265 {
00266     return fors_std_star_brighter_than(s1, s2, NULL);
00267 }
00268 
00278 static bool
00279 star_brighter_than(const fors_star *s1,
00280                    void *s2)
00281 {
00282     return fors_star_brighter_than(s1, s2, NULL);
00283 }
00284 
00293 static double
00294 distsq_shift(const fors_star *s,
00295              const fors_std_star *std,
00296              double shiftx,
00297              double shifty)
00298 {
00299     fors_point *shifted_pos = fors_point_new(std->pixel->x + shiftx,
00300                                              std->pixel->y + shifty);
00301     
00302     double result = fors_point_distsq(s->pixel, shifted_pos);
00303     
00304     fors_point_delete(&shifted_pos);
00305     
00306     return result;
00307 }
00308 
00317 static bool
00318 star_nearer(const fors_star *s1,
00319             const fors_star *s2,
00320             void *data) 
00321 {
00322     struct {
00323         double shift_x, shift_y;
00324         const fors_std_star *ref;
00325     } *d = data;
00326     /* Cast is safe, see caller */
00327     
00328     return 
00329         distsq_shift(s1, d->ref, d->shift_x, d->shift_y) <
00330         distsq_shift(s2, d->ref, d->shift_x, d->shift_y);
00331 }
00332 
00333 
00334 #undef cleanup
00335 #define cleanup \
00336 do { \
00337     fors_std_star_list_delete(&std_ccd       , fors_std_star_delete); \
00338     fors_std_star_list_delete(&std_ccd_bright, fors_std_star_delete); \
00339     fors_star_list_delete(&source_bright, fors_star_delete); \
00340 } while (0)
00341 
00360 void
00361 fors_identify(fors_star_list *stars, 
00362               fors_std_star_list *cat,
00363               const identify_method *im)
00364 {
00365     fors_std_star_list *std_ccd = NULL;  /* Subset of catalog stars
00366                                             which are inside the CCD */
00367 
00368     fors_std_star_list *std_ccd_bright = NULL; /* Brightest std stars */
00369 
00370     fors_star_list *source_bright  = NULL;     /* Subset of brightest stars */
00371 
00372     double offset = 0.0;
00373 
00374     int status;
00375 
00376     assure( stars != NULL, return, NULL );
00377 
00378     cpl_msg_info(cpl_func, "Identifying sources");
00379     cpl_msg_indent_more();
00380     
00381      
00382     /*
00383       fors_star_print_list(CPL_MSG_ERROR, stars);
00384       fors_std_star_print_list(CPL_MSG_ERROR, cat); 
00385     */
00386     
00387 
00388     cpl_msg_info(cpl_func, "Pattern matching");
00389     cpl_msg_indent_more();
00390     
00391     /* Select standards inside CCD */
00392     {
00393         double tolerance = 100; /* pixels */
00394         struct {
00395             int xlo, ylo;
00396             int xhi, yhi;
00397         } region;
00398     if (fors_star_list_size(stars) > 0) {
00399         region.xlo = fors_star_get_x(fors_star_list_min_val(stars,
00400                                 fors_star_get_x,
00401                                                             NULL), NULL) - tolerance;
00402         region.ylo = fors_star_get_y(fors_star_list_min_val(stars,
00403                                                             fors_star_get_y,
00404                                                             NULL), NULL) - tolerance;
00405         region.xhi = fors_star_get_x(fors_star_list_max_val(stars,
00406                                                             fors_star_get_x,
00407                                                             NULL), NULL) + tolerance;
00408         region.yhi = fors_star_get_y(fors_star_list_max_val(stars,
00409                                                             fors_star_get_y,
00410                                                             NULL), NULL) + tolerance;
00411         
00412     } else {
00413         region.xlo = 1;
00414         region.ylo = 1;
00415         region.xhi = 1000; /* Anything can go here, not used */
00416         region.yhi = 1000;
00417     }
00418         cpl_msg_debug(cpl_func, "Search region = (%d, %d) - (%d, %d)",
00419                       region.xlo, region.ylo,
00420                       region.xhi, region.yhi);
00421 
00422         std_ccd = fors_std_star_list_extract(
00423             cat, fors_std_star_duplicate, inside_region, &region);
00424 
00425         int multiple_entries = 0;
00426 
00427         if (fors_std_star_list_size(std_ccd) > 1) {
00428             bool found_double;
00429             do {
00430                 found_double = false;
00431                 
00432                 /* Searching for the nearest neighbour will permute the list,
00433                    do not do that while iterating the same list */
00434                 fors_std_star_list *tmp = 
00435                     fors_std_star_list_duplicate(std_ccd,
00436                                                  fors_std_star_duplicate);
00437                 
00438                 cpl_msg_debug(cpl_func, "%d stars left", fors_std_star_list_size(tmp));
00439 
00440                 fors_std_star *std;
00441                 
00442                 for (std = fors_std_star_list_first(tmp);
00443                      std != NULL && !found_double;
00444                      std = fors_std_star_list_next(tmp)) {
00445                     
00446                     fors_std_star *self = fors_std_star_list_kth_val(
00447                         std_ccd, 1,
00448                         (fors_std_star_list_func_eval)
00449                         fors_std_star_dist_arcsec,
00450                         std);
00451 
00452                     fors_std_star *nn = fors_std_star_list_kth_val(
00453                         std_ccd, 2,
00454                         (fors_std_star_list_func_eval)
00455                         fors_std_star_dist_arcsec,
00456                         std);
00457                     
00458                     double min_dist = fors_std_star_dist_arcsec(std, nn);
00459 
00460                     cpl_msg_debug(cpl_func, "dist = %f arcseconds", min_dist);
00461 
00462                     /* If very close, remove the one with the largest magnitude
00463                        error. 
00464                        
00465                        Do not try to combine the two magnitudes because
00466                        those estimates may or may not be correlated 
00467                     */
00468                     if (min_dist < 5) {
00469                         multiple_entries += 1;
00470 
00471                         if (std->dmagnitude > nn->dmagnitude) {
00472                             fors_std_star_list_remove(std_ccd, self);
00473                             fors_std_star_delete(&self);
00474                         } else {
00475                             fors_std_star_list_remove(std_ccd, nn);
00476                             fors_std_star_delete(&nn);
00477                         }
00478                         found_double = true;
00479                     }
00480                 }
00481                 
00482                 fors_std_star_list_delete(&tmp,
00483                                           fors_std_star_delete);
00484                 
00485             } while (found_double);
00486         }
00487         
00488         cpl_msg_info(cpl_func, "Found %d catalog star%s inside detector, "
00489                      "ignored %d repeated source%s",
00490                      fors_std_star_list_size(std_ccd),
00491                      fors_std_star_list_size(std_ccd) == 1 ? "" : "s",
00492                      multiple_entries,
00493                      multiple_entries == 1 ? "" : "s");
00494     }
00495 
00496     /* Select brightest std */
00497     if (fors_std_star_list_size(std_ccd) <= im->ncat) {
00498         std_ccd_bright = fors_std_star_list_duplicate(std_ccd,
00499                                                       fors_std_star_duplicate);
00500     }
00501     else {
00502         fors_std_star *kth =
00503             fors_std_star_list_kth(std_ccd,
00504                                    im->ncat + 1,
00505                                    fors_std_star_brighter_than, NULL);
00506 
00507         //fors_std_star_print_list(std_ccd);
00508         
00509         std_ccd_bright = fors_std_star_list_extract(
00510             std_ccd, fors_std_star_duplicate, 
00511             std_brighter_than, kth);
00512     }
00513 
00514     double sx_00, sy_00;
00515     
00516     if (fors_std_star_list_size(std_ccd_bright) < 3) {
00517 
00518         cpl_msg_warning(cpl_func, "Too few catalog stars (%d) available for pattern "
00519                         "matching, assuming FITS header WCS solution",
00520                         fors_std_star_list_size(std_ccd_bright));
00521 
00522         sx_00 = 0;
00523         sy_00 = 0;
00524     }
00525     else {
00526 
00527         double med_scale, med_angle;
00528 
00529         cpl_msg_info(cpl_func, "Using %d brightest standards",
00530                      fors_std_star_list_size(std_ccd_bright));
00531         
00532         fors_std_star_print_list(CPL_MSG_DEBUG, std_ccd_bright);
00533         
00534         /* Select sources */
00535         int n_sources = 
00536             (int) (fors_std_star_list_size(std_ccd_bright)*im->nsource + 0.5);
00537         
00538         if (fors_star_list_size(stars) <= n_sources) {
00539             source_bright = fors_star_list_duplicate(stars,
00540                                                      fors_star_duplicate);
00541         }
00542         else {
00543             fors_star *kth =
00544                 fors_star_list_kth(stars,
00545                                    n_sources + 1,
00546                                    fors_star_brighter_than, NULL);
00547             
00548             source_bright = fors_star_list_extract(
00549                 stars, fors_star_duplicate,
00550                 star_brighter_than, kth);
00551         }
00552         
00553         cpl_msg_info(cpl_func, "Using %d brightest sources", 
00554                      fors_star_list_size(source_bright));
00555         fors_star_print_list(CPL_MSG_DEBUG, source_bright);
00556         
00557         
00558         status = 0;
00559         match_patterns(source_bright, std_ccd_bright,
00560                        im->kappa,
00561                        &sx_00, &sy_00, &med_scale, &med_angle, &status);
00562 
00563         assure( !cpl_error_get_code(), return, "Pattern matching failed" );
00564 
00565 
00566         if (status) {
00567             cpl_msg_warning(cpl_func, 
00568                             "BAD pattern matching solution rejected.");
00569 
00570             if (med_scale > 1.1 || med_scale < 0.9) {
00571                 cpl_msg_warning(cpl_func, "Unexpected scale from pattern "
00572                 "matching (expected 1.0): assuming FITS header WCS solution");
00573             }
00574     
00575             offset = sqrt(sx_00 * sx_00 + sy_00 * sy_00);
00576     
00577             if (offset > im->max_offset) {
00578                 cpl_msg_warning(cpl_func, "Pattern matching identifications "
00579                 "are more than %.2f pixel off their expected positions (max "
00580                 "allowed was: %.2f). Pattern matching solution is rejected, "
00581                 "FITS header WCS solution is used instead!", offset, 
00582                 im->max_offset);
00583             }
00584     
00585             sx_00 = 0;
00586             sy_00 = 0;
00587         }
00588     }
00589     cpl_msg_indent_less();
00590 
00591    
00592     /* Finally, make the identification if a unique source is found 
00593        within the corrected position search radius.
00594 
00595        Use all catalog stars (inside CCD).
00596     */
00597     
00598     int number_of_ids = 0;
00599     double search_radius = im->search;
00600     bool require_unique = true;
00601 
00602     while (number_of_ids == 0 && search_radius <= im->max_search) {
00603         
00604         cpl_msg_info(cpl_func, "Identification");
00605         
00606         cpl_msg_indent_more();
00607         cpl_msg_info(cpl_func, "Average shift = (%.2f, %.2f) pixels",
00608                      sx_00,
00609                      sy_00);
00610         
00611         if (fabs(sx_00) > 10.0) {
00612             cpl_msg_warning(cpl_func, "Large x-shift");
00613         }
00614         if (fabs(sy_00) > 10.0) {
00615             cpl_msg_warning(cpl_func, "Large y-shift");
00616         }
00617         
00618         cpl_msg_info(cpl_func, "search_radius = %.2f pixels", search_radius);
00619 
00620         struct {
00621             double shift_x, shift_y;
00622             const fors_std_star *ref;
00623         } data;
00624 
00625         data.shift_x = sx_00;
00626         data.shift_y = sy_00;
00627         
00628     if (fors_star_list_size(stars) > 0) {
00629         for (data.ref = fors_std_star_list_first_const(std_ccd);
00630          data.ref != NULL;
00631          data.ref = fors_std_star_list_next_const(std_ccd)) {
00632         
00633         fors_star *nearest_1 = fors_star_list_kth(stars,
00634                               1,
00635                               star_nearer,
00636                               &data);
00637         
00638         fors_star *nearest_2 = fors_star_list_kth(stars,
00639                               2,
00640                               star_nearer,
00641                               &data);
00642         
00643         cpl_msg_debug(cpl_func, "Nearest candidates at distance %f and %f pixels",
00644                   sqrt(distsq_shift(nearest_1, data.ref, data.shift_x, data.shift_y)),
00645                   sqrt(distsq_shift(nearest_2, data.ref, data.shift_x, data.shift_y)));
00646         
00647         if (distsq_shift(nearest_1, data.ref, data.shift_x, data.shift_y) 
00648             <= 
00649             search_radius * search_radius) {
00650             
00651             if (!require_unique || 
00652             distsq_shift(nearest_2, data.ref, data.shift_x, data.shift_y)
00653             > search_radius * search_radius) {
00654             
00655             cpl_msg_debug(cpl_func, "unique source inside %f pixels",
00656                       search_radius);
00657             
00658             nearest_1->id = fors_std_star_duplicate(data.ref);
00659             number_of_ids += 1;
00660             }
00661         }
00662         }
00663     }
00664         
00665         cpl_msg_info(cpl_func, "Identified %d star%s",
00666                      number_of_ids, (number_of_ids == 1) ? "" : "s");
00667         
00668         if (number_of_ids == 0) {
00669             
00670             if (fabs(sx_00) > 0.1 && 
00671                 fabs(sy_00) > 0.1) {
00672                 
00673                 cpl_msg_warning(cpl_func,
00674                                 "No identifications made, "
00675                                 "set shift to zero and try again");
00676                 search_radius = 20;
00677                 require_unique = false;
00678                 
00679                 sx_00 = 0;
00680                 sy_00 = 0;
00681             }
00682             else {
00683                 require_unique = false;
00684 
00685                 search_radius *= 2;
00686                 
00687                 cpl_msg_warning(cpl_func,
00688                                 "No identifications made, "
00689                                 "double search radius and try again");
00690             }
00691         }
00692         
00693         cpl_msg_indent_less();
00694                 
00695     } /* while no identifications made */
00696 
00697     if (number_of_ids == 0) {
00698         cpl_msg_warning(cpl_func,
00699                         "No identifications made, max search radius reached: %f pixels",
00700                         im->max_search);
00701     }
00702     else {
00703     /* Sketch to recompute shift:
00704      *    Get median shifts (in x and y) of identified stars.
00705      *    Then do the equivalent of a single tour through the while() loop above
00706      *    
00707      *    This could perhaps be unified with the code above to avoid duplication
00708      */
00709     }
00710 
00711 
00712     cpl_msg_indent_less();
00713 
00714     cleanup;
00715     return;
00716 }
00717 
00726 static bool
00727 inside_region(const fors_std_star *std,
00728               void *reg)
00729 {
00730     const struct {
00731         int xlo, ylo;
00732         int xhi, yhi;
00733     } *region = reg;
00734 
00735     return
00736         region->xlo <= std->pixel->x && std->pixel->x <= region->xhi &&
00737         region->ylo <= std->pixel->y && std->pixel->y <= region->yhi;
00738 }
00739 
00740 
00741 
00742 #undef cleanup
00743 #define cleanup \
00744 do { \
00745     fors_point_list_delete(&std_points, fors_point_delete); \
00746     fors_point_list_delete(&source_points, fors_point_delete); \
00747     fors_pattern_list_delete(&std_patterns, fors_pattern_delete); \
00748     fors_pattern_list_delete(&source_patterns, fors_pattern_delete); \
00749     double_list_delete(&scales, double_delete); \
00750     double_list_delete(&angles, double_delete); \
00751     double_list_delete(&angle_cos, double_delete); \
00752     double_list_delete(&angle_sin, double_delete); \
00753     double_list_delete(&match_dist, double_delete); \
00754     double_list_delete(&shiftx, double_delete); \
00755     double_list_delete(&shifty, double_delete); \
00756 } while (0)
00757 
00773 static void
00774 match_patterns(const fors_star_list *stars, 
00775                const fors_std_star_list *std,
00776                double kappa,
00777                double *sx_00,
00778                double *sy_00,
00779                double *med_scale,
00780                double *med_angle,
00781                int    *status)
00782 {
00783     fors_point_list *std_points = NULL;
00784     fors_point_list *source_points = NULL;
00785 
00786     fors_pattern_list *std_patterns = NULL;
00787     fors_pattern_list *source_patterns = NULL;
00788 
00789     double_list *scales = NULL;
00790     double_list *angles = NULL;
00791     double_list *angle_cos = NULL;
00792     double_list *angle_sin = NULL;
00793     double_list *match_dist = NULL;
00794     double_list *shiftx = NULL;
00795     double_list *shifty = NULL;
00796 
00797     *status = 0;
00798 
00799     assure( sx_00 != NULL, return, NULL );
00800     assure( sy_00 != NULL, return, NULL );
00801   
00802     /* Build patterns */
00803     std_points = fors_point_list_new();
00804     {
00805         const fors_std_star *s;
00806         
00807         for (s = fors_std_star_list_first_const(std);
00808              s != NULL;
00809              s = fors_std_star_list_next_const(std)) {
00810             
00811             fors_point_list_insert(std_points,
00812                                    fors_point_new(s->pixel->x,
00813                                                   s->pixel->y));
00814         }
00815     }
00816     
00817     source_points = fors_point_list_new();
00818     {
00819         const fors_star *s;
00820         
00821         for (s = fors_star_list_first_const(stars);
00822              s != NULL;
00823              s = fors_star_list_next_const(stars)) {
00824             
00825             fors_point_list_insert(source_points,
00826                                    fors_point_new(s->pixel->x,
00827                                                   s->pixel->y));
00828         }
00829     }
00830 
00831     const double min_dist = 1.0; /* minimum distance between two 
00832                                     points in a pattern */
00833     double sigma_std = 0.0; /* No uncertainty of catalog patterns */
00834     std_patterns = 
00835         fors_pattern_new_from_points(std_points, min_dist, sigma_std);
00836     cpl_msg_info(cpl_func, "Created %d catalog patterns",
00837                  fors_pattern_list_size(std_patterns));
00838 
00839     double sigma_source;
00840     if (fors_star_list_size(stars) > 0) {
00841     sigma_source = fors_star_list_median(stars,
00842                          fors_star_extension, NULL);
00843     cpl_msg_info(cpl_func, "Average source extension = %.2f pixels", sigma_source);
00844     } else {
00845     sigma_source = -1; /* not used in this case */
00846     }
00847     source_patterns = 
00848     fors_pattern_new_from_points(source_points, min_dist, sigma_source);
00849 
00850     cpl_msg_info(cpl_func, "Created %d source patterns",
00851                  fors_pattern_list_size(source_patterns));
00852     
00853     scales = double_list_new();
00854     angles = double_list_new();
00855     angle_cos = double_list_new();
00856     angle_sin = double_list_new();
00857     match_dist = double_list_new();
00858 
00859     if ( fors_pattern_list_size(source_patterns) > 0) {
00860     /* Identify, 
00861        get average scale, orientation */
00862     fors_pattern *p;
00863         
00864     for (p = fors_pattern_list_first(std_patterns);
00865          p != NULL;
00866          p = fors_pattern_list_next(std_patterns)) {
00867         
00868         fors_pattern *nearest_source = 
00869         fors_pattern_list_min_val(source_patterns,
00870                       (fors_pattern_list_func_eval) fors_pattern_distsq,
00871                       p);
00872         
00873         double scale = fors_pattern_get_scale(p, nearest_source);
00874         double angle = fors_pattern_get_angle(p, nearest_source);
00875         double angle_c = cos(angle);
00876         double angle_s = sin(angle);
00877         double dist = sqrt(fors_pattern_distsq(p, nearest_source));
00878         double dist_per_error = fors_pattern_dist_per_error(p, nearest_source);
00879         
00880         cpl_msg_debug(cpl_func, "dist, ndist, scale, orientation = %f, %f, %f, %f",
00881               dist, dist_per_error, scale, angle * 360/(2*M_PI));
00882         
00883         /* Make identification if patterns match within error bars
00884            (defined above as sigma_source) 
00885         */
00886         if (dist_per_error < 1.0) {
00887         double_list_insert(scales   , double_duplicate(&scale));
00888         double_list_insert(angles   , double_duplicate(&angle));
00889         double_list_insert(angle_cos, double_duplicate(&angle_c));
00890         double_list_insert(angle_sin, double_duplicate(&angle_s));
00891         double_list_insert(match_dist, double_duplicate(&dist));
00892         }
00893     }
00894     }
00895     else {
00896     /* no source patterns to match */
00897     }
00898     
00899     if ( double_list_size(scales) >= 2 ) {
00900         double scale_avg   = double_list_median(scales, double_eval, NULL);
00901         double scale_stdev = double_list_mad(scales, double_eval, NULL) * STDEV_PR_MAD;
00902         
00903         cpl_msg_info(cpl_func, "Median scale = %.4f (standard deviation = %.4f)",
00904                      scale_avg, scale_stdev);
00905 
00906         *med_scale = scale_avg;
00907         
00908         if (scale_stdev > 0.2) {
00909             cpl_msg_warning(cpl_func, "Uncertain scale determination");
00910             *status = 1;
00911         }
00912         
00913         /* Represent each angle as a unit vector, compute average unit vector orientation.
00914            Compute median absolute deviation with respect to this average 
00915         */
00916         double angle_avg = atan2(double_list_mean(angle_sin, double_eval, NULL),
00917                                  double_list_mean(angle_cos, double_eval, NULL));
00918         double angle_stdev = STDEV_PR_MAD *
00919             double_list_median(angles, (double_list_func_eval) fors_angle_diff, &angle_avg);
00920         
00921         cpl_msg_info(cpl_func, "Average orientation = %.1f (standard deviation = %.1f degrees)",
00922                      angle_avg   * 360 / (2*M_PI),
00923                      angle_stdev * 360 / (2*M_PI));
00924 
00925         *med_angle = angle_avg;
00926         
00927         if (angle_avg > M_PI/4 || angle_avg < -M_PI/4) {
00928             cpl_msg_warning(cpl_func, "Expected orientation = 0 degrees");
00929             *status = 1;
00930             /* To model any orientation, we should use higher order (than zero) polynomials
00931                for the shifts */
00932         }
00933         
00934         double avg_dist   = double_list_mean(match_dist, double_eval, NULL);
00935         double false_dist = 1.0/sqrt(M_PI * fors_pattern_list_size(source_patterns));
00936         /* Average distance to nearest false match */
00937         
00938         cpl_msg_info(cpl_func, "Average match distance = %f", avg_dist);
00939         cpl_msg_info(cpl_func, "False match distance = %f", false_dist);
00940         cpl_msg_info(cpl_func, "Safety index = %.3f (should be >~ 5)", false_dist / avg_dist);
00941         if (false_dist / avg_dist < 1.5) {
00942             cpl_msg_warning(cpl_func, "Uncertain pattern matching");
00943             *status = 1;
00944         }
00945         
00946         /* Get shift from matches */
00947         shiftx = double_list_new();
00948         shifty = double_list_new();
00949         {
00950             fors_pattern *p;
00951             
00952             for (p = fors_pattern_list_first(std_patterns);
00953                  p != NULL;
00954                  p = fors_pattern_list_next(std_patterns)) {
00955                 
00956                 fors_pattern *nearest_source = 
00957                     fors_pattern_list_min_val(
00958                         source_patterns,
00959                         (fors_pattern_list_func_eval) fors_pattern_distsq,
00960                         p);
00961                 
00962                 double dist = sqrt(fors_pattern_distsq(p, nearest_source));
00963                 double dist_per_error = fors_pattern_dist_per_error(p, nearest_source);
00964                 double scale = fors_pattern_get_scale(p, nearest_source);
00965                 double angle = fors_pattern_get_angle(p, nearest_source);
00966                 
00967                 cpl_msg_debug(cpl_func, "scale, orientation, distance, norm.distance "
00968                               "= %f, %f, %f, %f",
00969                               scale, angle * 360/(2*M_PI), dist, dist_per_error);
00970                 
00971                 if (dist_per_error < 1.0 &&
00972                     fabs(scale - scale_avg)             <= kappa * scale_stdev &&
00973             fors_angle_diff(&angle, &angle_avg) <= kappa * angle_stdev) {
00974                     
00975                     /* Compute shift of the two patterns' reference stars */
00976                     double shift_x = fors_pattern_get_ref(nearest_source)->x - fors_pattern_get_ref(p)->x;
00977                     double shift_y = fors_pattern_get_ref(nearest_source)->y - fors_pattern_get_ref(p)->y;
00978                     
00979                     cpl_msg_debug(cpl_func, "Accepted, shift = (%f, %f) pixels", 
00980                                   shift_x, shift_y);
00981                     
00982                     double_list_insert(shiftx, double_duplicate(&shift_x));
00983                     double_list_insert(shifty, double_duplicate(&shift_y));
00984                 }
00985             } 
00986         }
00987 
00988     if (double_list_size(shiftx) > 0) {
00989         *sx_00 = double_list_median(shiftx, double_eval, NULL);
00990     }
00991     else {
00992         /* If this happens it is likely a bug, because
00993            we already checked that 'scales' is non-empty.
00994 
00995            But do not try to get the median of an empty list in any case,
00996            which would cause a crash
00997         */
00998         cpl_msg_warning(cpl_func, "No star identifications!");
00999             *status = 1;
01000     }
01001 
01002     if (double_list_size(shiftx) > 0) {
01003         *sy_00 = double_list_median(shifty, double_eval, NULL);
01004     }
01005     else {
01006         cpl_msg_warning(cpl_func, "No star identifications!");
01007             *status = 1;
01008     }
01009     }
01010     else {
01011         cpl_msg_warning(cpl_func,
01012                         "Too few (%d) matching patterns: assuming zero shift",
01013                         double_list_size(scales));
01014         *sx_00 = 0;
01015         *sy_00 = 0;
01016         *med_scale = 1.0;
01017         *med_angle = 0.0;
01018     }
01019 
01020     cleanup;
01021     return;
01022 }
01023 
01024 
01025 
01026     

Generated on Fri Mar 4 09:46:00 2011 for FORS Pipeline Reference Manual by  doxygen 1.4.7