Exemplo n.º 1
0
    def get_mwabeam(self, delays, frequency=150e6):
        """
        beam=MWA_GW.get_mwa_gwmap(delays, frequency=150e6)
        """

        # first go from altitude to zenith angle
        theta_horz = np.pi / 2 - self.AltAz_down.alt.radian
        phi_horz = self.AltAz_down.az.radian

        beamX, beamY = primary_beam.MWA_Tile_analytic(theta_horz,
                                                      phi_horz,
                                                      freq=frequency,
                                                      delays=delays,
                                                      zenithnorm=True,
                                                      power=True)
        return ((beamX + beamY) * 0.5)
Exemplo n.º 2
0
def plot_beam_pattern(obsid, obsfreq, obstime, ra, dec, cutoff=0.1):

    # extra imports from MWA_Tools to access database and beam models
    from mwa_pb import primary_beam as pb

    _az = np.linspace(0, 360, 3600)
    _za = np.linspace(0, 90, 900)
    az, za = np.meshgrid(_az, _za)

    ## TARGET TRACKING ##
    times = Time(obstime, format='gps')
    target = SkyCoord(ra, dec, unit=(u.hourangle, u.deg))
    location = EarthLocation(lat=-26.7033 * u.deg,
                             lon=116.671 * u.deg,
                             height=377.827 * u.m)

    altaz = target.transform_to(AltAz(obstime=times, location=location))
    targetAZ = altaz.az.deg
    targetZA = 90 - altaz.alt.deg
    colours = cm.viridis(np.linspace(1, 0, len(targetAZ)))

    ## MWA BEAM CALCULATIONS ##
    delays = get_common_obs_metadata(obsid)[
        4]  # x-pol and y-pol delays for obsid
    _, ptAZ, ptZA = mwa_alt_az_za(obsid)
    #ptAZ, _, ptZA = dbq.get_beam_pointing(obsid) # Az and ZA in degrees for obsid

    logger.info("obs pointing: ({0}, {1})".format(ptAZ, ptZA))

    xp, yp = pb.MWA_Tile_analytic(np.radians(za),
                                  np.radians(az),
                                  obsfreq * 1e6,
                                  delays=delays,
                                  zenithnorm=True)  #interp=True)
    pattern = np.sqrt((xp + yp) / 2.0).real  # sum to get "total intensity"
    logger.debug("Pattern: {0}".format(pattern))
    pmax = pattern.max()
    hpp = 0.5 * pmax  # half-power point
    logger.info("tile pattern maximum: {0:.3f}".format(pmax))
    logger.info("tile pattern half-max: {0:.3f}".format(hpp))
    pattern[np.where(pattern < cutoff)] = 0  # ignore everything below cutoff

    # figure out the fwhm
    fwhm_idx = np.where((pattern > 0.498 * pmax) & (pattern < 0.502 * pmax))
    fwhm_az_idx = fwhm_idx[1]
    fwhm_za_idx = fwhm_idx[0]

    # collapse beam pattern along axes
    pattern_ZAcol = pattern.mean(axis=0)
    pattern_AZcol = pattern.mean(axis=1)

    # figure out beam pattern value at target tracking points
    track_lines = []
    logger.info("beam power at target track points:")
    for ta, tz in zip(targetAZ, targetZA):
        xp, yp = pb.MWA_Tile_analytic(np.radians([[tz]]),
                                      np.radians([[ta]]),
                                      obsfreq * 1e6,
                                      delays=delays,
                                      zenithnorm=True)  #interp=False
        bp = (xp + yp) / 2
        track_lines.append(bp[0])
        logger.info("({0:.2f},{1:.2f}) = {2:.3f}".format(ta, tz, bp[0][0]))

    ## PLOTTING ##
    fig = plt.figure(figsize=(10, 8))
    gs = gridspec.GridSpec(4, 4)
    axP = plt.subplot(gs[1:, 0:3])
    axAZ = plt.subplot(gs[0, :3])
    axZA = plt.subplot(gs[1:, 3])
    axtxt = plt.subplot(gs[0, 3])

    # info text in right-hand corner axis
    axtxt.axis('off')
    infostr = """Obs ID: {0}
Frequency: {1:.2f}MHz
Beam Pmax: {2:.3f}
Beam half-Pmax: {3:.3f}
""".format(obsid, obsfreq, pmax, hpp)
    axtxt.text(0.01, 0.5, infostr, verticalalignment='center')

    logger.debug("az: {0}, za: {1}, pattern: {2}, pmax: {3}".format(
        az, za, pattern, pmax))

    # plot the actual beam patter over sky
    p = axP.contourf(az,
                     za,
                     pattern,
                     100,
                     cmap=plt.get_cmap('gist_yarg'),
                     vmax=pmax)  # plot beam contours
    axP.scatter(_az[fwhm_az_idx], _za[fwhm_za_idx], marker='.', s=1,
                color='r')  # plot the fwhm border
    axP.plot(ptAZ, ptZA, marker="+", ms=8, ls="",
             color='C0')  # plot the tile beam pointing
    for ta, tz, c in zip(targetAZ, targetZA, colours):
        axP.plot(ta, tz, marker="x", ms=8, ls="",
                 color=c)  # plot the target track through the beam
    axP.set_xlim(0, 360)
    axP.set_ylim(0, 90)
    axP.set_xticks(np.arange(0, 361, 60))
    axP.set_xlabel("Azimuth (deg)")
    axP.set_ylabel("Zenith angle (deg)")

    # setup and configure colourbar
    cbar_ax = fig.add_axes([0.122, -0.01, 0.58, 0.03])
    cbar = plt.colorbar(p,
                        cax=cbar_ax,
                        orientation='horizontal',
                        label="Zenith normalised power")
    cbar.ax.plot(hpp / pmax, [0.5], 'r.')
    for l, c in zip(track_lines, colours):
        cbar.ax.plot([l / pmax, l / pmax], [0, 1], color=c, lw=1.5)

    # plot collapsed beam patterns:
    axAZ.plot(_az, pattern_ZAcol, color='k')  # collapsed along ZA
    for ta, c in zip(targetAZ, colours):  # draw tracking points
        axAZ.axvline(ta, color=c)
    axAZ.set_xlim(0, 360)
    axAZ.set_xticks(np.arange(0, 361, 60))
    axAZ.set_xticklabels([])
    axAZ.set_yscale('log')

    axZA.plot(pattern_AZcol, _za, color='k')  # collapsed along AZ
    for tz, c in zip(targetZA, colours):  # draw tracking points
        axZA.axhline(tz, color=c)
    axZA.set_ylim(0, 90)
    axZA.set_yticklabels([])
    axZA.set_xscale('log')

    plt.savefig("{0}_{1:.2f}MHz_flattile.png".format(obsid, obsfreq),
                bbox_inches='tight')
