1#!/usr/bin/env python3
  2
  3"""Implementation of TransferFrame class components common to all S-Band and X-Band definitions."""
  4
  5import sys
  6import logging
  7from enum import Enum
  8from collections import defaultdict
  9from binascii import hexlify
 10
 11from chart.common.binary import unpack_uint8
 12from chart.common.binary import unpack_uint16_be
 13from chart.common.binary import unpack_uint24_be
 14from chart.common.binary import unpack_uint32_be
 15from chart.common.binary import unpack_int32_be
 16from chart.products.pus.ccsds import CCSDSTM
 17from chart.products.pus.ccsds_displayer import INDENTATION
 18from chart.products.pus.ccsds import CCSDSBadHeader
 19from chart.products.utils import Segment
 20
 21class TFException(Exception):
 22    pass
 23
 24
 25class TFBadSize(TFException):
 26    pass
 27
 28
 29class TFUnrecognisedType(TFException):
 30    pass
 31
 32
 33class TFNoTimestamp(TFException):
 34    pass
 35
 36
 37class TF_ASM(Segment):
 38    """Representation of Attached Sync Marker."""
 39
 40    # Attached Sync Marker length, Bytes
 41    LENGTH = 4
 42
 43    @property
 44    def attached_sync_marker(self):
 45        """Get Attached Sync Marker."""
 46        return unpack_uint32_be(self, 0)
 47
 48    def show(self, target=sys.stdout, indent=''):
 49        """Display basic ASM information to stdout. Write to `csv_writer` if not None."""
 50        target.write("""
 51{indent}CADU Synchronisation Marker: {len} - {data}
 52""".format(
 53        indent=indent,
 54        len=len(self.buff),
 55        data=self.hex_dump()))
 56
 57
 58class TF_trailer(Segment):
 59    """Representation of a TF_trailer."""
 60
 61    # Transfer Frame Trailer length, Bytes
 62    LENGTH = 4
 63
 64    # @property
 65    # def TF_trailer(self):
 66        # return self.buff
 67
 68    def show(self, target=sys.stdout, indent=''):
 69        """Display basic TF_trailer HEX Dump information to stdout. Write to `csv_writer` if not None."""
 70        target.write("""
 71{indent}TF_trailer: {len} - {data}
 72""".format(
 73        indent=indent,
 74        len=len(self.buff),
 75        data=self.hex_dump()))
 76
 77
 78class PEC(Segment):
 79    """Representation of Packet Error Correction codes used in CCSDS packets."""
 80    LENGTH = 2
 81    def show(self, target=sys.stdout, indent=''):
 82        target.write('{i}PEC: {pec}\n'.format(i=indent, pec=self.hex_dump()))
 83
 84
 85class TF_error_control(Segment):
 86    """Representation of a TF_error_control Check, Reed Solomon Code."""
 87    # Frame Error Control Field Length, Reed Solomon Code
 88    LENGTH = 160
 89
 90    # Reed Solomon Check
 91    @property
 92    def reed_solomon(self):
 93        """Get Reed Solomon checksum codes."""
 94        # print(ord(self.buff[0]))
 95        return self.buff
 96
 97    def show(self, target=sys.stdout, indent=''):
 98        """Display basic TF_error_control HEX Dump information to stdout. Write to `csv_writer` if not None."""
 99        target.write("""
100{indent}TF_error_control: {len} - {data}
101""".format(
102        indent=indent,
103        len=len(self.buff),
104        data=self.hex_dump()))
105
106
107class MMDS_TF(Segment):
108    """Representation of an S3 X-Band Transfer Frame.
109
110    Holder of one, or mamy, or a partial, or one or many + a partial CCSDS packet.
111    """
112    # Display name for the frame displayer
113    classname = 'MMDS TF'
114
115    # M_PDU Packet Zone length, Bytes
116    PACKET_ZONE_LENGTH = 1902
117
118    # Transfer Frame Length, Bytes
119    #    MMDS_TF_PRIMARY_HEADER_LENGTH (8) + M_PDU_HEADER_LENGTH (2) + PACKET_ZONE_LENGTH (1902)
120    LENGTH = 1912
121
122    def __init__(self, buff, file_pos):
123        super(MMDS_TF, self).__init__(buff, file_pos)
124        # Our XBAND_TF_header class is identical to CHART-S3 project's
125        # TransferFrame class.
126        # It's unclear what the old MMDS_TF_HEader is
127        self.mmds_tf_header = XBAND_TF_header(buff[0 : XBAND_TF_header.LENGTH])
128        self.tf_packet_data = buff[XBAND_TF_header.LENGTH :
129                                   XBAND_TF_header.LENGTH + MMDS_TF.PACKET_ZONE_LENGTH]
130        # self.ccsds = CCSDSTM(buff[MMDS_TF_header.LENGTH:])
131
132    def __str__(self):
133        return 'TF({ver} / {sid} / {chan_id} - {chan_fr})'.format(
134                ver=self.mmds_tf_header.transfer_frame_ver,
135                sid=self.mmds_tf_header.space_craft_id,
136                chan_id=self.mmds_tf_header.channel_id,
137                chan_fr=self.mmds_tf_header.oper_control_flag)
138
139    def show(self, target=sys.stdout, indent=''):
140        """Display ourselves."""
141        self.mmds_tf_header.show(target, indent)
142
143    # Transfer Frame Primary Header ...
144    @property
145    def length(self):
146        return self.LENGTH
147
148    @property
149    def channel_id(self):
150        return self.mmds_tf_header.channel_id
151
152    @property
153    def oper_control_flag(self):
154        return self.mmds_tf_header.oper_control_flag
155
156    @property
157    def first_header_ptr(self):
158        return self.mmds_tf_header.first_header_ptr
159
160    @property
161    def packet_data(self):
162        return self.tf_packet_data
163
164
165class MMDS_TF_error_control(Segment):
166    """Representation of a MMDS TF_error_control Check, Reed Solomon Code."""
167
168    # TBD Not yet decoded
169
170    # MMDS Frame Error Control Field Length, Reed Solomon Code
171    LENGTH = 128
172
173    # Reed Solomon Check
174    @property
175    def reed_solomon(self):
176        """Get  Reed Solomon Check."""
177        # print(ord(self.buff[0]))
178        return self.buff
179
180    def show(self, target=sys.stdout, indent=''):
181        """Display basic MMDS TF_error_control HEX Dump information to stdout. Write to `csv_writer` if not None."""
182        target.write("""
183{indent}TF_error_control: {len} - {data}
184""".format(
185        indent=indent,
186        len=len(self.buff),
187        data=self.hex_dump()))
188
189
190class XBAND_TF_header(Segment):
191    """Representation of XBand  Transfer Frame Primary Header.
192
193    See Jason-CS Packet Utilisation Standard, JC.ID.ASG.SY.00001
194    """
195
196    # Transfer Frame Primary Header (8) + M_PDU Header length (2), Bytes
197    LENGTH = 10
198
199    # Transfer Frame Primary Header Fields...
200    @property
201    def transfer_frame_ver(self):
202        """Get Transfer Frame Version Number."""
203        return self.buff[0] >> 6                                # ^ Byte 0, bits 1 - 2
204
205    @property
206    def space_craft_id(self):
207        """Get Space Craft ID."""
208        return (unpack_uint16_be(self.buff, 0) >> 6) & 0x3ff    # ^ Byte 0, bits 3 - 10
209
210    @property
211    def channel_id(self):
212        """Get Channel ID."""
213        return unpack_uint16_be(self.buff, 0) & 0x3f            # ^ Byte 0, bits 11 - 16
214
215    @property
216    def channel_frame_cnt(self):
217        """Get Channel Frame Counter."""
218        return unpack_uint24_be(self.buff, 2)                     # ^ Byte 2, bits 1 - 24
219
220    @property
221    def replay_flag(self):
222        """Get Signal Field Replay Flag."""
223        return self.buff[5] >> 7                                 # ^ Byte 5, bit 1
224
225    @property
226    def reserve_spare(self):
227        """Get Signal Field Reserved Spare."""
228        return unpack_uint8(self.buff, 5) & 0x80                # ^ Byte 5, bits 2 - 8
229
230    @property
231    def header_error_con(self):
232        """Get Header Error Control."""
233        return unpack_uint16_be(self.buff, 6)                     # ^ Byte 6, bits 1 - 16
234
235
236    # Transfer Frame M_PDU Header Fields...
237    @property
238    def header_reserved(self):
239        """Get Transfer Frame M_PDU Header Reserved."""
240        return self.buff[8] >> 3                                 # ^ Byte 8, bits 1 - 5
241
242    @property
243    def first_header_ptr(self):
244        """Get Transfer Frame M_PDU Header First header pointer."""
245        return unpack_uint16_be(self.buff, 8) & 0x07ff         # ^ Byte 8, bits 6 - 16
246
247
248    def show(self, target=sys.stdout, indent=''):
249        """Display the Transfer Frame Primary Header format."""
250        target.write("""{i}Transfer Frame:
251{i}{ii}Transfer Frame Version Number: {ver}
252{i}{ii}Spacecraft ID: {sid}
253{i}{ii}Virtual Channel ID: {cid}
254{i}{ii}Virtual Channel Frame Count: {cfc}
255{i}{ii}Signalling Field - Replay Flag: {rf}
256{i}{ii}Signalling Field - Reserved Spare: {rs}
257{i}{ii}Frame Header Error Control: {hec}
258{i}{ii}TF M_PDU Header - Reserved: {hr}
259{i}{ii}TF M_PDU Header - First header pointer: {fhp}
260""".format(
261    i=indent,
262    ii=INDENTATION,
263    ver=self.transfer_frame_ver,
264    sid=self.space_craft_id,
265    cid=self.channel_id,
266    cfc=self.channel_frame_cnt,
267    rf=self.replay_flag,
268    rs=self.reserve_spare,
269    hec=self.header_error_con,
270    hr=self.header_reserved,
271    fhp=self.first_header_ptr))
272
273
274class XBand_TF(Segment):
275    """Representation of a single S3 X-Band Transfer Frame.
276
277    Holder of one, or many, or a partial, or one or many + a partial CCSDS packet.
278    """
279    # XBAND_TF_header(10b) + TF_packet_data(1105b)
280    LENGTH = 1115
281
282    # Payload
283    PACKET_ZONE_LENGTH = 1105
284
285    # For frame displayer
286    classname = 'XBand TF'
287
288    def __init__(self, buff, file_pos=None):
289        super(XBand_TF, self).__init__(buff, file_pos)
290        # self.tdev_tc_header = None    # do we need this?
291        self.tf_header = XBAND_TF_header(buff[0: XBAND_TF_header.LENGTH])
292        self.tf_packet_data = buff[
293            XBAND_TF_header.LENGTH: XBAND_TF_header.LENGTH + XBand_TF.PACKET_ZONE_LENGTH]
294        # self.ccsds = CCSDSTM(
295            # buff[XBAND_TF_header.LENGTH : XBAND_TF_header.LENGTH + XBand_TF.PACKET_ZONE_LENGTH])
296
297    def __str__(self):
298        return 'TF({ver} / {sid} / {chan_id} - {frame_cnt})'.format(
299                ver=self.tf_header.transfer_frame_ver,
300                sid=self.tf_header.space_craft_id,
301                chan_id=self.tf_header.channel_id,
302                frame_cnt=self.tf_header.channel_frame_cnt)
303
304    def show(self, target=sys.stdout, indent=''):
305        """Display ourselves."""
306        self.tf_header.show(target, indent)
307
308    @property
309    def length(self):
310        return self.LENGTH
311
312    @property
313    def channel_id(self):
314        return self.tf_header.channel_id
315
316    @property
317    def channel_frame_cnt(self):
318        return self.tf_header.channel_frame_cnt
319
320    @property
321    def first_header_ptr(self):
322        return self.tf_header.first_header_ptr
323
324    @property
325    def packet_data(self):
326        return self.tf_packet_data
327
328
329class CORTEXHeader(Segment):
330    """Representation of a CORTEX Header.
331
332    Implementation of S-Band TransferFrame class.
333    See Packet Utilisation Standard,
334    Represents S3 S-Band NDIU Cortex Frame containing Transfer Frame.
335    The Cortex .tlm files contain the full NDIU Cortex Frame records.
336    """
337
338    # CORTEX Header Size, Bytes this is a fixed length header block
339    LENGTH = 64
340
341    # @property
342    # def CORTEX_header(self):
343        # return self.buff
344
345    @property
346    def message_length(self):
347        """Read our message length field and convert to bytes."""
348        # print('cortex header message length', unpack_uint24_be(self.buff, 0))
349        # for a in range(12):
350            # print(a, self.buff[a])
351        # 1/0
352        # This does not really match the spec in the document above
353        return unpack_uint32_be(self.buff, 4)
354
355    def show(self, target=sys.stdout, indent=''):
356        """Display basic cortex header HEX Dump information to stdout. Write to `csv_writer` if not None."""
357        target.write("""
358{indent}CORTEXHeader : {len} - {data}
359""".format(
360        indent=indent,
361        len=len(self.buff),
362        data=self.hex_dump()))
363
364
365class SBAND_TF_header(Segment):
366    """Representation of AOS Transfer Trame (VDCU) Primary Header.
367
368    See above for ASM Class
369    """
370
371    # this is a fixed length header block
372    LENGTH = 6
373
374    # Transfer Frame Primary Header ...
375    @property
376    def transfer_frame_ver(self):
377        """Get Transfer Frame Version Number."""
378        return self.buff[0] >> 6
379
380    @property
381    def space_craft_id(self):
382        """Get Space Craft ID."""
383        return (unpack_uint16_be(self.buff, 0) >> 4) & 0x3ff
384
385    @property
386    def channel_id(self):
387        """Get Channel ID."""
388        return (unpack_uint16_be(self.buff, 0) & 0x0f) >> 1
389
390    @property
391    def oper_control_flag(self):
392        """Get Operation Control Flag."""
393        return unpack_uint16_be(self.buff, 0) & 0x1
394
395    @property
396    def master_channel_frame_cnt(self):
397        """Get Master Channel Frame Counter."""
398        return self.buff[2]
399
400    @property
401    def virtual_channel_frame_cnt(self):
402        """Get Master Channel Frame Counter."""
403        return self.buff[3]
404
405    @property
406    def sec_header_flag(self):
407        """Get Secondary Header Flag."""
408
409        # ^ Byte 4, bit 1,  move right 15, & 0b1
410        return (unpack_uint16_be(self.buff, 4) >> 15) & 0b1
411
412    @property
413    def data_field_sync_flag(self):
414        """Get Transfer Frame data fields synchronise flag."""
415        # ^ Byte 4 bit 2, move right 14 & 0b1
416        return (unpack_uint16_be(self.buff, 4) >> 14) & 0b1
417
418    @property
419    def packet_order_flag(self):
420        """Get Transfer Frame packet order flag."""
421        # ^ Byte 4, bit 3, move right 13 & 0b1
422        return (unpack_uint16_be(self.buff, 4) >> 13) & 0b1
423
424    @property
425    def seg_length_id(self):
426        """Get Transfer Frame segment length ID."""
427        # ^ Byte 4, bits 4 - 5, move right 11 & 0b11
428        return (unpack_uint16_be(self.buff, 4) >> 11) & 0b11
429
430    @property
431    def first_header_ptr(self):
432        """Get Transfer Frame CCSDS First header pointer."""
433        # ^ Byte 4, bits 6 - 16
434        return unpack_uint16_be(self.buff, 4) & 0b11111111111
435
436    def show(self, target=sys.stdout, indent=''):
437        """Display the Transfer Frame Primary Header format."""
438        target.write("""{i}Transfer Frame:
439{i}{ii}Transfer Frame Version Number: {ver}
440{i}{ii}Space Craft ID: {sid}
441{i}{ii}Virtual Channel ID: {chan_id}
442{i}{ii}Operational Control Field Flag: {op_flag}
443{i}{ii}Master Channel Frame Counter: {master}
444{i}{ii}Virtual Channel Frame Counter: {virtual}
445{i}{ii}{ii}TF Secondary Header Flag: {h_sec}
446{i}{ii}{ii}TF Data Field Sync Flag: {h_syn}
447{i}{ii}{ii}TF Packet Order Flag: {h_po}
448{i}{ii}{ii}TF Segment Length ID: {h_seg}
449{i}{ii}{ii}TF CCSDS - First header pointer: {h_ptr}
450""".format(i=indent,
451           ii=INDENTATION,
452           indentation=INDENTATION,
453           ver=self.transfer_frame_ver,
454           sid=self.space_craft_id,
455           chan_id=self.channel_id,
456           op_flag=self.oper_control_flag,
457           master=self.master_channel_frame_cnt,
458           virtual=self.virtual_channel_frame_cnt,
459           h_sec=self.sec_header_flag,
460           h_syn=self.data_field_sync_flag,
461           h_po=self.packet_order_flag,
462           h_seg=self.seg_length_id,
463           h_ptr=self.first_header_ptr))
464
465    def write(self, handle):
466        """Store the (possibly modified) CCSDS packet to `handle` as a raw packet, no header."""
467        # print('writing {len} bytes'.format(len=self.length))
468        length = self.length
469        handle.write(self.buff[:length])
470        return length
471
472
473class CORTEX_trailer(Segment):
474    """Representation of a CORTEX_Trailer."""
475
476    # length of this block depends on GS, see settings.CORTEXt_GS_ATTRIBUTES
477    # LENGTH_MIN = 4
478    # LENGTH_MAX = 5
479
480    # @property
481    # def CORTEX_trailer(self):
482        # return self.buff
483
484    @property
485    def postamble(self):
486        return unpack_int32_be(self.buff, 0)
487
488    def show(self, target=sys.stdout, indent=''):
489        """Display basic CORTEX_trailer HEX Dump information to stdout. Write to `csv_writer` if not None."""
490        target.write("""
491{indent}CORTEX_trailer: {len} - {data}
492""".format(
493        indent=indent,
494        len=len(self.buff),
495        data=self.hex_dump()))
496
497
498class SBandTMTF(Segment):
499    """S-band Telemetry Transfer Frame."""
500
501    # 6 + 1103 + 4 + 2
502    VCDU_LENGTH = 1103
503    PACKET_LENGTH = VCDU_LENGTH + PEC.LENGTH
504
505    LENGTH = SBAND_TF_header.LENGTH + PACKET_LENGTH + TF_trailer.LENGTH
506
507    def __init__(self, buff, file_pos=None):
508        super(SBandTMTF, self).__init__(buff, file_pos)
509        self.tf_header = SBAND_TF_header(buff[0:SBAND_TF_header.LENGTH])
510        self.payload = TF_packet_data(
511            buff[SBAND_TF_header.LENGTH:
512                 SBAND_TF_header.LENGTH + SBandTMTF.VCDU_LENGTH + PEC.LENGTH])
513        self.ccsds = CCSDSTM(buff[SBAND_TF_header.LENGTH:
514                                  SBAND_TF_header.LENGTH + SBandTMTF.VCDU_LENGTH + PEC.LENGTH])
515        self.pec = PEC(buff[SBAND_TF_header.LENGTH + SBandTMTF.VCDU_LENGTH:
516                            SBAND_TF_header.LENGTH + SBandTMTF.VCDU_LENGTH + PEC.LENGTH])
517
518    def show(self, target=sys.stdout, indent=''):
519        """Display basic ASM information to stdout. Write to `csv_writer` if not None."""
520        self.tf_header.show(target, indent)
521        self.pec.show(target, indent)
522
523    @property
524    def channel_id(self):
525        return self.tf_header.channel_id
526
527    @property
528    def first_header_ptr(self):
529        return self.tf_header.first_header_ptr
530
531    @property
532    def packet_data(self):
533        return self.payload
534
535
536class CADU(Segment):
537    """Represent an S-BAND CADU frame."""
538
539    #  Frame, Channel Access Data Unit (CADU) length, Bytes
540    #  ASM (4) + TF_LENGTH (1115) + EC-RS (160)
541
542    # 4 + 1115 + 160
543    VCDU_LENGTH = 1103
544    RS_LENGTH = 160
545    LENGTH = TF_ASM.LENGTH + SBAND_TF_header.LENGTH + VCDU_LENGTH + TF_trailer.LENGTH + PEC.LENGTH + RS_LENGTH
546
547    def __init__(self, buff, file_pos=None):
548        super(CADU, self).__init__(buff, file_pos)
549        self.sync_marker = TF_ASM(buff[0:TF_ASM.LENGTH])
550        self.packet_data = TF_packet_data(buff[SBAND_TF_header.LENGTH:
551                                               SBAND_TF_header.LENGTH + CADU.VCDU_LENGTH + PEC.LENGTH])
552        self.reed_solomon = TF_error_control(buff[SBAND_TF_header.LENGTH + CADU.VCDU_LENGTH + PEC.LENGTH:
553                                        SBAND_TF_header.LENGTH + CADU.VCDU_LENGTH + PEC.LENGTH + CADU.RS_LENGTH])
554
555    def show(self, target=sys.stdout, indent=''):
556        """Display basic ASM information to stdout. Write to `csv_writer` if not None."""
557        self.sync_marker.show(target, indent)
558        self.packet_data.show(target, indent)
559        self.reed_solomon.show(target, indent)
560
561
562class CORTEXFrame(Segment):
563    """Representation of a single Cortex record holding a Transfer Frame.
564
565    For Cortex definition see
566    COMMAND RANGING & TELEMETRY UNIT CORTEX Series TCP-IP ETHERNET INTERFACE
567    https://dmtool.eumetsat.int/cs/idcplg?IdcService=EUM_GET_FILE&dRevLabel=2&dDocName=371846&allowInterrupt=1
568    (ยง2.4.5, p100).
569    """
570    label = 'Cortex frame'
571
572    # CORTEX_TF (multiples of 4 bytes)
573    #  CORTEX_header (64 byte)
574    #  TF_ASM sync marker (4 byte)
575    #  SBAND_TF_header VCDU header (6 byte)
576    #  bytes VCDU payload (1103 byte)
577    #  TF_trailer (4 byte)
578    #  TF_error_control (160 byte)
579    #  CORTEX_trailer (variable, 4 or 5 byte)
580
581    # CORTEX_TF_LENGTH_MIN = CORTEX_header.LENGTH + CADU.LENGTH + CORTEX_trailer.LENGTH_MIN
582    # CORTEX_TF_LENGTH_MAX = CORTEX_header.LENGTH + CADU.LENGTH + CORTEX_trailer.LENGTH_MAX
583
584    def __init__(self, buff, file_pos, product_id=None):  # buff, cortex_trailer_len=CORTEX_trailer.LENGTH_MAX):
585        super().__init__(buff, file_pos, product_id)
586        start = 0
587
588        self.cortex_header = CORTEXHeader(buff[start : start + CORTEXHeader.LENGTH])
589        end = self.cortex_header.message_length
590        # print('end starts at', end)
591        start += CORTEXHeader.LENGTH
592
593        self.tf_asm = TF_ASM(buff[start : start + TF_ASM.LENGTH])
594        start += TF_ASM.LENGTH
595
596        self.tf_header = SBAND_TF_header(buff[start : start + SBAND_TF_header.LENGTH])
597        start += self.tf_header.LENGTH
598
599        # Jump to the end and read the CORTEX postamble
600        self.cortex_trailer = CORTEX_trailer(buff[end - 4:end])
601        # print('cortex trailer int', unpack_int32_be(self.cortex_trailer.buff,0))
602        end -= 4
603
604        # Reed solomon bytes which we don't check
605        self.tf_error_control = TF_error_control(buff[end - TF_error_control.LENGTH: end])
606        end -= TF_error_control.LENGTH
607
608        # ?
609        end -= 7
610
611        # self.tf_trailer = TF_trailer(buff[end - TF_trailer.LENGTH: end])
612        # print('cortex tf trailer', self.tf_trailer.buff[0], self.tf_trailer.buff[1], self.tf_trailer.buff[2], self.tf_trailer.buff[3])
613        # end -= TF_trailer.LENGTH
614
615        self.tf_packet_data = buff[start : end]
616        # print('tf packet data', start, end, len(self.tf_packet_data))
617
618    def __str__(self):
619        return 'Cortex_TF({ver} / {sid} / {chan_id} - {chan_fr})'.format(
620                ver=self.tf_header.transfer_frame_ver,
621                sid=self.tf_header.space_craft_id,
622                chan_id=self.tf_header.channel_id,
623                chan_fr=self.tf_header.oper_control_flag)
624
625    @property
626    def length(self):
627        """Compute length of packet based on CORTEX header."""
628        # return CORTEXHeader.LENGTH + TF_ASM.LENGTH + SBAND_TF_header.LENGTH +\
629            # len(self.tf_packet_data) + TF_trailer.LENGTH + TF_error_control.length +\
630            # CORTEX_trailer.LENGTH
631        return self.cortex_header.message_length
632
633    @property
634    def channel_id(self):
635        # print('cortex tf retrieving channel id')
636        res = self.tf_header.channel_id
637        # print('cortex tf got channel id', res)
638        return res
639
640    @property
641    def first_header_ptr(self):
642        return self.tf_header.first_header_ptr
643
644    @property
645    def packet_data(self):
646        return self.tf_packet_data  # .packet_data
647
648    def show(self, target=sys.stdout, indent=''):
649        """Display basic cortex header HEX Dump information to stdout. Write to `csv_writer` if not None."""
650        from chart.products.pus.ccsds_io import gen_ccsds_from_frames
651        from chart.products.pus.ccsds_io import CCSDS_FIXED_HEADER
652        from chart.products.pus.ccsds_io import RawCCSDS
653        from chart.products.pus.exceptions import CCSDSBadHeader
654        from chart.products.pus.ccsds import IDLE_PACKET_APID
655        target.write("""{i}TF Header:
656{i}{ii}Sync marker: 0x{sync:x}
657""".format(
658    i=indent,
659    ii=INDENTATION,
660    sync=self.tf_asm.attached_sync_marker))
661        self.tf_header.show(target, indent)
662
663        target.write('{i}Headers in payload:\n'.format(i=indent))
664        hp = self.tf_header.first_header_ptr
665        buff = self.tf_packet_data.buff
666        if hp > 0:
667            target.write('{i}{ii}Not processing {cc} initial bytes\n'.format(
668                i=indent,
669                ii=INDENTATION,
670                cc=hp))
671
672        while hp < len(self.tf_packet_data):# - CCSDS_FIXED_HEADER):
673            ccsds = RawCCSDS(self.tf_packet_data[hp:])
674            try:
675                apid = ccsds.ccsds.apid
676            except (ValueError, IndexError):
677                apid = '?'
678
679            packet_def = None
680            filler = ''
681            if apid == IDLE_PACKET_APID:
682                filler = ' (idle)'
683
684            else:
685                try:
686                    packet_def = ccsds.packet_def
687                except CCSDSBadHeader:  # too small
688                    break
689                except (ValueError, IndexError):
690                    pass
691
692            ident = ''
693            if packet_def is not None:
694                ident = ' SPID {spid} ({name})'.format(spid=packet_def.spid,
695                                                         name=packet_def.name)
696
697            criteria = ''
698            try:
699                service = ccsds.ccsds.service
700            except CCSDSBadHeader:  # too small
701                break
702            except IndexError:
703                service = '?'
704
705            try:
706                subservice = ccsds.ccsds.subservice
707            except CCSDSBadHeader:  # too small
708                break
709            except IndexError:
710                subservice = '?'
711
712            try:
713                param1 = ccsds.ccsds.param1
714            except CCSDSBadHeader:  # too small
715                break
716            except (IndexError, ValueError):
717                param1 = '?'
718
719            try:
720                param2 = ccsds.ccsds.param2
721            except IndexError:
722                param2 = '?'
723
724            # First attempt to show full criteria - needs 9 bytes of packet
725            criteria = '{service}/{subservice}/{apid}/{param1}/{param2}'.format(
726                    service=service,
727                    subservice=subservice,
728                    apid=apid,
729                    param1=param1,
730                    param2=param2)
731
732            partial = ''
733            try:
734                length = ccsds.length
735            except CCSDSBadHeader:
736                length = '?'
737                parial = ' partial header'
738
739            if length != '?':
740                if length > len(ccsds.ccsds.buff):
741                    partial = ' partial payload (missing {m} bytes)'.format(
742                        m=length - len(ccsds.ccsds.buff))
743
744            target.write(
745                '{i}{ii}Pos {pos} Len {length} '\
746                'Crit {criteria}{ident}{partial}{filler}\n'.format(
747                    i=indent,
748                    ii=INDENTATION,
749                    pos=hp,
750                    length=length,
751                    criteria=criteria,
752                    ident=ident,
753                    partial=partial,
754                    filler=filler))
755
756            if length == '?':
757                break
758
759            hp += ccsds.length
760
761        remain = len(self.tf_packet_data) - hp
762        if remain > 0:
763            target.write('{i}{ii}Not processing {cc} trailing bytes\n'.format(
764                i=indent,
765                ii=INDENTATION,
766                cc=remain))
767
768        target.write("{i}CORTEX postamble: {pa}\n\n".format(
769            i=indent, pa=self.cortex_trailer.postamble))
770
771
772# class CortexFrame(Segment):
773#     """Representation of a variable length Cortex Xband frame.
774
775#     Main frame format from:
776#     https://dmtool.eumetsat.int/cs/idcplg?IdcService=EUM_GET_FILE&dRevLabel=2&dDocName=371846&allowInterrupt=1
777#     Section 2.4.5 TELEMETRY MESSAGE
778
779#     Payload section of is a raw VCDU.
780#     """
781#     pass