Пример #1
0
    moon = kat.sources.lookup['moon']
    with start_session(kat, **vars(opts)) as session:
        session.standard_setup(**vars(opts))
        session.nd_params = nd_off
        session.capture_start()
        once = True
        start_time = time.time()
        while once or time.time() < start_time + opts.max_duration:
            once = False
            moon = katpoint.Target('Moon, special')
            antenna = katpoint.Antenna(
                'ant1, -30:43:17.3, 21:24:38.5, 1038.0, 12.0, 18.4 -8.7 0.0, -0:05:30.6 0 -0:00:03.3 0:02:14.2 0:00:01.6 -0:01:30.6 0:08:42.1, 1.22'
            )  # find some way of getting this from session
            moon.antenna = antenna
            off1_azel = katpoint.construct_azel_target(
                wrap_angle(moon.azel()[0] + np.radians(10)),
                moon.azel()[1])
            off1_azel.antenna = antenna
            off1 = katpoint.construct_radec_target(off1_azel.radec()[0],
                                                   off1_azel.radec()[1])
            off1.antenna = antenna
            off1.name = 'off1'

            off2_azel = katpoint.construct_azel_target(
                wrap_angle(moon.azel()[0] - np.radians(10)),
                moon.azel()[1])
            off2_azel.antenna = antenna
            off2 = katpoint.construct_radec_target(off2_azel.radec()[0],
                                                   off2_azel.radec()[1])
            off2.antenna = antenna
            off2.name = 'off2'
Пример #2
0
def calc_pointing_offsets(session, beams, target, middle_time, temperature,
                          pressure, humidity):
    """Calculate pointing offsets per receptor based on primary beam fits.

    Parameters
    ----------
    session : :class:`katcorelib.observe.CaptureSession` object
        The active capture session
    beams : dict mapping receptor name to list of :class:`BeamPatternFit`
        Fitted primary beams, per receptor and per frequency chunk
    target : :class:`katpoint.Target` object
        The target on which offset pointings were done
    middle_time : float
        Unix timestamp at the middle of sequence of offset pointings, used to
        find the mean location of a moving target (and reference for weather)
    temperature, pressure, humidity : float
        Atmospheric conditions at middle time, used for refraction correction

    Returns
    -------
    pointing_offsets : dict mapping receptor name to offset data (10 floats)
        Pointing offsets per receptor in degrees, stored as a sequence of
          - requested (az, el) after refraction (input to the pointing model),
          - full (az, el) offset, including contributions of existing pointing
            model, any existing adjustment and newly fitted adjustment
            (useful for fitting new pointing models as it is independent),
          - full (az, el) adjustment on top of existing pointing model,
            replacing any existing adjustment (useful for reference pointing),
          - relative (az, el) adjustment on top of existing pointing model and
            adjustment (useful for verifying reference pointing), and
          - rough uncertainty (standard deviation) of (az, el) adjustment.

    """
    pointing_offsets = {}
    # Iterate over receptors
    for ant in sorted(session.observers):
        beams_freq = beams.get(ant.name, [])
        beams_freq = [b for b in beams_freq if b is not None and b.is_valid]
        if not beams_freq:
            user_logger.debug("%s had no valid primary beam fitted", ant.name)
            continue
        offsets_freq = np.array([b.center for b in beams_freq])
        offsets_freq_std = np.array([b.std_center for b in beams_freq])
        weights_freq = 1. / offsets_freq_std**2
        # Do weighted average of offsets over frequency chunks
        results = np.average(offsets_freq,
                             axis=0,
                             weights=weights_freq,
                             returned=True)
        pointing_offset = results[0]
        pointing_offset_std = np.sqrt(1. / results[1])
        user_logger.debug("%s x=%+7.2f'+-%.2f\" y=%+7.2f'+-%.2f\"", ant.name,
                          pointing_offset[0] * 60,
                          pointing_offset_std[0] * 3600,
                          pointing_offset[1] * 60,
                          pointing_offset_std[1] * 3600)
        # Get existing pointing adjustment
        receptor = getattr(session.kat, ant.name)
        az_adjust = receptor.sensor.pos_adjust_pointm_azim.get_value()
        el_adjust = receptor.sensor.pos_adjust_pointm_elev.get_value()
        existing_adjustment = deg2rad(np.array((az_adjust, el_adjust)))
        # Start with requested (az, el) coordinates, as they apply
        # at the middle time for a moving target
        requested_azel = target.azel(timestamp=middle_time, antenna=ant)
        # Correct for refraction, which becomes the requested value
        # at input of pointing model
        rc = RefractionCorrection()

        def refract(az, el):  # noqa: E306, E301
            """Apply refraction correction as at the middle of scan."""
            return [az, rc.apply(el, temperature, pressure, humidity)]

        refracted_azel = np.array(refract(*requested_azel))
        # More stages that apply existing pointing model and/or adjustment
        pointed_azel = np.array(ant.pointing_model.apply(*refracted_azel))
        adjusted_azel = pointed_azel + existing_adjustment
        # Convert fitted offset back to spherical (az, el) coordinates
        pointing_offset = deg2rad(np.array(pointing_offset))
        beam_center_azel = target.plane_to_sphere(*pointing_offset,
                                                  timestamp=middle_time,
                                                  antenna=ant)
        # Now correct the measured (az, el) for refraction and then apply the
        # existing pointing model and adjustment to get a "raw" measured
        # (az, el) at the output of the pointing model stage
        beam_center_azel = refract(*beam_center_azel)
        beam_center_azel = ant.pointing_model.apply(*beam_center_azel)
        beam_center_azel = np.array(beam_center_azel) + existing_adjustment
        # Make sure the offset is a small angle around 0 degrees
        full_offset_azel = wrap_angle(beam_center_azel - refracted_azel)
        full_adjust_azel = wrap_angle(beam_center_azel - pointed_azel)
        relative_adjust_azel = wrap_angle(beam_center_azel - adjusted_azel)
        # Cheap 'n' cheerful way to convert cross-el uncertainty to azim form
        offset_azel_std = pointing_offset_std / \
            np.array([np.cos(refracted_azel[1]), 1.])
        # We store all variants of the pointing offset since we have it all
        # at our fingertips here
        point_data = np.r_[rad2deg(refracted_azel),
                           rad2deg(full_offset_azel),
                           rad2deg(full_adjust_azel),
                           rad2deg(relative_adjust_azel), offset_azel_std]
        pointing_offsets[ant.name] = point_data
    return pointing_offsets