Exemplo n.º 3
0
def plot_beam(obs, fname, target, freq, time):

    logger.info("Getting observation metadata")
    metadata = get_obs_metadata(obs)

    logger.info("Loading data from file (this may take a while...)")
    theta, phi, beam = load_data(fname)

    logger.info("Re-normalising beam-pattern")
    az, za = np.meshgrid(np.radians(sorted(set(phi))),
                         np.radians(sorted(set(theta))))
    delays = [metadata['xdelays'], metadata['ydelays']]
    gx, gy = pb.MWA_Tile_analytic(za,
                                  az,
                                  freq=freq * 1e6,
                                  delays=delays,
                                  power=True,
                                  zenithnorm=True)
    tile_beam = (gx + gy) / 2.0

    # re-normalise tied-array beam to 1 at maximum and then apply tile beam
    # this acts to reduce the effects of simulation resolution (for plotting purposes)
    beam /= beam.max()
    beam *= np.ravel(tile_beam)

    # start setup for plotting
    lower_contour = 1e-2
    upper_contour = beam.max()

    fill_min = 7e-3
    fill_max = 0.95 * beam.max()
    beam[beam <= fill_min] = 0
    beam[beam >= fill_max] = fill_max

    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, polar=True, aspect='equal')

    # plot the beam pattern
    logger.info("Plotting beam pattern (this may take a while, too...)")
    cf_levels = np.linspace(lower_contour, upper_contour, num=20)
    #cf_levels_log = np.logspace(np.log10(lower_contour), np.log10(upper_contour), num=20)
    logger.info("     contour levels: max,min = {0}, {1}".format(
        cf_levels.max(), cf_levels.min()))
    cf_cmap = plt.get_cmap('gray_r')
    logger.info("     beam levels: max,min = {0},{1}".format(
        beam.max(), beam.min()))

    #sys.exit()
    logger.info("     plotting tied-array beam pattern contours")
    #cf_norm = LogNorm(vmin=cf_levels.min(), vmax=beam.max())
    #cf = ax.tricontourf(np.radians(phi), np.radians(theta), beam, cmap=cf_cmap, norm=cf_norm, levels=cf_levels)
    cf = ax.tricontourf(np.radians(phi),
                        np.radians(theta),
                        beam,
                        cmap=cf_cmap,
                        levels=cf_levels)
    cf.cmap.set_over('k')
    cf.cmap.set_under('white')

    # color bar setup
    logger.info("     assigning colorbar")
    cbar_levels = np.linspace(fill_min, fill_max, num=6)
    #cbar_levels_log = np.logspace(np.log10(fill_min), np.log10(fill_max), num=6)
    cbar = plt.colorbar(cf, shrink=0.9, pad=0.08)
    cbar.set_label(label="zenith normalised power", size=20, labelpad=20)
    cbar.set_ticks(cbar_levels)
    cbar.set_ticklabels([r"${0:.3f}$".format(x) for x in cbar_levels])
    cbar.ax.tick_params(labelsize=18)

    # plot the pointing centre
    logger.info("     plotting observation pointing centre")
    ax.plot(np.radians(metadata["az"]),
            np.radians(metadata["za"]),
            ls="",
            marker="+",
            ms=8,
            color='cyan',
            zorder=1002,
            label="pointing centre")

    # plot the target position on sky
    if target is not None:
        logger.info("     plotting target position")
        target_az = target.altaz.az.rad
        target_za = np.pi / 2 - target.altaz.alt.rad

        # get beam power for target
        bpt_x, bpt_y = pb.MWA_Tile_analytic(target_za,
                                            target_az,
                                            freq=freq * 1e6,
                                            delays=delays,
                                            power=True,
                                            zenithnorm=True)
        bpt = (bpt_x + bpt_y) / 2.0
        lognormbpt = log_normalise(bpt, cf_levels.min(), beam.max())
        logger.info("Beam power @ source: {0}".format(bpt))
        logger.info("   log-normalised: {0}".format(lognormbpt))

        # plot the target position on sky
        ax.plot(target_az,
                target_za,
                ls="",
                marker="o",
                color='C3',
                zorder=1002,
                label="   target:{} ".format(bpt))

        # plot the target on the color bar
        cbar.ax.plot(0.5, lognormbpt, color='C3', marker="o")

    # draw grid
    ax.grid(color='k', ls="--", lw=0.5)

    # azimuth labels
    ax.set_xticks(np.radians([0, 45, 90, 135, 180, 225, 270, 315]))
    ax.set_xticklabels([
        r"${0:d}^\circ$".format(int(np.ceil(x)))
        for x in np.degrees(ax.get_xticks())
    ],
                       color='k')

    # zenith angle labels
    ax.set_rlabel_position(250)
    ax.set_ylim(0, np.pi / 2)
    ax.set_yticks(np.radians([20, 40, 60, 80]))
    ax.set_yticklabels([
        r"${0:d}^\circ$".format(int(np.ceil(x)))
        for x in np.degrees(ax.get_yticks())
    ],
                       color='k')

    # title
    ax.set_title(
        "MWA tied-array beam\naz = {0:.2f}, za = {1:.2f}, freq = {2:.2f}MHz\n{3}"
        .format(metadata["az"], metadata["za"], freq, time.iso))

    logger.info("Saving figure")
    #plt.savefig("{0}_{1:.2f}MHz_tabeam.eps".format(obs, freq), bbox_inches="tight", format="eps")
    plt.savefig("{0}_{1:.2f}MHz_tabeam.png".format(obs, freq),
                bbox_inches="tight")
Exemplo n.º 4
0
def get_beam_power_over_time(beam_meta_data,
                             names_ra_dec,
                             dt=296,
                             centeronly=True,
                             verbose=False,
                             option='analytic',
                             degrees=False,
                             start_time=0):
    """
    Calulates the power (gain at coordinate/gain at zenith) for each source over time.

    get_beam_power_over_time(beam_meta_data, names_ra_dec,
                             dt=296, centeronly=True, verbose=False,
                             option = 'analytic')
    Args:
        beam_meta_data: [obsid,ra, dec, time, delays,centrefreq, channels]
                        obsid metadata obtained from meta.get_common_obs_metadata
        names_ra_dec: and array in the format [[source_name, RAJ, DecJ]]
        dt: time step in seconds for power calculations (default 296)
        centeronly: only calculates for the centre frequency (default True)
        verbose: prints extra data to (default False)
        option: primary beam model [analytic, advanced, full_EE]
        start_time: the time in seconds from the begining of the observation to
                    start calculating at
    """
    obsid, _, _, time, delays, centrefreq, channels = beam_meta_data
    names_ra_dec = np.array(names_ra_dec)
    logger.info("Calculating beam power for OBS ID: {0}".format(obsid))

    starttimes = np.arange(start_time, time + start_time, dt)
    stoptimes = starttimes + dt
    stoptimes[stoptimes > time] = time
    Ntimes = len(starttimes)
    midtimes = float(obsid) + 0.5 * (starttimes + stoptimes)

    if not centeronly:
        PowersX = np.zeros((len(names_ra_dec), Ntimes, len(channels)))
        PowersY = np.zeros((len(names_ra_dec), Ntimes, len(channels)))
        # in Hz
        frequencies = np.array(channels) * 1.28e6
    else:
        PowersX = np.zeros((len(names_ra_dec), Ntimes, 1))
        PowersY = np.zeros((len(names_ra_dec), Ntimes, 1))
        if centrefreq > 1e6:
            logger.warning(
                "centrefreq is greater than 1e6, assuming input with units of Hz."
            )
            frequencies = np.array([centrefreq])
        else:
            frequencies = np.array([centrefreq]) * 1e6
    if degrees:
        RAs = np.array(names_ra_dec[:, 1], dtype=float)
        Decs = np.array(names_ra_dec[:, 2], dtype=float)
    else:
        RAs, Decs = sex2deg(names_ra_dec[:, 1], names_ra_dec[:, 2])

    if len(RAs) == 0:
        sys.stderr.write('Must supply >=1 source positions\n')
        return None
    if not len(RAs) == len(Decs):
        sys.stderr.write('Must supply equal numbers of RAs and Decs\n')
        return None
    if verbose is False:
        #Supress print statements of the primary beam model functions
        sys.stdout = open(os.devnull, 'w')
    for itime in range(Ntimes):
        # this differ's from the previous ephem_utils method by 0.1 degrees
        _, Azs, Zas = mwa_alt_az_za(midtimes[itime],
                                    ra=RAs,
                                    dec=Decs,
                                    degrees=True)
        # go from altitude to zenith angle
        theta = np.radians(Zas)
        phi = np.radians(Azs)
        for ifreq in range(len(frequencies)):
            #Decide on beam model
            if option == 'analytic':
                rX, rY = primary_beam.MWA_Tile_analytic(
                    theta,
                    phi,
                    freq=frequencies[ifreq],
                    delays=delays,
                    zenithnorm=True,
                    power=True)
            elif option == 'advanced':
                rX, rY = primary_beam.MWA_Tile_advanced(
                    theta,
                    phi,
                    freq=frequencies[ifreq],
                    delays=delays,
                    zenithnorm=True,
                    power=True)
            elif option == 'full_EE':
                rX, rY = primary_beam.MWA_Tile_full_EE(theta,
                                                       phi,
                                                       freq=frequencies[ifreq],
                                                       delays=delays,
                                                       zenithnorm=True,
                                                       power=True)
        PowersX[:, itime, ifreq] = rX
        PowersY[:, itime, ifreq] = rY
    if verbose is False:
        sys.stdout = sys.__stdout__
    Powers = 0.5 * (PowersX + PowersY)
    return Powers
