
import numpy
import noema_estimator_config as noema
import all_estimator_utils as utils
from noema_tsys_table import Noema_Tsys
from noema_continuum_table import Noema_continuum
from inter_sensitivity import Inter_Sensitivity

rad_per_sec = numpy.pi/(180*3600)
lines = []

# Frontend/Backend specifications
# NOEMA receivers
nrec = noema.pfx_nrec # Number of receivers
if (noema.obs_kind == "line"):
    freqrange = [noema.pfx_fmin,noema.pfx_fmax]
elif (noema.obs_kind == "continuum"):
    freqrange = [noema.pfx_flomin,noema.pfx_flomax]
fresrange = [noema.pfx_resmin,noema.pfx_resmax]
gim = nrec*[noema.pfx_gim]  # [   ] Image gains for all bands
iffreq = noema.pfx_iffreq  # [GHz] Intermediate Frequency
contbandwidth = nrec*[noema.pfx_contbw]  # [MHz] Continuum bandwidth

# Now we interpolate Tsys at zenith and then correct for elevation effect
# We just need the PWV assumed for the frequency
if (noema.season == "winter"):
    obspwv = noema.pfx_winterpwv  # to be updated
else:  # Summer
    obspwv = noema.pfx_summerpwv  # to be updated

# Downcase...
noema.season = noema.season.lower()
noema.sideband = noema.sideband.lower()
noema.obs_kind = noema.obs_kind.lower()
noema.bw_kind = noema.bw_kind.lower()

# Available frequency ranges
irec = None
for jrec in xrange(nrec):
    if ((freqrange[0][jrec] < noema.freq) and (noema.freq < freqrange[1][jrec])):
        irec = jrec
        break

if (irec is None):
    raise ValueError,"Frequency outside receiver range"
    #say "I-NOEMA-SENSITIVITY-ESTIMATOR, Available frequency range:"
    #say "I-NOEMA-SENSITIVITY-ESTIMATOR, Hardware: "'noema.rx_kind'" Mode: "'noema.obs_kind'
    #for jrec 1 to nrec
    #   say "I-NOEMA-SENSITIVITY-ESTIMATOR,   * Receiver "'jrec'": "'freqrange[1,jrec]'"-"'freqrange[2,jrec]'" GHz"
    #next jrec

if (irec==3):  # band 4
    raise ValueError,"Band 4 is not offered in the present call for proposals for NOEMA"

# Available frequency resolutions
if (noema.obs_kind == "continuum"):
    lines += ["Continuum observation"]
    noema.fres = contbandwidth[irec]
    noema.vres = 0.
    noema.npol = 2
elif (noema.obs_kind == "line"):
    lines += ["Line observation"]
    if (noema.bw_kind == "velocity"):
        noema.fres = 1e6*noema.vres*noema.freq/noema.clight
    elif (noema.bw_kind == "frequency"):
        noema.vres = 1e-6*(noema.clight*noema.fres/noema.freq)
    else:
        raise ValueError,"Bandwidth input kind unavailable: %s" % (noema.bw_kind,)
else:
    raise ValueError,"Observation kind unavailable: %s" % (noema.obs_kind,)

if ((noema.fres < fresrange[0]) or (noema.fres > fresrange[1])):
    raise ValueError,"Frequency resolution outside correlator range"
    # say "I-NOEMA-SENSITIVITY-ESTIMATOR, Available range: "'fresrange[1]'"-"'fresrange[2]'" MHz"
    # if (noema.bw_kind.eq."velocity") then
      # say "I-NOEMA-SENSITIVITY-ESTIMATOR, i.e.: " 'fresrange[1]*noema.clight/noema.freq/1d3' "-" 'fresrange[2]*noema.clight/noema.freq/1d3' " km/s" /format a f0.2 a f0.0 a


# Number of antenna (summer is maintenance season)
if (noema.season == "summer"):
    noema.nantcall = noema.nantdef-1
else:
    noema.nantcall = noema.nantdef
noema.nant = noema.nantcall

# Antenna efficiency values
noema.anteff = noema.pfx_anteff[irec]

# Decorrelation coefficient
if (noema.season == "winter"):
    noema.decorrel = noema.pfx_decorrel_winter[irec]
else:
    noema.decorrel = noema.pfx_decorrel_summer[irec]

# Max elevation of source:
if noema.decl < noema.declmin:
    raise ValueError,"Sensitivity estimation not valid for source lower than %i degrees" % (noema.declmin,)
noema.elev = 90-noema.burelat+noema.decl # [deg]

# Interpolation in tabulated Tsys
noema.pwv = obspwv[irec]
if (noema.obs_kind == "line"):
    # Line case
    tsystable = '../etc/noema-tsys.bin'
    t = Noema_Tsys(tsystable)
    noema.tsys = t.interpolate(noema.season,noema.freq,noema.pwv,noema.elev)
elif (noema.obs_kind == "continuum"):
    # Continuum case
    tsystable = '../etc/noema-tsys-continuum.bin'
    t = Noema_continuum(tsystable)
    tsys_all = t.interpolate(noema.season,noema.freq,noema.pwv,noema.elev)
    noema.tsys = tsys_all[1]  # Second value is the average

# Integration time
# Compute track length according to declination
if (noema.decl >= 0):
  noema.tracktime = 8.0
  noema.ntrack = noema.teltime/noema.tracktime
elif (noema.decl < -30):
  noema.tracktime = 0.0
  noema.ntrack = 0.0
else:
  # Interpolate
  ddec  = [-30.0,-20.0,-10.0,0.0]
  dtime = [  0.0,  3.9,  6.5,8.2]
  noema.tracktime = numpy.interp(noema.decl,ddec,dtime)
  if (noema.tracktime > 8):
    noema.tracktime = 8.0
  noema.ntrack = noema.teltime/noema.tracktime

