1#!/usr/bin/env python3
  2
  3"""Decoding of binary header sections of RAPIDFILE TDEV blocks."""
  4
  5from datetime import datetime
  6
  7from chart.common.binary import unpack_uint16_be
  8from chart.common.binary import unpack_uint32_be
  9from chart.products.utils import Segment
 10from chart.project import SID
 11from chart import settings
 12
 13TDEV_TC_FILING_KEY_NORMAL = 100  # in TC packets if filing_key is 100, we can read the packet
 14TDEV_TC_FILING_KEY_ACK = 200  # in TC packets filing_key 200 means we can't read it
 15
 16# Standard ASCII character set
 17ASCII_CHARSET = 'latin-1'
 18
 19# Single zero character used to terminate some RAPID strings
 20NULL_CHAR = '\0'
 21
 22# Newline character
 23NEWLINE = '\n'
 24
 25# Some parts of the TDEV header are stored in a separate area after the main data block.
 26# The offset between the main and extended areas is not well documented and may specific
 27# to missions or SCOS versions
 28# EXTENDED_BUFFER_OFFSET = 3  # MTG
 29# EXTENDED_BUFFER_OFFSET = 3  # JCS
 30EXTENDED_BUFFER_OFFSET = settings.PUS_EXTENDED_BUFFER_OFFSET
 31
 32class TDEVError(Exception):
 33    pass
 34
 35class TDEVTimestampError(TDEVError):
 36    def __init__(self, message):
 37        super(TDEVTimestampError, self).__init__()
 38        self.message = message
 39
 40    @property
 41    def short_message(self):
 42        return 'ERROR: cannot decode'
 43
 44    def __str__(self):
 45        return 'TDEVTimestampError {mess}'.format(mess=self.message)
 46
 47class TDEVHeader(Segment):
 48    """TDEV Packet Header template as defined in EGOS-MCS-S2K-ADD-0024, Version 1.2, Page 10.
 49
 50    Also known as S2K Header."""
 51
 52    CADU_RECORDS = None
 53
 54    @staticmethod
 55    def init(sid=None):
 56        SID(name=sid.name,sid=sid)
 57        TDEVHeader.sid = sid
 58        TDEVHeader.CADU_RECORDS = [
 59            SID.special_packets.good_frame_spid,
 60            SID.special_packets.bad_frame_spid,
 61            SID.special_packets.unknown_packet_spid,
 62            SID.special_packets.bad_packet_spid,
 63            SID.special_packets.idle_packet_spid,
 64            SID.special_packets.idle_frame_spid,
 65        ]
 66
 67    # this is a fixed length header block
 68    LENGTH = 60
 69
 70    @property
 71    def tdev_c_tree(self):
 72        return unpack_uint16_be(self.buff, 0)
 73
 74    @property
 75    def tdev_access_flag(self):
 76        return unpack_uint16_be(self.buff, 2)
 77
 78    @property
 79    def tdev_spare_simul_flag(self):
 80        return self.buff[3]
 81
 82    @property
 83    def tdev_spare0(self):
 84        b = self.buff[3]
 85        return b & 0b11111100
 86
 87    @property
 88    def tdev_simul_flag(self):
 89        b = self.buff[3]
 90        return b & 0b00000011
 91
 92    @property
 93    def tdev_filing_time_secs(self):
 94        return unpack_uint32_be(self.buff, 4)
 95
 96    @property
 97    def tdev_filing_time_usecs(self):
 98        return unpack_uint32_be(self.buff, 8)
 99
