예제 #1
0
def above_horizon(target, observer, horizon=20.0, duration=0.0):
    """Check target visibility.
       Utility function to calculate ephem horizontal coordinates
    """

    # use local copies so you do not overwrite target time attribute
    horizon = ephem.degrees(str(horizon))

    # must be celestial target (ra, dec)
    # check that target is visible at start of track
    start_ = timestamp2datetime(time.time())
    [azim, elev] = _horizontal_coordinates(target, observer, start_)
    user_logger.trace("TRACE: target at start (az, el)= ({}, {})".format(
        azim, elev))
    if not elev > horizon:
        return False

    # check that target will be visible at end of track
    if duration:
        end_ = timestamp2datetime(time.time() + duration)
        [azim, elev] = _horizontal_coordinates(target, observer, end_)
        user_logger.trace("TRACE: target at end (az, el)= ({}, {})".format(
            azim, elev))
        return elev > horizon

    return True
예제 #2
0
def above_horizon(target, observer, horizon=20.0, duration=0.0):
    """Check target visibility."""
    # use local copies so you do not overwrite target time attribute
    horizon = ephem.degrees(str(horizon))

    if type(target) is not ephem.FixedBody:
        # anticipate katpoint special target for AzEl targets
        if 'alt' not in vars(target):
            raise RuntimeError('Unknown target type, exiting...')
        # 'StationaryBody' objects do not have RaDec coordinates
        # check pointing altitude is above minimum elevation limit
        return bool(target.alt >= horizon)

    # must be celestial target (ra, dec)
    # check that target is visible at start of track
    start_ = timestamp2datetime(time.time())
    [azim, elev] = __horizontal_coordinates__(target, observer, start_)
    user_logger.trace("TRACE: target at start (az, el)= ({}, {})".format(
        azim, elev))
    if not elev > horizon:
        return False

    # check that target will be visible at end of track
    if duration:
        end_ = timestamp2datetime(time.time() + duration)
        [azim, elev] = __horizontal_coordinates__(target, observer, end_)
        user_logger.trace("TRACE: target at end (az, el)= ({}, {})".format(
            azim, elev))
        return elev > horizon

    return True
예제 #3
0
    def test_time_conversion_symmetry(self):
        """Test katpoint and astrokat time conversion methods match and are symmetrical"""
        test_files = [
            "test_obs/below-horizon-sim.yaml",
            "test_obs/image-cals-sim.yaml",
            "test_obs/image-sim.yaml",
            "test_obs/image-single-sim.yaml",
            "test_obs/targets-sim.yaml",
            "test_obs/two-calib-sim.yaml",
        ]
        for test_file in test_files:
            file_path = yaml_path(test_file)
            yaml_start_time = extract_start_time(file_path)
            yaml_start_time_str = str(yaml_start_time)

            astrokat_sec_since_epoch = utility.datetime2timestamp(
                yaml_start_time)
            katpoint_sec_since_epoch = katpoint.Timestamp(
                yaml_start_time_str).secs
            self.assertAlmostEqual(
                astrokat_sec_since_epoch,
                katpoint_sec_since_epoch,
                places=6,
                msg="timestamp conversion mismatch {}".format(test_file))

            astrokat_datetime = utility.timestamp2datetime(
                astrokat_sec_since_epoch)
            katpoint_timestamp = katpoint.Timestamp(katpoint_sec_since_epoch)
            self.assertEqual(
                str(astrokat_datetime),
                yaml_start_time_str,
                msg="astrokat str time conversion mismatch for {}".format(
                    test_file))
            self.assertEqual(
                str(katpoint_timestamp),
                yaml_start_time_str,
                msg="katpoint str time conversion mismatch for {}".format(
                    test_file))
예제 #4
0
def main(args):
    """Run the observation.

    Run the observation script read arguments from yaml file

    """
    (opts, args) = astrokat.cli(
        os.path.basename(__file__),
        # remove redundant KAT-7 options
        long_opts_to_remove=[
            "--mode",
            "--dbe-centre-freq",
            "--no-mask",
            "--horizon",
            "--centre-freq",
        ],
        args=args,
    )

    # suppress the sessions noise diode, which is outdated
    # will use it again once functionality corrected
    # TODO: Currently the fire_noise_diode function in mkat_session.py is
    # outdated. This has to be updated to reflect the new noise diode pattern
    # implementation, and this default setting then removed.
    opts.nd_params = "off"

    # astrokat does not support observational command line parameters
    # all observational parameters must be in the YAML file
    # command line arguments will be dropped to unknown arguments
    unknown_args = [arg for arg in args if arg.startswith("--")]
    if any("horizon" in arg for arg in unknown_args):
        raise RuntimeError(
            "Command line option {} not supported. "
            "Please specify parameters in YAML file.".format("horizon"))
    # TODO: add correlator settings YAML option for config file

    # unpack observation from observation plan
    if opts.yaml:
        opts.obs_plan_params = read_yaml(opts.yaml)

    # ensure sessions has the YAML horizon value if given
    if "horizon" in opts.obs_plan_params:
        opts.horizon = opts.obs_plan_params["horizon"]
    else:
        opts.horizon = 20.0  # deg above horizon default

    # set log level
    if opts.debug:
        user_logger.setLevel(logging.DEBUG)
    if opts.trace:
        user_logger.setLevel(logging.TRACE)

    # process the flat list of targets into a structure with sources
    # convert celestial targets coordinates to all be equatorial (ra,dec)
    # horizontal coordinates (alt, az)
    #  for scans, the coordinates will be converted to enable delay tracking
    #  for tracks the coordinates will be left as is with no delay tracking
    # planetary bodies are passed through to katpoint Target as is
    # elliptical solar bodies such as comets are also passed through as katpoint Targets
    for obs_dict in opts.obs_plan_params['observation_loop']:
        start_ts = timestamp2datetime(time.time())
        if "durations" in opts.obs_plan_params:
            obs_time_info = opts.obs_plan_params["durations"]
            if "start_time" in obs_time_info:
                start_ts = obs_time_info["start_time"]
        mkat = astrokat.Observatory(datetime=start_ts)
        obs_targets = targets.read(obs_dict["target_list"],
                                   observer=mkat.observer)
        obs_dict['target_list'] = obs_targets

    # setup and observation
    with Telescope(opts) as kat:
        run_observation(opts, kat)