Exemplo n.º 5
0
def calc_jones(az, za, target_freq_Hz=205e6):
    za_rad = za * math.pi / 180.0
    az_rad = az * math.pi / 180.0
    za_rad = np.array([[za_rad]])
    az_rad = np.array([[az_rad]])

    gridpoint = mwa_sweet_spots.find_closest_gridpoint(az, za)
    print("Az = %.2f [deg], za = %.2f [deg], gridpoint = %d" % (az, za, gridpoint[0]))

    delays = gridpoint[4]
    delays = np.vstack((delays, delays))
    print(delays)
    print("-----------")

    Jones_FullEE = primary_beam.MWA_Tile_full_EE(za_rad,
                                                 az_rad,
                                                 target_freq_Hz,
                                                 delays=delays,
                                                 zenithnorm=True,
                                                 jones=True, interp=True)
    # swap axis to have Jones martix in the 1st
    Jones_FullEE_swap = np.swapaxes(np.swapaxes(Jones_FullEE, 0, 2), 1, 3)

    # TEST if equivalent to :
    Jones_FullEE_2D = Jones_FullEE_swap[:, :, 0, 0]
    # Jones_FullEE_2D = np.array([ [Jones_FullEE_swap[0, 0][0][0], Jones_FullEE_swap[0, 1][0][0]] , [Jones_FullEE_swap[1, 0][0][0], Jones_FullEE_swap[1, 1][0][0]] ])
    print("Jones FullEE:")
    print("----------------------")
    print(Jones_FullEE)
    print("----------------------")
    print(Jones_FullEE_2D)
    print("----------------------")

    beams = {}
    beams['XX'], beams['YY'] = primary_beam.MWA_Tile_full_EE(za_rad,
                                                             az_rad,
                                                             target_freq_Hz,
                                                             delays=delays,
                                                             zenithnorm=True,
                                                             power=True)
    print("Beams power = %.4f / %.4f" % (beams['XX'], beams['YY']))

    # Average Embeded Element model:
    print("size(delays) = %d" % np.size(delays))
    Jones_AEE = primary_beam.MWA_Tile_advanced(za_rad, az_rad, target_freq_Hz, delays=delays, jones=True)
    # Jones_AEE = primary_beam.MWA_Tile_advanced(np.array([[0]]), np.array([[0]]), target_freq_Hz,delays=delays, zenithnorm=True, jones=True)
    Jones_AEE_swap = np.swapaxes(np.swapaxes(Jones_AEE, 0, 2), 1, 3)
    Jones_AEE_2D = Jones_AEE_swap[:, :, 0, 0]
    # Jones_AEE_2D = np.array([ [Jones_AEE_swap[0, 0][0][0], Jones_AEE_swap[0, 1][0][0]], [Jones_AEE_swap[1, 0][0][0], Jones_AEE_swap[1, 1][0][0]] ])
    print("----------------------")
    print("Jones AEE:")
    print("----------------------")
    print(Jones_AEE)
    print("----------------------")
    print(Jones_AEE_2D)
    print("----------------------")

    Jones_Anal = primary_beam.MWA_Tile_analytic(za_rad,
                                                az_rad,
                                                target_freq_Hz,
                                                delays=delays,
                                                zenithnorm=True,
                                                jones=True)
    Jones_Anal_swap = np.swapaxes(np.swapaxes(Jones_Anal, 0, 2), 1, 3)
    Jones_Anal_2D = Jones_Anal_swap[:, :, 0, 0]

    print("----------------------  COMPARISON OF JONES MATRICES FEE vs. AEE ----------------------")
    print("Jones FullEE:")
    print("----------------------")
    print(Jones_FullEE_2D)
    print("----------------------")
    print()
    print("Jones AEE:")
    print("----------------------")
    print(Jones_AEE_2D)
    print("----------------------")
    print()
    print("----------------------")
    print("Jones Analytic:")
    print("----------------------")
    print(Jones_Anal_2D)
    print("----------------------")

    return (Jones_FullEE_2D, Jones_AEE_2D, Jones_Anal_2D)
Exemplo n.º 6
0
def calc_ratio(dec, target_freq_Hz, gridpoint):
    za = dec + 26.7
    az = 0

    print("\n\n\n\n")
    print("################################# DEC = %.2f #################################" % dec)
    za_rad = za * math.pi / 180.0
    az_rad = az * math.pi / 180.0
    za_rad = np.array([[za_rad]])
    az_rad = np.array([[az_rad]])

    # non-realistic scenario - I am ~tracking the source with the closest sweetspot - whereas I should be staying at the same sweetspot
    #   gridpoint=find_closest_gridpoint(za)
    #
    print("dec=%.4f [deg] -> za=%.4f [deg] - %s" % (dec, za, gridpoint))
    delays = gridpoint[4]
    delays = np.vstack((delays, delays))
    print(delays)
    print("-----------")

    Jones_FullEE = primary_beam.MWA_Tile_full_EE(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True,
                                                 jones=True, interp=True)
    # swap axis to have Jones martix in the 1st
    Jones_FullEE_swap = np.swapaxes(np.swapaxes(Jones_FullEE, 0, 2), 1, 3)

    # TEST if equivalent to :
    Jones_FullEE_2D = Jones_FullEE_swap[:, :, 0, 0]
    # Jones_FullEE_2D = np.array([ [Jones_FullEE_swap[0, 0][0][0], Jones_FullEE_swap[0, 1][0][0]], [Jones_FullEE_swap[1, 0][0][0], Jones_FullEE_swap[1, 1][0][0]] ])
    print("Jones FullEE:")
    print("----------------------")
    print(Jones_FullEE)
    print("----------------------")
    print(Jones_FullEE_2D)
    print("----------------------")

    #  Average Embeded Element model:
    print("size(delays) = %d" % np.size(delays))
    Jones_AEE = primary_beam.MWA_Tile_advanced(za_rad, az_rad, target_freq_Hz, delays=delays, jones=True)
    # Jones_AEE = primary_beam.MWA_Tile_advanced(np.array([[0]]), np.array([[0]]), target_freq_Hz, delays=delays, zenithnorm=True, jones=True)
    Jones_AEE_swap = np.swapaxes(np.swapaxes(Jones_AEE, 0, 2), 1, 3)
    Jones_AEE_2D = Jones_AEE_swap[:, :, 0, 0]
    # Jones_AEE_2D = np.array([ [Jones_AEE_swap[0, 0][0][0], Jones_AEE_swap[0, 1][0][0]], [Jones_AEE_swap[1, 0][0][0], Jones_AEE_swap[1, 1][0][0]] ])
    print("----------------------")
    print("Jones AEE:")
    print("----------------------")
    print(Jones_AEE)
    print("----------------------")
    print(Jones_AEE_2D)
    print("----------------------")

    # Analytical Model:
    # beams = {}
    # beams['XX'], beams['YY'] = primary_beam.MWA_Tile_analytic(za_rad, az_rad, target_freq_Hz, delays=delays, zenithnorm=True, jones=True)
    Jones_Anal = primary_beam.MWA_Tile_analytic(za_rad,
                                                az_rad,
                                                target_freq_Hz,
                                                delays=delays,
                                                zenithnorm=True,
                                                jones=True)
    Jones_Anal_swap = np.swapaxes(np.swapaxes(Jones_Anal, 0, 2), 1, 3)
    Jones_Anal_2D = Jones_Anal_swap[:, :, 0, 0]
    # Jones_Anal_2D = np.array([ [Jones_Anal_swap[0, 0][0][0], Jones_Anal_swap[0, 1][0][0]] , [Jones_Anal_swap[1, 0][0][0],Jones_Anal_swap[1, 1][0][0]] ])
    print("----------------------")
    print("Jones Analytic:")
    print("----------------------")
    print(Jones_Anal)
    print("----------------------")
    print(Jones_Anal_2D)
    print("----------------------")
    print("TEST:")
    print("----------------------")
    print("%.8f    %.8f" % (Jones_Anal_2D[0, 0], Jones_Anal_2D[0, 1]))
    print("%.8f    %.8f" % (Jones_Anal_2D[1, 0], Jones_Anal_2D[1, 1]))
    print("----------------------")

    # Use Jones_FullEE_2D as REAL sky and then ...
    B_sky = np.array([[1, 0], [0, 1]])
    Jones_FullEE_2D_H = np.transpose(Jones_FullEE_2D.conj())
    B_app = np.dot(Jones_FullEE_2D, np.dot(B_sky, Jones_FullEE_2D_H))  # E x B x E^H

    # test the procedure itself:
    Jones_FullEE_2D_H_Inv = inv2x2(Jones_FullEE_2D_H)
    Jones_FullEE_2D_Inv = inv2x2(Jones_FullEE_2D)
    B_sky_cal = np.dot(Jones_FullEE_2D_Inv, np.dot(B_app, Jones_FullEE_2D_H_Inv))
    print(B_sky)
    print("Recovered using FullEE model:")
    print(B_sky_cal)

    # calibrate back using AEE model :
    #   Jones_AEE_2D=Jones_Anal_2D # overwrite Jones_AEE_2D with Analytic to use it for calibration
    Jones_AEE_2D_H = np.transpose(Jones_AEE_2D.conj())
    Jones_AEE_2D_H_Inv = inv2x2(Jones_AEE_2D_H)
    Jones_AEE_2D_Inv = inv2x2(Jones_AEE_2D)
    B_sky_cal = np.dot(Jones_AEE_2D_Inv, np.dot(B_app, Jones_AEE_2D_H_Inv))
    print("Recovered using AEE model:")
    print(B_sky_cal)
    # I_cal = B_sky_cal[0, 0] + B_sky_cal[1, 1]
    #   print "FINAL : %.8f ratio = %.8f / 2 = %.8f" % (dec,abs(I_cal),(abs(I_cal)/2.00))
    # ratio = abs(B_sky_cal[0][0] / B_sky_cal[1][1])
    # FINAL VALUES for the paper to compare with Figure 4 in GLEAM paper :
    ratio_ms = B_sky_cal[0][0] / B_sky_cal[1][1]
    gleam_ratio = ratio_ms
    gleam_XX = B_sky_cal[0][0]
    gleam_YY = B_sky_cal[1][1]
    # gleam_q_leakage = (B_sky_cal[0][0] - B_sky_cal[1][1]) / (B_sky_cal[0][0] + B_sky_cal[1][1])
    print("DEBUG (DEC = %.2f deg) : GLEAM-ratio = %.4f = (%s / %s)" % (dec, gleam_ratio, gleam_XX, gleam_YY))

    return (gleam_ratio.real, gleam_XX, gleam_YY)
