Beispiel #1
0
    def __init__(self):
        #
        # Parser combinators
        #

        SPACES = spaces()
        optional_spaces = optional(SPACES)
        empty = SPACES.parsecmap(lambda x: EMPTY)
        comment = string('%%%') >> regex('.*')
        comment = comment.parsecmap(Comment)
        codepoint_hex = regex('[0-9A-F]+')
        codepoint_hex = codepoint_hex.parsecmap(lambda x: int(x, 16))
        codepoint = string('U+') >> codepoint_hex
        codepoint_seq = sepBy(codepoint, SPACES)
        codepoint_seq = codepoint_seq.parsecmap(tuple)
        arrow = string('=>')
        arrow = optional_spaces >> arrow << optional_spaces
        mapping = joint(
            codepoint_seq << arrow,
            codepoint_seq,
            optional(comment),
        )
        mapping = mapping.parsecmap(lambda x: Mapping(x[0], x[1], x[2]))
        line = try_choice(mapping, try_choice(
            comment,
            empty,
        ))
        self.parse = line.parse
Beispiel #2
0
        def component(duty_exp: DutyExpression) -> Parser:
            """Matches a string prefix and returns the associated type id, along
            with any parsed amounts and units according to their applicability,
            as a 4-tuple of (id, amount, monetary unit, measurement)."""
            prefix = duty_exp.prefix
            has_amount = duty_exp.duty_amount_applicability_code
            has_measurement = duty_exp.measurement_unit_applicability_code
            has_monetary = duty_exp.monetary_unit_applicability_code

            id = token(prefix).result(duty_exp)
            this_value = if_applicable(has_amount, decimal)
            this_monetary_unit = if_applicable(
                has_monetary,
                spaces() >> self._monetary_unit,
                # We must match the percentage if the amount should be there
                # and no monetary unit matches.
                default=(percentage_unit
                         if has_amount == ApplicabilityCode.MANDATORY else
                         optional(percentage_unit)),
            )
            this_measurement = if_applicable(
                has_measurement,
                optional(token("/")) >> self._measurement,
            )

            component = joint(id, this_value, this_monetary_unit,
                              this_measurement)
            measurement_only = joint(id, this_measurement).parsecmap(
                lambda t: (t[0], None, None, t[1]), )

            # It's possible for units that contain numbers (e.g. DTN => '100 kg')
            # to be confused with a simple specific duty (e.g 100.0 + kg)
            # So in the case that amounts are only optional and measurements are present,
            # we have to check for just measurements first.
            return (measurement_only
                    ^ component if has_amount == ApplicabilityCode.PERMITTED
                    and has_measurement != ApplicabilityCode.NOT_PERMITTED else
                    component).parsecmap(
                        lambda exp: component_output(
                            duty_expression=exp[0],
                            duty_amount=exp[1],
                            monetary_unit=exp[2],
                            component_measurement=exp[3],
                        ), )
Beispiel #3
0
def measurement(m: Measurement) -> Parser:
    """
    For measurement units and qualifiers, we match a human-readable version of
    the unit to its internal code.

    Units by themselves are always allowed, but only some combinations of units
    and qualifiers are permitted.
    """
    unit = abbrev(m.measurement_unit)
    if m.measurement_unit_qualifier:
        qualifier = token("/") >> abbrev(m.measurement_unit_qualifier)
    else:
        qualifier = empty
    return joint(unit, qualifier).result(m)
Beispiel #4
0
    def __init__(self, header):
        header_fields = defaultdict(str)
        for line in header.splitlines():
            field, label = line[:60], trim_whitespace(line[60:])
            header_fields[label] += field

        self.header_fields = dict(header_fields)

        self.version, self.type, satellite = p.Parser(
            p.joint(
                n_ANY(9).parsecmap(float), p.compose(n_ANY(11),
                                                     p.one_of('MON')),
                p.compose(n_ANY(19), n_ANY(1)))).parse(
                    self.header_fields['RINEX VERSION / TYPE'])
Beispiel #5
0
from base import pid
from parsing import *
from parsec import joint

Gyro = pid(0x10) >> \
    joint(
        field('X', int16),
        field('Y', int16),
        field('Z', int16),
        field('Temperature', int16)
    ).bind(to_dict)
Gyro >>= label_as('Gyro')
Beispiel #6
0
from parsec import joint
from parsing import *

minutes = packed('<B').parsecmap(lambda x: timedelta(minutes=x))
array200 = packed('<200s')

Message = joint(
    field('Interval', minutes),
    field('RepeatCount', byte),
    field('Message', array200)
    ).bind(to_dict)
