def geometric_delay(baselines, skypos, altaz=False, dircos=False, hadec=True,
                    units='mks', latitude=None):
    """
    ---------------------------------------------------------------------
    Estimates the geometric delays matrix for different baselines from 
    different sky positions. 

    Inputs:

    baselines: x, y, and z components of baseline vectors in a Mx3 numpy 
               array

    skypos: Nx2 (Alt-Az or HA-Dec) or Nx3 (direction cosines) numpy array
            of sky positions

    altaz: [Boolean flag, default=False] If True, skypos is in Alt-Az 
           coordinates system

    hadec: [Boolean flag, default=True] If True, skypos is in HA-Dec 
           coordinates system

    dircos: [Boolean flag, default=False] If True, skypos is in direction 
           cosines coordinates system

    units: Units of baselines. Default='mks'. Alternative is 'cgs'.

    latitude: Latitude of the observatory. Required if hadec is True.

    Outputs:

    geometric delays [NxM numpy array] Geometric delay for every combination
                     of baselines and skypos.

    ---------------------------------------------------------------------
    """

    try:
        baselines, skypos
    except NameError:
        raise NameError('baselines and/or skypos not defined in geometric_delay().')

    if (altaz)+(dircos)+(hadec) != 1:
        raise ValueError('One and only one of altaz, dircos, hadec must be set to True.')

    if hadec and (latitude is None):
        raise ValueError('Latitude must be specified when skypos is in HA-Dec format.')

    try:
        units
    except NameError:
        print 'No units provided. Assuming MKS units.'
        units = 'mks'

    if (units != 'mks') and (units != 'cgs'):
        print 'Units should be specified to be one of MKS or CGS. Default=MKS'
        print 'Proceeding with MKS units.'
        units = 'mks'

    if not isinstance(baselines, NP.ndarray):
        raise TypeError('baselines should be a Nx3 numpy array in geometric_delay().')

    if len(baselines.shape) == 1:
        baselines = baselines.reshape(1,-1)

    if baselines.shape[1] == 1:
        baselines = NP.hstack(baselines, NP.zeros((baselines.size,2)))
    elif baselines.shape[1] == 2:
        baselines = NP.hstack(baselines, NP.zeros((baselines.size,1)))
    elif baselines.shape[1] > 3:
        baselines = baselines[:,:3]

    if altaz or hadec:
        if len(skypos.shape) < 2:
            if skypos.size != 2:
                raise ValueError('Sky position in altitude-azimuth or HA-Dec should consist of 2 elements.')
            else:
                skypos = skypos.reshape(1,-1)
        elif len(skypos.shape) > 2:
            raise ValueError('Sky positions should be a Nx2 numpy array if using altitude-azimuth of HA-Dec.')
        else:
            if skypos.shape[1] != 2:
                raise ValueError('Sky positions should be a Nx2 numpy array if using altitude-azimuth of HA-Dec.')

        if altaz:
            dc = GEOM.altaz2dircos(skypos, 'degrees')
        else:
            dc = GEOM.altaz2dircos(GEOM.hadec2altaz(skypos, latitude, 'degrees'), 'degrees')
    else:
        if len(skypos.shape) < 2:
            if skypos.size != 3:
                raise ValueError('Sky position in direction cosines should consist of 3 elements.')
            else:
                skypos = skypos.reshape(1,-1)
        elif len(skypos.shape) > 2:
            raise ValueError('Sky positions should be a Nx3 numpy array if using direction cosines.')
        else:
            if skypos.shape[1] != 3:
                raise ValueError('Sky positions should be a Nx3 numpy array if using direction cosines.')
        
        dc = skypos

    # geometric_delays = delay_envelope(baselines, dc, units)[:,:,-1]
    geometric_delays = NP.dot(dc, baselines.T)/c
    return geometric_delays
    # pfbwin_interp = pfb_interp_func(chans)
    pfbwin_interp = NP.interp(chans, pfbfreq[useful_freq_range]/1e3, pfbwin[useful_freq_range])
    bandpass_shape = 10**(pfbwin_interp/10)

window = n_channels * DSP.windowing(n_channels, shape=bpass_shape, pad_width=n_pad, centering=True, area_normalize=True) 
bpass = base_bpass * bandpass_shape
n_snaps = int(t_obs/t_snap)
lst = (lst_init + (t_snap/3.6e3) * NP.arange(n_snaps)) * 15.0 # in degrees
if obs_mode == 'track':
    pointings_radec = NP.repeat(NP.asarray(pointing_init).reshape(-1,2), n_snaps, axis=0)
else:
    pointings_radec = NP.hstack((NP.asarray(lst-pointing_init[0]).reshape(-1,1), pointing_init[1]+NP.zeros(n_snaps).reshape(-1,1)))

pointings_hadec = NP.hstack(((lst-pointings_radec[:,0]).reshape(-1,1), pointings_radec[:,1].reshape(-1,1)))
pointings_altaz = GEOM.hadec2altaz(pointings_hadec, latitude, units='degrees')
pointings_dircos = GEOM.altaz2dircos(pointings_altaz, units='degrees')

## Interferometer parameters

