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