<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 The frames are CCSDS concatenated frames with a frames size of 220 bytes.
Step1:  AOS frames
AOS frames come from spacecraft 108 and virtual channels 1 and 2. Other combinations are most likely to corruted frames despite the fact that the Reed-Solomon decoder was successful.
Step2:  Virtual channel 1
The vast majority of frames belong to virtual channel 1, which seems to send real-time telemetry.
Step3:  We need to sort the data, since the different files we've loaded up are not in chronological order.
Step4:  There are space packets in may APIDs. The contents of each APID are shown belown in plot form, but it's not easy to guess what any of the values mean.
<ASSISTANT_TASK:>
Python Code:
def load_frames(path):
    frame_size = 220
    frames = np.fromfile(path, dtype = 'uint8')
    frames = frames[:frames.size//frame_size*frame_size].reshape((-1, frame_size))
    return frames
frames = load_frames('ATA_2021-09-18/ce5_frames.u8')
Explanation: The frames are CCSDS concatenated frames with a frames size of 220 bytes.
End of explanation
aos = [CE5_AOSFrame.parse(f) for f in frames]
collections.Counter([a.primary_header.transfer_frame_version_number for a in aos])
collections.Counter([a.primary_header.spacecraft_id for a in aos
                    if a.primary_header.transfer_frame_version_number == 1])
collections.Counter([a.primary_header.virtual_channel_id for a in aos
                    if a.primary_header.transfer_frame_version_number == 1
                    and a.primary_header.spacecraft_id == 108])
Explanation: AOS frames
AOS frames come from spacecraft 108 and virtual channels 1 and 2. Other combinations are most likely to corruted frames despite the fact that the Reed-Solomon decoder was successful.
End of explanation
[a.primary_header for a in aos if a.primary_header.virtual_channel_id == 1][:10]
vc1 = [a for a in aos if a.primary_header.virtual_channel_id == 1]
fc = np.array([a.primary_header.virtual_channel_frame_count for a in vc1])
[a.insert_zone for a in aos[:10]]
t_vc1 = np.datetime64('2012-08-01') + np.timedelta64(1, 's') * np.array([a.insert_zone.timestamp for a in vc1])
t_vc1[0]
plt.figure(figsize = (10,6), facecolor = 'w')
plt.plot(t_vc1, fc, '.')
plt.title("Chang'e 5 virtual channel 1 timestamps")
plt.xlabel('AOS frame timestamp')
plt.ylabel('AOS virtual channel frame counter');
plt.figure(figsize = (10,6), facecolor = 'w')
plt.plot(t_vc1[1:], np.diff(fc)-1, '.')
plt.title("Chang'e 5 spacecraft 91 virtual channel 1 frame loss")
plt.xlabel('AOS frame timestamp')
plt.ylabel('Frame loss');
Explanation: Virtual channel 1
The vast majority of frames belong to virtual channel 1, which seems to send real-time telemetry.
End of explanation
vc1_packets = list(ccsds.extract_space_packets(vc1, 108, 1, get_timestamps = True))
vc1_sp_headers = [ccsds.SpacePacketPrimaryHeader.parse(p[0]) for p in vc1_packets]
Explanation: We need to sort the data, since the different files we've loaded up are not in chronological order.
End of explanation
vc1_apids = collections.Counter([p.APID for p in vc1_sp_headers])
vc1_apids
vc1_by_apid = {apid : [p for h,p in zip(vc1_sp_headers, vc1_packets)
                           if h.APID == apid] for apid in vc1_apids}
plot_apids(vc1_by_apid, 108, 1)
Explanation: There are space packets in may APIDs. The contents of each APID are shown belown in plot form, but it's not easy to guess what any of the values mean.
End of explanation
<END_TASK>