# baseline_orientation = NP.asarray([0.0, 90.0]) # in degrees from East towards North
# baseline_length = 1000.0 + NP.zeros(2) # in m
# baseline_vect = NP.zeros((2,3))
# baseline_vect[:,0] = baseline_length * NP.cos(NP.radians(baseline_orientation))
# baseline_vect[:,1] = baseline_length * NP.sin(NP.radians(baseline_orientation))

# baseline_orientation_str = '{0:.1f}'.format(baseline_orientation)
# baseline_length_str = '{0:.1f}'.format(baseline_length)

# ia = RI.InterferometerArray(labels, bl, chans, telescope=telescope, latitude=latitude, A_eff=A_eff, freq_scale='GHz')
# # ia = RI.InterferometerArray(['B1','B2'], baseline_vect, chans, telescope=telescope, latitude=latitude, A_eff=A_eff, freq_scale='GHz')
# # ints = []
Beispiel #3
0
n_snaps = int(1.0 * t_obs / t_snap)
lst = (lst_init + (t_snap / 3.6e3) * NP.arange(n_snaps)) * 15.0  # in degrees
if obs_mode == 'track':
    pointings_radec = NP.repeat(NP.asarray(pointing_init).reshape(-1, 2),
                                n_snaps,
                                axis=0)
else:
    pointings_radec = NP.hstack(
        (NP.asarray(lst - pointing_init[0]).reshape(-1, 1),
         pointing_init[1] + NP.zeros(n_snaps).reshape(-1, 1)))

pointings_hadec = NP.hstack(((lst - pointings_radec[:, 0]).reshape(-1, 1),
                             pointings_radec[:, 1].reshape(-1, 1)))
pointings_altaz = GEOM.hadec2altaz(pointings_hadec, latitude, units='degrees')
pointings_dircos = GEOM.altaz2dircos(pointings_altaz, units='degrees')

use_GSM = args['ASM']
use_DSM = args['DSM']
use_NVSS = args['NVSS']
use_SUMSS = args['SUMSS']
use_MSS = args['MSS']
use_GLEAM = args['GLEAM']
use_PS = args['PS']

fg_str = ''
nside = args['nside']
flux_unit = args['flux_unit']

if use_GSM:
    fg_str = 'asm'
Beispiel #4
0
    snapshot_type_str = 'drift_averaged_'
if beam_switch:
    snapshot_type_str = 'beam_switches_'
if snapshots_range is not None:
    snapshot_type_str = 'snaps_{0[0]:0d}-{0[1]:0d}_'.format(snapshots_range)

duration_str = ''
if obs_mode in ['track', 'drift']:
    if (t_acc is not None) and (n_acc is not None):
        duration_str = '_{0:0d}x{1:.1f}s'.format(n_acc, t_acc)

pc = NP.asarray(pc).reshape(1,-1)
if pc_coords == 'dircos':
    pc_dircos = pc
elif pc_coords == 'altaz':
    pc_dircos = GEOM.altaz2dircos(pc, units='degrees')

spindex_seed_str = ''
if spindex_rms > 0.0:
    spindex_rms_str = '{0:.1f}'.format(spindex_rms)
else:
    spindex_rms = 0.0

if spindex_seed is not None:
    spindex_seed_str = '{0:0d}_'.format(spindex_seed)

for k in range(n_sky_sectors):
    if n_sky_sectors == 1:
        sky_sector_str = '_all_sky_'
    else:
        sky_sector_str = '_sky_sector_{0:0d}_'.format(k)
                lst = 0.5 * (lst_edges[1:] + lst_edges[:-1])
                t_snap = (lst_edges[1:] - lst_edges[:-1]) / 15.0 * 3.6e3
            else:
                lst = 0.5 * (lst_edges_left + lst_edges_right)
                t_snap = (lst_edges_right - lst_edges_left) / 15.0 * 3.6e3
        else:
            t_snap = 112.0 + NP.zeros(
                n_snaps)  # in seconds (needs to be generalized)
            lst = lst_wrapped + 0.5 * t_snap / 3.6e3 * 15.0

    # pointings_dircos_orig = GEOM.altaz2dircos(pointings_altaz_orig, units='degrees')
    # pointings_hadec_orig = GEOM.altaz2hadec(pointings_altaz_orig, latitude, units='degrees')
    # pointings_radec_orig = NP.hstack(((lst-pointings_hadec_orig[:,0]).reshape(-1,1), pointings_hadec_orig[:,1].reshape(-1,1)))
    # pointings_radec_orig[:,0] = pointings_radec_orig[:,0] % 360.0

    pointings_dircos = GEOM.altaz2dircos(pointings_altaz, units='degrees')
    pointings_hadec = GEOM.altaz2hadec(pointings_altaz,
                                       latitude,
                                       units='degrees')
    pointings_radec = NP.hstack(((lst - pointings_hadec[:, 0]).reshape(-1, 1),
                                 pointings_hadec[:, 1].reshape(-1, 1)))
    pointings_radec[:, 0] = pointings_radec[:, 0] % 360.0
    t_obs = NP.sum(t_snap)