Exemplo n.º 7
0
        logger.info("Correcting with AEE(%s) model at frequency %.2f Hz" %
                    (model, target_freq_Hz))
        # logging.getLogger("mwa_tile").setLevel(logging.DEBUG)
        Jones = primary_beam.MWA_Tile_advanced(za,
                                               az,
                                               target_freq_Hz,
                                               delays=delays,
                                               zenithnorm=options.zenithnorm,
                                               jones=True)
        if options.wsclean_image:
            sign_uv = -1
    elif model == 'analytic' or model == '2014':
        logger.info("Correcting with analytic model")
        Jones = primary_beam.MWA_Tile_analytic(za,
                                               az,
                                               target_freq_Hz,
                                               delays=delays,
                                               zenithnorm=options.zenithnorm,
                                               jones=True)
        if options.wsclean_image:
            sign_q = -1
    else:
        logger.error('Unknown model: %s' % model)
        sys.exit()

    # Swap axes so that Jones 2x2 matrix forms the first 2 dimensions
    Jones = np.swapaxes(np.swapaxes(Jones, 0, 2), 1, 3)
    Joneses[model] = Jones  # Keep for further analysis

    # visibilities corrected for Direction Independed Gain (DIG) - it is in former step in CASA:
    Vij_corrected = Vij  # Skip beam correction of cal solutions - this is done in CASA
def get_beam_power(obsid_data,
                   sources,
                   beam_model="analytic",
                   centeronly=True):

    obsid, _, _, duration, delays, centrefreq, channels = obsid_data

    # Figure out GPS times to evaluate the beam in order to map the drift scan
    midtimes = np.array([
        float(obsid) + 0.5 * duration
    ])  # although only includes one element, could in future be extended
    Ntimes = len(midtimes)

    # Make an EarthLocation for the MWA
    MWA_LAT = -26.7033  # degrees
    MWA_LON = 116.671  # degrees
    MWA_HEIGHT = 377.827  # metres
    mwa_location = EarthLocation(lat=MWA_LAT * u.deg,
                                 lon=MWA_LON * u.deg,
                                 height=MWA_HEIGHT * u.m)

    # Pre-allocate arrays for the beam patterns
    if not centeronly:
        PowersX = np.zeros((len(sources), Ntimes, len(channels)))
        PowersY = np.zeros((len(sources), Ntimes, len(channels)))
        frequencies = np.array(channels) * 1.28e6
    else:
        PowersX = np.zeros((len(sources), Ntimes, 1))
        PowersY = np.zeros((len(sources), Ntimes, 1))
        frequencies = [centrefreq]

    # Create arrays of the RAs and DECs to be sampled (already in degrees)
    #print "Constructing RA and DEC arrays for beam model..."
    logger.info("Constructing RA and DEC arrays for beam model...")
    RAs = np.array([x[0] for x in sources])
    Decs = np.array([x[1] for x in sources])
    if len(RAs) == 0:
        sys.stderr.write('Must supply >=1 source positions\n')
        return None
    if not len(RAs) == len(Decs):
        sys.stderr.write('Must supply equal numbers of RAs and Decs\n')
        return None

    # Turn RAs and DECs into SkyCoord objects that are to be fed to Alt/Az conversion
    coords = SkyCoord(RAs, Decs, unit=(u.deg, u.deg))

    # Make times to feed to Alt/Az conversion
    obstimes = Time(midtimes, format='gps', scale='utc')

    #print "Converting RA and DEC to Alt/Az and computing beam pattern..."
    logger.info(
        "Converting RA and DEC to Alt/Az and computing beam pattern...")
    for t, time in enumerate(obstimes):
        # Convert to Alt/Az given MWA position and observing time
        altaz = coords.transform_to(AltAz(obstime=time, location=mwa_location))

        # Change Altitude to Zenith Angle
        theta = np.pi / 2 - altaz.alt.rad
        phi = altaz.az.rad

        # Calculate beam pattern for each frequency, and store the results in a ndarray
        for f, freq in enumerate(frequencies):
            if beam_model == "analytic":
                rX, rY = primary_beam.MWA_Tile_analytic(theta,
                                                        phi,
                                                        freq=freq,
                                                        delays=delays,
                                                        zenithnorm=True,
                                                        power=True)
            elif beam_model == "FEE":
                rX, rY = primary_beam.MWA_Tile_full_EE(theta,
                                                       phi,
                                                       freq=freq,
                                                       delays=delays,
                                                       zenithnorm=True,
                                                       power=True)
            else:
                #print "Unrecognised beam model '{0}'. Defaulting to 'analytic'."
                logger.warning(
                    "Unrecognised beam model '{0}'. Defaulting to 'analytic'.")
                rX, rY = primary_beam.MWA_Tile_analytic(theta,
                                                        phi,
                                                        freq=freq,
                                                        delays=delays,
                                                        zenithnorm=True,
                                                        power=True)

            PowersX[:, t, f] = rX
            PowersY[:, t, f] = rY

    # Convert X and Y powers into total intensity
    Powers = 0.5 * (PowersX + PowersY)

    return Powers
