001    package edu.nrao.sss.model.resource.vla;
002    
003    import java.math.BigDecimal;
004    import java.util.ArrayList;
005    import java.util.HashMap;
006    import java.util.List;
007    import java.util.Map;
008    import java.util.Set;
009    
010    import static edu.nrao.sss.math.MathUtil.MC_INTERM_CALCS;
011    
012    import edu.nrao.sss.measure.Frequency;
013    import edu.nrao.sss.measure.FrequencyUnits;
014    import edu.nrao.sss.measure.TimeDuration;
015    import edu.nrao.sss.measure.TimeUnits;
016    
017    public enum BandwidthCode
018    {
019      //                                              Max Channels
020      //                             Nominal        ---------------
021      //  Code, Contnm, Spec Line, Bandwidth (MHz), IF-1,IF-2,IF-4
022      ZERO (0,   true,    true,     "50.0",          16,   8,   4),
023      ONE  (1,   true,    true,     "25.0",          32,  16,   8),
024      TWO  (2,   true,    true,     "12.5",          64,  32,  16),
025      THREE(3,   true,    true,      "6.25",        128,  64,  32),
026      FOUR (4,   true,    true,      "3.125",       256, 128,  64),
027      FIVE (5,   true,    true,      "1.5625",      512, 256, 128),
028      SIX  (6,   true,    true,      "0.78125",     512, 256, 128),
029      SEVEN(7,   true,    false,     "0.1953125",     0,   0,   0),
030      EIGHT(8,   false,   true,      "0.1953125",   256, 128,  64),
031      NINE (9,   false,   true,      "0.1953125",   512, 256, 128);
032      
033      private static List<BandwidthCode> continuumCodes;
034      private static List<BandwidthCode> spectralLineCodes;
035      
036      private boolean              validForContinuum;
037      private boolean              validForSpectralLine;
038      private int                  codeNumber;
039      private String               bwNominal;
040      private Map<IFMode, Integer> channels;
041      
042      private BandwidthCode(int codeNum,
043                            boolean validForCont, boolean validForSpecLine,
044                            String nominalMHz,
045                            int mode1Ch, int mode2Ch, int mode4Ch)
046      {
047        codeNumber = codeNum;
048        
049        validForContinuum    = validForCont;
050        validForSpectralLine = validForSpecLine;
051        
052        bwNominal = nominalMHz;
053        
054        channels = new HashMap<IFMode, Integer>();
055        channels.put(IFMode.ONE,  mode1Ch);
056        channels.put(IFMode.TWO,  mode2Ch);
057        channels.put(IFMode.FOUR, mode4Ch);
058      }
059      
060      /**
061       * Returns the subset of codes that may be used for continuum observations.
062       * @return the subset of codes that may be used for continuum observations.
063       */
064      public static List<BandwidthCode> getContinuumCodes()
065      {
066        if (continuumCodes == null)
067        {
068          continuumCodes = new ArrayList<BandwidthCode>();
069          
070          for (BandwidthCode code : BandwidthCode.values())
071            if (code.isValidForContinuum())
072              continuumCodes.add(code);
073        }
074        
075        return new ArrayList<BandwidthCode>(continuumCodes);
076      }
077      
078      /**
079       * Returns the subset of codes that may be used for spectral line observations.
080       * @return the subset of codes that may be used for spectral line observations.
081       */
082      public static List<BandwidthCode> getSpectralLineCodes()
083      {
084        if (spectralLineCodes == null)
085        {
086          spectralLineCodes = new ArrayList<BandwidthCode>();
087          
088          for (BandwidthCode code : BandwidthCode.values())
089            if (code.isValidForSpectralLine())
090              spectralLineCodes.add(code);
091        }
092        
093        return new ArrayList<BandwidthCode>(spectralLineCodes);
094      }
095    
096      /**
097       * Returns an integer representation of this band code.
098       * @return an integer representation of this band code.
099       */
100      public int getCodeNumber()  { return codeNumber; }
101      
102      /**
103       * Returns <i>true</i> if this bandwidth code is valid for
104       * continuum observations.
105       * @return <i>true</i> if this bandwidth code is valid for
106       *         continuum observations.
107       */
108      public boolean isValidForContinuum()  { return validForContinuum; }
109      
110      /**
111       * Returns <i>true</i> if this bandwidth code is valid for
112       * spectral line observations.
113       * @return <i>true</i> if this bandwidth code is valid for
114       *         spectral line  observations.
115       */
116      public boolean isValidForSpectralLine()  { return validForSpectralLine; }
117      
118      public Frequency getBandwidthNominal()
119      {
120        return new Frequency(bwNominal, FrequencyUnits.MEGAHERTZ).normalize();
121      }
122      
123      /**
124       * Returns the bandwidth associated with this code in continuum mode.
125       * 
126       * @return the bandwidth associated with this code in continuum mode.
127       */
128      public Frequency getBandwidthForContinuum()
129      {
130        return isValidForContinuum()?
131          new Frequency(bwNominal, FrequencyUnits.MEGAHERTZ).normalize() :
132          new Frequency("0.0", FrequencyUnits.HERTZ);
133      }
134      
135      /**
136       * Returns the bandwidth for this code and the given IF mode and processing
137       * options.
138       * 
139       * @param ifMode
140       *   the IF mode for which the bandwidth is desired.
141       *   Normally the IF mode does not influence bandwidth, and this parameter
142       *   may be <i>null</i>.  However, when channel-zero processing is requested,
143       *   we need to know the width of a single channel, and the IF mode is
144       *   required for that.  (See {@link #getResolution(IFMode, Set)}.)
145       *   
146       * @param spectralLineProcessing
147       *   a list of the spectral line processing options that will be used.
148       *   This parameter may be <i>null</i>.
149       * 
150       * @return
151       *   the bandwidth for this code and the given IF mode and processing options.
152       */
153      public Frequency getBandwidthForSpectralLine(IFMode ifMode,
154                                                   Set<ProcessingType> spectralLineProcessing)
155      {
156        //Quick exit if this is not a valid spectral line mode
157        if (!isValidForSpectralLine())
158          return new Frequency("0.0", FrequencyUnits.HERTZ);
159        
160        Frequency bw = new Frequency(bwNominal, FrequencyUnits.MEGAHERTZ);
161    
162        if (!performLagProcessing(spectralLineProcessing))
163        {
164          int ch = getChannels(ifMode, spectralLineProcessing);
165          
166          BigDecimal actualChannels = new BigDecimal(ch);
167          BigDecimal idealChannels  = actualChannels.add(BigDecimal.ONE);
168          
169          bw.divideBy(idealChannels).multiplyBy(actualChannels);
170        }
171    
172        return bw.normalize();
173      }
174      
175      /**
176       * Returns the maximum number of spectral channels that can be created
177       * using this band code and the given IF mode and processing options.
178       * 
179       * @param ifMode
180       *   the IF mode for which the number of channels is desired.
181       *   
182       * @param spectralLineProcessing
183       *   a list of the spectral line processing options that will be used.
184       *   This parameter may be <i>null</i>.
185       *   
186       * @return
187       *   the maximum number of channels for the given IF mode and
188       *   processing options.
189       */
190      public int getChannels(IFMode ifMode, 
191                             Set<ProcessingType> spectralLineProcessing)
192      {
193        int ch = channels.get(ifMode);
194        
195        if (ch != 0)
196        {
197          if (performHanningSmoothing(spectralLineProcessing))
198            ch /= 2;
199          
200          //JIRA EVL-555: Lose one channel ALL the time in spectral line modes
201          //if (putPseudoContinuumInChannelZero(spectralLineProcessing))
202          ch--;
203        }
204        
205        return ch;
206      }
207      
208      /**
209       * Returns the best spectral resolution that can be obtained using
210       * this band code and the given IF mode and processing options.
211       * 
212       * @param ifMode
213       *   the IF mode for which the resolution is desired.
214       *
215       * @param spectralLineProcessing
216       *   a list of the spectral line processing options that will be used.
217       *   This parameter may be <i>null</i>.
218       *   
219       * @return the best spectral resolution for the given parameters.
220       */
221      public Frequency getResolution(IFMode ifMode, 
222                                     Set<ProcessingType> spectralLineProcessing)
223      {
224        //Quick exit if this is not a valid spectral line mode
225        if (!isValidForSpectralLine())
226          return new Frequency("0.0", FrequencyUnits.HERTZ);
227    
228        Frequency bw = getBandwidthForSpectralLine(ifMode, spectralLineProcessing);
229    
230        int ch = getChannels(ifMode, spectralLineProcessing);
231    
232        Frequency resolution = bw.divideBy(new BigDecimal(ch));
233    
234        return resolution.normalize();
235      }
236      
237      /**
238       * Returns the time resolution that can be obtained using
239       * this band code and the given IF mode and processing options.
240       * If this is the continuum bandwidth code, the returned
241       * resolution will be zero.
242       *  
243       * @param ifMode
244       *   the IF mode for which the resolution is desired.
245       * 
246       * @param spectralLineProcessing
247       *   a list of the spectral line processing options that will be used.
248       *   This parameter may be <i>null</i>.
249       *   
250       * @return
251       *   the best time resolution for the given parameters.
252       */
253      public TimeDuration getTimeResolution(IFMode ifMode,
254                                            Set<ProcessingType> spectralLineProcessing)
255      {
256        //Quick exit if this is not a valid spectral line mode
257        if (!isValidForSpectralLine())
258          return new TimeDuration("0.0", TimeUnits.SECOND);
259        
260        BigDecimal hertz =
261          getResolution(ifMode, spectralLineProcessing).toUnits(FrequencyUnits.HERTZ);
262        
263        BigDecimal seconds = BigDecimal.ONE.divide(hertz, MC_INTERM_CALCS);
264        
265        return new TimeDuration(seconds, TimeUnits.SECOND).normalize();
266      }
267      
268      /**
269       * Returns the bandwidth associated with this code in spectral line mode.
270       * 
271       * @return the bandwidth associated with this code in spectral line mode.
272       */
273      public Frequency getBandwidthForSpectralLine()
274      {
275        return getBandwidthForSpectralLine(null, null);
276      }
277      
278      /**
279       * Returns the maximum number of spectral channels that can be created
280       * using this band code and the given IF mode.
281       * 
282       * @param ifMode
283       *   the IF mode for which the number of channels is desired.
284       *   
285       * @return the maximum number of channels for the given IF mode.
286       */
287      public int getChannels(IFMode ifMode)
288      {
289        return getChannels(ifMode, null);
290      }
291    
292      /**
293       * Returns the best spectral resolution that can be obtained using
294       * this band code and the given IF mode.
295       * 
296       * @param ifMode
297       *   the IF mode for which the resolution is desired.
298       * 
299       * @return the best spectral resolution for the given IF mode.
300       */
301      public Frequency getResolution(IFMode ifMode)
302      {
303        return getResolution(ifMode, null);
304      }
305    
306      /**
307       * Returns the number of lags associated with this bandwith code
308       * and IF mode.
309       * The results from this method make sense only for the
310       * {@link ProcessingType#LAG} spectral line processing option. 
311       * 
312       * @param ifMode
313       *   the IF mode for which the number of lags is desired.
314       *   
315       * @return
316       *   the number of lags associated with this bandwith code and IF mode.
317       */
318      public int getLags(IFMode ifMode)
319      {
320        //This embeds knowledge that LAG is not allowed w/ HANNING
321        //or when using Channel Zero for pseudo-continuum.
322        return 2 * channels.get(ifMode);
323      }
324    
325      private boolean performHanningSmoothing(Set<ProcessingType> spectralLineProcessing)
326      {
327        //Must explicitly choose to do smoothing; default is to not do so
328        return
329          spectralLineProcessing != null &&
330          spectralLineProcessing.contains(ProcessingType.HANNING);
331      }
332    
333      private boolean performLagProcessing(Set<ProcessingType> spectralLineProcessing)
334      {
335        //Must explicitly choose lag processing; default is to not do so
336        return
337          spectralLineProcessing != null &&
338          spectralLineProcessing.contains(ProcessingType.LAG);
339      }
340    
341      //============================================================================
342      // 
343      //============================================================================
344      /*
345      public static void main(String... args) throws Exception
346      {
347        System.out.println("CONTINUUM");
348        for (BandwidthCode bwc : BandwidthCode.values())
349        {
350          System.out.println(bwc.getCodeNumber() +
351                             ", bw=" + bwc.getBandwidthForContinuum());
352        }
353        System.out.println();
354        
355        Set<ProcessingType> procOpts = new HashSet<ProcessingType>();
356        procOpts.add(ProcessingType.HANNING);
357    
358        for (IFMode ifMode : IFMode.values())
359        {
360          System.out.println("IF MODE " + ifMode);
361          for (BandwidthCode bwc : BandwidthCode.values())
362          {
363            System.out.println(bwc.getCodeNumber() +
364                               ", bw=" + bwc.getBandwidthForSpectralLine(ifMode, null) +
365                               ", ch=" + bwc.getChannels(ifMode, null) +
366                               ", lags=" + bwc.getLags(ifMode) +
367                               ", freqRes=" + bwc.getResolution(ifMode, null) +
368                               ", timeRes=" + bwc.getTimeResolution(ifMode, null));
369          }
370          System.out.println();
371          
372          System.out.println("IF MODE " + ifMode + " w/ HANNING");
373          for (BandwidthCode bwc : BandwidthCode.values())
374          {
375            System.out.println(bwc.getCodeNumber() +
376                               ", bw=" + bwc.getBandwidthForSpectralLine(ifMode, procOpts) +
377                               ", ch=" + bwc.getChannels(ifMode, procOpts) +
378                            //   ", lags=" + bwc.getLags(ifMode) +
379                               ", freqRes=" + bwc.getResolution(ifMode, procOpts) +
380                               ", timeRes=" + bwc.getTimeResolution(ifMode, procOpts));
381          }
382          System.out.println();
383        }
384      }
385      */
386    }