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
00029
00030
00031 #ifndef OBSERVATION_H
00032 #define OBSERVATION_H
00033
00034 #include <complex>
00035 #include <fstream>
00036 #include <colormap.h>
00037 #include <fft_manager.h>
00038 #include <pixel_amp_array.h>
00039 #include <pixel_phase_array.h>
00040 #include "diffractive_wavefront.h"
00041
00042 namespace Arroyo {
00043
00044
00045 template <typename precision> class basic_otf;
00046
00050
00051 class observation_base {
00052
00053 public:
00054
00057 observation_base(){};
00058
00061 virtual ~observation_base(){};
00062
00065 virtual void read(const char* filename) = 0;
00066
00069 virtual void read(const iofits& iof) = 0;
00070
00073 virtual void write(const char* filename) const = 0;
00074
00077 virtual void write(iofits & iof) const = 0;
00078
00081 virtual void print(ostream & os, const char* prefix="") const = 0;
00082
00085 static observation_base* observation_base_factory(const char* filename);
00086
00089 static observation_base* observation_base_factory(const iofits& iof);
00090 };
00091
00092
00093 template<class precision>
00094 class basic_observation :
00095 public AO_sim_base,
00096 public observation_base,
00097 public Arroyo::pixel_amp_array<precision> {
00098
00099 private:
00100
00101 static const bool factory_registration;
00102
00105 string unique_name() const {return(string("basic observation"));};
00106
00107 protected:
00108
00109 double right_ascension_radians;
00110
00111 double declination_radians;
00112
00113 double pixel_scale_radians_per_pixel;
00114
00115 double wavelength_meters;
00116
00117 public:
00118
00121 basic_observation();
00122
00125 ~basic_observation(){};
00126
00129 basic_observation(const basic_observation & basic_obs){
00130 this->operator=(basic_obs);
00131 };
00132
00135 basic_observation(const char * filename){
00136 this->read(filename);
00137 };
00138
00141 basic_observation(const iofits & iof){
00142 this->read(iof);
00143 };
00144
00147 basic_observation(double ra_radians,
00148 double dec_radians,
00149 double pixel_scale_radians_per_pixel,
00150 double wavelength_meters,
00151 const pixel_amp_array<precision> & pixamparr);
00152
00153
00156 basic_observation(double aperture_diameter_meters,
00157 double focal_plane_image_size_arcsecs,
00158 double oversampling_factor,
00159 const basic_otf<precision> & otf);
00160
00161
00164
00165
00166
00167
00168
00169
00170
00171
00174 basic_observation & operator=(const basic_observation & basic_obs);
00175
00178 void read(const char * filename);
00179
00182 void read(const Arroyo::iofits & iof);
00183
00186 void write(const char * filename) const;
00187
00190 void write(Arroyo::iofits & iof) const;
00191
00194 void print(std::ostream & os, const char * prefix = "") const;
00195
00198 double get_ra() const {return(right_ascension_radians);};
00199
00202 double get_dec() const {return(declination_radians);};
00203
00206 double get_pixel_scale() const {return(pixel_scale_radians_per_pixel);};
00207
00210 double get_wavelength() const {return(wavelength_meters);};
00211
00219 vector<pair<double, double> > ensquared_energy() const;
00220
00221
00222
00225
00226
00227
00228
00229
00230 };
00231
00232 template <class precision>
00233 basic_otf<precision> operator * (const basic_otf<precision> & p1,
00234 double & fac);
00235
00236 template <class precision>
00237 basic_otf<precision> operator * (const basic_otf<precision> & p1,
00238 const basic_otf<precision> & p2);
00239
00240 template <class precision>
00241 basic_otf<precision> operator / (const basic_otf<precision> & p1,
00242 const basic_otf<precision> & p2);
00243
00244 template <class precision>
00245 basic_otf<precision> operator + (const basic_otf<precision> & p1,
00246 const basic_otf<precision> & p2);
00247
00248
00249 template<class precision>
00250 class basic_otf :
00251 public AO_sim_base {
00252
00253 private:
00254
00255 static const bool factory_registration;
00256
00259 string unique_name() const {return(string("optical transfer function"));};
00260
00261 protected:
00262
00263 double right_ascension_radians;
00264
00265 double declination_radians;
00266
00267 double pupil_plane_pixel_scale_meters;
00268
00269 double wavelength_meters;
00270
00271 pixel_amp_array<precision> OTF_amps;
00272 pixel_phase_array<precision> OTF_phases;
00273
00274 public:
00275
00278 basic_otf();
00279
00282 ~basic_otf(){};
00283
00286 basic_otf(const basic_otf & otf){
00287 this->operator=(otf);
00288 };
00289
00292 basic_otf(const char * filename){
00293 this->read(filename);
00294 };
00295
00298 basic_otf(const iofits & iof){
00299 this->read(iof);
00300 };
00301
00304 basic_otf(double ra_radians,
00305 double dec_radians,
00306 double pupil_plane_pixel_scale_meters,
00307 double wavelength_meters,
00308 const pixel_amp_array<precision> & otf_amps,
00309 const pixel_phase_array<precision> & otf_phases);
00310
00313 basic_otf(double aperture_diameter_meters,
00314 double pupil_plane_pixel_scale_meters,
00315 const basic_observation<precision> & basic_obs);
00316
00319 basic_otf & operator=(const basic_otf & otf);
00320
00323 void read(const char * filename);
00324
00327 void read(const Arroyo::iofits & iof);
00328
00331 void write(const char * filename) const;
00332
00335 void write(Arroyo::iofits & iof) const;
00336
00339 void print(std::ostream & os, const char * prefix = "") const;
00340
00343 double get_ra() const {return(right_ascension_radians);};
00344
00347 double get_dec() const {return(declination_radians);};
00348
00351 double get_pixel_scale() const {return(pupil_plane_pixel_scale_meters);};
00352
00355 double get_wavelength() const {return(wavelength_meters);};
00356
00359 vector<long> get_axes() const {return(this->OTF_amps.get_axes());};
00360
00363 complex<precision> data(int index) const {
00364 return(polar(this->OTF_amps.data(index),
00365 this->OTF_phases.data(index)));
00366 };
00367
00370 pixel_amp_array<precision> get_amps() const {
00371 return(OTF_amps);
00372 };
00373
00376 pixel_phase_array<precision> get_phases() const {
00377 return(OTF_phases);
00378 };
00379
00382 void set_data(int index, complex<precision> cmplx) const {
00383 this->OTF_amps.set_data(index, abs(cmplx));
00384 this->OTF_phases.set_data(index, arg(cmplx));
00385 };
00386
00389 void pad(int padlength) {
00390 this->OTF_amps.pad_array(padlength);
00391 this->OTF_phases.pad_array(padlength);
00392 }
00393
00396 void clip(int cliplength) {
00397 this->OTF_amps.clip_array(cliplength);
00398 this->OTF_phases.clip_array(cliplength);
00399 }
00400
00403 basic_otf<precision> & operator*=(const basic_otf<precision> & rhs);
00404
00407 basic_otf<precision> & operator/=(const basic_otf<precision> & rhs);
00408
00411 basic_otf<precision> & operator*=(double fac);
00412
00415 basic_otf<precision> & operator+=(const basic_otf<precision> & rhs);
00416
00419 basic_otf<precision> & operator-=(const basic_otf<precision> & rhs);
00420
00421 };
00422
00423 template<class precision>
00424 basic_observation<precision>::
00425 basic_observation(){
00426
00427 this->right_ascension_radians = 0;
00428 this->declination_radians = 0;
00429 this->pixel_scale_radians_per_pixel = -1;
00430 this->wavelength_meters = -1;
00431
00432 }
00433
00434 template<class precision>
00435 basic_observation<precision>::
00436 basic_observation(double ra_radians,
00437 double dec_radians,
00438 double pixscale_radians_per_pixel,
00439 double wavelength_meters,
00440 const pixel_amp_array<precision> & pixamparr){
00441
00442 this->right_ascension_radians = ra_radians;
00443 this->declination_radians = dec_radians;
00444 this->pixel_scale_radians_per_pixel = pixscale_radians_per_pixel;
00445 this->wavelength_meters = wavelength_meters;
00446
00447 this->pixel_amp_array<precision>::operator=(pixamparr);
00448
00449 }
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566 template<typename precision>
00567 basic_observation<precision>::basic_observation(double aperture_diameter_meters,
00568 double focal_plane_image_size_arcsecs,
00569 double oversampling_factor,
00570 const basic_otf<precision> & otf) {
00571
00572
00573 try{
00574
00575 if(aperture_diameter_meters<=0){
00576 cerr << "basic_observation::basic_observation error - aperture diameter "
00577 << aperture_diameter_meters
00578 << " out of range\n";
00579 throw(string("basic_observation::basic_observation"));
00580 }
00581
00582 if(focal_plane_image_size_arcsecs<=0){
00583 cerr << "basic_observation::basic_observation error - focal plane image size "
00584 << focal_plane_image_size_arcsecs
00585 << " out of range\n";
00586 throw(string("basic_observation::basic_observation"));
00587 }
00588
00589 if(oversampling_factor<=0){
00590 cerr << "basic_observation::basic_observation error - oversampling factor "
00591 << oversampling_factor
00592 << " out of range\n";
00593 throw(string("basic_observation::basic_observation"));
00594 }
00595
00596 double arcsecs_to_radians = M_PI/180./3600.;
00597
00598 vector<long> otf_axes = otf.get_axes();
00599 double nyquist_pixel_scale_radians_per_pixel =
00600 otf.get_wavelength() / 2. / aperture_diameter_meters;
00601 this->wavelength_meters = otf.get_wavelength();
00602 this->pixel_scale_radians_per_pixel = nyquist_pixel_scale_radians_per_pixel/oversampling_factor;
00603 long psf_dimen = (long)(ceil(focal_plane_image_size_arcsecs*arcsecs_to_radians/this->pixel_scale_radians_per_pixel));
00604 vector<long> psf_axes(2,psf_dimen);
00605 this->set_axes(psf_axes);
00606
00607
00608 int notf_elems = otf_axes[0]*otf_axes[1];
00609 int npsf_elems = psf_axes[0]*psf_axes[1];
00610 precision *otf_data;
00611 precision *psf_data;
00612 try{
00613 otf_data = new precision[2*notf_elems];
00614 psf_data = new precision[2*npsf_elems];
00615 } catch(...) {
00616 cerr << "basic_observation::basic_observation - error allocating memory\n";
00617 throw(string("basic_observation::basic_observation"));
00618 }
00619
00620
00621 double fac = 2*this->pixel_scale_radians_per_pixel/nyquist_pixel_scale_radians_per_pixel;
00622 double xslope = psf_axes[1]%2==1 ? 0 : M_PI*fac/(double)(otf_axes[1]);
00623 double yslope = psf_axes[0]%2==1 ? 0 : M_PI*fac/(double)(otf_axes[0]);
00624
00625
00626 double otf_x_halfpix=0, otf_y_halfpix=0;
00627 int otf_x_extrapix=1, otf_y_extrapix=1;
00628 if(otf_axes[1]%2==0){
00629 otf_x_halfpix = .5;
00630 otf_x_extrapix = 0;
00631 }
00632 if(otf_axes[0]%2==0){
00633 otf_y_halfpix = .5;
00634 otf_y_extrapix = 0;
00635 }
00636 double psf_x_halfpix=0, psf_y_halfpix=0;
00637 int psf_x_extrapix=1, psf_y_extrapix=1;
00638 if(this->axes[1]%2==0){
00639 psf_x_halfpix = .5;
00640 psf_x_extrapix = 0;
00641 }
00642 if(this->axes[0]%2==0){
00643 psf_y_halfpix = .5;
00644 psf_y_extrapix = 0;
00645 }
00646
00647
00648 int index;
00649 double twopi = 2*M_PI;
00650 double amp, phase;
00651 complex<precision> cmp;
00652 for(int i=-otf_axes[1]/2; i<otf_axes[1]/2+otf_x_extrapix; i++){
00653 for(int j=-otf_axes[0]/2; j<otf_axes[0]/2+otf_y_extrapix; j++){
00654 index = (i+otf_axes[1]/2)*otf_axes[0]+j+otf_axes[0]/2;
00655 cmp = otf.data(index);
00656 amp = abs(cmp);
00657 phase = fmod(arg(cmp) - xslope*(i+otf_x_halfpix) - yslope*(j+otf_y_halfpix), twopi);
00658 otf_data[2*index] = amp*cos(phase);
00659 otf_data[2*index+1] = amp*sin(phase);
00660 }
00661 }
00662
00663
00664 double sampling_factor = -1/(double)otf_axes[0]/oversampling_factor;
00665
00666 goertzel_reinsch_transform(otf_axes, psf_axes, sampling_factor, otf_data, psf_data);
00667
00668 delete [] otf_data;
00669
00670
00671 for(int i=0; i<npsf_elems; i++)
00672 this->pixeldata[i] =
00673 sqrt(psf_data[2*i]*psf_data[2*i] + psf_data[2*i+1]*psf_data[2*i+1]);
00674
00675 delete [] psf_data;
00676
00677 } catch(...) {
00678 cerr << "basic_observation::basic_observation error\n";
00679 throw(string("basic_observation::basic_observation"));
00680 }
00681 }
00682
00683
00684 template<class precision>
00685 basic_observation<precision> & basic_observation<precision>::
00686 operator=(const basic_observation<precision> & basic_obs){
00687 if(this==&basic_obs)
00688 return(*this);
00689
00690 this->right_ascension_radians = basic_obs.right_ascension_radians;
00691 this->declination_radians = basic_obs.declination_radians;
00692 this->pixel_scale_radians_per_pixel = basic_obs.pixel_scale_radians_per_pixel;
00693 this->wavelength_meters = basic_obs.wavelength_meters;
00694
00695 this->pixel_amp_array<precision>::operator=(basic_obs);
00696
00697 return(*this);
00698 }
00699
00700 template<class precision>
00701 void basic_observation<precision>::read(const char * filename){
00702 iofits iof;
00703 try{iof.open(filename);}
00704 catch(...){
00705 cerr << "basic_observation::read - "
00706 << "error opening file " << filename << endl;
00707 throw(string("basic_observation::read"));
00708 }
00709 try{this->read(iof);}
00710 catch(...){
00711 cerr << "basic_observation::read - "
00712 << "error reading "
00713 << this->unique_name() << " from file "
00714 << filename << endl;
00715 throw(string("basic_observation::read"));
00716 }
00717 }
00718
00719 template<class precision>
00720 void basic_observation<precision>::read(const iofits & iof){
00721
00722 if(!iof.key_exists("TYPE")){
00723 cerr << "basic_observation::read error - "
00724 << "unrecognized type of file\n";
00725 iof.print_header(cerr, "hdr dump");
00726 cerr << "hdu num " << iof.get_hdu_num() << " of "
00727 << iof.get_num_hdus() << endl;
00728 throw(string("basic_observation::read"));
00729 }
00730
00731 string type, comment;
00732 iof.read_key("TYPE", type, comment);
00733 if(type!=this->unique_name()){
00734 cerr << "basic_observation::read error - file of type "
00735 << type << " rather than of type "
00736 << this->unique_name() << endl;
00737 throw(string("basic_observation::read"));
00738 }
00739
00740 iof.read_key("CDELT1", this->pixel_scale_radians_per_pixel, comment);
00741 iof.read_key("CRVAL1", this->right_ascension_radians, comment);
00742 iof.read_key("CRVAL2", this->declination_radians, comment);
00743
00744 double radians_to_degrees = 180./M_PI;
00745 double degrees_to_radians = M_PI/180.;
00746
00747
00748 this->pixel_scale_radians_per_pixel *= -1*degrees_to_radians;
00749 this->right_ascension_radians *= degrees_to_radians;
00750 this->declination_radians *= degrees_to_radians;
00751
00752 comment = "observation wavelength (meters)";
00753 iof.read_key("WVLNGTH", this->wavelength_meters, comment);
00754
00755 this->pixel_amp_array<precision>::read(iof);
00756
00757 }
00758
00759 template<class precision>
00760 void basic_observation<precision>::write(const char * filename) const {
00761 iofits iof;
00762 try{iof.create(filename);}
00763 catch(...){
00764 cerr << "basic_observation::write - "
00765 << "error opening file " << filename << endl;
00766 throw(string("basic_observation::write"));
00767 }
00768 try{this->write(iof);}
00769 catch(...){
00770 cerr << "basic_observation::write - "
00771 << "error writing "
00772 << this->unique_name() << " to file "
00773 << filename << endl;
00774 throw(string("basic_observation::write"));
00775 }
00776 }
00777
00778 template<class precision>
00779 void basic_observation<precision>::write(iofits & iof) const {
00780
00781 double radians_to_degrees = 180./M_PI;
00782
00783 fits_header_data<double> fhd(this->get_axes());
00784 fhd.write(iof);
00785 string type = this->unique_name();
00786 string comment = "object type";
00787 iof.write_key("TYPE", type, comment);
00788
00789 iof.write_key("RADECSYS", string("FK4"), string("SYSTEM OF REF. COORD"));
00790 iof.write_key("EQUINOX", (long)2000, string("EQUINOX OF REF. COORD"));
00791 iof.write_key("CTYPE1", string("RA---TAN"), string("AXIS TYPE"));
00792 iof.write_key("CTYPE2", string("DEC--TAN"), string("AXIS TYPE"));
00793 iof.write_key("CD001001", (long)1, string("UNITLESS"));
00794 iof.write_key("CD002001", (long)0, string("UNITLESS"));
00795 iof.write_key("CD001002", (long)0, string("UNITLESS"));
00796 iof.write_key("CD002002", (long)1, string("UNITLESS"));
00797 iof.write_key("CDELT1", -1*this->pixel_scale_radians_per_pixel*radians_to_degrees, string("DEGREES/PIXEL"));
00798 iof.write_key("CDELT2", this->pixel_scale_radians_per_pixel*radians_to_degrees, string("DEGREES/PIXEL"));
00799 iof.write_key("CRPIX1", (long)(this->get_axes()[1]/2), string("REFERENCE PIXEL IN X"));
00800 iof.write_key("CRPIX2", (long)(this->get_axes()[0]/2), string("REFERENCE PIXEL IN Y"));
00801 iof.write_key("CRVAL1", this->right_ascension_radians*radians_to_degrees, string("R.A. (DEGREES)"));
00802 iof.write_key("CRVAL2", this->declination_radians*radians_to_degrees, string("DEC. (DEGREES)"));
00803
00804 comment = "observation wavelength (meters)";
00805 iof.write_key("WVLNGTH", this->wavelength_meters, comment);
00806
00807 this->pixel_amp_array<precision>::write(iof);
00808 }
00809
00810 template<class precision>
00811 void basic_observation<precision>::
00812 print(ostream & os, const char * prefix) const {
00813 int vlspc = 30;
00814 os.setf(ios::left, ios::adjustfield);
00815 os << prefix << "TYPE = " << setw(vlspc) << this->unique_name()
00816 << "/" << "object type" << endl;
00817 fits_header_data<double> fhd(this->get_axes());
00818 fhd.print(os, prefix);
00819 os << prefix << "RA = " << setw(vlspc) << this->right_ascension_radians
00820 << "/" << "right ascension (radians)" << endl;
00821
00822 os << prefix << "DEC = " << setw(vlspc) << this->declination_radians
00823 << "/" << "declination (radians)" << endl;
00824
00825 os << prefix << "PIXSCALE = " << setw(vlspc) << this->pixel_scale_radians_per_pixel
00826 << "/" << "pixel scale (radians)" << endl;
00827
00828 os << prefix << "WVLNGTH = " << setw(vlspc) << this->wavelength_meters
00829 << "/" << "wavelength (meters)" << endl;
00830
00831 }
00832
00833
00834 template<class precision>
00835 vector<pair<double, double> >
00836 basic_observation<precision>::ensquared_energy() const {
00837
00838 vector<long> axes = this->get_axes();
00839 int extrapix = 1;
00840 double halfpix = 0;
00841 if(axes[0]%2==0){
00842 extrapix = 0;
00843 halfpix = .5;
00844 }
00845
00846 int min_slit_width_pixels = extrapix==0 ? 2 : 1;
00847 int max_slit_width_pixels = axes[0];
00848
00849 int upper_left_index, upper_right_index, lower_left_index, lower_right_index;
00850 double ensquared_energy = 0;
00851
00852 vector<pair<double, double> > slit_widths_and_EEs;
00853
00854 for(int k=min_slit_width_pixels; k<max_slit_width_pixels; k+=2){
00855
00856 if(k==1)
00857 ensquared_energy += this->pixel_amp_array<double>::pixeldata[(axes[0]+1)*(axes[0]/2)];
00858 else {
00859 upper_left_index = ((axes[0]-k)/2)*(axes[0]+1);
00860 upper_right_index = upper_left_index + (k-1);
00861 lower_left_index = upper_left_index + axes[0]*(k-1);
00862 lower_right_index = lower_left_index + (k-1);
00863
00864 for(int m=upper_left_index; m<=upper_right_index; m++){
00865 ensquared_energy += this->pixel_amp_array<double>::pixeldata[m];
00866 }
00867
00868 for(int m=1; m<=(k-2); m++){
00869 ensquared_energy += this->pixel_amp_array<double>::pixeldata[upper_left_index+axes[0]*m];
00870 ensquared_energy += this->pixel_amp_array<double>::pixeldata[upper_right_index+axes[0]*m];
00871 }
00872
00873 for(int m=lower_left_index; m<=lower_right_index; m++){
00874 ensquared_energy += this->pixel_amp_array<double>::pixeldata[m];
00875 }
00876 }
00877 slit_widths_and_EEs.push_back(pair<double,double>(k*this->pixel_scale_radians_per_pixel,
00878 ensquared_energy));
00879
00880 }
00881 return(slit_widths_and_EEs);
00882 }
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003 template<class precision>
01004 basic_otf<precision>::
01005 basic_otf(){
01006
01007 this->OTF_amps = pixel_amp_array<precision>(vector<long>(2,0));
01008 this->OTF_phases = pixel_phase_array<precision>(vector<long>(2,0));
01009
01010 this->right_ascension_radians = 0;
01011 this->declination_radians = 0;
01012 this->pupil_plane_pixel_scale_meters = -1;
01013 this->wavelength_meters = -1;
01014 }
01015
01016 template<class precision>
01017 basic_otf<precision>::
01018 basic_otf(double ra_radians,
01019 double dec_radians,
01020 double pupil_plane_pixscale_meters,
01021 double wavelength_meters,
01022 const pixel_amp_array<precision> & otf_amps,
01023 const pixel_phase_array<precision> & otf_phases){
01024
01025 this->right_ascension_radians = ra_radians;
01026 this->declination_radians = dec_radians;
01027 this->pupil_plane_pixel_scale_meters = pupil_plane_pixscale_meters;
01028 this->wavelength_meters = wavelength_meters;
01029
01030 this->OTF_amps = otf_amps;
01031 this->OTF_phases = otf_phases;
01032
01033 }
01034
01035
01036 template<typename precision>
01037 basic_otf<precision>::basic_otf(double aperture_diameter_meters,
01038 double pupil_plane_pixscale_meters,
01039 const basic_observation<precision> & basic_obs) {
01040
01041
01042 try{
01043
01044 if(aperture_diameter_meters<=0){
01045 cerr << "basic_otf::basic_otf error - pupil plane pixel scale "
01046 << aperture_diameter_meters
01047 << " out of range\n";
01048 throw(string("basic_otf::basic_otf"));
01049 }
01050 if(pupil_plane_pixscale_meters<=0){
01051 cerr << "basic_otf::basic_otf error - pupil plane pixel scale "
01052 << pupil_plane_pixscale_meters
01053 << " out of range\n";
01054 throw(string("basic_otf::basic_otf"));
01055 }
01056
01057 this->wavelength_meters = basic_obs.get_wavelength();
01058 this->pupil_plane_pixel_scale_meters = pupil_plane_pixscale_meters;
01059
01060
01061 int otf_dimen = (int)ceil(2*aperture_diameter_meters/pupil_plane_pixscale_meters);
01062 if(otf_dimen%2==0) otf_dimen++;
01063 vector<long> otf_axes(2,otf_dimen);
01064 vector<long> psf_axes = basic_obs.get_axes();
01065
01066 int notf_elems = otf_dimen*otf_dimen;
01067 int npsf_elems = psf_axes[0]*psf_axes[1];
01068 precision *otf_data;
01069 precision *psf_data;
01070 try{
01071 otf_data = new precision[2*notf_elems];
01072 psf_data = new precision[2*npsf_elems];
01073 } catch(...) {
01074 cerr << "basic_otf::basic_otf - error allocating memory\n";
01075 throw(string("basic_otf::basic_otf"));
01076 }
01077
01078
01079 double xslope = otf_axes[1]%2==1 ? 0 : M_PI/(double)(psf_axes[1]);
01080 double yslope = otf_axes[0]%2==1 ? 0 : M_PI/(double)(psf_axes[0]);
01081
01082
01083 double otf_x_halfpix=0, otf_y_halfpix=0;
01084 int otf_x_extrapix=1, otf_y_extrapix=1;
01085 if(otf_axes[1]%2==0){
01086 otf_x_halfpix = .5;
01087 otf_x_extrapix = 0;
01088 }
01089 if(otf_axes[0]%2==0){
01090 otf_y_halfpix = .5;
01091 otf_y_extrapix = 0;
01092 }
01093 double psf_x_halfpix=0, psf_y_halfpix=0;
01094 int psf_x_extrapix=1, psf_y_extrapix=1;
01095 if(psf_axes[1]%2==0){
01096 psf_x_halfpix = .5;
01097 psf_x_extrapix = 0;
01098 }
01099 if(psf_axes[0]%2==0){
01100 psf_y_halfpix = .5;
01101 psf_y_extrapix = 0;
01102 }
01103
01104
01105 int index;
01106 double twopi = 2*M_PI;
01107 double amp, phase;
01108 for(int i=-psf_axes[1]/2; i<psf_axes[1]/2+psf_x_extrapix; i++){
01109 for(int j=-psf_axes[0]/2; j<psf_axes[0]/2+psf_y_extrapix; j++){
01110 index = (i+psf_axes[1]/2)*psf_axes[0]+j+psf_axes[0]/2;
01111 amp = basic_obs.data(index);
01112 phase = (i+psf_x_halfpix)*xslope + (j+psf_y_halfpix)*yslope;
01113 psf_data[2*index] = amp*cos(phase);
01114 psf_data[2*index+1] = amp*sin(phase);
01115 }
01116 }
01117
01118
01119
01120
01121
01122
01123
01124 double oversampling_factor =
01125 wavelength_meters / basic_obs.get_pixel_scale() / aperture_diameter_meters;
01126 double sampling_factor = 2/(double)otf_axes[0]/oversampling_factor;
01127
01128
01129 goertzel_reinsch_transform(psf_axes, otf_axes, sampling_factor, psf_data, otf_data);
01130
01131 delete [] psf_data;
01132
01133 this->OTF_amps = pixel_amp_array<precision>(vector<long>(2,otf_dimen));
01134 this->OTF_phases = pixel_phase_array<precision>(vector<long>(2,otf_dimen));
01135
01136 double nyquist_pixel_scale_radians_per_pixel =
01137 basic_obs.get_wavelength() / (double) (otf_axes[0]/2) / this->pupil_plane_pixel_scale_meters;
01138 double fac = basic_obs.get_pixel_scale()/nyquist_pixel_scale_radians_per_pixel;
01139
01140 xslope = psf_axes[1]%2==1 ? 0 : -2*M_PI*fac/(double)otf_axes[1];
01141 yslope = psf_axes[0]%2==1 ? 0 : -2*M_PI*fac/(double)otf_axes[0];
01142
01143 for(int i=-otf_axes[1]/2; i<otf_axes[1]/2+otf_x_extrapix; i++){
01144 for(int j=-otf_axes[0]/2; j<otf_axes[0]/2+otf_y_extrapix; j++){
01145 index = (i+otf_axes[1]/2)*otf_axes[0]+j+otf_axes[0]/2;
01146 amp = sqrt(otf_data[2*index]*otf_data[2*index] + otf_data[2*index+1]*otf_data[2*index+1]);
01147 phase = fmod(atan2(otf_data[2*index+1],otf_data[2*index]) -
01148 xslope*(i+otf_x_halfpix) -
01149 yslope*(j+otf_y_halfpix), twopi);
01150 this->OTF_amps.set_data(index, amp);
01151 this->OTF_phases.set_data(index, phase);
01152 }
01153 }
01154
01155 delete [] otf_data;
01156
01157 } catch(...) {
01158 cerr << "basic_otf::basic_otf error\n";
01159 throw(string("basic_otf::basic_otf"));
01160 }
01161 }
01162
01163
01164 template<class precision>
01165 basic_otf<precision> & basic_otf<precision>::
01166 operator=(const basic_otf<precision> & otf){
01167 if(this==&otf)
01168 return(*this);
01169
01170 this->right_ascension_radians = otf.right_ascension_radians;
01171 this->declination_radians = otf.declination_radians;
01172 this->pupil_plane_pixel_scale_meters = otf.pupil_plane_pixel_scale_meters;
01173 this->wavelength_meters = otf.wavelength_meters;
01174
01175 this->OTF_amps = otf.OTF_amps;
01176 this->OTF_phases = otf.OTF_phases;
01177
01178 return(*this);
01179 }
01180
01181 template<class precision>
01182 void basic_otf<precision>::read(const char * filename){
01183 iofits iof;
01184 try{iof.open(filename);}
01185 catch(...){
01186 cerr << "basic_otf::read - "
01187 << "error opening file " << filename << endl;
01188 throw(string("basic_otf::read"));
01189 }
01190 try{this->read(iof);}
01191 catch(...){
01192 cerr << "basic_otf::read - "
01193 << "error reading "
01194 << this->unique_name() << " from file "
01195 << filename << endl;
01196 throw(string("basic_otf::read"));
01197 }
01198 }
01199
01200 template<class precision>
01201 void basic_otf<precision>::read(const iofits & iof){
01202
01203 if(!iof.key_exists("TYPE")){
01204 cerr << "basic_otf::read error - "
01205 << "unrecognized type of file\n";
01206 iof.print_header(cerr, "hdr dump");
01207 cerr << "hdu num " << iof.get_hdu_num() << " of "
01208 << iof.get_num_hdus() << endl;
01209 throw(string("basic_otf::read"));
01210 }
01211
01212 string type, comment;
01213 iof.read_key("TYPE", type, comment);
01214 if(type!=this->unique_name()){
01215 cerr << "basic_otf::read error - file of type "
01216 << type << " rather than of type "
01217 << this->unique_name() << endl;
01218 throw(string("basic_otf::read"));
01219 }
01220
01221 iof.read_key("PIXSCALE", this->pupil_plane_pixel_scale_meters, comment);
01222 iof.read_key("CRVAL1", this->right_ascension_radians, comment);
01223 iof.read_key("CRVAL2", this->declination_radians, comment);
01224
01225 double radians_to_degrees = 180./M_PI;
01226 double degrees_to_radians = M_PI/180.;
01227
01228 comment = "observation wavelength (meters)";
01229 iof.read_key("WVLNGTH", this->wavelength_meters, comment);
01230
01231 this->OTF_amps.read(iof);
01232
01233 this->OTF_phases.read(iof);
01234
01235 }
01236
01237 template<class precision>
01238 void basic_otf<precision>::write(const char * filename) const {
01239 iofits iof;
01240 try{iof.create(filename);}
01241 catch(...){
01242 cerr << "basic_otf::write - "
01243 << "error opening file " << filename << endl;
01244 throw(string("basic_otf::write"));
01245 }
01246 try{this->write(iof);}
01247 catch(...){
01248 cerr << "basic_otf::write - "
01249 << "error writing "
01250 << this->unique_name() << " to file "
01251 << filename << endl;
01252 throw(string("basic_otf::write"));
01253 }
01254 }
01255
01256 template<class precision>
01257 void basic_otf<precision>::write(iofits & iof) const {
01258
01259 double radians_to_degrees = 180./M_PI;
01260
01261 fits_header_data<double> fhd(this->OTF_amps.get_axes());
01262 fhd.write(iof);
01263 string type = this->unique_name();
01264 string comment = "object type";
01265 iof.write_key("TYPE", type, comment);
01266
01267 comment = "pupil plane pixel scale (meters)";
01268 iof.write_key("PIXSCALE", this->pupil_plane_pixel_scale_meters, comment);
01269 iof.write_key("CRVAL1", this->right_ascension_radians*radians_to_degrees, string("R.A. (DEGREES)"));
01270 iof.write_key("CRVAL2", this->declination_radians*radians_to_degrees, string("DEC. (DEGREES)"));
01271
01272 comment = "observation wavelength (meters)";
01273 iof.write_key("WVLNGTH", this->wavelength_meters, comment);
01274
01275 this->OTF_amps.write(iof);
01276
01277 fhd.write(iof);
01278 this->OTF_phases.write(iof);
01279 }
01280
01281 template<class precision>
01282 void basic_otf<precision>::
01283 print(ostream & os, const char * prefix) const {
01284 int vlspc = 30;
01285 os.setf(ios::left, ios::adjustfield);
01286 os << prefix << "TYPE = " << setw(vlspc) << this->unique_name()
01287 << "/" << "object type" << endl;
01288 fits_header_data<double> fhd(this->OTF_amps.get_axes());
01289 fhd.print(os, prefix);
01290 os << prefix << "RA = " << setw(vlspc) << this->right_ascension_radians
01291 << "/" << "right ascension (radians)" << endl;
01292
01293 os << prefix << "DEC = " << setw(vlspc) << this->declination_radians
01294 << "/" << "declination (radians)" << endl;
01295
01296 os << prefix << "PIXSCALE = " << setw(vlspc) << this->pupil_plane_pixel_scale_meters
01297 << "/" << "pupil plane pixel scale (meters)" << endl;
01298
01299 os << prefix << "WVLNGTH = " << setw(vlspc) << this->wavelength_meters
01300 << "/" << "wavelength (meters)" << endl;
01301
01302 }
01303
01304 template<class precision>
01305 basic_otf<precision> &
01306 basic_otf<precision>::operator*=(const basic_otf<precision> & rhs){
01307
01308 if(this->get_axes()!=rhs.get_axes()){
01309 cerr << "basic_otf<precision>::operator*= error - mismatched axes\n";
01310 this->print(cerr, "lhs ");
01311 rhs.print(cerr, "rhs ");
01312 throw(string("basic_otf<precision>::operator*="));
01313 }
01314 vector<long> axes = rhs.get_axes();
01315 int nelems = axes[0]*axes[1];
01316 complex<precision> cmp;
01317 for(int i=0; i<nelems; i++){
01318 if(abs(this->data(i))!=0 && abs(rhs.data(i))!=0)
01319 this->set_data(i, this->data(i)*rhs.data(i));
01320 else
01321 this->set_data(i,0);
01322 }
01323 }
01324
01325 template<class precision>
01326 basic_otf<precision> &
01327 basic_otf<precision>::operator/=(const basic_otf<precision> & rhs){
01328
01329 if(this->get_axes()!=rhs.get_axes()){
01330 cerr << "basic_otf<precision>::operator/= error - mismatched axes\n";
01331 this->print(cerr, "lhs ");
01332 rhs.print(cerr, "rhs ");
01333 throw(string("basic_otf<precision>::operator/="));
01334 }
01335 vector<long> axes = rhs.get_axes();
01336 int nelems = axes[0]*axes[1];
01337 for(int i=0; i<nelems; i++){
01338 if(abs(this->data(i))!=0)
01339 this->set_data(i, this->data(i)/rhs.data(i));
01340 else
01341 this->set_data(i,0);
01342 }
01343 }
01344
01345 template<class precision>
01346 basic_otf<precision> &
01347 basic_otf<precision>::operator*=(double fac){
01348 this->OTF_amps *= fac;
01349 }
01350
01351 template<class precision>
01352 basic_otf<precision> &
01353 basic_otf<precision>::operator+=(const basic_otf<precision> & rhs){
01354
01355 if(this->get_axes()!=rhs.get_axes()){
01356 cerr << "basic_otf<precision>::operator+= error - mismatched axes\n";
01357 this->print(cerr, "lhs ");
01358 rhs.print(cerr, "rhs ");
01359 throw(string("basic_otf<precision>::operator+="));
01360 }
01361 vector<long> axes = rhs.get_axes();
01362 int nelems = axes[0]*axes[1];
01363 for(int i=0; i<nelems; i++)
01364 this->set_data(i, this->data(i)+rhs.data(i));
01365 }
01366
01367 template<class precision>
01368 basic_otf<precision> &
01369 basic_otf<precision>::operator-=(const basic_otf<precision> & rhs){
01370
01371 if(this->get_axes()!=rhs.get_axes()){
01372 cerr << "basic_otf<precision>::operator+= error - mismatched axes\n";
01373 this->print(cerr, "lhs ");
01374 rhs.print(cerr, "rhs ");
01375 throw(string("basic_otf<precision>::operator+="));
01376 }
01377 vector<long> axes = rhs.get_axes();
01378 int nelems = axes[0]*axes[1];
01379 for(int i=0; i<nelems; i++)
01380 this->set_data(i, this->data(i)-rhs.data(i));
01381 }
01382
01383 template <class precision>
01384 basic_otf<precision> operator * (const basic_otf<precision> &p1,
01385 double & fac){
01386 basic_otf<precision> tmp(p1);
01387 tmp.operator*=(fac);
01388 return(tmp);
01389 }
01390
01391 template <class precision>
01392 basic_otf<precision> operator * (const basic_otf<precision> &p1,
01393 const basic_otf<precision> &p2){
01394 basic_otf<precision> tmp(p1);
01395 tmp.operator*=(p2);
01396 return(tmp);
01397 }
01398
01399 template <class precision>
01400 basic_otf<precision> operator / (const basic_otf<precision> &p1,
01401 const basic_otf<precision> &p2){
01402 basic_otf<precision> tmp(p1);
01403 tmp.operator/=(p2);
01404 return(tmp);
01405 }
01406
01407 template <class precision>
01408 basic_otf<precision> operator + (const basic_otf<precision> &p1,
01409 const basic_otf<precision> &p2){
01410 basic_otf<precision> tmp(p1);
01411 tmp.operator+=(p2);
01412 return(tmp);
01413 }
01414
01415 template <class precision>
01416 basic_otf<precision> operator - (const basic_otf<precision> &p1,
01417 const basic_otf<precision> &p2){
01418 basic_otf<precision> tmp(p1);
01419 tmp.operator-=(p2);
01420 return(tmp);
01421 }
01422
01423 }
01424 #endif