Exemplo n.º 9
0
def createArrayFactor(za, az, pixel_area, data):
    """
    Primary function to calculate the array factor with the given information.

    Input:
      delays - the beamformer delays required for point tile beam (should be a set of 16 numbers)
      time -  the time at which to evaluate the target position (as we need Azimuth and Zenith angle, which are time dependent)
      obsfreq - the centre observing frequency for the observation
      eff - the array efficiency (frequency and pointing dependent, currently require engineers to calculate for us...)
      flagged_tiles - the flagged tiles from the calibration solution (in the RTS format)
      xpos - x position of the tiles
      ypos - y position of the tiles
      zpos - z position of the tiles
      theta_res - the zenith angle resolution in degrees
      phi_res - the azimuth resolution in degrees
      za_chunk - list of ZA to compute array factor for
      write -  whether to actually write a file to disk
      rank - mpi rank (thread index)
    Return:
      results -  a list of lists cotaining [ZA, Az, beam power], for all ZA and Az in the given band
    """
    obsid = data['obsid']
    ra = data['ra']
    dec = data['dec']
    times = data['time']
    delays = data['delays']
    xpos = data['x']
    ypos = data['y']
    zpos = data['z']
    cable_delays = data['cd']
    theta_res = data['tres']
    phi_res = data['pres']
    beam_model = data['beam']
    coplanar = data['coplanar']
    ord_version = data['ord_version']
    no_delays = data['no_delays']
    plot_jobid = data['plot_jobid']

    # work out core depent part of data to process
    nfreq = len(data['freqs'])
    ifreq = rank % nfreq
    ichunk = rank // nfreq
    obsfreq = data['freqs'][ifreq]

    logger.info( "rank {:3d} Calculating phase of each sky position for each tile".format(rank))
    # calculate the relevent wavenumber for (theta,phi)
    #logger.info( "rank {:3d} Calculating wavenumbers".format(rank))
    if ord_version:
        #gx, gy, gz = calc_geometric_delay_distance((np.pi / 2) - az, za)
        gx, gy, gz = calc_geometric_delay_distance(az, za)
        logger.debug("rank {:3d} Wavenumbers shapes: {} {} {}".format(rank, gx.shape, gy.shape, gz.shape))

        # phase of each sky position for each tile
        ph_tile = cal_phase_ord(xpos, ypos, zpos, cable_delays, gx, gy, gz, obsfreq,
                                coplanar=coplanar, no_delays=no_delays)
        gx = gy = gz = None # Dereference for garbage collection
    else:
        kx, ky, kz = calcWaveNumbers(obsfreq, (np.pi / 2) - az, za)
        #logger.debug("kx[0] {} ky[0] {} kz[0] {}".format(kx[0], ky[0], kz[0]))
        logger.debug("rank {:3d} Wavenumbers shapes: {} {} {}".format(rank, kx.shape, ky.shape, kz.shape))

        # phase of each sky position for each tile
        ph_tile = calcSkyPhase(xpos, ypos, zpos, kx, ky, kz, coplanar=coplanar)
        kx = ky = kz = None # Dereference for garbage collection


    requests.get('https://ws.mwatelescope.org/progress/update',
                 params={'jobid':plot_jobid,
                         'workid':rank,
                         'current':2,
                         'total':5,
                         'desc':'Tile beam'})

    # calculate the tile beam at the given Az,ZA pixel
    logger.info( "rank {:3d} Calculating tile beam".format(rank))
    if beam_model == 'hyperbeam':
        # This method is no longer needed as mwa_pb uses hyperbeam
        jones = beam.calc_jones_array(az, za, obsfreq, delays, [1.0] * 16, True)
        jones = jones.reshape(za.shape[0], 1, 2, 2)
        vis = pb.mwa_tile.makeUnpolInstrumentalResponse(jones, jones)
        jones = None # Dereference for garbage collection
        tile_xpol, tile_ypol = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real)
        vis = None # Dereference for garbage collection
    elif beam_model == 'analytic':
        tile_xpol, tile_ypol = pb.MWA_Tile_analytic(za, az,
                                                freq=obsfreq, delays=[delays, delays],
                                                zenithnorm=True,
                                                power=True)
    elif beam_model == 'advanced':
        tile_xpol, tile_ypol = pb.MWA_Tile_advanced(za, az,
                                                freq=obsfreq, delays=[delays, delays],
                                                zenithnorm=True,
                                                power=True)
    elif beam_model == 'full_EE':
        tile_xpol, tile_ypol = pb.MWA_Tile_full_EE(za, az,
                                                freq=obsfreq, delays=np.array([delays, delays]),
                                                zenithnorm=True,
                                                power=True,
                                                interp=False)
    #logger.info("rank {:3d} Combining tile pattern".format(rank))
    tile_pattern = np.divide(np.add(tile_xpol, tile_ypol), 2.0)
    tile_pattern = tile_pattern.flatten()
    logger.debug("max(tile_pattern) {}".format(max(tile_pattern)))


    omega_A_times = []
    sum_B_T_times = []
    sum_B_times   = []
    phased_array_pattern_times = []
    for ti, time in enumerate(times):
        requests.get('https://ws.mwatelescope.org/progress/update',
                    params={'jobid':plot_jobid,
                            'workid':rank,
                            'current':3+ti,
                            'total'  :3+len(times),
                            'desc':'{}/{} array factor'.format(1+ti, len(times))})
        # get the target azimuth and zenith angle in radians and degrees
        # these are defined in the normal sense: za = 90 - elevation, az = angle east of North (i.e. E=90)
        logger.info( "rank {:3d} Calculating phase of each sky position for the target at time {}".format(rank, time))
        srcAz, srcZA, _, _ = getTargetAZZA(ra, dec, time)# calculate the target (kx,ky,kz)

        if ord_version:
            #t_gx, t_gy, t_gz = calc_geometric_delay_distance((np.pi / 2) - srcAz, srcZA)
            t_gx, t_gy, t_gz = calc_geometric_delay_distance(srcAz, srcZA)

            # phase of each sky position for each tile
            ph_target = cal_phase_ord(xpos, ypos, zpos, cable_delays, t_gx, t_gy, t_gz, obsfreq,
                                      coplanar=coplanar, no_delays=no_delays)
        else:
            target_kx, target_ky, target_kz = calcWaveNumbers(obsfreq, (np.pi / 2) - srcAz, srcZA)

            # Get phase of target for each tile phase of each sky position for each tile
            ph_target = calcSkyPhase(xpos, ypos, zpos, target_kx, target_ky, target_kz)

        # determine the interference pattern seen for each tile
        logger.info( "rank {:3d} Calculating array_factor".format(rank))
        #logger.debug("rank {:3d} ti: {} ph_tile {}".format(rank, ti, ph_tile))
        #logger.debug("rank {:3d} ti: {} ph_target {}".format(rank, ti, ph_target))
        array_factor, array_factor_power = calcArrayFactor(ph_tile, ph_target)
        ph_target = None # Dereference for garbage collection

        #logger.debug("array_factor_power[0] {}".format(array_factor_power[0]))
        logger.debug("rank {:3d} array_factor[0] {}".format(rank, array_factor[0]))
        logger.debug("rank {:3d} array_factor shapes: {}".format(rank, array_factor.shape))
        logger.debug("rank {:3d} array factor maximum = {}".format(rank, np.amax(array_factor_power)))

        # calculate the phased array power pattern
        logger.info( "rank {:3d} Calculating phased array pattern".format(rank))
        logger.debug("rank {:3d} tile_pattern.shape {} array_factor_power.shape {}".format(rank, tile_pattern.shape, array_factor_power.shape))
        phased_array_pattern = np.multiply(tile_pattern, array_factor_power)
        #phased_array_pattern = tile_pattern[0][0] * np.abs(array_factor)**2 # indexing due to tile_pattern now being a 2-D array
        phased_array_pattern_times.append(phased_array_pattern)

        # add this contribution to the beam solid angle
        logger.info( "rank {:3d} Calculating omega_A".format(rank))
        #omega_A_array = np.sin(za) * array_factor_power * np.radians(theta_res) * np.radians(phi_res)
        #omega_A_array = np.multiply(np.multiply(np.multiply(np.sin(za), array_factor_power), np.radians(theta_res)), np.radians(phi_res))
        omega_A_array = np.multiply(array_factor_power, pixel_area)
        logger.debug("rank {:3d} omega_A_array shapes: {}".format(rank, omega_A_array.shape))
        omega_A = np.sum(omega_A_array)
        logger.debug("rank {:3d} freq {:.2f}MHz beam_area: {}".format(rank, obsfreq/1e6, omega_A))
        omega_A_times.append(omega_A)
        #logger.debug("omega_A[1] vals: za[1] {}, array_factor_power[1] {}, theta_res {}, phi_res {}".format(za[1], array_factor_power[1], theta_res, phi_res))
        #logger.debug("omega_A[1] {}".format(omega_A_array[1]))

        # Do a partial sum over the sky and frequency to be finished outside of the function
        sum_B_T, sum_B = partial_convolve_sky_map(az, za, pixel_area, phased_array_pattern, obsfreq, time)
        sum_B_T_times.append(sum_B_T)
        sum_B_times.append(sum_B)
    ph_tile = None # Dereference for garbage collection

    # Average over time
    #omega_A = np.average(omega_A_times)
    #sum_B_T = np.average(sum_B_T_times)
    #sum_B   = np.average(sum_B_times)
    #phased_array_pattern = np.average(phased_array_pattern_times, axis=0)
    logger.debug("rank {:3d} phased_array_pattern: {}".format(rank, phased_array_pattern))
    logger.debug("rank {:3d} omega_A_times: {}".format(rank, omega_A_times))
    logger.info("rank {:3d} done".format(rank))

    # return lists of results for each time step
    return np.array(omega_A_times), np.array(sum_B_T_times), np.array(sum_B_times), np.array(phased_array_pattern_times)
