Last active
August 29, 2015 14:14
-
-
Save alexrudy/49ded8844bb86ff867af to your computer and use it in GitHub Desktop.
Starlist Parsing Tools
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This is a parser for the Keck starlist format. | |
The format is documented at the Keck website: <https://www2.keck.hawaii.edu/observing/starlist.html> | |
Author: Alex Rudy, UCSC | |
Date: 2015-01-24 | |
License: BSD | |
""" | |
import warnings | |
from datetime import date, datetime | |
import astropy.units as u | |
import astropy.time | |
from astropy.coordinates import SkyCoord, FK4, FK5, AltAz | |
import re | |
_starlist_re_raw = r""" | |
^(?P<Name>.{1,15})[\s]+ # Target name must be the first 15 characters. | |
(?P<RA>(?:[\d]{1,2}[\s:][\s\d]?[\d][\s:][\s\d]?[\d](?:\.[\d]+)?)|(?:[\d]+\.[\d]+))[\s]+ # Right Ascension, HH MM SS.SS+ | |
(?P<Dec>(?:[+-]?[\d]{1,2}[\s:][\s\d]?[\d][\s:][\s\d]?[\d](?:\.[\d]+)?)|(?:[\d]+\.[\d]+)) # Declination, (-)DD MM SS.SS+ | |
(?:[\s]+(?P<Equinox>(?:[\d]{4}(?:\.[\d]+)?)|(?:APP)))?[\s]* # Equinox. | |
(?P<Keywords>.+)?$ # Everything else must be a keyword. | |
""" | |
_starlist_re = re.compile(_starlist_re_raw, re.VERBOSE) | |
_starlist_re_strict = r""" | |
^(?P<Name>.{15})\ # Target name must be the first 15 characters. | |
(?P<RA>[\d]{2}\ [\d]{2}\ [\d]{2}(?:\.[\d]+)?)\ # Right Ascension, HH MM SS.SS+ | |
(?P<Dec>[+-]?[\d]{1,2}\ [\d]{2}\ [\d]{2}(?:\.[\d]+)?)\ # Declination, (-)DD MM SS.SS+ | |
(?P<Equinox>(?:[\d]{4}(?:\.[\d]+)?)|(?:APP))[\ ]? # Equinox. | |
(?P<Keywords>[^\ ].+)?$ # Everything else must be a keyword. | |
""" | |
_starlist_token_parts = ["Name", "RA", "Dec", "Equinox", "Keywords"] | |
def verify_starlist_line(text, identifier="<stream line 0>", warning=False): | |
"""Verify that the given line is a valid starlist.""" | |
line = text | |
messages = [] | |
match = _starlist_re.match(text) | |
if not match: | |
raise ValueError("Couldn't parse '{0:s}', no regular expression match found.".format(text)) | |
data = match.groupdict("") | |
# Check the Name: | |
name_length = match.end('Name') - match.start('Name') + 1 | |
if name_length < 15: | |
messages.append(('Warning','Name','Name should be exactly 15 characters long (whitespace is ok.) len(Name)={0:d}'.format(name_length))) | |
# Check the RA starting position. | |
if match.start('RA') + 1 != 17: | |
messages.append(('Error','RA','RA must start in column 17. Start: {0:d}'.format(match.start('RA')+1))) | |
# Check the Dec starting token | |
if match.start('Dec') - match.end('RA') != 1: | |
messages.append(('Warning','Dec','RA and Dec should be separated by only a single space, found {0:d} characters.'.format(match.start('Dec') - match.end('RA')))) | |
# Check the Equinox starting token. | |
if match.start('Equinox') - match.end('Dec') != 1: | |
messages.append(('Warning','Equinox','Dec and Equinox should be separated by only a single space, found {0:d} characters.'.format(match.start('Equinox') - match.end('Dec')))) | |
if match.group("Keywords") and len(match.group("Keywords")): | |
for kwarg in match.group("Keywords").split(): | |
if kwarg.count("=") < 1: | |
messages.append(('Error', 'Keywords', 'Each keyword/value pair must have 1 "=", none found {!r}'.format(kwarg))) | |
if kwarg.count("=") > 1: | |
messages.append(('Error', 'Keywords', 'Each keyword/value pair must have 1 "=", {0:d} found {1!r}'.format(kwarg.count("="), kwarg))) | |
for severity, token, message in messages: | |
composed_message = "[{0}][{1} {2}] {3}".format(severity, identifier, token, message) | |
if warning: | |
warnings.warn(composed_message) | |
else: | |
print(composed_message) | |
def parse_starlist_line(text): | |
"""Parse a single line from a Keck formatted starlist, returning a dictionary of parsed values. | |
:param text: The starlist text line. | |
:returns: A dictionary of starlist object properties, set from teh starlist line. | |
:raises: ValueError if the line couldn't be parsed. | |
This function parses a single line from a starlist and returns a dictionary of items from that line. The followig keys are included: | |
- `Name`: The target name. | |
- `Position`: An astropy.coordinates object representing this position. | |
- Any other keyword/value pairs, which are found at the end of the starlist line, and formatted as ``keyword=value`` | |
""" | |
match = _starlist_re.match(text) | |
if not match: | |
raise ValueError("Couldn't parse '{}', no regular expression match found.".format(text)) | |
data = match.groupdict("") | |
if data.get('Equinox','') == '': | |
equinox = astropy.time.Time.now() | |
frame = AltAz | |
elif data['Equinox'] == "APP": | |
equinox = astropy.time.Time.now() | |
frame = 'fk5' | |
else: | |
equinox = astropy.time.Time(float(data['Equinox']), format='jyear', scale='utc') | |
if float(data['Equinox']) <= 1950: | |
equinox = astropy.time.Time(float(data['Equinox']), format='byear', scale='utc') | |
frame = 'fk4' | |
else: | |
frame = 'fk5' | |
position = SkyCoord(data["RA"], data["Dec"], unit=(u.hourangle, u.degree), equinox=equinox, frame=frame) | |
results = dict( | |
Name = data['Name'].strip(), | |
Position = position, | |
) | |
for keywordvalue in data.get("Keywords","").split(): | |
if keywordvalue.count("=") < 1: | |
warnings.warn("Illegal Keyword Argument: '{}'".format(keywordvalue)) | |
continue | |
keyword, value = keywordvalue.split("=",1) | |
keyword = keyword.strip() | |
if keyword in set(["Name", "Position"]): | |
warnings.warn("Illegal Keyword Name: '{}'".format(keyword)) | |
results[keyword] = value.strip() | |
return results | |
def read_skip_comments(filename, comments="#"): | |
"""Read a filename, yielding lines that don't start with comments.""" | |
with open(filename, 'r') as stream: | |
for line in stream: | |
if not line.startswith(comments): | |
yield line.strip().strip("\n\r") | |
def parse_starlist(starlist): | |
"""Parse a starlist into a sequence of dictionaries.""" | |
for line in read_skip_comments(starlist): | |
yield parse_starlist_line(line) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment