source: beamsgenerator.py@ 4

Last change on this file since 4 was 4, checked in by JohnLightfoot, 10 years ago

initial import

File size: 6.9 KB
Line 
1from __future__ import absolute_import
2
3import collections
4import numpy as np
5
6
7class Axis(object):
8 """Class to hold axis information.
9 """
10
11 def __init__(self, data, title='', units=''):
12 self.data = data
13 self.title = title
14 self.units = units
15
16
17class Image(object):
18 """Class to hold 2d image, with axes, units, title, etc.
19 """
20 def __init__(self, data, axes=[], title='', units=''):
21 self.data = data
22 self.axes = axes
23 self.title = title
24 self.units = units
25
26
27class BeamsGenerator(object):
28 """Class to generate the primary beam(s) of the simulated observation.
29 """
30
31 def __init__(self, previous_results):
32 self.previous_results = previous_results
33 self.result = collections.OrderedDict()
34
35 def run(self):
36 print 'BeamsGenerator.run'
37
38 uvmapgen = self.previous_results['uvmapgenerator']
39 fts = self.previous_results['fts']
40 telescope = self.previous_results['loadparameters']['substages']\
41 ['Telescope']
42
43 # number of mirror positions sampled by FTS
44 nsample = fts['ftsnsample']
45 fts_wn = fts['fts_wn']
46 fts_lambda = fts['fts_lambda']
47
48 # max pixel size (radians) that will fully sample the image.
49 # Sampling freq is 2 * Nyquist freq. So sampling freq = 2 * b_max / lambda_min.
50 bmax = uvmapgen['bmax']
51 self.result['max baseline [m]'] = bmax
52 max_pixsize = np.min(fts_lambda) / (2.0 * bmax)
53 self.result['max pixsize [rad]'] = max_pixsize
54
55 # Number of pixels per beam
56 # radius of first null of Airy disk is theta=1.22lambda/d. Number of
57 # pixels is beam diameter/max_pixsize. Calculate this for the
58 # longest wavelength, largest beam
59 max_beam_radius = 1.22 * np.max(fts_lambda) /\
60 telescope['Primary mirror diameter']
61 self.result['beam diam [rad]'] = 2.0 * max_beam_radius
62
63 # go out to radius of first null
64 rpix = int(max_beam_radius / max_pixsize)
65# rpix = 32
66 npix = 2 * rpix
67 self.result['npix'] = npix
68
69 # spatial axes same for all wavelengths
70 axis = np.arange(-rpix, rpix, dtype=np.float)
71 axis *= max_pixsize
72 axis *= 206264.806247
73 self.result['spatial axis [arcsec]'] = axis
74
75 # calculate beams for each point on fts_wn
76 # oversample uv grid by mult to minimise aliasing
77 mult = 5
78 mx,my = np.meshgrid(np.arange(-mult*rpix, mult*rpix),
79 np.arange(-mult*rpix, mult*rpix))
80 self.result['primary beam'] = collections.OrderedDict()
81 self.result['primary amplitude beam'] = collections.OrderedDict()
82 self.result['dirty beam'] = collections.OrderedDict()
83
84 for wn in fts_wn:
85 # first calculate primary beam
86 #
87 # assume uniform illumination of primary. uv goes out to
88 # +- bmax / lambda_min, fill uv plane appropriate to primary
89 # size
90 mirror = np.ones([mult*npix,mult*npix])
91 lambda_min = 1.0 / (np.max(fts_wn) * 100.0)
92 lamb = 1.0 / (wn * 100.0)
93 mirror[(np.sqrt(mx*mx + my*my) / (mult*rpix)) *
94 (bmax / lambda_min) > \
95 (0.5 * telescope['Primary mirror diameter'] / lamb)] = 0.0
96
97 # fftshift mirror to be centred at [0,0],
98 # do a 2d ftt of it,
99 # fftshift transform so that 0 freq is in centre of array
100 temp = np.fft.fftshift(mirror)
101 temp = np.fft.fft2(temp)
102 temp = np.fft.fftshift(temp)
103 # convert to real, should be real anyway as FT of symmetric function
104 # square to give intensity response
105 primary_amplitude_beam = np.real(temp)
106 primary_intensity_beam = np.power(np.abs(temp), 2)
107 # and normalise
108 primary_amplitude_beam /= np.max(primary_amplitude_beam)
109 primary_intensity_beam /= np.max(primary_intensity_beam)
110
111 # truncate primary beam pattern
112 primary_amplitude_beam = primary_amplitude_beam[
113 (mult-1)*rpix:(mult+1)*rpix,
114 (mult-1)*rpix:(mult+1)*rpix]
115 primary_intensity_beam = primary_intensity_beam[
116 (mult-1)*rpix:(mult+1)*rpix,
117 (mult-1)*rpix:(mult+1)*rpix]
118
119 # axes
120 axis1 = Axis(data=-axis, title='RA offset', units='arcsec')
121 axis2 = Axis(data=axis, title='Dec offset', units='arcsec')
122 image = Image(data=primary_intensity_beam, axes=[axis1, axis2],
123 title='Primary Beam %06.4g cm-1' % wn)
124 self.result['primary beam'][wn] = image
125
126 image = Image(data=primary_amplitude_beam, axes=[axis1, axis2],
127 title='Amplitude Primary Beam %06.4g cm-1' % wn)
128 self.result['primary amplitude beam'][wn] = image
129
130 # second calculate dirty beam
131 #
132 # 1. uv plane covered by discrete points so should use a
133 # slow dft rather than fft, which requires evenly spaced
134 # samples. Perhaps a bit slow though as calcs done in Python
135 # layer.
136 # 2. Could use shift theorem to give even-spaced equivalent
137 # of each discrete point, then use ffts. Equivalent to 1
138 # in speed.
139 # 3. Oversample uv plane and accept small error in point
140 # positions. Fastest but adequate?
141
142 uv = np.zeros([mult*npix,mult*npix])
143 maxbx = bmax / np.min(fts_lambda)
144 for bxby in uvmapgen['bxby']:
145 ibx = int(round(((bxby[0] / lamb) / maxbx) * mult * rpix))
146 if ibx < 0:
147 ibx = mult*npix + ibx
148 iby = int(round(((bxby[1] / lamb) / maxbx) * mult * rpix))
149 if iby < 0:
150 iby = mult*npix + iby
151 uv[ibx,iby] = 1.0
152
153 temp = np.fft.fftshift(uv)
154 temp = np.fft.fft2(temp)
155 temp = np.fft.fftshift(temp)
156 temp = np.power(temp, 2)
157 # convert to real, should be real anway as FT of symmetric function
158 dirty_beam = np.abs(temp)
159 # and normalise
160 dirty_beam /= np.max(dirty_beam)
161
162 # truncate dirty beam pattern
163 dirty_beam = dirty_beam[
164 (mult-1)*rpix:(mult+1)*rpix,
165 (mult-1)*rpix:(mult+1)*rpix]
166
167 # axes
168 axis = np.arange(-rpix, rpix, dtype=np.float)
169 axis *= (np.min(fts_lambda) / bmax)
170 axis *= 206264.806247
171 axis1 = Axis(data=-axis, title='RA offset', units='arcsec')
172 axis2 = Axis(data=axis, title='Dec offset', units='arcsec')
173
174 image = Image(data=dirty_beam, axes=[axis1, axis2],
175 title='Dirty Beam %06.4g cm-1' % wn)
176
177 self.result['dirty beam'][wn] = image
178
179 return self.result
180
181 def __repr__(self):
182 return 'PrimaryBeamGenerator'
183
Note: See TracBrowser for help on using the repository browser.