# Check if mosaic and compute new obseff
if (noema.mosaic):
    if (noema.trsh):
        raise ValueError,"Cannot combine track sharing and mosaic mode"
    # Wide field imaging --> compute the number of independent fields
    lambd = noema.clight/(noema.freq*1e9)
    diameter = noema.ant_diameter
    beam_fwhm = 1.2*lambd/diameter/(60.*rad_per_sec)   # Primary beam size in arcmin
    beam_area = 0.8*beam_fwhm**2*numpy.pi/(4*numpy.log(2))  # Beam area. The 0.8 tries to take into account the primary beam truncation.
    nbeam = noema.area/beam_area
    if (nbeam < 2):
      # noema.area [mosaic area] should be larger than 2xbeam area
      raise ValueError,"Mosaic area should be larger than twice the primary beam, i.e. %0.5f arcmin^2 at %0.3f GHz" % (2*beam_area,noema.freq)
    npoint = nbeam*noema.mos_field2point
    if (noema.ntrack == 0):
        npointpertrack = 0
    else:
        npointpertrack = npoint/noema.ntrack
    #
    # Max number of pointing per track: 1 cycle of pointing completed in 2 cycles between calibrators. 
    # Minimum integration time on a pointing: typically 10s. Time to move antennas between 2 positions: typically 8s.
    npointmax = noema.mos_tcycle/(noema.mos_tintmin+noema.mos_tslew)  # i.e. 150 fields
    if (npoint > npointmax):
        lines += [ "The field area requires %0.1f pointings" % (npoint,) ]
        if (npointpertrack > npointmax):
            raise ValueError, \
              "This translates into %0.1f pointings per track that is higher than the maximum value: %0.1f\n" % (npointpertrack,npointmax) + \
              "Either increase the observing time or decrease the field of view"
        else:
            lines += [ "The number of pointings per track is lower than %0.1f" % (npointmax,) ]
    # check whether the map is not too large
    # amaxuv: max area to avoid a visibility to get out of a uvcell during the minimal integration time (10s)
    # expression taken from pety and rodriguez-fernandez 2010: Amax= (6900*SyntheBeam/(dtmin*eta))^2
    # eta = adhoc factor to ensure condition. we take 2
    bsyn = numpy.sqrt(noema.synbeam[0]*noema.synbeam[1])/60.   # Synthesized beam in arcmin
    amaxuv = (6900*bsyn/(noema.mos_tintmin*noema.mos_eta))**2  # [arcmin^2] minimum integration time = 10s, eta=2
    if (noema.area > amaxuv):
        raise ValueError, \
          "The field of view area is too large given the required spatial resolution\n" + \
          "Observations would require an integration time shorter than %0.1f sec per subscan (current mininum at NOEMA)\n" % (noema.mos_tintmin,) + \
          "Largest possible area for the required resolution: %6.3f arcmin^2" % (amaxuv,)
    #
    tint = (6900/noema.mos_eta)*(bsyn/numpy.sqrt(noema.area)) # [sec]
    if (tint >= noema.mos_tintstd):
       noema.obseff = noema.obseffref
    else:
       noema.obseff = noema.obseffref*(tint+noema.mos_tslew)/tint
else:  # Non-mosaic
    noema.obseff = noema.obseffref


# Tobs is (Ttel-nTune*40)/obseff
if noema.ntrack < 1:
    ntune = 1  # no fraction of track for short projects
else:
    ntune = noema.ntrack
noema.soutime = (noema.teltime-ntune*noema.ttune)/noema.obseff
# Now distribute onsource time according to observation mode
if (noema.mosaic):
    noema.ontime = noema.soutime/nbeam
    lines += [""]
    lines += ["         Mosaic mode: "]
    lines += [utils.format_float_93f ("Wavelength:",lambd*1e3,"mm")]
    lines += [utils.format_integer_9d("Diameter:",diameter,"m")]
    lines += [utils.format_float_91f("Beam FWHM:",beam_fwhm*60,"arcsec")]
    lines += [utils.format_float_92f("Beam area:",beam_area,"arcmin^2")]
    lines += [utils.format_float_92f("Field area:",noema.area,"arcmin^2")]
    lines += [utils.format_float_91f("N independent beams:",nbeam,"")]
    lines += [utils.format_float_91f("Total N pointings:",npoint,"")]
    if (npoint > npointmax):
       lines += [utils.format_integer_9d("Minimum N pointings per track:",npointpertrack,"")]
    lines += [utils.format_float_91f("Recommanded int time:",tint,"sec")]
    lines += [utils.format_time("On-time per pointing:",noema.ontime*3600)]
elif (noema.trsh):
    # Track sharing
    noema.ontime = noema.soutime/noema.nsou
    lines += [""]
    lines += ["Track sharing mode:"]
    lines += [utils.format_time("On-time per source:",noema.ontime*3600)]
else:
    # Single field
    noema.ontime = noema.soutime

# Available spatial resolutions: Not yet implemented.

# Computes
s = Inter_Sensitivity(noema.freq,noema.fres,noema.npol,noema.tsys,noema.decorrel,
                      noema.ontime,noema.nant,noema.anteff,noema.synbeam)
lines += s.execute()


lines += [""]
lines += [utils.format_time("Total On-time:",noema.soutime*3600)]
lines += [utils.format_time("Total telescope time:",noema.teltime*3600)]
#
lines += [utils.format_float_91f("%4.1f tracks:" % (ntune,),ntune*noema.ttune," hr for instrument set up")]
lines += [utils.format_time("Track duration:",noema.tracktime*3600)]

# Display
for l in lines:
  print l
