# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import kaitaistruct
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
import satnogsdecoders.process


if getattr(kaitaistruct, 'API_VERSION', (0, 9)) < (0, 9):
    raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))

class Foresail1p(KaitaiStruct):
    """:field version_and_identity_len: skylink.version_and_identity_len
    :field identity: skylink.identity
    :field flags: skylink.header.flags
    :field frame_sequence: skylink.header.frame_sequence
    :field extension_length: skylink.header.extension_length
    :field vc: skylink.header.vc
    :field is_arq_on: skylink.header.is_arq_on
    :field is_authenticated: skylink.header.is_authenticated
    :field is_crced: skylink.header.is_crced
    :field sequence_control: skylink.header.sequence_control
    :field version: skylink.version
    :field len_identity: skylink.len_identity
    :field packet_id: pus.header.packet_id
    :field sequence: pus.header.sequence
    :field length: pus.header.length
    :field secondary_header: pus.header.secondary_header
    :field service_type: pus.header.service_type
    :field service_subtype: pus.header.service_subtype
    
    :field timestamp: pus.obc_housekeeping.timestamp.timestamp
    :field side: pus.obc_housekeeping.side
    :field fdir_mode: pus.obc_housekeeping.fdir_mode
    :field sys_watchdog_counter: pus.obc_housekeeping.sys_watchdog_counter
    :field scheduler: pus.obc_housekeeping.scheduler
    :field software_revision: pus.obc_housekeeping.software_revision
    :field uptime: pus.obc_housekeeping.uptime
    :field heap_free: pus.obc_housekeeping.heap_free
    :field cpu_load: pus.obc_housekeeping.cpu_load
    :field fs_free_space: pus.obc_housekeeping.fs_free_space
    :field arbiter_uptime: pus.obc_housekeeping.arbiter_uptime
    :field arbiter_age: pus.obc_housekeeping.arbiter_age
    :field arbiter_bootcount: pus.obc_housekeeping.arbiter_bootcount
    :field arbiter_temperature: pus.obc_housekeeping.arbiter_temperature
    :field side_a_bootcount: pus.obc_housekeeping.side_a_bootcount
    :field side_a_heartbeat: pus.obc_housekeeping.side_a_heartbeat
    :field side_a_fail_counter: pus.obc_housekeeping.side_a_fail_counter
    :field side_a_fail_reason: pus.obc_housekeeping.side_a_fail_reason
    :field side_b_bootcount: pus.obc_housekeeping.side_b_bootcount
    :field side_b_heartbeat: pus.obc_housekeeping.side_b_heartbeat
    :field side_b_fail_counter: pus.obc_housekeeping.side_b_fail_counter
    :field side_b_fail_reason: pus.obc_housekeeping.side_b_fail_reason
    :field arbiter_log_1: pus.obc_housekeeping.arbiter_log_1
    :field arbiter_log_2: pus.obc_housekeeping.arbiter_log_2
    :field arbiter_log_3: pus.obc_housekeeping.arbiter_log_3
    :field arbiter_log_4: pus.obc_housekeeping.arbiter_log_4
    
    :field eps_housekeeping_timestamp: pus.eps_housekeeping.timestamp.timestamp
    :field pcdu_uptime: pus.eps_housekeeping.pcdu_uptime
    :field pcdu_boot_count: pus.eps_housekeeping.pcdu_boot_count
    :field bb_boot_count: pus.eps_housekeeping.bb_boot_count
    :field apr_boot_count: pus.eps_housekeeping.apr_boot_count
    :field pdm_expected: pus.eps_housekeeping.pdm_expected
    :field pdm_faulted: pus.eps_housekeeping.pdm_faulted
    :field padding_byte: pus.eps_housekeeping.padding_byte
    :field eps_subsystem_state: pus.eps_housekeeping.eps_subsystem_state
    :field heater_pwm_on_time: pus.eps_housekeeping.heater_pwm_on_time
    :field current_apr_x: pus.eps_housekeeping.current_apr_x
    :field current_apr_y: pus.eps_housekeeping.current_apr_y
    :field voltage_apr_x: pus.eps_housekeeping.voltage_apr_x
    :field voltage_apr_y: pus.eps_housekeeping.voltage_apr_y
    :field current_apr_x_at_mpp: pus.eps_housekeeping.current_apr_x_at_mpp
    :field voltage_apr_x_at_mpp: pus.eps_housekeeping.voltage_apr_x_at_mpp
    :field current_apr_y_at_mpp: pus.eps_housekeeping.current_apr_y_at_mpp
    :field voltage_apr_y_at_mpp: pus.eps_housekeeping.voltage_apr_y_at_mpp
    :field voltage_battery: pus.eps_housekeeping.voltage_battery
    :field voltage_battery_lower: pus.eps_housekeeping.voltage_battery_lower
    :field voltage_payloads: pus.eps_housekeeping.voltage_payloads
    :field voltage_obc_adcs: pus.eps_housekeeping.voltage_obc_adcs
    :field voltage_uhf: pus.eps_housekeeping.voltage_uhf
    :field temperature_mcu_pcdu: pus.eps_housekeeping.temperature_mcu_pcdu
    :field temperature_mcu_bb: pus.eps_housekeeping.temperature_mcu_bb
    :field temperature_mcu_apr: pus.eps_housekeeping.temperature_mcu_apr
    :field temperature_battery: pus.eps_housekeeping.temperature_battery
    :field temperature_sp_x_minus: pus.eps_housekeeping.temperature_sp_x_minus
    :field temperature_sp_x_plus: pus.eps_housekeeping.temperature_sp_x_plus
    :field temperature_sp_y_minus: pus.eps_housekeeping.temperature_sp_y_minus
    :field temperature_sp_y_plus: pus.eps_housekeeping.temperature_sp_y_plus
    :field current_battery: pus.eps_housekeeping.current_battery
    :field current_battery_min: pus.eps_housekeeping.current_battery_min
    :field current_battery_max: pus.eps_housekeeping.current_battery_max
    :field current_pate_batt: pus.eps_housekeeping.current_pate_batt
    :field current_pb_batt: pus.eps_housekeeping.current_pb_batt
    :field current_pb_3v6: pus.eps_housekeeping.current_pb_3v6
    :field current_cam_3v6: pus.eps_housekeeping.current_cam_3v6
    :field current_mag_3v6: pus.eps_housekeeping.current_mag_3v6
    :field current_obc_3v6: pus.eps_housekeeping.current_obc_3v6
    :field current_uhf_3v6: pus.eps_housekeeping.current_uhf_3v6
    :field current_adcs_3v6: pus.eps_housekeeping.current_adcs_3v6
    :field current_min_pate: pus.eps_housekeeping.current_min_pate
    :field current_min_pb_batt: pus.eps_housekeeping.current_min_pb_batt
    :field current_min_pb_3v6: pus.eps_housekeeping.current_min_pb_3v6
    :field current_min_cam_3v6: pus.eps_housekeeping.current_min_cam_3v6
    :field current_min_mag_3v6: pus.eps_housekeeping.current_min_mag_3v6
    :field current_min_obc_3v6: pus.eps_housekeeping.current_min_obc_3v6
    :field current_min_uhf_3v6: pus.eps_housekeeping.current_min_uhf_3v6
    :field current_min_adcs_3v6: pus.eps_housekeeping.current_min_adcs_3v6
    :field current_max_pate: pus.eps_housekeeping.current_max_pate
    :field current_max_pb_batt: pus.eps_housekeeping.current_max_pb_batt
    :field current_max_pb_3v6: pus.eps_housekeeping.current_max_pb_3v6
    :field current_max_cam_3v6: pus.eps_housekeeping.current_max_cam_3v6
    :field current_max_mag_3v6: pus.eps_housekeeping.current_max_mag_3v6
    :field current_max_obc_3v6: pus.eps_housekeeping.current_max_obc_3v6
    :field current_max_uhf_3v6: pus.eps_housekeeping.current_max_uhf_3v6
    :field current_max_adcs_3v6: pus.eps_housekeeping.current_max_adcs_3v6
    
    :field uhf_housekeeping_timestamp: pus.uhf_housekeeping.timestamp.timestamp
    :field uhf_housekeeping_uptime: pus.uhf_housekeeping.uptime
    :field bootcount: pus.uhf_housekeeping.bootcount
    :field fdir_counter: pus.uhf_housekeeping.fdir_counter
    :field mcu_wd_reset_count: pus.uhf_housekeeping.mcu_wd_reset_count
    :field mbe_count: pus.uhf_housekeeping.mbe_count
    :field bus_sync_errors: pus.uhf_housekeeping.bus_sync_errors
    :field bus_len_errors: pus.uhf_housekeeping.bus_len_errors
    :field bus_crc_errors: pus.uhf_housekeeping.bus_crc_errors
    :field bus_receive_timeouts: pus.uhf_housekeeping.bus_receive_timeouts
    :field total_tx_frames: pus.uhf_housekeeping.total_tx_frames
    :field total_rx_frames: pus.uhf_housekeeping.total_rx_frames
    :field total_ham_tx_frames: pus.uhf_housekeeping.total_ham_tx_frames
    :field total_ham_rx_frames: pus.uhf_housekeeping.total_ham_rx_frames
    :field hardware_side_a_bootcount: pus.uhf_housekeeping.hardware_side_a_bootcount
    :field hardware_side_b_bootcount: pus.uhf_housekeeping.hardware_side_b_bootcount
    :field uhf_housekeeping_side: pus.uhf_housekeeping.side
    :field symbol_rate_rx: pus.uhf_housekeeping.symbol_rate_rx
    :field symbol_rate_tx: pus.uhf_housekeeping.symbol_rate_tx
    :field my_window_length: pus.uhf_housekeeping.my_window_length
    :field peer_window_length: pus.uhf_housekeeping.peer_window_length
    :field mcu_temperature: pus.uhf_housekeeping.mcu_temperature
    :field pa_temperature: pus.uhf_housekeeping.pa_temperature
    :field background_rssi: pus.uhf_housekeeping.background_rssi
    :field background_max_rssi: pus.uhf_housekeeping.background_max_rssi
    :field last_rssi: pus.uhf_housekeeping.last_rssi
    :field last_freq_offset: pus.uhf_housekeeping.last_freq_offset
    
    :field adcs_housekeeping_timestamp: pus.adcs_housekeeping.timestamp.timestamp
    :field determination_mode: pus.adcs_housekeeping.determination_mode
    :field control_mode: pus.adcs_housekeeping.control_mode
    :field mjd: pus.adcs_housekeeping.mjd
    :field position_x: pus.adcs_housekeeping.position_x
    :field position_y: pus.adcs_housekeeping.position_y
    :field position_z: pus.adcs_housekeeping.position_z
    :field velocity_x: pus.adcs_housekeeping.velocity_x
    :field velocity_y: pus.adcs_housekeeping.velocity_y
    :field velocity_z: pus.adcs_housekeeping.velocity_z
    :field low: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.low
    :field high: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.high
    :field combined_u5: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.combined_u5
    :field q1: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q1
    :field q2: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q2
    :field q3: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q3
    :field q4_metadata: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q4_metadata
    :field q4_index: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q4_index
    :field q4_sign: pus.adcs_housekeeping.estimated_attitude_compressed_quaternion.q4_sign
    :field estimated_angular_rate_x: pus.adcs_housekeeping.estimated_angular_rate_x
    :field estimated_angular_rate_y: pus.adcs_housekeeping.estimated_angular_rate_y
    :field estimated_angular_rate_z: pus.adcs_housekeeping.estimated_angular_rate_z
    :field estimated_magnetometer_bias_x: pus.adcs_housekeeping.estimated_magnetometer_bias_x
    :field estimated_magnetometer_bias_y: pus.adcs_housekeeping.estimated_magnetometer_bias_y
    :field estimated_magnetometer_bias_z: pus.adcs_housekeeping.estimated_magnetometer_bias_z
    :field estimated_gyro_bias_x: pus.adcs_housekeeping.estimated_gyro_bias_x
    :field estimated_gyro_bias_y: pus.adcs_housekeeping.estimated_gyro_bias_y
    :field estimated_gyro_bias_z: pus.adcs_housekeeping.estimated_gyro_bias_z
    :field attitude_variance: pus.adcs_housekeeping.attitude_variance
    :field angular_velocity_variance: pus.adcs_housekeeping.angular_velocity_variance
    :field mag_bias_variance: pus.adcs_housekeeping.mag_bias_variance
    :field gyro_bias_variance: pus.adcs_housekeeping.gyro_bias_variance
    :field magnetometer_measurement_x: pus.adcs_housekeeping.magnetometer_measurement_x
    :field magnetometer_measurement_y: pus.adcs_housekeeping.magnetometer_measurement_y
    :field magnetometer_measurement_z: pus.adcs_housekeeping.magnetometer_measurement_z
    :field gyroscope_measurement_x: pus.adcs_housekeeping.gyroscope_measurement_x
    :field gyroscope_measurement_y: pus.adcs_housekeeping.gyroscope_measurement_y
    :field gyroscope_measurement_z: pus.adcs_housekeeping.gyroscope_measurement_z
    :field sun_vector_measurement_x: pus.adcs_housekeeping.sun_vector_measurement_x
    :field sun_vector_measurement_y: pus.adcs_housekeeping.sun_vector_measurement_y
    :field sun_vector_measurement_z: pus.adcs_housekeeping.sun_vector_measurement_z
    
    :field event_timestamp: pus.event.timestamp.timestamp
    :field rid: pus.event.rid
    :field info: pus.event.info.b64encstring.info_data_b64_encoded
    
    :field callsign: repeater.ax25_header.dest_callsign_raw.callsign_ror.callsign
    :field ssid_mask: repeater.ax25_header.dest_ssid_raw.ssid_mask
    :field ssid: repeater.ax25_header.dest_ssid_raw.ssid
    :field src_callsign_raw_callsign: repeater.ax25_header.src_callsign_raw.callsign_ror.callsign
    :field src_ssid_raw_ssid_mask: repeater.ax25_header.src_ssid_raw.ssid_mask
    :field src_ssid_raw_ssid: repeater.ax25_header.src_ssid_raw.ssid
    :field ctl: repeater.ax25_header.ctl
    :field pid: repeater.ax25_header.pid
    :field payload: repeater.payload
    :field fcs: repeater.fcs
    
    .. seealso::
       Source - https://foresail.github.io/docs/FS1p_Space_Ground_Interface_Control_Sheet.pdf
    """
    def __init__(self, _io, _parent=None, _root=None):
        self._io = _io
        self._parent = _parent
        self._root = _root if _root else self
        self._read()

    def _read(self):
        self.skylink = Foresail1p.SkylinkFrame(self._io, self, self._root)
        if  ((self.skylink.header.vc == 0) or (self.skylink.header.vc == 1)) :
            self._raw_pus = self._io.read_bytes(((self._io.size() - self._io.pos()) - (4 * self.skylink.header.is_authenticated)))
            _io__raw_pus = KaitaiStream(BytesIO(self._raw_pus))
            self.pus = Foresail1p.ForesailPusFrame(_io__raw_pus, self, self._root)

        if self.skylink.header.vc == 3:
            self._raw_repeater = self._io.read_bytes(((self._io.size() - self._io.pos()) - (4 * self.skylink.header.is_crced)))
            _io__raw_repeater = KaitaiStream(BytesIO(self._raw_repeater))
            self.repeater = Foresail1p.Ax25Frame(_io__raw_repeater, self, self._root)

        if self.skylink.header.is_authenticated == 1:
            self.auth = self._io.read_bytes(4)

        if self.skylink.header.is_crced == 1:
            self.frame_crc = self._io.read_bytes(4)


    class Ax25Frame(KaitaiStruct):
        """
        .. seealso::
           Source - https://www.tapr.org/pub_ax25.html
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.ax25_header = Foresail1p.Ax25Header(self._io, self, self._root)
            self.payload = (self._io.read_bytes(((self._io.size() - self._io.pos()) - 2))).decode(u"ASCII")
            self.fcs = self._io.read_u2be()


    class Ax25Header(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.dest_callsign_raw = Foresail1p.CallsignRaw(self._io, self, self._root)
            self.dest_ssid_raw = Foresail1p.SsidMask(self._io, self, self._root)
            self.src_callsign_raw = Foresail1p.CallsignRaw(self._io, self, self._root)
            self.src_ssid_raw = Foresail1p.SsidMask(self._io, self, self._root)
            if (self.src_ssid_raw.ssid_mask & 1) == 0:
                self.digipeaters = []
                i = 0
                while True:
                    _ = Foresail1p.Digipeater(self._io, self, self._root)
                    self.digipeaters.append(_)
                    if (_.ssid_raw.ssid_mask & 1) != 0:
                        break
                    i += 1

            self.ctl = self._io.read_u1()
            if not self.ctl == 3:
                raise kaitaistruct.ValidationNotEqualError(3, self.ctl, self._io, u"/types/ax25_header/seq/5")
            self.pid = self._io.read_u1()
            if not self.pid == 240:
                raise kaitaistruct.ValidationNotEqualError(240, self.pid, self._io, u"/types/ax25_header/seq/6")


    class SkyStaticHeader(KaitaiStruct):
        """Skylink static header."""
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.flags = self._io.read_u1()
            self.frame_sequence = self._io.read_u2be()
            self.extension_length = self._io.read_u1()

        @property
        def is_arq_on(self):
            if hasattr(self, '_m_is_arq_on'):
                return self._m_is_arq_on

            self._m_is_arq_on = ((self.flags & 4) >> 2)
            return getattr(self, '_m_is_arq_on', None)

        @property
        def is_crced(self):
            if hasattr(self, '_m_is_crced'):
                return self._m_is_crced

            self._m_is_crced = ((self.flags & 16) >> 4)
            return getattr(self, '_m_is_crced', None)

        @property
        def sequence_control(self):
            if hasattr(self, '_m_sequence_control'):
                return self._m_sequence_control

            self._m_sequence_control = ((self.flags & 96) >> 5)
            return getattr(self, '_m_sequence_control', None)

        @property
        def is_authenticated(self):
            if hasattr(self, '_m_is_authenticated'):
                return self._m_is_authenticated

            self._m_is_authenticated = ((self.flags & 8) >> 3)
            return getattr(self, '_m_is_authenticated', None)

        @property
        def vc(self):
            if hasattr(self, '_m_vc'):
                return self._m_vc

            self._m_vc = (self.flags & 3)
            return getattr(self, '_m_vc', None)


    class PusHeader(KaitaiStruct):
        """CCSDS PUS header."""
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.packet_id = self._io.read_u2be()
            self.sequence = self._io.read_u2be()
            self.length = self._io.read_u2be()
            self.secondary_header = self._io.read_u1()
            self.service_type = self._io.read_u1()
            self.service_subtype = self._io.read_u1()


    class AdcsHousekeeping(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = Foresail1p.UnixTimestamp(self._io, self, self._root)
            self.determination_mode = self._io.read_u1()
            self.control_mode = self._io.read_u1()
            self.mjd = self._io.read_f4le()
            self.position_x = self._io.read_f4le()
            self.position_y = self._io.read_f4le()
            self.position_z = self._io.read_f4le()
            self.velocity_x = self._io.read_f4le()
            self.velocity_y = self._io.read_f4le()
            self.velocity_z = self._io.read_f4le()
            self.estimated_attitude_compressed_quaternion = Foresail1p.CompressedQuaternion(self._io, self, self._root)
            self.estimated_angular_rate_x = self._io.read_u2le()
            self.estimated_angular_rate_y = self._io.read_u2le()
            self.estimated_angular_rate_z = self._io.read_u2le()
            self.estimated_magnetometer_bias_x = self._io.read_u2le()
            self.estimated_magnetometer_bias_y = self._io.read_u2le()
            self.estimated_magnetometer_bias_z = self._io.read_u2le()
            self.estimated_gyro_bias_x = self._io.read_u2le()
            self.estimated_gyro_bias_y = self._io.read_u2le()
            self.estimated_gyro_bias_z = self._io.read_u2le()
            self.attitude_variance = self._io.read_u2le()
            self.angular_velocity_variance = self._io.read_u2le()
            self.mag_bias_variance = self._io.read_u2le()
            self.gyro_bias_variance = self._io.read_u2le()
            self.magnetometer_measurement_x = self._io.read_s2le()
            self.magnetometer_measurement_y = self._io.read_s2le()
            self.magnetometer_measurement_z = self._io.read_s2le()
            self.gyroscope_measurement_x = self._io.read_s2le()
            self.gyroscope_measurement_y = self._io.read_s2le()
            self.gyroscope_measurement_z = self._io.read_s2le()
            self.sun_vector_measurement_x = self._io.read_s2le()
            self.sun_vector_measurement_y = self._io.read_s2le()
            self.sun_vector_measurement_z = self._io.read_s2le()


    class Callsign(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.callsign = (self._io.read_bytes(6)).decode(u"ASCII")


    class UhfHousekeeping(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = Foresail1p.UnixTimestamp(self._io, self, self._root)
            self.uptime = self._io.read_u4le()
            self.bootcount = self._io.read_u2le()
            self.fdir_counter = self._io.read_u1()
            self.mcu_wd_reset_count = self._io.read_u1()
            self.mbe_count = self._io.read_u1()
            self.bus_sync_errors = self._io.read_u1()
            self.bus_len_errors = self._io.read_u1()
            self.bus_crc_errors = self._io.read_u1()
            self.bus_receive_timeouts = self._io.read_u1()
            self.total_tx_frames = self._io.read_u4le()
            self.total_rx_frames = self._io.read_u4le()
            self.total_ham_tx_frames = self._io.read_u4le()
            self.total_ham_rx_frames = self._io.read_u4le()
            self.hardware_side_a_bootcount = self._io.read_u1()
            self.hardware_side_b_bootcount = self._io.read_u1()
            self.side = self._io.read_u1()
            self.symbol_rate_rx = self._io.read_u1()
            self.symbol_rate_tx = self._io.read_u1()
            self.my_window_length = self._io.read_u4le()
            self.peer_window_length = self._io.read_u4le()
            self.mcu_temperature = self._io.read_s2le()
            self.pa_temperature = self._io.read_s2le()
            self.background_rssi = self._io.read_s2le()
            self.background_max_rssi = self._io.read_s2le()
            self.last_rssi = self._io.read_s2le()
            self.last_freq_offset = self._io.read_s2le()


    class UnixTimestamp(KaitaiStruct):
        """Unix timestamp."""
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = self._io.read_u4be()


    class Event(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = Foresail1p.UnixTimestamp(self._io, self, self._root)
            self.rid = self._io.read_u2be()
            self._raw_info = self._io.read_bytes_full()
            _io__raw_info = KaitaiStream(BytesIO(self._raw_info))
            self.info = Foresail1p.Event.Base64(_io__raw_info, self, self._root)

        class Base64(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None):
                self._io = _io
                self._parent = _parent
                self._root = _root if _root else self
                self._read()

            def _read(self):
                self._raw__raw_b64encstring = self._io.read_bytes_full()
                _process = satnogsdecoders.process.B64encode()
                self._raw_b64encstring = _process.decode(self._raw__raw_b64encstring)
                _io__raw_b64encstring = KaitaiStream(BytesIO(self._raw_b64encstring))
                self.b64encstring = Foresail1p.Event.Base64string(_io__raw_b64encstring, self, self._root)


        class Base64string(KaitaiStruct):
            def __init__(self, _io, _parent=None, _root=None):
                self._io = _io
                self._parent = _parent
                self._root = _root if _root else self
                self._read()

            def _read(self):
                self.info_data_b64_encoded = (self._io.read_bytes_full()).decode(u"UTF-8")



    class Digipeater(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.callsign_raw = Foresail1p.CallsignRaw(self._io, self, self._root)
            self.ssid_raw = Foresail1p.SsidMask(self._io, self, self._root)

        @property
        def has_been_repeated(self):
            if hasattr(self, '_m_has_been_repeated'):
                return self._m_has_been_repeated

            self._m_has_been_repeated = (self.ssid_raw.ssid_mask & 128) != 0
            return getattr(self, '_m_has_been_repeated', None)


    class EpsHousekeeping(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = Foresail1p.UnixTimestamp(self._io, self, self._root)
            self.pcdu_uptime = self._io.read_u4le()
            self.pcdu_boot_count = self._io.read_u1()
            self.bb_boot_count = self._io.read_u1()
            self.apr_boot_count = self._io.read_u1()
            self.pdm_expected = self._io.read_u1()
            self.pdm_faulted = self._io.read_u1()
            self.padding_byte = self._io.read_u1()
            self.eps_subsystem_state = self._io.read_u2le()
            self.heater_pwm_on_time = self._io.read_u2le()
            self.current_apr_x = self._io.read_s2le()
            self.current_apr_y = self._io.read_s2le()
            self.voltage_apr_x = self._io.read_s2le()
            self.voltage_apr_y = self._io.read_s2le()
            self.current_apr_x_at_mpp = self._io.read_s2le()
            self.voltage_apr_x_at_mpp = self._io.read_s2le()
            self.current_apr_y_at_mpp = self._io.read_s2le()
            self.voltage_apr_y_at_mpp = self._io.read_s2le()
            self.voltage_battery = self._io.read_s2le()
            self.voltage_battery_lower = self._io.read_s2le()
            self.voltage_payloads = self._io.read_s2le()
            self.voltage_obc_adcs = self._io.read_s2le()
            self.voltage_uhf = self._io.read_s2le()
            self.temperature_mcu_pcdu = self._io.read_s2le()
            self.temperature_mcu_bb = self._io.read_s2le()
            self.temperature_mcu_apr = self._io.read_s2le()
            self.temperature_battery = self._io.read_s2le()
            self.temperature_sp_x_minus = self._io.read_s2le()
            self.temperature_sp_x_plus = self._io.read_s2le()
            self.temperature_sp_y_minus = self._io.read_s2le()
            self.temperature_sp_y_plus = self._io.read_s2le()
            self.current_battery = self._io.read_s2le()
            self.current_battery_min = self._io.read_s2le()
            self.current_battery_max = self._io.read_s2le()
            self.current_pate_batt = self._io.read_s2le()
            self.current_pb_batt = self._io.read_s2le()
            self.current_pb_3v6 = self._io.read_s2le()
            self.current_cam_3v6 = self._io.read_s2le()
            self.current_mag_3v6 = self._io.read_s2le()
            self.current_obc_3v6 = self._io.read_s2le()
            self.current_uhf_3v6 = self._io.read_s2le()
            self.current_adcs_3v6 = self._io.read_s2le()
            self.current_min_pate = self._io.read_s2le()
            self.current_min_pb_batt = self._io.read_s2le()
            self.current_min_pb_3v6 = self._io.read_s2le()
            self.current_min_cam_3v6 = self._io.read_s2le()
            self.current_min_mag_3v6 = self._io.read_s2le()
            self.current_min_obc_3v6 = self._io.read_s2le()
            self.current_min_uhf_3v6 = self._io.read_s2le()
            self.current_min_adcs_3v6 = self._io.read_s2le()
            self.current_max_pate = self._io.read_s2le()
            self.current_max_pb_batt = self._io.read_s2le()
            self.current_max_pb_3v6 = self._io.read_s2le()
            self.current_max_cam_3v6 = self._io.read_s2le()
            self.current_max_mag_3v6 = self._io.read_s2le()
            self.current_max_obc_3v6 = self._io.read_s2le()
            self.current_max_uhf_3v6 = self._io.read_s2le()
            self.current_max_adcs_3v6 = self._io.read_s2le()


    class ForesailPusFrame(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.header = Foresail1p.PusHeader(self._io, self, self._root)
            if  ((self.header.service_type == 3) and (self.header.service_subtype == 2)) :
                self.obc_housekeeping = Foresail1p.ObcHousekeeping(self._io, self, self._root)

            if  ((self.header.service_type == 3) and (self.header.service_subtype == 3)) :
                self.eps_housekeeping = Foresail1p.EpsHousekeeping(self._io, self, self._root)

            if  ((self.header.service_type == 3) and (self.header.service_subtype == 4)) :
                self.uhf_housekeeping = Foresail1p.UhfHousekeeping(self._io, self, self._root)

            if  ((self.header.service_type == 3) and (self.header.service_subtype == 5)) :
                self.adcs_housekeeping = Foresail1p.AdcsHousekeeping(self._io, self, self._root)

            if  ((self.header.service_type == 4) and ( ((self.header.service_subtype == 1) or (self.header.service_subtype == 2) or (self.header.service_subtype == 3) or (self.header.service_subtype == 4)) )) :
                self.event = Foresail1p.Event(self._io, self, self._root)



    class Pdms(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.val = self._io.read_u1()

        @property
        def mag_3v6(self):
            if hasattr(self, '_m_mag_3v6'):
                return self._m_mag_3v6

            self._m_mag_3v6 = ((self.val & 16) >> 4)
            return getattr(self, '_m_mag_3v6', None)

        @property
        def uhf_3v6(self):
            if hasattr(self, '_m_uhf_3v6'):
                return self._m_uhf_3v6

            self._m_uhf_3v6 = ((self.val & 64) >> 6)
            return getattr(self, '_m_uhf_3v6', None)

        @property
        def pate_batt(self):
            if hasattr(self, '_m_pate_batt'):
                return self._m_pate_batt

            self._m_pate_batt = ((self.val & 1) >> 0)
            return getattr(self, '_m_pate_batt', None)

        @property
        def obc_3v6(self):
            if hasattr(self, '_m_obc_3v6'):
                return self._m_obc_3v6

            self._m_obc_3v6 = ((self.val & 32) >> 5)
            return getattr(self, '_m_obc_3v6', None)

        @property
        def cam_3v6(self):
            if hasattr(self, '_m_cam_3v6'):
                return self._m_cam_3v6

            self._m_cam_3v6 = ((self.val & 8) >> 3)
            return getattr(self, '_m_cam_3v6', None)

        @property
        def pb_3v6(self):
            if hasattr(self, '_m_pb_3v6'):
                return self._m_pb_3v6

            self._m_pb_3v6 = ((self.val & 4) >> 2)
            return getattr(self, '_m_pb_3v6', None)

        @property
        def adcs_3v6(self):
            if hasattr(self, '_m_adcs_3v6'):
                return self._m_adcs_3v6

            self._m_adcs_3v6 = ((self.val & 128) >> 7)
            return getattr(self, '_m_adcs_3v6', None)

        @property
        def pb_batt(self):
            if hasattr(self, '_m_pb_batt'):
                return self._m_pb_batt

            self._m_pb_batt = ((self.val & 2) >> 1)
            return getattr(self, '_m_pb_batt', None)


    class SkylinkFrame(KaitaiStruct):
        """
        .. seealso::
           Skylink Protocol Specification.pdf
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.version_and_identity_len = self._io.read_u1()
            self.identity = (self._io.read_bytes(self.len_identity)).decode(u"ASCII")
            if not self.identity == u"OH2F1S":
                raise kaitaistruct.ValidationNotEqualError(u"OH2F1S", self.identity, self._io, u"/types/skylink_frame/seq/1")
            self.header = Foresail1p.SkyStaticHeader(self._io, self, self._root)
            self.extensions = self._io.read_bytes(self.header.extension_length)

        @property
        def version(self):
            if hasattr(self, '_m_version'):
                return self._m_version

            self._m_version = ((self.version_and_identity_len & 248) >> 3)
            return getattr(self, '_m_version', None)

        @property
        def len_identity(self):
            if hasattr(self, '_m_len_identity'):
                return self._m_len_identity

            self._m_len_identity = (self.version_and_identity_len & 7)
            return getattr(self, '_m_len_identity', None)


    class SsidMask(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.ssid_mask = self._io.read_u1()

        @property
        def ssid(self):
            if hasattr(self, '_m_ssid'):
                return self._m_ssid

            self._m_ssid = ((self.ssid_mask & 15) >> 1)
            return getattr(self, '_m_ssid', None)


    class CompressedQuaternion(KaitaiStruct):
        """Metadata for the last component:
        - 2 bits: Index of the largest component (0-2)
        - 1 bit: Sign of the largest component (0 = positive, 1 = negative)
        - 1 bit: Unused
        """
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.low = self._io.read_u4le()
            self.high = self._io.read_u1()

        @property
        def q4_sign(self):
            if hasattr(self, '_m_q4_sign'):
                return self._m_q4_sign

            self._m_q4_sign = ((self.q4_metadata >> 1) & 1)
            return getattr(self, '_m_q4_sign', None)

        @property
        def q1(self):
            if hasattr(self, '_m_q1'):
                return self._m_q1

            self._m_q1 = ((self.combined_u5 >> 28) & 4095)
            return getattr(self, '_m_q1', None)

        @property
        def combined_u5(self):
            if hasattr(self, '_m_combined_u5'):
                return self._m_combined_u5

            self._m_combined_u5 = ((self.low << 8) | self.high)
            return getattr(self, '_m_combined_u5', None)

        @property
        def q4_metadata(self):
            if hasattr(self, '_m_q4_metadata'):
                return self._m_q4_metadata

            self._m_q4_metadata = (self.combined_u5 & 15)
            return getattr(self, '_m_q4_metadata', None)

        @property
        def q4_index(self):
            if hasattr(self, '_m_q4_index'):
                return self._m_q4_index

            self._m_q4_index = ((self.q4_metadata >> 2) & 3)
            return getattr(self, '_m_q4_index', None)

        @property
        def q2(self):
            if hasattr(self, '_m_q2'):
                return self._m_q2

            self._m_q2 = ((self.combined_u5 >> 16) & 4095)
            return getattr(self, '_m_q2', None)

        @property
        def q3(self):
            if hasattr(self, '_m_q3'):
                return self._m_q3

            self._m_q3 = ((self.combined_u5 >> 4) & 4095)
            return getattr(self, '_m_q3', None)


    class CallsignRaw(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self._raw__raw_callsign_ror = self._io.read_bytes(6)
            self._raw_callsign_ror = KaitaiStream.process_rotate_left(self._raw__raw_callsign_ror, 8 - (1), 1)
            _io__raw_callsign_ror = KaitaiStream(BytesIO(self._raw_callsign_ror))
            self.callsign_ror = Foresail1p.Callsign(_io__raw_callsign_ror, self, self._root)


    class ObcHousekeeping(KaitaiStruct):
        def __init__(self, _io, _parent=None, _root=None):
            self._io = _io
            self._parent = _parent
            self._root = _root if _root else self
            self._read()

        def _read(self):
            self.timestamp = Foresail1p.UnixTimestamp(self._io, self, self._root)
            self.side = self._io.read_u1()
            self.fdir_mode = self._io.read_u1()
            self.sys_watchdog_counter = self._io.read_u2le()
            self.scheduler = self._io.read_u1()
            self.software_revision = self._io.read_u1()
            self.uptime = self._io.read_u4le()
            self.heap_free = self._io.read_u1()
            self.cpu_load = self._io.read_u1()
            self.fs_free_space = self._io.read_u2le()
            self.arbiter_uptime = self._io.read_u2le()
            self.arbiter_age = self._io.read_u2le()
            self.arbiter_bootcount = self._io.read_u2le()
            self.arbiter_temperature = self._io.read_s2le()
            self.side_a_bootcount = self._io.read_u1()
            self.side_a_heartbeat = self._io.read_u1()
            self.side_a_fail_counter = self._io.read_u1()
            self.side_a_fail_reason = self._io.read_u1()
            self.side_b_bootcount = self._io.read_u1()
            self.side_b_heartbeat = self._io.read_u1()
            self.side_b_fail_counter = self._io.read_u1()
            self.side_b_fail_reason = self._io.read_u1()
            self.arbiter_log_1 = self._io.read_u2le()
            self.arbiter_log_2 = self._io.read_u2le()
            self.arbiter_log_3 = self._io.read_u2le()
            self.arbiter_log_4 = self._io.read_u2le()



