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))