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
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
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
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227 im->search = 5.0;
00228
00229
00230
00231
00232
00233
00234
00235 im->max_search = 5.0;
00236
00237
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
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;
00378
00379
00380 fors_std_star_list *std_ccd_bright = NULL;
00381
00382 fors_star_list *source_bright = NULL;
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
00399
00400
00401
00402 cpl_msg_info(cpl_func, "Pattern matching");
00403 cpl_msg_indent_more();
00404
00405
00406 {
00407 double tolerance = 100;
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;
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, ®ion);
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
00447
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
00477
00478
00479
00480
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
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
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
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
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
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
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
00768
00769
00770
00771
00772
00773
00774
00775
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
00796
00797
00798
00799
00800
00801
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 }
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
00944
00945
00946
00947
00948
00949
00950 }
00951 else {
00952
00953
00954
00955
00956
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
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;
01081
01082 double sigma_std = 0.0;
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;
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
01110
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
01133
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
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
01164
01165
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
01182
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
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
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
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
01245
01246
01247
01248
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