Пример #3
0
        # Start capture session, which creates HDF5 file
        with start_session(kat, **vars(opts)) as session:
            session.standard_setup(**vars(opts))
            session.capture_start()

            start_time = time.time()
            targets_observed = []
            # Keep going until the time is up
            keep_going = True
            while keep_going:
                keep_going = (opts.max_duration is not None) and opts.repeat
                targets_before_loop = len(targets_observed)
                # Iterate through source list, picking the next one that is up
                for target in observation_sources.iterfilter(el_limit_deg=opts.horizon):
                    target_future_azel = target.azel(timestamp=time.time()+opts.track_duration/2)
                    target = katpoint.construct_azel_target(katpoint.wrap_angle(target_future_azel[0]),katpoint.wrap_angle(target_future_azel[1]))
                    session.label('track')
                    user_logger.info("Initiating %g-second drift scan on target '%s'" % (opts.track_duration, target.name,))
                    # Split the total track on one target into segments lasting as long as the noise diode period
                    # This ensures the maximum number of noise diode firings
                    total_track_time = 0.
                    while total_track_time < opts.track_duration:
                        next_track = opts.track_duration - total_track_time
                        # Cut the track short if time ran out
                        if opts.max_duration is not None:
                            next_track = min(next_track, opts.max_duration - (time.time() - start_time))
                        if opts.nd_params['period'] > 0:
                            next_track = min(next_track, opts.nd_params['period'])
                        if next_track <= 0 or not session.track(target, duration=next_track, announce=False):
                            break
                        total_track_time += next_track
Пример #4
0
    if not kat.dry_run and kat.ants.req.mode('STOP') :
        user_logger.info("Setting Antenna Mode to 'STOP', Powering on Antenna Drives.")
        time.sleep(10)
    else:
        if not kat.dry_run :
            user_logger.error("Unable to set Antenna mode to 'STOP'.")


    once = True
    start_time = time.time()
    while once or  time.time() < start_time + opts.max_duration :
        once = False
        moon =  katpoint.Target('Moon, special')
        antenna = katpoint.Antenna('ant1, -30:43:17.3, 21:24:38.5, 1038.0, 12.0, 18.4 -8.7 0.0, -0:05:30.6 0 -0:00:03.3 0:02:14.2 0:00:01.6 -0:01:30.6 0:08:42.1, 1.22')  # find some way of getting this from session
        moon.antenna = antenna
        off1_azel = katpoint.construct_azel_target(wrap_angle(moon.azel()[0] + np.radians(10) ),moon.azel()[1] )
        off1_azel.antenna = antenna
        off1      = katpoint.construct_radec_target(off1_azel.radec()[0],off1_azel.radec()[1])
        off1.antenna = antenna
        off1.name = 'off1'

        off2_azel = katpoint.construct_azel_target(wrap_angle(moon.azel()[0] - np.radians(10) ),moon.azel()[1] )
        off2_azel.antenna = antenna
        off2      = katpoint.construct_radec_target(off2_azel.radec()[0],off2_azel.radec()[1])
        off2.antenna =  antenna 
        off2.name = 'off2'
        sources = katpoint.Catalogue(add_specials=False)
        sources.add(moon)
        sources.add(off2)
        sources.add(off1)
        txtlist = ', '.join(  [ "'%s'" % (target.name,)  for target in sources])