예제 #5
0
def run_observation(opts, kat):
    """Extract control and observation information provided in observation file."""
    obs_plan_params = opts.obs_plan_params
    # remove observation specific instructions housed in YAML file
    del opts.obs_plan_params

    # set up duration periods for observation control
    obs_duration = -1
    if "durations" in obs_plan_params:
        if "obs_duration" in obs_plan_params["durations"]:
            obs_duration = obs_plan_params["durations"]["obs_duration"]
    # check for nonsensical observation duration setting
    if abs(obs_duration) < 1e-5:
        user_logger.error(
            "Unexpected value: obs_duration: {}".format(obs_duration))
        return

    # TODO: the description requirement in sessions should be re-evaluated
    # since the schedule block has the description
    # Description argument in instruction_set should be retired, but is
    # needed by sessions
    # Assign proposal_description if available, else create a dummy
    if "description" not in vars(opts):
        session_opts = vars(opts)
        description = "Observation run"
        if "proposal_description" in vars(opts):
            description = opts.proposal_description
        session_opts["description"] = description

    nr_obs_loops = len(obs_plan_params["observation_loop"])
    with start_session(kat.array, **vars(opts)) as session:
        session.standard_setup(**vars(opts))

        # Each observation loop contains a number of observation cycles over LST ranges
        # For a single observation loop, only a start LST and duration is required
        # Target observation loop
        observation_timer = time.time()
        for obs_cntr, observation_cycle in enumerate(
                obs_plan_params["observation_loop"]):
            if nr_obs_loops > 1:
                user_logger.info("Observation loop {} of {}.".format(
                    obs_cntr + 1, nr_obs_loops))
                user_logger.info("Loop LST range {}.".format(
                    observation_cycle["LST"]))
            # Unpack all target information
            if not ("target_list" in observation_cycle.keys()):
                user_logger.error(
                    "No targets provided - stopping script instead of hanging around"
                )
                continue
            obs_targets = observation_cycle["target_list"]
            target_list = obs_targets["target"].tolist()
            # build katpoint catalogues for tidy handling of targets
            catalogue = collect_targets(kat.array, target_list)
            obs_tags = []
            for tgt in obs_targets:
                # catalogue names are no longer unique
                name = tgt["name"]
                # add tag evaluation to identify catalogue targets
                tags = tgt["target"].split(",")[1].strip()
                for cat_tgt in catalogue:
                    if name == cat_tgt.name:
                        if ("special" in cat_tgt.tags
                                or "xephem" in cat_tgt.tags
                                or tags == " ".join(cat_tgt.tags)):
                            tgt["target"] = cat_tgt
                            obs_tags.extend(cat_tgt.tags)
                            break
            obs_tags = list(set(obs_tags))
            cal_tags = [tag for tag in obs_tags if tag[-3:] == "cal"]

            # observer object handle to track the observation timing in a more user
            # friendly way
            #             observer = catalogue._antenna.observer
            ref_antenna = catalogue.antenna
            observer = ref_antenna.observer
            start_datetime = timestamp2datetime(time.time())
            observer.date = ephem.Date(start_datetime)
            user_logger.trace("TRACE: requested start time "
                              "({}) {}".format(
                                  datetime2timestamp(start_datetime),
                                  start_datetime))
            user_logger.trace("TRACE: observer at start\n {}".format(observer))

            # Only observe targets in valid LST range
            if nr_obs_loops > 1 and obs_cntr < nr_obs_loops - 1:
                [start_lst, end_lst] = get_lst(observation_cycle["LST"],
                                               multi_loop=True)
                if end_lst is None:
                    # for multi loop the end lst is required
                    raise RuntimeError(
                        'Multi-loop observations require end LST times')
                next_obs_plan = obs_plan_params["observation_loop"][obs_cntr +
                                                                    1]
                [next_start_lst, next_end_lst] = get_lst(next_obs_plan["LST"])
                user_logger.trace("TRACE: current LST range {}-{}".format(
                    ephem.hours(str(start_lst)), ephem.hours(str(end_lst))))
                user_logger.trace("TRACE: next LST range {}-{}".format(
                    ephem.hours(str(next_start_lst)),
                    ephem.hours(str(next_end_lst))))
            else:
                next_start_lst = None
                next_end_lst = None
                [start_lst, end_lst] = get_lst(observation_cycle["LST"])

            # Verify the observation is in a valid LST range
            # and that it is worth while continuing with the observation
            # Do not use float() values, ephem.hours does not convert as
            # expected
            local_lst = observer.sidereal_time()
            user_logger.trace("TRACE: Local LST {}".format(
                ephem.hours(local_lst)))
            # Only observe targets in current LST range
            log_msg = "Local LST outside LST range {}-{}".format(
                ephem.hours(str(start_lst)), ephem.hours(str(end_lst)))
            if float(start_lst) < end_lst:
                # lst ends before midnight
                if not _same_day(start_lst, end_lst, local_lst):
                    if obs_cntr < nr_obs_loops - 1:
                        user_logger.info(log_msg)
                    else:
                        user_logger.error(log_msg)
                    continue
            else:
                # lst ends after midnight
                if _next_day(start_lst, end_lst, local_lst):
                    if obs_cntr < nr_obs_loops - 1:
                        user_logger.info(log_msg)
                    else:
                        user_logger.error(log_msg)
                    continue

            # Verify that it is worth while continuing with the observation
            # The filter functions uses the current time as timestamps
            # and thus incorrectly set the simulation timestamp
            if not kat.array.dry_run:
                # Quit early if there are no sources to observe
                if len(catalogue.filter(el_limit_deg=opts.horizon)) == 0:
                    raise NoTargetsUpError(
                        "No targets are currently visible - "
                        "please re-run the script later")
                # Quit early if the observation requires all targets to be visible
                if opts.all_up and (len(
                        catalogue.filter(el_limit_deg=opts.horizon)) !=
                                    len(catalogue)):
                    raise NotAllTargetsUpError(
                        "Not all targets are currently visible - please re-run the script"
                        "with --visibility for information")

            # List sources and their associated functions from observation tags
            not_cals_filter_list = []
            for cal_type in cal_tags:
                not_cals_filter_list.append("~{}".format(cal_type))
                cal_array = [cal.name for cal in catalogue.filter(cal_type)]
                if len(cal_array) < 1:
                    continue  # do not display empty tags
                user_logger.info("{} calibrators are {}".format(
                    str.upper(cal_type[:-3]), cal_array))
            user_logger.info("Observation targets are [{}]".format(", ".join([
                repr(target.name)
                for target in catalogue.filter(not_cals_filter_list)
            ])))

            # TODO: setup of noise diode pattern should be moved to sessions
            #  so it happens in the line above
            if "noise_diode" in obs_plan_params:
                nd_setup = obs_plan_params["noise_diode"]
                nd_lead = nd_setup.get('lead_time')

                # Set noise diode period to multiple of correlator integration time.
                if not kat.array.dry_run:
                    cbf_corr = session.cbf.correlator
                    dump_period = cbf_corr.sensor.int_time.get_value()
                else:
                    dump_period = 0.5  # sec
                user_logger.debug(
                    'DEBUG: Correlator integration time {} [sec]'.format(
                        dump_period))

                if "cycle_len" in nd_setup:
                    if (nd_setup['cycle_len'] >= dump_period):
                        cycle_len_frac = nd_setup['cycle_len'] // dump_period
                        nd_setup['cycle_len'] = cycle_len_frac * dump_period
                        msg = ('Set noise diode period '
                               'to multiple of correlator dump period: '
                               'cycle length = {} [sec]'.format(
                                   nd_setup['cycle_len']))
                    else:
                        msg = ('Requested cycle length {}s '
                               '< correlator dump period {}s, '
                               'ND not synchronised with dump edge'.format(
                                   nd_setup['cycle_len'], dump_period))
                    user_logger.warning(msg)
                    noisediode.pattern(
                        kat.array,
                        nd_setup,
                        lead_time=nd_lead,
                    )

            # Adding explicit init after "Capture-init failed" exception was
            # encountered
            session.capture_init()
            user_logger.debug("DEBUG: Initialise capture start with timestamp "
                              "{} ({})".format(int(time.time()),
                                               timestamp2datetime(
                                                   time.time())))

            # Go to first target before starting capture
            user_logger.info("Slewing to first target")
            observe(session, ref_antenna, obs_targets[0], slewonly=True)
            # Only start capturing once we are on target
            session.capture_start()
            user_logger.trace("TRACE: capture start time after slew "
                              "({}) {}".format(time.time(),
                                               timestamp2datetime(
                                                   time.time())))
            user_logger.trace(
                "TRACE: observer after slew\n {}".format(observer))

            done = False
            sanity_cntr = 0
            while not done:
                # small errors can cause an infinite loop here
                # preventing infinite loops
                sanity_cntr += 1
                if sanity_cntr > 100000:
                    user_logger.error("While limit counter has reached {}, "
                                      "exiting".format(sanity_cntr))
                    break

                # Cycle through target list in order listed
                targets_visible = False
                time_remaining = obs_duration
                observation_timer = time.time()
                for tgt_cntr, target in enumerate(obs_targets):
                    katpt_target = target["target"]
                    user_logger.debug("DEBUG: {} {}".format(tgt_cntr, target))
                    user_logger.trace(
                        "TRACE: initial observer for target\n {}".format(
                            observer))
                    # check target visible before doing anything
                    # make sure the target would be visible for the entire duration
                    target_duration = target['duration']
                    visible = True
                    if type(katpt_target.body) is ephem.FixedBody:
                        visible = above_horizon(
                            target=katpt_target.body.copy(),
                            observer=observer.copy(),
                            horizon=opts.horizon,
                            duration=target_duration)
                    if not visible:
                        show_horizon_status = True
                        # warning for cadence targets only when they are due
                        if (target["cadence"] > 0
                                and target["last_observed"] is not None):
                            delta_time = time.time() - target["last_observed"]
                            show_horizon_status = delta_time >= target[
                                "cadence"]
                        if show_horizon_status:
                            user_logger.warn("Target {} below {} deg horizon, "
                                             "continuing".format(
                                                 target["name"], opts.horizon))
                        continue
                    user_logger.trace(
                        "TRACE: observer after horizon check\n {}".format(
                            observer))

                    # check and observe all targets with cadences
                    while_cntr = 0
                    cadence_targets = list(obs_targets)
                    while True:
                        tgt = cadence_target(cadence_targets)
                        if not tgt:
                            break
                        # check enough time remaining to continue
                        if obs_duration > 0 and time_remaining < tgt[
                                "duration"]:
                            done = True
                            break
                        # check target visible before doing anything
                        user_logger.trace("TRACE: cadence"
                                          "target\n{}\n {}".format(
                                              tgt, catalogue[tgt["name"]]))
                        user_logger.trace("TRACE: initial observer for cadence"
                                          "target\n {}".format(observer))
                        user_logger.trace(
                            "TRACE: observer before track\n {}".format(
                                observer))
                        user_logger.trace(
                            "TRACE: target observation # {} last observed "
                            "{}".format(tgt["obs_cntr"], tgt["last_observed"]))
                        cat_target = catalogue[tgt["name"]]
                        if above_horizon(
                                target=cat_target.body,
                                observer=cat_target.antenna.observer.copy(),
                                horizon=opts.horizon,
                                duration=tgt["duration"]):
                            if observe(session, ref_antenna, tgt,
                                       **obs_plan_params):
                                targets_visible += True
                                tgt["obs_cntr"] += 1
                                tgt["last_observed"] = time.time()
                            else:
                                # target not visibile to sessions anymore
                                cadence_targets.remove(tgt)
                            user_logger.trace(
                                "TRACE: observer after track\n {}".format(
                                    observer))
                            user_logger.trace(
                                "TRACE: target observation # {} last observed "
                                "{}".format(tgt["obs_cntr"],
                                            tgt["last_observed"]))
                        else:
                            cadence_targets.remove(tgt)
                        while_cntr += 1
                        if while_cntr > len(obs_targets):
                            break
                    if done:
                        break
                    user_logger.trace(
                        "TRACE: observer after cadence\n {}".format(observer))

                    # observe non cadence target
                    if target["cadence"] < 0:
                        user_logger.trace(
                            "TRACE: normal target\n {}".format(target))
                        user_logger.trace(
                            "TRACE: observer before track\n {}".format(
                                observer))
                        user_logger.trace("TRACE: ts before observe {}".format(
                            time.time()))
                        user_logger.trace("TRACE: target last "
                                          "observed {}".format(
                                              target["last_observed"]))

                        targets_visible += observe(session, ref_antenna,
                                                   target, **obs_plan_params)
                        user_logger.trace(
                            "TRACE: observer after track\n {}".format(
                                observer))
                        user_logger.trace("TRACE: ts after observe {}".format(
                            time.time()))
                        if targets_visible:
                            target["obs_cntr"] += 1
                            target["last_observed"] = time.time()
                        user_logger.trace(
                            "TRACE: target observation # {} last observed "
                            "{}".format(target["obs_cntr"],
                                        target["last_observed"]))
                        user_logger.trace(
                            "TRACE: observer after track\n {}".format(
                                observer))

                    # loop continuation checks
                    delta_time = time.time() - session.start_time
                    user_logger.trace(
                        "TRACE: time elapsed {} sec".format(delta_time))
                    user_logger.trace(
                        "TRACE: total obs duration {} sec".format(
                            obs_duration))
                    if obs_duration > 0:
                        time_remaining = obs_duration - delta_time
                        user_logger.trace(
                            "TRACE: time remaining {} sec".format(
                                time_remaining))

                        next_target = obs_targets[(tgt_cntr + 1) %
                                                  len(obs_targets)]
                        user_logger.trace("TRACE: next target before cadence "
                                          "check:\n{}".format(next_target))
                        # check if there is a cadence target that must be run
                        # instead of next target
                        for next_cadence_tgt_idx in range(
                                tgt_cntr + 1, len(obs_targets)):
                            next_cadence_target = obs_targets[
                                next_cadence_tgt_idx % len(obs_targets)]
                            if next_cadence_target["cadence"] > 0:
                                user_logger.trace(
                                    "TRACE: time needed for next obs "
                                    "{} sec".format(
                                        next_cadence_target["cadence"]))
                                next_target = obs_targets[next_cadence_tgt_idx
                                                          % len(obs_targets)]
                                continue
                        user_logger.trace("TRACE: next target after cadence "
                                          "check:\n{}".format(next_target))
                        user_logger.trace("TRACE: time needed for next obs "
                                          "{} sec".format(
                                              next_target["duration"]))
                        if (time_remaining < 1.0
                                or time_remaining < next_target["duration"]):
                            user_logger.info(
                                "Scheduled observation time lapsed - ending observation"
                            )
                            done = True
                            break

                # during dry-run when sessions exit time is reset so will be incorrect
                # outside the loop
                observation_timer = time.time()

                if obs_duration < 0:
                    user_logger.info(
                        "Observation list completed - ending observation")
                    done = True

                # for multiple loop, check start lst of next loop
                if next_start_lst is not None:
                    check_local_lst = observer.sidereal_time()
                    if (check_local_lst > next_start_lst) or (not _next_day(
                            next_start_lst, next_end_lst, check_local_lst)):
                        user_logger.info("Moving to next LST loop")
                        done = True

                # End if there is nothing to do
                if not targets_visible:
                    user_logger.warning(
                        "No more targets to observe - stopping script "
                        "instead of hanging around")
                    done = True

    user_logger.trace("TRACE: observer at end\n {}".format(observer))
    # display observation cycle statistics
    # currently only available for single LST range observations
    if nr_obs_loops < 2:
        print
        user_logger.info("Observation loop statistics")
        total_obs_time = observation_timer - session.start_time
        if obs_duration < 0:
            user_logger.info("Single run through observation target list")
        else:
            user_logger.info("Desired observation time {:.2f} sec "
                             "({:.2f} min)".format(obs_duration,
                                                   obs_duration / 60.0))
        user_logger.info("Total observation time {:.2f} sec "
                         "({:.2f} min)".format(total_obs_time,
                                               total_obs_time / 60.0))
        if len(obs_targets) > 0:
            user_logger.info("Targets observed :")
            for unique_target in np.unique(obs_targets["name"]):
                cntrs = obs_targets[obs_targets["name"] ==
                                    unique_target]["obs_cntr"]
                durations = obs_targets[obs_targets["name"] ==
                                        unique_target]["duration"]
                if np.isnan(durations).any():
                    user_logger.info("{} observed {} times".format(
                        unique_target, np.sum(cntrs)))
                else:
                    user_logger.info("{} observed for {} sec".format(
                        unique_target, np.sum(cntrs * durations)))
        print
