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
00033
00034
00035
00036 #include <string.h>
00037 #include <assert.h>
00038 #include <math.h>
00039 #include <float.h>
00040
00041 #include <cpl.h>
00042
00043 #include "irplib_utils.h"
00044 #include "irplib_strehl.h"
00045
00046
00050
00051
00052
00053
00054
00055
00056 #define IRPLIB_PI 3.1415926535897932384626433
00057
00058
00059 #define STREHL_ERROR_COEFFICIENT (IRPLIB_PI * 0.007 / 0.0271)
00060
00061 #ifndef IRPLIB_STREHL_RAD_CENTRAL
00062 #define IRPLIB_STREHL_RAD_CENTRAL 5
00063 #endif
00064
00065 #define IRPLIB_DISK_BG_MIN_PIX_NB 30
00066 #define IRPLIB_DISK_BG_REJ_LOW 0.1
00067 #define IRPLIB_DISK_BG_REJ_HIGH 0.1
00068
00069
00070
00071
00072
00073 static cpl_image * irplib_strehl_generate_otf(double, double, double, double,
00074 int, double);
00075 static double PSF_H1(double, double, double);
00076 static double PSF_H2(double, double);
00077 static double PSF_G(double, double);
00078 static double PSF_sinc(double);
00079 static double PSF_TelOTF(double, double);
00080
00081
00082
00083
00084
00087
00116
00117 cpl_error_code irplib_strehl_compute(const cpl_image * im,
00118 double m1,
00119 double m2,
00120 double lam,
00121 double dlam,
00122 double pscale,
00123 int size,
00124 double xpos,
00125 double ypos,
00126 double r1,
00127 double r2,
00128 double r3,
00129 int noise_box_sz,
00130 int noise_nsamples,
00131 double * strehl,
00132 double * strehl_err,
00133 double * star_bg,
00134 double * star_peak,
00135 double * star_flux,
00136 double * psf_peak,
00137 double * psf_flux,
00138 double * bg_noise)
00139 {
00140 cpl_image * psf;
00141 double star_radius, max_radius;
00142
00143 const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
00144 int ring[4];
00145
00146
00147 assert(window_size > 0.0);
00148 assert(STREHL_ERROR_COEFFICIENT > 0.0);
00149
00150
00151 cpl_ensure_code(im != NULL, CPL_ERROR_NULL_INPUT);
00152 cpl_ensure_code(strehl != NULL, CPL_ERROR_NULL_INPUT);
00153 cpl_ensure_code(strehl_err != NULL, CPL_ERROR_NULL_INPUT);
00154 cpl_ensure_code(star_bg != NULL, CPL_ERROR_NULL_INPUT);
00155 cpl_ensure_code(star_peak != NULL, CPL_ERROR_NULL_INPUT);
00156 cpl_ensure_code(star_flux != NULL, CPL_ERROR_NULL_INPUT);
00157 cpl_ensure_code(psf_peak != NULL, CPL_ERROR_NULL_INPUT);
00158 cpl_ensure_code(psf_flux != NULL, CPL_ERROR_NULL_INPUT);
00159
00160 cpl_ensure_code(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00161
00162 cpl_ensure_code(r1 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00163 cpl_ensure_code(r2 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00164 cpl_ensure_code(r3 > r2, CPL_ERROR_ILLEGAL_INPUT);
00165
00166
00167
00168
00169
00170 psf = irplib_strehl_generate_psf(m1, m2, lam, dlam, pscale, size);
00171 cpl_ensure_code(psf != NULL, CPL_ERROR_ILLEGAL_OUTPUT);
00172
00173
00174 *psf_peak = cpl_image_get_max(psf);
00175 cpl_image_delete(psf);
00176
00177 assert( *psf_peak > 0.0);
00178 *psf_flux = 1.0;
00179
00180
00181 *star_bg = irplib_strehl_ring_background(im, xpos, ypos, r2/pscale, r3/pscale,
00182 IRPLIB_BG_METHOD_AVER_REJ);
00183
00184
00185 star_radius = r1/pscale;
00186
00187
00188 *star_flux = irplib_strehl_disk_flux(im, xpos, ypos, star_radius, *star_bg);
00189
00190 cpl_ensure_code(*star_flux > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
00191
00192
00193 max_radius = window_size < star_radius ? window_size : star_radius;
00194 cpl_ensure_code(!irplib_strehl_disk_max(im, xpos, ypos, max_radius,
00195 star_peak), cpl_error_get_code());
00196 *star_peak -= *star_bg;
00197
00198 cpl_ensure_code(*star_peak > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
00199
00200
00201
00202 *strehl = (*star_peak * *psf_flux ) / ( *star_flux * *psf_peak);
00203
00204 if (*strehl > 1)
00205 cpl_msg_warning(cpl_func, "Extreme Strehl-ratio=%g, star_peak=%g, "
00206 "star_flux=%g, psf_peak=%g, psf_flux=%g", *strehl,
00207 *star_peak, *star_flux, *psf_peak, *psf_flux);
00208
00209
00210
00211
00212 ring[0] = (int)(0.5 + xpos);
00213 ring[1] = (int)(0.5 + ypos);
00214 ring[2] = (int)(0.5 + r2/pscale);
00215 ring[3] = (int)(0.5 + r3/pscale);
00216
00217 cpl_ensure_code(!cpl_flux_get_noise_ring(im, ring, noise_box_sz,
00218 noise_nsamples, bg_noise, NULL),
00219 cpl_error_get_code());
00220
00221 *strehl_err = STREHL_ERROR_COEFFICIENT * (*bg_noise) * pscale *
00222 star_radius * star_radius / *star_flux;
00223
00224
00225 cpl_ensure_code(*strehl_err >= 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
00226
00227 return CPL_ERROR_NONE;
00228 }
00229
00230
00246
00247 double irplib_strehl_disk_flux(const cpl_image * im,
00248 double xpos,
00249 double ypos,
00250 double rad,
00251 double bg)
00252 {
00253 const double sqr = rad * rad;
00254 double sqrest;
00255 const float * pim;
00256 double flux = 0.0;
00257 double yj, xi;
00258 int nx, ny;
00259 int lx, ly, ux, uy;
00260 int i, j;
00261
00262
00263
00264 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00265 cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
00266 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
00267 cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00268
00269 nx = cpl_image_get_size_x(im);
00270 ny = cpl_image_get_size_y(im);
00271
00272
00273 lx = (int)(xpos - rad);
00274 ly = (int)(ypos - rad);
00275 if (lx < 0) lx = 0;
00276 if (ly < 0) ly = 0;
00277
00278
00279 ux = (int)(xpos + rad) + 1;
00280 uy = (int)(ypos + rad) + 1;
00281 if (ux > (nx-1)) ux = nx-1;
00282 if (uy > (ny-1)) uy = ny-1;
00283
00284
00285 pim = cpl_image_get_data_float((cpl_image*)im);
00286 for (j=ly ; j<uy ; j++) {
00287 yj = (double)j - ypos;
00288 sqrest = sqr - yj * yj;
00289 for (i=lx; i<ux ; i++) {
00290 xi = (double)i - xpos;
00291 if (sqrest >= xi * xi && irplib_isnan(pim[i+j*nx]) == 0) {
00292 flux += (double)pim[i+j*nx] - bg;
00293 }
00294 }
00295 }
00296 return flux;
00297 }
00298
00299
00313
00314 double irplib_strehl_ring_background(const cpl_image * im,
00315 double xpos,
00316 double ypos,
00317 double rad_int,
00318 double rad_ext,
00319 irplib_strehl_bg_method mode)
00320 {
00321 int npix;
00322 const double sqr_int = rad_int * rad_int;
00323 const double sqr_ext = rad_ext * rad_ext;
00324 double dist;
00325 cpl_vector * pix_arr;
00326 const float * pim;
00327 double flux = 0.0;
00328 double yj, xi;
00329 int lx, ly, ux, uy;
00330 int nx, ny;
00331 int i, j;
00332
00333
00334 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00335 cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
00336 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
00337 cpl_ensure(rad_int > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00338 cpl_ensure(rad_ext > rad_int, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00339
00340 cpl_ensure(mode == IRPLIB_BG_METHOD_AVER_REJ ||
00341 mode == IRPLIB_BG_METHOD_MEDIAN,
00342 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
00343
00344 nx = cpl_image_get_size_x(im);
00345 ny = cpl_image_get_size_y(im);
00346
00347
00348 lx = (int)(xpos - rad_ext);
00349 ly = (int)(ypos - rad_ext);
00350 if (lx < 0) lx = 0;
00351 if (ly < 0) ly = 0;
00352
00353
00354 ux = (int)(xpos + rad_ext) + 1;
00355 uy = (int)(ypos + rad_ext) + 1;
00356 if (ux > (nx-1)) ux = nx-1;
00357 if (uy > (ny-1)) uy = ny-1;
00358
00359 npix = (ux - lx + 1) * (uy - ly + 1);
00360 cpl_ensure(npix >= IRPLIB_DISK_BG_MIN_PIX_NB, CPL_ERROR_DATA_NOT_FOUND, 0.0);
00361
00362
00363 pix_arr = cpl_vector_new(npix);
00364
00365
00366
00367
00368 pim = cpl_image_get_data_float((cpl_image*)im);
00369 npix = 0;
00370 for (j=ly ; j<uy ; j++) {
00371 yj = (double)j - ypos;
00372 for (i=lx ; i<ux; i++) {
00373 xi = (double)i - xpos;
00374 dist = yj * yj + xi * xi;
00375 if (sqr_int <= dist && dist <= sqr_ext &&
00376 irplib_isnan(pim[i+j*nx]) == 0) {
00377 cpl_vector_set(pix_arr, npix, (double)pim[i+j*nx]);
00378 npix++;
00379 }
00380 }
00381 }
00382
00383 if (npix < IRPLIB_DISK_BG_MIN_PIX_NB) {
00384 cpl_vector_delete(pix_arr);
00385 cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, 0.0);
00386 }
00387
00388
00389
00390
00391 cpl_vector_set_size(pix_arr, npix);
00392
00393 if (mode == IRPLIB_BG_METHOD_AVER_REJ) {
00394 const int low_ind = (int)((double)npix * IRPLIB_DISK_BG_REJ_LOW);
00395 const int high_ind = (int)((double)npix
00396 * (1.0 - IRPLIB_DISK_BG_REJ_HIGH));
00397
00398
00399 cpl_vector_sort(pix_arr, 1);
00400
00401 for (i=low_ind ; i<high_ind ; i++) {
00402 flux += cpl_vector_get(pix_arr, i);
00403 }
00404 if (high_ind - low_ind > 1) flux /= (double)(high_ind - low_ind);
00405 } else {
00406 flux = cpl_vector_get_median(pix_arr);
00407 }
00408
00409 cpl_vector_delete(pix_arr);
00410
00411 return flux;
00412 }
00413
00414
00434
00435 cpl_image * irplib_strehl_generate_psf(
00436 double m1,
00437 double m2,
00438 double lam,
00439 double dlam,
00440 double pscale,
00441 int size)
00442 {
00443 cpl_image * otf_image = irplib_strehl_generate_otf(m1, m2, lam, dlam,
00444 size, pscale);
00445
00446 if (otf_image == NULL) return NULL;
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457 if (cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
00458
00459
00460 cpl_image_abs(otf_image) ||
00461
00462
00463 cpl_image_normalise(otf_image, CPL_NORM_FLUX)) {
00464
00465 cpl_image_delete(otf_image);
00466 return NULL;
00467 }
00468
00469 return otf_image;
00470 }
00471
00474
00490
00491 static cpl_image * irplib_strehl_generate_otf(
00492 double m1,
00493 double m2,
00494 double lam,
00495 double dlam,
00496 int size,
00497 double pscale)
00498 {
00499 cpl_image * otf_image;
00500 double * otf_data;
00501 double obs_ratio ;
00502 double f_max ;
00503 int pix0 ;
00504 double a, x, y;
00505 double f, rsq, fc, invfc, lambda;
00506 double sincy;
00507 double invsize;
00508 register int pos;
00509 int i, j, k;
00510
00511
00512 cpl_ensure(m2 > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00513 cpl_ensure(m1 > m2, CPL_ERROR_ILLEGAL_INPUT, NULL);
00514 cpl_ensure(lam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00515 cpl_ensure(dlam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00516 cpl_ensure(size > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00517 cpl_ensure(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00518
00519
00520 pscale /= (double)206265;
00521 lam /= (double)1.0e6;
00522 dlam /= (double)1.0e6;
00523
00524
00525 obs_ratio = m2 / m1;
00526
00527
00528 pix0 = size/2;
00529 invsize = (double)1.0 / (double)size;
00530
00531
00532 f_max = m1 * pscale * (double)size / lam;
00533
00534
00535 otf_image = cpl_image_new(size, size, CPL_TYPE_DOUBLE);
00536 if (otf_image==NULL) return NULL;
00537 otf_data = cpl_image_get_data_double(otf_image);
00538
00539
00540
00541
00542 for (k=1 ; k<=9 ; k++) {
00543
00544 lambda = (double)(lam - dlam*(double)(k-5)/8.0);
00545 fc = (double)f_max * (double)lam / lambda;
00546 invfc = 1.0 / fc;
00547
00548
00549 pos = 0;
00550 for (j=0 ; j<size ; j++) {
00551 y = (double)(j-pix0);
00552 sincy = PSF_sinc(IRPLIB_PI * y * invsize);
00553 for (i=0 ; i<size ; i++) {
00554 x = (double)(i-pix0);
00555 rsq = x*x + y*y;
00556 if (rsq < fc*fc) {
00557 if (rsq < 0.01)
00558 a = 1.0;
00559 else {
00560 f = sqrt(rsq) * invfc;
00561 a = PSF_TelOTF(f,obs_ratio) *
00562 PSF_sinc(IRPLIB_PI * x * invsize) * sincy;
00563 }
00564 } else {
00565 a = 0.0;
00566 }
00567 otf_data[pos++] += a / 9.0;
00568 }
00569 }
00570 }
00571 return otf_image;
00572 }
00573
00574
00575
00576
00577 static double PSF_H1(
00578 double f,
00579 double u,
00580 double v)
00581 {
00582 const double e = fabs(1.0-v) > 0.0 ? -1.0 : 1.0;
00583
00584 return((v*v/IRPLIB_PI)*acos((f/v)*(1.0+e*(1.0-u*u)/(4.0*f*f))));
00585 }
00586
00587
00588
00589
00590 static double PSF_H2(double f,
00591 double u)
00592 {
00593 const double tmp1 = (2.0 * f) / (1.0 + u);
00594 const double tmp2 = (1.0 - u) / (2.0 * f);
00595
00596 return -1.0 * (f/IRPLIB_PI) * (1.0+u)
00597 * sqrt((1.0-tmp1*tmp1)*(1.0-tmp2*tmp2));
00598 }
00599
00600
00601
00602
00603 static double PSF_G(double f,
00604 double u)
00605 {
00606 if (f <= (1.0-u)/2.0) return(u*u);
00607 if (f >= (1.0+u)/2.0) return(0.0);
00608 else return(PSF_H1(f,u,1.0) + PSF_H1(f,u,u) + PSF_H2(f,u));
00609 }
00610
00611
00612
00613
00614 static double PSF_sinc(double x)
00615 {
00616 return fabs(x) > fabs(sin(x)) ? sin(x)/x : 1.0;
00617 }
00618
00619
00620
00621
00622 static double PSF_TelOTF(double f,
00623 double u)
00624 {
00625 return((PSF_G(f,1.0)+u*u*PSF_G(f/u,1.0)-2.0*PSF_G(f,u))/(1.0-u*u));
00626 }
00627
00628
00640
00641 cpl_error_code irplib_strehl_disk_max(const cpl_image * self,
00642 double xpos,
00643 double ypos,
00644 double radius,
00645 double * ppeak)
00646 {
00647
00648 const double sqr = radius * radius;
00649 double sqrest;
00650 const float * pself;
00651 float peak = FLT_MAX;
00652 double yj, xi;
00653 int nx, ny;
00654 int lx, ly, ux, uy;
00655 int i, j;
00656 cpl_boolean first = CPL_TRUE;
00657
00658
00659
00660 cpl_ensure_code(ppeak != NULL, CPL_ERROR_NULL_INPUT);
00661 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
00662 cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
00663 CPL_ERROR_UNSUPPORTED_MODE);
00664 cpl_ensure_code(radius > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00665
00666 nx = cpl_image_get_size_x(self);
00667 ny = cpl_image_get_size_y(self);
00668
00669
00670 lx = (int)(xpos - radius);
00671 ly = (int)(ypos - radius);
00672 if (lx < 0) lx = 0;
00673 if (ly < 0) ly = 0;
00674
00675
00676 ux = (int)(xpos + radius) + 1;
00677 uy = (int)(ypos + radius) + 1;
00678 if (ux > (nx-1)) ux = nx-1;
00679 if (uy > (ny-1)) uy = ny-1;
00680
00681
00682 pself = cpl_image_get_data_float((cpl_image*)self);
00683 for (j=ly ; j<uy ; j++) {
00684 yj = (double)j - ypos;
00685 sqrest = sqr - yj * yj;
00686 for (i=lx; i<ux ; i++) {
00687 xi = (double)i - xpos;
00688 if (sqrest >= xi * xi && irplib_isnan(pself[i+j*nx]) == 0) {
00689 if (first || pself[i+j*nx] > peak) {
00690 first = CPL_FALSE;
00691 peak = pself[i+j*nx];
00692 }
00693 }
00694 }
00695 }
00696
00697 cpl_ensure_code(!first, CPL_ERROR_DATA_NOT_FOUND);
00698
00699 *ppeak = (double)peak;
00700
00701 return CPL_ERROR_NONE;
00702 }