Ejemplo n.º 1
0
def make_cand_plot(d, im, data, loclabel, outname=''):
    """ Builds candidate plot.
    Expects phased, dedispersed data (cut out in time, dual-pol), image, and metadata
    loclabel is used to label the plot with (scan, segment, candint, dmind, dtind, beamnum).
    """

    # given d, im, data, make plot
    logger.info('Plotting...')
    logger.debug('(image, data) shape: (%s, %s)' %
                 (str(im.shape), str(data.shape)))

    assert len(
        loclabel
    ) == 6, 'loclabel should have (scan, segment, candint, dmind, dtind, beamnum)'
    scan, segment, candint, dmind, dtind, beamnum = loclabel

    # calc source location
    snrmin = im.min() / im.std()
    snrmax = im.max() / im.std()
    if snrmax > -1 * snrmin:
        l1, m1 = rt.calc_lm(d, im, minmax='max')
        snrobs = snrmax
    else:
        l1, m1 = rt.calc_lm(d, im, minmax='min')
        snrobs = snrmin
    pt_ra, pt_dec = d['radec']
    src_ra, src_dec = source_location(pt_ra, pt_dec, l1, m1)
    logger.info('Peak (RA, Dec): %s, %s' % (src_ra, src_dec))

    # build plot
    fig = plt.Figure(figsize=(8.5, 8))
    ax = fig.add_subplot(221, axisbg='white')

    # add annotating info
    ax.text(0.1,
            0.9,
            d['fileroot'],
            fontname='sans-serif',
            transform=ax.transAxes)
    ax.text(0.1,
            0.8,
            'sc %d, seg %d, int %d, DM %.1f, dt %d' %
            (scan, segment, candint, d['dmarr'][dmind], d['dtarr'][dtind]),
            fontname='sans-serif',
            transform=ax.transAxes)
    ax.text(0.1,
            0.7,
            'Peak: (' + str(np.round(l1, 3)) + ' ,' + str(np.round(m1, 3)) +
            '), SNR: ' + str(np.round(snrobs, 1)),
            fontname='sans-serif',
            transform=ax.transAxes)

    # plot dynamic spectra
    left, width = 0.6, 0.2
    bottom, height = 0.2, 0.7
    rect_dynsp = [left, bottom, width, height]
    rect_lc = [left, bottom - 0.1, width, 0.1]
    rect_sp = [left + width, bottom, 0.1, height]
    ax_dynsp = fig.add_axes(rect_dynsp)
    ax_lc = fig.add_axes(rect_lc)
    ax_sp = fig.add_axes(rect_sp)
    spectra = np.swapaxes(
        data.real, 0, 1
    )  # seems that latest pickle actually contains complex values in spectra...
    dd = np.concatenate(
        (spectra[..., 0], np.zeros_like(spectra[..., 0]), spectra[..., 1]),
        axis=1)  # make array for display with white space between two pols
    impl = ax_dynsp.imshow(dd,
                           origin='lower',
                           interpolation='nearest',
                           aspect='auto',
                           cmap=plt.get_cmap('Greys'))
    ax_dynsp.text(0.5,
                  0.95,
                  'RR LL',
                  horizontalalignment='center',
                  verticalalignment='center',
                  fontsize=16,
                  color='w',
                  transform=ax_dynsp.transAxes)
    ax_dynsp.set_yticks(range(0, len(d['freq']), 30))
    ax_dynsp.set_yticklabels(d['freq'][::30])
    ax_dynsp.set_ylabel('Freq (GHz)')
    ax_dynsp.set_xlabel('Integration (rel)')
    spectrum = spectra[:, len(spectra[0]) / 2].mean(
        axis=1
    )  # assume pulse in middle bin. get stokes I spectrum. **this is wrong in a minority of cases.**
    ax_sp.plot(spectrum, range(len(spectrum)), 'k.')
    ax_sp.plot(np.zeros(len(spectrum)), range(len(spectrum)), 'k:')
    ax_sp.set_ylim(0, len(spectrum))
    ax_sp.set_yticklabels([])
    xmin, xmax = ax_sp.get_xlim()
    ax_sp.set_xticks(np.linspace(xmin, xmax, 3).round(2))
    ax_sp.set_xlabel('Flux (Jy)')
    lc = dd.mean(axis=0)
    lenlc = len(data)  # old (stupid) way: lenlc = np.where(lc == 0)[0][0]
    ax_lc.plot(
        range(0, lenlc) + range(2 * lenlc, 3 * lenlc),
        list(lc)[:lenlc] + list(lc)[-lenlc:], 'k.')
    ax_lc.plot(
        range(0, lenlc) + range(2 * lenlc, 3 * lenlc),
        list(np.zeros(lenlc)) + list(np.zeros(lenlc)), 'k:')
    ax_lc.set_xlabel('Integration')
    ax_lc.set_ylabel('Flux (Jy)')
    ax_lc.set_xticks([
        0, 0.5 * lenlc, lenlc, 1.5 * lenlc, 2 * lenlc, 2.5 * lenlc, 3 * lenlc
    ])
    ax_lc.set_xticklabels(
        ['0',
         str(lenlc / 2),
         str(lenlc), '', '0',
         str(lenlc / 2),
         str(lenlc)])
    ymin, ymax = ax_lc.get_ylim()
    ax_lc.set_yticks(np.linspace(ymin, ymax, 3).round(2))

    # image
    ax = fig.add_subplot(223)
    fov = np.degrees(1. / d['uvres']) * 60.
    impl = ax.imshow(im.transpose(),
                     aspect='equal',
                     origin='upper',
                     interpolation='nearest',
                     extent=[fov / 2, -fov / 2, -fov / 2, fov / 2],
                     cmap=plt.get_cmap('Greys'),
                     vmin=0,
                     vmax=0.5 * im.max())
    ax.set_xlabel('RA Offset (arcmin)')
    ax.set_ylabel('Dec Offset (arcmin)')

    if not outname:
        outname = os.path.join(
            d['workdir'], 'cands_{}_sc{}-seg{}-i{}-dm{}-dt{}.png'.format(
                d['fileroot'], scan, segment, candint, dmind, dtind))

    try:
        canvas = FigureCanvasAgg(fig)
        canvas.print_figure(outname)
    except ValueError:
        logger.warn('Could not write figure to %s' % outname)
