ARTS  2.2.66
m_sensor.cc
Go to the documentation of this file.
1 /* Copyright (C) 2003-2012
2  Patrick Eriksson <Patrick.Eriksson@chalmers.se>
3  Stefan Buehler <sbuehler(at)ltu.se>
4 
5  This program is free software; you can redistribute it and/or modify it
6  under the terms of the GNU General Public License as published by the
7  Free Software Foundation; either version 2, or (at your option) any
8  later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18  USA. */
19 
20 
21 
22 /*===========================================================================
23  === File description
24  ===========================================================================*/
25 
39 /*===========================================================================
40  === External declarations
41  ===========================================================================*/
42 
43 #include <cmath>
44 #include <stdexcept>
45 #include <string>
46 #include "arts.h"
47 #include "check_input.h"
48 #include "interpolation_poly.h"
49 #include "math_funcs.h"
50 #include "messages.h"
51 #include "ppath.h"
52 #include "special_interp.h"
53 #include "xml_io.h"
54 #include "sensor.h"
55 #include "sorting.h"
56 #include "auto_md.h"
57 
58 extern const Numeric PI;
59 extern const Numeric DEG2RAD;
60 extern const Numeric RAD2DEG;
61 extern const Index GFIELD1_F_GRID;
62 extern const Index GFIELD4_FIELD_NAMES;
63 extern const Index GFIELD4_F_GRID;
64 extern const Index GFIELD4_ZA_GRID;
65 extern const Index GFIELD4_AA_GRID;
66 extern const Numeric SPEED_OF_LIGHT;
67 
68 
69 /*===========================================================================
70  === The functions (in alphabetical order)
71  ===========================================================================*/
72 
73 /* Workspace method: Doxygen documentation will be auto-generated */
74 void AntennaConstantGaussian1D(Index& antenna_dim,
75  Vector& mblock_za_grid,
76  Vector& mblock_aa_grid,
77  GriddedField4& r,
78  Matrix& antenna_los,
79  const Index& n_za_grid,
80  const Numeric& fwhm,
81  const Numeric& xwidth_si,
82  const Numeric& dx_si,
83  const Verbosity& verbosity)
84 {
85  if( dx_si > xwidth_si )
86  throw runtime_error( "It is demanded that dx_si <= xwidth_si." );
87 
88  AntennaSet1D( antenna_dim, mblock_aa_grid, verbosity );
89  antenna_responseGaussian( r, fwhm, xwidth_si, dx_si, verbosity );
90 
91  antenna_los.resize(1,1);
92  antenna_los(0,0) = 0.0;
93 
94  // za grid for response
96  const Index nr = r_za_grid.nelem();
97 
98  // Cumulative integral of response (factor /2 skipped, but does not matter)
99  Vector cumtrapz(nr);
100  cumtrapz[0] = 0;
101  for( Index i=1; i<nr; i++ )
102  { cumtrapz[i] = cumtrapz[i-1] + r.data(0,0,i-1,0) + r.data(0,0,i,0); }
103 
104  // Equally spaced vector between end points of cumulative sum
105  Vector csp;
106  nlinspace( csp, cumtrapz[0], cumtrapz[nr-1], n_za_grid );
107 
108  // Get mblock_za_grid by interpolation
109  mblock_za_grid.resize(n_za_grid);
110  ArrayOfGridPos gp(n_za_grid);
111  gridpos( gp, cumtrapz, csp );
112  Matrix itw(n_za_grid,2);
113  interpweights( itw, gp );
114  interp( mblock_za_grid, itw, r_za_grid, gp );
115 }
116 
117 
118 
119 /* Workspace method: Doxygen documentation will be auto-generated */
121  Matrix& sensor_los,
122  Matrix& antenna_los,
123  Index& antenna_dim,
124  Vector& mblock_za_grid,
125  Vector& mblock_aa_grid,
126  const Index& atmosphere_dim,
127  const Verbosity& verbosity)
128 {
129  // Sizes
130  const Index nmblock = sensor_pos.nrows();
131  const Index nant = antenna_los.nrows();
132 
133  // Input checks
134  chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
135  chk_if_in_range( "antenna_dim", antenna_dim, 1, 2 );
136  //
137  if( sensor_pos.ncols() != atmosphere_dim )
138  throw runtime_error( "The number of columns of sensor_pos must be "
139  "equal to the atmospheric dimensionality." );
140  if( atmosphere_dim <= 2 && sensor_los.ncols() != 1 )
141  throw runtime_error(
142  "For 1D and 2D, sensor_los shall have one column." );
143  if( atmosphere_dim == 3 && sensor_los.ncols() != 2 )
144  throw runtime_error( "For 3D, sensor_los shall have two columns." );
145  if( sensor_los.nrows() != nmblock )
146  {
147  ostringstream os;
148  os << "The number of rows of sensor_pos and sensor_los must be "
149  << "identical, but sensor_pos has " << nmblock << " rows,\n"
150  << "while sensor_los has " << sensor_los.nrows() << " rows.";
151  throw runtime_error( os.str() );
152  }
153  if( antenna_dim == 2 && atmosphere_dim < 3 )
154  {
155  throw runtime_error(
156  "If *antenna_dim* is 2, *atmosphere_dim* must be 3." );
157  }
158  if( antenna_dim != antenna_los.ncols() )
159  {
160  throw runtime_error(
161  "The number of columns of *antenna_los* must be *antenna_dim*.\n" );
162  }
163 
164  // Claculate new sensor_pos and sensor_los
165  const Matrix pos_copy = sensor_pos;
166  const Matrix los_copy = sensor_los;
167  //
168  sensor_pos.resize( nmblock*nant, pos_copy.ncols() );
169  sensor_los.resize( nmblock*nant, los_copy.ncols() );
170  //
171  for( Index ib=0; ib<nmblock; ib++ )
172  {
173  for( Index ia=0; ia<nant; ia++ )
174  {
175  const Index i = ib*nant + ia;
176 
177  sensor_pos(i,joker) = pos_copy(ib,joker);
178  sensor_los(i,joker) = los_copy(ib,joker);
179 
180  sensor_los(i,0) += antenna_los(ia,0);
181  if( antenna_dim == 2 )
182  sensor_los(i,1) += antenna_los(ia,1);
183  }
184  }
185 
186  // Set other variables
187  AntennaOff( antenna_dim, mblock_za_grid, mblock_aa_grid, verbosity );
188  //
189  antenna_los.resize( 1, 1 );
190  antenna_los = 0;
191 }
192 
193 
194 
195 /* Workspace method: Doxygen documentation will be auto-generated */
196 void AntennaOff(// WS Output:
197  Index& antenna_dim,
198  Vector& mblock_za_grid,
199  Vector& mblock_aa_grid,
200  const Verbosity& verbosity)
201 {
202  CREATE_OUT2;
203 
204  out2 << " Sets the antenna dimensionality to 1.\n";
205  antenna_dim = 1;
206 
207  out2 << " Sets *mblock_za_grid* to have length 1 with value 0.\n";
208  mblock_za_grid.resize(1);
209  mblock_za_grid[0] = 0;
210 
211  out2 << " Sets *mblock_aa_grid* to be an empty vector.\n";
212  mblock_aa_grid.resize(0);
213 }
214 
215 
216 
217 /* Workspace method: Doxygen documentation will be auto-generated */
218 void AntennaSet1D(// WS Output:
219  Index& antenna_dim,
220  Vector& mblock_aa_grid,
221  const Verbosity& verbosity)
222 {
223  CREATE_OUT2;
224  CREATE_OUT3;
225 
226  out2 << " Sets the antenna dimensionality to 1.\n";
227  out3 << " antenna_dim = 1\n";
228  out3 << " mblock_aa_grid is set to be an empty vector\n";
229  antenna_dim = 1;
230  mblock_aa_grid.resize(0);
231 }
232 
233 
234 
235 /* Workspace method: Doxygen documentation will be auto-generated */
236 void AntennaSet2D(// WS Output:
237  Index& antenna_dim,
238  // WS Input:
239  const Index& atmosphere_dim,
240  const Verbosity& verbosity)
241 {
242  CREATE_OUT2;
243  CREATE_OUT3;
244 
245  if( atmosphere_dim != 3 )
246  throw runtime_error("Antenna dimensionality 2 is only allowed when the "
247  "atmospheric dimensionality is 3." );
248  out2 << " Sets the antenna dimensionality to 1.\n";
249  out3 << " antenna_dim = 2\n";
250  antenna_dim = 2;
251 }
252 
253 
254 
255 /* Workspace method: Doxygen documentation will be auto-generated */
257  const Numeric& fwhm,
258  const Numeric& xwidth_si,
259  const Numeric& dx_si,
260  const Verbosity&)
261 {
262  if( dx_si > xwidth_si )
263  throw runtime_error( "It is demanded that dx_si <= xwidth_si." );
264 
265  Vector x, y;
266  gaussian_response_autogrid( x, y, 0, fwhm, xwidth_si, dx_si );
267 
268  r.set_name( "Antenna response" );
269 
270  r.set_grid_name( 0, "Polarisation" );
271  r.set_grid( 0, MakeArray<String>( "NaN" ) );
272 
273  r.set_grid_name( 1, "Frequency" );
274  r.set_grid( 1, Vector(1,-999) );
275 
276  r.set_grid_name( 2, "Zenith angle" );
277  r.set_grid( 2, x );
278 
279  r.set_grid_name( 3, "Azimuth angle" );
280  r.set_grid( 3, Vector(1,0) );
281 
282  const Index n = y.nelem();
283  r.data.resize( 1, 1, n, 1 );
284  r.data(0,0,joker,0) = y;
285 }
286 
287 
288 
289 /* Workspace method: Doxygen documentation will be auto-generated */
291  GriddedField4& r,
292  const Numeric& leff,
293  const Numeric& xwidth_si,
294  const Numeric& dx_si,
295  const Index& nf,
296  const Numeric& fstart,
297  const Numeric& fstop,
298  const Verbosity& verbosity )
299 {
300  r.set_name( "Antenna response" );
301 
302  r.set_grid_name( 0, "Polarisation" );
303  r.set_grid( 0, MakeArray<String>( "NaN" ) );
304 
305  r.set_grid_name( 3, "Azimuth angle" );
306  r.set_grid( 3, Vector(1,0) );
307 
308  Vector f_grid;
309  VectorNLogSpace( f_grid, nf, fstart, fstop, verbosity );
310  r.set_grid_name( 1, "Frequency" );
311  r.set_grid( 1, f_grid );
312 
313  // Calculate response for highest frequency, with xwidth_si scaled from
314  // fstart
315  Vector x, y;
316  Numeric fwhm = RAD2DEG * SPEED_OF_LIGHT / ( leff * fstop );
317  gaussian_response_autogrid( x, y, 0, fwhm, (fstop/fstart)*xwidth_si, dx_si );
318 
319  r.set_grid_name( 2, "Zenith angle" );
320  r.set_grid( 2, x );
321 
322  const Index n = y.nelem();
323  r.data.resize( 1, nf, n, 1 );
324  //
325  r.data(0,nf-1,joker,0) = y;
326  //
327  for( Index i=0; i<nf-1; i++ )
328  {
329  fwhm = RAD2DEG * SPEED_OF_LIGHT / ( leff * f_grid[i] );
330  gaussian_response( y, x, 0, fwhm );
331  r.data(0,i,joker,0) = y;
332  }
333 }
334 
335 
336 
337 /* Workspace method: Doxygen documentation will be auto-generated */
339  const Numeric& resolution,
340  const Verbosity&)
341 {
342  r.resize( 1 );
343  r[0].set_name( "Backend channel response function" );
344 
345  Vector x(2);
346 
347  r[0].set_grid_name( 0, "Frequency" );
348  x[1] = resolution / 2.0;
349  x[0] = -x[1];
350  r[0].set_grid( 0, x );
351 
352  r[0].data.resize( 2 );
353  r[0].data[0] = 1/ resolution;
354  r[0].data[1] = r[0].data[0];
355 }
356 
357 
358 
359 /* Workspace method: Doxygen documentation will be auto-generated */
361  const Numeric& fwhm,
362  const Numeric& xwidth_si,
363  const Numeric& dx_si,
364  const Verbosity&)
365 {
366  r.resize( 1 );
367  Vector x, y;
368 
369  gaussian_response_autogrid( x, y, 0, fwhm, xwidth_si, dx_si );
370 
371  r[0].set_name( "Backend channel response function" );
372 
373  r[0].set_grid_name( 0, "Frequency" );
374  r[0].set_grid( 0, x );
375 
376  const Index n = y.nelem();
377  r[0].data.resize( n );
378  for( Index i=0; i<n; i++ )
379  r[0].data[i] = y[i];
380 }
381 
382 
383 
384 /* Workspace method: Doxygen documentation will be auto-generated */
385 void f_gridFromSensorAMSU(// WS Output:
386  Vector& f_grid,
387  // WS Input:
388  const Vector& lo,
389  const ArrayOfVector& f_backend,
390  const ArrayOfArrayOfGriddedField1& backend_channel_response,
391  // Control Parameters:
392  const Numeric& spacing,
393  const Verbosity& verbosity)
394 {
395  CREATE_OUT2;
396  CREATE_OUT3;
397 
398  // Find out how many channels we have in total:
399  // f_backend is an array of vectors, containing the band frequencies for each Mixer.
400  Index n_chan = 0;
401  for (Index i=0; i<f_backend.nelem(); ++i)
402  for (Index s=0; s<f_backend[i].nelem(); ++s)
403  ++n_chan;
404 
405  // Checks on input quantities:
406 
407  // There must be at least one channel:
408  if (n_chan < 1)
409  {
410  ostringstream os;
411  os << "There must be at least one channel.\n"
412  << "(The vector *lo* must have at least one element.)";
413  throw runtime_error(os.str());
414  }
415 
416  // Is number of LOs consistent in all input variables?
417  if ( (f_backend.nelem() != lo.nelem()) ||
418  (backend_channel_response.nelem() != lo.nelem()) )
419  {
420  ostringstream os;
421  os << "Variables *lo_multi*, *f_backend_multi* and *backend_channel_response_multi*\n"
422  << "must have same number of elements (number of LOs).";
423  throw runtime_error(os.str());
424  }
425 
426  // Is number of bands consistent for each LO?
427  for (Index i=0; i<f_backend.nelem(); ++i)
428  if (f_backend[i].nelem() != backend_channel_response[i].nelem())
429  {
430  ostringstream os;
431  os << "Variables *f_backend_multi* and *backend_channel_response_multi*\n"
432  << "must have same number of bands for each LO.";
433  throw runtime_error(os.str());
434  }
435 
436  // Start the actual work.
437 
438  // We construct the necessary input for function find_effective_channel_boundaries,
439  // which will identify channel boundaries, taking care of overlaping channels.
440 
441  // A "flat" vector of nominal band frequencies (two for each AMSU channel):
442  Vector f_backend_flat(2*n_chan);
443 
444  // A "flat" list of channel response functions (two for each AMSU channel)
445  ArrayOfGriddedField1 backend_channel_response_flat(2*n_chan);
446 
447  // Counts position inside the flat arrays during construction:
448  Index j=0;
449 
450  for (Index i=0; i<f_backend.nelem(); ++i)
451  for (Index s=0; s<f_backend[i].nelem(); ++s)
452  {
453  const GriddedField1& this_grid = backend_channel_response[i][s];
454  const Numeric this_f_backend = f_backend[i][s];
455 
456  // Signal sideband:
457  f_backend_flat[j] = this_f_backend;
458  backend_channel_response_flat[j] = this_grid;
459  j++;
460 
461  // Image sideband:
462  Numeric offset = this_f_backend - lo[i];
463  Numeric f_image = lo[i] - offset;
464  f_backend_flat[j] = f_image;
465  backend_channel_response_flat[j] = this_grid;
466  j++;
467  }
468 
469  // We build up a total list of absolute frequency ranges for
470  // both signal and image sidebands:
471  Vector fmin(2*n_chan), fmax(2*n_chan);
472 
473  // We have to add some additional margin at the band edges,
474  // otherwise the instrument functions are not happy. Define
475  // this in terms of the grid spacing:
476  Numeric delta = 1*spacing;
477 
478  // Call subfunction to do the actual work of merging overlapping
479  // channels and identifying channel boundaries:
481  fmax,
482  f_backend_flat,
483  backend_channel_response_flat,
484  delta, verbosity);
485 
486  // Create f_grid_array. This is an array of Numeric, so that we
487  // can use the STL push_back function.
488  ArrayOfNumeric f_grid_array;
489 
490  for (Index i=0; i<fmin.nelem(); ++i)
491  {
492  // Band width:
493  const Numeric bw = fmax[i] - fmin[i];
494 
495  // How many grid intervals do I need?
496  const Numeric npf = ceil(bw/spacing);
497 
498  // How many grid points to store? - Number of grid intervals
499  // plus 1.
500  const Index npi = (Index) npf + 1;
501 
502  // Create the grid for this band:
503  Vector grid;
504  nlinspace(grid,fmin[i],fmax[i],npi);
505 
506 
507  out3 << " Band range " << i << ": " << grid << "\n";
508 
509  // Append to f_grid_array:
510  f_grid_array.reserve(f_grid_array.nelem()+npi);
511  for (Index s=0; s<npi; ++s)
512  f_grid_array.push_back(grid[s]);
513  }
514 
515  // Copy result to output vector:
516  f_grid = f_grid_array;
517 
518  out2 << " Total number of frequencies in f_grid: " << f_grid.nelem() << "\n";
519  // cout << "f_grid: " << f_grid << "\n";
520 }
521 
522 void f_gridFromSensorAMSUgeneric(// WS Output:
523  Vector& f_grid,
524  // WS Input:
525  const ArrayOfVector& f_backend_multi, // Center frequency for each channel
526  const ArrayOfArrayOfGriddedField1& backend_channel_response_multi,
527  // Control Parameters:
528  const Numeric& spacing,
529  const Vector& verbosityVect,
530  const Verbosity& verbosity)
531 {
532  CREATE_OUT2;
533  CREATE_OUT3;
534  Index numFpoints = 0;
535  // Calculate the total number of frequency points
536  for(Index idx = 0;idx<backend_channel_response_multi.nelem();++idx)
537  {
538  for(Index idy = 0;idy<backend_channel_response_multi[idx].nelem();++idy)
539  {
540  numFpoints += backend_channel_response_multi[idx][idy].get_grid_size(0);
541  }
542  }
543 
544  Index numChan = backend_channel_response_multi.nelem(); // number of channels
545 
546  // Checks on input quantities:
547 
548  // There must be at least one channel:
549  if (numChan < 1)
550  {
551  ostringstream os;
552  os << "There must be at least one channel.\n"
553  << "(The vector *lo* must have at least one element.)";
554  throw runtime_error(os.str());
555  }
556 
557 
558  // Start the actual work.
559  // We construct the necessary input for function find_effective_channel_boundaries,
560  // which will identify channel boundaries, taking care of overlaping channels.
561 
562 
563  // A "flat" vector of nominal band frequencies (one for each AMSU channel):
564  Vector f_backend_flat(numChan);
565  // A "flat" list of channel response functions (one for each AMSU channel)
566  ArrayOfGriddedField1 backend_channel_response_nonflat(numChan);
567 
568  // Save the correct verbosity value to each passband
569  Vector FminVerbosityVect(numFpoints);
570  Vector FmaxVerbosityVect(numFpoints);
571  Vector VerbosityValVect(numFpoints);
572  Index VerbVectIdx =0;
573 
574 
575  for(Index i =0;i<f_backend_multi.nelem();++i)
576  {
577  for(Index ii =0;ii<f_backend_multi[i].nelem();++ii)
578  {
579  const GriddedField1& this_grid = backend_channel_response_multi[i][ii];
580  const Numeric this_f_backend = f_backend_multi[i][ii];
581  // Signal passband :
582  f_backend_flat[i] = this_f_backend;
583  backend_channel_response_nonflat[i] = this_grid;
584  for(Index idy=1; idy < backend_channel_response_multi[i][ii].get_grid_size(0);++idy)
585  {
586  if((backend_channel_response_multi[i][ii].data[idy-1]==0) && (backend_channel_response_multi[i][ii].data[idy]>0))
587  {
588  FminVerbosityVect[VerbVectIdx] = f_backend_multi[i][ii]+backend_channel_response_multi[i][ii].get_numeric_grid(0)[idy];
589  VerbosityValVect[VerbVectIdx] = verbosityVect[i];
590  }
591  if((backend_channel_response_multi[i][ii].data[idy-1]>0) && (backend_channel_response_multi[i][ii].data[idy]==0))
592  {
593  FmaxVerbosityVect[VerbVectIdx] = f_backend_multi[i][ii]+backend_channel_response_multi[i][ii].get_numeric_grid(0)[idy];
594  VerbVectIdx ++;
595  }
596  }
597  }
598  }
599  // We build up a total list of absolute frequency ranges for all passbands
600  // Code reused from the function "f_gridFromSensorAMSU"
601  Vector fmin,fmax; // - these variables will be resized, therefore len(1) is enough for now.,
602 
603  // We have to add some additional margin at the band edges,
604  // otherwise the instrument functions are not happy. Define
605  // this in terms of the grid spacing:
606  const Numeric delta = 10;
607 
608  // Call subfunction to do the actual work of merging overlapping
609  // channels and identifying channel boundaries:
611  fmax,
612  f_backend_flat,
613  backend_channel_response_nonflat,
614  delta, verbosity);
615 
616  // Create f_grid_array. This is an array of Numeric, so that we
617  // can use the STL push_back function.
618  ArrayOfNumeric f_grid_array;
619 
620  for (Index i=0; i<fmin.nelem(); ++i)
621  {
622  // Bandwidth:
623  const Numeric bw = fmax[i] - fmin[i];
624  Numeric npf = ceil(bw/spacing); // Set a default value
625 
626  // How many grid intervals do I need?
627  Index verbIdx = 0;
628  if(verbosityVect.nelem()>0)
629  {
630  // find the grid needed for the particular part of passband
631  for(Index ii =0;ii<VerbVectIdx;++ii)
632  {
633  if((FminVerbosityVect[ii]>=fmin[i]) &&(FmaxVerbosityVect[ii]<=fmax[i]))
634  {
635  if(verbIdx ==0)
636  {
637  verbIdx = ii;
638  }
639  else
640  {
641  if(VerbosityValVect[ii]<VerbosityValVect[verbIdx])
642  {
643  verbIdx = ii;
644  }
645  }
646  }
647  }
648  if(spacing > VerbosityValVect[verbIdx])
649  {
650  npf = ceil(bw/VerbosityValVect[verbIdx]); // is the default value to coarse?
651  }
652  else
653  {
654  npf = ceil(bw/spacing); // Default value
655  }
656  }
657 
658 
659  // How many grid points to store? - Number of grid intervals
660  // plus 1.
661  const Index npi = (Index) npf + 1;
662 
663  // What is the actual grid spacing inside the band?
664  const Numeric gs = bw/npf;
665 
666  // Create the grid for this band:
667  Vector grid(fmin[i], npi, gs);
668 
669  out3 << " Band range " << i << ": " << grid << "\n";
670 
671  // Append to f_grid_array:
672  f_grid_array.reserve(f_grid_array.nelem()+npi);
673  for (Index s=0; s<npi; ++s)
674  f_grid_array.push_back(grid[s]);
675  }
676 
677  // Copy result to output vector:
678  f_grid = f_grid_array;
679 
680  out2 << " Total number of frequencies in f_grid: " << f_grid.nelem() << "\n";
681 
682 }
683 
684 
685 
686 
687 
688 /* Workspace method: Doxygen documentation will be auto-generated */
689 void f_gridFromSensorHIRS(// WS Output:
690  Vector& f_grid,
691  // WS Input:
692  const Vector& f_backend,
693  const ArrayOfGriddedField1& backend_channel_response,
694  // Control Parameters:
695  const Numeric& spacing,
696  const Verbosity& verbosity)
697 {
698  CREATE_OUT2;
699  CREATE_OUT3;
700 
701  // Check input
702  if (spacing <= 0) {
703  ostringstream os;
704  os << "Expected positive spacing. Found spacing to be: "
705  << spacing << "\n";
706  throw runtime_error(os.str());
707  }
708  // Call subfunction to get channel boundaries. Also does input
709  // consistency checking for us.
710  Vector fmin, fmax;
711 
712  // We have to add some additional margin at the band edges,
713  // otherwise the instrument functions are not happy. Define
714  // this in terms of the grid spacing:
715  Numeric delta = 1*spacing;
716 
718  fmax,
719  f_backend,
720  backend_channel_response,
721  delta, verbosity);
722 
723  // Ok, now we just have to create a frequency grid for each of the
724  // fmin/fmax ranges.
725 
726  // Create f_grid_array. This is an array of Numeric, so that we
727  // can use the STL push_back function.
728  ArrayOfNumeric f_grid_array;
729 
730  for (Index i=0; i<fmin.nelem(); ++i)
731  {
732  // Band width:
733  const Numeric bw = fmax[i] - fmin[i];
734 
735  // How many grid intervals do I need?
736  const Numeric npf = ceil(bw/spacing);
737 
738  // How many grid points to store? - Number of grid intervals
739  // plus 1.
740  const Index npi = (Index) npf + 1;
741 
742  // Create the grid for this band:
743  Vector grid;
744  nlinspace(grid,fmin[i],fmax[i],npi);
745 
746  out3 << " Band range " << i << ": " << grid << "\n";
747 
748  // Append to f_grid_array:
749  f_grid_array.reserve(f_grid_array.nelem()+npi);
750  for (Index s=0; s<npi; ++s)
751  f_grid_array.push_back(grid[s]);
752  }
753 
754  // Copy result to output vector:
755  f_grid = f_grid_array;
756 
757  out2 << " Total number of frequencies in f_grid: " << f_grid.nelem() << "\n";
758 
759 }
760 
761 
762 /* Workspace method: Doxygen documentation will be auto-generated */
763 void sensor_responseAntenna(// WS Output:
764  Sparse& sensor_response,
765  Vector& sensor_response_f,
766  ArrayOfIndex& sensor_response_pol,
767  Vector& sensor_response_za,
768  Vector& sensor_response_aa,
769  Vector& sensor_response_za_grid,
770  Vector& sensor_response_aa_grid,
771  // WS Input:
772  const Vector& sensor_response_f_grid,
773  const ArrayOfIndex& sensor_response_pol_grid,
774  const Index& atmosphere_dim,
775  const Index& antenna_dim,
776  const Matrix& antenna_los,
777  const GriddedField4& antenna_response,
778  const Index& sensor_norm,
779  const Verbosity& verbosity)
780 {
781  CREATE_OUT3;
782 
783  // Basic checks
784  chk_if_in_range( "atmosphere_dim", atmosphere_dim, 1, 3 );
785  chk_if_in_range( "antenna_dim", antenna_dim, 1, 2 );
786  chk_if_bool( "sensor_norm", sensor_norm );
787 
788  // Some sizes
789  const Index nf = sensor_response_f_grid.nelem();
790  const Index npol = sensor_response_pol_grid.nelem();
791  const Index nza = sensor_response_za_grid.nelem();
792  const Index naa = max( Index(1), sensor_response_aa_grid.nelem() );
793  const Index nin = nf * npol * nza * naa;
794 
795  // Initialise a output stream for runtime errors and a flag for errors
796  ostringstream os;
797  bool error_found = false;
798 
799 
800  // Check that sensor_response variables are consistent in size
801  if( sensor_response_f.nelem() != nin )
802  {
803  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
804  << "grid variables (sensor_response_f_grid etc.).\n";
805  error_found = true;
806  }
807  if( sensor_response.nrows() != nin )
808  {
809  os << "The sensor block response matrix *sensor_response* does not have\n"
810  << "right size compared to the sensor grid variables\n"
811  << "(sensor_response_f_grid etc.).\n";
812  error_found = true;
813  }
814 
815 
816  // Checks related to antenna dimension
817  if( antenna_dim == 2 && atmosphere_dim < 3 )
818  {
819  os << "If *antenna_dim* is 2, *atmosphere_dim* must be 3.\n";
820  error_found = true;
821  }
822  if( antenna_dim == 1 && sensor_response_aa_grid.nelem() )
823  {
824  os << "If *antenna_dim* is 1, *sensor_response_aa_grid* (and\n"
825  << "*mblock_aa_grid*) must be empty.";
826  error_found = true;
827  }
828 
829 
830  // Check of antenna_los
831  if( antenna_dim != antenna_los.ncols() )
832  {
833  os << "The number of columns of *antenna_los* must be *antenna_dim*.\n";
834  error_found = true;
835  }
836  // We allow angles in antenna_los to be unsorted
837 
838 
839  // Checks of antenna_response polarisation dimension
840  //
841  const Index lpolgrid =
842  antenna_response.get_string_grid(GFIELD4_FIELD_NAMES).nelem();
843  //
844  if( lpolgrid != 1 && lpolgrid != npol )
845  {
846  os << "The number of polarisation in *antenna_response* must be 1 or be\n"
847  << "equal to the number of polarisations used (determined by\n"
848  << "*stokes_dim* or *sensor_pol*).\n";
849  error_found = true;
850  }
851 
852 
853  // Checks of antenna_response frequency dimension
854  //
855  ConstVectorView aresponse_f_grid =
856  antenna_response.get_numeric_grid(GFIELD4_F_GRID);
857  //
858  chk_if_increasing( "f_grid of antenna_response", aresponse_f_grid );
859  //
860  Numeric f_dlow = 0.0;
861  Numeric f_dhigh = 0.0;
862  //
863  f_dlow = min(sensor_response_f_grid) - aresponse_f_grid[0];
864  f_dhigh = last(aresponse_f_grid) - max(sensor_response_f_grid);
865  //
866  if( aresponse_f_grid.nelem() > 1 )
867  {
868  if( f_dlow < 0 )
869  {
870  os << "The frequency grid of *antenna_response is too narrow. It must\n"
871  << "cover all considered frequencies (*f_grid*), if the length\n"
872  << "is > 1. The grid needs to be expanded with "<<-f_dlow<<" Hz in\n"
873  << "the lower end.\n";
874  error_found = true;
875  }
876  if( f_dhigh < 0 )
877  {
878  os << "The frequency grid of *antenna_response is too narrow. It must\n"
879  << "cover all considered frequencies (*f_grid*), if the length\n"
880  << "is > 1. The grid needs to be expanded with "<<-f_dhigh<<" Hz in\n"
881  << "the upper end.\n";
882  error_found = true;
883  }
884  }
885 
886 
887  // Checks of antenna_response za dimension
888  //
889  ConstVectorView aresponse_za_grid =
890  antenna_response.get_numeric_grid(GFIELD4_ZA_GRID);
891  //
892  chk_if_increasing( "za_grid of *antenna_response*", aresponse_za_grid );
893  //
894  if( aresponse_za_grid.nelem() < 2 )
895  {
896  os << "The zenith angle grid of *antenna_response* must have >= 2 values.\n";
897  error_found = true;
898 
899  }
900  //
901  // Check if the relative grid added to the antenna_los za angles
902  // outside sensor_response_za_grid.
903  //
904  Numeric za_dlow = 0.0;
905  Numeric za_dhigh = 0.0;
906  //
907  za_dlow = min(antenna_los(joker,0)) + aresponse_za_grid[0] -
908  min(sensor_response_za_grid);
909  za_dhigh = max(sensor_response_za_grid) - ( max(antenna_los(joker,0)) +
910  last(aresponse_za_grid) );
911  //
912  if( za_dlow < 0 )
913  {
914  os << "The WSV *sensor_response_za_grid* is too narrow. It should be\n"
915  << "expanded with "<<-za_dlow<<" deg in the lower end. This change\n"
916  << "should be probably applied to *mblock_za_grid*.\n";
917  error_found = true;
918  }
919  if( za_dhigh < 0 )
920  {
921  os << "The WSV *sensor_response_za_grid* is too narrow. It should be\n"
922  << "expanded with "<<-za_dhigh<<" deg in the higher end. This change\n"
923  << "should be probably applied to *mblock_za_grid*.\n";
924  error_found = true;
925  }
926 
927 
928  // Checks of antenna_response aa dimension
929  //
930  ConstVectorView aresponse_aa_grid =
931  antenna_response.get_numeric_grid(GFIELD4_AA_GRID);
932  //
933  if( antenna_dim == 1 )
934  {
935  if( aresponse_aa_grid.nelem() != 1 )
936  {
937  os << "The azimuthal dimension of *antenna_response* must be 1 if\n"
938  << "*antenna_dim* equals 1.\n";
939  error_found = true;
940  }
941  }
942  else
943  {
944  chk_if_increasing( "aa_grid of antenna_response", aresponse_aa_grid );
945  //
946  if( aresponse_za_grid.nelem() < 2 )
947  {
948  os << "The zenith angle grid of *antenna_response* must have >= 2\n"
949  << "values.\n";
950  error_found = true;
951  }
952  // Check if the relative grid added to the antena_los aa angles
953  // outside sensor_response_aa_grid.
954  //
955  Numeric aa_dlow = 0.0;
956  Numeric aa_dhigh = 0.0;
957  //
958  aa_dlow = min(antenna_los(joker,1)) + aresponse_aa_grid[0] -
959  min(sensor_response_aa_grid);
960  aa_dhigh = max(sensor_response_aa_grid) - ( max(antenna_los(joker,1)) +
961  last(aresponse_aa_grid) );
962  //
963  if( aa_dlow < 0 )
964  {
965  os << "The WSV *sensor_response_aa_grid* is too narrow. It should be\n"
966  << "expanded with "<<-aa_dlow<<" deg in the lower end. This change\n"
967  << "should be probably applied to *mblock_aa_grid*.\n";
968  error_found = true;
969  }
970  if( aa_dhigh < 0 )
971  {
972  os << "The WSV *sensor_response_aa_grid* is too narrow. It should be\n"
973  << "expanded with "<<-aa_dhigh<<" deg in the higher end. This change\n"
974  << "should be probably applied to *mblock_aa_grid*.\n";
975  error_found = true;
976  }
977  }
978 
979 
980  // If errors where found throw runtime_error with the collected error
981  // message.
982  if (error_found)
983  throw runtime_error(os.str());
984 
985  // And finally check if grids and data size match
986  antenna_response.checksize_strict();
987 
988 
989  // Call the core function
990  //
991  Sparse hantenna;
992  //
993  if( antenna_dim == 1 )
994  antenna1d_matrix( hantenna, antenna_dim, antenna_los, antenna_response,
995  sensor_response_za_grid, sensor_response_f_grid,
996  npol, sensor_norm );
997  else
998  antenna2d_simplified( hantenna, antenna_dim, antenna_los, antenna_response,
999  sensor_response_za_grid, sensor_response_aa_grid,
1000  sensor_response_f_grid, npol, sensor_norm );
1001 
1002  // Here we need a temporary sparse that is copy of the sensor_response
1003  // sparse matrix. We need it since the multiplication function can not
1004  // take the same object as both input and output.
1005  Sparse htmp = sensor_response;
1006  sensor_response.resize( hantenna.nrows(), htmp.ncols());
1007  mult( sensor_response, hantenna, htmp );
1008 
1009  // Some extra output.
1010  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1011  << "x" << sensor_response.ncols() << "\n";
1012 
1013  // Update sensor_response_za_grid
1014  sensor_response_za_grid = antenna_los(joker,0);
1015 
1016  // Update sensor_response_aa_grid
1017  if( antenna_dim == 2 )
1018  sensor_response_aa_grid = antenna_los(joker,1);
1019 
1020  // Set aux variables
1021  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1022  sensor_response_za, sensor_response_aa,
1023  sensor_response_f_grid, sensor_response_pol_grid,
1024  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1025 }
1026 
1027 
1028 
1029 
1030 
1031 /* Workspace method: Doxygen documentation will be auto-generated */
1032 void sensor_responseBackend(// WS Output:
1033  Sparse& sensor_response,
1034  Vector& sensor_response_f,
1035  ArrayOfIndex& sensor_response_pol,
1036  Vector& sensor_response_za,
1037  Vector& sensor_response_aa,
1038  Vector& sensor_response_f_grid,
1039  // WS Input:
1040  const ArrayOfIndex& sensor_response_pol_grid,
1041  const Vector& sensor_response_za_grid,
1042  const Vector& sensor_response_aa_grid,
1043  const Vector& f_backend,
1044  const ArrayOfGriddedField1& backend_channel_response,
1045  const Index& sensor_norm,
1046  const Verbosity& verbosity)
1047 {
1048  CREATE_OUT3;
1049 
1050  // Some sizes
1051  const Index nf = sensor_response_f_grid.nelem();
1052  const Index npol = sensor_response_pol_grid.nelem();
1053  const Index nza = sensor_response_za_grid.nelem();
1054  const Index naa = sensor_response_aa_grid.nelem();
1055  const Index nin = nf * npol * nza;
1056  // Note that there is no distinction between za and aa grids after the antenna
1057 
1058  // Initialise an output stream for runtime errors and a flag for errors
1059  ostringstream os;
1060  bool error_found = false;
1061 
1062  // Check that sensor_response variables are consistent in size
1063  if( sensor_response_f.nelem() != nin )
1064  {
1065  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
1066  << "grid variables (sensor_response_f_grid etc.).\n";
1067  error_found = true;
1068  }
1069  if( naa && naa != nza )
1070  {
1071  os << "Incorrect size of *sensor_response_aa_grid*.\n";
1072  error_found = true;
1073  }
1074  if( sensor_response.nrows() != nin )
1075  {
1076  os << "The sensor block response matrix *sensor_response* does not have\n"
1077  << "right size compared to the sensor grid variables\n"
1078  << "(sensor_response_f_grid etc.).\n";
1079  error_found = true;
1080  }
1081 
1082  // We allow f_backend to be unsorted, but must be inside sensor_response_f_grid
1083  if( min(f_backend) < min(sensor_response_f_grid) )
1084  {
1085  os << "At least one value in *f_backend* (" << min(f_backend)
1086  << ") below range\ncovered by *sensor_response_f_grid* ("
1087  << min(sensor_response_f_grid) << ").\n";
1088  error_found = true;
1089  }
1090  if( max(f_backend) > max(sensor_response_f_grid) )
1091  {
1092  os << "At least one value in *f_backend* (" << max(f_backend)
1093  << ") above range\ncovered by *sensor_response_f_grid* ("
1094  << max(sensor_response_f_grid) << ").\n";
1095  error_found = true;
1096  }
1097 
1098  // Check number of columns in backend_channel_response
1099  //
1100  const Index nrp = backend_channel_response.nelem();
1101  //
1102  if( nrp != 1 && nrp != f_backend.nelem() )
1103  {
1104  os << "The WSV *backend_channel_response* must have 1 or n elements,\n"
1105  << "where n is the length of *f_backend*.\n";
1106  error_found = true;
1107  }
1108 
1109  // If errors where found throw runtime_error with the collected error
1110  // message (before error message gets too long).
1111  if( error_found )
1112  throw runtime_error(os.str());
1113 
1114  Numeric f_dlow = 0.0;
1115  Numeric f_dhigh = 0.0;
1116 
1117  for( Index i=0; i<nrp; i++ )
1118  {
1119  ConstVectorView bchr_f_grid =
1120  backend_channel_response[i].get_numeric_grid(GFIELD1_F_GRID);
1121 
1122  if( bchr_f_grid.nelem() != backend_channel_response[i].data.nelem() )
1123  {
1124  os << "Mismatch in size of grid and data in element " << i
1125  << "\nof *sideband_response*.\n";
1126  error_found = true;
1127  }
1128 
1129  if( !is_increasing( bchr_f_grid ) )
1130  {
1131  os << "The frequency grid of element " << i
1132  << " in *backend_channel_response*\nis not strictly increasing.\n";
1133  error_found = true;
1134  }
1135 
1136  // Check if the relative grid added to the channel frequencies expands
1137  // outside sensor_response_f_grid.
1138  //
1139  Numeric f1 = f_backend[i] + bchr_f_grid[0] - min(sensor_response_f_grid);
1140  Numeric f2 = (max(sensor_response_f_grid) -
1141  f_backend[i]) - last(bchr_f_grid);
1142  //
1143  f_dlow = min( f_dlow, f1 );
1144  f_dhigh = min( f_dhigh, f2 );
1145  }
1146 
1147  if( f_dlow < 0 )
1148  {
1149  os << "The WSV *sensor_response_f_grid* is too narrow. It should be\n"
1150  << "expanded with "<<-f_dlow<<" Hz in the lower end. This change\n"
1151  << "should be applied to either *f_grid* or the sensor part in\n"
1152  << "front of *sensor_responseBackend*.\n";
1153  error_found = true;
1154  }
1155  if( f_dhigh < 0 )
1156  {
1157  os << "The WSV *sensor_response_f_grid* is too narrow. It should be\n"
1158  << "expanded with "<<-f_dhigh<<" Hz in the higher end. This change\n"
1159  << "should be applied to either *f_grid* or the sensor part in\n"
1160  << "front of *sensor_responseBackend*.\n";
1161  error_found = true;
1162  }
1163 
1164  // If errors where found throw runtime_error with the collected error
1165  // message.
1166  if (error_found)
1167  throw runtime_error(os.str());
1168 
1169 
1170  // Call the core function
1171  //
1172  Sparse hbackend;
1173  //
1174  spectrometer_matrix( hbackend, f_backend, backend_channel_response,
1175  sensor_response_f_grid, npol, nza, sensor_norm );
1176 
1177  // Here we need a temporary sparse that is copy of the sensor_response
1178  // sparse matrix. We need it since the multiplication function can not
1179  // take the same object as both input and output.
1180  Sparse htmp = sensor_response;
1181  sensor_response.resize( hbackend.nrows(), htmp.ncols());
1182  mult( sensor_response, hbackend, htmp );
1183 
1184  // Some extra output.
1185  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1186  << "x" << sensor_response.ncols() << "\n";
1187 
1188  // Update sensor_response_f_grid
1189  sensor_response_f_grid = f_backend;
1190 
1191  // Set aux variables
1192  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1193  sensor_response_za, sensor_response_aa,
1194  sensor_response_f_grid, sensor_response_pol_grid,
1195  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1196 }
1197 
1198 
1199 
1200 /* Workspace method: Doxygen documentation will be auto-generated */
1202  Vector& sensor_response_f,
1203  ArrayOfIndex& sensor_response_pol,
1204  Vector& sensor_response_za,
1205  Vector& sensor_response_aa,
1206  Vector& sensor_response_f_grid,
1207  const ArrayOfIndex& sensor_response_pol_grid,
1208  const Vector& sensor_response_za_grid,
1209  const Vector& sensor_response_aa_grid,
1210  const Vector& f_backend,
1211  const ArrayOfGriddedField1& backend_channel_response,
1212  const Index& sensor_norm,
1213  const Numeric& df1,
1214  const Numeric& df2,
1215  const Verbosity& verbosity)
1216 {
1217  // All needed checks are done in sensor_responseBackend
1218 
1219  Sparse H1=sensor_response, H2=sensor_response;
1220 
1221  // Some needed vectors
1222  Vector f_backend_shifted;
1223  Vector fdummy=sensor_response_f, fdummy_grid=sensor_response_f_grid;
1224 
1225  // Cycle 1
1226  f_backend_shifted = f_backend;
1227  f_backend_shifted += df1;
1228  //
1229  sensor_responseBackend(H1, fdummy, sensor_response_pol,
1230  sensor_response_za, sensor_response_aa,
1231  fdummy_grid, sensor_response_pol_grid,
1232  sensor_response_za_grid, sensor_response_aa_grid,
1233  f_backend_shifted, backend_channel_response,
1234  sensor_norm, verbosity);
1235  // Cycle 2
1236  f_backend_shifted = f_backend;
1237  f_backend_shifted += df2;
1238  //
1239  sensor_responseBackend(H2, sensor_response_f, sensor_response_pol,
1240  sensor_response_za, sensor_response_aa,
1241  sensor_response_f_grid, sensor_response_pol_grid,
1242  sensor_response_za_grid, sensor_response_aa_grid,
1243  f_backend_shifted, backend_channel_response,
1244  sensor_norm, verbosity);
1245 
1246  // Total response
1247  sub( sensor_response, H2, H1 );
1248 
1249  // sensor_response_f_grid shall be f_backend
1250  sensor_response_f_grid = f_backend;
1251 
1252  // Set aux variables
1253  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1254  sensor_response_za, sensor_response_aa,
1255  sensor_response_f_grid, sensor_response_pol_grid,
1256  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1257 }
1258 
1259 
1260 
1261 /* Workspace method: Doxygen documentation will be auto-generated */
1263  Sparse& sensor_response,
1264  Vector& sensor_response_f,
1265  ArrayOfIndex& sensor_response_pol,
1266  Vector& sensor_response_za,
1267  Vector& sensor_response_aa,
1268  Vector& sensor_response_za_grid,
1269  Vector& sensor_response_aa_grid,
1270  // WS Input:
1271  const Vector& sensor_response_f_grid,
1272  const ArrayOfIndex& sensor_response_pol_grid,
1273  const Numeric& w1,
1274  const Numeric& w2,
1275  const Verbosity& verbosity)
1276 {
1277  CREATE_OUT3;
1278 
1279  if( sensor_response_za_grid.nelem() != 2 )
1280  throw runtime_error(
1281  "This method requires that the number of observation directions is 2." );
1282 
1283  if( sensor_response_pol_grid.nelem() != 1 )
1284  throw runtime_error(
1285  "This method handles (so far) only single polarisation cases." );
1286 
1287  const Index n = sensor_response_f_grid.nelem();
1288 
1289  // Form H matrix representing beam switching
1290  Sparse Hbswitch( n, 2*n );
1291  Vector hrow( 2*n, 0.0 );
1292  //
1293  for( Index i=0; i<n; i++ )
1294  {
1295  hrow[i] = w1;
1296  hrow[i+n] = w2;
1297  //
1298  Hbswitch.insert_row( i, hrow );
1299  //
1300  hrow = 0;
1301  }
1302 
1303  // Here we need a temporary sparse that is copy of the sensor_response
1304  // sparse matrix. We need it since the multiplication function can not
1305  // take the same object as both input and output.
1306  Sparse Htmp = sensor_response;
1307  sensor_response.resize( Hbswitch.nrows(), Htmp.ncols() );
1308  mult( sensor_response, Hbswitch, Htmp );
1309 
1310  // Some extra output.
1311  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1312  << "x" << sensor_response.ncols() << "\n";
1313 
1314  // Update sensor_response_za_grid
1315  const Numeric za = sensor_response_za_grid[1];
1316  sensor_response_za_grid.resize(1);
1317  sensor_response_za_grid[0] = za;
1318 
1319  // Update sensor_response_aa_grid
1320  if( sensor_response_aa_grid.nelem() > 0 )
1321  {
1322  const Numeric aa = sensor_response_aa_grid[1];
1323  sensor_response_aa_grid.resize(1);
1324  sensor_response_aa_grid[0] = aa;
1325  }
1326 
1327  // Set aux variables
1328  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1329  sensor_response_za, sensor_response_aa,
1330  sensor_response_f_grid, sensor_response_pol_grid,
1331  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1332 }
1333 
1334 
1335 
1336 /* Workspace method: Doxygen documentation will be auto-generated */
1338  Sparse& sensor_response,
1339  Vector& sensor_response_f,
1340  ArrayOfIndex& sensor_response_pol,
1341  Vector& sensor_response_za,
1342  Vector& sensor_response_aa,
1343  Vector& sensor_response_f_grid,
1344  // WS Input:
1345  const ArrayOfIndex& sensor_response_pol_grid,
1346  const Vector& sensor_response_za_grid,
1347  const Vector& sensor_response_aa_grid,
1348  const Verbosity& verbosity)
1349 {
1350  CREATE_OUT3;
1351 
1352  if( sensor_response_za_grid.nelem() != 1 )
1353  throw runtime_error(
1354  "This method requires that the number of observation directions is 1." );
1355 
1356  if( sensor_response_pol_grid.nelem() != 1 )
1357  throw runtime_error(
1358  "This method handles (so far) only single polarisation cases." );
1359 
1360  const Index n = sensor_response_f_grid.nelem();
1361  const Index n2 = n/2;
1362 
1363  if( sensor_response.nrows() != n )
1364  throw runtime_error( "Assumptions of method are not fulfilled, "
1365  "considering number of rows in *sensor_response* "
1366  "and length of *sensor_response_f_grid*." );
1367 
1368  if( !is_multiple(n,2) )
1369  throw runtime_error( "There is an odd number of total frequencies, "
1370  "which is not consistent with the assumptions of "
1371  "the method." );
1372 
1373 
1374  // Form H matrix representing frequency switching
1375  Sparse Hbswitch( n2, n );
1376  Vector hrow( n, 0.0 );
1377  //
1378  for( Index i=0; i<n2; i++ )
1379  {
1380  hrow[i] = -1;
1381  hrow[i+n2] = 1;
1382  //
1383  Hbswitch.insert_row( i, hrow );
1384  //
1385  hrow = 0;
1386  }
1387 
1388  // Here we need a temporary sparse that is copy of the sensor_response
1389  // sparse matrix. We need it since the multiplication function can not
1390  // take the same object as both input and output.
1391  Sparse Htmp = sensor_response;
1392  sensor_response.resize( Hbswitch.nrows(), Htmp.ncols() );
1393  mult( sensor_response, Hbswitch, Htmp );
1394 
1395  // Some extra output.
1396  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1397  << "x" << sensor_response.ncols() << "\n";
1398 
1399  // Update sensor_response_f_grid
1400  const Vector f = sensor_response_f_grid;
1401  sensor_response_f_grid.resize(n2);
1402  sensor_response_f_grid = f[Range(n2,n2)];
1403 
1404  // Set aux variables
1405  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1406  sensor_response_za, sensor_response_aa,
1407  sensor_response_f_grid, sensor_response_pol_grid,
1408  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1409 }
1410 
1411 
1412 
1413 /* Workspace method: Doxygen documentation will be auto-generated */
1414 void sensor_responseIF2RF(// WS Output:
1415  Vector& sensor_response_f,
1416  Vector& sensor_response_f_grid,
1417  // WS Input:
1418  const Numeric& lo,
1419  const String& sideband_mode,
1420  const Verbosity&)
1421 {
1422  // Check that frequencies are not too high. This might be a floating limit.
1423  // For this we use the variable f_lim, given in Hz.
1424  Numeric f_lim = 30e9;
1425  if( max(sensor_response_f_grid) > f_lim )
1426  throw runtime_error( "The frequencies seem to already be given in RF." );
1427 
1428 
1429  // Lower band
1430  if( sideband_mode == "lower" )
1431  {
1432  sensor_response_f *= -1;
1433  sensor_response_f_grid *= -1;
1434  sensor_response_f += lo;
1435  sensor_response_f_grid += lo;
1436  }
1437 
1438  // Upper band
1439  else if( sideband_mode=="upper" )
1440  {
1441  sensor_response_f += lo;
1442  sensor_response_f_grid += lo;
1443  }
1444 
1445  // Unknown option
1446  else
1447  {
1448  throw runtime_error(
1449  "Only allowed options for *sideband _mode* are \"lower\" and \"upper\"." );
1450  }
1451 }
1452 
1453 
1454 
1455 /* Workspace method: Doxygen documentation will be auto-generated */
1456 void sensor_responseFillFgrid(// WS Output:
1457  Sparse& sensor_response,
1458  Vector& sensor_response_f,
1459  ArrayOfIndex& sensor_response_pol,
1460  Vector& sensor_response_za,
1461  Vector& sensor_response_aa,
1462  Vector& sensor_response_f_grid,
1463  // WS Input:
1464  const ArrayOfIndex& sensor_response_pol_grid,
1465  const Vector& sensor_response_za_grid,
1466  const Vector& sensor_response_aa_grid,
1467  const Index& polyorder,
1468  const Index& nfill,
1469  const Verbosity& verbosity)
1470 {
1471  CREATE_OUT3;
1472 
1473  // Some sizes
1474  const Index nf = sensor_response_f_grid.nelem();
1475  const Index npol = sensor_response_pol_grid.nelem();
1476  const Index nza = sensor_response_za_grid.nelem();
1477  const Index naa = max( Index(1), sensor_response_aa_grid.nelem() );
1478  const Index nin = nf * npol * nza * naa;
1479 
1480  // Initialise a output stream for runtime errors and a flag for errors
1481  ostringstream os;
1482  bool error_found = false;
1483 
1484  // Check that sensor_response variables are consistent in size
1485  if( sensor_response_f.nelem() != nin )
1486  {
1487  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
1488  << "grid variables (sensor_response_f_grid etc.).\n";
1489  error_found = true;
1490  }
1491  if( sensor_response.nrows() != nin )
1492  {
1493  os << "The sensor block response matrix *sensor_response* does not have\n"
1494  << "right size compared to the sensor grid variables\n"
1495  << "(sensor_response_f_grid etc.).\n";
1496  error_found = true;
1497  }
1498 
1499  // Check polyorder and nfill
1500  if( polyorder < 2 || polyorder > 7 )
1501  {
1502  os << "Accepted range for *polyorder* is [3,7].\n";
1503  error_found = true;
1504  }
1505  if( nfill < 1 )
1506  {
1507  os << "The argument *nfill* must be > 1.\n";
1508  error_found = true;
1509  }
1510 
1511  // If errors where found throw runtime_error with the collected error
1512  // message.
1513  if (error_found)
1514  throw runtime_error(os.str());
1515 
1516 
1517  // New frequency grid
1518  //
1519  const Index n1 = nfill+1;
1520  const Index n2 = nfill+2;
1521  const Index nnew = (nf-1)*n1 + 1;
1522  //
1523  Vector fnew( nnew );
1524  //
1525  for( Index i=0; i<nf-1; i++ )
1526  {
1527  Vector fp(n2);
1528  nlinspace( fp, sensor_response_f_grid[i], sensor_response_f_grid[i+1], n2 );
1529  fnew[Range(i*n1,n2)] = fp;
1530  }
1531 
1532  // Find interpolation weights
1533  //
1534  ArrayOfGridPosPoly gp( nnew );
1535  Matrix itw( nnew, polyorder+1 );
1536  //
1537  gridpos_poly( gp, sensor_response_f_grid, fnew, polyorder );
1538  interpweights( itw, gp );
1539 
1540  // Set up H for this part
1541  //
1542  Sparse hpoly( nnew * npol * nza * naa, nin );
1543  Vector hrow( nin, 0.0 );
1544  Index row = 0;
1545  //
1546  for( Index iza=0; iza<nza; iza++ )
1547  {
1548  for( Index iaa=0; iaa<naa; iaa++ )
1549  {
1550  for( Index iv=0; iv<nnew; iv++ )
1551  {
1552  for( Index ip=0; ip<npol; ip++ )
1553  {
1554  const Index col0 = (iza*naa+iaa)*nf*npol;
1555  for( Index i=0; i<gp[iv].idx.nelem(); i++ )
1556  {
1557  const Numeric w = gp[iv].w[i];
1558  if( abs(w) > 1e-5 )
1559  {
1560  hrow[col0+gp[iv].idx[i]*npol+ip] = w;
1561  }
1562  }
1563  hpoly.insert_row( row, hrow );
1564  for( Index i=0; i<gp[iv].idx.nelem(); i++ )
1565  { hrow[col0+gp[iv].idx[i]*npol+ip] = 0; }
1566  row += 1;
1567  }
1568  }
1569  }
1570  }
1571 
1572  // Here we need a temporary sparse that is copy of the sensor_response
1573  // sparse matrix. We need it since the multiplication function can not
1574  // take the same object as both input and output.
1575  Sparse htmp = sensor_response;
1576  sensor_response.resize( hpoly.nrows(), htmp.ncols());
1577  mult( sensor_response, hpoly, htmp );
1578 
1579  // Some extra output.
1580  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1581  << "x" << sensor_response.ncols() << "\n";
1582 
1583  // Update sensor_response_za_grid
1584  sensor_response_f_grid = fnew;
1585 
1586  // Set aux variables
1587  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1588  sensor_response_za, sensor_response_aa,
1589  sensor_response_f_grid, sensor_response_pol_grid,
1590  sensor_response_za_grid, sensor_response_aa_grid, 1 );
1591 }
1592 
1593 
1594 
1595 /* Workspace method: Doxygen documentation will be auto-generated */
1596 void sensor_responseInit(// WS Output:
1597  Sparse& sensor_response,
1598  Vector& sensor_response_f,
1599  ArrayOfIndex& sensor_response_pol,
1600  Vector& sensor_response_za,
1601  Vector& sensor_response_aa,
1602  Vector& sensor_response_f_grid,
1603  ArrayOfIndex& sensor_response_pol_grid,
1604  Vector& sensor_response_za_grid,
1605  Vector& sensor_response_aa_grid,
1606  // WS Input:
1607  const Vector& f_grid,
1608  const Vector& mblock_za_grid,
1609  const Vector& mblock_aa_grid,
1610  const Index& antenna_dim,
1611  const Index& atmosphere_dim,
1612  const Index& stokes_dim,
1613  const Index& sensor_norm,
1614  const Verbosity& verbosity)
1615 {
1616  CREATE_OUT2;
1617  CREATE_OUT3;
1618 
1619  // Check input
1620 
1621  // Basic variables
1622  chk_if_in_range( "stokes_dim", stokes_dim, 1, 4 );
1623  chk_if_in_range( "antenna_dim", antenna_dim, 1, 2 );
1624  chk_if_bool( "sensor_norm", sensor_norm );
1625 
1626  // f_grid (could in fact be decreasing, but an increasing grid is
1627  // demanded in other parts).
1628  chk_if_increasing( "f_grid", f_grid );
1629 
1630  // mblock_za_grid
1631  if( mblock_za_grid.nelem() == 0 )
1632  throw runtime_error( "The measurement block zenith angle grid is empty." );
1633  if( !is_increasing(mblock_za_grid) && !is_decreasing(mblock_za_grid) )
1634  throw runtime_error(
1635  "The WSV *mblock_za_grid* must be strictly increasing or decreasing." );
1636 
1637  // mblock_aa_grid
1638  if( antenna_dim == 1 )
1639  {
1640  if( mblock_aa_grid.nelem() != 0 )
1641  throw runtime_error(
1642  "For antenna_dim = 1, the azimuthal angle grid must be empty." );
1643  }
1644  else
1645  {
1646  if( atmosphere_dim < 3 )
1647  throw runtime_error( "2D antennas (antenna_dim=2) can only be "
1648  "used with 3D atmospheres." );
1649  if( mblock_aa_grid.nelem() == 0 )
1650  {
1651  ostringstream os;
1652  os << "The measurement block azimuthal angle grid is empty despite"
1653  << "a 2D antenna pattern is flagged (*antenna_dim*).";
1654  throw runtime_error( os.str() );
1655  }
1656  if( !is_increasing(mblock_aa_grid) && !is_decreasing(mblock_aa_grid) )
1657  throw runtime_error(
1658  "The WSV *mblock_aa_grid* must be strictly increasing or decreasing." );
1659  }
1660 
1661 
1662  // Set grid variables
1663  sensor_response_f_grid = f_grid;
1664  sensor_response_za_grid = mblock_za_grid;
1665  sensor_response_aa_grid = mblock_aa_grid;
1666  //
1667  sensor_response_pol_grid.resize(stokes_dim);
1668  //
1669  for( Index is=0; is<stokes_dim; is++ )
1670  {
1671  sensor_response_pol_grid[is] = is + 1;
1672  }
1673 
1674 
1675  // Set aux variables
1676  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1677  sensor_response_za, sensor_response_aa,
1678  sensor_response_f_grid, sensor_response_pol_grid,
1679  sensor_response_za_grid, sensor_response_aa_grid, 1 );
1680 
1681  //Set response matrix to identity matrix
1682  //
1683  const Index n = sensor_response_f.nelem();
1684  //
1685  out2 << " Initialising *sensor_reponse* as a identity matrix.\n";
1686  out3 << " Size of *sensor_response*: " << n << "x" << n << "\n";
1687  //
1688  sensor_response.make_I( n, n );
1689 }
1690 
1691 
1692 /* Workspace method: Doxygen documentation will be auto-generated */
1693 void sensorOff(// WS Output:
1694  Sparse& sensor_response,
1695  Vector& sensor_response_f,
1696  ArrayOfIndex& sensor_response_pol,
1697  Vector& sensor_response_za,
1698  Vector& sensor_response_aa,
1699  Vector& sensor_response_f_grid,
1700  ArrayOfIndex& sensor_response_pol_grid,
1701  Vector& sensor_response_za_grid,
1702  Vector& sensor_response_aa_grid,
1703  Index& antenna_dim,
1704  Vector& mblock_za_grid,
1705  Vector& mblock_aa_grid,
1706  const Index& stokes_dim,
1707  const Vector& f_grid,
1708  const Verbosity& verbosity)
1709 {
1710  // Checks are done in sensor_responseInit.
1711 
1712  AntennaOff( antenna_dim, mblock_za_grid, mblock_aa_grid, verbosity );
1713 
1714  // Dummy variables (The method is independent of atmosphere_dim.
1715  // atmosphere_dim used below just for some checks when antenna is active, not
1716  // relevant here. ).
1717  const Index sensor_norm = 1, atmosphere_dim = 1;
1718 
1719  sensor_responseInit( sensor_response, sensor_response_f,
1720  sensor_response_pol, sensor_response_za, sensor_response_aa,
1721  sensor_response_f_grid, sensor_response_pol_grid,
1722  sensor_response_za_grid, sensor_response_aa_grid, f_grid,
1723  mblock_za_grid, mblock_aa_grid, antenna_dim, atmosphere_dim,
1724  stokes_dim, sensor_norm, verbosity );
1725 }
1726 
1727 
1728 
1729 /* Workspace method: Doxygen documentation will be auto-generated */
1730 void sensor_responseMixer(// WS Output:
1731  Sparse& sensor_response,
1732  Vector& sensor_response_f,
1733  ArrayOfIndex& sensor_response_pol,
1734  Vector& sensor_response_za,
1735  Vector& sensor_response_aa,
1736  Vector& sensor_response_f_grid,
1737  // WS Input:
1738  const ArrayOfIndex& sensor_response_pol_grid,
1739  const Vector& sensor_response_za_grid,
1740  const Vector& sensor_response_aa_grid,
1741  const Numeric& lo,
1742  const GriddedField1& sideband_response,
1743  const Index& sensor_norm,
1744  const Verbosity& verbosity)
1745 {
1746  CREATE_OUT3;
1747 
1748  // Some sizes
1749  const Index nf = sensor_response_f_grid.nelem();
1750  const Index npol = sensor_response_pol_grid.nelem();
1751  const Index nza = sensor_response_za_grid.nelem();
1752  const Index naa = sensor_response_aa_grid.nelem();
1753  const Index nin = nf * npol * nza;
1754  // Note that there is no distinction between za and aa grids after the antenna
1755 
1756  // Frequency grid of for sideband response specification
1757  ConstVectorView sbresponse_f_grid =
1758  sideband_response.get_numeric_grid(GFIELD1_F_GRID);
1759 
1760  // Initialise a output stream for runtime errors and a flag for errors
1761  ostringstream os;
1762  bool error_found = false;
1763 
1764  // Check that sensor_response variables are consistent in size
1765  if( sensor_response_f.nelem() != nin )
1766  {
1767  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
1768  << "grid variables (sensor_response_f_grid etc.).\n";
1769  error_found = true;
1770  }
1771  if( naa && naa != nza )
1772  {
1773  os << "Incorrect size of *sensor_response_aa_grid*.\n";
1774  error_found = true;
1775  }
1776  if( sensor_response.nrows() != nin )
1777  {
1778  os << "The sensor block response matrix *sensor_response* does not have\n"
1779  << "right size compared to the sensor grid variables\n"
1780  << "(sensor_response_f_grid etc.).\n";
1781  error_found = true;
1782  }
1783 
1784  // Check that the lo frequency is within the sensor_response_f_grid
1785  if( lo <= sensor_response_f_grid[0] || lo >= last(sensor_response_f_grid) )
1786  {
1787  os << "The given local oscillator frequency is outside the sensor\n"
1788  << "frequency grid. It must be within the *sensor_response_f_grid*.\n";
1789  error_found = true;
1790  }
1791 
1792  // Checks of sideband_response, partly in combination with lo
1793  if( sbresponse_f_grid.nelem() != sideband_response.data.nelem() )
1794  {
1795  os << "Mismatch in size of grid and data in *sideband_response*.\n";
1796  error_found = true;
1797  }
1798  if( sbresponse_f_grid.nelem() < 2 )
1799  {
1800  os << "At least two data points must be specified in "
1801  << "*sideband_response*.\n";
1802  error_found = true;
1803  }
1804  if( !is_increasing( sbresponse_f_grid ) )
1805  {
1806  os << "The frequency grid of *sideband_response* must be strictly\n"
1807  << "increasing.\n";
1808  error_found = true;
1809  }
1810  if( fabs(last(sbresponse_f_grid)+sbresponse_f_grid[0]) > 0 )
1811  {
1812  os << "The end points of the *sideband_response* frequency grid must be\n"
1813  << "symmetrically placed around 0. That is, the grid shall cover a\n"
1814  << "a range that can be written as [-df,df]. \n";
1815  error_found = true;
1816  }
1817 
1818  // Check that response function does not extend outside sensor_response_f_grid
1819  Numeric df_high = lo + last(sbresponse_f_grid) - last(sensor_response_f_grid);
1820  Numeric df_low = sensor_response_f_grid[0] - lo - sbresponse_f_grid[0];
1821  if( df_high > 0 && df_low > 0 )
1822  {
1823  os << "The *sensor_response_f* grid must be extended by at least\n"
1824  << df_low << " Hz in the lower end and " << df_high << " Hz in the\n"
1825  << "upper end to cover frequency range set by *sideband_response*\n"
1826  << "and *lo*. Or can the frequency grid of *sideband_response* be\n"
1827  << "decreased?";
1828  error_found = true;
1829  }
1830  else if( df_high > 0 )
1831  {
1832  os << "The *sensor_response_f* grid must be extended by at " << df_high
1833  << " Hz\nin the upper end to cover frequency range set by\n"
1834  << "*sideband_response* and *lo*. Or can the frequency grid of\n"
1835  << "*sideband_response* be decreased?";
1836  error_found = true;
1837  }
1838  else if( df_low > 0 )
1839  {
1840  os << "The *sensor_response_f* grid must be extended by at " << df_low
1841  << " Hz\nin the lower end to cover frequency range set by\n"
1842  << "*sideband_response* and *lo*. Or can the frequency grid of\n"
1843  << "*sideband_response* be decreased?";
1844  error_found = true;
1845  }
1846 
1847  // If errors where found throw runtime_error with the collected error
1848  // message.
1849  if (error_found)
1850  throw runtime_error(os.str());
1851 
1852 
1853  //Call the core function
1854  //
1855  Sparse hmixer;
1856  Vector f_mixer;
1857  //
1858  mixer_matrix( hmixer, f_mixer, lo, sideband_response,
1859  sensor_response_f_grid, npol, nza, sensor_norm );
1860 
1861  // Here we need a temporary sparse that is copy of the sensor_response
1862  // sparse matrix. We need it since the multiplication function can not
1863  // take the same object as both input and output.
1864  Sparse htmp = sensor_response;
1865  sensor_response.resize( hmixer.nrows(), htmp.ncols() );
1866  mult( sensor_response, hmixer, htmp );
1867 
1868  // Some extra output.
1869  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
1870  << "x" << sensor_response.ncols() << "\n";
1871 
1872  // Update sensor_response_f_grid
1873  sensor_response_f_grid = f_mixer;
1874 
1875  // Set aux variables
1876  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
1877  sensor_response_za, sensor_response_aa,
1878  sensor_response_f_grid, sensor_response_pol_grid,
1879  sensor_response_za_grid, sensor_response_aa_grid, 0 );
1880 }
1881 
1882 
1883 
1885  Sparse& sensor_response,
1886  Vector& sensor_response_f,
1887  ArrayOfIndex& sensor_response_pol,
1888  Vector& sensor_response_za,
1889  Vector& sensor_response_aa,
1890  Vector& sensor_response_f_grid,
1891  // WS Input:
1892  const ArrayOfIndex& sensor_response_pol_grid,
1893  const Vector& sensor_response_za_grid,
1894  const Vector& sensor_response_aa_grid,
1895  const Vector& lo_multi,
1896  const ArrayOfGriddedField1& sideband_response_multi,
1897  const ArrayOfString& sideband_mode_multi,
1898  const ArrayOfVector& f_backend_multi,
1899  const ArrayOfArrayOfGriddedField1& backend_channel_response_multi,
1900  const Index& sensor_norm,
1901  const Verbosity& verbosity)
1902 {
1903  // Some sizes
1904  const Index nf = sensor_response_f_grid.nelem();
1905  const Index npol = sensor_response_pol_grid.nelem();
1906  const Index nza = sensor_response_za_grid.nelem();
1907  const Index naa = sensor_response_aa_grid.nelem();
1908  const Index nin = nf * npol * nza;
1909  // Note that there is no distinction between za and aa grids after the antenna
1910  const Index nlo = lo_multi.nelem();
1911 
1912  // Initialise a output stream for runtime errors and a flag for errors
1913  ostringstream os;
1914  bool error_found = false;
1915 
1916  // Check that sensor_response variables are consistent in size
1917  if( sensor_response_f.nelem() != nin )
1918  {
1919  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
1920  << "grid variables (sensor_response_f_grid etc.).\n";
1921  error_found = true;
1922  }
1923  if( naa && naa != nza )
1924  {
1925  os << "Incorrect size of *sensor_response_aa_grid*.\n";
1926  error_found = true;
1927  }
1928  if( sensor_response.nrows() != nin )
1929  {
1930  os << "The sensor block response matrix *sensor_response* does not have\n"
1931  << "right size compared to the sensor grid variables\n"
1932  << "(sensor_response_f_grid etc.).\n";
1933  error_found = true;
1934  }
1935 
1936  // Check that response data are consistent with respect to number of
1937  // mixer/reciever chains.
1938  if( sideband_response_multi.nelem() != nlo )
1939  {
1940  os << "Inconsistency in length between *lo_mixer* and "
1941  << "*sideband_response_multi*.\n";
1942  error_found = true;
1943  }
1944  if( sideband_mode_multi.nelem() != nlo )
1945  {
1946  os << "Inconsistency in length between *lo_mixer* and "
1947  << "*sideband_mode_multi*.\n";
1948  error_found = true;
1949  }
1950  if( f_backend_multi.nelem() != nlo )
1951  {
1952  os << "Inconsistency in length between *lo_mixer* and "
1953  << "*f_backend_multi*.\n";
1954  error_found = true;
1955  }
1956  if( backend_channel_response_multi.nelem() != nlo )
1957  {
1958  os << "Inconsistency in length between *lo_mixer* and "
1959  << "*backend_channel_response_multi*.\n";
1960  error_found = true;
1961  }
1962 
1963  // If errors where found throw runtime_error with the collected error
1964  // message. Data for each mixer and reciever chain are checked below.
1965  if (error_found)
1966  throw runtime_error(os.str());
1967 
1968 
1969  // Variables for data to be appended
1970  Array<Sparse> sr;
1971  ArrayOfVector srfgrid;
1972  ArrayOfIndex cumsumf(nlo+1,0);
1973 
1974  for( Index ilo=0; ilo<nlo; ilo++ )
1975  {
1976  // Copies of variables that will be changed, but must be
1977  // restored for next loop
1978  Sparse sr1 = sensor_response;
1979  Vector srf1 = sensor_response_f;
1980  ArrayOfIndex srpol1 = sensor_response_pol;
1981  Vector srza1 = sensor_response_za;
1982  Vector sraa1 = sensor_response_aa;
1983  Vector srfgrid1 = sensor_response_f_grid;
1984 
1985  // Call single reciever methods. Try/catch for improved error message.
1986  try
1987  {
1988  sensor_responseMixer(sr1, srf1, srpol1, srza1, sraa1, srfgrid1,
1989  sensor_response_pol_grid,
1990  sensor_response_za_grid,
1991  sensor_response_aa_grid,
1992  lo_multi[ilo],
1993  sideband_response_multi[ilo],
1994  sensor_norm,
1995  verbosity);
1996 
1997  sensor_responseIF2RF(srf1, srfgrid1,
1998  lo_multi[ilo],
1999  sideband_mode_multi[ilo],
2000  verbosity);
2001 
2002  sensor_responseBackend(sr1, srf1, srpol1, srza1, sraa1, srfgrid1,
2003  sensor_response_pol_grid,
2004  sensor_response_za_grid,
2005  sensor_response_aa_grid,
2006  f_backend_multi[ilo],
2007  backend_channel_response_multi[ilo],
2008  sensor_norm, verbosity);
2009  }
2010  catch( runtime_error e )
2011  {
2012  ostringstream os2;
2013  os2 << "Error when dealing with receiver/mixer chain (1-based index) "
2014  << ilo+1 << ":\n" << e.what();
2015  throw runtime_error(os2.str());
2016  }
2017 
2018  // Store in temporary arrays
2019  sr.push_back( sr1 );
2020  srfgrid.push_back( srfgrid1 );
2021  //
2022  cumsumf[ilo+1] = cumsumf[ilo] + srfgrid1.nelem();
2023  }
2024 
2025  // Append data to create sensor_response_f_grid
2026  //
2027  const Index nfnew = cumsumf[nlo];
2028  sensor_response_f_grid.resize( nfnew );
2029  //
2030  for( Index ilo=0; ilo<nlo; ilo++ )
2031  {
2032  for( Index i=0; i<srfgrid[ilo].nelem(); i++ )
2033  {
2034  sensor_response_f_grid[cumsumf[ilo]+i] = srfgrid[ilo][i];
2035  }
2036  }
2037 
2038  // Append data to create total sensor_response
2039  //
2040  const Index ncols = sr[0].ncols();
2041  const Index npolnew = sensor_response_pol_grid.nelem();
2042  const Index nfpolnew = nfnew * npolnew;
2043  //
2044  sensor_response.resize( nza*nfpolnew, ncols );
2045  //
2046  Vector dummy( ncols, 0.0 );
2047  //
2048  for( Index ilo=0; ilo<nlo; ilo++ )
2049  {
2050  const Index nfpolthis = (cumsumf[ilo+1]-cumsumf[ilo]) * npolnew;
2051 
2052  assert( sr[ilo].nrows() == nza*nfpolthis );
2053  assert( sr[ilo].ncols() == ncols );
2054 
2055  for( Index iz=0; iz<nza; iz++ )
2056  {
2057  for( Index i=0; i<nfpolthis; i++ )
2058  {
2059  // "Poor mans" transfer of a row from one sparse to another
2060  for( Index ic=0; ic<ncols; ic++ )
2061  { dummy[ic] = sr[ilo](iz*nfpolthis+i,ic); }
2062 
2063  sensor_response.insert_row( iz*nfpolnew+cumsumf[ilo]*npolnew+i,
2064  dummy );
2065  }
2066  }
2067  }
2068 
2069  // Set aux variables
2070  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
2071  sensor_response_za, sensor_response_aa,
2072  sensor_response_f_grid, sensor_response_pol_grid,
2073  sensor_response_za_grid, sensor_response_aa_grid, 0 );
2074 }
2075 
2076 
2077 
2078 /* Workspace method: Doxygen documentation will be auto-generated */
2080  Sparse& sensor_response,
2081  Vector& sensor_response_f,
2082  ArrayOfIndex& sensor_response_pol,
2083  Vector& sensor_response_za,
2084  Vector& sensor_response_aa,
2085  ArrayOfIndex& sensor_response_pol_grid,
2086  // WS Input:
2087  const Vector& sensor_response_f_grid,
2088  const Vector& sensor_response_za_grid,
2089  const Vector& sensor_response_aa_grid,
2090  const Index& stokes_dim,
2091  const String& y_unit,
2092  const ArrayOfIndex& sensor_pol,
2093  const Verbosity&)
2094 {
2095  // Vectors for extracting polarisation components
2096  //
2097  Numeric w = 0.5;
2098  if( y_unit == "PlanckBT" || y_unit == "RJBT" )
2099  { w = 1.0; }
2100  //
2101  ArrayOfVector pv;
2102  stokes2pol( pv, w );
2103 
2104  // Some sizes
2105  const Index nnew = sensor_pol.nelem();
2106  const Index nf = sensor_response_f_grid.nelem();
2107  const Index npol = sensor_response_pol_grid.nelem();
2108  const Index nza = sensor_response_za_grid.nelem();
2109 
2110  // Initialise an output stream for runtime errors and a flag for errors
2111  ostringstream os;
2112  bool error_found = false;
2113 
2114  // For this method, the za and aa can be both "independent" and "dependent".
2115  // The size of sensor_response resolves this
2116  //
2117  bool za_aa_independent = true;
2118  //
2119  const Index naa = max( Index(1), sensor_response_aa_grid.nelem() );
2120  Index nfz = nf * nza;
2121  Index nin = nfz *npol;
2122  //
2123  if( sensor_response.nrows() == nin )
2124  { za_aa_independent = false; }
2125  else if( sensor_response.nrows() == nin*naa )
2126  { nfz *= naa; nin *= naa; }
2127  else
2128  {
2129  os << "The sensor block response matrix *sensor_response* does not have\n"
2130  << "right size compared to the sensor grid variables\n"
2131  << "(sensor_response_f_grid etc.).\n";
2132  error_found = true;
2133  }
2134 
2135  // Check that sensor_response variables are consistent in size
2136  if( sensor_response_f.nelem() != nin )
2137  {
2138  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
2139  << "grid variables (sensor_response_f_grid etc.).\n";
2140  error_found = true;
2141  }
2142  if( naa && naa != nza )
2143  {
2144  os << "Incorrect size of *sensor_response_aa_grid*.\n";
2145  error_found = true;
2146  }
2147  if( npol != stokes_dim )
2148  {
2149  os << "Number of input polarisation does not match *stokes_dim*.\n";
2150  error_found = true;
2151  }
2152  if( nnew == 0 )
2153  {
2154  os << "The WSV *sensor_pol* can not be empty.\n";
2155  error_found = true;
2156  }
2157  // If errors where found throw runtime_error with the collected error
2158  // message (before it gets too long)
2159  if( error_found )
2160  throw runtime_error(os.str());
2161 
2162  // Check polarisation data more in detail
2163  for( Index i=0; i<npol && !error_found; i++ )
2164  {
2165  if( sensor_response_pol_grid[i] != i+1 )
2166  {
2167  os << "The input polarisations must be I, Q, U and V (up to "
2168  << "stokes_dim). It seems that input data are for other "
2169  << "polarisation components.";
2170  error_found = true;
2171  }
2172  }
2173  for( Index i=0; i<nnew && !error_found; i++ )
2174  {
2175  if( sensor_pol[i] < 1 || sensor_pol[i] > 10 )
2176  {
2177  os <<
2178  "The elements of *sensor_pol* must be inside the range [1,10].\n";
2179  error_found = true;
2180  }
2181  }
2182  // If errors where found throw runtime_error with the collected error
2183  // message (before it gets too long)
2184  if( error_found )
2185  throw runtime_error(os.str());
2186 
2187  for( Index i=0; i<nnew && !error_found; i++ )
2188  {
2189  if( pv[sensor_pol[i]-1].nelem() > stokes_dim )
2190  {
2191  os << "You have selected an output polarisation that is not covered "
2192  << "by present value of *stokes_dim* (the later has to be "
2193  << "increased).";
2194  error_found = true;
2195  }
2196  }
2197  // If errors where found throw runtime_error with the collected error
2198  // message
2199  if( error_found )
2200  throw runtime_error(os.str());
2201 
2202  // Form H matrix representing polarisation response
2203  //
2204  Sparse Hpol( nfz*nnew, nin );
2205  Vector hrow( nin, 0.0 );
2206  Index row = 0;
2207  //
2208  for( Index i=0; i<nfz; i++ )
2209  {
2210  Index col = i*npol;
2211  for( Index in=0; in<nnew; in++ )
2212  {
2213  Index p = sensor_pol[in] - 1;
2214  //
2215  for( Index iv=0; iv<pv[p].nelem(); iv++ )
2216  { hrow[col+iv] = pv[p][iv]; }
2217  //
2218  Hpol.insert_row( row, hrow );
2219  //
2220  hrow = 0;
2221  row += 1;
2222  }
2223  }
2224 
2225  // Here we need a temporary sparse that is copy of the sensor_response
2226  // sparse matrix. We need it since the multiplication function can not
2227  // take the same object as both input and output.
2228  Sparse Htmp = sensor_response;
2229  sensor_response.resize( Hpol.nrows(), Htmp.ncols());
2230  mult( sensor_response, Hpol, Htmp );
2231 
2232  // Update sensor_response_pol_grid
2233  sensor_response_pol_grid = sensor_pol;
2234 
2235  // Set aux variables
2236  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
2237  sensor_response_za, sensor_response_aa,
2238  sensor_response_f_grid, sensor_response_pol_grid,
2239  sensor_response_za_grid, sensor_response_aa_grid,
2240  za_aa_independent );
2241 }
2242 
2243 
2244 
2245 /* Workspace method: Doxygen documentation will be auto-generated */
2247  Sparse& sensor_response,
2248  const Vector& sensor_response_f_grid,
2249  const ArrayOfIndex& sensor_response_pol_grid,
2250  const Vector& sensor_response_za_grid,
2251  const Vector& sensor_response_aa_grid,
2252  const Index& stokes_dim,
2253  const Matrix& stokes_rotation,
2254  const Verbosity& )
2255 {
2256  // Basic checks
2257  chk_if_in_range( "stokes_dim", stokes_dim, 1, 4 );
2258 
2259  // Some sizes
2260  const Index nf = sensor_response_f_grid.nelem();
2261  const Index npol = sensor_response_pol_grid.nelem();
2262  const Index nza = sensor_response_za_grid.nelem();
2263  const Index naa = max( Index(1), sensor_response_aa_grid.nelem() );
2264  const Index nin = nf * npol * nza * naa;
2265 
2266 
2267  //---------------------------------------------------------------------------
2268  // Initialise a output stream for runtime errors and a flag for errors
2269  ostringstream os;
2270  bool error_found = false;
2271 
2272  // Check that sensor_response variables are consistent in size
2273  if( sensor_response.nrows() != nin )
2274  {
2275  os << "The sensor block response matrix *sensor_response* does not have\n"
2276  << "right size compared to the sensor grid variables\n"
2277  << "(sensor_response_f_grid etc.).\n";
2278  error_found = true;
2279  }
2280 
2281  // Check special stuff for this method
2282  if( stokes_dim < 3 )
2283  {
2284  os << "To perform a rotation of the Stokes coordinate system,\n"
2285  << "*stokes_dim* must be >= 3.\n";
2286  error_found = true;
2287  }
2288  if( stokes_rotation.nrows() != nza || stokes_rotation.ncols() != naa )
2289  {
2290  os << "Incorrect number of angles in *stokes_rotation*. The size of this\n"
2291  << "matrix must match *sensor_response_za_grid* and "
2292  << "*sensor_response_aa_grid*.\n";
2293  error_found = true;
2294  }
2295 
2296  // If errors where found throw runtime_error with the collected error
2297  // message.
2298  if (error_found)
2299  throw runtime_error(os.str());
2300  //---------------------------------------------------------------------------
2301 
2302 
2303  // Set up the H matrix for applying rotation
2304  //
2305  Sparse hrot( sensor_response.nrows(), sensor_response.ncols() );
2306  Vector row( hrot.ncols(), 0 );
2307  Index irow = -1;
2308  //
2309  for( Index iaa=0; iaa<naa; iaa++ )
2310  {
2311  for( Index iza=0; iza<nza; iza++ )
2312  {
2313  const Numeric a = cos( 2 * DEG2RAD * stokes_rotation(iza,iaa) );
2314  const Numeric b = sin( 2 * DEG2RAD * stokes_rotation(iza,iaa) );
2315 
2316  for( Index ifr=0; ifr<nf; ifr++ )
2317  {
2318  for( Index ip=0; ip<npol; ip++ )
2319  {
2320  irow += 1;
2321  if( ip==0 || ip==3 ) // Row 1 and 4 of Mueller matrix
2322  { row[irow] = 1; }
2323  else if( ip==1 ) // Row 2
2324  { row[irow] = a; row[irow+1] = b; }
2325  else // Row 3
2326  { row[irow] = a; row[irow-1] = -b; }
2327  hrot.insert_row( irow, row );
2328  // Make sure that set values are not affecting next row
2329  // (note that column irow+1 always will be overwritten)
2330  row[irow] = 0;
2331  if( ip == 2 )
2332  { row[irow-1] = 0; }
2333  }
2334  }
2335  }
2336  }
2337 
2338  // Here we need a temporary sparse that is copy of the sensor_response
2339  // sparse matrix. We need it since the multiplication function can not
2340  // take the same object as both input and output.
2341  Sparse htmp = sensor_response;
2342  sensor_response.resize( htmp.nrows(), htmp.ncols()); //Just in case!
2343  mult( sensor_response, hrot, htmp );
2344 }
2345 
2346 
2347 
2348 void sensor_responseGenericAMSU(// WS Output:
2349  Vector& f_grid,
2350  Index& antenna_dim,
2351  Vector& mblock_za_grid,
2352  Vector& mblock_aa_grid,
2353  Sparse& sensor_response,
2354  Vector& sensor_response_f,
2355  ArrayOfIndex& sensor_response_pol,
2356  Vector& sensor_response_za,
2357  Vector& sensor_response_aa,
2358  Vector& sensor_response_f_grid,
2359  ArrayOfIndex& sensor_response_pol_grid,
2360  Vector& sensor_response_za_grid,
2361  Vector& sensor_response_aa_grid,
2362  Index& sensor_norm,
2363  // WS Input:
2364  const Index& atmosphere_dim,
2365  const Index& stokes_dim,
2366  const Matrix& sensor_description_amsu,
2367  // WS Generic Input:
2368  const Numeric& spacing,
2369  const Verbosity& verbosity)
2370  {
2371  // Number of instrument channels:
2372  const Index n = sensor_description_amsu.nrows();
2373  const Index m = sensor_description_amsu.ncols();
2374 
2375  // FIXME - Oscar Isoz-090413
2376  // add error checks
2377  //
2378 
2379  // The meaning of the columns in sensor_description_amsu is:
2380  // LO frequency, channel center offset from LO, channel width, second offset from LO (to be extended if needed);
2381  // (All in Hz.)
2382  //
2383  if(5>sensor_description_amsu.ncols() )
2384  {
2385  ostringstream os;
2386  os << "Input variable sensor_description_amsu must have atleast five columns, but it has "
2387  << sensor_description_amsu.ncols() << ".";
2388  throw runtime_error( os.str() );
2389  }
2390 
2391  ConstVectorView lo_multi = sensor_description_amsu(Range(joker),0);
2392  ConstMatrixView offset = sensor_description_amsu(Range(joker),Range(1,m-2)); // Remember to ignore column 2..
2393  ConstVectorView verbosityVectIn = sensor_description_amsu(Range(joker),m-1);
2394  ConstVectorView width = sensor_description_amsu(Range(joker),m-2);
2395 
2396 
2397 
2398  //is there any undefined verbosity values in the vector?
2399  //Set the verbosity to one third of the bandwidth to make sure that the passband flanks does't overlap
2400  const Numeric minRatioVerbosityVsFdiff = 10; // To be used when to passbands are closer then one verbosity value
2401  Index totNumPb =0;
2402  Vector verbosityVect(n);
2403 
2404  for(Index idx = 0;idx<n;++idx)
2405  {
2406  if((verbosityVectIn[idx] ==0) || (verbosityVectIn[idx]> width[idx]))
2407  {
2408  verbosityVect[idx] = ((Numeric)width[idx])/3;
2409  }
2410  else
2411  {
2412  verbosityVect[idx] = verbosityVectIn[idx];
2413  }
2414  }
2415 
2416  // Create a vector to store the number of passbands (PB) for each channel
2417  Vector numPBpseudo(n); // store values used for calc
2418  Vector numPB(n); // Store the true values
2419  // Find the number of IFs for each channel and calculate the number of passbands
2420  for (Index i=0;i<n;++i)
2421  {
2422  numPB[i] = 0 ; // make sure that it is zero
2423  for(Index j=0;j<(m-2);++j)
2424  {
2425  if(j!=2)
2426  {
2427  if (offset(i,j)>0)
2428  {
2429  numPB[i]++;
2430  }
2431  }
2432  }
2433  numPB[i] = 1 << (int)numPB[i] ;// number of passbands= 2^numLO
2434  if(numPB[i] ==1)
2435  {
2436  numPBpseudo[i] = 2;
2437  }
2438  else
2439  {
2440  numPBpseudo[i] = numPB[i];
2441  }
2442 
2443  totNumPb += (int)numPB[i];
2444 
2445  if(numPB[i]>4)
2446  {
2447  ostringstream os;
2448  os << "This function does currently not support more than 4 passbands per channel"
2449  << numPB[i]<< ".";
2450  throw runtime_error( os.str() );
2451  }
2452  }
2453 
2454  // Find the center frequencies for all sub-channels
2455  // Create one center frequency for each passband
2456  ArrayOfArrayOfGriddedField1 backend_channel_response_multi(n);
2457  ArrayOfVector f_backend_multi(n); // changed !!!
2458  for (Index i=0; i<n; ++i)
2459  {
2460  // Channel frequencies
2461  Vector &f = f_backend_multi[i];
2462  f.resize(1);
2463  f[0] = lo_multi[i]+0.0*width[i];//(offset(i,0)+offset(i,1));
2464 
2465  //channel response
2466  const Index numVal = 4;
2467  backend_channel_response_multi[i].resize(1);
2468  GriddedField1& b_resp = backend_channel_response_multi[i][0];
2469  b_resp.set_name("Backend channel response function");
2470  b_resp.resize(numVal*(Index)numPBpseudo[i]);
2471  Vector f_range(numVal*(Index)numPBpseudo[i]);
2472  Numeric pbOffset = 0;
2473  b_resp.set_grid_name(0,"Frequency");
2474 
2475  Numeric slope =0; // 1900;
2476  // To avoid overlapping passbands in the AMSU-A sensor, reduce the passbands of each channel by a few Hz
2477  for(Index pbOffsetIdx = 0;pbOffsetIdx<numPBpseudo[i];++pbOffsetIdx)
2478  { // Filter response
2479  slope = width[i]/100;
2480  f_range[pbOffsetIdx*numVal+0] = -0.5*width[i]-0*slope;
2481  f_range[pbOffsetIdx*numVal+1] = -0.5*width[i]+1*slope;
2482  f_range[pbOffsetIdx*numVal+2] = +0.5*width[i]-1*slope;
2483  f_range[pbOffsetIdx*numVal+3] = +0.5*width[i]+0*slope;
2484 
2485  b_resp.data[pbOffsetIdx*numVal+0] = 0.0/numPB[i];;
2486  b_resp.data[pbOffsetIdx*numVal+1] = 1.0/numPB[i];
2487  b_resp.data[pbOffsetIdx*numVal+2] = 1.0/numPB[i];
2488  b_resp.data[pbOffsetIdx*numVal+3] = 0.0/numPB[i];
2489 
2490  if(numPB[i] ==1)
2491  {
2492  if(pbOffsetIdx==0)
2493  {
2494  pbOffset = -0.0*width[i];
2495  b_resp.data[pbOffsetIdx*numVal+0] = 0;
2496  b_resp.data[pbOffsetIdx*numVal+1] = 1.0/1;
2497 
2498  b_resp.data[pbOffsetIdx*numVal+2] = 1.0/1;
2499  b_resp.data[pbOffsetIdx*numVal+3] = 1.0/1;
2500  f_range[pbOffsetIdx*numVal+0] = -0.5*width[i]-2*slope;
2501  f_range[pbOffsetIdx*numVal+1] = -0.5*width[i]-1*slope;
2502  f_range[pbOffsetIdx*numVal+2] = -0.5*width[i]+1*slope;
2503  f_range[pbOffsetIdx*numVal+3] = -0.5*width[i]+2*slope;
2504  }
2505  if(pbOffsetIdx ==1)
2506  {
2507  pbOffset = 0.0*width[i]; //just a dummy band
2508  b_resp.data[pbOffsetIdx*numVal+0] = 1.0/1;
2509  b_resp.data[pbOffsetIdx*numVal+1] = 1.0/1;
2510  b_resp.data[pbOffsetIdx*numVal+2] = 1.0/1;
2511  b_resp.data[pbOffsetIdx*numVal+3] = 0;
2512  f_range[pbOffsetIdx*numVal+0] = +0.5*width[i]-3*slope;
2513  f_range[pbOffsetIdx*numVal+1] = +0.5*width[i]-2*slope;
2514  f_range[pbOffsetIdx*numVal+2] = +0.5*width[i]-1*slope;
2515  f_range[pbOffsetIdx*numVal+3] = +0.5*width[i]+0*slope-10;
2516  // without the extra '-10' it will fail due to too narrow backend sensor response.
2517  }
2518  }
2519  else if (numPB[i]==2)
2520  { // move the passband in frequency to the correct frequency
2521  if(pbOffsetIdx==0)
2522  {
2523  pbOffset = -offset(i,0);
2524  }
2525  if(pbOffsetIdx==1)
2526  {
2527  pbOffset = offset(i,0);
2528  }
2529  }
2530  if(numPB[i]==4)
2531  {
2532  if(pbOffsetIdx==0)
2533  {
2534  pbOffset = -offset(i,0)-offset(i,1);
2535  }
2536  if(pbOffsetIdx==1)
2537  {
2538  pbOffset = -offset(i,0)+offset(i,1);
2539  }
2540  if(pbOffsetIdx==2)
2541  {
2542  pbOffset = offset(i,0)-offset(i,1);
2543  }
2544  if(pbOffsetIdx==3)
2545  {
2546  pbOffset = offset(i,0)+offset(i,1);
2547  }
2548  }
2549  for(Index iii=0;iii<numVal;++iii)
2550  {
2551  f_range[pbOffsetIdx*numVal+iii] += 1*pbOffset;
2552  }
2553  }
2554  // Are any passbands overlapping?
2555  for(Index ii = 2;ii<(f_range.nelem()-2);++ii)
2556  {
2557  if(((b_resp.data[ii-1]==1) && (b_resp.data[ii]==0) &&(b_resp.data[ii+1]==0) && (b_resp.data[ii+2]==1)))
2558  {
2559  if((f_range[ii]>=f_range[ii+1])) // Overlapping passbands
2560  {
2561  if((f_range[ii+2]-f_range[ii-1])>verbosityVectIn[i]) // difference in frequency between passbands
2562  {
2563  f_range[ii+1] = f_range[ii+2]-verbosityVectIn[i]/2;
2564  f_range[ii] = f_range[ii-1]+verbosityVectIn[i]/2;
2565  }
2566  else
2567  {
2568  f_range[ii-1] = (f_range[ii]+f_range[ii+2])/2-2*verbosityVectIn[i]/minRatioVerbosityVsFdiff;
2569  f_range[ii+1] = (f_range[ii]+f_range[ii+2])/2+verbosityVectIn[i]/minRatioVerbosityVsFdiff;
2570  f_range[ii] = f_range[ii-1]+verbosityVectIn[i]/minRatioVerbosityVsFdiff ;
2571  f_range[ii+2] = f_range[ii+1]+verbosityVectIn[i]/minRatioVerbosityVsFdiff;
2572  }
2573  }
2574  }
2575  }
2576  b_resp.set_grid(0,f_range);
2577  }
2578 
2579  // construct sideband response
2580  ArrayOfGriddedField1 sideband_response_multi(n);
2581  for (Index i=0;i<n;++i)
2582  {
2583  GriddedField1& r = sideband_response_multi[i];
2584  r.set_name("Sideband response function");
2585  r.resize((Index)numPBpseudo[i]);
2586  Vector f((Index)numPBpseudo[i]);
2587  if(numPB[i]==1)
2588  {
2589  r.data[0]=0.5;
2590  f[0] = -.0*width[i];
2591  r.data[1]=0.5;
2592  f[1] = .0*width[i];
2593  }
2594  else if(numPB[i]==2)
2595  {
2596  r.data[0]=1/numPB[i];
2597  r.data[1]=1/numPB[i];
2598  f[0] = -1*offset(i,0)-0.5*width[i];
2599  f[1] = +1*offset(i,0)+0.5*width[i];
2600  }
2601  else if(numPB[i]==4)
2602  {
2603  r.data[0]=1/numPB[i];
2604  r.data[1]=1/numPB[i];
2605  r.data[2]=1/numPB[i];
2606  r.data[3]=1/numPB[i];
2607  f[0] = -offset(i,0)-offset(i,1)-0.5*width[i];;
2608  f[1] = -offset(i,0)+offset(i,1)-0.5*width[i];;
2609  f[2] = +offset(i,0)-offset(i,1)+0.5*width[i];;
2610  f[3] = +offset(i,0)+offset(i,1)+0.5*width[i];;
2611 
2612  }
2613  r.set_grid_name(0, "Frequency");
2614  r.set_grid(0,f);
2615  }
2616 
2617  sensor_norm =1;
2619  // out
2620  f_grid,
2621  // in
2622  f_backend_multi,
2623  backend_channel_response_multi,
2624  spacing,
2625  verbosityVect,
2626  verbosity);
2627 
2628  // do some final work...
2629  AntennaOff( // out
2630  antenna_dim,
2631  mblock_za_grid,
2632  mblock_aa_grid,
2633  // in
2634  verbosity);
2635 
2637  // out
2638  sensor_response,
2639  sensor_response_f, sensor_response_pol,
2640  sensor_response_za, sensor_response_aa,
2641  sensor_response_f_grid,
2642  sensor_response_pol_grid,
2643  sensor_response_za_grid,
2644  sensor_response_aa_grid,
2645  // in
2646  f_grid,
2647  mblock_za_grid,
2648  mblock_aa_grid,
2649  antenna_dim,
2650  atmosphere_dim,
2651  stokes_dim,
2652  sensor_norm,
2653  verbosity);
2654 
2655  Index numLO = lo_multi.nelem();
2656  // Variables for data to be appended
2657  // Based on code from m_sensor->sensor_responseMultiMixerBackend()
2658  Array<Sparse> sr;
2659  ArrayOfVector srfgrid;
2660  ArrayOfIndex cumsumf(numLO+1,0);
2661  const Index nza = sensor_response_za_grid.nelem();
2662 
2663  // Do this for all channels ....
2664  for( Index idxLO = 0; idxLO < numLO ; idxLO++)
2665  {
2666  Sparse sr1 = sensor_response;
2667  Vector srf1 = sensor_response_f;
2668  ArrayOfIndex srpol1 = sensor_response_pol;
2669  Vector srza1 = sensor_response_za;
2670  Vector sraa1 = sensor_response_aa;
2671  Vector srfgrid1 = sensor_response_f_grid;
2672 
2673 
2674  sensor_responseBackend( // out
2675  sr1,
2676  srf1,
2677  srpol1,
2678  srza1,
2679  sraa1,
2680  srfgrid1,
2681 
2682  //in
2683  sensor_response_pol_grid,
2684  sensor_response_za_grid,
2685  sensor_response_aa_grid,
2686  f_backend_multi[idxLO],
2687  backend_channel_response_multi[idxLO],
2688  sensor_norm,
2689  verbosity);
2690 
2691  // Store in temporary arrays
2692  sr.push_back( sr1 );
2693  srfgrid.push_back( srfgrid1 );
2694  //
2695  cumsumf[idxLO+1] = cumsumf[idxLO] + srfgrid1.nelem();
2696 
2697  }
2698 
2699  // Append data to create sensor_response_f_grid
2700  //
2701  const Index nfnew = cumsumf[numLO];
2702  sensor_response_f_grid.resize( nfnew );
2703  //
2704  for( Index ilo=0; ilo<numLO; ilo++ )
2705  {
2706  for( Index i=0; i<srfgrid[ilo].nelem(); i++ )
2707  {
2708  sensor_response_f_grid[cumsumf[ilo]+i] = srfgrid[ilo][i];
2709 
2710  }
2711  }
2712 
2713 
2714  const Index ncols = sr[0].ncols();
2715  const Index npolnew = sensor_response_pol_grid.nelem();
2716  const Index nfpolnew = nfnew * npolnew;
2717  //
2718  sensor_response.resize( nza*nfpolnew, ncols );
2719  //
2720  Vector dummy( ncols, 0.0 );
2721  //
2722  for( Index ilo=0; ilo<numLO; ilo++ )
2723  {
2724  const Index nfpolthis = (cumsumf[ilo+1]-cumsumf[ilo]) * npolnew;
2725 
2726  assert( sr[ilo].nrows() == nza*nfpolthis );
2727  assert( sr[ilo].ncols() == ncols );
2728 
2729  for( Index iz=0; iz<nza; iz++ )
2730  {
2731  for( Index i=0; i<nfpolthis; i++ )
2732  {
2733  // "Poor mans" transfer of a row from one sparse to another
2734  for( Index ic=0; ic<ncols; ic++ )
2735  { dummy[ic] = sr[ilo](iz*nfpolthis+i,ic); }
2736 
2737  sensor_response.insert_row( iz*nfpolnew+cumsumf[ilo]*npolnew+i,
2738  dummy );
2739  }
2740  }
2741  }
2742 
2743  sensor_aux_vectors( // out
2744  sensor_response_f,
2745  sensor_response_pol,
2746  sensor_response_za,
2747  sensor_response_aa,
2748  sensor_response_f_grid,
2749  //in
2750  sensor_response_pol_grid,
2751  sensor_response_za_grid,
2752  sensor_response_aa_grid,
2753  0);
2754  }
2755 
2756 
2757 void sensor_responseSimpleAMSU(// WS Output:
2758  Vector& f_grid,
2759  Index& antenna_dim,
2760  Vector& mblock_za_grid,
2761  Vector& mblock_aa_grid,
2762  Sparse& sensor_response,
2763  Vector& sensor_response_f,
2764  ArrayOfIndex& sensor_response_pol,
2765  Vector& sensor_response_za,
2766  Vector& sensor_response_aa,
2767  Vector& sensor_response_f_grid,
2768  ArrayOfIndex& sensor_response_pol_grid,
2769  Vector& sensor_response_za_grid,
2770  Vector& sensor_response_aa_grid,
2771  Index& sensor_norm,
2772  // WS Input:
2773  const Index& atmosphere_dim,
2774  const Index& stokes_dim,
2775  const Matrix& sensor_description_amsu,
2776  // WS Generic Input:
2777  const Numeric& spacing,
2778  const Verbosity& verbosity)
2779 {
2780  // Check that sensor_description_amsu has the right dimension:
2781  if ( 3 != sensor_description_amsu.ncols() )
2782  {
2783  ostringstream os;
2784  os << "Input variable sensor_description_amsu must have three columns, but it has "
2785  << sensor_description_amsu.ncols() << ".";
2786  throw runtime_error( os.str() );
2787  }
2788 
2789  // Number of instrument channels:
2790  const Index n = sensor_description_amsu.nrows();
2791 
2792  // The meaning of the columns in sensor_description_amsu is:
2793  // LO frequency, channel center offset from LO, channel width.
2794  // (All in Hz.)
2795  ConstVectorView lo_multi = sensor_description_amsu(Range(joker),0);
2796  ConstVectorView offset = sensor_description_amsu(Range(joker),1);
2797  ConstVectorView width = sensor_description_amsu(Range(joker),2);
2798 
2799  // Channel frequencies:
2800  ArrayOfVector f_backend_multi(n);
2801  for (Index i=0; i<n; ++i) {
2802  Vector& f = f_backend_multi[i];
2803  f.resize(1);
2804  f[0] = lo_multi[i] + offset[i];
2805  }
2806 
2807  // Construct channel response
2808  ArrayOfArrayOfGriddedField1 backend_channel_response_multi(n);
2809  for (Index i=0; i<n; ++i) {
2810  backend_channel_response_multi[i].resize(1);
2811  GriddedField1& r = backend_channel_response_multi[i][0];
2812  r.set_name("Backend channel response function");
2813  r.resize(2);
2814 
2815  // Frequency range:
2816  Vector f(2);
2817  f[0] = - 0.5 * width[i];
2818  f[1] = + 0.5 * width[i];
2819  r.set_grid_name(0, "Frequency");
2820  r.set_grid(0,f);
2821 
2822  // Response:
2823  r.data[0] = 1;
2824  r.data[1] = 1;
2825  }
2826 
2827  // Construct sideband response:
2828  ArrayOfGriddedField1 sideband_response_multi(n);
2829  for (Index i=0; i<n; ++i) {
2830  GriddedField1& r = sideband_response_multi[i];
2831  r.set_name("Sideband response function");
2832  r.resize(2);
2833 
2834  // Frequency range:
2835  Vector f(2);
2836  f[0] = - (offset[i] + 0.5*width[i]);
2837  f[1] = + (offset[i] + 0.5*width[i]);
2838  r.set_grid_name(0, "Frequency");
2839  r.set_grid(0,f);
2840 
2841  // Response:
2842  r.data[0] = 0.5;
2843  r.data[1] = 0.5;
2844  }
2845 
2846  // Set sideband mode:
2847  ArrayOfString sideband_mode_multi(n,"upper");
2848 
2849  // We want to automatically normalize the sensor response data, so set sensor_norm to 1:
2850  sensor_norm = 1;
2851 
2852  // Now the rest is just to use some workspace methods:
2853  // ---------------------------------------------------
2854 
2855  f_gridFromSensorAMSU(f_grid, lo_multi,
2856  f_backend_multi, backend_channel_response_multi,
2857  spacing, verbosity);
2858 
2859  AntennaOff(antenna_dim, mblock_za_grid, mblock_aa_grid, verbosity);
2860 
2861  sensor_responseInit(sensor_response,
2862  sensor_response_f, sensor_response_pol,
2863  sensor_response_za, sensor_response_aa,
2864  sensor_response_f_grid,
2865  sensor_response_pol_grid,
2866  sensor_response_za_grid,
2867  sensor_response_aa_grid, f_grid, mblock_za_grid,
2868  mblock_aa_grid, antenna_dim, atmosphere_dim,
2869  stokes_dim, sensor_norm,
2870  verbosity);
2871 
2872  sensor_responseMultiMixerBackend(sensor_response, sensor_response_f,
2873  sensor_response_pol,
2874  sensor_response_za,
2875  sensor_response_aa,
2876  sensor_response_f_grid,
2877  sensor_response_pol_grid,
2878  sensor_response_za_grid,
2879  sensor_response_aa_grid, lo_multi,
2880  sideband_response_multi,
2881  sideband_mode_multi,
2882  f_backend_multi,
2883  backend_channel_response_multi,
2884  sensor_norm,
2885  verbosity);
2886 
2887 }
2888 
2889 // Declare select functions needed by WMRFSelectChannels:
2890 
2891 void Select(// WS Generic Output:
2892  Vector& needles,
2893  // WS Generic Input:
2894  const Vector& haystack,
2895  const ArrayOfIndex& needleind,
2896  const Verbosity& verbosity);
2897 
2898 template< class T >
2899 void Select(// WS Generic Output:
2900  Array<T>& needles,
2901  // WS Generic Input:
2902  const Array<T>& haystack,
2903  const ArrayOfIndex& needleind,
2904  const Verbosity& verbosity);
2905 
2906 void Select(// WS Generic Output:
2907  Sparse& needles,
2908  // WS Generic Input:
2909  const Sparse& haystack,
2910  const ArrayOfIndex& needleind,
2911  const Verbosity& verbosity);
2912 
2913 /* Workspace method: Doxygen documentation will be auto-generated */
2914 void WMRFSelectChannels(// WS Output:
2915  Vector& f_grid,
2916  Sparse& wmrf_weights,
2917  Vector& f_backend,
2918  // WS Input:
2919  const ArrayOfIndex& wmrf_channels,
2920  const Verbosity& verbosity)
2921 {
2922  CREATE_OUT2;
2923  CREATE_OUT3;
2924 
2925  // For error messages:
2926  ostringstream os;
2927 
2928  // Some checks of input parameters:
2929 
2930  // wmrf_weights must have same number of rows as f_backend, and same
2931  // number of columns as f_grid.
2932  if ( (wmrf_weights.nrows() != f_backend.nelem()) ||
2933  (wmrf_weights.ncols() != f_grid.nelem()) )
2934  {
2935  os << "The WSV *wmrf_weights* must have same number of rows as\n"
2936  << "*f_backend*, and same number of columns as *f_grid*.\n"
2937  << "wmrf_weights.nrows() = " << wmrf_weights.nrows() << "\n"
2938  << "f_backend.nelem() = " << f_backend.nelem() << "\n"
2939  << "wmrf_weights.ncols() = " << wmrf_weights.ncols() << "\n"
2940  << "f_grid.nelem() = " << f_grid.nelem();
2941  throw runtime_error(os.str());
2942  }
2943 
2944  // wmrf_channels must be strictly increasing (no repetitions).
2945  chk_if_increasing("wmrf_channels", wmrf_channels);
2946 
2947  // All selected channels must be within the original set of
2948  // channels.
2949  if ( min(wmrf_channels)<0 )
2950  {
2951  os << "Min(wmrf_channels) must be >= 0, but it is "
2952  << min(wmrf_channels) << ".";
2953  }
2954  if ( max(wmrf_channels)>=f_backend.nelem() )
2955  {
2956  os << "Max(wmrf_channels) must be less than the total number of channels.\n"
2957  << "(We use zero-based indexing!)\n"
2958  << "The actual value you have is "
2959  << max(wmrf_channels) << ".";
2960  }
2961 
2962  if (wmrf_channels.nelem()==f_backend.nelem())
2963  {
2964  // No channels to be removed, I can return the original grid.
2965  out2 << " Retaining all channels.\n";
2966  }
2967  else
2968  {
2969  out2 << " Reducing number of channels from "
2970  << f_backend.nelem() << " to " << wmrf_channels.nelem() << ".\n";
2971  }
2972 
2973 
2974  // Now the real work starts:
2975 
2976  // 1. Remove unwanted channels from f_backend:
2977  Select(f_backend, f_backend, wmrf_channels, verbosity);
2978 
2979  // 2. Remove unwanted channels from wmrf_weights. (We also have to
2980  // do something about the frequency dimension of wmrf_weights, but
2981  // we'll do that later.)
2982  Select(wmrf_weights, wmrf_weights, wmrf_channels, verbosity);
2983 
2984  // 3. Identify, which frequencies are still needed, and which are
2985  // now obsolete. We store the still needed frequencies in an
2986  // ArrayOfIndex.
2987 
2988  // Create f_grid_array. We do not store the frequencies themselves,
2989  // but the indices of the frequencies to use.
2990  ArrayOfIndex selection;
2991  // Make sure that selection does not have to be reallocated along
2992  // the way. (This is purely to improve performance a bit.)
2993  selection.reserve(f_grid.nelem());
2994 
2995  // Go through f_grid, and check for each frequency whether it is in
2996  // the set of WMRF frequencies for any of the channels.
2997  assert( wmrf_weights.nrows() == f_backend.nelem() );
2998  assert( wmrf_weights.ncols() == f_grid.nelem() );
2999  for (Index fi=0; fi<wmrf_weights.ncols(); ++fi)
3000  {
3001  Index i;
3002  for (i=0; i<wmrf_weights.nrows(); ++i)
3003  {
3004  if ( wmrf_weights(i,fi) != 0 )
3005  {
3006  selection.push_back(fi);
3007  break;
3008  }
3009  }
3010  if (i==wmrf_weights.nrows())
3011  {
3012  out3 << " The frequency with index " << fi
3013  << " is not used by any channel.\n";
3014  }
3015  }
3016 
3017  if (selection.nelem()==f_grid.nelem())
3018  {
3019  // No frequencies were removed, I can return the original grid.
3020  out2 << " No unnecessary frequencies, leaving f_grid untouched.\n";
3021  }
3022  else if (selection.nelem() == 0)
3023  {
3024  throw runtime_error("No frequencies found for any selected channels.\n");
3025  }
3026  else
3027  {
3028  out2 << " Reducing number of frequency grid points from "
3029  << f_grid.nelem() << " to " << selection.nelem() << ".\n";
3030  }
3031 
3032  // 4. Select the right frequencies in f_grid:
3033  Select(f_grid, f_grid, selection, verbosity);
3034 
3035  // 5. Select the right frequencies in wmrf_weights. This is a bit
3036  // tricky, since Select works on the row dimension. So we have to
3037  // take the transpose.
3038  Sparse wt(wmrf_weights.ncols(), wmrf_weights.nrows());
3039  transpose(wt, wmrf_weights);
3040  Select(wt, wt, selection, verbosity);
3041  wmrf_weights.resize(wt.ncols(), wt.nrows());
3042  transpose(wmrf_weights, wt);
3043 }
3044 
3045 
3046 /* Workspace method: Doxygen documentation will be auto-generated */
3047 void sensor_responseWMRF(// WS Output:
3048  Sparse& sensor_response,
3049  Vector& sensor_response_f,
3050  ArrayOfIndex& sensor_response_pol,
3051  Vector& sensor_response_za,
3052  Vector& sensor_response_aa,
3053  Vector& sensor_response_f_grid,
3054  // WS Input:
3055  const ArrayOfIndex& sensor_response_pol_grid,
3056  const Vector& sensor_response_za_grid,
3057  const Vector& sensor_response_aa_grid,
3058  const Sparse& wmrf_weights,
3059  const Vector& f_backend,
3060  const Verbosity& verbosity)
3061 {
3062  CREATE_OUT3;
3063 
3064  // Some sizes
3065  const Index nf = sensor_response_f_grid.nelem();
3066  const Index npol = sensor_response_pol_grid.nelem();
3067  const Index nza = sensor_response_za_grid.nelem();
3068  const Index naa = sensor_response_aa_grid.nelem();
3069  const Index nin = nf * npol * nza;
3070  // Note that there is no distinction between za and aa grids after the antenna
3071 
3072  // Initialise output stream for runtime errors and a flag for errors
3073  ostringstream os;
3074  bool error_found = false;
3075 
3076  // Check that sensor_response variables are consistent in size
3077  if( sensor_response_f.nelem() != nin )
3078  {
3079  os << "Inconsistency in size between *sensor_response_f* and the sensor\n"
3080  << "grid variables (sensor_response_f_grid etc.).\n";
3081  error_found = true;
3082  }
3083  if( naa && naa != nza )
3084  {
3085  os << "Incorrect size of *sensor_response_aa_grid*.\n";
3086  error_found = true;
3087  }
3088  if( sensor_response.nrows() != nin )
3089  {
3090  os << "The sensor block response matrix *sensor_response* does not have\n"
3091  << "right size compared to the sensor grid variables\n"
3092  << "(sensor_response_f_grid etc.).\n";
3093  error_found = true;
3094  }
3095 
3096  if (nin == 0)
3097  {
3098  os << "One of f_grid, pol_grid, za_grid are empty. Sizes are: ("
3099  << nf << ", " << npol << ", " << nza << ")" << "\n";
3100  error_found = true;
3101  }
3102 
3103  // Check number of rows in WMRF weight matrix
3104  //
3105  const Index nrw = wmrf_weights.nrows();
3106  //
3107  if( nrw != f_backend.nelem() )
3108  {
3109  os << "The WSV *wmrf_weights* must have as many rows\n"
3110  << "as *f_backend* has elements.\n"
3111  << "wmrf_weights.nrows() = " << nrw << "\n"
3112  << "f_backend.nelem() = " << f_backend.nelem() << "\n";
3113  error_found = true;
3114  }
3115 
3116  // Check number of columns in WMRF weight matrix
3117  //
3118  const Index ncw = wmrf_weights.ncols();
3119  //
3120  if( ncw != sensor_response_f_grid.nelem() )
3121  {
3122  os << "The WSV *wmrf_weights* must have as many columns\n"
3123  << "as *sensor_response_f_grid* has elements.\n"
3124  << "wmrf_weights.ncols() = " << ncw << "\n"
3125  << "sensor_response_f_grid.nelem() = " << sensor_response_f_grid.nelem() << "\n";
3126  error_found = true;
3127  }
3128 
3129  // If errors where found throw runtime_error with the collected error
3130  // message (before error message gets too long).
3131  if( error_found )
3132  throw runtime_error(os.str());
3133 
3134 
3135  // Ok, now the actual work.
3136 
3137  // Here we need a temporary sparse that is copy of the sensor_response
3138  // sparse matrix. We need it since the multiplication function can not
3139  // take the same object as both input and output.
3140  Sparse htmp = sensor_response;
3141  sensor_response.resize( wmrf_weights.nrows(), htmp.ncols());
3142  mult( sensor_response, wmrf_weights, htmp );
3143 
3144  // Some extra output.
3145  out3 << " Size of *sensor_response*: " << sensor_response.nrows()
3146  << "x" << sensor_response.ncols() << "\n";
3147 
3148  // Update sensor_response_f_grid
3149  sensor_response_f_grid = f_backend;
3150 
3151  // Set aux variables
3152  sensor_aux_vectors( sensor_response_f, sensor_response_pol,
3153  sensor_response_za, sensor_response_aa,
3154  sensor_response_f_grid, sensor_response_pol_grid,
3155  sensor_response_za_grid, sensor_response_aa_grid, 0 );
3156 
3157 }
3158 
3159 
3160 
3161 /* Workspace method: Doxygen documentation will be auto-generated */
3163  Vector& y,
3164  Vector& y_f,
3165  const Matrix& iy,
3166  const Index& stokes_dim,
3167  const Vector& f_grid,
3168  const Numeric& df,
3169  const Verbosity& verbosity )
3170 {
3171  // Some dummy values
3172  const Index sensor_norm = 1, atmosphere_dim = 1;
3173 
3174  // Init sensor reponse
3175  //
3176  Index antenna_dim;
3177  Vector mblock_za_grid, mblock_aa_grid, sensor_response_f;
3178  Vector sensor_response_za, sensor_response_aa, sensor_response_f_grid;
3179  Vector sensor_response_za_grid, sensor_response_aa_grid;
3180  ArrayOfIndex sensor_response_pol, sensor_response_pol_grid;
3181  Sparse sensor_response;
3182  //
3183  AntennaOff( antenna_dim, mblock_za_grid, mblock_aa_grid, verbosity );
3184 
3185 
3186  sensor_responseInit( sensor_response, sensor_response_f,
3187  sensor_response_pol, sensor_response_za, sensor_response_aa,
3188  sensor_response_f_grid, sensor_response_pol_grid,
3189  sensor_response_za_grid, sensor_response_aa_grid, f_grid,
3190  mblock_za_grid, mblock_aa_grid, antenna_dim, atmosphere_dim,
3191  stokes_dim, sensor_norm, verbosity );
3192 
3193  // Center position of "channels"
3194  Vector f_backend;
3195  linspace( f_backend, f_grid[0]+df/2, last(f_grid), df );
3196 
3197  // Create channel response
3199  backend_channel_responseFlat( r, df, verbosity );
3200 
3201  // New sensor response
3202  sensor_responseBackend( sensor_response, sensor_response_f,
3203  sensor_response_pol, sensor_response_za,
3204  sensor_response_aa, sensor_response_f_grid,
3205  sensor_response_pol_grid, sensor_response_za_grid,
3206  sensor_response_aa_grid, f_backend, r, sensor_norm,
3207  verbosity );
3208 
3209  // Some sizes
3210  const Index nf = f_grid.nelem();
3211  const Index n = sensor_response.nrows();
3212 
3213  // Convert iy to a vector
3214  //
3215  Vector iyb( nf*stokes_dim );
3216  //
3217  for( Index is=0; is<stokes_dim; is++ )
3218  { iyb[Range(is,nf,stokes_dim)] = iy(joker,is); }
3219 
3220  // y and y_f
3221  //
3222  y_f = sensor_response_f;
3223  y.resize( n );
3224  mult( y, sensor_response, iyb );
3225 }
3226 
void AntennaOff(Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, const Verbosity &verbosity)
WORKSPACE METHOD: AntennaOff.
Definition: m_sensor.cc:196
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:35
const Numeric PI
void gaussian_response(Vector &y, const Vector &x, const Numeric &x0, const Numeric &fwhm)
gaussian_response
Definition: sensor.cc:452
void gaussian_response_autogrid(Vector &x, Vector &y, const Numeric &x0, const Numeric &fwhm, const Numeric &xwidth_si, const Numeric &dx_si)
gaussian_response_autogrid
Definition: sensor.cc:412
void sensor_responseGenericAMSU(Vector &f_grid, Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, ArrayOfIndex &sensor_response_pol_grid, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, Index &sensor_norm, const Index &atmosphere_dim, const Index &stokes_dim, const Matrix &sensor_description_amsu, const Numeric &spacing, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseGenericAMSU.
Definition: m_sensor.cc:2348
void spectrometer_matrix(Sparse &H, ConstVectorView ch_f, const ArrayOfGriddedField1 &ch_response, ConstVectorView sensor_f, const Index &n_pol, const Index &n_sp, const Index &do_norm)
spectrometer_matrix
Definition: sensor.cc:1096
ConstMatrixView transpose(ConstMatrixView m)
Const version of transpose.
Definition: matpackI.cc:1799
Index nelem() const
Number of elements.
Definition: array.h:176
void resize(const GriddedField1 &gf)
Make this GriddedField1 the same size as the given one.
void f_gridFromSensorAMSUgeneric(Vector &f_grid, const ArrayOfVector &f_backend_multi, const ArrayOfArrayOfGriddedField1 &backend_channel_response_multi, const Numeric &spacing, const Vector &verbosityVect, const Verbosity &verbosity)
WORKSPACE METHOD: f_gridFromSensorAMSUgeneric.
Definition: m_sensor.cc:522
Explicit construction of Arrays.
Definition: make_array.h:51
Declarations having to do with the four output streams.
const Index GFIELD4_ZA_GRID
void chk_if_increasing(const String &x_name, const ArrayOfIndex &x)
chk_if_increasing
Definition: check_input.cc:132
ConstVectorView get_numeric_grid(Index i) const
Get a numeric grid.
void f_gridFromSensorHIRS(Vector &f_grid, const Vector &f_backend, const ArrayOfGriddedField1 &backend_channel_response, const Numeric &spacing, const Verbosity &verbosity)
WORKSPACE METHOD: f_gridFromSensorHIRS.
Definition: m_sensor.cc:689
The Vector class.
Definition: matpackI.h:556
Numeric interp(ConstVectorView itw, ConstVectorView a, const GridPos &tc)
Red 1D Interpolate.
void antenna1d_matrix(Sparse &H, const Index &antenna_dim, ConstMatrixView antenna_los, const GriddedField4 &antenna_response, ConstVectorView za_grid, ConstVectorView f_grid, const Index n_pol, const Index do_norm)
antenna1d_matrix
Definition: sensor.cc:79
const ArrayOfString & get_string_grid(Index i) const
Get a string grid.
bool is_increasing(ConstVectorView x)
Checks if a vector is sorted and strictly increasing.
Definition: logic.cc:275
The Sparse class.
Definition: matpackII.h:55
const Index GFIELD1_F_GRID
Numeric last(ConstVectorView x)
last
Definition: math_funcs.cc:183
The range class.
Definition: matpackI.h:148
bool is_multiple(const Index &x, const Index &y)
Checks if an integer is a multiple of another integer.
Definition: logic.cc:74
void sensor_responseSimpleAMSU(Vector &f_grid, Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, ArrayOfIndex &sensor_response_pol_grid, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, Index &sensor_norm, const Index &atmosphere_dim, const Index &stokes_dim, const Matrix &sensor_description_amsu, const Numeric &spacing, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseSimpleAMSU.
Definition: m_sensor.cc:2757
Index ncols() const
Returns the number of columns.
Definition: matpackII.cc:62
void sensor_responseAntenna(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, const Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Index &atmosphere_dim, const Index &antenna_dim, const Matrix &antenna_los, const GriddedField4 &antenna_response, const Index &sensor_norm, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseAntenna.
Definition: m_sensor.cc:763
void sensor_responseFillFgrid(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Index &polyorder, const Index &nfill, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseFillFgrid.
Definition: m_sensor.cc:1456
void sensor_aux_vectors(Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, ConstVectorView sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, ConstVectorView sensor_response_za_grid, ConstVectorView sensor_response_aa_grid, const Index za_aa_independent)
sensor_aux_vectors
Definition: sensor.cc:627
void AntennaConstantGaussian1D(Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, GriddedField4 &r, Matrix &antenna_los, const Index &n_za_grid, const Numeric &fwhm, const Numeric &xwidth_si, const Numeric &dx_si, const Verbosity &verbosity)
WORKSPACE METHOD: AntennaConstantGaussian1D.
Definition: m_sensor.cc:74
cmplx FADDEEVA() w(cmplx z, double relerr)
Definition: Faddeeva.cc:679
void sensor_responseBeamSwitching(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, const Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Numeric &w1, const Numeric &w2, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseBeamSwitching.
Definition: m_sensor.cc:1262
This file contains basic functions to handle XML data files.
void sensor_responseStokesRotation(Sparse &sensor_response, const Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Index &stokes_dim, const Matrix &stokes_rotation, const Verbosity &)
WORKSPACE METHOD: sensor_responseStokesRotation.
Definition: m_sensor.cc:2246
void sensorOff(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, ArrayOfIndex &sensor_response_pol_grid, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, const Index &stokes_dim, const Vector &f_grid, const Verbosity &verbosity)
WORKSPACE METHOD: sensorOff.
Definition: m_sensor.cc:1693
void chk_if_bool(const String &x_name, const Index &x)
chk_if_bool
Definition: check_input.cc:73
void WMRFSelectChannels(Vector &f_grid, Sparse &wmrf_weights, Vector &f_backend, const ArrayOfIndex &wmrf_channels, const Verbosity &verbosity)
WORKSPACE METHOD: WMRFSelectChannels.
Definition: m_sensor.cc:2914
void make_I(Index r, Index c)
Make Identity matrix.
Definition: matpackII.cc:430
void sensor_responseBackendFrequencySwitching(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Vector &f_backend, const ArrayOfGriddedField1 &backend_channel_response, const Index &sensor_norm, const Numeric &df1, const Numeric &df2, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseBackendFrequencySwitching.
Definition: m_sensor.cc:1201
const Index GFIELD4_F_GRID
void backend_channel_responseGaussian(ArrayOfGriddedField1 &r, const Numeric &fwhm, const Numeric &xwidth_si, const Numeric &dx_si, const Verbosity &)
WORKSPACE METHOD: backend_channel_responseGaussian.
Definition: m_sensor.cc:360
void AntennaMultiBeamsToPencilBeams(Matrix &sensor_pos, Matrix &sensor_los, Matrix &antenna_los, Index &antenna_dim, Vector &mblock_za_grid, Vector &mblock_aa_grid, const Index &atmosphere_dim, const Verbosity &verbosity)
WORKSPACE METHOD: AntennaMultiBeamsToPencilBeams.
Definition: m_sensor.cc:120
Index nelem() const
Returns the number of elements.
Definition: matpackI.cc:180
Contains sorting routines.
void sensor_responseIF2RF(Vector &sensor_response_f, Vector &sensor_response_f_grid, const Numeric &lo, const String &sideband_mode, const Verbosity &)
WORKSPACE METHOD: sensor_responseIF2RF.
Definition: m_sensor.cc:1414
void mult(VectorView y, const ConstMatrixView &M, const ConstVectorView &x)
Matrix Vector multiplication.
Definition: matpackI.cc:1648
Sensor modelling functions.
The implementation for String, the ARTS string class.
Definition: mystring.h:63
Index ncols() const
Returns the number of columns.
Definition: matpackI.cc:838
The global header file for ARTS.
void sensor_responseWMRF(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Sparse &wmrf_weights, const Vector &f_backend, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseWMRF.
Definition: m_sensor.cc:3047
#define max(a, b)
Definition: continua.cc:20461
void set_grid(Index i, const Vector &g)
Set a numeric grid.
void sensor_responseMixer(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Numeric &lo, const GriddedField1 &sideband_response, const Index &sensor_norm, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseMixer.
Definition: m_sensor.cc:1730
void nlinspace(Vector &x, const Numeric start, const Numeric stop, const Index n)
nlinspace
Definition: math_funcs.cc:261
void ySimpleSpectrometer(Vector &y, Vector &y_f, const Matrix &iy, const Index &stokes_dim, const Vector &f_grid, const Numeric &df, const Verbosity &verbosity)
WORKSPACE METHOD: ySimpleSpectrometer.
Definition: m_sensor.cc:3162
Index nrows() const
Returns the number of rows.
Definition: matpackII.cc:56
const Numeric SPEED_OF_LIGHT
void sensor_responseFrequencySwitching(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseFrequencySwitching.
Definition: m_sensor.cc:1337
void Select(Vector &needles, const Vector &haystack, const ArrayOfIndex &needleind, const Verbosity &verbosity)
Definition: m_select.h:72
void sensor_responsePolarisation(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_f_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Index &stokes_dim, const String &y_unit, const ArrayOfIndex &sensor_pol, const Verbosity &)
WORKSPACE METHOD: sensor_responsePolarisation.
Definition: m_sensor.cc:2079
virtual void checksize_strict() const
Strict consistency check.
void gridpos(ArrayOfGridPos &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Numeric &extpolfac)
Set up a grid position Array.
#define abs(x)
Definition: continua.cc:20458
const Numeric DEG2RAD
const Joker joker
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:29
The Matrix class.
Definition: matpackI.h:788
void VectorNLogSpace(Vector &x, const Index &n, const Numeric &start, const Numeric &stop, const Verbosity &verbosity)
WORKSPACE METHOD: VectorNLogSpace.
void f_gridFromSensorAMSU(Vector &f_grid, const Vector &lo, const ArrayOfVector &f_backend, const ArrayOfArrayOfGriddedField1 &backend_channel_response, const Numeric &spacing, const Verbosity &verbosity)
WORKSPACE METHOD: f_gridFromSensorAMSU.
Definition: m_sensor.cc:385
void gridpos_poly(ArrayOfGridPosPoly &gp, ConstVectorView old_grid, ConstVectorView new_grid, const Index order, const Numeric &extpolfac)
Set up grid positions for higher order interpolation.
void mixer_matrix(Sparse &H, Vector &f_mixer, const Numeric &lo, const GriddedField1 &filter, ConstVectorView f_grid, const Index &n_pol, const Index &n_sp, const Index &do_norm)
mixer_matrix
Definition: sensor.cc:496
Header file for special_interp.cc.
Propagation path structure and functions.
const Numeric RAD2DEG
void AntennaSet1D(Index &antenna_dim, Vector &mblock_aa_grid, const Verbosity &verbosity)
WORKSPACE METHOD: AntennaSet1D.
Definition: m_sensor.cc:218
Header file for interpolation_poly.cc.
void chk_if_in_range(const String &x_name, const Index &x, const Index &x_low, const Index &x_high)
chk_if_in_range
Definition: check_input.cc:101
void sensor_responseInit(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, ArrayOfIndex &sensor_response_pol_grid, Vector &sensor_response_za_grid, Vector &sensor_response_aa_grid, const Vector &f_grid, const Vector &mblock_za_grid, const Vector &mblock_aa_grid, const Index &antenna_dim, const Index &atmosphere_dim, const Index &stokes_dim, const Index &sensor_norm, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseInit.
Definition: m_sensor.cc:1596
void AntennaSet2D(Index &antenna_dim, const Index &atmosphere_dim, const Verbosity &verbosity)
WORKSPACE METHOD: AntennaSet2D.
Definition: m_sensor.cc:236
void resize(Index n)
Assignment operator from VectorView.
Definition: matpackI.cc:798
#define min(a, b)
Definition: continua.cc:20460
void antenna_responseGaussian(GriddedField4 &r, const Numeric &fwhm, const Numeric &xwidth_si, const Numeric &dx_si, const Verbosity &)
WORKSPACE METHOD: antenna_responseGaussian.
Definition: m_sensor.cc:256
A constant view of a Vector.
Definition: matpackI.h:292
void set_name(const String &s)
Set name of this gridded field.
const Index GFIELD4_FIELD_NAMES
void sub(Sparse &A, const Sparse &B, const Sparse &C)
Sparse - Sparse subtraction.
Definition: matpackII.cc:920
A constant view of a Matrix.
Definition: matpackI.h:596
void sensor_responseBackend(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Vector &f_backend, const ArrayOfGriddedField1 &backend_channel_response, const Index &sensor_norm, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseBackend.
Definition: m_sensor.cc:1032
void set_grid_name(Index i, const String &s)
Set grid name.
void resize(Index r, Index c)
Resize function.
Definition: matpackII.cc:469
void antenna2d_simplified(Sparse &H, const Index &antenna_dim, ConstMatrixView antenna_los, const GriddedField4 &antenna_response, ConstVectorView za_grid, ConstVectorView aa_grid, ConstVectorView f_grid, const Index n_pol, const Index do_norm)
antenna2d_simplified
Definition: sensor.cc:247
bool is_decreasing(ConstVectorView x)
Checks if a vector is sorted in reversed order and is strictly decreasing.
Definition: logic.cc:324
#define CREATE_OUT3
Definition: messages.h:214
void backend_channel_responseFlat(ArrayOfGriddedField1 &r, const Numeric &resolution, const Verbosity &)
WORKSPACE METHOD: backend_channel_responseFlat.
Definition: m_sensor.cc:338
void linspace(Vector &x, const Numeric start, const Numeric stop, const Numeric step)
linspace
Definition: math_funcs.cc:228
void interpweights(VectorView itw, const GridPos &tc)
Red 1D interpolation weights.
void insert_row(Index r, Vector v)
Insert row function.
Definition: matpackII.cc:264
void antenna_responseVaryingGaussian(GriddedField4 &r, const Numeric &leff, const Numeric &xwidth_si, const Numeric &dx_si, const Index &nf, const Numeric &fstart, const Numeric &fstop, const Verbosity &verbosity)
WORKSPACE METHOD: antenna_responseVaryingGaussian.
Definition: m_sensor.cc:290
#define CREATE_OUT2
Definition: messages.h:213
void stokes2pol(ArrayOfVector &s2p, const Numeric &nv)
stokes2pol
Definition: sensor.cc:1196
Index nrows() const
Returns the number of rows.
Definition: matpackI.cc:832
void resize(Index b, Index p, Index r, Index c)
Resize function.
Definition: matpackIV.cc:1403
void sensor_responseMultiMixerBackend(Sparse &sensor_response, Vector &sensor_response_f, ArrayOfIndex &sensor_response_pol, Vector &sensor_response_za, Vector &sensor_response_aa, Vector &sensor_response_f_grid, const ArrayOfIndex &sensor_response_pol_grid, const Vector &sensor_response_za_grid, const Vector &sensor_response_aa_grid, const Vector &lo_multi, const ArrayOfGriddedField1 &sideband_response_multi, const ArrayOfString &sideband_mode_multi, const ArrayOfVector &f_backend_multi, const ArrayOfArrayOfGriddedField1 &backend_channel_response_multi, const Index &sensor_norm, const Verbosity &verbosity)
WORKSPACE METHOD: sensor_responseMultiMixerBackend.
Definition: m_sensor.cc:1884
void resize(Index r, Index c)
Resize function.
Definition: matpackI.cc:1580
void find_effective_channel_boundaries(Vector &fmin, Vector &fmax, const Vector &f_backend, const ArrayOfGriddedField1 &backend_channel_response, const Numeric &delta, const Verbosity &verbosity)
Calculate channel boundaries from instrument response functions.
Definition: sensor.cc:1325
const Index GFIELD4_AA_GRID