Пример #5
0
def reversescan(session, target, nd_period=None, lead_time=None, **kwargs):
    """Reverse scan observation.

    This scan is done in "Reverse"
    This means that it is given an area to scan (via kwargs)
    rather that a target and parameters

    Parameters
    ----------
    session: `CaptureSession`
    target: katpoint.Target
    nd_period: float
        noisediode period
    lead_time: float
        noisediode trigger lead time

    """
    # trigger noise diode if set
    trigger(session.kat, duration=nd_period, lead_time=lead_time)
    if 'radec_p1' in kwargs and 'radec_p2' in kwargs:
        # means that there is a target area
        # find lowest setting part or
        # highest rising part
        antenna = copy.copy(target.antenna)
        target_list = []
        for i in range(1, _MAX_POINTS_IN_SCAN_AREA_POLYGON + 1):
            key = 'radec_p%i' % i
            if key in kwargs:
                target_list.append(
                    katpoint.Target(
                        't%i,radec,%s' % (i, kwargs[key]),
                        antenna=target.antenna,
                    ))
    else:
        user_logger.error(
            "No scan area defined - require radec_p1 and radec_p2")
        return False
    direction = kwargs.get("direction", False)
    scan_speed = kwargs.get("scan_speed", _DEFAULT_SCAN_SPEED_ARCMIN_PER_SEC)

    obs_start_ts = katpoint.Timestamp(time.time()).to_ephem_date()
    # use 1 deg offset to pre-position >4 min in the future to take into account slewing
    el, az_min, az_max, t_start, t_end = _get_scan_area_extents(target_list,
                                                                antenna,
                                                                obs_start_ts,
                                                                offset_deg=1)

    # TODO: get horizon limit from observation - may want to pass limits of
    #       the "acceptable elevation extent" into _get_scan_area_extents.
    if _MIN_HORIZON_EL_FOR_SCAN_DEG > np.degrees(el):
        user_logger.warning(
            "Source and scan below horizon: %s < %s",
            np.degrees(el),
            _MIN_HORIZON_EL_FOR_SCAN_DEG,
        )
        return False

    scan_target = katpoint.construct_azel_target(katpoint.wrap_angle(az_min),
                                                 el)
    scan_target.name = target.name
    # katpoint destructively set dates and times during calculation
    # restore datetime before continuing
    scan_target.antenna = antenna
    scan_target.antenna.observer.date = obs_start_ts
    user_logger.info("Slew to scan start")  # slew to target.
    target_visible = session.track(scan_target, duration=0.0, announce=False)
    if not target_visible:
        user_logger.warning(
            "Start of scan is not visible!  Elevation: %.1f deg.",
            np.degrees(el))
        return False

    # This is the real scan
    obs_start_ts = katpoint.Timestamp(time.time()).to_ephem_date()
    el, az_min, az_max, t_start, t_end = _get_scan_area_extents(
        target_list, antenna, obs_start_ts)
    scan_target = katpoint.construct_azel_target(
        katpoint.wrap_angle((az_min + az_max) / 2.), el)
    scan_target.name = target.name
    # katpoint destructively set dates and times during calculation
    # restore datetime before continuing
    scan_target.antenna = antenna
    scan_target.antenna.observer.date = obs_start_ts

    scan_start = np.degrees(az_min - (az_min + az_max) / 2.)
    scan_end = np.degrees(az_max - (az_min + az_max) / 2.)

    scanargs = {}
    if "projection" in kwargs:
        scanargs["projection"] = kwargs["projection"]

    # take into account projection effects of the sky and convert to degrees per second
    # E.g., 5 arcmin/s should translate to 5/60/cos(el) deg/s
    scan_speed = (scan_speed / 60.0) / np.cos(el)
    scanargs["duration"] = abs(scan_start -
                               scan_end) / scan_speed  # Duration in seconds
    user_logger.info("Scan duration is %.2f and scan speed is %.2f deg/s",
                     scanargs["duration"], scan_speed)
    user_logger.info("Start Time: %s", t_start)
    user_logger.info("End Time: %s", t_end)
    num_scan_lines = 0
    while time.time() <= t_end.secs:
        if direction:
            scanargs["start"] = scan_start, 0.0
            scanargs["end"] = scan_end, 0.0
        else:
            scanargs["start"] = scan_end, 0.0
            scanargs["end"] = scan_start, 0.0
        user_logger.info("Azimuth scan extent [%.1f, %.1f]" %
                         (scanargs["start"][0], scanargs["end"][0]))
        target_visible = scan(session,
                              scan_target,
                              nd_period=nd_period,
                              lead_time=lead_time,
                              **scanargs)
        direction = not direction
        if target_visible:
            num_scan_lines += 1
    user_logger.info("Scan completed - %s scan lines", num_scan_lines)
    return num_scan_lines > 0