Ejemplo n.º 2
0
def make_cand_plot(d, im, data, loclabel, outname=''):
    """ Builds candidate plot.
    Expects phased, dedispersed data (cut out in time, dual-pol), image, and metadata
    loclabel is used to label the plot with (scan, segment, candint, dmind, dtind, beamnum).
    """

    # given d, im, data, make plot
    logger.info('Plotting...')
    logger.debug('(image, data) shape: (%s, %s)' % (str(im.shape), str(data.shape)))

    assert len(loclabel) == 6, 'loclabel should have (scan, segment, candint, dmind, dtind, beamnum)'
    scan, segment, candint, dmind, dtind, beamnum = loclabel

    # calc source location
    snrmin = im.min()/im.std()
    snrmax = im.max()/im.std()
    if snrmax > -1*snrmin:
        l1, m1 = rt.calc_lm(d, im, minmax='max')
        snrobs = snrmax
    else:
        l1, m1 = rt.calc_lm(d, im, minmax='min')
        snrobs = snrmin
    pt_ra, pt_dec = d['radec']
    src_ra, src_dec = source_location(pt_ra, pt_dec, l1, m1)
    logger.info('Peak (RA, Dec): %s, %s' % (src_ra, src_dec))

    # build plot
    fig = plt.Figure(figsize=(8.5,8))
    ax = fig.add_subplot(221, axisbg='white')

    # add annotating info
    ax.text(0.1, 0.9, d['fileroot'], fontname='sans-serif', transform = ax.transAxes)
    ax.text(0.1, 0.8, 'sc %d, seg %d, int %d, DM %.1f, dt %d' % (scan, segment, candint, d['dmarr'][dmind], d['dtarr'][dtind]), fontname='sans-serif', transform = ax.transAxes)
    ax.text(0.1, 0.7, 'Peak: (' + str(np.round(l1, 3)) + ' ,' + str(np.round(m1, 3)) + '), SNR: ' + str(np.round(snrobs, 1)), fontname='sans-serif', transform = ax.transAxes)

    # plot dynamic spectra
    left, width = 0.6, 0.2
    bottom, height = 0.2, 0.7
    rect_dynsp = [left, bottom, width, height]
    rect_lc = [left, bottom-0.1, width, 0.1]    
    rect_sp = [left+width, bottom, 0.1, height]
    ax_dynsp = fig.add_axes(rect_dynsp)
    ax_lc = fig.add_axes(rect_lc)    
    ax_sp = fig.add_axes(rect_sp)
    spectra = np.swapaxes(data.real,0,1)      # seems that latest pickle actually contains complex values in spectra...
    dd = np.concatenate( (spectra[...,0], np.zeros_like(spectra[...,0]), spectra[...,1]), axis=1)    # make array for display with white space between two pols
    impl = ax_dynsp.imshow(dd, origin='lower', interpolation='nearest', aspect='auto', cmap=plt.get_cmap('Greys'))
    ax_dynsp.text(0.5, 0.95, 'RR LL', horizontalalignment='center', verticalalignment='center', fontsize=16, color='w', transform = ax_dynsp.transAxes)
    ax_dynsp.set_yticks(range(0,len(d['freq']),30))
    ax_dynsp.set_yticklabels(d['freq'][::30])
    ax_dynsp.set_ylabel('Freq (GHz)')
    ax_dynsp.set_xlabel('Integration (rel)')
    spectrum = spectra[:,len(spectra[0])/2].mean(axis=1)      # assume pulse in middle bin. get stokes I spectrum. **this is wrong in a minority of cases.**
    ax_sp.plot(spectrum, range(len(spectrum)), 'k.')
    ax_sp.plot(np.zeros(len(spectrum)), range(len(spectrum)), 'k:')
    ax_sp.set_ylim(0, len(spectrum))
    ax_sp.set_yticklabels([])
    xmin,xmax = ax_sp.get_xlim()
    ax_sp.set_xticks(np.linspace(xmin,xmax,3).round(2))
    ax_sp.set_xlabel('Flux (Jy)')
    lc = dd.mean(axis=0)
    lenlc = len(data)  # old (stupid) way: lenlc = np.where(lc == 0)[0][0]
    ax_lc.plot(range(0,lenlc)+range(2*lenlc,3*lenlc), list(lc)[:lenlc] + list(lc)[-lenlc:], 'k.')
    ax_lc.plot(range(0,lenlc)+range(2*lenlc,3*lenlc), list(np.zeros(lenlc)) + list(np.zeros(lenlc)), 'k:')
    ax_lc.set_xlabel('Integration')
    ax_lc.set_ylabel('Flux (Jy)')
    ax_lc.set_xticks([0,0.5*lenlc,lenlc,1.5*lenlc,2*lenlc,2.5*lenlc,3*lenlc])
    ax_lc.set_xticklabels(['0',str(lenlc/2),str(lenlc),'','0',str(lenlc/2),str(lenlc)])
    ymin,ymax = ax_lc.get_ylim()
    ax_lc.set_yticks(np.linspace(ymin,ymax,3).round(2))

    # image
    ax = fig.add_subplot(223)
    fov = np.degrees(1./d['uvres'])*60.
    impl = ax.imshow(im.transpose(), aspect='equal', origin='upper',
                     interpolation='nearest', extent=[fov/2, -fov/2, -fov/2, fov/2],
                     cmap=plt.get_cmap('Greys'), vmin=0, vmax=0.5*im.max())
    ax.set_xlabel('RA Offset (arcmin)')
    ax.set_ylabel('Dec Offset (arcmin)')

    if not outname:
        outname = os.path.join(d['workdir'],
                               'cands_{}_sc{}-seg{}-i{}-dm{}-dt{}.png'.format(d['fileroot'], scan,
                                                                              segment, candint, dmind, dtind))

    try:
        canvas = FigureCanvasAgg(fig)
        canvas.print_figure(outname)
    except ValueError:
        logger.warn('Could not write figure to %s' % outname)