Exemplo n.º 10
0
    def get_mwapointing_grid(self,
                             frequency=150e6,
                             minprob=0.01,
                             minelevation=45,
                             returndelays=False,
                             returnpower=False):
        """

        :rtype: SkyCoord
        :param frequency: Frequency in Hz
        :param minprob: Minimum probability value
        :param minelevation: Minimum elevation angle, in degrees
        :param returndelays: If True, return delay values as well as MWA grid points
        :param returnpower: If True, return delay values AND primary beam power in that direction as well as MWA grid points
        :return: astropy.coordinates.SkyCoord object containing MWA coordinate grid
        """
        """
        RADec=MWA_GW.get_mwapointing_grid(frequency=150e6, minprob=0.01, minelevation=45
        returndelays=False, returnpower=False)
        if returndelays=True, returns:
        RADec,delays
        
        if returnpower=True, returns:
        RADec,delays,power
        """

        if (self.MWA_grid is None) or (self.MWA_grid.data is None):
            self.critical('Unable to find MWA grid points')
            if not (returndelays or returnpower):
                return None
            else:
                if returnpower:
                    return None, None, None
                else:
                    return None, None

        # has it been downsampled already
        gwmap = self.gwmap_down
        AltAz = self.AltAz_down

        gridRADec = self.MWA_grid.get_radec()

        self.debug('Computing pointing for %s' % (self.obstime))

        # figure out what fraction is above horizon
        pointingmap = (gwmap * (AltAz.alt > 0)).sum()
        if pointingmap < minprob:
            self.info(
                'Insufficient power (%.3f) above horizon (>%.3f required)\n' %
                (pointingmap, minprob))
            if not (returndelays or returnpower):
                return None
            else:
                if returnpower:
                    return None, None, None
                else:
                    return None, None

        # first go from altitude to zenith angle
        theta_horz = np.pi / 2 - AltAz.alt.radian
        phi_horz = AltAz.az.radian

        mapsum = np.zeros((len(self.MWA_grid.data)))
        start = timer()
        for igrid in range(len(self.MWA_grid.data)):
            beamX, beamY = primary_beam.MWA_Tile_analytic(
                theta_horz,
                phi_horz,
                freq=frequency,
                delays=self.MWA_grid.data[igrid]['delays'],
                zenithnorm=True,
                power=True)

            mapsum[igrid] = ((beamX + beamY) * 0.5 * gwmap).sum()
        # this is the best grid point
        # such that it is over our minimum elevation
        igrid = np.where(mapsum == mapsum[
            self.MWA_grid.data['elevation'] > minelevation].max())[0][0]
        end = timer()
        self.info("Best pointing found in %.1f s" % (end - start))
        self.info("Looped over %d grid points" % (len(self.MWA_grid.data)))

        if not (self.MWA_grid.data['elevation'][igrid] > minelevation):
            self.info('Elevation %.1f deg too low\n' %
                      self.MWA_grid.data['elevation'][igrid])
            # too close to horizon
            if not (returndelays or returnpower):
                return None
            else:
                if returnpower:
                    return None, None, None
                else:
                    return None, None

        msg = 'Best pointing at RA,Dec=%.1f,%.1f; Az,El=%.1f,%.1f: power=%.3f'
        self.info(msg %
                  (gridRADec[igrid].ra.value, gridRADec[igrid].dec.value,
                   self.MWA_grid.data['azimuth'][igrid],
                   self.MWA_grid.data['elevation'][igrid], mapsum[igrid]))

        if mapsum[igrid] < minprob:
            msg = 'Pointing at Az,El=%.1f,%.1f has power=%.3f < min power (%.3f)\n'
            self.info(msg % (self.MWA_grid.data['azimuth'][igrid],
                             self.MWA_grid.data['elevation'][igrid],
                             mapsum[igrid], minprob))

            if not (returndelays or returnpower):
                return None
            else:
                if returnpower:
                    return None, None, None
                else:
                    return None, None

        if not (returndelays or returnpower):
            return gridRADec[igrid]
        else:
            if not returnpower:
                return gridRADec[igrid], self.MWA_grid.data[igrid]['delays']
            else:
                return gridRADec[igrid], self.MWA_grid.data[igrid][
                    'delays'], mapsum[igrid]