elif pointing_info is not None:
    pointing_init = NP.asarray(pointing_info[1:])
    lst_init = pointing_info[0]
    pointing_file = None
    if t_snap is None:
        raise NameError(
            't_snap must be provided for an automated observing run')
    def observe(
        self, timestamp, Tsys, bandpass, pointing_center, skymodel, tobs, pb_min=0.1, fov_radius=None, lst=None
    ):

        if bandpass.size != self.bp.shape[1]:
            raise ValueError("bandpass length does not match.")

        self.Tsys = self.Tsys + [Tsys]
        self.vis_rms_freq = self.vis_rms_freq + [
            2.0 * FCNST.k * Tsys / self.A_eff / self.eff_Q / NP.sqrt(2) / tobs / self.freq_resolution / CNST.Jy
        ]
        self.tobs = self.tobs + [tobs]
        self.lst = self.lst + [lst]

        if self.timestamp == []:
            self.bp = NP.asarray(bandpass).reshape(1, -1)
            self.pointing_center = NP.asarray(pointing_center).reshape(1, -1)
        else:
            self.bp = NP.vstack((self.bp, NP.asarray(bandpass).reshape(1, -1)))
            self.pointing_center = NP.vstack((self.pointing_center, NP.asarray(pointing_center).reshape(1, -1)))

        pointing_lon = self.pointing_center[-1, 0]
        pointing_lat = self.pointing_center[-1, 1]

        if self.skycoords == "radec":
            if self.pointing_coords == "hadec":
                if lst is not None:
                    pointing_lon = lst - self.pointing_center[-1, 0]
                    pointing_lat = self.pointing_center[-1, 1]
                else:
                    raise ValueError(
                        "LST must be provided. Sky coordinates are in RA-Dec format while pointing center is in HA-Dec format."
                    )
            elif self.pointing_coords == "altaz":
                pointing_lonlat = lst - GEOM.altaz2hadec(self.pointing_center[-1, :], self.latitude, units="degrees")
                pointing_lon = pointing_lonlat[0]
                pointing_lat = pointing_lonlat[1]
        elif self.skycoords == "hadec":
            if self.pointing_coords == "radec":
                if lst is not None:
                    pointing_lon = lst - self.pointing_center[-1, 0]
                    pointing_lat = self.pointing_center[-1, 1]
                else:
                    raise ValueError(
                        "LST must be provided. Sky coordinates are in RA-Dec format while pointing center is in HA-Dec format."
                    )
            elif self.pointing_coords == "altaz":
                pointing_lonlat = lst - GEOM.altaz2hadec(self.pointing_center[-1, :], self.latitude, units="degrees")
                pointing_lon = pointing_lonlat[0]
                pointing_lat = pointing_lonlat[1]
        else:
            if self.pointing_coords == "radec":
                if lst is not None:
                    pointing_lonlat = GEOM.hadec2altaz(
                        NP.asarray([lst - self.pointing_center[-1, 0], self.pointing_center[-1, 1]]),
                        self.latitude,
                        units="degrees",
                    )
                    pointing_lon = pointing_lonlat[0]
                    pointing_lat = pointing_lonlat[1]
                else:
                    raise ValueError(
                        "LST must be provided. Sky coordinates are in Alt-Az format while pointing center is in RA-Dec format."
                    )
            elif self.pointing_coords == "hadec":
                pointing_lonlat = GEOM.hadec2altaz(self.pointing_center, self.latitude, units="degrees")
                pointing_lon = pointing_lonlat[0]
                pointing_lat = pointing_lonlat[1]

        pointing_phase = 0.0

        baseline_in_local_frame = self.baseline
        if self.baseline_coords == "equatorial":
            baseline_in_local_frame = GEOM.xyz2enu(self.baseline, self.latitude, "degrees")

        ptmp = self.pointing_center[-1, :]  # Convert pointing center to Alt-Az coordinates
        if self.pointing_coords == "hadec":
            ptmp = GEOM.hadec2altaz(self.pointing_center[-1, :], self.latitude, units="degrees")
        elif self.pointing_coords == "radec":
            if lst is not None:
                ptmp = GEOM.hadec2altaz(
                    NP.asarray([lst - self.pointing_center[-1, 0], self.pointing_center[-1, 1]]),
                    self.latitude,
                    units="degrees",
                )
            else:
                raise ValueError(
                    "LST must be provided. Sky coordinates are in Alt-Az format while pointing center is in RA-Dec format."
                )

        ptmp = GEOM.altaz2dircos(ptmp, "degrees")  # Convert pointing center to direction cosine coordinates

        pointing_phase = (
            2.0
            * NP.pi
            * NP.dot(baseline_in_local_frame.reshape(1, -1), ptmp.reshape(-1, 1))
            * self.channels.reshape(1, -1)
            / FCNST.c
        )

        if fov_radius is None:
            fov_radius = 90.0

        # PDB.set_trace()
        m1, m2, d12 = GEOM.spherematch(
            pointing_lon,
            pointing_lat,
            skymodel.catalog.location[:, 0],
            skymodel.catalog.location[:, 1],
            fov_radius,
            maxmatches=0,
        )

        # if fov_radius is not None:
        #     m1, m2, d12 = GEOM.spherematch(pointing_lon, pointing_lat, skymodel.catalog.location[:,0], skymodel.catalog.location[:,1], fov_radius, maxmatches=0)
        # else:
        #     m1 = [0] * skymodel.catalog.location.shape[0]
        #     m2 = xrange(skymodel.catalog.location.shape[0])
        #     d12 = GEOM.sphdist(NP.empty(skymodel.catalog.shape[0]).fill(pointing_lon), NP.empty(skymodel.catalog.shape[0]).fill(pointing_lat), skymodel.catalog.location[:,0], skymodel.catalog.location[:,1])

        if len(d12) != 0:
            pb = NP.empty((len(d12), len(self.channels)))
            fluxes = NP.empty((len(d12), len(self.channels)))

            coords_str = self.skycoords
            if self.skycoords == "radec":
                coords_str = "altaz"
                source_positions = GEOM.hadec2altaz(
                    NP.hstack(
                        (
                            NP.asarray(lst - skymodel.catalog.location[m2, 0]).reshape(-1, 1),
                            skymodel.catalog.location[m2, 1].reshape(-1, 1),
                        )
                    ),
                    self.latitude,
                    "degrees",
                )

            for i in xrange(len(self.channels)):
                # pb[:,i] = PB.primary_beam_generator(d12, self.channels[i]/1.0e9, 'degrees', self.telescope)
                pb[:, i] = PB.primary_beam_generator(
                    source_positions, self.channels[i] / 1.0e9, "altaz", self.telescope
                )
                fluxes[:, i] = (
                    skymodel.catalog.flux_density[m2]
                    * (self.channels[i] / skymodel.catalog.frequency) ** skymodel.catalog.spectral_index[m2]
                )

            geometric_delays = DLY.geometric_delay(
                baseline_in_local_frame,
                source_positions,
                altaz=(coords_str == "altaz"),
                hadec=(coords_str == "hadec"),
                latitude=self.latitude,
            )
            self.geometric_delays = self.geometric_delays + [geometric_delays.reshape(len(source_positions))]

            phase_matrix = 2.0 * NP.pi * NP.repeat(
                geometric_delays.reshape(-1, 1), len(self.channels), axis=1
            ) * NP.repeat(self.channels.reshape(1, -1), len(d12), axis=0) - NP.repeat(pointing_phase, len(d12), axis=0)

            skyvis = NP.sum(
                pb
                * fluxes
                * NP.repeat(NP.asarray(bandpass).reshape(1, -1), len(d12), axis=0)
                * NP.exp(-1j * phase_matrix),
                axis=0,
            )
            if fov_radius is not None:
                self.obs_catalog_indices = self.obs_catalog_indices + [m2]
                # self.obs_catalog = self.obs_catalog + [skymodel.catalog.subset(m2)]
        else:
            print "No sources found in the catalog within matching radius. Simply populating the observed visibilities with noise."
            skyvis = NP.zeros((1, len(self.channels)))

        if self.timestamp == []:
            self.skyvis_freq = skyvis.reshape(1, -1)
            self.vis_noise_freq = self.vis_rms_freq[-1] * (
                NP.random.randn(len(self.channels)).reshape(1, -1)
                + 1j * NP.random.randn(len(self.channels)).reshape(1, -1)
            )
            self.vis_freq = self.skyvis_freq + self.vis_noise_freq
        else:
            self.skyvis_freq = NP.vstack((self.skyvis_freq, skyvis.reshape(1, -1)))
            self.vis_noise_freq = NP.vstack(
                (
                    self.vis_noise_freq,
                    self.vis_rms_freq[-1]
                    * (
                        NP.random.randn(len(self.channels)).reshape(1, -1)
                        + 1j * NP.random.randn(len(self.channels)).reshape(1, -1)
                    ),
                )
            )
            self.vis_freq = NP.vstack(
                (self.vis_freq, (self.skyvis_freq[-1, :] + self.vis_noise_freq[-1, :]).reshape(1, -1))
            )

        self.timestamp = self.timestamp + [timestamp]
