Consultative Committee on Space Data Systems Next Gen Uplink Working Group Landsat Data Continuity Mission (LDCM) CCSDS File Extraction Protocol (CFDP) ‐ A Working Guideline In Pseudo Code NGU_11‐xx H. Garon1, V.Sank1, K.Dearth NASA Goddard Space Flight Center, Greenbelt, MD 20771 1 MEI CCSDS Next Gen Uplink WG meeting 16 May 2011 Introduction and Background The CCSDS File delivery Protocol (CFDP) format as specified within the Landsat Data Continuity Mission (LDCM) Space‐To‐Ground Interface Control Document (ICD) [1] is both precise and comprehensive. Like most specifications, though, a leap in logic is still required when it comes to actually building something that works in practice. Further exacerbating a complicated housekeeping problem, LDCM can transmit a mission data stream containing up to four CFDP files interleaved simultaneously. Simple extraction routines that may succeed in handling a single file at a time will usually fail under those circumstances. Our intent here is remove any ambiguity with respect to building a CFDP file extraction routine and touch upon the subtler details in implementation, particularly as it pertains to LDCM. Discussion LDCM’s implementation of CFDP does follow the CCSDS Blue Book recommendations within CCSDS 727.0‐B‐3 [2]. LDCM does follow CCSDS in general with the two exceptions – (1) reversing the attached synchronization markers (ASM) distinguishing code frame from transfer frame and (2), having a code frame asynchronous to the transfer frame. The rationale for these particular design decisions and more for LDCM were based upon an operational supposition. In order to fulfill its basic scientific mission given a limited contact time between satellite and ground station, LDCM required an effective bit‐error‐rate (BER) of less than 10‐12 over a 440 Msps downlink. The veracity of that supposition has since been demonstrated[3] and was realizable, in part, because LDCM also relied upon a 7/8‐rate Low‐density Parity Check (LDPC) codec with fixed‐size blocks. Referring to figure 1, the LDPC frame markers delineating every code block are clearly shown asynchronous with respect to the frame markers corresponding to each transfer frame or Channel Access Data Unit (CADU). Although not discussed explicitly in [2], the CADUs correspond to the basic building blocks of every CFDP file within LDCM. In LDCM’s specific implementation of the CFDP, the Transaction ID within each CADU’s Transfer Frame Primary Header is always zero. As a substitute to using the Transaction ID, the Virtual Channel ID (VCID) is used instead. Interleaving the CADUs for up to four CFDP files simultaneously requires that the VCID be unique and consistent in order to uniquely and distinctly identify each file from the others. The only way a VCID can be reused in starting a new CFDP file is by first closing any previous file having the same VCID. Figure 1. LDCM Asynchronous Data Transfer Frame and Code Frame We recognize that providing pseudo‐code can still leave a gap between definition and implementation. For that reason we chose to build the pseudo‐code within a Mathworks Matlabtm development environment. The Matlab environment is well‐ defined and the Mathworks support is extraordinary, both online and in the local help. The result is a “working” pseudo‐code (LDCMCFDPreader.m) in the sense that it can be used to construct a fully functional executable. In fact, the code listing at the end of this document was debugged using over 60 Gbytes of post‐decoder mission data covering a very wide range of transmission possibilities. Its purpose, nonetheless, remains to serve as pseudo‐code and as a vehicle to assist the user in porting to a preferential environment and platform. A few words are relevant concerning the robustness of the code. The code is not a fully general implementation because it does not allow for the possibility of bit errors in the data stream. Converting the code to handle most anticipated errors should be straightforward. As an example, the detection of the Attached Sync Marker (ASM) defining the beginning of each CADU could be easily generalized by allowing a bit “vote” for identification of the best ASM position, using a combination of preceding CADU and post‐CADU ASM position and ASM bit‐matching correlation. This process is very similar to the methods used to maintain decoder lock. Structure of the Code The key to building a successful CFDP extraction routine lies in recognizing the boundaries of each CADU. The boundaries of a CADU are sacrosanct and, once defined, are invariant. Following the flow diagram of figure 2, once the ASM of a CADU is detected processing of the CADU begins. For LDCM the CADU has a fixed required length of 1034 bytes. As discussed above, our code makes no allowances for bit errors so the end boundary of the CADU is expected after accumulating 1034 bytes after the start of the ASM. All decoding of the CADU follows a Big Endian format with sequential byte order establishing byte significance, e.g., a 16 bit word is composed of two bytes, the first byte in order being the most significant byte and the second byte least significant. Bit significance within a byte follows the same precedence with the highest order bit having the greatest weight. Each CADU contains at a minimum the ASM (4 bytes) and a Transfer Frame Header (6 bytes) followed by an M_PDU Header (2 bytes). The remaining 1022 bytes may contain a combination of headers and mission data or just mission data. In stark contrast to the ASM, Transfer Frame Header and M_PDU Header, the remaining subheaders and/or data do not have to fit neatly into a single CADU. These subheaders and data also follow one of two fixed sequences whose constituents are dictated by the subheaders themselves, viz., Sequence 1: Space Packet Primary Header, CFDP Header, File Directive Header with either Metadata PDU or End‐Of‐File PDU, or Sequence 2: Space Packet Primary Header, CFDP Header, Offset Header and Mission Data Header, followed by data with length defined in Mission Data Header. Any of these subheaders may split across a pair of CADU boundaries. On the other hand, data can split across multiple CADU boundaries. Many parameters within the subheaders are linked (or related) to the value of parameters in other subheaders. This offers the opportunity to do cross‐checking if desired as well as reconstruction should a bit error occur in reception. By using a switch operator based upon an assigned task, the code can handle each of the subheaders independently. The primary problem is to retain housekeeping of key parameters that are required to transit between one CADU to another CADU. Since *That opens additional possibilities for error recovery, regardless of whether that error occurs either in omission or value. The structure of CFDP has sufficient rigidity and internal redundancy that it could allow us to “fill” in the case of omission and “reconstruct” in the case of value, especially with regard to single isolated bit errors. The CCSDS recommendation, though, is to dispose of the file in the event of an unrecoverable (hard) error and issue a negative acknowledgment (NAK). LDCM can transmit up to four CFDP files simultaneously, the next CADU in a CADU sequence appropriate to a given Virtual Channel does not have to follow immediately in order. In fact, Virtual Channels (and their corresponding CADUs) may appear and disappear at any time prior to completing file extraction. The code anticipates this by putting all the key housekeeping parameters for a given Virtual Channel into a single structure specifically for that Virtual Channel. This permits each Virtual Channel to be totally self‐contained and self‐tracking. The code restricts the number of simultaneous CFDP file extractions to four but this may be easily changed to any number. Termination of the CFDP file extraction sequence for a given Virtual Channel requires at a minimum the reception of an End‐Of‐File (EOF) PDU. How much fill data is allowed pass the EOF PDU is driven by the LDCM maximum file size (1 Gbyte = 1024*1024*1024 bytes). Less than 1 Gbyte the file is terminated at the CADU containing the EOF PDU. The only other case corresponds to a file terminated precisely at end of the CADU corresponding to the 1 Gbyte boundary. Summary The LDCM implementation of the CFDP is intentionally designed to be both simple and straightforward. Our goal is to enable the widest possible audience of international cooperators to successfully download the satellite’s real time scientific payload. The discussion above and attached pseudocode should help make that possible. References: [1] Landsat Data Continuity Mission (LDCM) Spacecraft‐To‐Ground Interface Control Document, 70‐P58230P, Rev C, Orbital Sciences Corporation (ODC), Space Systems Group, Gilbert, AZ, December 2010. [2] CCSDS File Delivery Protocol (CFDP), Recommendation for Space Data System Standards, CCSDS 727.0‐B‐3, Blue Book, June 2005. [3] H.Garon, V.Sank, D.Fisher, K.Dearth, J.Gal‐Edd and A.Ames, "Successful Use of a High Rate LDPC Code with High Data Rate in a Restricted Band",CCSDS Spring 2010 Technical Meeting (SLS‐ RFM Working Group), Portsmouth, VA, May 2010. Figure 2. Basic flow for LDCM CFDP Reader. Listing for LDCM CFDP Reader Version 1.0 clear; clc %% % Title.........LDCMCFDPreader % Version ......1.0 % Description...Reads raw data files on output from high data rate (HDR) % ..............telemetry receiver decoder, extracts up to four embedded % ..............CCSDS File Delivery Protocol(CFDP) files simultaneously. % ..............The particular flavor of CFDP is biased towards the Landsat % ..............Data Continuity Mission (LDCM) as specified in the LDCM % ..............Spacecraft-To-Ground Interface Control Document. % Author(s).....H. Garon / V. Sank % ..............NASA GSFC Code 567 % ..............301-286-4387 % Date..........28 January 2011 % This code may be freely distributed. All we ask is that the authors % remain identified along with the code, even if the code is substantially % modified. %% Filename_RawData = 'UA L0R_MEDSEA PIE DATA VCALL Thu Jun 17 15.48.13 2010.rec'; %Filename_RawData = 'C INCREMENTING PIE DATA VCALL Thu Jun 17 17.20.04 2010.rec'; fpath_input = 'C:\\Data\\10_06_17 SC_Demod Tests\\'; % Basics TRUE = 1; FALSE = 0; % Set debug, output controls screen = 1; debug = FALSE; save_output = TRUE; verbose = 1; Report_Frequency = 100; Report_Count = -1; dateform = 'yyyymmddHHMMSS'; DTC0 = datestr(now, dateform); % Maximum CFDP extracted file size for LDCM is % precisely 1 Gbyte mxLDCMCFDPfilesize = 1024*1024*1024; sVCID = strvcat('OLI Real-Time', ... 'TIRS Real-Time', ... 'Playback 1 Mission Data', ... 'Playback 1 SSOH',... 'Playback 2 Mission Data', ... 'Playback 2 SSOH', ... 'Fill'); lutVCID = [0 5 8 10 12 14 63]; Fill_Frame = 63; sReplayFlag = strvcat('Real-Time Stream', 'Playback from SSR'); sVCusage = strvcat('not used', 'increment at roll-over'); sPDUtype = strvcat('File Directive','File Data'); sDirection = strvcat('Toward file receiver','1'); sXmtMode = strvcat('0','Unacknowledged'); sCRCflag = strvcat('CRC not present', 'CRC available'); sSrcEntity = strvcat('OLI Instrument','TIRS Instrument', ' ','Ancillary Data Only'); sAPID = strvcat('OLI CFDP Metadata PDU','OLI CFDP End-Of-File PDU',... 'OLI Line Header', 'OLI CRC', 'OLI Frame Header', 'Ancillary Data',... 'OLI Pixels-PAN1_ODD-Compressed','OLI Pixels-PAN1_EVEN-Compressed',... 'OLI Pixels-BLUE_Compressed','OLI Pixels-C/A-Compressed','OLI Pixels-NIR-Compressed',... 'OLI Pixels-RED_Compressed','OLI Pixels-GREEN-Compressed',... 'OLI Pixels-PAN2_ODD_Compressed','OLI Pixels-PAN2_EVEN_Compressed',... 'OLI Pixels-SWIR2_Compressed','OLI Pixels-SWIR1-Compressed',... 'OLI Pixels-CIRRUS_Compressed','OLI Pixels-DARK-Compressed',... 'OLI Pixels-PAN1_ODD-Uncompressed','OLI Pixels-PAN1_EVEN-Uncompressed',... 'OLI Pixels-BLUE_Uncompressed','OLI Pixels-C/A-Uncompressed','OLI Pixels-NIR-Uncompressed',... 'OLI Pixels-RED_Uncompressed','OLI Pixels-GREEN-Uncompressed',... 'OLI Pixels-PAN2_ODD_Uncompressed','OLI Pixels-AN2_EVEN_Uncompressed',... 'OLI Pixels-SWIR2_Uncompressed','OLI Pixels-SWIR1-Uncompressed',... 'OLI Pixels-CIRRUS_Uncompressed','OLI Pixels-DARK-Uncompressed',... 'TIRS CFDP Metadata PDU','TIRS CFDP End-Of-File PDU',... 'TIRS Line Header', 'TIRS CRC', 'TIRS Pixels 10.8 um', 'TIRS Pixels DARK', 'TIRS Pixels 12 um',... 'Reserved', 'Reserved', 'Reserved', 'Reserved', 'Reserved',... 'Reserved', 'Reserved', 'Reserved', 'Reserved', 'Reserved',... 'Reserved', 'Reserved', 'Reserved', 'Reserved', 'Reserved', ... 'Fill Packet'); lutAPID = [0 1 2 3 4 5 ... 256 257 258 259 260 261 262 263 264 265 266 267 268 ... 768 769 770 771 772 773 774 775 776 777 778 779 780 ... 1024 1025 1026 1027 1792 1793 1794 ... 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 ... 2047]; sFileReadErrors = strvcat('No read errors','Premature end-of-file'); x00 x01 x03 x0F x2E x35 x3F x53 x7F x80 x1A x1D xCF xF0 xF8 xFC xFF x0FA x7FF = = = = = = = = = = = = = = = = = = = uint8(0); uint8(1); uint8(3); uint8(15); uint8(hex2dec('2e')); uint8(hex2dec('35')); uint8(hex2dec('3f')); uint8(hex2dec('53')); uint8(hex2dec('7f')); uint8(hex2dec('80')); uint8(hex2dec('1a')); uint8(hex2dec('1d')); uint8(hex2dec('cf')); uint8(hex2dec('f0')); uint8(hex2dec('f8')); uint8(hex2dec('fc')); uint8(255); uint8(250); uint16(2047); % Hierarchy ASM1 = [x1A Length_ASM1 ASM2 = [x35 Length_ASM2 ... xCF xFC x1D]; = length(ASM1); x2E xF8 x53]; = length(ASM2); Length_XferFrameDataField = 1024; M_PDUhdr = [x00 x00]; Length_M_PDUhdr = length(M_PDUhdr); Length_M_PDU = 2; Length_M_PDU_PacketZone = Length_XferFrameDataField - Length_M_PDU; XferFramePrimaryHdr = [x00 x00 x00 x00 x00 x00]; Length_XferFramePrimaryHdr = length(XferFramePrimaryHdr); SpacePacketPrimaryHdr = [x00 x00 x00 x00 x00 x00]; Length_SpacePacketPrimaryHdr = length(SpacePacketPrimaryHdr); CFDPhdr = [x00 x00 x00 x00 x00 x00 x00 x00]; Length_CFDPhdr = length(CFDPhdr); %SpacecraftID = uint8(xFC)&bitshift(SpacePacketPrimaryHdr(1),2) ... % | (x03&bitshift(SpacePacketPrimaryHdr(2),-6)); CFDPFileDirective = x00; Length_FileDirectiveCode = length(CFDPFileDirective); EndOfFilePDU = [x00 x00 x00 x00 x00 x00 x00 x00 x00]; Flag_EOF_PDU = uint8(4); Length_FileChecksum = 4; Length_FileSize = 4; Length_EndOfFilePDU = length(EndOfFilePDU); MetadataPDU = zeros(1,21,'uint8'); Length_MetadataPDU = length(MetadataPDU); pOffset = [x00 x00 x00 x00]; Length_Offset = length(pOffset); MissionDataHdr = [x00 x00 x00 x00]; Length_MissionDataHdr = length(MissionDataHdr); % Channel Access Data Unit (CADU) - Basic building block for both OLI and % TIRS. LDCM CADU has a fixed length of 1034 bytes (8272 bits) Length_CADU = Length_ASM2 + Length_XferFramePrimaryHdr + Length_XferFrameDataField; Length_MessageData = 892; %bytes Length_LDPC_CRC = 128; %bytes Length_RandomizedLength = 1020; %bytes % Build byte offset table from ASM2 ... ASM2_offset = 0; XferFramePrimaryHdr_offset = Length_ASM2; XferFrameDataField_offset = XferFramePrimaryHdr_offset + Length_XferFramePrimaryHdr; M_PDUhdr_offset = XferFrameDataField_offset; M_PDU_PacketZone_offset = M_PDUhdr_offset + Length_M_PDU; L8 = Length_CADU; bfr_uint8 = zeros(1, L8, 'uint8'); % As stated above, LDCM has the capability to transmit up to four virtual % channels at the same time. This implies separate housekeeping must be % maintained for each virtual channel in use. These channels have the % following assigned priorites: % a. OLI Real-Time % b. TIRS Real-time % c. One Virtual Channel on Playback 1 % d. One Virtual Channel on Playback 2 % Both playback virtual channels can contain either OLI, TIRS, Ancillary % Data or Stored State of Health (SSOH). VCID = 0; index = uint8(0); mxSimultaneousCFDPfiles = 4; for index = 1:mxSimultaneousCFDPfiles VC(index).indexVCID = 0; VC(index).VCID = VCID; VC(index).Task = 'Space Packet Primary Header'; VC(index).CFDPfileID = 0; VC(index).CFDPextractedfile = sprintf('%02i_%s_%s.bin', VCID, sVCID(3,:),DTC0); VC(index).CFDPlogID = 0; VC(index).CFDPlog = sprintf('%02i_%s_%s.log', VCID, sVCID(3,:),DTC0); VC(index).nbyteswritten = 0; VC(index).nbytesCFDP = 0; VC(index).BytesRemaining = 0; VC(index).BytesRemaining_MissionData = 0; VC(index).Length_HDR = 0; VC(index).SPoffset = 0; VC(index).SpacePacketPrimaryHdr = SpacePacketPrimaryHdr; VC(index).CFDPoffset = 0; VC(index).CFDPhdr = CFDPhdr; VC(index).CFDPFDoffset = 0; VC(index).CFDPFileDirective = CFDPFileDirective; VC(index).MetadataOffset = 0; VC(index).MetadataPDU = MetadataPDU; VC(index).EOFPDUoffset = 0; VC(index).EndOfFilePDU = EndOfFilePDU; VC(index).Flag_EOF_PDU = Flag_EOF_PDU; VC(index).OFFSEToffset = 0; VC(index).pOffset = pOffset; VC(index).MDHoffset = 0; VC(index).MissionDataHdr = MissionDataHdr; VC(index).APIDcount = zeros(1,length(lutAPID)); VC(index).buffer = zeros(1, L8, 'uint8'); end nASM = 0; FileReadError = 0; FileProcessComplete = FALSE; ReadCounter = 0; % Open Filename_RawData as directed by user (found at the very beginning of % this code) input_file = sprintf('%s%s',fpath_input,Filename_RawData); fid = fopen(input_file,'r'); CADU_index = 0; %CADU_index = 337051; %'C INCREMENTING PIE DATA VCALL Thu Jun 17 17.20.04 2010.rec' CADU_index = 321365; %'UA L0R_MEDSEA PIE DATA VCALL Thu Jun 17 15.48.13 2010.rec' CADU_index_byteseek = CADU_index*Length_CADU; status = fseek(fid, CADU_index_byteseek, 'bof'); % Extract the CFDP file (or files). Execution terminates upon encountering % an end-of-file within the raw data input file. indexVCIDprevious = -1; SequenceCount = 0; while(~FileProcessComplete) % Read raw goop into buffer - should be a chunk (in bytes)at least the % size of a CADU bfr_uint8 = fread(fid, L8, 'uint8'); if(feof(fid)) FileProcessComplete = TRUE; break; end ReadCounter = ReadCounter + 1; % Search for ASM CADU_found = FALSE; ASM2_offset = 0; for i = 1:L8-Length_ASM2 j = i; for k = 1:Length_ASM2 if(bfr_uint8(j) ~= ASM2(k)) break; end j = j + 1; end if(k == Length_ASM2) ASM2_offset = i; CADU_found = TRUE; break; end end if(~CADU_found) % If you encounter this, it's reasonable to stop processing and % declare the error fprintf(screen,'\n'); fprintf(screen,'ERROR: ASM (0x352ef853)not detected after CADU %i.\n',CADU_index); fprintf(screen,' This could imply that the input file is corrupt.\n'); FileProcessComplete = TRUE; break; end if(CADU_found) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Transfer Frame primary header % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% XferFramePrimaryHdr_offset = ASM2_offset + Length_ASM2; for i = 1:Length_XferFramePrimaryHdr XferFramePrimaryHdr(i) = bfr_uint8(XferFramePrimaryHdr_offset+i-1); end MasterChIDVersion = bitand(x03,bitshift(XferFramePrimaryHdr(1),-6)); SCID = bitor(bitand(xFC,bitshift(XferFramePrimaryHdr(1),2)), ... bitand(x03,bitshift(XferFramePrimaryHdr(2),-6))); VCID = bitand(x3F,XferFramePrimaryHdr(2)); VCframecount = uint32(256*((256*XferFramePrimaryHdr(3))+XferFramePrimaryHdr(4))+XferFramePrimaryHdr(5)); ReplayFlag = bitand(x01,bitshift(XferFramePrimaryHdr(6),-7)); VCusage = bitand(x01,bitshift(XferFramePrimaryHdr(6),-6)); Spare_SF = bitand(x03,bitshift(XferFramePrimaryHdr(6),-4)); VCframecountCycle = bitand(x0F,XferFramePrimaryHdr(6)); XferFrameDataField_offset = XferFramePrimaryHdr_offset + Length_XferFramePrimaryHdr; % Fill frame (VCID = 63) is used explicitly to maintain contact % consistency. If this is a fill frame then there's (almost) % nothing to do. We first consider all other possible VCIDs ... if(VCID ~= Fill_Frame) for indexVCID = 1:length(lutVCID) if(VCID == lutVCID(indexVCID)) break; end end indexFound = FALSE; indexNotInUse = FALSE; if(indexVCID ~= indexVCIDprevious) % Virtual Channel already in use? for index = 1:mxSimultaneousCFDPfiles if(VC(index).indexVCID == indexVCID) indexFound = TRUE; break; end end % If not already in play then attempt to assign new Virtual % Channel if(~indexFound) for index = 1:mxSimultaneousCFDPfiles if(VC(index).indexVCID == 0) indexNotInUse = TRUE; break; end end if(~indexNotInUse) % If you now encounter this, it's again reasonable to stop processing % and declare an error ... fprintf(screen,'\n'); fprintf(screen,'ERROR: Only %i Virtual Channels are allowed simultaneously\n',mxSimultaneousCFDPfiles); fprintf(screen,' with this code. An attempt has been made to open a new\n'); fprintf(screen,' Virtual Channel without completing a previously engaged\n'); fprintf(screen,' extraction. This could imply that the input file is corrupt.\n'); FileProcessComplete = TRUE; break; end end if(indexNotInUse) VC(index).VCID = VCID; VC(index).indexVCID = indexVCID; % Open detailed log at the beginning of extraction DTC0 = datestr(now, dateform); VC(index).CFDPlog = sprintf('%02i_%s_%s.log', VCID, strtrim(sVCID(indexVCID,:)),DTC0); VC(index).CFDPlogID = fopen(VC(index).CFDPlog, 'a+'); SpacecraftID = ''; if(SCID == x0FA) SpacecraftID = 'LDCM'; end fprintf(VC(index).CFDPlogID, '\n>> CADU index : %i\n',CADU_index); fprintf(VC(index).CFDPlogID, '[Transfer Frame Primary Header]\n'); fprintf(VC(index).CFDPlogID, ' Master Channel ID Version : %02i\n', MasterChIDVersion); fprintf(VC(index).CFDPlogID, ' SCID : 0x%03x (%s)\n', SCID, SpacecraftID); fprintf(VC(index).CFDPlogID, ' VCID : %02i %s\n', VCID, sVCID(indexVCID,:)); fprintf(VC(index).CFDPlogID, ' VC frame count : %06i\n', VCframecount); fprintf(VC(index).CFDPlogID, ' Replay flag : %s\n', sReplayFlag((ReplayFlag+1),:)); fprintf(VC(index).CFDPlogID, ' VC usage : %s\n', sVCusage((VCusage+1),:)); fprintf(VC(index).CFDPlogID, ' VC frame count cycle : %i\n', VCframecountCycle); end end indexVCIDprevious = indexVCID; VC(index).buffer = uint8(bfr_uint8); %////////////////////////////////////////////////////////////////////////// %/ M_PDU header / %////////////////////////////////////////////////////////////////////////// M_PDUhdr_offset = XferFrameDataField_offset; M_PDUhdr(1) = VC(index).buffer(M_PDUhdr_offset+0); M_PDUhdr(2) = VC(index).buffer(M_PDUhdr_offset+1); Spare_M_PDUhdr = bitand(uint8(hex2dec('1F')),bitshift(M_PDUhdr(1),-3)); FirstHdrPointer = uint16(256*uint16(bitand(uint8(7),M_PDUhdr(1)))) ... + uint16(M_PDUhdr(2)); M_PDU_PacketZone_offset = M_PDUhdr_offset + Length_M_PDUhdr; % % % % % % % % % % % % % Processing splits at this point based upon precedents. Literally. The ASM, Transfer Frame Primary Header and the M_PDU Header are always self-contained and aligned within a single CADU. All the remaining headers and data can split across multiple CADUs. The simplest case occurs where there are no headers following the M_PDU for the remainder of the CADU. A FirstHdrPointer = 0x7FF within the M_PDU Header serves as a flag for precisely that case. With FirstHdrPointer = 0x7FF all that's necessary is to update counters and store the remainder of the CADU. Most of the code below supports the much more complicated case where there is at least one header embedded in the remaining bytes of the CADU. Handling a split header or fixed length PDU properly becomes the driving force % behind the housekeeping. % % Handle special case where there are no headers defined in the % CADU. This is when the FirstHdrPointer = 0x7FF (2047 decimal). if((FirstHdrPointer == x7FF)&&(VC(index).BytesRemaining == 0)) % Nothing much to do but stuff bytes into current CFDP file being extracted... if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[M_PDU Packet Header]\n'); fprintf(VC(index).CFDPlogID, ' 1st Header Offset (bytes) : No packet header in zone\n'); fprintf(VC(index).CFDPlogID, ' Mission data (bytes remaining) = %i\n', VC(index).BytesRemaining_MissionData); end if(VC(index).BytesRemaining_MissionData > 0) VC(index).BytesRemaining_MissionData = VC(index).BytesRemaining_MissionData ... - (L8 - (M_PDU_PacketZone_offset-1)); end else if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[M_PDU Packet Header]\n'); fprintf(VC(index).CFDPlogID, ' 1st Header Offset (bytes) : %i\n', FirstHdrPointer); fprintf(VC(index).CFDPlogID, ' Mission data (bytes remaining) = %i\n', VC(index).BytesRemaining_MissionData); end VC(index).SPoffset = 0; ContinuousLoop = TRUE; while (ContinuousLoop) switch VC(index).Task case 'Space Packet Primary Header' %//////////////////////////////////////////////////////////////////////// %/ Space Packet Primary Header / %//////////////////////////////////////////////////////////////////////// % Multiple Space Packet Primary Headers can % occur within a single CADU. Using a pre-set % offset (SPoffset > 0) as a flag signals that % possibility ... if(VC(index).SPoffset == 0) VC(index).SPoffset = M_PDU_PacketZone_offset+FirstHdrPointer; end VC(index).Length_HDR = Length_SpacePacketPrimaryHdr; if(VC(index).BytesRemaining > 0) VC(index).SPoffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).SPoffset+VC(index).Length_HDR -1) <= L8) i = Length_SpacePacketPrimaryHdr-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR VC(index).SpacePacketPrimaryHdr(i) = VC(index).buffer(VC(index).SPoffset+j-1); i = i + 1; end SpacePktVersion = bitand(uint8(7),bitshift(VC(index).SpacePacketPrimaryHdr(1),-5)); SpacePktType = bitand(x01,bitshift(VC(index).SpacePacketPrimaryHdr(1),-4)); SecHdrFlag = bitand(x01,bitshift(VC(index).SpacePacketPrimaryHdr(1),-3)); APID = uint16(256*uint16(bitand(uint8(7),VC(index).SpacePacketPrimaryHdr(1)))) ... + uint16(VC(index).SpacePacketPrimaryHdr(2)); SpacePktSeqFlags = bitand(x03,bitshift(VC(index).SpacePacketPrimaryHdr(3),-6)); SpacePktSeqCount = uint16(256*uint16(bitand(x3F,VC(index).SpacePacketPrimaryHdr(3)))) ... + uint16(VC(index).SpacePacketPrimaryHdr(4)); SpacePacketLength= uint16(256*uint16(VC(index).SpacePacketPrimaryHdr(5))) + uint16(VC(index).SpacePacketPrimaryHdr(6)); for indexAPID = 1:length(lutAPID) if(APID == lutAPID(indexAPID)) break; end end % A Space Packet can span multiple M_PDUs or one % M_PDU can contain multiple Space Packets. if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[Space Packet Primary Header]\n'); fprintf(VC(index).CFDPlogID, ' Space Packet Version : %i\n', SpacePktVersion); fprintf(VC(index).CFDPlogID, ' Space Packet Type : %i\n', SpacePktType); fprintf(VC(index).CFDPlogID, ' Secondary Header Flag : %i\n', SecHdrFlag); fprintf(VC(index).CFDPlogID, ' APID : %04i %s\n', APID, sAPID(indexAPID,:)); fprintf(VC(index).CFDPlogID, ' Space Packet Seq Flags : %i\n', SpacePktSeqFlags); fprintf(VC(index).CFDPlogID, ' Space Packet Seq Count : %i\n', SpacePktSeqCount); fprintf(VC(index).CFDPlogID, ' Space Packet Length : %i\n', SpacePacketLength); end if(APID == 0) %Metadata PDU % First check to see if we've completely wrapped % previous CFDP file ... if(VC(index).CFDPfileID > 0) % If VC(index).CFDPfileID exists (> 0) then there is an error fprintf(screen,'\n'); fprintf(screen,'ERROR: Second Metadata PDU (APID = 0) encountered prior to\n'); fprintf(screen,' detecting the end-of-file PDU (APID = 1). This\n'); fprintf(screen,' implies either the file is corrupt or there is\n'); fprintf(screen,' a read error.\n'); FileReadError = 1; FileProcessComplete = TRUE; break; end %////////////////////////////////////////////////////////////////////////// % Initiate construction of an encapsulated CFDP file. % CFDP involves the transmission of Protocol Data Units (PDUs). % Sequence of transmission follows: % a. Metadata PDU (APID = 0) % b. File Data PDUs (possibly spanning multiple CADUs) % c. EOF PDU (APID = 1) % The first useable CADU within the search file will employ % an APID = 0000. This designates a CFDP Metadata PDU. % This is your hint to start piecing together the % encapsulated data file. You should also be aware that no % attempt is made to align a PDU with the CCSDS frame. % "BytesRemaining" is a key housekeeping variable used % to keep track of the number of bytes required to complete % a header across multiple CADUs. %////////////////////////////////////////////////////////////////////////// VC(index).CFDPextractedfile = sprintf('%02i_%s_%s.bin', VC(index).VCID, strtrim(sVCID(VC(index).indexVCID,:)),DTC0); VC(index).CFDPfileID = fopen(VC(index).CFDPextractedfile, 'a+'); VC(index).APIDcount = zeros(1,length(lutAPID)); VC(index).nbyteswritten = 0; if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '>> CADU index : %i\n',CADU_index); fprintf(VC(index).CFDPlogID, '[Transfer Frame Primary Header]\n'); fprintf(VC(index).CFDPlogID, ' Master Channel ID Version : %02i\n', MasterChIDVersion); fprintf(VC(index).CFDPlogID, ' fprintf(VC(index).CFDPlogID, ' VC(index).VCID, sVCID(VC(index).indexVCID,:)); fprintf(VC(index).CFDPlogID, ' VCframecount); fprintf(VC(index).CFDPlogID, ' sReplayFlag((ReplayFlag+1),:)); fprintf(VC(index).CFDPlogID, ' sVCusage((VCusage+1),:)); fprintf(VC(index).CFDPlogID, ' VCframecountCycle); end end SCID VCID : 0x%03x\n', SCID); : %02i %s\n', VC frame count : %06i\n', Replay flag : %s\n', VC usage : %s\n', VC frame count cycle : %i\n', % Update the APID incidence counter VC(index).APIDcount(indexAPID) = VC(index).APIDcount(indexAPID) + 1; VC(index).Task = 'CFDP Header'; VC(index).BytesRemaining = 0; else % Space Packet primary header is split across two M_PDUs VC(index).Task = 'Space Packet Primary Header'; VC(index).BytesRemaining = (VC(index).SPoffset+Length_SpacePacketPrimaryHdr-1) - L8; i = 1; for j = VC(index).SPoffset:L8 VC(index).SpacePacketPrimaryHdr(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end case 'CFDP Header' %//////////////////////////////////////////////////////////////////////// %/ CFDP header / %//////////////////////////////////////////////////////////////////////// VC(index).CFDPoffset = VC(index).SPoffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_CFDPhdr; if(VC(index).BytesRemaining > 0) VC(index).CFDPoffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).CFDPoffset+VC(index).Length_HDR-1) <= L8) i = Length_CFDPhdr-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR VC(index).CFDPhdr(i) = VC(index).buffer(VC(index).CFDPoffset+j-1); i = i + 1; end CFDPversion = bitand(uint8(7),bitshift(VC(index).CFDPhdr(1),-5)); PDUtype = bitand(uint8(1),bitshift(VC(index).CFDPhdr(1),-4)); Direction = bitand(uint8(1),bitshift(VC(index).CFDPhdr(1),-3)); XmissionMode = bitand(uint8(1),bitshift(VC(index).CFDPhdr(1),-2)); CRCflag = bitand(uint8(1),bitshift(VC(index).CFDPhdr(1),-1)); Rsrv0_CFDP = bitand(uint8(1),VC(index).CFDPhdr(1)); PDUdatalength = uint16(256*uint16(VC(index).CFDPhdr(2))) + uint16(VC(index).CFDPhdr(3)); Rsrv1_CFDP = bitand(uint8(1),bitshift(VC(index).CFDPhdr(4),-7)); EntityID_Length = bitand(uint8(7),bitshift(VC(index).CFDPhdr(4),-4)); Rsrv2_CFDP = bitand(uint8(1),bitshift(VC(index).CFDPhdr(4),-3)); TransSeq_Length = bitand(uint8(7),VC(index).CFDPhdr(4)); SourceEntityID = VC(index).CFDPhdr(5); TransSeqNum = uint16(256*uint16(VC(index).CFDPhdr(6))) + uint16(VC(index).CFDPhdr(7)); DestEntityID = VC(index).CFDPhdr(8); if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[CFDP File Data PDU]\n'); fprintf(VC(index).CFDPlogID, ' CFDP version fprintf(VC(index).CFDPlogID, ' PDU type : %i\n', CFDPversion); : %s\n', fprintf(VC(index).CFDPlogID, ' Direction : %s\n', fprintf(VC(index).CFDPlogID, ' Xmission Mode : %s\n', fprintf(VC(index).CFDPlogID, ' CRC flag : %s\n', sPDUtype(PDUtype+1,:)); sDirection(Direction+1,:)); sXmtMode(XmissionMode+1,:)); sCRCflag(CRCflag+1,:)); fprintf(VC(index).CFDPlogID, ' PDU data length : %i\n', PDUdatalength); switch APID case 0 fprintf(VC(index).CFDPlogID, ' *length = File Directive + Metadata PDU\n'); case 1 fprintf(VC(index).CFDPlogID, ' otherwise fprintf(VC(index).CFDPlogID, ' *length = File Directive + EOF PDU\n'); *length = Offset HDR + MDH + File Data\n'); end if(APID ~= 2047) fprintf(VC(index).CFDPlogID, ' Entity ID Length : %i\n', fprintf(VC(index).CFDPlogID, ' Transaction Seq Length : %i\n', fprintf(VC(index).CFDPlogID, ' SourceEntityID : %s\n', fprintf(VC(index).CFDPlogID, ' Transaction Sequence # : 0x%x\n', fprintf(VC(index).CFDPlogID, ' Destination Entity ID : 0x%x\n', EntityID_Length); TransSeq_Length); sSrcEntity(SourceEntityID+1,:)); TransSeqNum); DestEntityID); end end VC(index).Task = 'Offset'; if((APID == 0) ||(APID == 1)) VC(index).Task = 'CFDP File Directive'; end VC(index).BytesRemaining = 0; else % CFDP header is split across two M_PDUs VC(index).Task = 'CFDP Header'; VC(index).BytesRemaining = (VC(index).CFDPoffset+Length_CFDPhdr-1) - L8; i = 1; for j = VC(index).CFDPoffset:L8 VC(index).CFDPhdr(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end case 'Offset' %//////////////////////////////////////////////////////////////////////// %/ Offset / %//////////////////////////////////////////////////////////////////////// VC(index).OFFSEToffset = VC(index).CFDPoffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_Offset; if(VC(index).BytesRemaining > 0) VC(index).OFFSEToffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).OFFSEToffset+VC(index).Length_HDR -1) <= L8) i = Length_Offset-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR VC(index).pOffset(i) = VC(index).buffer(VC(index).OFFSEToffset+j-1); i = i + 1; end vOffset = uint32(256*(256*(256*VC(index).pOffset(1)+VC(index).pOffset(2))+VC(index).pOffset(3))+VC(index).pOffset(4)); if(verbose > 0) fprintf(VC(index).CFDPlogID, ' Offset (bytes) : %i\n', vOffset); end VC(index).Task = 'Mission Data Header'; VC(index).BytesRemaining = 0; else % Offset is split across two M_PDUs VC(index).Task = 'Offset'; VC(index).BytesRemaining = (VC(index).OFFSEToffset+Length_Offset-1) - L8; i = 1; for j = VC(index).OFFSEToffset:L8 VC(index).pOffset(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end case 'Mission Data Header' %//////////////////////////////////////////////////////////////////////// %/ Mission Data Header / %//////////////////////////////////////////////////////////////////////// VC(index).MDHoffset = VC(index).OFFSEToffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_MissionDataHdr; if(VC(index).BytesRemaining > 0) VC(index).MDHoffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).MDHoffset+VC(index).Length_HDR -1) <= L8) i = Length_MissionDataHdr-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR VC(index).MissionDataHdr(i) = VC(index).buffer(VC(index).MDHoffset+j-1); i = i + 1; end CFDP_APID = uint16(256*uint16(bitand(uint8(7),VC(index).MissionDataHdr(1)))) ... + uint16(VC(index).MissionDataHdr(2)); MissionDataLength = uint16(256*uint16(VC(index).MissionDataHdr(3))) ... + uint16(VC(index).MissionDataHdr(4)); for i = 1:length(lutAPID) if(CFDP_APID == lutAPID(i)) break; end end if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[Mission Data Header]\n'); fprintf(VC(index).CFDPlogID, ' CFDP APID : %04i %s\n', CFDP_APID, sAPID(i,:)); fprintf(VC(index).CFDPlogID, ' Mission Data Length : %i\n', MissionDataLength); end % Anticipate the next space packet primary header VC(index).Task = 'Space Packet Primary Header'; VC(index).SPoffset = MissionDataLength + (VC(index).MDHoffset+VC(index).Length_HDR); VC(index).BytesRemaining = 0; VC(index).BytesRemaining_MissionData = 0; if(VC(index).SPoffset > L8) VC(index).BytesRemaining_MissionData = (VC(index).SPoffset-1) - L8; VC(index).SPoffset = 0; break; end else % Mission Data Header is split across two M_PDUs VC(index).Task = 'Mission Data Header'; VC(index).BytesRemaining = (VC(index).MDHoffset+Length_MissionDataHdr-1) - L8; i = 1; for j = VC(index).MDHoffset:L8 VC(index).MissionDataHdr(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end case 'CFDP File Directive' %//////////////////////////////////////////////////////////////////////// %/ CFDP File Directive / %//////////////////////////////////////////////////////////////////////// VC(index).CFDPFDoffset = VC(index).CFDPoffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_FileDirectiveCode; if(VC(index).BytesRemaining > 0) VC(index).CFDPFDoffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if(VC(index).CFDPFDoffset <= L8) VC(index).CFDPFileDirectiveCode = VC(index).buffer(VC(index).CFDPFDoffset); if(verbose > 0) fprintf(VC(index).CFDPlogID, ' File Directive Code : 0x%02x\n', VC(index).CFDPFileDirectiveCode); end switch VC(index).CFDPFileDirectiveCode case 4 VC(index).Task = 'End-Of-File PDU'; case 7 VC(index).Task = 'Metadata PDU'; otherwise VC(index).Task = 'Metadata PDU'; if(APID == 1) VC(index).Task = 'End-Of-File PDU'; end end VC(index).BytesRemaining = 0; else % Just ran out of room for a single byte... VC(index).Task = 'CFDP File Directive'; VC(index).BytesRemaining = 1; break; %Forces exit from while loop end case 'Metadata PDU' %//////////////////////////////////////////////////////////////////////// %/ Metadata PDU / %//////////////////////////////////////////////////////////////////////// VC(index).MetadataOffset = VC(index).CFDPFDoffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_MetadataPDU; if(VC(index).BytesRemaining > 0) VC(index).MetadataOffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).MetadataOffset+VC(index).Length_HDR-1) <= L8) i = Length_MetadataPDU-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR MetadataPDU(i) = VC(index).buffer(VC(index).MetadataOffset+j-1); i = i + 1; end SegControl = (bitand(x80,MetadataPDU(1))~=0); Rsvd_MD = bitand(x7F,MetadataPDU(1)); FileSize_MD = uint32(256*(256*(256*MetadataPDU(2)+MetadataPDU(3))+MetadataPDU(4))+MetadataPDU(5)); SRCnamelength = MetadataPDU(6); SRCfilename = char(MetadataPDU(7:13)); DESnamelength = MetadataPDU(14); DESfilename = char(MetadataPDU(15:21)); if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[CFDP Metadata PDU]\n'); fprintf(VC(index).CFDPlogID, ' Segmentation Control : %i\n', SegControl); fprintf(VC(index).CFDPlogID, ' Source filename : %s\n', SRCfilename); fprintf(VC(index).CFDPlogID, ' Destination filename : %s\n', DESfilename); end VC(index).Task = 'Space Packet Primary Header'; VC(index).SPoffset = VC(index).MetadataOffset+VC(index).Length_HDR; VC(index).BytesRemaining = 0; else % Store what remains of buffer ... VC(index).Task = 'Metadata PDU'; VC(index).BytesRemaining = (VC(index).MetadataOffset+VC(index).Length_HDR-1) - L8; i = 1; for j = VC(index).MetadataOffset:L8 MetadataPDU(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end case 'End-Of-File PDU' %//////////////////////////////////////////////////////////////////////// %/ End-Of-File PDU / %//////////////////////////////////////////////////////////////////////// VC(index).EOFPDUoffset = VC(index).CFDPFDoffset + VC(index).Length_HDR; VC(index).Length_HDR = Length_EndOfFilePDU; if(VC(index).BytesRemaining > 0) VC(index).EOFPDUoffset = M_PDU_PacketZone_offset; VC(index).Length_HDR = VC(index).BytesRemaining; end if((VC(index).EOFPDUoffset+VC(index).Length_HDR-1) <= L8) i = Length_EndOfFilePDU-VC(index).Length_HDR+1; for j = 1:VC(index).Length_HDR VC(index).EndOfFilePDU(i) = VC(index).buffer(VC(index).EOFPDUoffset+j-1); i = i + 1; end ConditionCode = bitshift(bitand(xF0,VC(index).EndOfFilePDU(1)),-4); Spare_EOF = bitand(x0F,VC(index).EndOfFilePDU(1)); FileChksum = uint32(256*(256*(256*VC(index).EndOfFilePDU(2)+VC(index).EndOfFilePDU(3))+VC(index).EndOfFilePDU(4))+VC(index).EndOfFil ePDU(5)); FileSize = uint32(256*(256*(256*VC(index).EndOfFilePDU(6)+VC(index).EndOfFilePDU(7))+VC(index).EndOfFilePDU(8))+VC(index).EndOfFil ePDU(9)); if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '[End Of File PDU]\n'); fprintf(VC(index).CFDPlogID, ' Condition Code : 0x%02x\n', ConditionCode); fprintf(VC(index).CFDPlogID, ' File Check Sum : 0x%08x\n', FileChksum); fprintf(VC(index).CFDPlogID, ' File Size (Kbyte) : %07i\n', FileSize/1024); end VC(index).Task = 'Space Packet Primary Header'; VC(index).SPoffset = VC(index).EOFPDUoffset+VC(index).Length_HDR; VC(index).BytesRemaining = 0; else % Store what remains of buffer ... VC(index).Task = 'End-Of-File PDU'; VC(index).BytesRemaining = (VC(index).EOFPDUoffset+Length_EndOfFilePDU-1) - L8; i = 1; for j = VC(index).EOFPDUoffset:L8 VC(index).EndOfFilePDU(i) = VC(index).buffer(j); i = i + 1; end break; %Forces exit from while loop end end % End switch end % End while loop (that's what the breaks are for within switch) end % End CADU read % If we've been working on building the CFDP file then % dump the remainder of the block to the file from (and including) % the M_PDU header. Update counters as well. if(VC(index).CFDPfileID > 0) count = fwrite(VC(index).CFDPfileID, VC(index).buffer(M_PDUhdr_offset:L8), 'uint8'); VC(index).nbyteswritten = VC(index).nbyteswritten + count; VC(index).nbytesCFDP = VC(index).nbytesCFDP + count; if(VC(index).APIDcount(2) > 0) if(VC(index).nbyteswritten >= mxLDCMCFDPfilesize) indexVCIDprevious = -1; if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '\n'); fprintf(VC(index).CFDPlogID, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(VC(index).CFDPlogID, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(VC(index).CFDPlogID, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(VC(index).CFDPlogID, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end if(verbose > 0) fprintf(screen, '\n'); fprintf(screen, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(screen, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(screen, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(screen, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end fprintf(screen, '\n'); end % Close log file fclose(VC(index).CFDPlogID); VC(index).CFDPlogID = 0; end if(VC(index).CFDPfileID > 0) % Close extraction file fclose(VC(index).CFDPfileID); VC(index).CFDPfileID = 0; VC(index).indexVCID = 0; end end end end else %//////////////////////////////////////////////////////////////////////// %/ Fill frame processing - Terminate any CFDP files for / %/ which an End-Of-File PDU has been received / %/ (APIDcount(2) > 0) / %//////////////////////////////////////////////////////////////////////// if(indexVCIDprevious ~= -1) for index = 1:mxSimultaneousCFDPfiles FileExtractionStillActive = FALSE; if(VC(index).indexVCID > 0) if(VC(index).APIDcount(2) > 0) if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '\n'); fprintf(VC(index).CFDPlogID, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(VC(index).CFDPlogID, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(VC(index).CFDPlogID, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(VC(index).CFDPlogID, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end if(verbose > 0) fprintf(screen, '\n'); fprintf(screen, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(screen, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(screen, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(screen, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end fprintf(screen, '\n'); end % Close log file fclose(VC(index).CFDPlogID); VC(index).CFDPlogID = 0; end if(VC(index).CFDPfileID > 0) % Close extraction file fclose(VC(index).CFDPfileID); VC(index).CFDPfileID = 0; VC(index).indexVCID = 0; end else FileExtractionStillActive = TRUE; end end end if(~FileExtractionStillActive) indexVCIDprevious = -1; end end end CADU_index = CADU_index + 1; if(verbose > 0) Report_Count = mod(Report_Count+1,Report_Frequency); if(Report_Count == 0) fprintf(screen, 'CADU(index, read): %010i %010i\n',CADU_index, ReadCounter); end end end end fclose(fid); for index = 1:mxSimultaneousCFDPfiles if(VC(index).CFDPlogID > 0) fprintf(VC(index).CFDPlogID, '\n'); fprintf(VC(index).CFDPlogID, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(VC(index).CFDPlogID, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(VC(index).CFDPlogID, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(VC(index).CFDPlogID, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end fclose(VC(index).CFDPlogID); VC(index).CFDPlogID = 0; if(verbose > 0) fprintf(screen, '\n'); fprintf(screen, ' Extraction stopped: %s\n', datestr(now, dateform)); fprintf(screen, ' File statistics: CADU_index = %i\n', CADU_index ); fprintf(screen, ' bytes written = %i\n', VC(index).nbytesCFDP ); for i=1:length(lutAPID) if(VC(index).APIDcount(i) > 0) fprintf(screen, ' APID %04i = %i\n', lutAPID(i),VC(index).APIDcount(i)); end end fprintf(screen, '\n'); end end if(VC(index).CFDPfileID > 0) fclose(VC(index).CFDPfileID); VC(index).CFDPfileID = 0; VC(index).indexVCID = 0; end end Listing for LDCM CFDP Reader Version 1.0