mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
speckleAmpPSD.hpp
Go to the documentation of this file.
1 /** \file speckleAmpPSD.hpp
2  * \author Jared R. Males (jaredmales@gmail.com)
3  * \brief Calculates the PSD of speckle intensity given a modified Fourier mode amplitude PSD.
4  * \ingroup mxAO_files
5  *
6  */
7 
8 #ifndef speckleAmpPSD_hpp
9 #define speckleAmpPSD_hpp
10 
11 #include <vector>
12 
13 #include <Eigen/Dense>
14 
15 #include "../../math/constants.hpp"
16 #include "../../math/randomT.hpp"
17 #include "../../math/vectorUtils.hpp"
18 
19 #include "../../sigproc/signalWindows.hpp"
20 #include "../../sigproc/psdUtils.hpp"
21 #include "../../sigproc/psdFilter.hpp"
22 #include "../../sigproc/psdVarMean.hpp"
23 #include "../../sigproc/averagePeriodogram.hpp"
24 
25 #include "jincFuncs.hpp"
26 
27 namespace mx
28 {
29 namespace AO
30 {
31 namespace analysis
32 {
33 
34 
35 
36 
37 
38 
39 /// Calculate the PSD of the speckle intensity given the PSD of Fourier mode amplitude
40 /**
41  * Does so by generating a time-series from the PSD using Fourier-domain convolution, and calculating
42  * the average periodogram.
43  *
44  * A Hann window is used.
45  *
46  * Statistics are not currently normalized. You need to normalize to match expected contrast if desired.
47  *
48  * Will also calculate the binned-variances in the generated time-series, if the arguments vars and bins are not null pointers.
49  *
50  * \todo Figure out what's going on with psdFilter normalization!
51  * \todo probably need to not do overlapped averaging in periodogram. Could drop mean-sub then.
52  */
53 template<typename realT>
54 int speckleAmpPSD( std::vector<realT> & spFreq, ///< [out] The frequency grid of the output PSD
55  std::vector<realT> & spPSD, ///< [out] The speckle amplitude PSD corresponding to the freq coordinates. Will be resized.
56  const std::vector<realT> & freq, ///< [in] The Frequency grid of the input PSD
57  const std::vector<realT> & fmPSD, ///< [in] The Fourier mode PSD.
58  const std::vector<std::complex<realT>> & fmXferFxn, ///< [in] The complex error transfer function, as a function of freq
59  const std::vector<realT> & nPSD, ///< [in] The open-loop noise PSD
60  const std::vector<std::complex<realT>> & nXferFxn, ///< [in] The noise transfer function, as a function of freq
61  int N, ///< [in] The number of trials to use in calculating the amplitude PSD.
62  std::vector<realT> * vars = nullptr, ///< [out] [optional] The binned variances of the time series generated.
63  std::vector<realT> * bins = nullptr, ///< [in] [optional] The bin sizes to use in calculating vars.
64  bool noPSD = false ///< [in] [optional] if true then the PSD is not actually calculated, only the binned variances
65  )
66 {
67 
68  std::vector<realT> psd2, npsd2;
69  std::vector<std::complex<realT>> xfer2, nxfer2;
70 
71  bool hasZero = false;
72  if(freq[0] == 0) hasZero = true;
73 
74  //First augment to two-sided DFT form
75  mx::sigproc::augment1SidedPSD( psd2, fmPSD, !hasZero, 0.5);
76  mx::sigproc::augment1SidedPSD( npsd2, nPSD, !hasZero, 0.5);
77 
78 
79  //And augment the xfer fxns to two sided form
80  sigproc::augment1SidedPSD( xfer2, fmXferFxn, !hasZero, 1.0);
81  sigproc::augment1SidedPSD( nxfer2, nXferFxn, !hasZero, 1.0);
82 
83  //The time sampling
84  realT dt = 1./(2*freq.back());
85 
86  //Indices for getting the middle half
87  int Nwd = 1.0*psd2.size();
88  int NwdStart = 0.5*psd2.size() - 0.5*Nwd;
89 
90  int Nsamp = 1.0*psd2.size();
91  int NsampStart = 0.5*Nwd - 0.5*Nsamp;
92 
93  sigproc::averagePeriodogram<realT> globalAvgPgram(Nsamp*0.1, dt);
94  spPSD.resize(globalAvgPgram.size());
95  for(size_t n=0; n<spPSD.size(); ++n) spPSD[n] = 0;
96 
97  std::vector<std::vector<realT>> means;
98  if( bins != nullptr && vars != nullptr) means.resize(bins->size());
99 
100  //Calculate the Fourier Mode variance for normalization
101  realT fmVar = sigproc::psdVar( freq, fmPSD);
102  //and the noise variance
103  realT nVar = sigproc::psdVar( freq, nPSD);
104 
105  #pragma omp parallel
106  {
107  //Filters for imposing the PSDs
108  mx::sigproc::psdFilter<realT,1> filt;
109  filt.psd(psd2,freq[1]-freq[0]);
110 
111  mx::sigproc::psdFilter<realT,1> nfilt;
112  nfilt.psd(npsd2,freq[1]-freq[0]);
113 
114  //FFTs for going to Fourier domain and back to time domain.
115  math::fft::fftT<realT, std::complex<realT>, 1, 0> fft(psd2.size());
116  math::fft::fftT<std::complex<realT>, realT, 1, 0> fftB(psd2.size(), MXFFT_BACKWARD);
117 
118  //Fourier transform working memmory
119  std::vector<std::complex<realT>> tform1(psd2.size());
120  std::vector<std::complex<realT>> tform2(psd2.size());
121 
122  std::vector<std::complex<realT>> Ntform1(psd2.size());
123  std::vector<std::complex<realT>> Ntform2(psd2.size());
124 
125  //Normally distributed random numbers
126  math::normDistT<realT> normVar;
127  normVar.seed();
128 
129  //The two modal amplitude series
130  std::vector<realT> fm_n(psd2.size());
131  std::vector<realT> fm_nm(psd2.size());
132 
133  //The two noise amplitude series
134  std::vector<realT> N_n(psd2.size());
135  std::vector<realT> N_nm(psd2.size());
136 
137  //The speckle amplitude
138  std::vector<realT> vnl( Nwd );
139  std::vector<realT> vn( Nsamp );
140 
141  //Periodogram averager
142  sigproc::averagePeriodogram<realT> avgPgram(Nsamp*0.1, dt);//, 0, 1);
143  avgPgram.win(sigproc::window::hann);
144 
145  //The temporary periodogram
146  std::vector<realT> tpgram(avgPgram.size());//spPSD.size());
147 
148  #pragma omp for
149  for(int k=0; k < N; ++k)
150  {
151  //Generate the time-series
152  for(int i=0; i< psd2.size(); ++i)
153  {
154  fm_n[i] = normVar;
155  N_n[i] = normVar;
156  N_nm[i] = normVar;
157  }
158 
159  //Filter and normalize the fourier mode time series
160  filt(fm_n);
161  math::vectorMeanSub(fm_n);
162  realT actvar = math::vectorVariance(fm_n);
163  realT norm = sqrt(fmVar/actvar);
164  for(size_t q=0; q<fm_n.size(); ++q) fm_n[q] *= norm;
165 
166  //And move it to the Fourier domain
167  fft(tform1.data(), fm_n.data());
168 
169 
170  //Filter and normalize the measurement mode time series
171  nfilt.filter(N_n);
172  nfilt.filter(N_nm);
173 
174  realT Nactvar = 0.5*(math::vectorVariance(N_n) + math::vectorVariance(N_nm));
175  norm = sqrt(nVar/Nactvar);
176  for(size_t q=0; q<fm_n.size(); ++q) N_n[q] *= norm;
177  for(size_t q=0; q<fm_n.size(); ++q) N_nm[q] *= norm;
178 
179  //And move them to the Fourier domain
180  fft(Ntform1.data(), N_n.data());
181  fft(Ntform2.data(), N_nm.data());
182 
183  std::complex<realT> scale = std::complex<realT>((tform1.size()),0) ;
184 
185  //Apply the modal phase shift, and apply the measurement noise.
186  for(size_t m=0;m<tform1.size();++m)
187  {
188  // Apply the phase shift to form the 2nd time series
189  tform2[m] = tform1[m]*exp( std::complex<realT>(0, math::half_pi<realT>() ));
190 
191  //Apply the augmented ETF to two time-series
192  tform1[m] *= xfer2[m]/scale;
193  tform2[m] *= xfer2[m]/scale;
194 
195  //Ntform2[m] = Ntform1[m]*exp( std::complex<realT>(0, math::half_pi<realT>() ));
196 
197  Ntform1[m] *= nxfer2[m]/scale;
198  Ntform2[m] *= nxfer2[m]/scale;
199  }
200 
201  //<<<<<<<<****** Transform back to the time domain.
202  fftB(fm_n.data(), tform1.data());
203  fftB(fm_nm.data(), tform2.data());
204  fftB(N_n.data(), Ntform1.data());
205  fftB(N_nm.data(), Ntform2.data());
206 
207  //Calculate the speckle amplitude and mean-subtract
208  realT mn = 0;
209  for(int i= 0; i< Nwd; ++i)
210  {
211  realT h1 = fm_n[i+NwdStart]+N_n[i+NwdStart];
212  realT h2 = fm_nm[i+NwdStart]+N_nm[i+NwdStart];
213 
214  vnl[i] = (pow(h1,2) + pow(h2,2));
215  mn += vnl[i];
216  }
217  mn /= vnl.size();
218 
219  //Extract middle sample and mean subtract
220  for(int i=0; i<Nsamp; ++i) vn[i] = vnl[i+NsampStart] - mn;
221 
222  //Calculate PSD of the speckle amplitude
223  if(!noPSD) avgPgram(tpgram, vn);
224 
225  //Accumulate
226  #pragma omp critical
227  {
228  if( bins != nullptr && vars != nullptr) math::vectorBinMeans( means, *bins, vn);
229  for(size_t i=0; i< spPSD.size(); ++i) spPSD[i] += tpgram[i];
230  }
231  }
232  }//pragma omp parallel
233 
234 
235  if(!noPSD)
236  {
237  //-- Calculate frequency grid (which is maybe not identical to input freq)
238  realT df = (1.0)/(2.0*spPSD.size()*dt);
239  spFreq.resize(spPSD.size());
240  for(size_t i =0; i< spFreq.size(); ++i)
241  {
242  spFreq[i] = globalAvgPgram[i];
243  spPSD[i] /= N;
244  }
245  //realT spVar = sigproc::psdVar1sided(df, spPSD.data(), spPSD.size());
246  //for(size_t i=0; i< spPSD.size(); ++i) spPSD[i] *= (fmVar*fmVar/spVar);
247  }
248 
249 
250  //Calculate binned variances.
251  if( bins != nullptr && vars != nullptr)
252  {
253  vars->resize(bins->size());
254  for(size_t i=0; i< bins->size(); ++i)
255  {
256  (*vars)[i] = math::vectorVariance(means[i]) ;
257  }
258  }
259 
260  return 0;
261 }
262 
263 
264 /// Calculate the variance of the mean vs bin size in a speckle time-series
265 /**
266  * Does so by generating a time-series from the PSD using Fourier-domain convolution, then
267  * calculates the binned-variances in the generated time-series.
268  *
269  * This is just a wrapper for speckleAmpPSD, with no actual PSD calculation.
270  *
271  */
272 template<typename realT>
273 int speckleAmpVarMean( std::vector<realT> & vars, ///< [out] The binned variances of the time series generated.
274  std::vector<realT> & bins, ///< [in] The bin sizes to use to calculate the variances.
275  std::vector<realT> & freq, ///< [in] The Frequency grid of the input PSD
276  std::vector<realT> & fmPSD, ///< [in] The open-loop Fourier mode PSD.
277  std::vector<std::complex<realT>> & fmXferFxn, ///< [in] The complex error transfer function, as a function of freq
278  std::vector<realT> & nPSD, ///< [in] The open-loop noise PSD
279  std::vector<std::complex<realT>> & nXferFxn, ///< [in] The noise transfer function, as a function of freq
280  int N ///< [in] The number of trials to use in calculating the amplitude time-series.
281  )
282 {
283  std::vector<realT> spFreq, spPSD;
284 
285  return speckleAmpPSD( spFreq, spPSD,freq, fmPSD, fmXferFxn, nPSD, nXferFxn, N, &vars, &bins, true);
286 }
287 
288 } //namespace analysis
289 } //namespace AO
290 } //namespace mx
291 
292 #endif //speckleAmpPSD_hpp
void seed(typename ranengT::result_type seedval)
Set the seed of the random engine.
Definition: randomT.hpp:96
Calculate the average periodogram of a time-series.
int resize(size_t avgLen)
Resize the periodogram working memory, setting up the 1/2-overlapped optimum case.
size_t size()
Return the size of the periodogram.
std::vector< realT > & win()
Get a reference to the window vector.
constexpr units::realT k()
Boltzmann Constant.
Definition: constants.hpp:71
void augment1SidedPSD(vectorTout &psdTwoSided, vectorTin &psdOneSided, bool addZeroFreq=false, typename vectorTin::value_type scale=0.5)
Augment a 1-sided PSD to standard 2-sided FFT form.
Definition: psdUtils.hpp:794
realT psdVar(const std::vector< realT > &f, const std::vector< realT > &PSD, realT half=0.5)
Calculate the variance of a 1-D PSD.
Definition: psdUtils.hpp:133
void hann(realT *filt, int N)
The Hann Window.
void vectorMeanSub(valueT *vec, size_t sz)
Subtract the mean from a vector.
int vectorBinMeans(std::vector< vectorT > &means, binVectorT &binSzs, const vectorT &v)
Calculate and accumulate the means of a timeseries in bins of various sizes.
valueT vectorVariance(const valueT *vec, size_t sz, valueT mean)
Calculate the variance of a vector relative to a supplied mean value.
The +/- Jinc functions for analyzing contrast.
The mxlib c++ namespace.
Definition: mxError.hpp:107
int speckleAmpVarMean(std::vector< realT > &vars, std::vector< realT > &bins, std::vector< realT > &freq, std::vector< realT > &fmPSD, std::vector< std::complex< realT >> &fmXferFxn, std::vector< realT > &nPSD, std::vector< std::complex< realT >> &nXferFxn, int N)
Calculate the variance of the mean vs bin size in a speckle time-series.
int speckleAmpPSD(std::vector< realT > &spFreq, std::vector< realT > &spPSD, const std::vector< realT > &freq, const std::vector< realT > &fmPSD, const std::vector< std::complex< realT >> &fmXferFxn, const std::vector< realT > &nPSD, const std::vector< std::complex< realT >> &nXferFxn, int N, std::vector< realT > *vars=nullptr, std::vector< realT > *bins=nullptr, bool noPSD=false)
Calculate the PSD of the speckle intensity given the PSD of Fourier mode amplitude.