bl_lengths = NP.sqrt(NP.sum(bl**2,axis=1))

decl = -27.0 # in degrees
lat = -27.0 # in degrees
ha_range = 4.0 # in hrs
ha_step = 1.0/60. # in hrs
ha = 15.0*NP.arange(-ha_range/2, ha_range/2, ha_step)
ha = NP.asarray(ha).reshape(-1,1)
dec = decl + NP.zeros((len(ha),1))
dec.reshape(-1,1)

hadec = NP.hstack((ha, dec))

altaz = GEOM.hadec2altaz(hadec, lat, units='degrees')

dircos = GEOM.altaz2dircos(altaz, units='degrees')

dm = DLY.delay_envelope(bl, dircos, units='mks')

min_delays = dm[:,:,1]-dm[:,:,0]
max_delays = NP.sum(dm,axis=2)

fig = PLT.figure(figsize=(14,8))
ax = PLT.axes([0.12,0.1,0.8,0.8])
PLT.xlim(NP.min(bl_lengths)-1.0,NP.max(bl_lengths)+100.0)
PLT.ylim(1e9*(NP.min(min_delays)-1.e-8),1e9*(NP.max(max_delays)+1.e-8))
PLT.xlabel('Baseline Length [m]', fontsize=18, weight='semibold')
PLT.ylabel('Delay [ns]', fontsize=18, weight='semibold')
PLT.title('Delay Envelope', fontsize=18, weight='semibold')

