Line data Source code
1 : // Based on code/alma/apps/UVWCoords
2 :
3 : #include <casacore/casa/Logging/LogIO.h>
4 : #include <msvis/MSVis/MSUVWGenerator.h>
5 : #include <casacore/measures/Measures/MEpoch.h>
6 : #include <casacore/measures/Measures/MFrequency.h>
7 : #include <casacore/measures/Measures/MPosition.h>
8 : #include <casacore/ms/MeasurementSets/MSColumns.h>
9 : #include <casacore/ms/MeasurementSets/MSAntennaColumns.h>
10 : #include <casacore/measures/Measures/MCBaseline.h>
11 : #include <casacore/casa/Utilities/GenSort.h>
12 : #include <casacore/casa/Utilities/CountedPtr.h>
13 : #include <map>
14 :
15 : using namespace casacore;
16 : namespace casa {
17 :
18 : // The UvwCoords ctor has lines for the antennas, antenna offsets, and station
19 : // positions. This ctor assumes they're present in msc_p if present at all.
20 16 : MSUVWGenerator::MSUVWGenerator(MSColumns &msc_ref, const MBaseline::Types bltype,
21 16 : const Muvw::Types) :
22 16 : msc_p(msc_ref),
23 16 : bl_csys_p(MBaseline::Ref(bltype)), // MBaseline::J2000, ITRF, etc.
24 16 : antColumns_p(msc_p.antenna()),
25 16 : antPositions_p(antColumns_p.positionMeas()),
26 16 : antOffset_p(antColumns_p.offsetMeas()),
27 16 : refpos_p(antPositions_p(0)), // We use the first antenna for the reference
28 16 : feedOffset_p(msc_p.feed().positionMeas())
29 : {
30 : // It seems that using a String is the only safe way to go from say,
31 : // MPosition::ITRF to MBaseline::ITRF.
32 16 : MBaseline::getType(refposref_p,
33 32 : MPosition::showType(refpos_p.getRef().getType()));
34 :
35 16 : fill_bl_an(bl_an_p);
36 16 : }
37 :
38 16 : MSUVWGenerator::~MSUVWGenerator(){
39 16 : }
40 :
41 16 : void MSUVWGenerator::fill_bl_an(Vector<MVBaseline>& bl_an_p)
42 : {
43 16 : nant_p = antPositions_p.table().nrow();
44 16 : logSink() << LogIO::DEBUG1 << "nant_p: " << nant_p << LogIO::POST;
45 :
46 16 : Double max_baseline = -1.0;
47 : Double bl_len;
48 :
49 16 : const ScalarColumn<Double>& antDiams = antColumns_p.dishDiameter();
50 16 : Double smallestDiam = antDiams(0);
51 16 : Double secondSmallestDiam = antDiams(0);
52 :
53 16 : bl_an_p.resize(nant_p);
54 446 : for(uInt an = 0; an < nant_p; ++an){
55 : // MVBaselines are basically xyz Vectors, not Measures.
56 430 : bl_an_p[an] = MVBaseline(refpos_p.getValue(), antPositions_p(an).getValue());
57 :
58 : // MVBaseline has functions to return the length, but Manhattan distances
59 : // are good enough for this, and faster than a sqrt.
60 430 : Vector<Double> bluvw(bl_an_p[an].getValue());
61 430 : bl_len = fabs(bluvw[0]) + fabs(bluvw[1]) + fabs(bluvw[2]);
62 :
63 430 : if(bl_len > max_baseline)
64 81 : max_baseline = bl_len;
65 :
66 430 : if(antDiams(an) < secondSmallestDiam){
67 0 : if(antDiams(an) < smallestDiam){
68 0 : secondSmallestDiam = smallestDiam;
69 0 : smallestDiam = antDiams(an);
70 : }
71 : else
72 0 : secondSmallestDiam = antDiams(an);
73 : }
74 430 : }
75 :
76 : // Setting timeRes_p to 0.0025 * the time for a 1 radian phase change on the
77 : // longest baseline at 2x the primary beamwidth should be sufficiently short
78 : // for Earth based observations. Space-based baselines will move faster, but
79 : // probably don't have the data rate to support full beam imaging. An
80 : // alternative limit could come from |d(uvw)/dt|/|uvw|, but that is
81 : // guaranteed to be at least somewhat larger than this limit (replace the
82 : // 2.44 with 2, and remove the dish diameters and max_baseline).
83 : //
84 : // Do not raise the 0.0025 coefficient by too much, since the times used for
85 : // UVW calculation could be biased by as much as -0.5 * timeRes_p if more
86 : // than one integration fits into timeRes_p. timeRes_p is not intended so
87 : // much for skipping calculations as it is for preventing antUVW_p from being
88 : // calculated for each row in the same integration. The integration interval
89 : // may change within an MS, but ideally antUVW_p is calculated once per
90 : // integration (timeRes_p <~ integration) and there is no bias.
91 16 : timeRes_p = 0.0025 * 24.0 * 3600.0 / (6.283 * 2.44) *
92 16 : sqrt(smallestDiam * secondSmallestDiam) / max_baseline;
93 16 : }
94 :
95 5139 : void MSUVWGenerator::uvw_an(const MEpoch& timeCentroid, const Int fldID,
96 : const Bool WSRTConvention)
97 : {
98 5139 : const MDirection& phasedir = msc_p.field().phaseDirMeas(fldID);
99 5139 : MeasFrame measFrame(refpos_p, timeCentroid, phasedir);
100 5139 : MVBaseline mvbl;
101 5139 : MBaseline basMeas;
102 :
103 : //logSink() << LogIO::DEBUG1
104 : // << "timeCentroid: " << timeCentroid
105 : // << "\nfldID: " << fldID
106 : // << "\nphasedir: " << phasedir
107 : // << LogIO::POST;
108 :
109 5139 : MBaseline::Ref basref(refposref_p);
110 5139 : basMeas.set(mvbl, basref); // in antenna frame
111 5139 : basMeas.getRefPtr()->set(measFrame);
112 :
113 : // Setup a machine for converting a baseline vector from the antenna frame to
114 : // bl_csys_p's frame
115 5139 : MBaseline::Convert elconv(basMeas, bl_csys_p);
116 :
117 74475 : for(uInt i = 0; i < nant_p; ++i){
118 : //TODO: (Soon!) Antenna offsets are not handled yet.
119 69336 : basMeas.set(bl_an_p[i], basref);
120 69336 : MBaseline basOutFrame = elconv(basMeas);
121 : //MBaseline::Types botype = MBaseline::castType(basOutFrame.getRef().getType());
122 69336 : MVuvw uvwOutFrame(basOutFrame.getValue(), phasedir.getValue());
123 :
124 69336 : antUVW_p[i] = uvwOutFrame.getValue();
125 69336 : }
126 :
127 : // Tony Willis supplied this crucial bit of history: the WSRT definition of
128 : // phase is opposite to that of the VLA - the WSRT definition of the l,m
129 : // direction cosines is that l increases toward decreasing RA, whereas the
130 : // VLA definition is that l increases toward increasing RA. AIPS seems to
131 : // understand this. (For a completely useless piece of trivia, the WSRT defn
132 : // of phase is consistent with that of the Cambridge one-mile telescope as
133 : // Wim Brouw set things up to be in agreement with the one-mile telescope ca
134 : // 1968.)
135 : // RR: It is not just l, v and w are flipped as well.
136 5139 : if(WSRTConvention)
137 0 : antUVW_p = -antUVW_p;
138 5139 : }
139 :
140 : // antUVW_p must be set up for the correct timeCentroid and phase direction by
141 : // uvw_an() before calling this.
142 0 : void MSUVWGenerator::uvw_bl(const uInt ant1, const uInt,// feed1,
143 : const uInt ant2, const uInt,// feed2,
144 : Array<Double>& uvw)
145 : {
146 : //uvw.resize(3); // Probably redundant. Does it significantly slow things down?
147 : //TODO: Feed offsets are not handled yet.
148 0 : uvw = antUVW_p[ant2] - antUVW_p[ant1];
149 0 : }
150 :
151 16 : Bool MSUVWGenerator::make_uvws(const Vector<Int> flds)
152 : {
153 16 : ArrayColumn<Double>& UVWcol = msc_p.uvw();
154 16 : const ScalarMeasColumn<MEpoch>& timeCentMeas = msc_p.timeCentroidMeas();
155 16 : const ScalarColumn<Int> fieldID(msc_p.fieldId());
156 16 : const ScalarColumn<Int> ant1(msc_p.antenna1());
157 16 : const ScalarColumn<Int> ant2(msc_p.antenna2());
158 16 : const ScalarColumn<Int> feed1(msc_p.feed1());
159 16 : const ScalarColumn<Int> feed2(msc_p.feed2());
160 16 : const ScalarColumn<Int> obsID(msc_p.observationId());
161 :
162 : // Use a time ordered index to minimize the number of calls to uvw_an.
163 : // TODO: use field as a secondary sort key.
164 16 : Vector<uInt> tOI;
165 16 : GenSortIndirect<Double>::sort(tOI, msc_p.timeCentroid().getColumn());
166 :
167 : // Having uvw_an() calculate positions for each antenna for every field is
168 : // somewhat inefficient since in a multiconfig MS not all antennas will be
169 : // used in each time interval, but it's not clear that determining which
170 : // antennas will be used for a given uvw_an() call would be any more
171 : // efficient. It's not horribly inefficient, because uvw_an() is O(nant_p),
172 : // and uvw_bl() is only called for baselines that are actually used.
173 16 : antUVW_p.resize(nant_p);
174 :
175 16 : logSink() << LogOrigin("MSUVWGenerator", "make_uvws") << LogIO::NORMAL3;
176 :
177 16 : logSink() << LogIO::DEBUG1 << "timeRes_p: " << timeRes_p << LogIO::POST;
178 :
179 : try{
180 16 : Bool oldWsrtConvention = (msc_p.observation().telescopeName()(obsID(tOI[0])) ==
181 : "WSRT");
182 :
183 : // Ensure a call to uvw_an on the 1st iteration.
184 16 : const Unit sec("s");
185 16 : Double oldTime = timeCentMeas(tOI[0]).get(sec).getValue() - 2.0 * timeRes_p;
186 16 : Int oldFld = -2;
187 :
188 : // IO reordering buffer to avoid poor IO patterns on files not sorted by time
189 16 : std::map<uInt, Vector<Double> > writebuffer;
190 :
191 8430040 : for(uInt row = 0; row < msc_p.nrow(); ++row){
192 8430024 : uInt toir = tOI[row];
193 8430024 : const MEpoch & timecentmeas = timeCentMeas(toir);
194 8430024 : Double currTime = timecentmeas.get(sec).getValue();
195 8430024 : Int currFld = fieldID(toir);
196 8430024 : Bool newWsrtConvention = (msc_p.observation().telescopeName()(obsID(toir)) ==
197 : "WSRT");
198 8430024 : if ((row % (1<<22)) == 0) {
199 : logSink() << LogIO::NORMAL << "Rows handled: "
200 16 : << row << "/" << msc_p.nrow() << LogIO::POST;
201 : }
202 :
203 8430024 : if(currTime - oldTime > timeRes_p || currFld != oldFld
204 8424885 : || newWsrtConvention != oldWsrtConvention){
205 5139 : oldTime = currTime;
206 5139 : oldFld = currFld;
207 :
208 5139 : if(newWsrtConvention != oldWsrtConvention){
209 : logSink() << LogIO::WARN
210 : << "There is a switch to or from the WSRT convention at row "
211 : << toir << ".\nWatch for an inconsistency in the sign of UVW."
212 0 : << LogIO::POST;
213 0 : oldWsrtConvention = newWsrtConvention;
214 : }
215 :
216 : //logSink() << LogIO::DEBUG1 << "currTime: " << currTime
217 : // << "\ncurrFld: " << currFld << LogIO::POST;
218 5139 : uvw_an(timecentmeas, currFld, newWsrtConvention);
219 : }
220 :
221 8430024 : if(flds[fieldID(toir)] > -1){
222 : // uvw_bl(ant1(toir), ant2(toir),
223 : // feed1(toir), feed2(toir), UVWcol(toir));
224 : // collect output and write larger chunk in row sequential order
225 8188548 : writebuffer[toir] = antUVW_p[ant2(toir)] - antUVW_p[ant1(toir)];
226 8188548 : if (writebuffer.size() > 100000) {
227 7900158 : for (auto it = writebuffer.begin(); it != writebuffer.end(); ++it) {
228 7900079 : UVWcol.put(it->first, it->second);
229 : }
230 79 : writebuffer.clear();
231 : }
232 : }
233 8430024 : }
234 : // flush rest of buffer
235 288485 : for (auto it = writebuffer.begin(); it != writebuffer.end(); ++it) {
236 288469 : UVWcol.put(it->first, it->second);
237 : }
238 16 : }
239 0 : catch(AipsError x){
240 : logSink() << LogIO::SEVERE << "Caught exception: " << x.getMesg()
241 0 : << LogIO::POST;
242 0 : throw(AipsError("Error in MSUVWGenerator::make_uvws."));
243 : return false;
244 0 : }
245 16 : return true;
246 16 : }
247 :
248 : // void MSUVWGenerator::get_ant_offsets(const MDirection& dir_with_a_frame)
249 : // {
250 : // // This appears to be a required column of the ANTENNA table in version 2.0
251 : // // of the MeasurementSet definition
252 : // // (http://aips2.nrao.edu/docs/notes/229/229.html), so it is assumed to be
253 : // // present. However, it is usually a set of zeroes, based on the common
254 : // // belief that it is only needed for heterogeneous arrays, since the
255 : // // receivers of homogeneous arrays move in concert. That is not true when
256 : // // there are independent pointing errors.
257 :
258 : // // Convert ant_offset_measures to Vectors and check for nonzeroness.
259 : // ant_offset_vec_p.resize(nant);
260 : // for(uInt n = 0; n < nant; ++n)
261 : // ant_offset_vec_p[n] = ant_offset_meas_p.convert(0, pointingdir).getValue();
262 : // }
263 :
264 : // Bool MSUVWGenerator::set_receiv_offsets(const MDirection& dir_with_a_frame)
265 : // {
266 : // // This appears to be a required column of the FEED table in version 2.0
267 : // // of the MeasurementSet definition
268 : // // (http://aips2.nrao.edu/docs/notes/229/229.html), so it is assumed to be
269 : // // present. However, it is usually a set of zeroes, based on the common
270 : // // belief that it is only needed for heterogeneous arrays, since the
271 : // // receivers of homogeneous arrays move in concert. That is not true when
272 : // // there are independent pointing errors.
273 :
274 : // // Convert feed_offset_measures to Vectors and check for nonzeroness.
275 : // Vector<Vector<Double> > offsetvects;
276 : // offsetvects.resize(nant);
277 : // for(uInt n = 0; n < nant; ++n){
278 : // offsetvects[n] = ant_offsets->convert(0, pointingdir).getValue();
279 : // if(ant_offsets[n] != ant_offsets[0]){
280 : // varying_offsets = true;
281 : // }
282 : // }
283 :
284 : // ignore_offsets = true;
285 : // return ignore_offsets;
286 : // }
287 :
288 : using namespace casacore;
289 : } // Ends namespace casa.
|