class Cosmos::CcsdsTransferFrames::CcsdsTransferFrameProtocol
Given a stream of ccsds transfer frames, extract ccsds space packets based on the first header pointer and packet lengths.
Only read is supported.
Constants
- FIRST_HEADER_POINTER_BITS
- FIRST_HEADER_POINTER_BIT_OFFSET
- FRAME_ERROR_CONTROL_FIELD_LENGTH
- FRAME_OPERATIONAL_CONTROL_FIELD_LENGTH
- FRAME_PRIMARY_HEADER_LENGTH
- FRAME_VIRTUAL_CHANNEL_BITS
- FRAME_VIRTUAL_CHANNEL_BIT_OFFSET
- IDLE_FRAME_FIRST_HEADER_POINTER
- IDLE_PACKET_APID
- NO_PACKET_START_FIRST_HEADER_POINTER
- SPACE_PACKET_APID_BITS
- SPACE_PACKET_APID_BIT_OFFSET
- SPACE_PACKET_HEADER_LENGTH
- SPACE_PACKET_LENGTH_BITS
- SPACE_PACKET_LENGTH_BIT_OFFSET
- VIRTUAL_CHANNEL_COUNT
- VirtualChannel
Public Class Methods
@param transfer_frame_length [Integer] Length of transfer frame in bytes @param transfer_frame_secondary_header_length [Integer] Length of
transfer frame secondary header in bytes
@param transfer_frame_has_operational_control_field [Boolean] Flag
indicating if the transfer frame operational control field is present or not
@param transfer_frame_has_frame_error_control_field [Boolean] Flag
indicating if the transfer frame error control field is present or not
@param prefix_packets [Boolean] Flag indicating if each space packet should
be prefixed with the transfer frame headers from the frame where it started.
@param include_idle_packets [Boolean] Flag indicating if idle packets
should be included or discarded.
@param allow_empty_data [true/false/nil] See Protocol#initialize
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 63 def initialize( transfer_frame_length, transfer_frame_secondary_header_length, transfer_frame_has_operational_control_field, transfer_frame_has_frame_error_control_field, prefix_packets = false, include_idle_packets = false, allow_empty_data = nil) super(allow_empty_data) @frame_length = Integer(transfer_frame_length) @frame_headers_length = FRAME_PRIMARY_HEADER_LENGTH + Integer(transfer_frame_secondary_header_length) @frame_trailer_length = 0 has_ocf = ConfigParser.handle_true_false(transfer_frame_has_operational_control_field) @frame_trailer_length += FRAME_OPERATIONAL_CONTROL_FIELD_LENGTH if has_ocf has_fecf = ConfigParser.handle_true_false(transfer_frame_has_frame_error_control_field) @frame_trailer_length += FRAME_ERROR_CONTROL_FIELD_LENGTH if has_fecf @frame_data_field_length = @frame_length - @frame_headers_length - @frame_trailer_length @packet_prefix_length = 0 @prefix_packets = ConfigParser.handle_true_false(prefix_packets) @packet_prefix_length += @frame_headers_length if @prefix_packets @include_idle_packets = ConfigParser.handle_true_false(include_idle_packets) end
Public Instance Methods
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 98 def read_data(data) @data << data if (@data.length >= @frame_length) frame = @data.slice!(0, @frame_length) process_frame(frame) end packet_data = get_packet() # Potentially allow blank string to be sent to other protocols if no # packet is ready in this one if (Symbol === packet_data && packet_data == :STOP && data.length <= 0) return super(data) end return packet_data end
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 92 def reset super() @data = '' @virtual_channels = Array.new(VIRTUAL_CHANNEL_COUNT) { VirtualChannel.new } end
Private Instance Methods
Get a packet from the virtual channel packet queues of stored packets from processed frames.
If idle packets are not included, extracted idle packets are discarded and extraction is retried until a non-idle packet is found or no more complete packets are left in any of the virtual channel packet queues.
@return [String] Packet data, if the queues contained at least one
complete packet.
@return [Symbol] :STOP, if the queues do not contain any complete
packets.
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 136 def get_packet @virtual_channels.each do |vc| # avoid extracting incomplete packets while (vc.packet_queue.length >= 2 || (vc.packet_queue.length == 1 && vc.pending_incomplete_packet_bytes_left == 0)) packet_data = vc.packet_queue.shift return packet_data if (@include_idle_packets || packet_data.length < SPACE_PACKET_HEADER_LENGTH) apid = BinaryAccessor.read( @packet_prefix_length * 8 + SPACE_PACKET_APID_BIT_OFFSET, SPACE_PACKET_APID_BITS, :UINT, packet_data, :BIG_ENDIAN) return packet_data unless (apid == IDLE_PACKET_APID) end end # If the packet queues contains any more whole packets they will be # handled in subsequent calls to this method. Cosmos will ensure that # read_data() is called until it returns :STOP, which allows us to # clear all whole packets. # no complete packet for any virtual channel return :STOP end
Handle packet continuation when processing a transfer frame.
First ensures that any incomplete packet has enough data for the packet header to determine its length and then tries to complete it.
If the first header pointer indicates that a packet starts in this frame, the frame_data_field parameter will be modified by removing everything before the first header pointer.
@param virtual_channel [Int] Transfer frame virtual channel. @param frame_data_field [String] Transfer frame data field. @param first_header_pointer [Int] First header pointer value.
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 210 def handle_packet_continuation(virtual_channel, frame_data_field, first_header_pointer) vc = @virtual_channels[virtual_channel] if (vc.packet_queue.length == 0 || vc.pending_incomplete_packet_bytes_left == 0) # no packet in queue to be continued return if (first_header_pointer == NO_PACKET_START_FIRST_HEADER_POINTER) frame_data_field.replace(frame_data_field[first_header_pointer..-1]) return end packet_continuation = nil if (first_header_pointer == NO_PACKET_START_FIRST_HEADER_POINTER) packet_continuation = frame_data_field else packet_continuation = frame_data_field.slice!(0, first_header_pointer) end if (vc.packet_queue[-1].length < @packet_prefix_length + SPACE_PACKET_HEADER_LENGTH) # Pending incomplete packet does not yet heave header, try to # complete header and get length before processing further. rest_of_packet_header_length = vc.pending_incomplete_packet_bytes_left if (rest_of_packet_header_length > packet_continuation.length) # Not enough continuation to complete packet header, first header # pointer takes precedence and packet is cut short. vc.packet_queue[-1] << packet_continuation vc.pending_incomplete_packet_bytes_left = 0 return end vc.packet_queue[-1] << packet_continuation.slice!(0, rest_of_packet_header_length) # actual length in ccsds space packet is stored value plus one space_packet_data_field_length = BinaryAccessor.read( @packet_prefix_length * 8 + SPACE_PACKET_LENGTH_BIT_OFFSET, SPACE_PACKET_LENGTH_BITS, :UINT, vc.packet_queue[-1], :BIG_ENDIAN) + 1 vc.pending_incomplete_packet_bytes_left = space_packet_data_field_length end if (first_header_pointer == NO_PACKET_START_FIRST_HEADER_POINTER) # packet continues past this frame or ends exactly at end of this # frame according to first header pointer if (vc.pending_incomplete_packet_bytes_left < packet_continuation.length) # Packet length is inconsistent with first header pointer, since it # indicates that the packet ends before the end of this frame. # # Complete the packet based on the packet length and ignore the # rest of the data in the frame (will use first header pointer to # re-sync with start of next packet in a later frame). vc.packet_queue[-1] << packet_continuation[0, vc.pending_incomplete_packet_bytes_left] vc.pending_incomplete_packet_bytes_left = 0 return end # First header pointer and packet length are consistent, append whole frame. vc.packet_queue[-1] << packet_continuation vc.pending_incomplete_packet_bytes_left -= frame_data_field.length return end # packet ends before the end of this frame according to first header # pointer if (vc.pending_incomplete_packet_bytes_left < packet_continuation.length) # Packet length is inconsistent with first header pointer, since it # indicates that the packet ends before the first header pointer. # # Complete the packet based on the packet length and ignore the data # between the packet end and the first header pointer. packet_continuation.replace(packet_continuation[0, vc.pending_incomplete_packet_bytes_left]) end # If the packet length is too long compared to the first header # pointer, the first header pointer takes precedence and the packet is # cut short. vc.packet_queue[-1] << packet_continuation vc.pending_incomplete_packet_bytes_left = 0 end
Extract packets from a transfer frame and store them in the packet queue.
First handles packet continuation of any incomplete packet and then handles the rest of the packets in the frame.
@param frame [String] Transfer frame data.
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 171 def process_frame(frame) first_header_pointer = BinaryAccessor.read( FIRST_HEADER_POINTER_BIT_OFFSET, FIRST_HEADER_POINTER_BITS, :UINT, frame, :BIG_ENDIAN) return if (first_header_pointer == IDLE_FRAME_FIRST_HEADER_POINTER) virtual_channel = BinaryAccessor.read( FRAME_VIRTUAL_CHANNEL_BIT_OFFSET, FRAME_VIRTUAL_CHANNEL_BITS, :UINT, frame, :BIG_ENDIAN) frame_data_field = frame[@frame_headers_length, @frame_data_field_length] handle_packet_continuation(virtual_channel, frame_data_field, first_header_pointer) return if (first_header_pointer == NO_PACKET_START_FIRST_HEADER_POINTER) frame_headers = frame[0, @frame_headers_length] store_packets(virtual_channel, frame_headers, frame_data_field) end
Extract all packets from the remaining frame data field, and store them in the packet queue.
It is assumed that packet continuation data from any previously unfinished packets has been removed from the frame data field prior, and hence that the given remaining frame data field starts at a space packet header.
Handles both complete packets and unfinished packets which will be finished in a later frame via handle_packet_continuation
().
@param virtual_channel [Int] Transfer frame virtual channel. @param frame_headers [String] Transfer frame headers, only used if prefixing packets. @param frame_data_field [String] (Remaining) transfer frame data field.
# File lib/cosmos/ccsds_transfer_frames/ccsds_transfer_frame_protocol.rb, line 309 def store_packets(virtual_channel, frame_headers, frame_data_field) vc = @virtual_channels[virtual_channel] while (frame_data_field.length > 0) do if (frame_data_field.length < SPACE_PACKET_HEADER_LENGTH) if (@prefix_packets) vc.packet_queue << (frame_headers.clone << frame_data_field) else vc.packet_queue << frame_data_field end vc.pending_incomplete_packet_bytes_left = SPACE_PACKET_HEADER_LENGTH - frame_data_field.length return end # actual length in ccsds space packet is stored value plus one space_packet_data_field_length = BinaryAccessor.read( SPACE_PACKET_LENGTH_BIT_OFFSET, SPACE_PACKET_LENGTH_BITS, :UINT, frame_data_field, :BIG_ENDIAN) + 1 space_packet_length = SPACE_PACKET_HEADER_LENGTH + space_packet_data_field_length if (space_packet_length > frame_data_field.length) if (@prefix_packets) vc.packet_queue << (frame_headers.clone << frame_data_field) else vc.packet_queue << frame_data_field end vc.pending_incomplete_packet_bytes_left = space_packet_length - frame_data_field.length return end if (@prefix_packets) vc.packet_queue << (frame_headers.clone << frame_data_field.slice!(0, space_packet_length)) else vc.packet_queue << frame_data_field.slice!(0, space_packet_length) end end end