Message >>= label_as('Periodic Message')
Beispiel #7
0
    def __init__(
        self,
        duty_expressions: Iterable[DutyExpression],
        monetary_units: Iterable[MonetaryUnit],
        permitted_measurements: Iterable[Measurement],
        component_output: Type[TrackedModel] = MeasureComponent,
    ):
        # Decimal numbers are a sequence of digits (without a left-trailing zero)
        # followed optionally by a decimal point and a number of digits (we have seen
        # some percentage values have three decimal digits).  Money values are similar
        # but only 2 digits are allowed after the decimal.
        # TODO: work out if float will cause representation problems.
        decimal = regex(r"(0|[1-9][0-9]*)([.][0-9]+)?").parsecmap(float)

        # Specific duty amounts reference various types of unit.
        # For monetary units, the expression just contains the same code as is
        # present in the sentence. Percentage values correspond to no unit.
        self._monetary_unit = (reduce(try_choice, map(code, monetary_units))
                               if monetary_units else fail)
        percentage_unit = token("%").result(None)

        # We have to try and parse measurements with qualifiers first
        # else we may match the first part of a unit without the qualifier
        with_qualifier = [
            m for m in permitted_measurements
            if m.measurement_unit_qualifier is not None
        ]
        no_qualifier = [
            m for m in permitted_measurements
            if m.measurement_unit_qualifier is None
        ]
        measurements = [
            measurement(m) for m in chain(with_qualifier, no_qualifier)
        ]
        self._measurement = reduce(try_choice,
                                   measurements) if measurements else fail

        # Each measure component can have an amount, monetary unit and measurement.
        # Which expression elements are allowed in a component is controlled by
        # the duty epxression applicability codes. We convert the duty expressions
        # into parsers that will only parse the elements that are permitted for this type.
        def component(duty_exp: DutyExpression) -> Parser:
            """Matches a string prefix and returns the associated type id, along
            with any parsed amounts and units according to their applicability,
            as a 4-tuple of (id, amount, monetary unit, measurement)."""
            prefix = duty_exp.prefix
            has_amount = duty_exp.duty_amount_applicability_code
            has_measurement = duty_exp.measurement_unit_applicability_code
            has_monetary = duty_exp.monetary_unit_applicability_code

            id = token(prefix).result(duty_exp)
            this_value = if_applicable(has_amount, decimal)
            this_monetary_unit = if_applicable(
                has_monetary,
                spaces() >> self._monetary_unit,
                # We must match the percentage if the amount should be there
                # and no monetary unit matches.
                default=(percentage_unit
                         if has_amount == ApplicabilityCode.MANDATORY else
                         optional(percentage_unit)),
            )
            this_measurement = if_applicable(
                has_measurement,
                optional(token("/")) >> self._measurement,
            )

            component = joint(id, this_value, this_monetary_unit,
                              this_measurement)
            measurement_only = joint(id, this_measurement).parsecmap(
                lambda t: (t[0], None, None, t[1]), )

            # It's possible for units that contain numbers (e.g. DTN => '100 kg')
            # to be confused with a simple specific duty (e.g 100.0 + kg)
            # So in the case that amounts are only optional and measurements are present,
            # we have to check for just measurements first.
            return (measurement_only
                    ^ component if has_amount == ApplicabilityCode.PERMITTED
                    and has_measurement != ApplicabilityCode.NOT_PERMITTED else
                    component).parsecmap(
                        lambda exp: component_output(
                            duty_expression=exp[0],
                            duty_amount=exp[1],
                            monetary_unit=exp[2],
                            component_measurement=exp[3],
                        ), )

        # Duty sentences can only be of a finite length – each expression may only
        # appear once and in order of increasing expression id. So we try all expressions
        # in order and filter out the None results for ones that did not match.
        expressions = ([
            component(exp) ^ empty
            for exp in sorted(duty_expressions, key=lambda e: e.sid)
        ] if duty_expressions else [fail])
        self._sentence = joint(*expressions).parsecmap(
            lambda sentence: [exp for exp in sentence if exp is not None], )
Beispiel #8
0
from parsec import joint
from base import pid, count
from parsing import *

RadFET = pid(0x35) >> joint(field('Status', byte), field(
    'Temperature', uint32), field('Voltages', count(uint32, 3))).bind(to_dict)
RadFET >>= label_as('RadFET')
Beispiel #9
0
from parsec import joint
from base import pid, count
from parsing import *

ExperimentalSunSStatus = joint(
    field('ALS_ACK', uint16),
    field('ALS_Presence', uint16),
    field('ALS_ADC_Valid', uint16),
)
ExperimentalSunSStatus >>= to_dict

SingleALS = count(uint16, 4)

ALSs = count(SingleALS, 3)

Temperatures = joint(
    field('Structure', uint16),
    field('A', uint16),
    field('B', uint16),
    field('C', uint16),
    field('D', uint16),
).bind(to_dict)

