// This C++ program has been written to demonstrate the // convolutional resampling algorithm used in radio // interferometry. It should compile with: // g++ -O2 tConvolve.cc -o tConvolve // Compiler option -O3 will optimize away the gridding! // // The challenge is to minimize the run time - specifically // the time per grid addition. On a MacBookPro and an Opteron // this is about 8ns. // // For further details contact Tim.Cornwell@csiro.au // June 5, 2007 #include #include #include #include #include #include using std::cout; using std::endl; using std::complex; using std::abs; // Typedefs for easy testing // Cost of using double for Coord is low, cost for // double for Real is also low typedef float Coord; typedef float Real; typedef std::complex Value; // Perform standard data independent gridding // // u,v,w - components of spatial frequency // data - values to be gridded // freq - temporal frequency (inverse wavelengths) // cellSize - size of one grid cell in wavelengths // C - convolution function // support - Total width of convolution function=2*support+1 // overSample - Oversampling factor for the convolution function // cOffset - offsets into convolution function per data point // grid - Output grid // int generic(const std::vector& u, const std::vector& v, const std::vector& w, std::vector& data, const std::vector& freq, const Coord cellSize, const std::vector& C, const int support, const int overSample, const std::vector& cOffset, std::vector& grid) { const int gSize = static_cast(std::sqrt(static_cast(grid.size()))); std::cout << "Grid size = " << gSize << std::endl; const int nSamples = u.size(); const int nChan = freq.size(); int cSize=2*(support+1)*overSample+1; int cCenter=(cSize-1)/2; // Grid grid.assign(grid.size(), Value(0.0)); cout << "+++++ Forward processing +++++" << endl; clock_t start,finish; double time; Real sumwt=0.0; start = clock(); // Loop over all samples adding them to the grid // First scale to the correct pixel location // Then find the fraction of a pixel to the nearest pixel // Loop over the entire support, calculating weights from // the convolution function and adding the scaled // visibility to the grid. for (int i=0;i& u, const std::vector& v, const std::vector& w, std::vector& data, const std::vector& freq, const Coord cellSize, std::vector& grid) { cout << "*************************** Standard gridding ***********************" << endl; int support=3; // Support for gridding function in pixels const int overSample=100; cout << "Support = " << support << " pixels" << endl; // Convolution function // We take this to be the product of two Gaussian. More often it // is the product of two prolate spheroidal wave functions int cSize=2*(support+1)*overSample+1; std::vector C(cSize*cSize); int cCenter=(cSize-1)/2; // Keep this symmetrically to streamline index handling later.... for (int i=0;i cOffset; cOffset.assign(data.size(),0); return generic(u, v, w, data, freq, cellSize, C, support, overSample, cOffset, grid); } // Perform w projection (data dependent) gridding // // u,v,w - components of spatial frequency // data - values to be gridded // nSamples - number of visibility samples // freq - temporal frequency (inverse wavelengths) // cellSize - size of one grid cell in wavelengths // gSize - size of grid in pixels (per axis) // support - Total width of convolution function=2*support+1 // wCellSize - size of one w grid cell in wavelengths // wSize - Size of lookup table in w int wprojection(const std::vector& u, const std::vector& v, const std::vector& w, std::vector& data, const std::vector& freq, const Coord cellSize, const Coord baseline, const int wSize, std::vector& grid) { const int nSamples = u.size(); const int nChan = freq.size(); cout << "************************* W projection gridding *********************" << endl; int support=static_cast(1.5*sqrt(abs(baseline)*static_cast(cellSize)*freq[0])/cellSize); int overSample=8; cout << "Support = " << support << " pixels" << endl; const Coord wCellSize=2*baseline*freq[0]/wSize; cout << "W cellsize = " << wCellSize << " wavelengths" << endl; // Convolution function. This should be the convolution of the // w projection kernel (the Fresnel term) with the convolution // function used in the standard case. The latter is needed to // suppress aliasing. In practice, we calculate entire function // by Fourier transformation. Here we take an approximation that // is good enough. int cSize=2*(support+1)*overSample+1; int cCenter=(cSize-1)/2; std::vector C(cSize*cSize*wSize); for (int k=0;k(std::cos(r2/(w*fScale))); } } } else { for (int j=0;j(std::exp(-r2)); } } } } std::vector cOffset(data.size()); for (int i=0;i u(nSamples); std::vector v(nSamples); std::vector w(nSamples); std::vector data(nSamples*nChan); for (int i=0;i freq(nChan); for (int i=0;i grid(gSize*gSize); standard(u, v, w, data, freq, cellSize, grid); wprojection(u, v, w, data, freq, cellSize, baseline, wSize, grid); cout << "Done" << endl; return 0; }