Пример #6
0
                                      katpoint.rad2deg(dec))
        Ntarget = katpoint.construct_radec_target(ra, dec2)
        Ntarget.antenna = bf_ants
        Ntarget.name = target_name + '_R'
        target = Ntarget
        print target
        print target.name

        # Get onto beamformer target
        session.track(target, duration=5)
        # Perform a drift scan if selected
        if opts.drift_scan:
            transit_time = katpoint.Timestamp() + opts.target_duration / 2.0
            # Stationary transit point becomes new target
            az, el = target.azel(timestamp=transit_time)
            target = katpoint.construct_azel_target(katpoint.wrap_angle(az),
                                                    el)
            # Go to transit point so long
            session.track(target, duration=0)
        # Only start capturing once we are on target
        session.capture_start()

        print "sleeping 10 secs, will be removed later in dpc is not asynchronous"
        time.sleep(10)

        # for targets in list
        print "kat.ptuse_1.req.ptuse_target_start (" + data_product_id + ", " + beam_id + ", " + target.name + ")"
        reply = kat.ptuse_1.req.ptuse_target_start(data_product_id, beam_id,
                                                   target.name)
        print "kat.ptuse_1.req.ptuse_target_start returned " + str(reply)
Пример #7
0
def fit_pointing_model(filename, opts):
    # declare the globals to update their values
    global az
    global el
    global measured_delta_az
    global measured_delta_el
    global std_delta_az
    global std_delta_el
    global keep

    # These fields contain strings, while the rest of the fields are assumed to contain floats
    string_fields = ['dataset', 'target', 'timestamp_ut', 'data_unit']

    # Load old pointing model, if given
    old_model = None
    if opts.pmfilename:
        try:
            old_model = katpoint.PointingModel(file(opts.pmfilename).readline())
            print("Loaded %d-parameter pointing model from '%s'" % (len(old_model), opts.pmfilename))
        except IOError:
            raise RuntimeError("Could not load old pointing model from '%s'" % (opts.pmfilename,))
    # Load data file in one shot as an array of strings
    data = np.loadtxt(filename, dtype='string', comments='#', delimiter=', ')
    # Interpret first non-comment line as header
    fields = data[0].tolist()
    # By default, all fields are assumed to contain floats
    formats = np.tile(np.float, len(fields))
    # The string_fields are assumed to contain strings - use data's string type, as it is of sufficient length
    formats[[fields.index(name) for name in string_fields if name in fields]] = data.dtype
    # Convert to heterogeneous record array
    data = np.rec.fromarrays(data[1:].transpose(), dtype=list(zip(fields, formats)))
    # Load antenna description string from first line of file and construct antenna object from it
    antenna = katpoint.Antenna(file(filename).readline().strip().partition('=')[2])
    # Use the pointing model contained in antenna object as the old model (if not overridden by file)
    # If the antenna has no model specified, a default null model will be used
    if old_model is None:
        old_model = antenna.pointing_model
    # Obtain desired fields and convert to radians
    az, el = wrap_angle(deg2rad(data['azimuth'])), deg2rad(data['elevation'])
    measured_delta_az, measured_delta_el = deg2rad(data['delta_azimuth']), deg2rad(data['delta_elevation'])
    # Uncertainties are optional
    min_std = deg2rad(opts.min_rms / 60. / np.sqrt(2))
    std_delta_az = np.clip(deg2rad(data['delta_azimuth_std']), min_std, np.inf) \
            if 'delta_azimuth_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(az))
    std_delta_el = np.clip(deg2rad(data['delta_elevation_std']), min_std, np.inf) \
            if 'delta_elevation_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(el))

    targets = data['target']
    keep = data['keep'].astype(np.bool) if 'keep' in data.dtype.fields else np.tile(True, len(targets))
    # List of unique targets in data set and target index for each data point
    unique_targets = np.unique(targets).tolist()
    target_index = np.array([unique_targets.index(t) for t in targets])

    # Initialise new pointing model and set default enabled parameters
    new_model = katpoint.PointingModel()
    num_params = len(new_model)
    default_enabled = np.nonzero(list(old_model.values()))[0]
    # If the old model is empty / null, select the most basic set of parameters for starters
    if len(default_enabled) == 0:
        default_enabled = np.array([1, 3, 4, 5, 6, 7]) - 1
    enabled_params = np.tile(False, num_params)
    enabled_params[default_enabled] = True
    enabled_params = enabled_params.tolist()

    old = PointingResults(old_model)
    new = PointingResults(new_model)

    # Fit new pointing model and update results
    params, sigma_params = new_model.fit(az[keep], el[keep], measured_delta_az[keep], measured_delta_el[keep],
                                         std_delta_az[keep], std_delta_el[keep], enabled_params)
    new.update(new_model)

    # Axis limit to be applied to all residual plots
    resid_lim = 1.2 * old.abs_sky_error.max()

    # Save pointing model to file
    outfile = file(opts.outfilebase + '.csv', 'w')
    # The original pointing model description string was comma-separated
    outfile.write(new_model.description.replace(" ", ", "))
    outfile.close()
    print("Saved %d-parameter pointing model to '%s'" % (len(new_model), opts.outfilebase + '.csv'))
    # Turn data recarray into list of dicts and add residuals to the mix
    extended_data = []
    for n in range(len(data)):
        rec_dict = dict(list(zip(data.dtype.names, data[n])))
        rec_dict['keep'] = int(keep[n])
        rec_dict['old_residual_xel'] = rad2deg(old.residual_xel[n])
        rec_dict['old_residual_el'] = rad2deg(old.residual_el[n])
        rec_dict['new_residual_xel'] = rad2deg(new.residual_xel[n])
        rec_dict['new_residual_el'] = rad2deg(new.residual_el[n])
        extended_data.append(rec_dict)
    # Format the data similar to analyse_point_source_scans output CSV file, with four new columns at the end
    fields = '%(dataset)s, %(target)s, %(timestamp_ut)s, %(azimuth).7f, %(elevation).7f, ' \
             '%(delta_azimuth).7f, %(delta_azimuth_std).7f, %(delta_elevation).7f, %(delta_elevation_std).7f, ' \
             '%(data_unit)s, %(beam_height_I).7f, %(beam_height_I_std).7f, %(beam_width_I).7f, ' \
             '%(beam_width_I_std).7f, %(baseline_height_I).7f, %(baseline_height_I_std).7f, %(refined_I).0f, ' \
             '%(beam_height_HH).7f, %(beam_width_HH).7f, %(baseline_height_HH).7f, %(refined_HH).0f, ' \
             '%(beam_height_VV).7f, %(beam_width_VV).7f, %(baseline_height_VV).7f, %(refined_VV).0f, ' \
             '%(frequency).7f, %(flux).4f, %(temperature).2f, %(pressure).2f, %(humidity).2f, %(wind_speed).2f, ' \
             '%(keep)d, %(old_residual_xel).7f, %(old_residual_el).7f, %(new_residual_xel).7f, %(new_residual_el).7f\n'
    field_names = [name.partition(')')[0] for name in fields[2:].split(', %(')]
    # Save residual data and flags to file
    outfile2 = file(opts.outfilebase + '_data.csv', 'w')
    outfile2.write('# antenna = %s\n' % antenna.description)
    outfile2.write(', '.join(field_names) + '\n')
    outfile2.writelines([fields % rec for rec in extended_data])
    outfile2.close()

    # Pointing model report
    print('Generating report, this may take a few minutes...')
    nice_filename =  os.path.splitext(os.path.basename(filename))[0]+ '_pointing_model'
    pp = PdfPages(nice_filename+'.pdf')

    pagetext = []
    i = 0
    tmpstr = ""
    linelength = 5
    pagetext.append("List of targets used:")
    for tar in list(set(unique_targets)):
        if  i % linelength == linelength-1 :
            pagetext.append(tmpstr)
            tmpstr = ""
        i = i + 1
        tmpstr +='%s, '%(tar)
    pagetext.append(tmpstr)
    pagetext.append("Pointing metrics for fitted points. (N= %i  Fitting Data Points) "%(np.sum(keep)))
    pagetext.append("New Pointing model:")
    pagetext.append(new_model.description.replace(" ", ", "))
    pagetext.append("All sky RMS = %.3f' (robust %.3f') " % (new.sky_rms, new.robust_sky_rms))
    fig = plt.figure(None,figsize = (10,16))
    plt.figtext(0.1,0.1,'\n'.join(pagetext),fontsize=12)
    fig.savefig(pp,format='pdf')
    plt.close(fig)


    # List of colors used to represent different targets in scatter plots
    scatter_colors = ('b', 'r', 'g', 'k', 'c', 'm', 'y')
    target_colors = np.tile(scatter_colors, 1 + len(unique_targets) // len(scatter_colors))[:len(unique_targets)]
    # Quantity loosely related to the declination of the source
    north = (np.pi / 2. - el) / (np.pi / 2.) * np.cos(az)
    pseudo_dec = -np.ones(len(unique_targets))
    for n, ind in enumerate(target_index):
        if north[n] > pseudo_dec[ind]:
            pseudo_dec[ind] = north[n]
    north_to_south = np.flipud(np.argsort(pseudo_dec))
    target_colors = target_colors[north_to_south][target_index]

    for idx in np.unique(target_index):
        fig = plt.figure(1, figsize=(15, 10))
        fig.clear()

        # Store highlighted target index on figure object
        fig.highlighted_target = idx

        # Axes to contain detail residual plots - initialise plots with old residuals
        ax = fig.add_axes([0.27, 0.74, 0.2, 0.2])
        ax.axhline(0, color='k', zorder=0)
        plot_data_and_tooltip(ax, rad2deg(az), rad2deg(old.residual_xel) * 60.)
        ax.axis([-180., 180., -resid_lim, resid_lim])
        ax.set_xticks([])
        ax.yaxis.set_ticks_position('right')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        ax.set_ylabel('Cross-EL offset')
        ax.set_title('RESIDUALS')

        ax = fig.add_axes([0.27, 0.54, 0.2, 0.2])
        ax.axhline(0, color='k', zorder=0)
        plot_data_and_tooltip(ax, rad2deg(az), rad2deg(old.residual_el) * 60.)
        ax.axis([-180., 180., -resid_lim, resid_lim])
        ax.set_xlabel('Azimuth (deg)')
        ax.yaxis.set_ticks_position('right')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        ax.set_ylabel('EL offset')

        ax = fig.add_axes([0.27, 0.26, 0.2, 0.2])
        ax.axhline(0, color='k', zorder=0)
        plot_data_and_tooltip(ax, rad2deg(el), rad2deg(old.residual_xel) * 60.)
        ax.axis([0., 90., -resid_lim, resid_lim])
        ax.set_xticks([])
        ax.yaxis.set_ticks_position('right')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        ax.set_ylabel('Cross-EL offset')

        ax = fig.add_axes([0.27, 0.06, 0.2, 0.2])
        ax.axhline(0, color='k', zorder=0)
        plot_data_and_tooltip(ax, rad2deg(el), rad2deg(old.residual_el) * 60.)
        ax.axis([0., 90., -resid_lim, resid_lim])
        ax.set_xlabel('Elevation (deg)')
        ax.yaxis.set_ticks_position('right')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        ax.set_ylabel('EL offset')

        # Axes to contain quiver plot - plot static measurement locations in ARC projection as a start
        ax = fig.add_axes([0.5, 0.43, 0.5, 0.5], projection='polar')
        plot_data_and_tooltip(ax, np.pi/2. - az, np.pi/2. - el)
        segms = quiver_segments(old.residual_az, old.residual_el, 0.)
        ax.quiv = mpl.collections.LineCollection(segms, color='0.3')
        ax.add_collection(ax.quiv)
        ax.set_xticks(deg2rad(np.arange(0., 360., 90.)))
        ax.set_xticklabels(['E', 'N', 'W', 'S'])
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(angle_formatter))
        ax.set_ylim(0., np.pi / 2.)
        ax.set_yticks(deg2rad(np.arange(0., 90., 10.)))
        ax.set_yticklabels([])

        # Axes to contain before/after residual plot
        ax = fig.add_axes([0.5, 0.135, 0.25, 0.25], projection='polar')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        plot_data_and_tooltip(ax, np.arctan2(old.residual_el, old.residual_xel), old.abs_sky_error)
        ax.set_xticklabels([])
        ax.set_title('OLD')
        fig.text(0.625, 0.09, "$\chi^2$ = %.1f" % (old.chi2,), ha='center', va='baseline')
        fig.text(0.625, 0.06, "all sky rms = %.3f' (robust %.3f')" % (old.sky_rms, old.robust_sky_rms),
                 ha='center', va='baseline')
        old.metrics(target_index == fig.highlighted_target)
        fig.text(0.625, 0.03, "target sky rms = %.3f' (robust %.3f')" % (old.sky_rms, old.robust_sky_rms),
                 ha='center', va='baseline', fontdict=dict(color=(0.25,0,0,1)))
        old.metrics(keep)

        ax = fig.add_axes([0.75, 0.135, 0.25, 0.25], projection='polar')
        ax.yaxis.set_major_formatter(mpl.ticker.FuncFormatter(arcmin_formatter))
        plot_data_and_tooltip(ax, np.arctan2(new.residual_el, new.residual_xel), new.abs_sky_error)
        ax.set_xticklabels([])
        ax.set_title('NEW')
        fig.text(0.875, 0.09, "$\chi^2$ = %.1f" % (new.chi2,), ha='center', va='baseline')
        fig.text(0.875, 0.06, "all sky rms = %.3f' (robust %.3f')" % (new.sky_rms, new.robust_sky_rms),
                 ha='center', va='baseline')
        new.metrics(target_index == fig.highlighted_target)
        fig.text(0.875, 0.03, "target sky rms = %.3f' (robust %.3f')" % (new.sky_rms, new.robust_sky_rms),
                 ha='center', va='baseline', fontdict=dict(color=(0.25,0,0,1)))
        new.metrics(keep)

        param_button_color = ['0.65', '0.0']
        param_button_weight = ['normal', 'bold']
        # For display purposes, throw out unused parameters P2 and P10
        display_params = list(range(num_params))
        display_params.pop(9)
        display_params.pop(1)
        def setup_param_button(p):
            """Set up individual parameter toggle button."""
            param = display_params[p]
            param_button = mpl.widgets.Button(fig.add_axes([0.09, 0.94 - (0.85 + p * 0.9) / len(display_params),
                                                            0.03, 0.85 / len(display_params)]), 'P%d' % (param + 1,))
            fig.text(0.19, 0.94 - (0.5 * 0.85 + p * 0.9) / len(display_params), '', ha='right', va='center')
            fig.text(0.24, 0.94 - (0.5 * 0.85 + p * 0.9) / len(display_params), '', ha='right', va='center')
            state = enabled_params[param]
            param_button.label.set_color(param_button_color[state])
            param_button.label.set_weight(param_button_weight[state])
            def toggle_param_callback(event):
                state = not enabled_params[param]
                enabled_params[param] = state
                param_button.label.set_color(param_button_color[state])
                param_button.label.set_weight(param_button_weight[state])
                save_button.color = (0.85, 0, 0)
                save_button.hovercolor = (0.95, 0, 0)
                update(fig)
            param_button.on_clicked(toggle_param_callback)
            return param_button # This is to stop the gc from deleting the data
        param_buttons = [setup_param_button(p) for p in range(len(display_params))] 

        # Add old pointing model and labels
        list_o_names = 'Ant:%s , Datasets:'%(antenna.name) + ' ,'.join(np.unique(data['dataset']).tolist() )
        fig.text(0.905, 0.98,list_o_names, horizontalalignment='right',fontsize=10)
        fig.text(0.053, 0.95, 'OLD', ha='center', va='bottom', size='large')
        fig.text(0.105, 0.95, 'MODEL', ha='center', va='bottom', size='large')
        fig.text(0.16, 0.95, 'NEW', ha='center', va='bottom', size='large')
        fig.text(0.225, 0.95, 'STD', ha='center', va='bottom', size='large')
        for p, param in enumerate(display_params):
            param_str = param_to_str(old_model, param) if list(old_model.values())[param] else ''
            fig.text(0.085, 0.94 - (0.5 * 0.85 + p * 0.9) / len(display_params), param_str, ha='right', va='center')

        # Create target selector buttons and related text (title + target string)
        fig.text(0.565, 0.95, 'TARGET', ha='center', va='bottom', size='large')
        fig.text(0.565, 0.89, unique_targets[fig.highlighted_target], ha='center', va='top', fontdict=dict(color=(0.25,0,0,1)))

        quiver_scale = 0.1 * 10 * np.pi / 6 / deg2rad(old.robust_sky_rms / 60.)
        fig.axes[4].quiv.set_segments(quiver_segments(new.residual_az, new.residual_el, quiver_scale))

        # Target state: 0 = flagged, 1 = unflagged, 2 = highlighted
        target_state = keep * ((target_index == fig.highlighted_target) + 1)
        # Specify colours of flagged, unflagged and highlighted dots, respectively, as RGBA tuples
        dot_colors = np.choose(target_state, np.atleast_3d(np.vstack([(1,1,1,1), (0,0,1,1), (1,0,0,1)]))).T
        for ax in fig.axes[:7]:
            ax.dots.set_facecolors(dot_colors)

        fig.texts[-1].set_text(unique_targets[fig.highlighted_target])
        for p, param in enumerate(display_params):
            fig.texts[2*p + 6].set_text(param_to_str(new_model, param) if enabled_params[param] else '')
            # HACK to convert sigmas to arcminutes, but not for P9 and P12 (which are scale factors)
            # This functionality should really reside inside the PointingModel class
            std_param = rad2deg(sigma_params[param]) * 60. if param not in [8, 11] else sigma_params[param]
            std_param_str = ("%.2f'" % std_param) if param not in [8, 11] else ("%.0e" % std_param)
            fig.texts[2*p + 7].set_text(std_param_str if enabled_params[param] and opts.use_stats else '')
            # Turn parameter string bold if it changed significantly from old value
            if np.abs(params[param] - list(old_model.values())[param]) > 3.0 * sigma_params[param]:
                fig.texts[2*p + 6].set_weight('bold')
                fig.texts[2*p + 7].set_weight('bold')
            else:
                fig.texts[2*p + 6].set_weight('normal')
                fig.texts[2*p + 7].set_weight('normal')
        daz_az, del_az, daz_el, del_el, quiver, before, after = fig.axes[:7]
        # Update residual plots
        daz_az.dots.set_offsets(np.c_[rad2deg(az), rad2deg(new.residual_xel) * 60.])
        del_az.dots.set_offsets(np.c_[rad2deg(az), rad2deg(new.residual_el) * 60.])
        daz_el.dots.set_offsets(np.c_[rad2deg(el), rad2deg(new.residual_xel) * 60.])
        del_el.dots.set_offsets(np.c_[rad2deg(el), rad2deg(new.residual_el) * 60.])
        after.dots.set_offsets(np.c_[np.arctan2(new.residual_el, new.residual_xel), new.abs_sky_error])
        resid_lim = 1.2 * max(new.abs_sky_error.max(), old.abs_sky_error.max())
        daz_az.set_ylim(-resid_lim, resid_lim)
        del_az.set_ylim(-resid_lim, resid_lim)
        daz_el.set_ylim(-resid_lim, resid_lim)
        del_el.set_ylim(-resid_lim, resid_lim)
        before.set_ylim(0, resid_lim)
        after.set_ylim(0, resid_lim)

        fig.savefig(pp,format='pdf')