Ejemplo n.º 3
0
def make_cand_plot(d, im, data, loclabel, version=2, snrs=[], outname=''):
    """ Builds a new candidate plot, distinct from the original plots produced by make_cand_plot.
    Expects phased, dedispersed data (cut out in time, dual-pol), image, and metadata

    version 2 is the new one (thanks to bridget andersen). version 1 is the initial one.
    loclabel is used to label the plot with (scan, segment, candint, dmind, dtind, beamnum).
    snrs is array for an (optional) SNR histogram plot.
    d are used to label the plots with useful information.
    """

    # given d, im, data, make plot
    logger.info('Plotting...')
    logger.debug('(image, data) shape: (%s, %s)' %
                 (str(im.shape), str(data.shape)))

    assert len(
        loclabel
    ) == 6, 'loclabel should have (scan, segment, candint, dmind, dtind, beamnum)'
    scan, segment, candint, dmind, dtind, beamnum = loclabel

    # calc source location
    snrmin = im.min() / im.std()
    snrmax = im.max() / im.std()
    if snrmax > -1 * snrmin:
        l1, m1 = rt.calc_lm(d, im, minmax='max')
        snrobs = snrmax
    else:
        l1, m1 = rt.calc_lm(d, im, minmax='min')
        snrobs = snrmin
    pt_ra, pt_dec = d['radec']
    src_ra, src_dec = source_location(pt_ra, pt_dec, l1, m1)
    logger.info('Peak (RA, Dec): %s, %s' % (src_ra, src_dec))

    # convert l1 and m1 from radians to arcminutes
    l1arcm = l1 * 180. * 60. / np.pi
    m1arcm = m1 * 180. * 60. / np.pi

    if version == 1:
        # build plot
        fig = plt.Figure(figsize=(8.5, 8))
        ax = fig.add_subplot(221, axisbg='white')

        # add annotating info
        ax.text(0.1,
                0.9,
                d['fileroot'],
                fontname='sans-serif',
                transform=ax.transAxes)
        ax.text(0.1,
                0.8,
                'sc %d, seg %d, int %d, DM %.1f, dt %d' %
                (scan, segment, candint, d['dmarr'][dmind], d['dtarr'][dtind]),
                fontname='sans-serif',
                transform=ax.transAxes)
        ax.text(0.1,
                0.7,
                'Peak: (' + str(np.round(l1, 3)) + ' ,' +
                str(np.round(m1, 3)) + '), SNR: ' + str(np.round(snrobs, 1)),
                fontname='sans-serif',
                transform=ax.transAxes)

        # plot dynamic spectra
        left, width = 0.6, 0.2
        bottom, height = 0.2, 0.7
        rect_dynsp = [left, bottom, width, height]
        rect_lc = [left, bottom - 0.1, width, 0.1]
        rect_sp = [left + width, bottom, 0.1, height]
        ax_dynsp = fig.add_axes(rect_dynsp)
        ax_lc = fig.add_axes(rect_lc)
        ax_sp = fig.add_axes(rect_sp)
        spectra = np.swapaxes(
            data.real, 0, 1
        )  # seems that latest pickle actually contains complex values in spectra...
        dd = np.concatenate(
            (spectra[..., 0], np.zeros_like(spectra[..., 0]), spectra[..., 1]),
            axis=1)  # make array for display with white space between two pols
        logger.debug('{0}'.format(dd.shape))
        impl = ax_dynsp.imshow(dd,
                               origin='lower',
                               interpolation='nearest',
                               aspect='auto',
                               cmap=plt.get_cmap('Greys'))
        ax_dynsp.text(0.5,
                      0.95,
                      'RR LL',
                      horizontalalignment='center',
                      verticalalignment='center',
                      fontsize=16,
                      color='w',
                      transform=ax_dynsp.transAxes)
        ax_dynsp.set_yticks(range(0, len(d['freq']), 30))
        ax_dynsp.set_yticklabels(d['freq'][::30])
        ax_dynsp.set_ylabel('Freq (GHz)')
        ax_dynsp.set_xlabel('Integration (rel)')
        spectrum = spectra[:, len(spectra[0]) / 2].mean(
            axis=1
        )  # assume pulse in middle bin. get stokes I spectrum. **this is wrong in a minority of cases.**
        ax_sp.plot(spectrum, range(len(spectrum)), 'k.')
        ax_sp.plot(np.zeros(len(spectrum)), range(len(spectrum)), 'k:')
        ax_sp.set_ylim(0, len(spectrum))
        ax_sp.set_yticklabels([])
        xmin, xmax = ax_sp.get_xlim()
        ax_sp.set_xticks(np.linspace(xmin, xmax, 3).round(2))
        ax_sp.set_xlabel('Flux (Jy)')
        lc = dd.mean(axis=0)
        lenlc = len(data)  # old (stupid) way: lenlc = np.where(lc == 0)[0][0]
        ax_lc.plot(
            range(0, lenlc) + range(2 * lenlc, 3 * lenlc),
            list(lc)[:lenlc] + list(lc)[-lenlc:], 'k.')
        ax_lc.plot(
            range(0, lenlc) + range(2 * lenlc, 3 * lenlc),
            list(np.zeros(lenlc)) + list(np.zeros(lenlc)), 'k:')
        ax_lc.set_xlabel('Integration')
        ax_lc.set_ylabel('Flux (Jy)')
        ax_lc.set_xticks([
            0, 0.5 * lenlc, lenlc, 1.5 * lenlc, 2 * lenlc, 2.5 * lenlc,
            3 * lenlc
        ])
        ax_lc.set_xticklabels([
            '0',
            str(lenlc / 2),
            str(lenlc), '', '0',
            str(lenlc / 2),
            str(lenlc)
        ])
        ymin, ymax = ax_lc.get_ylim()
        ax_lc.set_yticks(np.linspace(ymin, ymax, 3).round(2))

        # image
        ax = fig.add_subplot(223)
        fov = np.degrees(1. / d['uvres']) * 60.
        logger.debug('{0}'.format(im.shape))
        impl = ax.imshow(im.transpose(),
                         aspect='equal',
                         origin='upper',
                         interpolation='nearest',
                         extent=[fov / 2, -fov / 2, -fov / 2, fov / 2],
                         cmap=plt.get_cmap('Greys'),
                         vmin=0,
                         vmax=0.5 * im.max())
        ax.set_xlabel('RA Offset (arcmin)')
        ax.set_ylabel('Dec Offset (arcmin)')

    elif version == 2:
        # build overall plot
        fig = plt.Figure(figsize=(12.75, 8))

        # add metadata in subfigure
        ax = fig.add_subplot(2, 3, 1, axisbg='white')

        # calculate the overall dispersion delay: dd
        f1 = d['freq_orig'][0]
        f2 = d['freq_orig'][len(d['freq_orig']) - 1]
        dd = 4.15 * d['dmarr'][dmind] * (f1**(-2) - f2**(-2))

        # add annotating info
        start = 1.1  # these values determine the spacing and location of the annotating information
        space = 0.07
        left = 0.0
        ax.text(left,
                start,
                d['fileroot'],
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - space,
                'Peak (arcmin): (' + str(np.round(l1arcm, 3)) + ', ' +
                str(np.round(m1arcm, 3)) + ')',
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        # split the RA and Dec and display in a nice format
        ra = src_ra.split()
        dec = src_dec.split()
        ax.text(left,
                start - 2 * space,
                'Peak (RA, Dec): (' + ra[0] + ':' + ra[1] + ':' + ra[2][0:4] +
                ', ' + dec[0] + ':' + dec[1] + ':' + dec[2][0:4] + ')',
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 3 * space,
                'Source: ' + str(d['source']),
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 4 * space,
                'scan: ' + str(scan),
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 5 * space,
                'segment: ' + str(segment),
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 6 * space,
                'integration: ' + str(candint),
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 7 * space,
                'DM = ' + str(d['dmarr'][dmind]) + ' (index ' + str(dmind) +
                ')',
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 8 * space,
                'dt = ' +
                str(np.round(d['inttime'] * d['dtarr'][dtind], 3) * 1e3) +
                ' ms' + ' (index ' + str(dtind) + ')',
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 9 * space,
                'disp delay = ' + str(np.round(dd, 1)) + ' ms',
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        ax.text(left,
                start - 10 * space,
                'SNR: ' + str(np.round(snrobs, 1)),
                fontname='sans-serif',
                transform=ax.transAxes,
                fontsize='small')
        # set the plot invisible so that it doesn't interfere with annotations
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
        ax.spines['bottom'].set_color('white')
        ax.spines['top'].set_color('white')
        ax.spines['right'].set_color('white')
        ax.spines['left'].set_color('white')

        # plot full dynamic spectra
        left, width = 0.75, 0.2 * 2. / 3.
        bottom, height = 0.2, 0.7
        rect_dynsp1 = [
            left, bottom, width / 3., height
        ]  # three rectangles for each panel of the spectrum (RR, RR+LL, LL)
        rect_dynsp2 = [left + width / 3., bottom, width / 3., height]
        rect_dynsp3 = [left + 2. * width / 3., bottom, width / 3., height]
        rect_lc1 = [left, bottom - 0.1, width / 3., 0.1]
        rect_lc2 = [left + width / 3., bottom - 0.1, width / 3., 0.1]
        rect_lc3 = [left + 2. * width / 3., bottom - 0.1, width / 3., 0.1]
        rect_sp = [left + width, bottom, 0.1 * 2. / 3., height]
        ax_dynsp1 = fig.add_axes(rect_dynsp1)
        ax_dynsp2 = fig.add_axes(
            rect_dynsp2, sharey=ax_dynsp1)  # sharey so that axes line up
        ax_dynsp3 = fig.add_axes(rect_dynsp3, sharey=ax_dynsp1)
        # make RR+LL and LL dynamic spectra y labels invisible so they don't interfere with the plots
        [label.set_visible(False) for label in ax_dynsp2.get_yticklabels()]
        [label.set_visible(False) for label in ax_dynsp3.get_yticklabels()]
        ax_sp = fig.add_axes(rect_sp, sharey=ax_dynsp3)
        [label.set_visible(False) for label in ax_sp.get_yticklabels()]
        ax_lc1 = fig.add_axes(rect_lc1)
        ax_lc2 = fig.add_axes(rect_lc2, sharey=ax_lc1)
        ax_lc3 = fig.add_axes(rect_lc3, sharey=ax_lc1)
        [label.set_visible(False) for label in ax_lc2.get_yticklabels()]
        [label.set_visible(False) for label in ax_lc3.get_yticklabels()]

        # now actually plot the data
        spectra = np.swapaxes(data.real, 0, 1)
        dd1 = spectra[..., 0]
        dd2 = spectra[..., 0] + spectra[..., 1]
        dd3 = spectra[..., 1]
        colormap = 'viridis'
        logger.debug('{0}'.format(dd1.shape))
        logger.debug('{0}'.format(dd2.shape))
        logger.debug('{0}'.format(dd3.shape))
        impl1 = ax_dynsp1.imshow(dd1,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        impl2 = ax_dynsp2.imshow(dd2,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        impl3 = ax_dynsp3.imshow(dd3,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        ax_dynsp1.set_yticks(range(0, len(d['freq']), 30))
        ax_dynsp1.set_yticklabels(d['freq'][::30])
        ax_dynsp1.set_ylabel('Freq (GHz)')
        ax_dynsp1.set_xlabel('RR')
        ax_dynsp1.xaxis.set_label_position('top')
        ax_dynsp2.set_xlabel('RR+LL')
        ax_dynsp2.xaxis.set_label_position('top')
        ax_dynsp3.set_xlabel('LL')
        ax_dynsp3.xaxis.set_label_position('top')
        [label.set_visible(False) for label in ax_dynsp1.get_xticklabels()
         ]  # set xlabels invisible so that they don't interefere with lc plots
        ax_dynsp1.get_yticklabels()[0].set_visible(
            False)  # This one y label was getting in the way

        # plot stokes I spectrum of the candidate pulse (assume middle bin)
        spectrum = spectra[:, len(spectra[0]) / 2].mean(
            axis=1)  # select stokes I middle bin
        ax_sp.plot(spectrum, range(len(spectrum)), 'k.')
        ax_sp.plot(np.zeros(len(spectrum)), range(len(spectrum)),
                   'r:')  # plot 0 Jy dotted line
        xmin, xmax = ax_sp.get_xlim()
        ax_sp.set_xticks(np.linspace(xmin, xmax, 3).round(2))
        ax_sp.set_xlabel('Flux (Jy)')

        # plot mean flux values for each time bin
        lc1 = dd1.mean(axis=0)
        lc2 = dd2.mean(axis=0)
        lc3 = dd3.mean(axis=0)
        lenlc = len(data)
        ax_lc1.plot(range(0, lenlc), list(lc1)[:lenlc], 'k.')
        ax_lc2.plot(range(0, lenlc), list(lc2)[:lenlc], 'k.')
        ax_lc3.plot(range(0, lenlc), list(lc3)[:lenlc], 'k.')
        ax_lc1.plot(range(0, lenlc), list(np.zeros(lenlc)),
                    'r:')  # plot 0 Jy dotted line for each plot
        ax_lc2.plot(range(0, lenlc), list(np.zeros(lenlc)), 'r:')
        ax_lc3.plot(range(0, lenlc), list(np.zeros(lenlc)), 'r:')
        ax_lc2.set_xlabel('Integration (rel)')
        ax_lc1.set_ylabel('Flux (Jy)')
        ax_lc1.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_lc1.set_xticklabels(
            ['0', str(lenlc / 2), str(lenlc)]
        )  # note I chose to only show the '0' label for one of the plots to avoid messy overlap
        ax_lc2.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_lc2.set_xticklabels(['', str(lenlc / 2), str(lenlc)])
        ax_lc3.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_lc3.set_xticklabels(['', str(lenlc / 2), str(lenlc)])
        ymin, ymax = ax_lc1.get_ylim()
        ax_lc1.set_yticks(np.linspace(ymin, ymax, 3).round(2))

        # readjust the x tick marks on the dynamic spectra so that they line up with the lc plots
        ax_dynsp1.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_dynsp2.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_dynsp3.set_xticks([0, 0.5 * lenlc, lenlc])

        # plot second set of dynamic spectra (averaged across frequency bins to get SNR=2 for the detected candidate)
        left, width = 0.45, 0.1333
        bottom, height = 0.1, 0.4
        rect_dynsp1 = [left, bottom, width / 3., height]
        rect_dynsp2 = [left + width / 3., bottom, width / 3., height]
        rect_dynsp3 = [left + 2. * width / 3., bottom, width / 3., height]
        rect_sp = [left + width, bottom, 0.1 * 2. / 3., height]
        ax_dynsp1 = fig.add_axes(rect_dynsp1)
        ax_dynsp2 = fig.add_axes(rect_dynsp2, sharey=ax_dynsp1)
        ax_dynsp3 = fig.add_axes(rect_dynsp3, sharey=ax_dynsp1)
        # make RR+LL and LL dynamic spectra y labels invisible so they don't interfere with the plots
        [label.set_visible(False) for label in ax_dynsp2.get_yticklabels()]
        [label.set_visible(False) for label in ax_dynsp3.get_yticklabels()]
        ax_sp = fig.add_axes(rect_sp, sharey=ax_dynsp3)
        [label.set_visible(False) for label in ax_sp.get_yticklabels()]

        # calculate the number of frequency rows to average together (make the plot have an SNR of 2)
        n = int((2. * (len(spectra))**0.5 / snrobs)**2)
        if n == 0:  # if n==0 then don't average any (avoids errors for modding and dividing by 0)
            dd1avg = dd1
            dd3avg = dd3
        else:
            # otherwise, add zeros onto the data so that it's length is cleanly divisible by n (makes it easier to average over)
            dd1zerotemp = np.concatenate(
                (np.zeros((n - len(spectra) % n, len(spectra[0])),
                          dtype=dd1.dtype), dd1),
                axis=0)
            dd3zerotemp = np.concatenate(
                (np.zeros((n - len(spectra) % n, len(spectra[0])),
                          dtype=dd3.dtype), dd3),
                axis=0)
            # make them masked arrays so that the appended zeros do not affect average calculation
            zeros = np.zeros((len(dd1), len(dd1[0])))
            ones = np.ones((n - len(spectra) % n, len(dd1[0])))
            masktemp = np.concatenate((ones, zeros), axis=0)
            dd1zero = ma.masked_array(dd1zerotemp, mask=masktemp)
            dd3zero = ma.masked_array(dd3zerotemp, mask=masktemp)
            # average together the data
            dd1avg = np.array([], dtype=dd1.dtype)
            for i in range(len(spectra[0])):
                temp = dd1zero[:, i].reshape(-1, n)
                tempavg = np.reshape(np.mean(temp, axis=1), (len(temp), 1))
                temprep = np.repeat(
                    tempavg, n, axis=0
                )  # repeats the mean values to create more pixels (easier to properly crop when it is finally displayed)
                if i == 0:
                    dd1avg = temprep
                else:
                    dd1avg = np.concatenate((dd1avg, temprep), axis=1)
            dd3avg = np.array([], dtype=dd3.dtype)
            for i in range(len(spectra[0])):
                temp = dd3zero[:, i].reshape(-1, n)
                tempavg = np.reshape(np.mean(temp, axis=1), (len(temp), 1))
                temprep = np.repeat(tempavg, n, axis=0)
                if i == 0:
                    dd3avg = temprep
                else:
                    dd3avg = np.concatenate((dd3avg, temprep), axis=1)
        dd2avg = dd1avg + dd3avg  # add together to get averaged RR+LL spectrum
        colormap = 'viridis'
        if n == 0:  # again, if n==0 then don't crop the spectra because no zeroes were appended
            dd1avgcrop = dd1avg
            dd2avgcrop = dd2avg
            dd3avgcrop = dd3avg
        else:  # otherwise, crop off the appended zeroes
            dd1avgcrop = dd1avg[len(ones):len(dd1avg), :]
            dd2avgcrop = dd2avg[len(ones):len(dd2avg), :]
            dd3avgcrop = dd3avg[len(ones):len(dd3avg), :]
        logger.debug('{0}'.format(dd1avgcrop.shape))
        logger.debug('{0}'.format(dd2avgcrop.shape))
        logger.debug('{0}'.format(dd3avgcrop.shape))
        impl1 = ax_dynsp1.imshow(dd1avgcrop,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        impl2 = ax_dynsp2.imshow(dd2avgcrop,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        impl3 = ax_dynsp3.imshow(dd3avgcrop,
                                 origin='lower',
                                 interpolation='nearest',
                                 aspect='auto',
                                 cmap=plt.get_cmap(colormap))
        ax_dynsp1.set_yticks(range(0, len(d['freq']), 30))
        ax_dynsp1.set_yticklabels(d['freq'][::30])
        ax_dynsp1.set_ylabel('Freq (GHz)')
        ax_dynsp1.set_xlabel('RR')
        ax_dynsp1.xaxis.set_label_position('top')
        ax_dynsp2.set_xlabel('Integration (rel)')
        ax2 = ax_dynsp2.twiny()
        ax2.set_xlabel('RR+LL')
        [label.set_visible(False) for label in ax2.get_xticklabels()]
        ax_dynsp3.set_xlabel('LL')
        ax_dynsp3.xaxis.set_label_position('top')

        # plot stokes I spectrum of the candidate pulse in the averaged data (assume middle bin)
        ax_sp.plot(dd2avgcrop[:, len(dd2avgcrop[0]) / 2] / 2.,
                   range(len(dd2avgcrop)), 'k.')
        ax_sp.plot(np.zeros(len(dd2avgcrop)), range(len(dd2avgcrop)), 'r:')
        xmin, xmax = ax_sp.get_xlim()
        ax_sp.set_xticks(np.linspace(xmin, xmax, 3).round(2))
        ax_sp.get_xticklabels()[0].set_visible(False)
        ax_sp.set_xlabel('Flux (Jy)')

        # readjust the x tick marks on the dynamic spectra
        ax_dynsp1.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_dynsp1.set_xticklabels(['0', str(lenlc / 2), str(lenlc)])
        ax_dynsp2.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_dynsp2.set_xticklabels(['', str(lenlc / 2), str(lenlc)])
        ax_dynsp3.set_xticks([0, 0.5 * lenlc, lenlc])
        ax_dynsp3.set_xticklabels(['', str(lenlc / 2), str(lenlc)])

        # plot the image and zoomed cutout
        ax = fig.add_subplot(2, 3, 4)
        fov = np.degrees(1. / d['uvres']) * 60.
        impl = ax.imshow(im.transpose(),
                         aspect='equal',
                         origin='upper',
                         interpolation='nearest',
                         extent=[fov / 2, -fov / 2, -fov / 2, fov / 2],
                         cmap=plt.get_cmap('viridis'),
                         vmin=0,
                         vmax=0.5 * im.max())
        ax.set_xlabel('RA Offset (arcmin)')
        ax.set_ylabel('Dec Offset (arcmin)')
        ax.autoscale(
            False
        )  # to stop the plot from autoscaling when we plot the triangles that label the location
        # add markers on the axes to indicate the measured position of the candidate
        ax.scatter(x=[l1arcm],
                   y=[-fov / 2],
                   c='#ffff00',
                   s=60,
                   marker='^',
                   clip_on=False)
        ax.scatter(x=[fov / 2],
                   y=[m1arcm],
                   c='#ffff00',
                   s=60,
                   marker='>',
                   clip_on=False)
        ax.set_frame_on(
            False
        )  # makes it so the axis does not intersect the location triangles (for cosmetic reasons)

        # add a zoomed cutout image of the candidate (set width at 5*synthesized beam)
        key = d['vrange'].keys()[0]
        umax = d['urange'][key]
        vmax = d['vrange'][key]
        uvdist = (umax**2 + vmax**2)**0.5
        sbeam = np.degrees(
            d['uvoversample'] /
            uvdist) * 60.  # calculate synthesized beam in arcminutes
        # figure out the location to center the zoomed image on
        xratio = len(im[0]) / fov  # pix/arcmin
        yratio = len(im) / fov  # pix/arcmin
        mult = 5  # sets how many times the synthesized beam the zoomed FOV is
        xmin = max(0, int(len(im[0]) / 2 - (m1arcm + sbeam * mult) * xratio))
        xmax = int(len(im[0]) / 2 - (m1arcm - sbeam * mult) * xratio)
        ymin = max(0, int(len(im) / 2 - (l1arcm + sbeam * mult) * yratio))
        ymax = int(len(im) / 2 - (l1arcm - sbeam * mult) * yratio)
        left, width = 0.231, 0.15
        bottom, height = 0.465, 0.15
        rect_imcrop = [left, bottom, width, height]
        ax_imcrop = fig.add_axes(rect_imcrop)
        logger.debug('{0}'.format(im.transpose()[xmin:xmax, ymin:ymax].shape))
        logger.debug('{0} {1} {2} {3}'.format(xmin, xmax, ymin, ymax))
        impl = ax_imcrop.imshow(im.transpose()[xmin:xmax, ymin:ymax],
                                aspect=1,
                                origin='upper',
                                interpolation='nearest',
                                extent=[-1, 1, -1, 1],
                                cmap=plt.get_cmap('viridis'),
                                vmin=0,
                                vmax=0.5 * im.max())
        # setup the axes
        ax_imcrop.set_ylabel('Dec (arcmin)')
        ax_imcrop.set_xlabel('RA (arcmin)')
        ax_imcrop.xaxis.set_label_position('top')
        ax_imcrop.xaxis.tick_top()
        xlabels = [
            str(np.round(l1arcm + sbeam * mult / 2, 1)), '',
            str(np.round(l1arcm, 1)), '',
            str(np.round(l1arcm - sbeam * mult / 2, 1))
        ]
        ylabels = [
            str(np.round(m1arcm - sbeam * mult / 2, 1)), '',
            str(np.round(m1arcm, 1)), '',
            str(np.round(m1arcm + sbeam * mult / 2, 1))
        ]
        ax_imcrop.set_xticklabels(xlabels)
        ax_imcrop.set_yticklabels(ylabels)
        # change axis label location of inset so it doesn't interfere with the full picture
        ax_imcrop.get_yticklabels()[0].set_verticalalignment('bottom')

        # create SNR versus N histogram for the whole observation (properties for each candidate in the observation given by prop)
        if len(snrs):
            left, width = 0.45, 0.2
            bottom, height = 0.6, 0.3
            rect_snr = [left, bottom, width, height]
            ax_snr = fig.add_axes(rect_snr)
            pos_snrs = snrs[snrs >= 0]
            neg_snrs = snrs[snrs < 0]
            if not len(
                    neg_snrs):  # if working with subset and only positive snrs
                neg_snrs = pos_snrs
                nonegs = True
            else:
                nonegs = False
            minval = 5.5
            maxval = 8.0
            # determine the min and max values of the x axis
            if min(pos_snrs) < min(np.abs(neg_snrs)):
                minval = min(pos_snrs)
            else:
                minval = min(np.abs(neg_snrs))
            if max(pos_snrs) > max(np.abs(neg_snrs)):
                maxval = max(pos_snrs)
            else:
                maxval = max(np.abs(neg_snrs))

            # positive SNR bins are in blue
            # absolute values of negative SNR bins are taken and plotted as red x's on top of positive blue bins for compactness
            n, b, patches = ax_snr.hist(pos_snrs,
                                        50, (minval, maxval),
                                        facecolor='blue',
                                        zorder=1)
            vals, bin_edges = np.histogram(np.abs(neg_snrs), 50,
                                           (minval, maxval))
            bins = np.array([(bin_edges[i] + bin_edges[i + 1]) / 2.
                             for i in range(len(vals))])
            vals = np.array(vals)
            if not nonegs:
                ax_snr.scatter(bins[vals > 0],
                               vals[vals > 0],
                               marker='x',
                               c='orangered',
                               alpha=1.0,
                               zorder=2)
            ax_snr.set_xlabel('SNR')
            ax_snr.set_xlim(left=minval - 0.2)
            ax_snr.set_xlim(right=maxval + 0.2)
            ax_snr.set_ylabel('N')
            ax_snr.set_yscale('log')
            # draw vertical line where the candidate SNR is
            ax_snr.axvline(x=np.abs(snrobs), linewidth=1, color='y', alpha=0.7)

    else:
        logger.warn('make_cand_plot version not recognized.')

    if not outname:
        outname = os.path.join(
            d['workdir'], 'cands_{}_sc{}-seg{}-i{}-dm{}-dt{}.png'.format(
                d['fileroot'], scan, segment, candint, dmind, dtind))

    try:
        canvas = FigureCanvasAgg(fig)
        canvas.print_figure(outname)
    except ValueError:
        logger.warn('Could not write figure to %s' % outname)
Ejemplo n.º 4
0
def plot_cand(mergepkl, candnum=-1, outname='', threshold=0, **kwargs):
    """ Create detailed plot of a single candidate.
    threshold is min of sbs(SNR) used to filter candidates to select with candnum.
    kwargs passed to rt.set_pipeline
    """

    d = pickle.load(open(mergepkl, 'r'))
    loc, prop = read_candidates(mergepkl)
    
    if not os.path.dirname(d['filename']):
        d['filename'] = os.path.join(d['workdir'], d['filename'])

    # feature columns
    if 'snr2' in d['features']:
        snrcol = d['features'].index('snr2')
    elif 'snr1' in d['features']:
        snrcol = d['features'].index('snr1')
    if 'l2' in d['features']:
        lcol = d['features'].index('l2')
    elif 'l1' in d['features']:
        lcol = d['features'].index('l1')
    if 'm2' in d['features']:
        mcol = d['features'].index('m2')
    elif 'm1' in d['features']:
        mcol = d['features'].index('m1')
        
    scancol = d['featureind'].index('scan')
    segmentcol = d['featureind'].index('segment')
    intcol = d['featureind'].index('int')
    dtindcol = d['featureind'].index('dtind')
    dmindcol = d['featureind'].index('dmind')

    # sort and prep candidate list
    snrs = n.array([prop[i][snrcol] for i in range(len(prop))])
    select = n.where(n.abs(snrs) > threshold)[0]
    loc = loc[select]
    prop = [prop[i] for i in select]

    if candnum < 0:
        logger.info('Getting candidates...')
        for i in range(len(loc)):
            logger.info("%d %s %s" % (i, str(loc[i]), str(prop[i][snrcol])))
        return (loc, n.array([prop[i][snrcol] for i in range(len(prop))]))
    else:
        logger.info('Reproducing and visualizing candidate %d at %s with properties %s.' % (candnum, loc[candnum], prop[candnum]))

        # get cand info
        scan = loc[candnum, scancol]
        segment = loc[candnum, segmentcol]
        dmind = loc[candnum, dmindcol]
        dtind = loc[candnum, dtindcol]
        candint = loc[candnum, intcol]
        dmarrorig = d['dmarr']
        dtarrorig = d['dtarr']

        # set up state dict
        d['starttime_mjd'] = d['starttime_mjddict'][scan]
        d['scan'] = scan
        d['nsegments'] = len(d['segmenttimesdict'][scan])
#        d2 = rt.set_pipeline(d['filename'], scan, fileroot=d['fileroot'], savecands=False, savenoise=False, nsegments=nsegments, **kwargs)
        d['segmenttimes'] = d['segmenttimesdict'][scan]
        d['savecands'] = False; d['savenoise'] = False
        for key in kwargs.keys():
            logger.info('Setting %s to %s' % (key, kwargs[key]))
            d[key] = kwargs[key]

        # reproduce
        im, data = rt.pipeline_reproduce(d, segment, (candint, dmind, dtind))  # with candnum, pipeline will return cand image and data

        # calc source location
        snrmin = im.min()/im.std()
        snrmax = im.max()/im.std()
        if snrmax > -1*snrmin:
            l1, m1 = rt.calc_lm(d, im, minmax='max')
        else:
            l1, m1 = rt.calc_lm(d, im, minmax='min')
        pt_ra, pt_dec = d['radec']
        src_ra, src_dec = source_location(pt_ra, pt_dec, l1, m1)
        logger.info('Peak (RA, Dec): %s, %s' % (src_ra, src_dec))

        # plot it
        logger.info('Plotting...')
        fig = plt.Figure(figsize=(8.5,8))
        ax = fig.add_subplot(221, axisbg='white')

        # # first plot dm-t distribution beneath
        times0 = int2mjd(d, loc)
        times0 = times0 - times0[0]
        times = times0[candnum]
        dms0 = n.array(dmarrorig)[list(loc[:,dmindcol])]
        dms = dmarrorig[loc[candnum,dmindcol]]
        snr0 = [prop[i][snrcol] for i in range(len(prop))]
        snr = snr0[candnum]
        snrmin = 0.8 * min(d['sigma_image1'], d['sigma_image2'])
        # plot positive
        good = n.where(snr0 > 0)[0]
        ax.scatter(times0[good], dms0[good], s=(snr0[good]-snrmin)**5, facecolor='none', linewidth=0.2, clip_on=False)
        ax.scatter(times, dms, s=(snr-snrmin)**5, facecolor='none', linewidth=2, clip_on=False)
        # plot negative
        good = n.where(snr0 < 0)
        ax.scatter(times0[good], dms0[good], s=(n.abs(snr0)[good]-snrmin)**5, marker='x', edgecolors='k', linewidth=0.2, clip_on=False)
        ax.set_ylim(dmarrorig[0], dmarrorig[-1])
        ax.set_xlim(times0.min(), times0.max())
        ax.set_xlabel('Time (s)')
        ax.set_ylabel('DM (pc/cm3)')

        # # then add annotating info
        ax.text(0.1, 0.9, d['fileroot']+'_sc'+str(scan), fontname='sans-serif', transform = ax.transAxes)
        ax.text(0.1, 0.8, 'seg %d, int %d, DM %.1f, dt %d' % (segment, loc[candnum, intcol], dmarrorig[loc[candnum, dmindcol]], dtarrorig[loc[candnum,dtindcol]]), fontname='sans-serif', transform = ax.transAxes)

        ax.text(0.1, 0.7, 'Peak: (' + str(n.round(l1, 3)) + '\' ,' + str(n.round(m1, 3)) + '\'), SNR: ' + str(n.round(snr, 1)), fontname='sans-serif', transform = ax.transAxes)

        # plot dynamic spectra
        left, width = 0.6, 0.2
        bottom, height = 0.2, 0.7
        rect_dynsp = [left, bottom, width, height]
        rect_lc = [left, bottom-0.1, width, 0.1]    
        rect_sp = [left+width, bottom, 0.1, height]
        ax_dynsp = fig.add_axes(rect_dynsp)
        ax_lc = fig.add_axes(rect_lc)    
        ax_sp = fig.add_axes(rect_sp)
        spectra = n.swapaxes(data.real,0,1)      # seems that latest pickle actually contains complex values in spectra...
        dd = n.concatenate( (spectra[...,0], n.zeros_like(spectra[...,0]), spectra[...,1]), axis=1)    # make array for display with white space between two pols
        impl = ax_dynsp.imshow(dd, origin='lower', interpolation='nearest', aspect='auto', cmap=plt.get_cmap('Greys'))
        ax_dynsp.text(0.5, 0.95, 'RR LL', horizontalalignment='center', verticalalignment='center', fontsize=16, color='w', transform = ax_dynsp.transAxes)
        ax_dynsp.set_yticks(range(0,len(d['freq']),30))
        ax_dynsp.set_yticklabels(d['freq'][::30])
        ax_dynsp.set_ylabel('Freq (GHz)')
        ax_dynsp.set_xlabel('Integration (rel)')
        spectrum = spectra[:,len(spectra[0])/2].mean(axis=1)      # assume pulse in middle bin. get stokes I spectrum. **this is wrong in a minority of cases.**
        ax_sp.plot(spectrum, range(len(spectrum)), 'k.')
        ax_sp.plot(n.zeros(len(spectrum)), range(len(spectrum)), 'k:')
        ax_sp.set_ylim(0, len(spectrum))
        ax_sp.set_yticklabels([])
        xmin,xmax = ax_sp.get_xlim()
        ax_sp.set_xticks(n.linspace(xmin,xmax,3).round(2))
        ax_sp.set_xlabel('Flux (Jy)')
        lc = dd.mean(axis=0)
        lenlc = n.where(lc == 0)[0][0]
        ax_lc.plot(range(0,lenlc)+range(2*lenlc,3*lenlc), list(lc)[:lenlc] + list(lc)[-lenlc:], 'k.')
        ax_lc.plot(range(0,lenlc)+range(2*lenlc,3*lenlc), list(n.zeros(lenlc)) + list(n.zeros(lenlc)), 'k:')
        ax_lc.set_xlabel('Integration')
        ax_lc.set_ylabel('Flux (Jy)')
        ax_lc.set_xticks([0,0.5*lenlc,lenlc,1.5*lenlc,2*lenlc,2.5*lenlc,3*lenlc])
        ax_lc.set_xticklabels(['0',str(lenlc/2),str(lenlc),'','0',str(lenlc/2),str(lenlc)])
        ymin,ymax = ax_lc.get_ylim()
        ax_lc.set_yticks(n.linspace(ymin,ymax,3).round(2))

        # image
        ax = fig.add_subplot(223)
        fov = n.degrees(1./d['uvres'])*60.
#        ax.scatter(((xpix/2-srcra[0])-0.05*xpix)*fov/xpix, (ypix/2-srcdec[0])*fov/ypix, s=80, marker='<', facecolor='none')
#        ax.scatter(((xpix/2-srcra[0])+0.05*xpix)*fov/xpix, (ypix/2-srcdec[0])*fov/ypix, s=80, marker='>', facecolor='none')
        impl = ax.imshow(im.transpose(), aspect='equal', origin='upper', interpolation='nearest', extent=[fov/2, -fov/2, -fov/2, fov/2], cmap=plt.get_cmap('Greys'), vmin=0, vmax=0.5*im.max())
        ax.set_xlabel('RA Offset (arcmin)')
        ax.set_ylabel('Dec Offset (arcmin)')

        if not outname:
            outname = os.path.join(d['workdir'], 'cands_%s_sc%dseg%di%ddm%ddt%d.png' % (d['fileroot'], scan, segment, loc[candnum, intcol], dmind, dtind))
            canvas = FigureCanvasAgg(fig)
            canvas.print_figure(outname)

        return ([],[])