hadec_text = ax.text(0.05, 0.9, '', transform=ax.transAxes, fontsize=18)
window = n_channels * DSP.windowing(n_channels, shape=bpass_shape, pad_width=n_pad, centering=True, area_normalize=True)
bandpass_shape = DSP.PFB_empirical(nchan, 32, 0.25, 0.25)
window = n_channels * DSP.windowing(n_channels, shape=bpass_shape, pad_width=n_pad, centering=True, area_normalize=True)
bpass = base_bpass * bandpass_shape
n_snaps = int(t_obs / t_snap)
lst = (lst_init + (t_snap / 3.6e3) * NP.arange(n_snaps)) * 15.0  # in degrees
if obs_mode == "track":
    pointings_radec = NP.repeat(NP.asarray(pointing_init).reshape(-1, 2), n_snaps, axis=0)
else:
    pointings_radec = NP.hstack(
        (NP.asarray(lst - pointing_init[0]).reshape(-1, 1), pointing_init[1] + NP.zeros(n_snaps).reshape(-1, 1))
    )

pointings_hadec = NP.hstack(((lst - pointings_radec[:, 0]).reshape(-1, 1), pointings_radec[:, 1].reshape(-1, 1)))
pointings_altaz = GEOM.hadec2altaz(pointings_hadec, latitude, units="degrees")
pointings_dircos = GEOM.altaz2dircos(pointings_altaz, units="degrees")

## Interferometer parameters

# baseline_orientation = NP.asarray([0.0, 90.0]) # in degrees from East towards North
# baseline_length = 1000.0 + NP.zeros(2) # in m
# baseline_vect = NP.zeros((2,3))
# baseline_vect[:,0] = baseline_length * NP.cos(NP.radians(baseline_orientation))
# baseline_vect[:,1] = baseline_length * NP.sin(NP.radians(baseline_orientation))

# baseline_orientation_str = '{0:.1f}'.format(baseline_orientation)
# baseline_length_str = '{0:.1f}'.format(baseline_length)

# ia = RI.InterferometerArray(labels, bl, chans, telescope=telescope, latitude=latitude, A_eff=A_eff, freq_scale='GHz')
# # ia = RI.InterferometerArray(['B1','B2'], baseline_vect, chans, telescope=telescope, latitude=latitude, A_eff=A_eff, freq_scale='GHz')
# # ints = []
            if not beam_switch:
                lst = 0.5*(lst_edges[1:]+lst_edges[:-1])
                t_snap = (lst_edges[1:]-lst_edges[:-1]) / 15.0 * 3.6e3
            else:
                lst = 0.5*(lst_edges_left + lst_edges_right)
                t_snap = (lst_edges_right - lst_edges_left) / 15.0 * 3.6e3
        else:
            t_snap = 112.0 + NP.zeros(n_snaps)   # in seconds (needs to be generalized)
            lst = lst_wrapped + 0.5 * t_snap/3.6e3 * 15.0

    # pointings_dircos_orig = GEOM.altaz2dircos(pointings_altaz_orig, units='degrees')
    # pointings_hadec_orig = GEOM.altaz2hadec(pointings_altaz_orig, latitude, units='degrees')
    # pointings_radec_orig = NP.hstack(((lst-pointings_hadec_orig[:,0]).reshape(-1,1), pointings_hadec_orig[:,1].reshape(-1,1)))
    # pointings_radec_orig[:,0] = pointings_radec_orig[:,0] % 360.0

    pointings_dircos = GEOM.altaz2dircos(pointings_altaz, units='degrees')
    pointings_hadec = GEOM.altaz2hadec(pointings_altaz, latitude, units='degrees')
    pointings_radec = NP.hstack(((lst-pointings_hadec[:,0]).reshape(-1,1), pointings_hadec[:,1].reshape(-1,1)))
    pointings_radec[:,0] = pointings_radec[:,0] % 360.0
    t_obs = NP.sum(t_snap)
elif pointing_info is not None:
    pointing_init = NP.asarray(pointing_info[1:])
    lst_init = pointing_info[0]
    pointing_file = None
    if t_snap is None:
        raise NameError('t_snap must be provided for an automated observing run')

    if (n_snaps is None) and (t_obs is None):
        raise NameError('n_snaps or t_obs must be provided for an automated observing run')
    elif (n_snaps is not None) and (t_obs is not None):
        raise ValueError('Only one of n_snaps or t_obs must be provided for an automated observing run')