예제 #6
0
def source_elevation(catalogue, ref_antenna):
    """
        Generates a plot of elevation over time for 24 hour period
        for all sources in provided catalogue at a specific location

        @param catalogue: katpoint.Catalogue object
        @param ref_antenna: katpoint.Antenna object

        @return: matplotlib figure handle
    """
    catalogue.antenna = ref_antenna
    horizon = numpy.degrees(ref_antenna.observer.horizon)
    # All times and timestamps assumed UTC, no special conversion to
    # accommodate SAST allowed to prevent confusion
    creation_date = catalogue.antenna.observer.date
    creation_timestamp = datetime2timestamp(creation_date.datetime())
    time_range = creation_timestamp + numpy.arange(0, 24. * 60. * 60., 360.)
    timestamps = [timestamp2datetime(ts) for ts in time_range]

    fig = plt.figure(figsize=(15, 7), facecolor='white')
    ax = plt.subplot(111)
    plt.subplots_adjust(right=0.8)
    fontP = FontProperties()
    fontP.set_size('small')

    for cnt, target in enumerate(catalogue.targets):
        elev = []
        for idx, timestamp in enumerate(timestamps):
            catalogue.antenna.observer.date = ephem.Date(timestamp)
            target.body.compute(catalogue.antenna.observer)
            elev.append(numpy.degrees(target.body.alt))

        label = '{} '.format(target.name)
        target.tags.remove('radec')
        target.tags.remove('target') if 'target' in target.tags else None
        label += ', '.join(target.tags)

        myplot, = plt.plot_date(timestamps,
                                elev,
                                fmt='.',
                                linewidth=0,
                                label=label)
    ax.axhspan(15, horizon, facecolor='k', alpha=0.1)
    plt.grid()
    plt.legend(loc='center left',
               bbox_to_anchor=(1, 0.5),
               prop={'size': 10},
               numpoints=1)
    plt.ylabel('Elevation (deg)')
    plt.ylim(15, 90)
    plt.yticks(fontsize=10)

    # fix tick positions for proper time axis display
    utc_hrs = [timestamps[0] + timedelta(hours=hr) for hr in range(0, 25, 1)]
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax.set_xlim(utc_hrs[0], utc_hrs[-1])
    ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(24),
                                                  interval=1))
    locs = ax.get_xticks()
    locs_labels = matplotlib.dates.num2date(locs)
    locator = matplotlib.ticker.FixedLocator(locs)
    ax.xaxis.set_major_locator(locator)
    ax.margins(x=0)
    utc_timestamps = [locs_lbl.strftime('%H:%M') for locs_lbl in locs_labels]

    lst_timestamps = []
    for locs_ts in locs_labels:
        catalogue.antenna.observer.date = ephem.Date(locs_ts)
        lst_time = '{}'.format(catalogue.antenna.observer.sidereal_time())
        lst_time_str = datetime.strptime(lst_time,
                                         '%H:%M:%S.%f').strftime('%H:%M')
        lst_timestamps.append(lst_time_str)

    ax.set_xticklabels(lst_timestamps, rotation=30, fontsize=10)
    ax.set_xlabel('Local Sidereal Time')

    ax2 = ax.twiny()
    box = ax2.get_position()
    ax2.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax2.set_xlim(ax.get_xlim())
    ax2.set_xticks(ax.get_xticks())
    ax2.margins(x=0)
    ax2.xaxis.set_major_locator(locator)
    ax2.set_xticklabels(utc_timestamps, rotation=30, fontsize=10)
    ax2.set_xlabel('Time (UTC) starting from {}'.format(
        datetime.utcfromtimestamp(creation_timestamp).strftime(
            '%Y-%m-%d %H:%M:%S')))

    imfile = 'elevation_utc_lst.png'
    print('Elevation plot {}'.format(imfile))
    plt.savefig(imfile, dpi=300)
    return fig
