Given a bunch of scalars associated with intensity-type basepols, place
    them onto a grid. There should be only two polarizations represented (e.g.
    RR, LL); baselines for one of them will be put into the upper triangle of
    the grid, while baselines for the other will be put into the lower triangle.

    `bps` is an iterable that yields a superset of all bps to be gridded.

    `items` is an iterable that yields tuples of (bp, value). (`bps` is
    therefore a bit redundant with it, but this structure makes it so that we
    don't need to iterate of `items` twice, which can be convenient.)

    Returns: (pol1, pol2, ants, data), where

    pol1
      The polarization gridded into the upper triangle
    pol2
      The polarization gridded into the lower triangle
    ants
      An array of the antenna numbers seen in `bps`
    data
      An n-by-n grid of the values, where n is the size of `ants`. Unsampled
      basepols are filled with NaNs, or masked if `mask` is True.

    The basepol (ant1, ant2, pol1) is gridded into `data[i1,i2]`, where `i1`
    is the index of `ant1` in `ants`, etc. The basepol (ant1, ant2, pol2) is
    gridded into `data[i2,i1]`.

def grid_bp_data (bps, items, mask=True):
    """Given a bunch of scalars associated with intensity-type basepols, place
    them onto a grid. There should be only two polarizations represented (e.g.
    RR, LL); baselines for one of them will be put into the upper triangle of
    the grid, while baselines for the other will be put into the lower triangle.

    `bps` is an iterable that yields a superset of all bps to be gridded.

    `items` is an iterable that yields tuples of (bp, value). (`bps` is
    therefore a bit redundant with it, but this structure makes it so that we
    don't need to iterate of `items` twice, which can be convenient.)

    Returns: (pol1, pol2, ants, data), where

    pol1
      The polarization gridded into the upper triangle
    pol2
      The polarization gridded into the lower triangle
    ants
      An array of the antenna numbers seen in `bps`
    data
      An n-by-n grid of the values, where n is the size of `ants`. Unsampled
      basepols are filled with NaNs, or masked if `mask` is True.

    The basepol (ant1, ant2, pol1) is gridded into `data[i1,i2]`, where `i1`
    is the index of `ant1` in `ants`, etc. The basepol (ant1, ant2, pol2) is
    gridded into `data[i2,i1]`.

    """
    seen_ants = set ()
    seen_pols = set ()

    for ant1, ant2, pol in (bp_to_aap (bp) for bp in bps):
        seen_ants.add (ant1)
        seen_ants.add (ant2)
        seen_pols.add (pol)

    if len (seen_pols) != 2:
        raise Exception ('can only work with 2 polarizations')
    pol1, pol2 = sorted (seen_pols)

    seen_ants = np.array (sorted (seen_ants))
    ant_map = dict ((a, i) for (i, a) in enumerate (seen_ants))

    data = None
    n = len (seen_ants)

    for bp, value in items:
        if data is None:
            data = np.empty ((n, n), dtype=np.result_type (value))
            data.fill (np.nan)

        ant1, ant2, pol = bp_to_aap (bp)
        i1 = ant_map[ant1]
        i2 = ant_map[ant2]

        if pol == pol1:
            data[i1,i2] = value
        else:
            data[i2,i1] = value

    if mask:
        data = np.ma.MaskedArray (data, ~np.isfinite (data))

    return pol1, pol2, seen_ants, data