def isotropic_radiators_array_field_pattern(nax1, nax2, sep1, sep2=None,
                                            skypos=None, wavelength=1.0,
                                            east2ax1=None, skycoords='altaz'):
    try:
        nax1, nax2, sep1
    except NameError:
        raise NameError('Number of radiators along axis 1 and 2 and their separation must be specified. Check inputs.')

    if skypos is None:
        raise NameError('skypos must be specified in Alt-Az or direction cosine units as a Numpy array. Check inputs.')

    if not isinstance(nax1, int):
        raise TypeError('nax1 must be a positive integer.')
    elif nax1 <= 0:
        raise ValueError('nax1 must be a positive integer.')

    if not isinstance(nax2, int):
        raise TypeError('nax2 must be a positive integer.')
    elif nax2 <= 0:
        raise ValueError('nax2 must be a positive integer.')

    if not isinstance(sep1, (int,float)):
        raise TypeError('sep1 must be a positive scalar.')
    elif sep1 <= 0:
        raise ValueError('sep1 must be a positive value.')

    if sep2 is None:
        sep2 = sep1

    if not isinstance(wavelength, (int,float)):
        raise TypeError('wavelength must be a positive scalar.')
    elif wavelength <= 0:
        raise ValueError('wavelength must be a positive value.')

    if not isinstance(east2ax1, (int,float)):
        raise TypeError('east2ax1 must be a scalar.')

    if not isinstance(skypos, NP.ndarray):
        raise TypeError('skypos must be a Numpy array.')
    
    if skycoords is not None:
        if (skycoords != 'altaz') and (skycoords != 'dircos'):
            raise ValueError('skycoords must be "altaz" or "dircos" or None (default).')
        elif skycoords == 'altaz':
            if skypos.ndim < 2:
                if len(skypos) == 2:
                    skypos = NP.asarray(skypos).reshape(1,2)
                else:
                    raise ValueError('skypos must be a Nx2 Numpy array.')
            elif skypos.ndim > 2:
                raise ValueError('skypos must be a Nx2 Numpy array.')
            else:
                if skypos.shape[1] != 2:
                    raise ValueError('skypos must be a Nx2 Numpy array.')
                elif NP.any(skypos[:,0] < 0.0) or NP.any(skypos[:,0] > 90.0):
                    raise ValueError('Altitudes in skypos have to be positive and <= 90 degrees')
        else:
            if skypos.ndim < 2:
                if (len(skypos) == 2) or (len(skypos) == 3):
                    skypos = NP.asarray(skypos).reshape(1,-1)
                else:
                    raise ValueError('skypos must be a Nx2 Nx3 Numpy array.')
            elif skypos.ndim > 2:
                raise ValueError('skypos must be a Nx2 or Nx3 Numpy array.')
            else:
                if (skypos.shape[1] < 2) or (skypos.shape[1] > 3):
                    raise ValueError('skypos must be a Nx2 or Nx3 Numpy array.')
                else:
                    if NP.any(NP.abs(skypos[:,0]) > 1.0) or NP.any(NP.abs(skypos[:,1]) > 1.0):
                        raise ValueError('skypos in transverse direction cosine coordinates found to be exceeding unity.')
                    else:
                        if skypos.shape[1] == 3:
                            eps = 1.0e-10
                            if NP.any(NP.abs(NP.sqrt(NP.sum(skypos**2, axis=1)) - 1.0) > eps) or NP.any(skypos[:,2] < 0.0):
                                print 'Warning: skypos in direction cosine coordinates along line of sight found to be negative or some direction cosines are not unit vectors. Resetting to correct values.'
                                skypos[:,2] = 1.0 - NP.sqrt(NP.sum(skypos[:,:2]**2,axis=1))
                        else:
                            skypos = NP.hstack((skypos, 1.0 - NP.asarray(NP.sqrt(NP.sum(skypos[:,:2]**2,axis=1))).reshape(-1,1)))
    else:
        raise ValueError('skycoords has not been set.')

    skypos_dircos_relative = NP.empty((skypos.shape[0],3))
    if east2ax1 is not None:
        if not isinstance(east2ax1, (int, float)):
            raise TypeError('east2ax1 must be a scalar value.')
        else:
            if skycoords == 'altaz':
                skypos_dircos_relative = GEOM.altaz2dircos(NP.hstack((skypos[:,0].reshape(-1,1),NP.asarray(skypos[:,1]-east2ax1).reshape(-1,1))), 'degrees')
            else:
                angle = NP.radians(east2ax1)
                rotation_matrix = NP.asarray([[NP.cos(angle),  NP.sin(angle), 0.0],
                                              [-NP.sin(angle), NP.cos(angle), 0.0],
                                              [0.0,            0.0,           1.0]])
                skypos_dircos_relative = NP.dot(skypos, rotation_matrix.T)
    else:
        if skycoords == 'altaz':
            skypos_dircos_relative = GEOM.altaz2dircos(skypos, 'degrees')
        else:
            skypos_dircos_relative = skypos

    phi = 2 * NP.pi * sep1 * skypos_dircos_relative[:,0] / wavelength 
    psi = 2 * NP.pi * sep2 * skypos_dircos_relative[:,1] / wavelength 

    pb = NP.sin(0.5*nax1*phi)/NP.sin(0.5*phi) * NP.sin(0.5*nax2*psi)/NP.sin(0.5*psi) / (nax1*nax2)
    return pb