#     plt.close(fig)

    pp.close()
Пример #8
0
def angle_formatter(x, pos=None):
    return theta_formatter(wrap_angle(np.pi / 2.0 - x), pos)
Пример #9
0
def angle_formatter(x, pos=None):
    return theta_formatter(wrap_angle(np.pi / 2.0 - x), pos)
Пример #10
0
# Interpret first non-comment line as header
fields = data[0].tolist()
# By default, all fields are assumed to contain floats
formats = np.tile(np.float, len(fields))
# The string_fields are assumed to contain strings - use data's string type, as it is of sufficient length
formats[[fields.index(name) for name in string_fields if name in fields]] = data.dtype
# Convert to heterogeneous record array
data = np.rec.fromarrays(data[1:].transpose(), dtype=zip(fields, formats))
# Load antenna description string from first line of file and construct antenna object from it
antenna = katpoint.Antenna(file(filename).readline().strip().partition('=')[2])
# Use the pointing model contained in antenna object as the old model (if not overridden by file)
# If the antenna has no model specified, a default null model will be used
if old_model is None:
    old_model = antenna.pointing_model
# Obtain desired fields and convert to radians
az, el = wrap_angle(deg2rad(data['azimuth'])), deg2rad(data['elevation'])
measured_delta_az, measured_delta_el = deg2rad(data['delta_azimuth']), deg2rad(data['delta_elevation'])
# Uncertainties are optional
min_std = deg2rad(opts.min_rms / 60. / np.sqrt(2))
std_delta_az = np.clip(deg2rad(data['delta_azimuth_std']), min_std, np.inf) \
               if 'delta_azimuth_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(az))