Exemplo n.º 11
0
def createArrayFactor(targetAZ, targetZA, targetAZdeg, targetZAdeg,
                      obsid, delays, time, obsfreq, eff, flagged_tiles,
                      theta_res, phi_res,
                      coplanar, zenith, za_chunk, write):
    """
    Primary function to calculate the array factor with the given information.

    Input:
      targetAZ, targetZA, targetAZdeg, targetZAdeg: target sky position (Az, ZA) in radians and degrees
      obsid - the observation ID for which to create the phased array beam
      delays - the beamformer delays required for point tile beam (should be a set of 16 numbers)
      obstime -  the time at which to evaluate the target position (as we need Azimuth and Zenith angle, which are time dependent)
      obsfreq - the centre observing frequency for the observation
      eff - the array efficiency (frequency and pointing dependent, currently require engineers to calculate for us...)
      flagged_tiles - the flagged tiles from the calibration solution (in the RTS format)
      theta_res - the zenith angle resolution in degrees
      phi_res - the azimuth resolution in degrees
      coplanar - whether or not to assume that the z-components of antenna locations is 0m
      zenith - force the pointing to be zenith (i.e. ZA = 0deg, Az = 0deg)
      za_chunk - list of ZA to compute array factor for
      write -  whether to actually write a file to disk
    Return:
      results -  a list of lists cotaining [ZA, Az, beam power], for all ZA and Az in the given band
    """
    # convert frequency to wavelength
    obswl = obsfreq / c.value

    # calculate the target (kx,ky,kz)
    target_kx, target_ky, target_kz = calcWaveNumbers(obswl, (np.pi / 2) - targetAZ, targetZA)


    results = []

    # is this the last process?
    rank = size - 1

    # we will also calculate the beam area contribution of this part of the sky
    omega_A = 0
    array_factor_max = -1

    # TODO: this is the important bit, and I'm sure we are doing this the dumb, slow way...
    # for each ZA "pixel", 90deg inclusive
    for za in za_chunk:
        # for each Az "pixel", 360deg not included
        for az in genAZZA(0, 2 * np.pi, np.radians(phi_res)):

            # calculate the relevent wavenumber for (theta,phi)
            kx, ky, kz = calcWaveNumbers(obswl, (np.pi / 2) - az, za)
            array_factor = 0 + 0.j

            # determine the interference pattern seen for each tile
            for x, y, z in zip(xpos, ypos, zpos):
                ph = kx * x + ky * y + kz * z
                ph_target = target_kx * x + target_ky * y + target_kz * z
                array_factor += np.cos(ph - ph_target) + 1.j * np.sin(ph - ph_target)

            # normalise to unity at pointing position
            array_factor /= len(xpos)
            array_factor_power = np.abs(array_factor)**2

            # keep track of maximum value calculated
            if (array_factor_power > array_factor_max):
                array_factor_max = array_factor_power

            # calculate the tile beam at the given Az,ZA pixel
            #tile_xpol,tile_ypol = pb.MWA_Tile_analytic([[za]],[[az]],freq=obsfreq,delays=[delays,delays],power=True,zenithnorm=True,interp=False)
            tile_xpol, tile_ypol = pb.MWA_Tile_analytic(za, az, freq=obsfreq, delays=delays, power=True, zenithnorm=True)
            tile_pattern = (tile_xpol + tile_ypol) / 2.0

            # calculate the phased array power pattern
            phased_array_pattern = tile_pattern * array_factor_power
            #phased_array_pattern = tile_pattern[0][0] * np.abs(array_factor)**2 # indexing due to tile_pattern now being a 2-D array

            # append results to a reference list for later
            results.append([np.degrees(za), np.degrees(az), phased_array_pattern])

            # add this contribution to the beam solid angle
            omega_A += np.sin(za) * array_factor_power * np.radians(theta_res) * np.radians(phi_res)

    logger.info("worker {0}, array factor maximum = {1}".format(rank, array_factor_max))

    # write a file based on rank of the process being used
    if write:
        logger.info("writing file from worker {0}".format(rank))
        with open(oname.replace(".dat",".{0}.dat".format(rank)), 'w') as f:
            if rank == 0:
                # if master process, write the header information first and then the data
                f.write("##File Type: Far field\n##File Format: 3\n##Source: mwa_tiedarray\n##Date: {0}\n".format(time.iso))
                f.write("** File exported by FEKO kernel version 7.0.1-482\n\n")
                f.write("#Request Name: FarField\n#Frequency: {0}\n".format(obsfreq))
                f.write("#Coordinate System: Spherical\n#No. of Theta Samples: {0}\n#No. of Phi Samples: {1}\n".format(ntheta, nphi))
                f.write("#Result Type: Gain\n#No. of Header Lines: 1\n")
                f.write('#\t"Theta"\t"Phi"\t"Re(Etheta)"\t"Im(Etheta)"\t"Re(Ephi)"\t"Im(Ephi)"\t"Gain(Theta)"\t"Gain(Phi)"\t"Gain(Total)"\n')

            for res in results:
                # write each line of the data
                # we actually need to rotate out phi values by: phi = pi/2 - az because that's what FEKO expects.
                # values are calculated using that convetion, so we need to represent that here
                f.write("{0}\t{1}\t0\t0\t0\t0\t0\t0\t{2}\n".format(res[0], res[1], res[2]))

    else:
        logger.warn("worker {0} not writing".format(rank))

    return omega_A
def get_beam_power_over_time(names_ra_dec,
                             common_metadata=None,
                             dt=296,
                             centeronly=True,
                             verbose=False,
                             option='analytic',
                             degrees=False,
                             start_time=0):
    """Calculates the zenith normalised power for each source over time.

    Parameters
    ----------
    names_ra_dec : `list`
        An array in the format [[source_name, RAJ, DecJ]]
    common_metadata : `list`, optional
        The list of common metadata generated from :py:meth:`vcstools.metadb_utils.get_common_obs_metadata`
    dt : `int`, optional
        The time interval of how often powers are calculated. |br| Default: 296.
    centeronly : `boolean`, optional
        Only calculates for the centre frequency. |br| Default: `True`.
    verbose : `boolean`, optional
        If `True` will not supress the output from mwa_pb. |br| Default: `False`.
    option : `str`, optional
        The primary beam model to use out of [analytic, advanced, full_EE, hyperbeam]. |br| Default: analytic.
    degrees : `boolean`, optional
        If true assumes RAJ and DecJ are in degrees. |br| Default: `False`.
    start_time : `int`, optional
        The time in seconds from the begining of the observation to start calculating at. |br| Default: 0.

    Returns
    -------
    Powers : `numpy.array`, (len(names_ra_dec), ntimes, nfreqs)
        The zenith normalised power for each source over time.
    """
    if common_metadata is None:
        common_metadata = get_common_obs_metadata(obsid)
    obsid, _, _, time, delays, centrefreq, channels = common_metadata
    names_ra_dec = np.array(names_ra_dec)
    amps = [1.0] * 16
    logger.debug("Calculating beam power for OBS ID: {0}".format(obsid))

    if option == 'hyperbeam':
        if "mwa_hyperbeam" not in sys.modules:
            logger.error(
                "mwa_hyperbeam not installed so can not use hyperbeam to create a beam model. Exiting"
            )
            sys.exit(1)
        beam = mwa_hyperbeam.FEEBeam(config.h5file)

    # Work out time steps to calculate over
    starttimes = np.arange(start_time, time + start_time, dt)
    stoptimes = starttimes + dt
    stoptimes[stoptimes > time] = time
    ntimes = len(starttimes)
    midtimes = float(obsid) + 0.5 * (starttimes + stoptimes)

    # Work out frequency steps
    if centeronly:
        if centrefreq > 1e6:
            logger.warning(
                "centrefreq is greater than 1e6, assuming input with units of Hz."
            )
            frequencies = np.array([centrefreq])
        else:
            frequencies = np.array([centrefreq]) * 1e6
        nfreqs = 1
    else:
        # in Hz
        frequencies = np.array(channels) * 1.28e6
        nfreqs = len(channels)

    # Set up np power array
    PowersX = np.zeros((len(names_ra_dec), ntimes, nfreqs))
    PowersY = np.zeros((len(names_ra_dec), ntimes, nfreqs))

    # Convert RA and Dec to desired units
    if degrees:
        RAs = np.array(names_ra_dec[:, 1], dtype=float)
        Decs = np.array(names_ra_dec[:, 2], dtype=float)
    else:
        RAs, Decs = sex2deg(names_ra_dec[:, 1], names_ra_dec[:, 2])
    # Then check if they're valid
    if len(RAs) == 0:
        sys.stderr.write('Must supply >=1 source positions\n')
        return None
    if not len(RAs) == len(Decs):
        sys.stderr.write('Must supply equal numbers of RAs and Decs\n')
        return None

    if verbose is False:
        #Supress print statements of the primary beam model functions
        sys.stdout = open(os.devnull, 'w')
    for itime in range(ntimes):
        # this differ's from the previous ephem_utils method by 0.1 degrees
        _, Azs, Zas = mwa_alt_az_za(midtimes[itime],
                                    ra=RAs,
                                    dec=Decs,
                                    degrees=True)
        # go from altitude to zenith angle
        theta = np.radians(Zas)
        phi = np.radians(Azs)
        for ifreq in range(nfreqs):
            #Decide on beam model
            if option == 'analytic':
                rX, rY = primary_beam.MWA_Tile_analytic(
                    theta,
                    phi,
                    freq=frequencies[ifreq],
                    delays=delays,
                    zenithnorm=True,
                    power=True)
            elif option == 'advanced':
                rX, rY = primary_beam.MWA_Tile_advanced(
                    theta,
                    phi,
                    freq=frequencies[ifreq],
                    delays=delays,
                    zenithnorm=True,
                    power=True)
            elif option == 'full_EE':
                rX, rY = primary_beam.MWA_Tile_full_EE(theta,
                                                       phi,
                                                       freq=frequencies[ifreq],
                                                       delays=delays,
                                                       zenithnorm=True,
                                                       power=True)
            elif option == 'hyperbeam':
                jones = beam.calc_jones_array(phi, theta,
                                              int(frequencies[ifreq]),
                                              delays[0], amps, True)
                jones = jones.reshape(1, len(phi), 2, 2)
                vis = primary_beam.mwa_tile.makeUnpolInstrumentalResponse(
                    jones, jones)
                rX, rY = (vis[:, :, 0, 0].real, vis[:, :, 1, 1].real)
        PowersX[:, itime, ifreq] = rX
        PowersY[:, itime, ifreq] = rY
    if verbose is False:
        sys.stdout = sys.__stdout__
    Powers = 0.5 * (PowersX + PowersY)
    return Powers