예제 #7
0
def run_observation(opts, kat):

    # extract control and observation information provided in observation file
    obs_plan_params = opts.obs_plan_params
    # remove observation specific instructions housed in YAML file
    del opts.obs_plan_params

    # set up duration periods for observation control
    obs_duration = -1
    if 'durations' in obs_plan_params:
        if 'obs_duration' in obs_plan_params['durations']:
            obs_duration = obs_plan_params['durations']['obs_duration']

    # Each observation loop contains a number of observation cycles over LST ranges
    # For a single observation loop, only a start LST and duration is required
    for observation_cycle in obs_plan_params['observation_loop']:
        # Unpack all target information
        if not ('target_list' in observation_cycle.keys()):
            user_logger.error('No targets provided - '
                              'stopping script instead of hanging around')
            continue
        obs_targets = read_targets(observation_cycle['target_list'])
        target_list = obs_targets['target'].tolist()
        # build katpoint catalogues for tidy handling of targets
        catalogue = collect_targets(kat.array, target_list)
        for tgt in obs_targets:
            tgt['target'] = catalogue[tgt['name']]
        # observer object handle to track the observation timing in a more user friendly way
        observer = catalogue._antenna.observer

        # Only observe targets in valid LST range
        [start_lst, end_lst] = get_lst(observation_cycle['LST'])

        # Verify that it is worth while continuing with the observation
        # The filter functions uses the current time as timestamps
        # and thus incorrectly set the simulation timestamp
        if not kat.array.dry_run:
            # Quit early if there are no sources to observe
            if len(catalogue.filter(el_limit_deg=opts.horizon)) == 0:
                raise NoTargetsUpError('No targets are currently visible - '
                                       'please re-run the script later')
            # Quit early if the observation requires all targets to be visible
            if opts.all_up and (len(catalogue.filter(
                    el_limit_deg=opts.horizon)) != len(catalogue)):
                raise NotAllTargetsUpError(
                    'Not all targets are currently visible - '
                    'please re-run the script with --visibility for information'
                )
        user_logger.info('Imaging targets are [{}]'.format(', '.join([
            repr(target.name)
            for target in catalogue.filter(['~bpcal', '~gaincal'])
        ])))
        user_logger.info("Bandpass calibrators are [{}]".format(', '.join(
            [repr(bpcal.name) for bpcal in catalogue.filter('bpcal')])))
        user_logger.info("Gain calibrators are [{}]".format(', '.join(
            [repr(gaincal.name) for gaincal in catalogue.filter('gaincal')])))

        # TODO: the description requirement in sessions should be re-evaluated
        # since the schedule block has the description
        # Description argument in instruction_set should be retired, but is needed by sessions
        # Assign proposal_description if available, else create a dummy
        if 'description' not in vars(opts):
            session_opts = vars(opts)
            description = 'Observation run'
            if 'proposal_description' in vars(opts):
                description = opts.proposal_description
            session_opts['description'] = description

        # Target observation loop
        with start_session(kat.array, **vars(opts)) as session:
            session.standard_setup(**vars(opts))
            start_datetime = timestamp2datetime(time.time())
            observer.date = ephem.Date(start_datetime)
            user_logger.trace('TRACE: requested start time ({}) {}'.format(
                datetime2timestamp(start_datetime), start_datetime))

            user_logger.trace('TRACE: observer at start\n {}'.format(observer))

            # Verify the observation is in a valid LST range
            # and that it is worth while continuing with the observation
            # Do not use float() values, ephem.hours does not convert as expected
            local_lst = observer.sidereal_time()
            # Only observe targets in current LST range
            if start_lst < end_lst:
                in_range = (
                    (ephem.hours(local_lst) >= ephem.hours(str(start_lst)))
                    and (ephem.hours(local_lst) < ephem.hours(str(end_lst))))
                if not in_range:
                    user_logger.error(
                        'Local LST outside LST range {}-{}'.format(
                            ephem.hours(str(start_lst)),
                            ephem.hours(str(end_lst))))
                    continue
            else:
                # else assume rollover at midnight to next day
                out_range = (
                    (ephem.hours(local_lst) < ephem.hours(str(start_lst)))
                    and (ephem.hours(local_lst) > ephem.hours(str(end_lst))))
                if out_range:
                    user_logger.error(
                        'Local LST outside LST range {}-{}'.format(
                            ephem.hours(str(start_lst)),
                            ephem.hours(str(end_lst))))
                    continue
            # TODO: setup of noise diode pattern should be moved to sessions so it happens in the line above
            if 'noise_diode' in obs_plan_params.keys():
                noisediode.pattern(kat.array, session,
                                   obs_plan_params['noise_diode'])

            # Adding explicit init after "Capture-init failed" exception was encountered
            session.capture_init()
            user_logger.debug(
                'DEBUG: Initialise capture start with timestamp {} ({})'.
                format(int(time.time()), timestamp2datetime(time.time())))

            # Go to first target before starting capture
            user_logger.info('Slewing to first target')
            observe(session, obs_targets[0], slewonly=True)
            # Only start capturing once we are on target
            session.capture_start()
            user_logger.trace(
                'TRACE: capture start time after slew ({}) {}'.format(
                    time.time(), timestamp2datetime(time.time())))
            user_logger.trace(
                'TRACE: observer after slew\n {}'.format(observer))

            done = False
            sanity_cntr = 0
            while not done:
                # small errors can cause an infinite loop here
                # preventing infinite loops
                sanity_cntr += 1
                if sanity_cntr > 100000:
                    user_logger.error(
                        'While limit counter has reached {}, exiting'.format(
                            sanity_cntr))
                    break

                # Cycle through target list in order listed
                targets_visible = False
                time_remaining = obs_duration

                for cnt, target in enumerate(obs_targets):
                    katpt_target = target['target']
                    user_logger.debug('DEBUG: {} {}'.format(cnt, target))
                    user_logger.trace(
                        'TRACE: initial observer for target\n {}'.format(
                            observer))
                    # check target visible before doing anything
                    if not above_horizon(katpt_target, horizon=opts.horizon):
                        user_logger.warn(
                            'Target {} below {} deg horizon, continuing'.
                            format(target['name'], opts.horizon))
                        continue
                    user_logger.trace(
                        'TRACE: observer after horizon check\n {}'.format(
                            observer))

                    # check and observe all targets with cadences
                    while_cntr = 0
                    cadence_targets = list(obs_targets)
                    while True:
                        tgt = cadence_target(cadence_targets)
                        if not tgt:
                            break
                        # check enough time remaining to continue
                        if (obs_duration > 0
                                and time_remaining < tgt['duration']):
                            done = True
                            break
                        # check target visible before doing anything
                        user_logger.trace(
                            'TRACE: cadence target\n {}\n {}'.format(
                                tgt, catalogue[tgt['name']]))
                        user_logger.trace(
                            'TRACE: initial observer for cadence target\n {}'.
                            format(observer))
                        user_logger.trace(
                            'TRACE: observer before track\n {}'.format(
                                observer))
                        user_logger.trace(
                            'TRACE: target observation # {} last observed {}'.
                            format(tgt['obs_cntr'], tgt['last_observed']))
                        if above_horizon(catalogue[tgt['name']],
                                         horizon=opts.horizon):
                            if observe(session, tgt, **obs_plan_params):
                                targets_visible += True
                                tgt['obs_cntr'] += 1
                                tgt['last_observed'] = time.time()
                            else:
                                # target not visibile to sessions anymore
                                cadence_targets.remove(tgt)
                            user_logger.trace(
                                'TRACE: observer after track\n {}'.format(
                                    observer))
                            user_logger.trace(
                                'TRACE: target observation # {} last observed {}'
                                .format(tgt['obs_cntr'], tgt['last_observed']))
                        else:
                            cadence_targets.remove(tgt)
                        while_cntr += 1
                        if while_cntr > len(obs_targets):
                            break
                    if done:
                        break
                    user_logger.trace(
                        'TRACE: observer after cadence\n {}'.format(observer))

                    # observe non cadence target
                    if target['cadence'] < 0:
                        user_logger.trace(
                            'TRACE: normal target\n {}'.format(target))
                        user_logger.trace(
                            'TRACE: observer before track\n {}'.format(
                                observer))
                        user_logger.trace('TRACE: ts before observe {}'.format(
                            time.time()))
                        user_logger.trace(
                            'TRACE: target last observed {}'.format(
                                target['last_observed']))

                        targets_visible += observe(session, target,
                                                   **obs_plan_params)

                        user_logger.trace(
                            'TRACE: observer after track\n {}'.format(
                                observer))
                        user_logger.trace('TRACE: ts after observe {}'.format(
                            time.time()))
                        if targets_visible:
                            target['obs_cntr'] += 1
                            target['last_observed'] = time.time()
                        user_logger.trace(
                            'TRACE: target observation # {} last observed {}'.
                            format(target['obs_cntr'],
                                   target['last_observed']))
                        user_logger.trace(
                            'TRACE: observer after track\n {}'.format(
                                observer))

                    # loop continuation checks
                    delta_time = time.time() - session.start_time
                    user_logger.trace(
                        'TRACE: time elapsed {} sec'.format(delta_time))
                    user_logger.trace(
                        'TRACE: total obs duration {} sec'.format(
                            obs_duration))
                    if obs_duration > 0:
                        time_remaining = obs_duration - delta_time
                        user_logger.trace(
                            'TRACE: time remaining {} sec'.format(
                                time_remaining))
                        next_target = obs_targets[(cnt + 1) % len(obs_targets)]
                        for next_tgt_idx in range(cnt + 1, len(obs_targets)):
                            next_target = obs_targets[next_tgt_idx %
                                                      len(obs_targets)]
                            user_logger.trace(
                                'TRACE: time needed for next obs {} sec'.
                                format(next_target['cadence']))
                            if next_target['cadence'] > 0:
                                continue
                            user_logger.trace(
                                'TRACE: time needed for next obs {} sec'.
                                format(next_target['duration']))
                        if time_remaining < 1. or \
                                time_remaining < next_target['duration']:
                            user_logger.info(
                                'Scheduled observation time lapsed - ending observation'
                            )
                            done = True
                            break

                if obs_duration < 0:
                    user_logger.info(
                        'Observation list completed - ending observation')
                    done = True

                # End if there is nothing to do
                if not targets_visible:
                    user_logger.warning(
                        'No more targets to observe - stopping script instead of hanging around'
                    )
                    done = True

        user_logger.trace('TRACE: observer at end\n {}'.format(observer))
        # display observation cycle statistics
        print
        user_logger.info("Observation loop statistics")
        total_obs_time = time.time() - session.start_time
        if obs_duration < 0:
            user_logger.info('Single run through observation target list')
        else:
            user_logger.info(
                "Desired observation time {:.2f} sec ({:.2f} min)".format(
                    obs_duration, obs_duration / 60.))
        user_logger.info(
            "Total observation time {:.2f} sec ({:.2f} min)".format(
                total_obs_time, total_obs_time / 60.))
        if len(obs_targets) > 0:
            user_logger.info("Targets observed :")
            for unique_target in np.unique(obs_targets['name']):
                cntrs = obs_targets[obs_targets['name'] ==
                                    unique_target]['obs_cntr']
                durations = obs_targets[obs_targets['name'] ==
                                        unique_target]['duration']
                if np.isnan(durations).any():
                    user_logger.info('{} observed {} times'.format(
                        unique_target, np.sum(cntrs)))
                else:
                    user_logger.info('{} observed for {} sec'.format(
                        unique_target, np.sum(cntrs * durations)))
        print