std_delta_el = np.clip(deg2rad(data['delta_elevation_std']), min_std, np.inf) \
               if 'delta_elevation_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(el))
targets = data['target']
keep = data['keep'].astype(np.bool) if 'keep' in data.dtype.fields else np.tile(True, len(targets))

# Initialise new pointing model and set default enabled parameters
new_model = katpoint.PointingModel()
num_params = len(new_model)
default_enabled = np.nonzero(old_model.values())[0]
# If the old model is empty / null, select the most basic set of parameters for starters
# By default, all fields are assumed to contain floats
formats = np.tile(np.float, len(fields))
# The string_fields are assumed to contain strings - use data's string type, as it is of sufficient length
formats[[fields.index(name) for name in string_fields
         if name in fields]] = data.dtype
# Convert to heterogeneous record array
data = np.rec.fromarrays(data[1:].transpose(),
                         dtype=list(zip(fields, formats)))
# Load antenna description string from first line of file and construct antenna object from it
antenna = katpoint.Antenna(open(filename).readline().strip().partition('=')[2])
# Use the pointing model contained in antenna object as the old model (if not overridden by file)
# If the antenna has no model specified, a default null model will be used
if old_model is None:
    old_model = antenna.pointing_model
# Obtain desired fields and convert to radians
az, el = wrap_angle(deg2rad(data['azimuth'])), deg2rad(data['elevation'])
measured_delta_az, measured_delta_el = deg2rad(data['delta_azimuth']), deg2rad(
    data['delta_elevation'])
# Uncertainties are optional
min_std = deg2rad(opts.min_rms / 60. / np.sqrt(2))
std_delta_az = np.clip(deg2rad(data['delta_azimuth_std']), min_std, np.inf) \
               if 'delta_azimuth_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(az))
std_delta_el = np.clip(deg2rad(data['delta_elevation_std']), min_std, np.inf) \
               if 'delta_elevation_std' in data.dtype.fields and opts.use_stats else np.tile(min_std, len(el))
targets = data['target']
keep = data['keep'].astype(
    np.bool) if 'keep' in data.dtype.fields else np.tile(True, len(targets))

# Initialise new pointing model and set default enabled parameters
new_model = katpoint.PointingModel()
num_params = len(new_model)