def dipole_field_pattern(length, skypos, dipole_coords=None, dipole_orientation=None,
                         skycoords=None, wavelength=1.0, ground_plane=None,
                         angle_units=None, half_wave_dipole_approx=True):

    try:
        length, skypos
    except NameError:
        raise NameError('Dipole length and sky positions must be specified. Check inputs.')

    if not isinstance(length, (int,float)):
        raise TypeError('Dipole length should be a scalar.')

    if length <= 0.0:
        raise ValueError('Dipole length should be positive.')

    if not isinstance(wavelength, (int,float)):
        raise TypeError('Wavelength should be a scalar.')
 
    if wavelength <= 0.0:
        raise ValueError('Wavelength should be positive.')

    if ground_plane is not None:
        if not isinstance(ground_plane, (int,float)):
            raise TypeError('Height above ground plane should be a scalar.')

        if ground_plane <= 0.0:
            raise ValueError('Height above ground plane should be positive.')


    if dipole_coords is not None:
        if not isinstance(dipole_coords, str):
            raise TypeError('dipole_coords must be a string. Allowed values are "altaz" and "dircos"')
        elif (dipole_coords != 'altaz') and (dipole_coords != 'dircos'):
            raise ValueError('dipole_coords must be "altaz" or "dircos".')

    if skycoords is not None:
        if not isinstance(skycoords, str):
            raise TypeError('skycoords must be a string. Allowed values are "altaz" and "dircos"')
        elif (skycoords != 'altaz') and (skycoords != 'dircos'):
            raise ValueError('skycoords must be "altaz" or "dircos".')

    if (dipole_coords is None) and (skycoords is None):
        raise ValueError('At least one of dipole_coords and skycoords must be specified. Allowed values are "altaz" and "dircos"')
    elif (dipole_coords is not None) and (skycoords is None):
        skycoords = dipole_coords
    elif (dipole_coords is None) and (skycoords is not None):
        dipole_coords = skycoords    
    
    if (skycoords == 'altaz') or (dipole_coords == 'altaz'):
        if angle_units is None:
            angle_units = 'degrees'
        elif not isinstance(angle_units, str):
            raise TypeError('angle_units must be a string. Allowed values are "degrees" and "radians".')
        elif (angle_units != 'degrees') and (angle_units != 'radians'):
            raise ValueError('angle_units must be "degrees" or "radians".')

    if skycoords == 'altaz':
        skypos = NP.asarray(skypos)
        if angle_units == 'radians':
            skypos = NP.degrees(skypos)

        if skypos.ndim < 2:
            if len(skypos) == 2:
                skypos = NP.asarray(skypos).reshape(1,2)
            else:
                raise ValueError('skypos must be a Nx2 Numpy array.')
        elif skypos.ndim > 2:
            raise ValueError('skypos must be a Nx2 Numpy array.')
        else:
            if skypos.shape[1] != 2:
                raise ValueError('skypos must be a Nx2 Numpy array.')
            elif NP.any(skypos[:,0] < 0.0) or NP.any(skypos[:,0] > 90.0):
                raise ValueError('Altitudes in skypos have to be positive and <= 90 degrees')
        
        skypos_dircos = GEOM.altaz2dircos(skypos, 'degrees')
    else:
        if skypos.ndim < 2:
            if (len(skypos) == 2) or (len(skypos) == 3):
                skypos = NP.asarray(skypos).reshape(1,-1)
            else:
                raise ValueError('skypos must be a Nx2 Nx3 Numpy array.')
        elif skypos.ndim > 2:
            raise ValueError('skypos must be a Nx2 or Nx3 Numpy array.')
        else:
            if (skypos.shape[1] < 2) or (skypos.shape[1] > 3):
                raise ValueError('skypos must be a Nx2 or Nx3 Numpy array.')
            else:
                if NP.any(NP.abs(skypos[:,0]) > 1.0) or NP.any(NP.abs(skypos[:,1]) > 1.0):
                    raise ValueError('skypos in transverse direction cosine coordinates found to be exceeding unity.')
                else:
                    if skypos.shape[1] == 3:
                        eps = 1.0e-10
                        if NP.any(NP.abs(NP.sqrt(NP.sum(skypos**2, axis=1)) - 1.0) > eps) or NP.any(skypos[:,2] < 0.0):
                            print 'Warning: skypos in direction cosine coordinates along line of sight found to be negative or some direction cosines are not unit vectors. Resetting to correct values.'
                            skypos[:,2] = 1.0 - NP.sqrt(NP.sum(skypos[:,:2]**2,axis=1))
                    else:
                        skypos = NP.hstack((skypos, 1.0 - NP.asarray(NP.sqrt(NP.sum(skypos[:,:2]**2,axis=1))).reshape(-1,1)))
                        
        skypos_dircos = skypos

    if dipole_coords == 'altaz':
        dipole_orientation = NP.asarray(dipole_orientation)
        if angle_units == 'radians':
            dipole_orientation = NP.degrees(dipole_orientation)

        if dipole_orientation.ndim < 2:
            if len(dipole_orientation) == 2:
                dipole_orientation = NP.asarray(dipole_orientation).reshape(1,2)
            else:
                raise ValueError('dipole_orientation must be a Nx2 Numpy array.')
        elif dipole_orientation.ndim > 2:
            raise ValueError('dipole_orientation must be a Nx2 Numpy array.')
        else:
            if dipole_orientation.shape[1] != 2:
                raise ValueError('dipole_orientation must be a Nx2 Numpy array.')
            elif NP.any(dipole_orientation[:,0] < 0.0) or NP.any(dipole_orientation[:,0] > 90.0):
                raise ValueError('Altitudes in dipole_orientation have to be positive and <= 90 degrees')
        
        dipole_orientation_dircos = GEOM.altaz2dircos(dipole_orientation, 'degrees')
    else:
        if dipole_orientation.ndim < 2:
            if (len(dipole_orientation) == 2) or (len(dipole_orientation) == 3):
                dipole_orientation = NP.asarray(dipole_orientation).reshape(1,-1)
            else:
                raise ValueError('dipole_orientation must be a Nx2 Nx3 Numpy array.')
        elif dipole_orientation.ndim > 2:
            raise ValueError('dipole_orientation must be a Nx2 or Nx3 Numpy array.')
        else:
            if (dipole_orientation.shape[1] < 2) or (dipole_orientation.shape[1] > 3):
                raise ValueError('dipole_orientation must be a Nx2 or Nx3 Numpy array.')
            else:
                if NP.any(NP.abs(dipole_orientation[:,0]) > 1.0) or NP.any(NP.abs(dipole_orientation[:,1]) > 1.0):
                    raise ValueError('dipole_orientation in transverse direction cosine coordinates found to be exceeding unity.')
                else:
                    if dipole_orientation.shape[1] == 3:
                        eps = 1.0e-10
                        if NP.any(NP.abs(NP.sqrt(NP.sum(dipole_orientation**2, axis=1)) - 1.0) > eps) or NP.any(dipole_orientation[:,2] < 0.0):
                            print 'Warning: dipole_orientation in direction cosine coordinates along line of sight found to be negative or some direction cosines are not unit vectors. Resetting to correct values.'
                            dipole_orientation[:,2] = 1.0 - NP.sqrt(NP.sum(dipole_orientation[:,:2]**2,axis=1))
                    else:
                        dipole_orientation = NP.hstack((dipole_orientation, 1.0 - NP.asarray(NP.sqrt(NP.sum(dipole_orientation[:,:2]**2,axis=1))).reshape(-1,1)))
            
        dipole_orientation_dircos = dipole_orientation

    # PDB.set_trace()

    k = 2 * NP.pi / wavelength
    h = 0.5 * length
    
    dot_product = NP.dot(dipole_orientation_dircos, skypos_dircos.T)
    angles = NP.arccos(dot_product)

    eps = 1.e-10
    zero_angles_ind = NP.abs(NP.abs(dot_product) - 1.0) < eps
    reasonable_angles_ind = NP.abs(NP.abs(dot_product) - 1.0) > eps

    # field_pattern = NP.empty_like(angles)

    max_pattern = 1.0 # Normalization factor
    if half_wave_dipole_approx:
        field_pattern = NP.cos(0.5 * NP.pi * NP.cos(angles)) / NP.sin(angles)
    else:
        max_pattern = 1.0 - NP.cos(k * h) # Maximum occurs at angles = NP.pi / 2
        field_pattern = (NP.cos(k * h * NP.cos(angles)) - NP.cos(k * h)) / NP.sin(angles)

    field_pattern[zero_angles_ind] = k * h * NP.tan(0.5 * angles[zero_angles_ind]) * NP.sin(0.5 * k * h * (1.0 + NP.cos(angles[zero_angles_ind])))
    # field_pattern[zero_angles_ind] = 0.0

    if ground_plane is not None: # Ground plane formulas to be verified. Use with caution
        skypos_altaz = GEOM.dircos2altaz(skypos_dircos, 'radians')
        ground_pattern = 2 * NP.cos(k * ground_plane * NP.sin(skypos_altaz[:,0]))
    else:
        ground_pattern = 1.0

    return (field_pattern / max_pattern) * ground_pattern