100    @property
101    def tdev_create_time_secs(self):
102        return unpack_uint32_be(self.buff, 12)
103
104    @property
105    def tdev_create_time_usecs(self):
106        return unpack_uint32_be(self.buff, 16)
107
108    @property
109    def tdev_create_id(self):
110        return unpack_uint32_be(self.buff, 20)
111
112    @property
113    def tdev_sc_id(self):
114        return unpack_uint16_be(self.buff, 24)
115
116    @property
117    def tdev_gs_id(self):
118        return unpack_uint16_be(self.buff, 26)
119
120    @property
121    def tdev_size(self):
122        return unpack_uint32_be(self.buff, 28)
123
124    # @property
125    # def tdev_s2k_header_type_ver(self):
126        # return self.buff[32]
127
128    @property
129    def tdev_s2k_header_type(self):
130        from chart.products.rapidfile.rapid import TDEV_HEADER_TYPE
131        from chart.products.rapidfile.rapid import UnknownTDEVHdr
132
133        header_type = self.buff[32] >> 4
134        try:
135            return TDEV_HEADER_TYPE(header_type)
136
137        except ValueError:
138            raise UnknownTDEVHdr(header_type)
139
140    @property
141    def tdev_version(self):
142        b = self.buff[32]
143        return b & 0x0F
144
145    @property
146    def tdev_spare_flags(self):
147        return self.buff[33]
148
149    @property
150    def tdev_spare2(self):
151        b = self.tdev_spare_flags
152        return b & 0b11000000
153
154    @property
155    def tdev_filing_flag(self):
156        b = self.tdev_spare_flags
157        return b & 0b00100000
158
159    @property
160    def tdev_distrib_flag(self):
161        b = self.buff[33]
162        return b & 0b00010000
163
164    @property
165    def tdev_timestamp_type(self):
166        b = self.buff[33]
167        return b & 0b00001100
168
169    @property
170    def tdev_time_quality_flag(self):
171        b = self.buff[33]
172        return b & 0b00000011
173
174    @property
175    def tdev_stream_id(self):
176        return unpack_uint16_be(self.buff, 34)
177
178    @property
179    def tdev_sequence_counter(self):
180        return unpack_uint32_be(self.buff, 36)
181
182    @property
183    def tdev_filing_key(self):
184        return unpack_uint32_be(self.buff, 40)
185
186    @property
187    def tdev_retr_key_1(self):
188        return unpack_uint16_be(self.buff, 44)
189
190    @property
191    def tdev_retr_key_2(self):
192        return unpack_uint16_be(self.buff, 46)
193
194    @property
195    def tdev_mission_id(self):
196        return unpack_uint16_be(self.buff, 48)
197
198    @property
199    def tdev_context_id(self):
200        return unpack_uint16_be(self.buff, 50)
201
202    @property
203    def tdev_domain_id(self):
204        return unpack_uint16_be(self.buff, 52)
205
206    @property
207    def tdev_db_release(self):
208        return unpack_uint16_be(self.buff, 54)
209
210    @property
211    def tdev_db_issue(self):
212        return unpack_uint16_be(self.buff, 56)
213
214    @property
215    def tdev_spare1(self):
216        return unpack_uint16_be(self.buff, 58)
217
218    @property
219    def length(self):
220        return self.LENGTH
221
222    @property
223    def spid(self):
224        return self.tdev_filing_key
225
226    @property
227    def timestamp(self):
228        """Extract filing time timestamp from TDEV_header."""
229        return datetime.utcfromtimestamp(self.tdev_filing_time_secs).replace(
230                microsecond=self.tdev_filing_time_usecs)
231
232    @property
233    def create_timestamp(self):
234        """Extract create time timestamp from TDEV_header."""
235        return datetime.utcfromtimestamp(self.tdev_create_time_secs).replace(
236                microsecond=self.tdev_create_time_usecs)
237
238    @property
239    def rapid_type(self):
240        """Set Rapid Record type, CADU or CCSDS."""
241        # After reading the SPID from a RapidFile block, we determine it's a CADU block
242        # if it matches any of these values
243        # Can't be done at module level because the SID configuration file won't have been read
244        from chart.products.rapidfile.rapid import RapidType
245        if self.spid in TDEVHeader.CADU_RECORDS:
246            return RapidType.CADU
247
248        else:
249            return RapidType.CCSDS
250
251    def show(self, indent, indentation):
252        """Display the Rapid Record TDEV Header format."""
253        return """{i}TDEV header:
254{i}{ii}tdev_c_tree: {c_tree}
255{i}{ii}tdev_access_flag: {access_flag}
256{i}{ii}tdev_spare_simul_flag: {spare_simul_flag}
257{i}{ii}tdev_filing_time secs: {filing_time_secs} ({timestamp})
258{i}{ii}tdev_filing time usecs: {filing_time_usecs}
259{i}{ii}tdev_create time secs: {create_time_secs} ({create_time})
260{i}{ii}tdev_create time usecs: {create_time_usecs}
261{i}{ii}tdev_create_id: {create_id}
262{i}{ii}tdev_sc_id: {sc_id}
263{i}{ii}tdev_gs_id: {gs_id}
264{i}{ii}tdev_size: {size}
265{i}{ii}tdev_s2k_header_type: {s2k_header_type.value} ({s2k_header_type.name})
266{i}{ii}tdev_version: {version}
267{i}{ii}tdev_spare_flags: {spare_flags}
268{i}{ii}tdev_stream_id: {stream_id}
269{i}{ii}tdev_sequence_counter: {sequence_counter}
270{i}{ii}tdev_filing_key: {filing_key}
271{i}{ii}tdev_retr_key_1: {retr_key_1}
272{i}{ii}tdev_retr_key_2: {retr_key_2}
273{i}{ii}tdev_mission_id: {mission_id}
274{i}{ii}tdev_context_id: {context_id}
275{i}{ii}tdev_domain_id: {domain_id}
276{i}{ii}tdev_db_release: {db_release}
277{i}{ii}tdev_db_issue: {db_issue}
278{i}{ii}tdev_spare1: {spare1}
279""".format(
280    i=indent,
281    ii=indentation,
282    c_tree=self.tdev_c_tree,
283    access_flag=self.tdev_access_flag,
284    spare_simul_flag=self.tdev_spare_simul_flag,
285    filing_time_secs=self.tdev_filing_time_secs,
286    timestamp=self.timestamp,
287    filing_time_usecs=self.tdev_filing_time_usecs,
288    create_time_secs=self.tdev_create_time_secs,
289    create_time_usecs=self.tdev_create_time_usecs,
290    create_time=datetime.utcfromtimestamp(self.tdev_create_time_secs).replace(
291        microsecond=self.tdev_create_time_usecs),
292    create_id=self.tdev_create_id,
293    sc_id=self.tdev_sc_id,
294    gs_id=self.tdev_gs_id,
295    size=self.tdev_size,
296    s2k_header_type=self.tdev_s2k_header_type,
297    version=self.tdev_version,
298    spare_flags=self.tdev_spare_flags,
299    stream_id=self.tdev_stream_id,
300    sequence_counter=self.tdev_sequence_counter,
301    filing_key=self.tdev_filing_key,
302    retr_key_1=self.tdev_retr_key_1,
303    retr_key_2=self.tdev_retr_key_2,
304    mission_id=self.tdev_mission_id,
305    context_id=self.tdev_context_id,
306    domain_id=self.tdev_domain_id,
307    db_release=self.tdev_db_release,
308    db_issue=self.tdev_db_issue,
309    spare1=self.tdev_spare1)
310
311
312class TDEVTMHeader(Segment):
313    # Older data required 16 here. We don't have a way to detect the correct
314    # header length per-product
315    ### project dependant   LENGTH = 20
316    # JS eps-sg REQUIRES THIS TO BE 16. Is it the padding feature? (+4Bytes if padding=1)
317    LENGTH = settings.TDEV_TM_HEADER_LENGTH
318
319
320class TDEVTCHeader(Segment):
321    """Fixed length part of the TDEV telecommand header. Also called TDEVSIItlcPkt.
322
323    Or TDEVSIItlcPktHdr."""
324    LENGTH = 44
325
326    @property
327    def uplink_time_s(self):
328        return unpack_uint32_be(self.buff, 0)
329
330    @property
331    def uplink_time_us(self):
332        return unpack_uint32_be(self.buff, 4)
333
334    @property
335    def uplink_time(self):
336        """Extract uplink time from TDEV TC header."""
337        try:
338            return datetime.utcfromtimestamp(self.uplink_time_s).replace(
339                microsecond=self.uplink_time_us)
340        except ValueError:
341            raise TDEVTimestampError('Cannot decode uplink time from {coarse}:{fine}'.format(
342                coarse=self.uplink_time_s, fine=self.uplink_time_us))
343
344    @property
345    def execut_time_s(self):
346        return unpack_uint32_be(self.buff, 8)
347
348    @property
349    def execut_time_us(self):
350        return unpack_uint32_be(self.buff, 12)
351
352    @property
353    def execut_time(self):
354        """Extract execut time from TDEV TC header."""
355        try:
356            return datetime.utcfromtimestamp(self.execut_time_s).replace(
357                microsecond=self.execut_time_us)
358        except ValueError:
359            raise TDEVTimestampError('Cannot decode uplink time from {coarse}:{fine}'.format(
360                coarse=self.execut_time_s, fine=self.execut_time_us))
361
362    @property
363    def last_update_time_s(self):
364        return unpack_uint32_be(self.buff, 16)
365
366    @property
367    def last_update_time_us(self):
368        return unpack_uint32_be(self.buff, 20)
369
370    @property
371    def last_update_time(self):
372        """Extract last update time from TDEV TC header."""
373        return datetime.utcfromtimestamp(self.last_update_time_s).replace(
374            microsecond=self.last_update_time_us)
375
376    @property
377    def request_id(self):
378        return unpack_uint32_be(self.buff, 24)
379
380    @property
381    def request_element_index(self):
382        return unpack_uint16_be(self.buff, 28)
383
384    @property
385    def variable_address_size(self):
386        return unpack_uint16_be(self.buff, 30)
387
388    @property
389    def pus_apid(self):
390        return unpack_uint16_be(self.buff, 32)
391
392    @property
393    def pus_source_sequence_counter(self):
394        return unpack_uint16_be(self.buff, 34)
395
396    @property
397    def pus_service_type(self):
398        return self.buff[36]
399
400    @property
401    def pus_service_subtype(self):
402        return self.buff[37]
403
404    @property
405    def pus_ack_flags(self):
406        return self.buff[38]
407
408    @property
409    def uplink_flag(self):
410        return self.buff[39]
411
412    @property
413    def command_source_host(self):
414        return self.buff[40]
415
416    @property
417    def command_source_type(self):
418        return self.buff[41]
419
420    @property
421    def request_details_fixed_size(self):
422        return unpack_uint16_be(self.buff, 42)
423
424    def show(self, indent, indentation):
425        """Display the Rapid Record TDEV Header format."""
426        try:
427            execut_time = self.execut_time
428        except TDEVTimestampError as e:
429            execut_time = e.short_message
430
431        return """{i}TDEV TC header:
432{ii}uplink_time: {self.uplink_time_s}/{self.uplink_time_us} ({self.uplink_time})
433{ii}execut_time: {self.execut_time_s}/{self.execut_time_us} ({execut_time})
434{ii}last_update_time: {self.last_update_time_s}/{self.last_update_time_us} ({self.last_update_time})
435{ii}request_id: {self.request_id}
436{ii}request_element_index: {self.request_element_index}
437{ii}variable_address_size: {self.variable_address_size}
438{ii}pus_apid: {self.pus_apid}
439{ii}pus_source_sequence_counter: {self.pus_source_sequence_counter}
440{ii}pus_service_type: {self.pus_service_type}
441{ii}pus_service_subtype: {self.pus_service_subtype}
442{ii}pus_ack_flags: {self.pus_ack_flags}
443{ii}uplink_flag: {self.uplink_flag}
444{ii}command_source_host: {self.command_source_host}
445{ii}request_details_fixed_size: {self.request_details_fixed_size}
446""".format(
447    i=indent,
448    ii=indent + indentation,
449    self=self,
450    execut_time=execut_time,
451    )
452
453
454class TDEVTCVarHeader(Segment):
455    """Also named CMDrqstPktDataDetails in documentation.
456
457    Taken from SCOS-2000 Commanding Architectural Design Document Appendix A, from p231,
458    definition for release 3.1.
459    also
460    EOMCS Command Source Handler - Delta ICD (1084998 V1A)."""
461
462    def __init__(self, buff, tdev_tc_header):
463        self.buff = buff
464        self.tdev_tc_header = tdev_tc_header
465        # Some parameters are stored in a separate segment of the original buffer,
466        # in a potentially mission-specific offset
467        self.extended_buff = buff[EXTENDED_BUFFER_OFFSET:]
468
469    @property
470    def command_name(self):
471        """Name of telecommand.
472
473        This field is defined as 8 chars + null terminator so we only need to read 8 bytes,
474        but we pull in 9 fir clarity and to help detect bad values."""
475        return self.buff[0:9].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
476
477    @property
478    def command_description(self):
479        """Description of telecommand."""
480        return self.buff[9:34].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
481
482    @property
483    def parent_seq_id(self):
484        """Name of parent sequence."""
485        return self.buff[34:43].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
486
487    @property
488    def destination_id(self):
489        """Destination ID."""
490        return self.buff[43:74].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
491
492    @property
493    def command_type(self):
494        """Type identifier."""
495        return self.buff[74:83].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
496
497    @property
498    def subsystem(self):
499        """Subsystem identifier."""
500        return self.buff[83]
501
502    @property
503    def group_block_info(self):
504        """Encoded block indicating whether start/middle/end of group or block. Only 6 LSB used."""
505        return self.buff[84]
506
507    @property
508    def source_id(self):
509        """Command Source Identifier used to pass back PTV/CEV notifications."""
510        return unpack_uint16_be(self.buff, 85)
511
512    @property
513    def interlock_type(self):
514        """Indicating NONE, LOCAL, GLOBAL, SUBSYSTEM."""
515        return self.buff[87]
516
517    @property
518    def interlock_stage_id(self):
519        """Identifier of verification stage definition to which command is interlocking to."""
520        return unpack_uint16_be(self.buff, 88)
521
522    @property
523    def interlock_stage_type(self):
524        """Type of stage, e.g. Acceptance, Completion."""
525        return self.buff[90]
526
527    @property
528    def notification_info(self):
529        """Flags indicating whether notification is required by sources for PTV/CEV."""
530        return self.buff[91]
531
532    @property
533    def check_states(self):
534        """Encoded block indicating whether the PTV/CEV checks are enabled, disabled or overridden.
535
536        Only 6 LSB used."""
537        return self.buff[92]
538
539    @property
540    def ptv_status(self):
541        """Encoded block indicating the status of each PTV check."""
542        return unpack_uint16_be(self.extended_buff, 93)
543
544    @property
545    def overall_verification_status(self):
546        """Overall verification status.
547
548        Indicating which different stage statuses request currently has."""
549        return unpack_uint16_be(self.extended_buff, 95)
550
551    @property
552    def num_params(self):
553        """Number of command parameters in this request."""
554        return unpack_uint16_be(self.extended_buff, 97)
555
556    @property
557    def request_data_size(self):
558        """Total size (in bytes) of this request, including the encoded spacecraft data."""
559        return unpack_uint16_be(self.extended_buff, 99)
560
561    @property
562    def load_tt_apid(self):
563        """APID value for the load time-tagged wrapper (if applicable)."""
564        return unpack_uint16_be(self.extended_buff, 101)
565
566    @property
567    def load_tt_seq(self):
568        """Source sequence count value for the load time-tagged wrapper (if applicable)."""
569        return unpack_uint16_be(self.extended_buff, 103)
570
571    @property
572    def obq_info(self):
573        """Encoded block indicating whether this request is timetagged.
574        And whether it is an OBQ management command (e.g. delete, reset, etc.)."""
575        return self.extended_buff[105]
576
577    @property
578    def enc_exec_time(self):
579        """Encoded execution time (in OBT format).
580        Only applicable if time-tagged command."""
581        return self.extended_buff[106:106+8].buff
582
583    @property
584    def hdr_format(self):
585        """Mission specific packet header format identifier."""
586        return self.extended_buff[114]
587
588    @property
589    def service_mode(self):
590        """TC Service mode (Packet/CLTU)."""
591        return self.extended_buff[115]
592
593    @property
594    def uplink_mode(self):
595        """TCUplink mode (AD/BD)."""
596        return self.extended_buff[116]
597
598    @property
599    def vc_id(self):
600        """Virtual Channel Id."""
601        return self.extended_buff[117]
602
603    @property
604    def map_id(self):
605        """VMAP Id."""
606        return self.extended_buff[118]
607
608    @property
609    def frame_type(self):
610        """Frame type (AD, BD, BC). Only appicable for CLTU mode."""
611        return self.extended_buff[119]
612
613    @property
614    def frame_count(self):
615        """Frame count used by this TC. Only appicable for CLTU mode."""
616        return self.extended_buff[120]
617
618    @property
619    def num_exec_ver_stages(self):
620        """Number of execution verification stages in this request."""
621        return self.extended_buff[121]
622
623    @property
624    def ack_flags(self):
625        """Acknowledgement flags."""
626        return self.extended_buff[122]
627
628    @property
629    def ver_states(self):
630        """Array of verification stage statuses."""
631        # Array of 31 elements of 2 bytes shorts
632        return self.extended_buff[123:123+62].buff
633
634    @property
635    def tt_obq_status(self):
636        """Onboard queue status for time-tagged command request."""
637        return unpack_uint16_be(self.extended_buff, 185)
638
639    @property
640    def optional_param_info(self):
641        """Optional parameter field info."""
642        return unpack_uint32_be(self.extended_buff, 187)
643
644    @property
645    def parameter_details_data_size(self):
646        """Length of variable parameter details data."""
647        return unpack_uint16_be(self.extended_buff, 191)
648
649    @property
650    def encoded_data_size(self):
651        """Length of encoded data stored in this request."""
652        return unpack_uint16_be(self.extended_buff, 193)
653
654    @property
655    def tc_request_id(self):
656        """External Telecommand request ID."""
657        return unpack_uint32_be(self.extended_buff, 195)
658
659    @property
660    def obqd_id(self):
661        """To identify which on-board queue model applicable to."""
662        return unpack_uint16_be(self.extended_buff, 199)
663
664    @property
665    def subschedule_id(self):
666        """To identify which subschedule, if any, applicable to."""
667        return unpack_uint32_be(self.extended_buff, 205) # 201 )
668
669    @property
670    def tt_load_status(self):
671        """Current load status of timetagged command."""
672        return self.extended_buff[205 + 4]
673
674    @property
675    def tt_del_status(self):
676        """Current delete status of timetagged command."""
677        return self.extended_buff[206 + 4]
678
679    @property
680    def predicted_time(self):
681        """Predicted time of execution of command."""
682        return unpack_uint32_be(self.extended_buff, 207 + 4)
683
684    @property
685    def delete_apid(self):
686        """APID of delete command affecting this command (tt only)."""
687        return unpack_uint16_be(self.extended_buff, 211 + 4)
688
689    @property
690    def delete_ssc(self):
691        """SSC of delete command affecting this command (tt only)."""
692        return unpack_uint16_be(self.extended_buff, 213 + 4)
693
694    @property
695    def delete_release_time(self):
696        """Release time of delete command affecting this command (tt only)."""
697        return unpack_uint32_be(self.extended_buff, 215 + 4)
698
699
700#     For MTG none of this section is verified, and byte locations do not fully match
701#    unclear documentation
702#    for JCS, S3 best match for ops_angle and ops_orbit. No match for abs_ops_orbit
703#    (which may not be set anyway) and limited matching of othe parameters
704    @property
705    def action_cmd_id(self):
706        """ID of associated action command."""
707        # introduction of further 9 byte offset
708        return self.extended_buff[219+9 : 228+9].decode(ASCII_CHARSET).rstrip(NULL_CHAR)
709
710    @property
711    def ops_angle(self):
712        """OPS info - Orbit angle at command execution."""
713        return settings.OPS_ANGLE_FACTOR * (unpack_uint32_be(self.extended_buff, settings.TDEVTC_OPS_DATA_OFFSET))
714
715    @property
716    def ops_orbit(self):
717        """OPS info - Relative Orbit at command execution."""
718        return unpack_uint32_be(self.extended_buff, 232+9)
719
720    # Not applicable for S3 - Hence -4 applied for following parameters
721    @property
722    def abs_ops_orbit(self):
723        """OPS info - Absolute Orbit at command execution."""
724        return unpack_uint32_be(self.extended_buff, 236+9)
725    # Not applicable for S3 - Hence -4 applied for following parameters
726
727    @property
728    def is_MRC(self):
729        """Flag indicating execution of a repetitive schedule of commands, to be repeated every repeat cycle."""
730        return self.extended_buff[settings.TDEVTC_MRC_DATA_OFFSET]
731
732    @property
733    def is_reinserted(self):
734        """Flag indicating that after they have executed they are re-inserted in the command stack."""
735        return self.extended_buff[settings.TDEVTC_MRC_DATA_OFFSET + 1]
736
737    @property
738    def next_reinserted(self):
739        """."""
740        return self.extended_buff[settings.TDEVTC_MRC_DATA_OFFSET + 2]
741
742    @property
743    def trans_count(self):
744        """Re-transmission number."""
745        return unpack_uint16_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 3)
746
747    @property
748    def key_type(self):
749        """Authentication Data - keyType."""
750        return unpack_uint16_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 5)
751
752    @property
753    def secure_cmd_mode(self):
754        """Authentication Data - Secure commanding mode."""
755        return unpack_uint16_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 7)
756
757    @property
758    def LAC(self):
759        """Authentication Data - LAC."""
760        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 9)
761
762    @property
763    def MAC(self):
764        """Authentication Data - MAC."""
765        return self.extended_buff[settings.TDEVTC_MRC_DATA_OFFSET + 13 : settings.TDEVTC_MRC_DATA_OFFSET + 29].hex() #decode(ASCII_CHARSET).rstrip(NULL_CHAR)
766
767    @property
768    def no_of_segments(self):
769        """Number of TC Segments the command has been sent in."""
770        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 29)
771
772    @property
773    def has_map_id(self):
774        """True if command has a associated MAP ID."""
775        return self.extended_buff[settings.TDEVTC_MRC_DATA_OFFSET + 33]
776
777    @property
778    def pid(self):
779        """PID."""
780        return unpack_uint16_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 34)
781
782    @property
783    def cat(self):
784        """CAT."""
785        return unpack_uint16_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 36)
786
787    @property
788    def enc_acc_exec_rep_time_s(self):
789        """Encoded acceptance time data."""
790        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 38)
791
792    @property
793    def enc_acc_exec_rep_time_us(self):
794        """Encoded acceptance time data."""
795        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 42)
796
797    @property
798    def enc_acc_exec_rep_time(self):
799        """Encoded acceptance time data."""
800        return datetime.utcfromtimestamp(self.enc_acc_exec_rep_time_s).replace(
801            microsecond=self.enc_acc_exec_rep_time_us)
802
803    @property
804    def enc_start_exec_rep_time_s(self):
805        """Encoded start execution time data."""
806        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 46)
807    @property
808    def enc_start_exec_rep_time_us(self):
809        """Encoded start execution time data."""
810        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 50)
811
812    @property
813    def enc_start_exec_rep_time(self):
814        """Encoded start execution time data."""
815        return datetime.utcfromtimestamp(self.enc_start_exec_rep_time_s).replace(
816            microsecond=self.enc_start_exec_rep_time_us)
817
818    @property
819    def enc_prog_exec_rep_time_s(self):
820        """Encoded progress execution time data."""
821        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 54)
822
823    @property
824    def enc_prog_exec_rep_time_us(self):
825        """Encoded progress execution time data."""
826        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 58)
827
828    @property
829    def enc_prog_exec_rep_time(self):
830        """Encoded progress execution time data."""
831        return datetime.utcfromtimestamp(self.enc_prog_exec_rep_time_s).replace(
832            microsecond=self.enc_prog_exec_rep_time_us)
833
834    @property
835    def enc_end_exec_rep_time_s(self):
836        """Encoded end execution time data."""
837        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 62)
838
839    @property
840    def enc_end_exec_rep_time_us(self):
841        """Encoded end execution time data."""
842        return unpack_uint32_be(self.extended_buff, settings.TDEVTC_MRC_DATA_OFFSET + 66)
843
844    @property
845    def enc_end_exec_rep_time(self):
846        """Encoded end execution time data."""
847        return datetime.utcfromtimestamp(self.enc_end_exec_rep_time_s).replace(
848            microsecond=self.enc_end_exec_rep_time_us)
849
850    @property
851    def variable_data(self):
852        """Variable Command data holding num_param times the CMDparam stucture,
853        see EGOS-MCS-S2K-ICD-0016, CMDdataDetails."""
854        # extract variable command data
855        return self.buff[self.tdev_tc_header.request_details_fixed_size : len(self.buff)].buff
856
857    @property
858    def ext_source_id(self):
859        """External procedure source id."""
860        return unpack_uint32_be(self.buff, self.tdev_tc_header.request_details_fixed_size - 4)
861
862    def show(self, indent, indentation):
863        """Display the Rapid Record TDEV Header Variable"""
864
865        # Only applicable to polar orbiting missions: taken from products/events/tc_events.py
866        if self.ops_angle is None or (self.ops_angle == 0.0 and  self.ops_orbit == 0):
867            mcr_flags = ""
868        else:
869            mcr_flags = """
870{ii}ops_angle: {self.ops_angle}
871{ii}ops_orbit: {self.ops_orbit}
872{ii}abs_ops_orbit: {self.abs_ops_orbit}
873{ii}ext_source_id: {self.ext_source_id}
874{ii}is_MRC: {self.is_MRC} {ii}is_reinserted: {self.is_reinserted} {ii}next_reinserted: {self.next_reinserted}
875                    """.format(ii=indent+indent, self=self)
876
877        return """{i}TDEV TC Variable header:
878{ii}size: {size}
879{ii}command_name: {self.command_name}
880{ii}command_description: {self.command_description}
881{ii}parent_seq_id: {self.parent_seq_id}
882{ii}destination_id: {self.destination_id}
883{ii}command_type: {self.command_type}
884{ii}subsystem: {self.subsystem}
885{ii}group_block_info: {self.group_block_info}
886{ii}source_id: {self.source_id}
887{ii}interlock_type: {self.interlock_type}
888{ii}interlock_stage_id: {self.interlock_stage_id}
889{ii}interlock_stage_type: {self.interlock_stage_type}
890{ii}notification_info: {self.notification_info}
891{ii}check_states: {self.check_states}
892{ii}ptv_status: {self.ptv_status}
893{ii}overall_verification_status: {self.overall_verification_status}
894
895{ii}service_mode: {self.service_mode}
896{ii}uplink_mode: {self.uplink_mode}
897{ii}vc_id: {self.vc_id}
898{ii}map_id: {self.map_id}
899{ii}frame_type: {self.frame_type}
900{ii}frame_count: {self.frame_count}
901{ii}num_exec_ver_stages: {self.num_exec_ver_stages}
902{ii}ack_flags: {self.ack_flags}
903{ii}num_params: {self.num_params}
904{ii}request_data_size: {self.request_data_size}
905{ii}parameter_details_data_size: {self.parameter_details_data_size}
906{ii}encoded_data_size: {self.encoded_data_size}
907{ii}Sub-Schedule Id: {self.subschedule_id}
908
909{ii}tt_obq_status: {self.tt_obq_status}
910{ii}tt_load_status: {self.tt_load_status}
911{ii}tt_del_status: {self.tt_del_status}
912{ii}predicted_time: {self.predicted_time}
913{ii}delete_apid: {self.delete_apid}
914{ii}delete_ssc: {self.delete_ssc}
915{ii}delete_release_time: {self.delete_release_time}
916{ii}action_cmd_id: {self.action_cmd_id}
917{flags}
918""".format(i=indent,
919           ii=indent+indentation,
920           size=len(self.buff),
921           flags=mcr_flags,
922           self=self)
923
924# Include above, when required for detailed investigations...
925# {ii}is_MRC: {self.is_MRC}
926# {ii}is_reinserted: {self.is_reinserted}
927# {ii}next_reinserted: {self.next_reinserted}
928# {ii}trans_count: {self.trans_count}
929# {ii}key_type: {self.key_type}
930# {ii}secure_cmd_mode: {self.secure_cmd_mode}
931
932# {ii}LAC: {self.LAC}
933# {ii}MAC: {self.MAC}
934# {ii}no_of_segments: {self.no_of_segments}
935# {ii}has_map_id: {self.has_map_id}
936# {ii}pid: {self.pid}
937# {ii}cat: {self.cat}
938
939# {ii}optional_param_info: {self.optional_param_info}
940# {ii}ver_states: {self.ver_states}
941# {ii}variable_data: {self.variable_data}
942
943
944class TDEVTCCevPtvDetails(Segment):
945    """Taken from SCOS-2000 Commanding Architectural Design Document Appendix B,
946    definition for release 3.1."""
947
948    @property
949    def version_major(self):
950        """Major Version number."""
951        return self.buff[0]
952
953    @property
954    def version_minor(self):
955        """Minor Version number."""
956        return self.buff[1]
957
958    @property
959    def num_verification_results(self):
960        """Identifier of verification stage definition to which command is interlocking to."""
961        return unpack_uint16_be(self.buff, 2)
962
963    def show(self, indent, hex=False):
964        """Display the TDEV TC Cev/Ptv Details."""
965        return """{i}TDEV TC Cev/Ptv Details :
966{ii}size: {size}
967{ii}version_major: {self.version_major}
968{ii}version_minor: {self.version_minor}
969{ii}num_verification_results: {self.num_verification_results}{hex}
970""".format(i=indent,
971           ii=indent+indent,
972           size=len(self.buff),
973           self=self,
974           hex='\n{ii}Hex Dump: {hex}'.format(ii=indent+indent, hex=self.hex_dump()) if hex else '')
975
976    def show_single(self):
977        """Return a brief one-line representation of ourselves."""
978        return 'size: {size}, '\
979            'version_major: {self.version_major}, '\
980            'version_minor: {self.version_minor}, '\
981            'num_verification_results: {self.num_verification_results}'.format(self=self, size = len(self.buff))