00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
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;
00366
00367
00368 fors_std_star_list *std_ccd_bright = NULL;
00369
00370 fors_star_list *source_bright = NULL;
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
00384
00385
00386
00387
00388 cpl_msg_info(cpl_func, "Pattern matching");
00389 cpl_msg_indent_more();
00390
00391
00392 {
00393 double tolerance = 100;
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;
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, ®ion);
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
00433
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
00463
00464
00465
00466
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
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
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
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
00593
00594
00595
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 }
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
00704
00705
00706
00707
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
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;
00832
00833 double sigma_std = 0.0;
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;
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
00861
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
00884
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
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
00914
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
00931
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
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
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
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
00993
00994
00995
00996
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