def plot_beam(obs, target, cal, freq):

    metadata = get_common_obs_metadata(obs)
    phi = np.linspace(0, 360, 3600)
    theta = np.linspace(0, 90, 900)

    # make coordinate grid
    az, za = np.meshgrid(np.radians(phi), np.radians(theta))

    # compute beam and plot
    delays = metadata[4]  #x and y delays
    logger.debug("delays: {0}".format(delays))
    logger.debug("freq*1e6: {0}".format(freq * 1e6))
    logger.debug("za: {0}".format(za))
    logger.debug("az: {0}".format(az))

    gx, gy = pb.MWA_Tile_analytic(za,
                                  az,
                                  freq=int(freq * 1e6),
                                  delays=delays,
                                  power=True,
                                  zenithnorm=True)
    beam = (gx + gy) / 2.0

    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, polar=True, aspect='auto')

    # filled contour setup
    lower_contour = 7e-3
    upper_contour = beam.max()
    fill_min = 1e-2  # 1% of zenith power
    fill_max = 0.95 * beam.max()  # 95% max beam power ( != zenith power)

    Z = np.copy(beam)
    Z[Z <= fill_min] = 0
    Z[Z >= fill_max] = fill_max

    cc_levels = np.logspace(np.log10(lower_contour),
                            np.log10(upper_contour),
                            num=20)
    cc_cmap = plt.get_cmap('gray_r')

    cc_norm = LogNorm(vmin=cc_levels.min(), vmax=beam.max())
    cc = ax.contourf(az,
                     za,
                     Z,
                     levels=cc_levels,
                     cmap=cc_cmap,
                     norm=cc_norm,
                     zorder=1000)
    cc.cmap.set_over('black')  # anything over max is black
    cc.cmap.set_under('white')  # anything under min is white

    # contour lines steup
    cs_levels = np.logspace(np.log10(fill_min), np.log10(fill_max), num=5)
    cs_cmap = plt.get_cmap('gist_heat')
    cs_norm = LogNorm(vmin=cs_levels.min(), vmax=cs_levels.max())
    cs = ax.contour(az,
                    za,
                    beam,
                    levels=cs_levels,
                    cmap=cs_cmap,
                    norm=cs_norm,
                    zorder=1001)

    # color bar setup
    cbarCC = plt.colorbar(cc, pad=0.08, shrink=0.9)
    cbarCC.set_label(label="zenith normalised power", size=20)
    cbarCC.set_ticks(cs_levels)
    cbarCC.set_ticklabels([r"${0:.2f}$".format(x) for x in cs_levels])
    cbarCC.ax.tick_params(labelsize=18)
    #add lines from the contours to the filled color map
    cbarCC.add_lines(cs)

    # plot the pointing centre of the tile beam
    _, pointing_AZ, pointing_ZA = mwa_alt_az_za(obs)
    ax.plot(np.radians(pointing_AZ),
            np.radians(pointing_ZA),
            ls="",
            marker="+",
            ms=8,
            color='C3',
            zorder=1002,
            label="pointing centre")

    if target is not None:
        # color map for tracking target positions
        target_colors = cm.viridis(np.linspace(0, 1, len(target.obstime.gps)))
        logger.info("obs time length: {0} seconds".format(
            len(target.obstime.gps)))
        for t, color in zip(target, target_colors):
            target_az = t.altaz.az.rad
            target_za = np.pi / 2 - t.altaz.alt.rad

            # get beam power for target
            bpt_x, bpt_y = pb.MWA_Tile_analytic(
                target_za,
                target_az,
                freq=freq * 1e6,
                delays=[metadata[4][0], metadata[4][1]],
                power=True,
                zenithnorm=True)
            bpt = (bpt_x + bpt_y) / 2.0
            lognormbpt = log_normalise(bpt, cc_levels.min(), beam.max())
            logger.debug("bpt, cc_levels, beam: {0}, {1}, {2}".format(
                bpt, cc_levels.min(), beam.max()))
            logger.info("Beam power @ source @ gps: {0}: {1}".format(
                t.obstime.gps, bpt))
            logger.info("log-normalised BP: {0}".format(lognormbpt))

            # plot the target position on sky
            ax.plot(target_az,
                    target_za,
                    ls="",
                    marker="o",
                    color=color,
                    zorder=1002,
                    label="target @ {0} ({1})".format(t.obstime.gps, bpt))

            # plot the target on the color bar
            cbarCC.ax.plot(0.5, lognormbpt, color=color, marker="o")

    if cal is not None:
        # color map for tracking calibrator position
        calibrator_colors = cm.viridis(np.linspace(0, 1, len(cal.obstime.gps)))

        for c, color in zip(cal, calibrator_colors):
            cal_az = c.altaz.az.rad
            cal_za = np.pi / 2 - c.altaz.alt.rad

            # get the beam power for calibrator
            bpc_x, bpc_y = pb.MWA_Tile_analytic([[cal_za]], [[cal_az]],
                                                freq=freq * 1e6,
                                                delays=metadata[4],
                                                power=True,
                                                zenithnorm=True)
            bpc = (bpc_x + bpc_y) / 2.0
            lognormbpc = log_normalise(bpc, cc_levels.min(), beam.max())
            logger.info("Beam power @ calibrator @ gps:{0}: {1:.3f}".format(
                c.obstime.gps, bpc[0][0]))
            logger.info("log-normalised BP: {0:.3f}".format(lognormbpc[0][0]))

            # plot the calibrator position on sky
            ax.plot(cal_az,
                    cal_za,
                    ls="",
                    marker="^",
                    color=color,
                    zorder=1002,
                    label="cal @ {0} ({1:.2f})".format(c.obstime.gps,
                                                       bpc[0][0]))

            # plot the calibrator on the color bar
            cbarCC.ax.plot(0.5, lognormbpc, color=color, marker="^")

    # draw grid
    ax.grid(color='k', ls="--", lw=0.5)

    # azimuth labels
    ax.set_xticks(np.radians([0, 45, 90, 135, 180, 225, 270, 315]))
    ax.set_xticklabels([
        r"${0:d}^\circ$".format(int(np.ceil(x)))
        for x in np.degrees(ax.get_xticks())
    ],
                       color='k')

    #Zenith angle labels
    ax.set_rlabel_position(250)
    ax.set_yticks(np.radians([20, 40, 60, 80]))
    ax.set_yticklabels([
        r"${0:d}^\circ$".format(int(np.ceil(x)))
        for x in np.degrees(ax.get_yticks())
    ],
                       color='k')

    # Title
    ax.set_title(
        "MWA Tile beam (FEE)\naz = {0:.2f}, za = {1:.2f}, freq = {2:.2f}MHz\nobsid = {3}"
        .format(pointing_AZ, pointing_ZA, freq, obs))

    plt.legend(bbox_to_anchor=(0.95, -0.05), ncol=2)
    #plt.savefig("{0}_{1:.2f}MHz_tile.eps".format(obs, freq), bbox_inches="tight", format="eps")
    logger.info("Saving plot as output file: {0}_{1:.2f}MHz_tile.png".format(
        obs, freq))
    plt.savefig("{0}_{1:.2f}MHz_tile.png".format(obs, freq),
                bbox_inches="tight")