def table_line( datetime, target, horizon, sep_angle=None, cal_limit=None, sol_limit=None, lst=False, notes="", ): """Construct a line of target information to display on command line output. Parameters ---------- datetime: ephem.Date ephem date and time object target: katpoint.Catalogue target from katpoint.Catalogue object horizon: float minimum pointing angle in degrees sep_angle: float separation angle in degrees [optional param] cal_limit: float maximum separation angle between target and calibrator [optional] sol_limit: float minimum separation angle between target and Sun [optional] lst: str display times in LST rather than UTC notes: str user provided extra information Returns ------- <name> <risetime UTC> <settime UTC> <Separation> <Notes> """ observatory = Observatory(horizon=horizon, datetime=datetime) rise_time = observatory._ephem_risetime_(target.body, lst=lst) set_time = observatory._ephem_settime_(target.body, lst=lst) if not lst: rise_time = rise_time.datetime().strftime("%H:%M:%S") set_time = set_time.datetime().strftime("%H:%M:%S") else: rise_time = str(rise_time) set_time = str(set_time) clo_clr = bcolors.ENDC sep_note = "" if sep_angle is not None: sep_note = "%.2f" % sep_angle if cal_limit is not None: if sep_angle > cal_limit: clo_clr = bcolors.WARNING sep_note += " ***" if sol_limit is not None: if sep_angle < sol_limit: clo_clr = bcolors.FAIL sep_note += " ***" table_info = "{: <16}{: <32}{: <16}{: <16}{: <16}{: <16}{: <16}{: <16}\n".format( target.name, " ".join(target.tags), str(target.body._ra), str(target.body._dec), rise_time, set_time, sep_note, notes, ) return clo_clr + table_info + bcolors.ENDC
def best_cal_cover(catalogue, katpt_target, ref_antenna): """Find calibrator with best coverage (>80%) of target visibility period. else return 2 calibrators to cover the whole target visibility period Parameters ---------- catalogue: katpoint.Catalogue katpt_target: katpoint.Target ref_antenna: katpoint.Antenna Returns ------- calibrator: katpoint.Target object closest calibrator separation_angle: float separation angle in degrees buffer_calibrator: katpoint.Target object additional calibrator for LST coverage buffer_separation: float additional separation_angle in degrees """ calibrator, separation = _closest_calibrator_(catalogue, katpt_target.body, ref_antenna.observer) buffer_calibrator = None buffer_separation = 180.0 horizon = numpy.degrees(ref_antenna.observer.horizon) if separation > 20.0: # calibrator rises some time after target # add another calibrator preceding the target observatory = Observatory(horizon=horizon, datetime=ref_antenna.observer.date) [tgt_rise_time, tgt_set_time ] = observatory.target_rise_and_set_times(katpt_target.body, lst=False) closest_cals = [] for each_cal in catalogue: try: [cal_rise_time, cal_set_time ] = observatory.target_rise_and_set_times(each_cal.body, lst=False) except ephem.NeverUpError: continue except ephem.AlwaysUpError: continue delta_time_to_cal_rise = cal_set_time - tgt_rise_time if delta_time_to_cal_rise > 0: # calc that rise before the target closest_cals.append([each_cal.name, delta_time_to_cal_rise]) delta_time_to_cal_set = tgt_set_time - cal_rise_time if delta_time_to_cal_set > 0: # calc that sets after the target closest_cals.append([each_cal.name, delta_time_to_cal_set]) if len(closest_cals) > 0: buffer_cal_idx = numpy.array(closest_cals)[:, 1].astype( float).argmin() buffer_calibrator = catalogue[closest_cals[buffer_cal_idx][0]] buffer_separation = ephem.separation(katpt_target.body, buffer_calibrator.body) buffer_separation = numpy.degrees(buffer_separation) return calibrator, separation, buffer_calibrator, buffer_separation
def main(creation_time, horizon=20., solar_angle=20., cal_tags=None, target=None, header_info=None, view_tags=None, mkat_catalogues=False, lst=False, all_cals=False, user_text_only=False, save_fig=False, infile=None, viewfile=None, outfile=None, **kwargs): """Run calibration observation.""" # set defaults if cal_tags is None: cal_tags = ['gain'] if view_tags is None: view_tags = ['elevation'] observatory = Observatory() location = observatory.location node_config_available = observatory.node_config_available ref_antenna = katpoint.Antenna(location) ref_antenna.observer.date = ephem.Date(creation_time) ref_antenna.observer.horizon = ephem.degrees(str(horizon)) text_only = (user_text_only or global_text_only) if viewfile is not None: # check if view file in CSV or YAML data_dict = read_yaml(viewfile) if data_dict: catalogue = katpoint.Catalogue() catalogue.antenna = ref_antenna for observation_cycle in data_dict["observation_loop"]: for target_item in observation_cycle["target_list"]: name, target = katpoint_target_string(target_item) catalogue.add(katpoint.Target(target)) else: # assume CSV # output observation stats for catalogue with open(viewfile, 'r') as fin: catalogue = katpoint.Catalogue(fin) obs_summary = obs_table( ref_antenna, catalogue=catalogue, solar_sep=solar_angle, lst=lst, ) print(obs_summary) if not text_only: for view_option in view_tags: cp_cat = deepcopy(catalogue) if "elevation" in view_option: plot_func = source_elevation if "solarangle" in view_option: plot_func = source_solar_angle if "riseset" in view_option: plot_func = source_rise_set plot_func(cp_cat, ref_antenna) plt.show() return if mkat_catalogues and os.path.isdir(mkat_catalogues): catalogue_path = mkat_catalogues config_file_available = True else: catalogue_path = "katconfig/user/catalogues" config_file_available = False # before doing anything, verify that calibrator catalogues can be accessed if not os.path.isdir(catalogue_path) and not node_config_available: msg = "Could not access calibrator catalogue default location\n" msg += "add explicit location of catalogue folder using --cat-path <dirname>" raise RuntimeError(msg) # constructing observational catalogue observation_catalogue = katpoint.Catalogue() observation_catalogue.antenna = ref_antenna # targets to obtain calibrators for header = "" cal_targets = [] if target is not None: if len(target) > 2: # input target from command line target = [tgt.strip() for tgt in target] target = ", ".join( map(str, [target[0], "radec target", target[1], target[2]])) cal_targets = [katpoint.Target(target)] elif len(target) < 2: # input solar body from command line solar_body = target[0].capitalize() katpt_target = katpoint.Target("{}, special".format(solar_body)) cal_targets = [katpt_target] else: # if len(target) == 2 # input target from command line target = [tgt.strip() for tgt in target] target = ", ".join( map(str, [target[0], "xephem target", target[1]])) cal_targets = [katpoint.Target(target)] else: # assume the targets are in a file if infile is None: raise RuntimeError('Specify --target or CSV catalogue --infile') with open(infile, "r") as fin: # extract targets tagged to be used for calibrator selection for line in fin.readlines(): if line[0] == "#": # catch and keep header lines header += line continue if len(line) < 1: # ignore empty lines continue if "calref" in line: target = line.strip().replace("calref", "target") cal_targets.append(katpoint.Target(target)) else: # add target to catalogue target = line.strip().replace("radec", "radec target") observation_catalogue.add(katpoint.Target(target)) # if not reference target for calibrator selection is specified, # simply select the first target listed in the catalogue if len(cal_targets) < 1: cal_targets = [observation_catalogue.targets[0]] for target in cal_targets: # add calibrator catalogues and calibrators to catalogue for cal_tag in cal_tags: cal_catalogue = os.path.join( catalogue_path, "Lband-{}-calibrators.csv".format(caltag_dict[cal_tag])) try: fin = open(cal_catalogue, 'r') if config_file_available: calibrators = katpoint.Catalogue(fin) elif node_config_available: calibrators = katpoint.Catalogue( observatory.read_file_from_node_config(cal_catalogue)) else: # user specified calibrator file calibrators = katpoint.Catalogue(fin) except (AssertionError, IOError): msg = bcolors.WARNING msg += "Unable to open {}\n".format(cal_catalogue) msg += "Observation file will still be created," "please add calibrator manually\n" msg += bcolors.ENDC print(msg) continue if "gain" in cal_tag or "delay" in cal_tag: # for secondary calibrators such as gain: # find the closest calibrator calibrator, separation_angle = get_cal(calibrators, target, ref_antenna) observation_catalogue = add_target(calibrator, observation_catalogue, tag=cal_tag + "cal") else: # for primary calibrators: if all_cals: # show all calibrators for calibrator in calibrators: observation_catalogue = add_target( calibrator, observation_catalogue, tag=cal_tag + "cal") else: # find the best coverage over the target visibility period [ calibrator, separation_angle, buffer_calibrator, buffer_calibrator_separation_angle ] = best_cal_cover(calibrators, target, ref_antenna) if (buffer_calibrator is not None and buffer_calibrator_separation_angle < 90.0): observation_catalogue = add_target( buffer_calibrator, observation_catalogue, tag=cal_tag + "cal", ) observation_catalogue = add_target(calibrator, observation_catalogue, tag=cal_tag + "cal") observation_catalogue = add_target(target, observation_catalogue) # write observation catalogue catalogue_header = write_header(header_info, userheader=header) catalogue_data = observation_catalogue.sort() if outfile is not None: filename = os.path.splitext(os.path.basename(outfile))[0] + ".csv" outfile = os.path.join(os.path.dirname(outfile), filename) write_catalogue(outfile, catalogue_header, catalogue_data) print("Observation catalogue {}".format(outfile)) # output observation stats for catalogue obs_summary = obs_table( ref_antenna, catalogue=catalogue_data, ref_tgt_list=cal_targets, solar_sep=solar_angle, lst=lst, ) print(obs_summary) if global_text_only and not user_text_only: msg = "Required matplotlib functionalities not available\n" msg += "Cannot create elevation plot\n" msg += "Only producing catalogue file and output to screen" print(msg) if not text_only: # create elevation plot for sources obs_catalogue = catalogue_header for target in catalogue_data: obs_catalogue += "{}\n".format(target) source_elevation(observation_catalogue, ref_antenna) if save_fig: imfile = "elevation_utc_lst.png" print("Elevation plot {}".format(imfile)) plt.savefig(imfile, dpi=300) plt.show() plt.close()
def table_line( datetime, target, horizon, sep_angle=None, cal_limit=None, sol_limit=None, lst=False, notes="", ): """Construct a line of target information to display on command line output. Parameters ---------- datetime: ephem.Date ephem date and time object target: katpoint.Catalogue target from katpoint.Catalogue object horizon: float minimum pointing angle in degrees sep_angle: float separation angle in degrees [optional param] cal_limit: float maximum separation angle between target and calibrator [optional] sol_limit: float minimum separation angle between target and Sun [optional] lst: str display times in LST rather than UTC notes: str user provided extra information Returns ------- <name> <risetime UTC> <settime UTC> <Separation> <Notes> """ observatory = Observatory(horizon=horizon, datetime=datetime) [rise_time, set_time] = observatory.target_rise_and_set_times(target.body, lst=lst) if type(rise_time) is ephem.Angle: rise_time = str(rise_time) set_time = str(set_time) elif type(rise_time) is ephem.Date: rise_time = rise_time.datetime().strftime("%H:%M:%S") set_time = set_time.datetime().strftime("%H:%M:%S") else: rise_time = None set_time = None clo_clr = bcolors.ENDC sep_note = "" if sep_angle is not None: sep_note = "%.2f" % sep_angle if cal_limit is not None: if sep_angle > cal_limit: clo_clr = bcolors.WARNING sep_note += " ***" if sol_limit is not None: if sep_angle < sol_limit: clo_clr = bcolors.FAIL sep_note += " ***" # ephem has difference between defining astrometric coordinates # for FixedBody vs Body objects try: RA = target.body._ra DECL = target.body._dec except AttributeError: RA = target.body.a_ra DECL = target.body.a_dec table_info = "{: <16}{: <32}{: <16}{: <16}{: <16}{: <16}{: <16}{: <16}\n".format( target.name, " ".join(target.tags), str(RA), str(DECL), rise_time, set_time, sep_note, notes, ) return clo_clr + table_info + bcolors.ENDC