예제 #8
0
def source_elevation(catalogue, ref_antenna):
    """Generate a plot of elevation over time for 24 hour period.

    For all sources in provided catalogue at a specific location

    Parameters
    ----------
    catalogue: katpoint.Catalogue
    ref_antenna: katpoint.Antenna
        A MeerKAT reference antenna

    Returns
    -------
        matplotlib figure handle

    """
    catalogue.antenna = ref_antenna
    horizon = numpy.degrees(ref_antenna.observer.horizon)
    # All times and timestamps assumed UTC, no special conversion to
    # accommodate SAST allowed to prevent confusion
    creation_date = catalogue.antenna.observer.date
    creation_timestamp = datetime2timestamp(creation_date.datetime())
    time_range = creation_timestamp + numpy.arange(0, 24.0 * 60.0 * 60.0,
                                                   360.0)
    timestamps = [timestamp2datetime(ts) for ts in time_range]

    fig = plt.figure(figsize=(15, 7), facecolor="white")
    ax = plt.subplot(111)
    plt.subplots_adjust(right=0.8)
    fontP = FontProperties()
    fontP.set_size("small")

    for cnt, target in enumerate(catalogue.targets):
        elev = []
        for idx, timestamp in enumerate(timestamps):
            catalogue.antenna.observer.date = ephem.Date(timestamp)
            target.body.compute(catalogue.antenna.observer)
            elev.append(numpy.degrees(target.body.alt))

        label = "{} ".format(target.name)
        target.tags.remove("radec")
        if "target" in target.tags:
            target.tags.remove("target")
        label += ", ".join(target.tags)

        myplot, = plt.plot_date(timestamps,
                                elev,
                                fmt='.',
                                linewidth=0,
                                label=label)
    ax.axhspan(15, horizon, facecolor="k", alpha=0.1)
    plt.grid()
    plt.legend(loc='center left',
               bbox_to_anchor=(1, 0.5),
               prop={'size': 10},
               numpoints=1)
    plt.ylabel("Elevation (deg)")
    plt.ylim(15, 90)
    plt.yticks(fontsize=10)

    # fix tick positions for proper time axis display
    utc_hrs = [timestamps[0] + timedelta(hours=hr) for hr in range(0, 25, 1)]
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax.set_xlim(utc_hrs[0], utc_hrs[-1])
    ax.xaxis.set_major_locator(mdates.HourLocator(byhour=range(24),
                                                  interval=1))
    locs = ax.get_xticks()
    locs_labels = matplotlib.dates.num2date(locs)
    locator = matplotlib.ticker.FixedLocator(locs)
    ax.xaxis.set_major_locator(locator)
    utc_timestamps = [locs_lbl.strftime("%H:%M") for locs_lbl in locs_labels]

    lst_timestamps = []
    for locs_ts in locs_labels:
        catalogue.antenna.observer.date = ephem.Date(locs_ts)
        lst_time = "{}".format(catalogue.antenna.observer.sidereal_time())
        lst_time_str = datetime.strptime(lst_time,
                                         "%H:%M:%S.%f").strftime("%H:%M")
        lst_timestamps.append(lst_time_str)

    ax.set_xticklabels(lst_timestamps, rotation=30, fontsize=10)
    ax.set_xlabel("Local Sidereal Time")

    ax2 = ax.twiny()
    box = ax2.get_position()
    ax2.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax2.set_xlim(ax.get_xlim())
    ax2.set_xticks(ax.get_xticks())
    ax2.xaxis.set_major_locator(locator)
    ax2.set_xticklabels(utc_timestamps, rotation=30, fontsize=10)
    ax2.set_xlabel('Time (UTC) starting from {}'.format(
        datetime.utcfromtimestamp(creation_timestamp).strftime(
            '%Y-%m-%d %H:%M:%S')))

    return fig