ExperimentalSunSPrimary = pid(0x11) \
                          >> joint(
                            field('WhoAmI ', byte),
                            field('Status', ExperimentalSunSStatus),
                            field('VisibleLight', ALSs),
                            field('Temperatures', Temperatures),
                          ).bind(to_dict)
ExperimentalSunSPrimary >>= label_as('ExpSunS.Primary')
Beispiel #10
0
from base import pid
from parsing import *
from parsec import joint

Sail = pid(0x18) >> \
    joint(
        field('Temperature', uint16),
        field('Open', boolean)
    ).bind(to_dict)
Sail >>= label_as('Sail')
Beispiel #11
0
from base import pid, label_as, field, to_dict
from parsing import byte, uint16
from parsec import Parser, Value

from emulator.beacon_parser import eps_controller_a_telemetry_parser, eps_controller_b_telemetry_parser, \
    error_counting_telemetry, experiment_telemetry_parser, mcu_temperature_parser
from emulator.beacon_parser.parser import BitArrayParser

from math import ceil

PayloadWhoAmI = pid(0x30) >> count(byte, 1)
PayloadWhoAmI >>= label_as('Payload Who Am I')

PayloadHousekeeping = pid(0x34) >> joint(
    field('INT 3V3D', uint16),
    field('OBC 3V3D', uint16),
).bind(to_dict)
PayloadHousekeeping >>= label_as('Payload Housekeeping')

##########################################################################


class PayloadOBCTelemetryParser:
    def __init__(self):
        self.storage = {}

    def write(self, category, name, value):
        if category not in self.storage.keys():
            self.storage[category] = {}

        self.storage[category][name] = value
Beispiel #12
0
from parsec import spaces
from parsec import sepBy
from parsec import sepBy1

logger = logging.getLogger(__name__)

optionalspaces = optional(spaces())
arrow = optionalspaces >> string('->') << optionalspaces

identifier = (regex('[a-zA-Z_$][a-zA-Z_$0-9]*') ^ string('<init>')
              ^ string('<clinit>'))
className = sepBy1(identifier, string('$'))
packagedFullName = sepBy1(identifier, string('.'))
packagedClassName = packagedFullName.parsecmap(lambda l: '.'.join(l))
typeName = packagedClassName | regex('[a-z]+')
javatype = joint(typeName, optional(string('[]')))

methodName = identifier
methodArguments = sepBy(optionalspaces >> javatype << optionalspaces,
                        string(','))
methodArguments = string('(') >> methodArguments << string(')')

linenumber = regex('[0-9]+').parsecmap(lambda s: int(s))
linenumbers = joint(
    linenumber << string(':'),
    linenumber << string(':'),
)

member = joint(
    optional(linenumbers),
    javatype << spaces(),
Beispiel #13
0
from parsec import joint
from parsing import *

CounterConfig = joint(
    field('Limit', byte),
    field('Increment', byte),
    field('Decrement', byte),
    field('Zero', byte),
).bind(to_dict)
CounterConfig >>= label_as('Counter Config')

ErrorCounters = joint(field('Comm', CounterConfig),
                      field('Eps', CounterConfig), field('RTC', CounterConfig),
                      field('Imtq', CounterConfig),
                      field('N25q Flash 1', CounterConfig),
                      field('N25q Flash 2', CounterConfig),
                      field('N25q Flash 3', CounterConfig),
                      field('N25q TMR', CounterConfig),
                      field('FRAM TMR', CounterConfig),
                      field('Payload', CounterConfig),
                      field('Camera', CounterConfig),
                      field('Suns', CounterConfig),
                      field('Antenna Primary', CounterConfig),
                      field('Antenna Backup', CounterConfig)).bind(to_dict)
ErrorCounters >>= label_as('Error Counters')
Beispiel #14
0
from parsec import joint
from parsing import *

time = packed('<Q').parsecmap(lambda x: timedelta(milliseconds=x))

TimeCorrection = joint(field('Internal Clock Weight', int16),
                       field('External Clock Weight', int16)).bind(to_dict)

TimeCorrection >>= label_as('Time Correction')

MissionTime = joint(field('Internal Time', time), field('External Time',
                                                        time)).bind(to_dict)

MissionTime >>= label_as('Mission Time')
Beispiel #15
0
from parsec import joint, count

from adcs import *
from antenna import *
from error_counters import *
from message import *
from sail import *
from time import *


def flat(values):
    result = {}

    for v in values:
        key = v.keys()[0]
        result[key] = v[key]

    return result


PersistentStateParser = joint(AntennaConfiguration, MissionTime,
                              TimeCorrection, SailState, ErrorCounters,
                              AdcsConfiguration, Message).parsecmap(flat)

__all__ = ['PersistentStateParser']