bl_lengths = NP.sqrt(NP.sum(bl ** 2, axis=1))

decl = -27.0  # in degrees
lat = -27.0  # in degrees
ha_range = 4.0  # in hrs
ha_step = 1.0 / 60.0  # in hrs
ha = 15.0 * NP.arange(-ha_range / 2, ha_range / 2, ha_step)
ha = NP.asarray(ha).reshape(-1, 1)
dec = decl + NP.zeros((len(ha), 1))
dec.reshape(-1, 1)

hadec = NP.hstack((ha, dec))

altaz = GEOM.hadec2altaz(hadec, lat, units="degrees")

dircos = GEOM.altaz2dircos(altaz, units="degrees")

dm = DLY.delay_envelope(bl, dircos, units="mks")

min_delays = dm[:, :, 1] - dm[:, :, 0]
max_delays = NP.sum(dm, axis=2)

fig = PLT.figure(figsize=(14, 8))
ax = PLT.axes([0.12, 0.1, 0.8, 0.8])
PLT.xlim(NP.min(bl_lengths) - 1.0, NP.max(bl_lengths) + 100.0)
PLT.ylim(1e9 * (NP.min(min_delays) - 1.0e-8), 1e9 * (NP.max(max_delays) + 1.0e-8))
PLT.xlabel("Baseline Length [m]", fontsize=18, weight="semibold")
PLT.ylabel("Delay [ns]", fontsize=18, weight="semibold")
PLT.title("Delay Envelope", fontsize=18, weight="semibold")

hadec_text = ax.text(0.05, 0.9, "", transform=ax.transAxes, fontsize=18)