FORS Pipeline Reference Manual 4.9.9
fors_identify.c
00001 /* $Id: fors_identify.c,v 1.56 2012/01/27 18:53:56 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: 2012/01/27 18:53:56 $
00024  * $Revision: 1.56 $
00025  * $Name: fors-4_9_9 $
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 /* Restore if pattern matching needs to be restored
00090 
00091     name = "ncat";
00092     full_name = cpl_sprintf("%s.%s", context, name);
00093     p = cpl_parameter_new_value(full_name,
00094                                 CPL_TYPE_INT,
00095                                 "Number of catalog stars to use in "
00096                                 "pattern matching",
00097                                 context,
00098                                 10);
00099                                          
00100     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00101     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00102     cpl_parameterlist_append(parameters, p);
00103     cpl_free((void *)full_name);
00104 
00105 
00106     name = "nsource";
00107     full_name = cpl_sprintf("%s.%s", context, name);
00108     p = cpl_parameter_new_value(full_name,
00109                                 CPL_TYPE_DOUBLE,
00110                                 "Number of sources to use in "
00111                                 "pattern matching, pr. catalog star",
00112                                 context,
00113                                 15.0);
00114                                          
00115     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00116     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00117     cpl_parameterlist_append(parameters, p);
00118     cpl_free((void *)full_name);
00119 
00120     name = "kappa";
00121     full_name = cpl_sprintf("%s.%s", context, name);
00122     p = cpl_parameter_new_value(full_name,
00123                                 CPL_TYPE_DOUBLE,
00124                                 "Rejection parameter (scale, angle) used "
00125                                 "in pattern matching ",
00126                                 context,
00127                                 5.0);
00128     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00129     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00130     cpl_parameterlist_append(parameters, p);
00131     cpl_free((void *)full_name);
00132 
00133 End of restoring if pattern matching has to be restored */
00134     
00135 /*
00136 
00137     name = "search";
00138     full_name = cpl_sprintf("%s.%s", context, name);
00139     p = cpl_parameter_new_value(full_name,
00140                                 CPL_TYPE_DOUBLE,
00141                                 "Search radius (pixels)",
00142                                 context,
00143                                 5.0);
00144     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00145     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00146     cpl_parameterlist_append(parameters, p);
00147     cpl_free((void *)full_name);
00148     
00149     name = "maxsearch";
00150     full_name = cpl_sprintf("%s.%s", context, name);
00151     p = cpl_parameter_new_value(full_name,
00152                                 CPL_TYPE_DOUBLE,
00153                                 "Maximum search radius (pixels)",
00154                                 context,
00155                                 20.0);
00156     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00157     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00158     cpl_parameterlist_append(parameters, p);
00159     cpl_free((void *)full_name);
00160 
00161 */
00162 
00163     name = "maxoffset";
00164     full_name = cpl_sprintf("%s.%s", context, name);
00165     p = cpl_parameter_new_value(full_name,
00166                                 CPL_TYPE_DOUBLE,
00167     "Maximum acceptable offset between the image and catalogue WCS (pixels)",
00168                                 context,
00169                                 150.0);
00170     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
00171     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00172     cpl_parameterlist_append(parameters, p);
00173     cpl_free((void *)full_name);
00174     
00175     return;
00176 }
00177 
00178 #undef cleanup
00179 #define cleanup \
00180 do { \
00181     cpl_free((void *)name); \
00182 } while (0)
00183 
00192 identify_method *
00193 fors_identify_method_new(const cpl_parameterlist *parameters, const char *context)
00194 {
00195     identify_method *im = cpl_malloc(sizeof(*im));
00196     const char *name = NULL;
00197 
00198     cpl_msg_info(cpl_func, "Identification parameters:");
00199        
00200 /* Restore if pattern matching to be restored
00201 
00202     cpl_msg_indent_more();
00203     name = cpl_sprintf("%s.%s", context, "ncat");
00204     im->ncat = dfs_get_parameter_int_const(parameters, name);
00205     cpl_free((void *)name); name = NULL;
00206     cpl_msg_indent_less();
00207 
00208        
00209     cpl_msg_indent_more();
00210     name = cpl_sprintf("%s.%s", context, "nsource");
00211     im->nsource = dfs_get_parameter_double_const(parameters, name);
00212     cpl_free((void *)name); name = NULL;
00213     cpl_msg_indent_less();
00214 
00215        
00216     cpl_msg_indent_more();
00217     name = cpl_sprintf("%s.%s", context, "kappa");
00218     im->kappa = dfs_get_parameter_double_const(parameters, name);
00219     cpl_free((void *)name); name = NULL;
00220     cpl_msg_indent_less();
00221 
00222 End of restore if pattern matching to be restored */
00223     
00224 
00225     // cpl_msg_indent_more();
00226     // name = cpl_sprintf("%s.%s", context, "search");
00227     im->search = 5.0; // dfs_get_parameter_double_const(parameters, name);
00228     // cpl_free((void *)name); name = NULL;
00229     // cpl_msg_indent_less();
00230     
00231    
00232     // cpl_msg_indent_more();
00233     // name = cpl_sprintf("%s.%s", context, "maxsearch");
00234     // name = cpl_sprintf("%s.%s", context, "search"); // Same value for max search
00235     im->max_search = 5.0; // dfs_get_parameter_double_const(parameters, name);
00236     // cpl_free((void *)name); name = NULL;
00237     // cpl_msg_indent_less();
00238 
00239 
00240     cpl_msg_indent_more();
00241     name = cpl_sprintf("%s.%s", context, "maxoffset");
00242     im->max_offset = dfs_get_parameter_double_const(parameters, name);
00243     cpl_free((void *)name); name = NULL;
00244     cpl_msg_indent_less();
00245     
00246 
00247     assure( !cpl_error_get_code(), return NULL, NULL );
00248     
00249     return im;
00250 }
00251 
00255 void
00256 fors_identify_method_delete(identify_method **em)
00257 {
00258     if (em && *em) {
00259         cpl_free(*em); *em = NULL;
00260     }
00261     return;
00262 }
00263 
00273 static bool
00274 std_brighter_than(const fors_std_star *s1,
00275                   void *s2)
00276 {
00277     return fors_std_star_brighter_than(s1, s2, NULL);
00278 }
00279 
00289 static bool
00290 star_brighter_than(const fors_star *s1,
00291                    void *s2)
00292 {
00293     return fors_star_brighter_than(s1, s2, NULL);
00294 }
00295 
00304 static double
00305 distsq_shift(const fors_star *s,
00306              const fors_std_star *std,
00307              double shiftx,
00308              double shifty)
00309 {
00310     fors_point *shifted_pos = fors_point_new(std->pixel->x + shiftx,
00311                                              std->pixel->y + shifty);
00312     
00313     double result = fors_point_distsq(s->pixel, shifted_pos);
00314     
00315     fors_point_delete(&shifted_pos);
00316     
00317     return result;
00318 }
00319 
00328 static bool
00329 star_nearer(const fors_star *s1,
00330             const fors_star *s2,
00331             void *data) 
00332 {
00333     struct {
00334         double shift_x, shift_y;
00335         const fors_std_star *ref;
00336     } *d = data;
00337     /* Cast is safe, see caller */
00338     
00339     return 
00340         distsq_shift(s1, d->ref, d->shift_x, d->shift_y) <
00341         distsq_shift(s2, d->ref, d->shift_x, d->shift_y);
00342 }
00343 
00344 
00345 #undef cleanup
00346 #define cleanup \
00347 do { \
00348     fors_std_star_list_delete(&std_ccd       , fors_std_star_delete); \
00349     fors_std_star_list_delete(&std_ccd_bright, fors_std_star_delete); \
00350     fors_star_list_delete(&source_bright, fors_star_delete); \
00351 } while (0)
00352 
00371 void
00372 fors_identify(fors_star_list *stars, 
00373               fors_std_star_list *cat,
00374               const identify_method *im,
00375               cpl_image **histogram)
00376 {
00377     fors_std_star_list *std_ccd = NULL;  /* Subset of catalog stars
00378                                             which are inside the CCD */
00379 
00380     fors_std_star_list *std_ccd_bright = NULL; /* Brightest std stars */
00381 
00382     fors_star_list *source_bright  = NULL;     /* Subset of brightest stars */
00383 
00384     double offset = 0.0;
00385     double sx_00, sy_00;
00386     int status;
00387 
00388     if (histogram)
00389         *histogram = NULL;
00390 
00391     assure( stars != NULL, return, NULL );
00392 
00393     cpl_msg_info(cpl_func, "Identifying sources");
00394     cpl_msg_indent_more();
00395 
00396 
00397     /*
00398       fors_star_print_list(CPL_MSG_ERROR, stars);
00399       fors_std_star_print_list(CPL_MSG_ERROR, cat); 
00400      */
00401 
00402     cpl_msg_info(cpl_func, "Pattern matching");
00403     cpl_msg_indent_more();
00404 
00405     /* Select standards inside CCD */
00406     {
00407         double tolerance = 100; /* pixels */
00408         struct {
00409             int xlo, ylo;
00410             int xhi, yhi;
00411         } region;
00412         if (fors_star_list_size(stars) > 0) {
00413             region.xlo = fors_star_get_x(fors_star_list_min_val(stars,
00414                     fors_star_get_x,
00415                     NULL), NULL) - tolerance;
00416             region.ylo = fors_star_get_y(fors_star_list_min_val(stars,
00417                     fors_star_get_y,
00418                     NULL), NULL) - tolerance;
00419             region.xhi = fors_star_get_x(fors_star_list_max_val(stars,
00420                     fors_star_get_x,
00421                     NULL), NULL) + tolerance;
00422             region.yhi = fors_star_get_y(fors_star_list_max_val(stars,
00423                     fors_star_get_y,
00424                     NULL), NULL) + tolerance;
00425 
00426         } else {
00427             region.xlo = 1;
00428             region.ylo = 1;
00429             region.xhi = 1000; /* Anything can go here, not used */
00430             region.yhi = 1000;
00431         }
00432         cpl_msg_debug(cpl_func, "Search region = (%d, %d) - (%d, %d)",
00433                       region.xlo, region.ylo,
00434                       region.xhi, region.yhi);
00435 
00436         std_ccd = fors_std_star_list_extract(
00437                 cat, fors_std_star_duplicate, inside_region, &region);
00438 
00439         int multiple_entries = 0;
00440 
00441         if (fors_std_star_list_size(std_ccd) > 1) {
00442             bool found_double;
00443             do {
00444                 found_double = false;
00445 
00446                 /* Searching for the nearest neighbour will permute the list,
00447                    do not do that while iterating the same list */
00448                 fors_std_star_list *tmp = 
00449                         fors_std_star_list_duplicate(std_ccd,
00450                                 fors_std_star_duplicate);
00451 
00452                 cpl_msg_debug(cpl_func, "%d stars left", fors_std_star_list_size(tmp));
00453 
00454                 fors_std_star *std;
00455 
00456                 for (std = fors_std_star_list_first(tmp);
00457                         std != NULL && !found_double;
00458                         std = fors_std_star_list_next(tmp)) {
00459 
00460                     fors_std_star *self = fors_std_star_list_kth_val(
00461                             std_ccd, 1,
00462                             (fors_std_star_list_func_eval)
00463                             fors_std_star_dist_arcsec,
00464                             std);
00465 
00466                     fors_std_star *nn = fors_std_star_list_kth_val(
00467                             std_ccd, 2,
00468                             (fors_std_star_list_func_eval)
00469                             fors_std_star_dist_arcsec,
00470                             std);
00471 
00472                     double min_dist = fors_std_star_dist_arcsec(std, nn);
00473 
00474                     cpl_msg_debug(cpl_func, "dist = %f arcseconds", min_dist);
00475 
00476                     /* If very close, remove the one with the largest magnitude
00477                        error. 
00478 
00479                        Do not try to combine the two magnitudes because
00480                        those estimates may or may not be correlated 
00481                      */
00482                     if (min_dist < 5) {
00483                         multiple_entries += 1;
00484 
00485                         if (std->dmagnitude > nn->dmagnitude) {
00486                             fors_std_star_list_remove(std_ccd, self);
00487                             fors_std_star_delete(&self);
00488                         } else {
00489                             fors_std_star_list_remove(std_ccd, nn);
00490                             fors_std_star_delete(&nn);
00491                         }
00492                         found_double = true;
00493                     }
00494                 }
00495 
00496                 fors_std_star_list_delete(&tmp,
00497                                           fors_std_star_delete);
00498 
00499             } while (found_double);
00500         }
00501 
00502         cpl_msg_info(cpl_func, 
00503                      "%d catalog star%s are expected inside detector, "
00504                      "ignored %d repeated source%s",
00505                      fors_std_star_list_size(std_ccd),
00506                      fors_std_star_list_size(std_ccd) == 1 ? "" : "s",
00507                      multiple_entries,
00508                      multiple_entries == 1 ? "" : "s");
00509     }
00510 
00511 
00512     if (0) {
00513         /* Select brightest std */
00514         if (fors_std_star_list_size(std_ccd) <= im->ncat) {
00515             std_ccd_bright = fors_std_star_list_duplicate(std_ccd,
00516                     fors_std_star_duplicate);
00517         }
00518         else {
00519             fors_std_star *kth =
00520                     fors_std_star_list_kth(std_ccd,
00521                             im->ncat + 1,
00522                             fors_std_star_brighter_than, NULL);
00523 
00524             //fors_std_star_print_list(std_ccd);
00525 
00526             std_ccd_bright = fors_std_star_list_extract(
00527                     std_ccd, fors_std_star_duplicate, 
00528                     std_brighter_than, kth);
00529         }
00530 
00531         if (fors_std_star_list_size(std_ccd_bright) < 3) {
00532 
00533             cpl_msg_warning(cpl_func, 
00534                     "Too few catalog stars (%d) available for pattern "
00535                     "matching, assuming FITS header WCS solution",
00536                     fors_std_star_list_size(std_ccd_bright));
00537 
00538             sx_00 = 0;
00539             sy_00 = 0;
00540         }
00541         else {
00542 
00543             double med_scale, med_angle;
00544 
00545             cpl_msg_info(cpl_func, "Using %d brightest standards",
00546                          fors_std_star_list_size(std_ccd_bright));
00547 
00548             fors_std_star_print_list(CPL_MSG_DEBUG, std_ccd_bright);
00549 
00550             /* Select sources */
00551             int n_sources = 
00552                     (int) (fors_std_star_list_size(std_ccd_bright)*im->nsource + 0.5);
00553 
00554             if (fors_star_list_size(stars) <= n_sources) {
00555                 source_bright = fors_star_list_duplicate(stars,
00556                         fors_star_duplicate);
00557             }
00558             else {
00559                 fors_star *kth =
00560                         fors_star_list_kth(stars,
00561                                 n_sources + 1,
00562                                 fors_star_brighter_than, NULL);
00563 
00564                 source_bright = fors_star_list_extract(
00565                         stars, fors_star_duplicate,
00566                         star_brighter_than, kth);
00567             }
00568 
00569             cpl_msg_info(cpl_func, "Using %d brightest sources", 
00570                          fors_star_list_size(source_bright));
00571             fors_star_print_list(CPL_MSG_DEBUG, source_bright);
00572 
00573 
00574             status = 0;
00575             match_patterns(source_bright, std_ccd_bright,
00576                            im->kappa,
00577                            &sx_00, &sy_00, &med_scale, &med_angle, &status);
00578 
00579             assure( !cpl_error_get_code(), return, "Pattern matching failed" );
00580 
00581 
00582             if (status) {
00583                 cpl_msg_warning(cpl_func, 
00584                         "BAD pattern matching solution rejected.");
00585 
00586                 if (med_scale > 1.1 || med_scale < 0.9) {
00587                     cpl_msg_warning(cpl_func, "Unexpected scale from pattern "
00588                             "matching (expected 1.0): assuming FITS header WCS solution");
00589                 }
00590 
00591                 offset = sqrt(sx_00 * sx_00 + sy_00 * sy_00);
00592 
00593                 if (offset > im->max_offset) {
00594                     cpl_msg_warning(cpl_func, "Pattern matching identifications "
00595                             "are more than %.2f pixel off their expected positions (max "
00596                             "allowed was: %.2f). Pattern matching solution is rejected, "
00597                             "FITS header WCS solution is used instead!", offset, 
00598                             im->max_offset);
00599                 }
00600 
00601                 sx_00 = 0;
00602                 sy_00 = 0;
00603             }
00604         }
00605         cpl_msg_indent_less();
00606 
00607         cpl_msg_info(cpl_func, 
00608                      "Average shift from pattern matching = (%.2f, %.2f) pixels", 
00609                      sx_00, sy_00);
00610     }
00611 
00612     /*
00613      * Begin here alternative algorithm to pattern matching
00614      */
00615 
00616     double               search_radius = im->max_offset;
00617     const fors_std_star *catalog_star;
00618     const fors_star     *ccd_star;
00619     float                dx, dy;
00620     int                  npix = (2 * search_radius + 1);
00621     cpl_image           *histo = cpl_image_new(npix, npix, CPL_TYPE_INT);
00622     int                 *dhisto = cpl_image_get_data(histo);
00623     cpl_size             xpos, ypos;
00624     int                  i, rebin, max;
00625     int                  found = 0;
00626 
00627     for (catalog_star = fors_std_star_list_first_const(std_ccd);
00628             catalog_star != NULL;
00629             catalog_star = fors_std_star_list_next_const(std_ccd)) {
00630 
00631         for (ccd_star = fors_star_list_first_const(stars);
00632                 ccd_star != NULL;
00633                 ccd_star = fors_star_list_next_const(stars)) {
00634 
00635             dx = catalog_star->pixel->x - ccd_star->pixel->x;
00636             dy = catalog_star->pixel->y - ccd_star->pixel->y;
00637 
00638             if (fabs(dx) < search_radius) {
00639                 if (fabs(dy) < search_radius) {
00640                     xpos = search_radius + floor(dx + 0.5);
00641                     ypos = search_radius + floor(dy + 0.5);
00642                     ++dhisto[xpos + ypos * npix];
00643                 }
00644             }
00645         }
00646     }
00647 
00648     max = 0;
00649     for (i = 0; i < npix * npix; i++) {
00650         if (max < dhisto[i]) {
00651             max = dhisto[i];
00652         }
00653     }
00654 
00655     if (max == 0) {
00656         cpl_msg_warning(cpl_func, 
00657                 "WCS offset determination failed.");
00658         sx_00 = 0.0;
00659         sy_00 = 0.0;
00660     }
00661     else {
00662         rebin = 0;
00663         for (i = 0; i < npix * npix; i++) {
00664             if (max == dhisto[i]) {
00665                 if (found) {
00666                     cpl_image *rebinned;
00667                     found = 2;
00668                     rebin = 1;
00669                     cpl_msg_warning(cpl_func,
00670                                     "More than one WCS offset found, try rebinning...");
00671                     rebinned = cpl_image_rebin(histo, 1, 1, 3, 3);
00672                     cpl_image_delete(histo);
00673                     histo = rebinned;
00674                     dhisto = cpl_image_get_data(histo);
00675                     npix = cpl_image_get_size_x(histo);
00676                     search_radius /= 3;
00677                     break;
00678                 }
00679                 else {
00680                     found = 1;
00681                 }
00682             }
00683         }
00684 
00685         if (found > 1) {
00686             found = max = 0;
00687             for (i = 0; i < npix * npix; i++) {
00688                 if (max < dhisto[i]) {
00689                     max = dhisto[i];
00690                 }
00691             }
00692 
00693             for (i = 0; i < npix * npix; i++) {
00694                 if (max == dhisto[i]) {
00695                     if (found) {
00696 
00697                         /*
00698                          * Check if connected to previously found maxima
00699                          */
00700 
00701                         int connected = 0;
00702 
00703                         if (i > 0) {
00704                             if (max == dhisto[i-1]) {
00705                                 connected = 1;
00706                             }
00707                         }
00708                         if (i > npix) {
00709                             if (max == dhisto[i-npix-1] || 
00710                                     max == dhisto[i-npix]   ||
00711                                     max == dhisto[i-npix+1]) {
00712                                 connected = 1;
00713                             }
00714                         }
00715 
00716                         if (!connected) {
00717                             found = 2;
00718                             cpl_msg_warning(cpl_func,
00719                                             "WCS offset determination failed.");
00720                             sx_00 = 0.0;
00721                             sy_00 = 0.0;
00722                             break;
00723                         }
00724                     }
00725                     else {
00726                         found = 1;
00727                     }
00728                 }
00729             }
00730         }
00731     }
00732 
00733     if (found == 1) {
00734         cpl_image_get_maxpos(histo, &xpos, &ypos);
00735         xpos--;
00736         ypos--;
00737 
00738         /*
00739          * Refine peak position
00740          */
00741 
00742         float xmean = 0.0;
00743         float ymean = 0.0;
00744         int   i, j, count = 0;
00745 
00746         for (i = xpos - 1; i <= xpos; i++) {
00747             for (j = ypos - 1; j <= ypos; j++) {
00748                 if (i >= 0 && i < npix) {
00749                     if (j >= 0 && j < npix) {
00750                         xmean += i * dhisto[i + j*npix];
00751                         ymean += j * dhisto[i + j*npix];
00752                         count += dhisto[i + j*npix];
00753                     }
00754                 }
00755             }
00756         }
00757 
00758         sx_00 = search_radius - xmean / count - 0.5;
00759         sy_00 = search_radius - ymean / count - 0.5;
00760 
00761         if (rebin) {
00762             sx_00 *= 3;
00763             sy_00 *= 3;
00764         }
00765 
00766         /*
00767         cpl_msg_info(cpl_func, 
00768                  "Average shift from histogram method = (%.2f, %.2f) pixels", 
00769                  sx_00, sy_00);
00770          */
00771 
00772         //        cpl_image_save(histo, "histogram.fits", CPL_BPP_32_SIGNED, NULL,
00773         //                       CPL_IO_DEFAULT);
00774         //
00775         //        cpl_image_delete(histo);
00776     }
00777 
00778     if (histogram) 
00779         *histogram = histo;
00780     else
00781         cpl_image_delete(histo);
00782 
00783     offset = sqrt(sx_00 * sx_00 + sy_00 * sy_00);
00784 
00785     if (offset > im->max_offset) {
00786         cpl_msg_warning(cpl_func, "Offset with respect to WCS is %.2f pixel "
00787                 "(max allowed was: %.2f). This offset is rejected.",
00788                 offset, im->max_offset);
00789 
00790         sx_00 = 0;
00791         sy_00 = 0;
00792     }
00793 
00794     /*
00795      * End here alternative algorithm to pattern matching
00796      */
00797 
00798     /* Finally, make the identification if a unique source is found 
00799        within the corrected position search radius.
00800 
00801        Use all catalog stars (inside CCD).
00802      */
00803 
00804     if (sx_00 == 0.0 && sy_00 == 0.0) {
00805         cpl_msg_warning(cpl_func, "No standard star could be identified.");
00806         cpl_msg_indent_less();
00807         cleanup;
00808         return;
00809     }
00810 
00811     int number_of_ids = 0;
00812     bool require_unique = true;
00813     search_radius = im->search;
00814 
00815     while (number_of_ids == 0 && search_radius <= im->max_search) {
00816 
00817         cpl_msg_info(cpl_func, "Identification");
00818 
00819         cpl_msg_indent_more();
00820         cpl_msg_info(cpl_func, "Average shift with WCS = (%.2f, %.2f) pixels",
00821                      sx_00,
00822                      sy_00);
00823 
00824         if (fabs(sx_00) > 10.0) {
00825             cpl_msg_warning(cpl_func, "Large x-shift");
00826         }
00827         if (fabs(sy_00) > 10.0) {
00828             cpl_msg_warning(cpl_func, "Large y-shift");
00829         }
00830 
00831         cpl_msg_info(cpl_func, "search_radius = %.2f pixels", search_radius);
00832 
00833         struct {
00834             double shift_x, shift_y;
00835             const fors_std_star *ref;
00836         } data;
00837 
00838         data.shift_x = sx_00;
00839         data.shift_y = sy_00;
00840 
00841         if (fors_star_list_size(stars) > 0) {
00842             for (data.ref = fors_std_star_list_first_const(std_ccd);
00843                     data.ref != NULL;
00844                     data.ref = fors_std_star_list_next_const(std_ccd)) {
00845 
00846                 fors_star *nearest_1 = fors_star_list_kth(stars,
00847                         1,
00848                         star_nearer,
00849                         &data);
00850 
00851                 if (fors_star_list_size(stars) > 1) {
00852 
00853                     fors_star *nearest_2 = fors_star_list_kth(stars,
00854                             2,
00855                             star_nearer,
00856                             &data);
00857 
00858                     cpl_msg_debug(cpl_func, "Nearest candidates at "
00859                                   "distance %f and %f pixels",
00860                                   sqrt(distsq_shift(nearest_1, data.ref, 
00861                                           data.shift_x, 
00862                                           data.shift_y)),
00863                                           sqrt(distsq_shift(nearest_2, data.ref, 
00864                                                   data.shift_x, 
00865                                                   data.shift_y)));
00866 
00867                     if (distsq_shift(nearest_1, data.ref, 
00868                             data.shift_x, data.shift_y) <= 
00869                             search_radius * search_radius) {
00870 
00871                         if (!require_unique || 
00872                                 distsq_shift(nearest_2, data.ref, 
00873                                         data.shift_x, data.shift_y) > 
00874                         search_radius * search_radius) {
00875 
00876                             cpl_msg_debug(cpl_func, 
00877                                     "unique source inside %f pixels",
00878                                     search_radius);
00879 
00880                             nearest_1->id = fors_std_star_duplicate(data.ref);
00881                             number_of_ids += 1;
00882                         }
00883                     }
00884                 }
00885                 else {
00886                     cpl_msg_debug(cpl_func, "Nearest candidate at "
00887                             "distance %f pixels",
00888                             sqrt(distsq_shift(nearest_1, data.ref, 
00889                                     data.shift_x, 
00890                                     data.shift_y)));
00891                     if (distsq_shift(nearest_1, data.ref, 
00892                             data.shift_x, data.shift_y) <=
00893                             search_radius * search_radius) {
00894 
00895                         cpl_msg_debug(cpl_func, 
00896                                 "unique source inside %f pixels",
00897                                 search_radius);
00898 
00899                         nearest_1->id = fors_std_star_duplicate(data.ref);
00900                         number_of_ids += 1;
00901                     }
00902                 }
00903             }
00904         }
00905 
00906         cpl_msg_info(cpl_func, "Identified %d star%s",
00907                      number_of_ids, (number_of_ids == 1) ? "" : "s");
00908 
00909         if (number_of_ids == 0) {
00910 
00911             if (fabs(sx_00) > 0.1 && 
00912                     fabs(sy_00) > 0.1) {
00913 
00914                 cpl_msg_debug(cpl_func,
00915                         "No identifications made, "
00916                         "set shift to zero and try again");
00917                 search_radius = 20;
00918                 require_unique = false;
00919 
00920                 sx_00 = 0;
00921                 sy_00 = 0;
00922             }
00923             else {
00924                 require_unique = false;
00925 
00926                 search_radius *= 2;
00927 
00928                 cpl_msg_debug(cpl_func,
00929                               "No identifications made, "
00930                               "double search radius and try again");
00931             }
00932         }
00933 
00934         cpl_msg_indent_less();
00935 
00936     } /* while no identifications made */
00937 
00938     if (number_of_ids == 0) {
00939         cpl_msg_warning(cpl_func,
00940                 "No identifications made, "
00941                 "within search radius %f pixels",
00942                 im->max_search);
00943         /* When maxsearchradius was a parameter
00944 
00945         cpl_msg_warning(cpl_func,
00946                         "No identifications made, "
00947                         "max search radius reached: %f pixels",
00948                         im->max_search);
00949          */
00950     }
00951     else {
00952         /* Sketch to recompute shift:
00953          *    Get median shifts (in x and y) of identified stars.
00954          *    Then do the equivalent of a single tour through the while() loop above
00955          *    
00956          *    This could perhaps be unified with the code above to avoid duplication
00957          */
00958     }
00959 
00960 
00961     cpl_msg_indent_less();
00962 
00963     cleanup;
00964     return;
00965 }
00966 
00975 static bool
00976 inside_region(const fors_std_star *std,
00977               void *reg)
00978 {
00979     const struct {
00980         int xlo, ylo;
00981         int xhi, yhi;
00982     } *region = reg;
00983 
00984     return
00985         region->xlo <= std->pixel->x && std->pixel->x <= region->xhi &&
00986         region->ylo <= std->pixel->y && std->pixel->y <= region->yhi;
00987 }
00988 
00989 
00990 
00991 #undef cleanup
00992 #define cleanup \
00993 do { \
00994     fors_point_list_delete(&std_points, fors_point_delete); \
00995     fors_point_list_delete(&source_points, fors_point_delete); \
00996     fors_pattern_list_delete(&std_patterns, fors_pattern_delete); \
00997     fors_pattern_list_delete(&source_patterns, fors_pattern_delete); \
00998     double_list_delete(&scales, double_delete); \
00999     double_list_delete(&angles, double_delete); \
01000     double_list_delete(&angle_cos, double_delete); \
01001     double_list_delete(&angle_sin, double_delete); \
01002     double_list_delete(&match_dist, double_delete); \
01003     double_list_delete(&shiftx, double_delete); \
01004     double_list_delete(&shifty, double_delete); \
01005 } while (0)
01006 
01022 static void
01023 match_patterns(const fors_star_list *stars, 
01024                const fors_std_star_list *std,
01025                double kappa,
01026                double *sx_00,
01027                double *sy_00,
01028                double *med_scale,
01029                double *med_angle,
01030                int    *status)
01031 {
01032     fors_point_list *std_points = NULL;
01033     fors_point_list *source_points = NULL;
01034 
01035     fors_pattern_list *std_patterns = NULL;
01036     fors_pattern_list *source_patterns = NULL;
01037 
01038     double_list *scales = NULL;
01039     double_list *angles = NULL;
01040     double_list *angle_cos = NULL;
01041     double_list *angle_sin = NULL;
01042     double_list *match_dist = NULL;
01043     double_list *shiftx = NULL;
01044     double_list *shifty = NULL;
01045 
01046     *status = 0;
01047 
01048     assure( sx_00 != NULL, return, NULL );
01049     assure( sy_00 != NULL, return, NULL );
01050   
01051     /* Build patterns */
01052     std_points = fors_point_list_new();
01053     {
01054         const fors_std_star *s;
01055         
01056         for (s = fors_std_star_list_first_const(std);
01057              s != NULL;
01058              s = fors_std_star_list_next_const(std)) {
01059             
01060             fors_point_list_insert(std_points,
01061                                    fors_point_new(s->pixel->x,
01062                                                   s->pixel->y));
01063         }
01064     }
01065     
01066     source_points = fors_point_list_new();
01067     {
01068         const fors_star *s;
01069         
01070         for (s = fors_star_list_first_const(stars);
01071              s != NULL;
01072              s = fors_star_list_next_const(stars)) {
01073             
01074             fors_point_list_insert(source_points,
01075                                    fors_point_new(s->pixel->x,
01076                                                   s->pixel->y));
01077         }
01078     }
01079 
01080     const double min_dist = 1.0; /* minimum distance between two 
01081                                     points in a pattern */
01082     double sigma_std = 0.0; /* No uncertainty of catalog patterns */
01083     std_patterns = 
01084         fors_pattern_new_from_points(std_points, min_dist, sigma_std);
01085     cpl_msg_info(cpl_func, "Created %d catalog patterns",
01086                  fors_pattern_list_size(std_patterns));
01087 
01088     double sigma_source;
01089     if (fors_star_list_size(stars) > 0) {
01090     sigma_source = fors_star_list_median(stars,
01091                          fors_star_extension, NULL);
01092     cpl_msg_info(cpl_func, "Average source extension = %.2f pixels", sigma_source);
01093     } else {
01094     sigma_source = -1; /* not used in this case */
01095     }
01096     source_patterns = 
01097     fors_pattern_new_from_points(source_points, min_dist, sigma_source);
01098 
01099     cpl_msg_info(cpl_func, "Created %d source patterns",
01100                  fors_pattern_list_size(source_patterns));
01101     
01102     scales = double_list_new();
01103     angles = double_list_new();
01104     angle_cos = double_list_new();
01105     angle_sin = double_list_new();
01106     match_dist = double_list_new();
01107 
01108     if ( fors_pattern_list_size(source_patterns) > 0) {
01109     /* Identify, 
01110        get average scale, orientation */
01111     fors_pattern *p;
01112         
01113     for (p = fors_pattern_list_first(std_patterns);
01114          p != NULL;
01115          p = fors_pattern_list_next(std_patterns)) {
01116         
01117         fors_pattern *nearest_source = 
01118         fors_pattern_list_min_val(source_patterns,
01119                       (fors_pattern_list_func_eval) fors_pattern_distsq,
01120                       p);
01121         
01122         double scale = fors_pattern_get_scale(p, nearest_source);
01123         double angle = fors_pattern_get_angle(p, nearest_source);
01124         double angle_c = cos(angle);
01125         double angle_s = sin(angle);
01126         double dist = sqrt(fors_pattern_distsq(p, nearest_source));
01127         double dist_per_error = fors_pattern_dist_per_error(p, nearest_source);
01128         
01129         cpl_msg_debug(cpl_func, "dist, ndist, scale, orientation = %f, %f, %f, %f",
01130               dist, dist_per_error, scale, angle * 360/(2*M_PI));
01131         
01132         /* Make identification if patterns match within error bars
01133            (defined above as sigma_source) 
01134         */
01135         if (dist_per_error < 1.0) {
01136         double_list_insert(scales   , double_duplicate(&scale));
01137         double_list_insert(angles   , double_duplicate(&angle));
01138         double_list_insert(angle_cos, double_duplicate(&angle_c));
01139         double_list_insert(angle_sin, double_duplicate(&angle_s));
01140         double_list_insert(match_dist, double_duplicate(&dist));
01141         }
01142     }
01143     }
01144     else {
01145     /* no source patterns to match */
01146     }
01147     
01148     if ( double_list_size(scales) >= 2 ) {
01149         double scale_avg   = double_list_median(scales, double_eval, NULL);
01150         double scale_stdev = double_list_mad(scales, double_eval, NULL) * STDEV_PR_MAD;
01151         
01152         cpl_msg_info(cpl_func, "Median scale = %.4f +- %.4f",
01153                      scale_avg, scale_stdev);
01154 
01155         *med_scale = scale_avg;
01156         
01157         if (scale_stdev > 0.2) {
01158             cpl_msg_warning(cpl_func, "Uncertain scale determination");
01159             *status = 1;
01160         }
01161         
01162         /* 
01163          * Represent each angle as a unit vector, compute average 
01164          * unit vector orientation.
01165          * Compute median absolute deviation with respect to this average 
01166          */
01167         double angle_avg = atan2(double_list_mean(angle_sin, double_eval, NULL),
01168                                  double_list_mean(angle_cos, double_eval, NULL));
01169         double angle_stdev = STDEV_PR_MAD *
01170             double_list_median(angles, (double_list_func_eval) fors_angle_diff, &angle_avg);
01171         
01172         cpl_msg_info(cpl_func, "Average orientation = %.1f +- %.1f degrees",
01173                      angle_avg   * 360 / (2*M_PI),
01174                      angle_stdev * 360 / (2*M_PI));
01175 
01176         *med_angle = angle_avg;
01177         
01178         if (angle_avg > M_PI/4 || angle_avg < -M_PI/4) {
01179             cpl_msg_warning(cpl_func, "Expected orientation = 0 degrees");
01180             *status = 1;
01181             /* To model any orientation, we should use higher order (than zero) polynomials
01182                for the shifts */
01183         }
01184         
01185         double avg_dist   = double_list_mean(match_dist, double_eval, NULL);
01186         double false_dist = 1.0/sqrt(M_PI * fors_pattern_list_size(source_patterns));
01187         /* Average distance to nearest false match */
01188         
01189         cpl_msg_info(cpl_func, "Average match distance = %f pixel", avg_dist);
01190         cpl_msg_info(cpl_func, "False match distance = %f pixel", false_dist);
01191         cpl_msg_info(cpl_func, "Safety index = %.3f (should be >~ 5)", 
01192                      false_dist / avg_dist);
01193         if (false_dist / avg_dist < 1.5) {
01194             cpl_msg_warning(cpl_func, "Uncertain pattern matching");
01195             *status = 1;
01196         }
01197         
01198         /* Get shift from matches */
01199         shiftx = double_list_new();
01200         shifty = double_list_new();
01201         {
01202             fors_pattern *p;
01203             
01204             for (p = fors_pattern_list_first(std_patterns);
01205                  p != NULL;
01206                  p = fors_pattern_list_next(std_patterns)) {
01207                 
01208                 fors_pattern *nearest_source = 
01209                     fors_pattern_list_min_val(
01210                         source_patterns,
01211                         (fors_pattern_list_func_eval) fors_pattern_distsq,
01212                         p);
01213                 
01214                 double dist = sqrt(fors_pattern_distsq(p, nearest_source));
01215                 double dist_per_error = fors_pattern_dist_per_error(p, nearest_source);
01216                 double scale = fors_pattern_get_scale(p, nearest_source);
01217                 double angle = fors_pattern_get_angle(p, nearest_source);
01218                 
01219                 cpl_msg_debug(cpl_func, "scale, orientation, distance, norm.distance "
01220                               "= %f, %f, %f, %f",
01221                               scale, angle * 360/(2*M_PI), dist, dist_per_error);
01222                 
01223                 if (dist_per_error < 1.0 &&
01224                     fabs(scale - scale_avg)             <= kappa * scale_stdev &&
01225             fors_angle_diff(&angle, &angle_avg) <= kappa * angle_stdev) {
01226                     
01227                     /* Compute shift of the two patterns' reference stars */
01228                     double shift_x = fors_pattern_get_ref(nearest_source)->x - fors_pattern_get_ref(p)->x;
01229                     double shift_y = fors_pattern_get_ref(nearest_source)->y - fors_pattern_get_ref(p)->y;
01230                     
01231                     cpl_msg_debug(cpl_func, "Accepted, shift = (%f, %f) pixels", 
01232                                   shift_x, shift_y);
01233                     
01234                     double_list_insert(shiftx, double_duplicate(&shift_x));
01235                     double_list_insert(shifty, double_duplicate(&shift_y));
01236                 }
01237             } 
01238         }
01239 
01240     if (double_list_size(shiftx) > 0) {
01241         *sx_00 = double_list_median(shiftx, double_eval, NULL);
01242     }
01243     else {
01244         /* If this happens it is likely a bug, because
01245            we already checked that 'scales' is non-empty.
01246 
01247            But do not try to get the median of an empty list in any case,
01248            which would cause a crash
01249         */
01250         cpl_msg_warning(cpl_func, "No standardstar identifications!");
01251             *status = 1;
01252     }
01253 
01254     if (double_list_size(shiftx) > 0) {
01255         *sy_00 = double_list_median(shifty, double_eval, NULL);
01256     }
01257     else {
01258         cpl_msg_warning(cpl_func, "No standardstar identifications!");
01259             *status = 1;
01260     }
01261     }
01262     else {
01263         cpl_msg_warning(cpl_func,
01264                         "Too few (%d) matching patterns: assuming zero shift",
01265                         double_list_size(scales));
01266         *sx_00 = 0;
01267         *sy_00 = 0;
01268         *med_scale = 1.0;
01269         *med_angle = 0.0;
01270     }
01271 
01272     cleanup;
01273     return;
01274 }
01275 
01276 
01277 
01278