POLAR MAPPING (October 22, 1999)
HESSI is a Fourier-transform imager and its primary data form consists of samples in the Fourier (u,v) plane of the Fourier transform of a flare. There is a rich heritage of Fourier transform imaging, most of it in the radio domain, but most Fourier imaging in astronomy has been done in rectangular, Cartesian coordinates, where the ``dirty map'' D(x,y) is given by the transform of visibilities:
But HESSI is a spinning telescope, and the samples in the U,V plane are on circles , one for each subcollimator and harmonic. Therefore it seems to make sense to go into polar coordinates, and it turns out that this is an efficient mapping strategy. (The method does not assume that the spin axis remains fixed.)
We must convert the Fourier transform from rectangular coordinates (x,y) to polar coordinates ( in map space, and (u,v) to in visibility space:
But the integral over the radial wavenumber k becomes a sum over the number of subcollimators (and possibly their harmonics), because the visibility is sampled on circles:
The above integral is a convolution, and therefore intrinsically faster to compute and more compact for computation than equation (1). This in a nutshell explains why Polar Mapping for HESSI is more efficient than rectangular mapping: the integrals are one-dimensional, and if the samples of visibility are equally spaced on the (u,v) circles (impossible in rectangular coordinates), FFT methods may be used in an efficient manner.
;+ ; NAME: polar_mapper ; ; PURPOSE: ; MAKES A POLAR MAP FROM A CALIBRATED EVENT LIST ; ; USAGE: ; 1. Make a model map and call sim_calib_eventlist ; 2. Convert the counts to visibilities using calibevents2vis ; 4. Get the aspect structure for the same time bins ; 5. Shift the visibility phases to sun center ; 6. Make the polar map with vis2pmap ; ; INPUTS: ; det_index = scalar, 0-27 ; ce = calibrated event list structure ; xy_offset = [x,y] vector of inertial coords of map center (arcsec) ; /plot Set this if you want plots and images ; ; OUTPUTS: ; dmap = radius and azimuth map ; dmap_xy = x,y projection (annulus) of dmap ; ; EXAMPLE: ;; 1. MAKE THE MODEL MAP ; model=fltarr(64,64) ; model(31,11)=1 ; ;; 2. CREATE THE CALIBRATED EVENT LIST for the selected det_index ; det_index=5 ; xy_offset=[650,200] ; sim_xy_offset=xy_offset+25 ; sim_photons=2000 ; det_mask=intarr(27) & det_mask(det_index)=1 ; bin_size=2L^12 ; microseconds (this is ok except for det_index=0) ; num_bins=(2L^22)/bin_size ; time_range=[0,4] ; If this is changed, polar_mapper won't work ; o = obj_new( 'hsi_calib_eventlist') ; timebindef = o->get( /time_bin_def ) ; get the default array ; timebindef(det_index)=bin_size ; ce = o->getcalibeventlist(det_index, SIM_TIME_range=time_range, $ ; TIME_RANGE=time_range, SIM_PHOT=sim_photons, $ ; A2D_INDEX_MASK=det_mask,TIME_BIN_DEF=timebindef,SIM_MODEL=model, $ ; SIM_XYOFFSET=sim_xy_offset, XYOFFSET=xy_offset) ;,/saszero ;; 3. Call the mapper: ; dmap=polar_mapper(det_index,ce,xy_offset,/plot) ; ; ; RESTRICTIONS: ; Works only for time_range=[0,4] and for a single subcollimator. ; If the map position is several pitches away from the flare centroid, ; the map will be poor. ; ; CALLS: ; hsi_grid_parameters ; calibevents2vis, (converts counts to visibilities) ; ls_sin_cos ; ls_amp_pha_ctr ; vis2pmap, which makes a polar map calling: ; convl (the key to speed) ; pmap2xy (interpolates the polar map onto xy coordinates) ; ; VERSION HISTORY: ; VERS. 1 ; Revised to match compute_model_ce.pro Sept 17, 1999 ; VERS. 2 Sept. 27, 1999 -- EJS ; Various bugs squashed ; Forced visibility to be Hermitian ; VERS. 3 ; Replaced Bessel-fcn array with FFTs ; Added CONVL program for fast convolution (extends convolve.pro). ; Made to work with an externally-produced calib_eventlist ; Lots of bugs remain. Oct 26, 1999 -- EJS ; ; NOTES: ; The grid_angle is used when correcting for aspect, but not in ; mapping, so the azimuth of the source comes out wrong. ; No general times yet: We extract only the first 1/2 of 1 ; rotation from an entire rotation. ; ; Maps are in the wrong hemisphere (S instead of N), probably ; the result of negatively increasing roll angles. ; There is something suspicious about the first dark ring in array ; dmap_xy -- the scaling may be wrong ; ;- function polar_mapper,det_index,ce,xy_offset,plot=plot ; ; ; RESTRICTIONS: ; WORKS ONLY FOR TIMERANGE=[0,4] ; pitch=((hsi_grid_parameters()).pitch)(det_index) grid_angle=((hsi_grid_parameters()).orient)(det_index) if keyword_set(plot) then plot,ce.dx,ce.dy,tit='ASPECT VALUES' ; CONVERT THE COUNTS TO VISIBILITIES: message,'Running calibevents2vis',/cont vis0=calibevents2vis(det_index,ce, cycle_factor=2) ; best cycle_factor ; GET THE PHASE FACTOR FOR MAP PHASE CENTER alpha=2*!pi*((ce.dx)*cos(grid_angle)+(ce.dy)*sin(grid_angle))/pitch ; there may need to be a sign change ; SHIFT THE VISIBILITY PHASES TO SUN CENTER: i=complex(0,1) vis1=vis0*exp(-i*(alpha)) ; This may be the wrong place to do this. ; ; Probably it should be done in calibevents2vis ; HERMITIANIZE THE VISIBILITIES -- USE ONLY TIMERANGE=[0,2] SECONDS vis=vis1 num_bins=n_elements(vis) vis(num_bins/2:num_bins-1)=conj(vis(0:num_bins/2-1)) if keyword_set(plot) then BEGIN !p.multi=[0,1,2] window,4,xsize=1000,ysize=400 plot,ce.roll_angle,ce.count,xsty=1,tit='COUNT RATE',xtit='ROLL ANGLE (radians) plot,ce.roll_angle,vis0,tit='Visibility (vis0)',xsty=1,xtit='ROLL ANGLE (radians) !p.multi=0 wset,0 endif ; THEN CREATE AN ANNULAR DIRTY MAP: mapwidth=256 dmap_xy=1 message,'Calling vis2pmap...',/continue if keyword_set(plot) then begin message,'Calling vis2pmap with /plot set',/cont dmap=vis2pmap(vis,ce,det_index,mapwidth ,dmap_xy,/plot) endif else begin message,'Calling vis2pmap with /plot NOT set',/cont dmap=vis2pmap(vis,ce,det_index,mapwidth ,dmap_xy) endelse return,dmap end
function vis2pmap,vis,ce,det_index,map_width,dmap_xy,plot=plot ;+ ; NAME: VIS2PMAP ; ; PURPOSE: Converts visibilities into a polar coordinate map ; ; ; ; METHOD: ; Compute the convolution of visibility and exp(i*k*r*cos(roll_angle)) ; ; ; INPUTS: ; vis=complex array of N_vis values for phi=0 to 2*!pi ; Note that the vis must be computed with phase center at the spin axis. ; ce=calib_eventlist structure ; det_index=0-27 ; map_width = scalar width (and height) of map to be made ; ; OUTPUTS: ; dmap(j_r,n_theta) = "dirty" map ; Lots of plots if optional keyword plot is set ; Optional return of rectangular map if dmap_xy exists ; ; EXAMPLE: ; N=512 ; roll_angle=findgen(N)*2*!pi/N ; det_index=4 ; pitch=((hsi_grid_parameters()).pitch)(det_index) ; i=complex(0,1) ; vis=100*exp(i*2*!pi*605.*cos(roll_angle)/pitch) ; count=100+float(vis) ; phase_map_ctr=2*!pi*600.*cos(roll_angle)/pitch ; ce={roll_angle:roll_angle,count:count,phase_map_ctr:phase_map_ctr} ; map_width=200 ; dmap=vis2pmap(vis,ce,det_index,map_width,/plot) ; ; ; FUNCTIONS CALLED: ; convl.pro ; ; RESTRICTIONS: ; Makes only annular maps at present ; The visibilities must have been phase shifted to the polar center ; ; VERSION HISTORY: ; VERS. 1 EJS Mar 29, 1999 ; VERS. 2 EJS Sept 28, 1999 ; Doubled width of annulus, increased final map to 360-deg coverage. ; VERS 3 EJS Oct 15, 1999 ; Replaced spin radius by a call to phase2radius ; fixed an error in r2 ; VERS 4 EJS Oct 26, 1999 ; Changed Bessel function calls to convl calls. ; NOTES: ;- ; function vis2pmap,vis,ce,det_index,map_width,dmap_xy,plot=plot message,'Beginning...',/continue !p.multi=[0,1,2] ; 1. Look at the visibilities and compute the max spatial freq therein. print,'Look at the visibilities and compute the max spatial freq therein.' ; roll_angle=ce.roll_angle phase_map_ctr=ce.phase_map_ctr sigma=-1. radius=phase2radius(ce,det_index,sigma) message,'Deduced spin radius from phase_map_ctr:='+string(radius),/cont print,'sigma=',sigma r0=fix(radius+0.5) ; radius of the phase center r1=(r0-map_width)>1 & r2=r1+2*map_width ; min and max radii to be mapped if (r1 LE 0) then begin message,'r1 LE 0' endif phi1=-!pi & phi2=!pi ; min and max azimuths to be mapped if (keyword_set(plot)) then begin plot,float(vis),xsty=1,tit='Real Part of Visibilities' plot,imaginary(vis),xsty=1,tit='Imaginary Part of Visibilities' endif pitch=((hsi_grid_parameters()).pitch)(det_index) if (max(abs(vis)) LT .0001) then message,'All visibilities are essentially 0' Nroll=n_elements(roll_angle) k=2*!pi/pitch r=r1+findgen(r2-r1) kr=k*r i=complex(0,1) dmap=fltarr(Nroll,r2-r1) message,'Doing'+string(r2-r1)+' radii...',/cont for j=0,r2-r1-1 do begin v=exp(-i*kr[j]*cos(roll_angle)) ; if ((j mod 10) eq 0) then plot,v,tit='r='+string(j+r1),xsty=1 ; wait,0.1 d=convl(v,vis) dmap[*,r[j]-r1]=d ; 1st arg is azimuth, 2nd is radius endfor if (keyword_set(plot)) then begin erase ndx=n_elements(dmap(*,0)) & ndy=n_elements(dmap(0,*)) window,1,xsize=ndx,ysize=ndy,title='Real Part of DMAP' print,'Pausing for 1 sec..' & wait,1 erase tvscl,dmap endif ; Modified Oct 26, 99 message,'Calling pmap2xy.pro...' ,/continue ; dmap: 1st index is azimuth (0 to 2*!pi), 2nd is radius dmap_xy=pmap2xy(dmap,r1,r2) message,'r1,r0,r2='+string(r1)+string(r0)+string(r2),/continue if (keyword_set(plot)) then begin if (!d.name NE 'PS') then window,0,xsize=960,ysize=960 tvscl,dmap_xy endif message,'Done. Returning',/cont return,dmap end
function pmap2xy,polmap,r1,r2 ; NAME pmap2xy ; ; PURPOSE: ; Finds the x,y points inside an annulus of radii r1, r2, converts ; to polar coordinates and uses interpolation to extract values from ; a polar map polmap of size ( ; INPUTS: ; polmap: ; 1st index is azimuth (0 to 360 degrees) ; 2nd index of polmap is radius (asec) ; r1,r2 = integer values of inner and outer radii covered by pol ; ; OUTPUT: ; a square map of size 960 x 960 with 2 asec pixels ;- x=findgen(960)#replicate(1,960) y=transpose(x) r=2*sqrt((x-479.5)^2+(y-479.5)^2) n_az=n_elements(polmap(*,0)) i_radius=r-r1 j_azimuth=(n_az/2)*(1-atan((y-479.5),(x-479.5))/!pi) ;j_azimuth=(n_az/2)*((2+atan((y-479.5),(x-479.5))/!pi) mod 2) polmap=float(polmap) d=interpolate(polmap,j_azimuth,i_radius,missing=0) return,d end
;+ ; NAME: ls_amp_pha_ctr.pro ; ; PURPOSE: Returns the least-square fit of y(angle) to the function ; A*cos(phase_map_ctr(angle))+B*sin(phase_map_ctr(angle)) ; or equivalently, AMP*cos(phase_map_ctr(angle)-PHA). ; The result can be used to fit visibilities to HESSI ; countrates, even in the ranges of slow modulation. ; ; METHOD: ; Theta=phase_map_ctr ; Minimize the total squared deviation H of the fit to ; y[i] = A*cos(Theta[i])+B*sin(Theta[i])) ; ; H=total{ [y - A*cos(Theta) - B*sin(Theta)]^2 } ; ; If the angle[i] are chosen to make the cos and sin terms uncorrelated ; then the regression matrix is diagonal. If, they are well correlated ; or nearly anti-correlated, the matrix is singular, and the method fails. ; The results may be meaningless unless r_p has been ; chosen such that phi[angle] ranges over a large fraction of [0,2*!pi]. ; ; INPUTS: ; y=modulation profile (countrates), one for each angle ; angle=vector of angles (radians), possibly irregular ; phase_map_ctr=Array of phases of map center relative to line ; of maximum response, one for each roll angle ; ; OUTPUTS: ; [A,B] cos,sin amplitudes, OR ; [amplitude,phase] if optional amp_pha is set, ; If sigma exists as an argument, the RMS deviation of the fit ; is returned in sigma. ; If y has only 2 elements, sigma is necessarily 0 (exact fit). ; If yfit is a defined argument, fit to y will be returned in it. ; ; EXAMPLE: ; ;Simple simulation of map-center phases ; ;Create an array of angles: ; N=512 ; angle=findgen(N)*!pi/N - !pi/4 ; ;Create some aspect coords: ; x_asp=1.0*randomn(seed,N) ; y_asp=1.0*randomn(seed,N) ; ;Select an inertial map center: ; X_map=600. ; Y_map=200. ; ;Compute the spacecraft coords of map center: ; x_map_sc = x_asp + X_map*cos(angle) - Y_map*sin(angle) ; y_map_sc = y_asp + Y_map*cos(angle) + X_map*sin(angle) ; ;Choose a value for the peak_resp_offset (normally from hessi_grm): ; peak_resp_offset=!pi/6 ; ;Pick a sub_collimator: ; ang_pitch=39.45 & det_index=4 ; grid_angle=0. & h=1 ; ;Now make the array of map-center phases: ; ; phase_map_ctr=2*!pi*h*(x_map_sc*cos(grid_angle)+y_map_sc*sin(grid_angle)$ ; +peak_resp_offset)/ang_pitch ; ;Select a range of indices: ; inx=180+findgen(20) ; y=cos(phase_map_ctr)+0.25*randomn(seed,N) ; z=ls_amppha_pmc(angle(inx),y(inx),phase_map_ctr(inx),yfit,sig) ; plot,angle,y,psym=10,xsty=1 & oplot,angle(inx),yfit,thick=3 ; ; RESTRICTIONS: ; If the data set is enormous, this is slow. ; For a way to speed it up, ; see Press and Rybicki, Ap.J., 338, 277, 1989. ; ; angle must =0 or !pi where the modulation is slowest, and ; angle must= +!pi/2 or -!pi/2 where modulation is fastest ; ; REVISION HISTORY: ; Version 1.0, Derived from ls_coscos, EJS, June 15, 1999. ; Version 2, Added double and bad-fit flag, EJS Sept 30, 1999 ; NOTE: if the sine vector is nearly constant, a bad fit results. ; This is because the sine is at an extremum and the cosine is ; very small, so it has to cover all the y variability, and its ; coefficient will be large. (Similarly if cosine = const.) ;- function ls_amp_pha_ctr,angl,y,phase_map_ctr,yfit,sigma, $ AMP_PHA=amp_pha,verb=verb,double=double,flag=flag ; message,'stopping' ; Compute the sine and cosine arrays if keyword_set(double) then begin if keyword_set(verb) then message,'Going to double precision',/continue phase_map_ctr=double(phase_map_ctr) endif cosine=cos(phase_map_ctr) sine=sin(phase_map_ctr) if (keyword_set(verb)) then message, $ 'Working with '+String(n_elements(phi))+' angles',/cont ; The regression matrix m00=total(cosine^2) & m01=total(sine*cosine) m11= total(sine^2) del=m00*m11-m01^2 if abs(del) lt 1.e-2*(abs(m00)+abs(m11)) then begin message,'Ill-conditioned LS fit.',/continue ;message,string(del)+string(m00)+string(m01)+string(m11),/continue flag=1 endif c0=total(y*cosine) c1=total(y*sine) A=(c0*m11-c1*m01)/del ; Solution to M # [A,B] = [c0,c1] B=(c1*m00-c0*m01)/del AB=[A,B] yfit=A*cosine+B*sine ; = amp*cos(phase_map_ctr-pha) ; compute the goodness of fit if (n_elements(sigma) GT 0) then sigma=sqrt(variance(y-yfit)) if keyword_set(amp_pha) then AB=[sqrt(a^2+b^2),atan(B,A)] return,AB end
function phase2radius,ce,det_index,rmsdev ;+ ; NAME: phase2radius ; ; PURPOSE: ; ; Finds the average spin radius from the pitch and the ; calib_eventlist tags .phase_map_center and .roll_angle ; for detector det_index ; ; INPUTS: ; ce=calib_eventlist structure ; det_index= scalar (0-27) ; ; OUTPUTS: ; radius = scalar FLOAT ; ; VERSION HISTORY: ; Upgraded inputs to structure ce ; EJS Oct. 26, 1999 ;- pitch=((hsi_grid_parameters()).pitch)(det_index mod 9) if n_elements(rmsdev) NE 0 then rmsdev=-1.0 yfit=1. z=ls_sin_cos(ce.roll_angle,ce.phase_map_ctr,1,yfit,rmsdev) radius=sqrt(z(0)^2+z(1)^2)*pitch/(2*!pi) return,radius end
FUNCTION CONVL,A,P ;+ ; PURPOSE: ; IDL routine to convolve array A with a point spread function P ; ; CALLING SEQUENCE: C=CONVL(A,P) ; ; INPUTS: ; A = map vector or array (REAL OR COMPLEX) ; P = PSF vector or array, same size as A ; ; OUTPUT: ; Convolved matrix or vector, shifted to make the origin the same ; as A. If P is 1 at array center, C=A ; ; RESTRICTIONS: ; A and P must be vectors or matrices of the same size, dimension LE 2 ; ; NOTES: ; The astron program "convolve" does not allow input arrays of ; equal size, so this program is complementary to it. ; With this function, ; total(C)=total(A)*total(P) ; convl(A,P)=convl(P,A) ; convl(A,P1+P2)=convl(A,P1)+convl(A,P2) ; ; VERSION HISTORY: ; VERS. 2.0 OCTOBER 25, 1999, EJS ; ;- sz_a=size(a) & sz_p=size(p) if (sz_a(0) NE sz_p(0)) then message,"Input arrays don't have same dimensions" if (sz_a(0) eq 1) then $ if (sz_a(1) NE sz_p(1)) then message,'Input vectors have different sizes' if (sz_a(0) eq 2) then begin if ( (sz_a(1) NE sz_p(1)) OR (sz_a(2) NE sz_p(2)) ) then $ message,'Input arrays have different sizes' n1=sz_a(1) & n2=sz_a(2) endif if (sz_a(0) gt 2) then message,'Arrays of dimension 3 or higher not supported' c=float(fft(fft(a,1)*fft(p,1),-1) ) if sz_a(0) eq 1 then c=shift(c,sz_a(1)/2) $ else c=shift(c,sz_a(1)/2,sz_a(2)/2) return,c end
Edward J. Schmahl Last modified: Nov 8 09:55 EDT 1999