嫦娥工程探月雷达数据解析

PDS4格式

基本介绍

PDS4,是NASA开发的一种专门用于存储行星探索任务的数据系统。数据的后缀有两种:2BL和2B(2CL和2C)。2BL文件是一个XML格式的文件(XML 被设计用来结构化、存储以及传输信息。),里面记录了拍摄器材、拍摄时间和曝光参数等信息,最重要的是它存储了一个文件路径,也就是同名的2B文件,2B文件里面才是实际的图片数据。

探月雷达数据主要结构

简化后的文件结构

<?xml version="1.0" encoding="UTF-8"?>  #声明文档是XML格式,并且使用UTF-8编码
<Product_Observational xmlns="http://pds.nasa.gov/pds4/pds/v1" xmlns:pds="http://pds.nasa.gov/pds4/pds/v1">   #PDS4的特有元素,描述观测数据产品的容器,定义命名空间

    <Identification_Area>
        #识别信息(Identification_Area):包括产品的唯一标识符、版本、标题等。
    </Identification_Area>

    <Observation_Area>
    # 观测信息(Observation_Area):描述了观测的详细信息,包括时间坐标、观测目的、处理级别、科学参数等。
    </Observation_Area>

    <File_Area_Observational>
    #需要了解的核心部分,描述了观测数据文件的组织结构,包括文件名、文件属性、记录格式、字段属性和组结构。通过这些信息,用户可以了解如何访问和解释文件中的数据。
        <File> #存放实际数据的文件名
            <file_name>CE4_GRAS_LPR-1_SCI_N_20201119112501_20201120143000_0155_A.2B</file_name>
            <local_identifier>None</local_identifier>
            <creation_date_time>2020-12-23T02:36:00.000Z</creation_date_time>
            <file_size unit="byte">19565385</file_size>
            <records>2380</records>
        </File>
        <Table_Binary> #二进制数据
            <offset unit="byte">0</offset>
            <records>2380</records>
            <Record_Binary> # 描述单个记录的结构
                <fields>28</fields>
                <groups>1</groups>
                <record_length unit="byte">32883</record_length>
                <Field_Binary> #用于描述存储在二进制文件中的单个字段的属性和结构。
                    <name>FRAME_IDENTIFICATION</name> #字段的名称,提供了字段内容的描述性标识。
                    <field_number>1</field_number>
                    <field_location unit="byte">1</field_location>
                    <data_type>UnsignedByte</data_type>
                    <field_length unit="byte">4</field_length>
                    <field_format>None</field_format>
                    <unit>none</unit>
                    <description>Frame Identifier, 0x146F1111: Channel 1 data</description>
                </Field_Binary>
                <Group_Field_Binary>  # 二进制表中的组结构,描述二进制表中一组字段重复模式的数据
                    <name>ECHO_DATA</name> # 
                    <group_number>1</group_number>
                    <repetitions>8192</repetitions>
                    <fields>1</fields>
                    <groups>0</groups>
                    <group_location unit="byte">115</group_location># 从第几个数据开始
                    <group_length unit="byte">32768</group_length>  #8192*4 byte

                    <Field_Binary>
                        <name>ECHO_DATA</name>   #雷达回波数据
                        <field_number>1</field_number>
                        <field_location unit="byte">1</field_location>
                        <data_type>IEEE754LSBSingle</data_type> #数据格式
                        <field_length unit="byte">4</field_length> #需要重复的数据每个所占的数据大小
                        <field_format>F.6</field_format>
                        <unit>none</unit>
                        <description>There are 8192 echoes, with a sample interval of 2.5ns.</description>
                    </Field_Binary>
                </Group_Field_Binary>
            </Record_Binary>
        </Table_Binary>
    </File_Area_Observational>
</Product_Observational>

数据解析

1.通过import xml.etree.ElementTree这个库,查找XML文件中的元素和字段。

2.获取File_Area_Observational信息,得到存储数据的二进制文件文件名

file_area = root.find('pds:File_Area_Observational', namespaces)

3.打开二进制文件并读取所有数据

    with open(binary_file, 'rb') as bin_file:
        data = bin_file.read()

4.读懂Table_Binary的结构,弄懂每个Field_Binary的含义

5.利用find这个工具,根据结构一层一层的查找需要的数据定义:比如这个字段从第几个字节开始,占用多少个字节,这个字段的格式,将这些数据保存到变量
例如:

    # 获取field_length元素
    field_length_element = field_binary.find('pds:field_length', namespaces)
    if field_length_element is not None:
        field_length = int(field_length_element.text)
        print(f"Field_length: {field_length}")
    else:
        print("field_length_element not found.")

得到这些关键数据

image.png

6.根据这些变量来确定循环执行的次数,每次的偏移量是多少。解析保存数据,然后绘图

image.png

7.得到看结果如下:
嫦娥4号的单个2B数据图像:

image.png

嫦娥5号的一次回波数据:

image.png

嫦娥5号的单个2B数据图像:

image.png

遇到的问题和注意事项

1.嫦娥4号用的是小端存储,嫦娥5号数据用的是大端存储,电脑解析的时候默认是小端存储,需要根据2BL文件中的定义决定解析方式

2.2BL中数据位置(field_location)标注开始是从1开始的(1表示字段从记录的第1个字节开始),python读取2B二进制文件,放入一个一维数组,索引是从0开始的,需要field_location-1 作为数组的索引。最开始看到offset =0 ,所以就按照field_location作为数组的索引来读取,得到的数据有点奇怪。

3.数据量很少,嫦娥4号的数据需要根据X,Y的位置信息,将多个2B文件拼接,但是很多下载链接失效

解析文件

我把我的数据解析的代码和测试数据打包后,放到了这个仓库里边了,下载后直接运行Python文件就可以看到效果,欢迎大家Star。

嫦娥四号数据解析:

import xml.etree.ElementTree as ET
import numpy as np
import matplotlib.pyplot as plt
import struct
# XML文件路径
# xml_file = 'CE4_GRAS_LPR-2B_SCI_N_20201219044001_20201220084500_0163_A.2BL'
xml_file = 'CE4_GRAS_LPR-2A_SCI_N_20201119112501_20201120143000_0155_A.2BL'

# 解析XML文件
tree = ET.parse(xml_file)
root = tree.getroot()

# 定义命名空间
namespaces = {
    'pds': 'http://pds.nasa.gov/pds4/pds/v1',
    'sp': 'http://pds.nasa.gov/pds4/sp/v1'
}

# 获取Identification_Area信息
identification_area = root.find('pds:Identification_Area', namespaces)
logical_identifier = identification_area.find('pds:logical_identifier', namespaces).text
version_id = identification_area.find('pds:version_id', namespaces).text
title = identification_area.find('pds:title', namespaces).text
print(f"Logical Identifier: {logical_identifier}")
print(f"Version ID: {version_id}")
print(f"Title: {title}")

# 获取Observation_Area信息
observation_area = root.find('pds:Observation_Area', namespaces)
start_date_time = observation_area.find('pds:Time_Coordinates/pds:start_date_time', namespaces).text
stop_date_time = observation_area.find('pds:Time_Coordinates/pds:stop_date_time', namespaces).text
print(f"Start Date Time: {start_date_time}")
print(f"Stop Date Time: {stop_date_time}")

# 获取Mission_Area信息
mission_area = observation_area.find('pds:Mission_Area', namespaces)
product_id = mission_area.find('pds:product_id', namespaces).text
product_version = mission_area.find('pds:product_version', namespaces).text
product_level = mission_area.find('pds:product_level', namespaces).text
instrument_name = mission_area.find('pds:instrument_name', namespaces).text
print(f"Product ID: {product_id}")
print(f"Product Version: {product_version}")
print(f"Product Level: {product_level}")
print(f"Instrument Name: {instrument_name}")

# 获取Lander_Location信息
lander_location = mission_area.find('pds:Lander_Location', namespaces)
longitude = lander_location.find('pds:longitude', namespaces).text
latitude = lander_location.find('pds:latitude', namespaces).text
print(f"Lander Longitude: {longitude}")
print(f"Lander Latitude: {latitude}")

# # 获取File_Area_Observational信息
file_area = root.find('pds:File_Area_Observational', namespaces)
# 解析文件信息
file = file_area.find('pds:File', namespaces)
file_name = file.find('pds:file_name', namespaces).text
creation_date_time = file.find('pds:creation_date_time', namespaces).text
file_size = file.find('pds:file_size', namespaces).text
print(f"File Name: {file_name}")
print(f"Creation Date Time: {creation_date_time}")
print(f"File Size: {file_size}")

binary_file = file_name
# 打开二进制文件并读取所有数据
with open(binary_file, 'rb') as bin_file:
    data = bin_file.read()
# 解析记录二进制信息
table_binary = file_area.find('pds:Table_Binary', namespaces)
if table_binary is not None:
    record_binary = table_binary.find('pds:Record_Binary', namespaces)
    if record_binary is not None:
        # 遍历Record_Binary下的所有Field_Binary元素
        for field_binary in record_binary.findall('pds:Field_Binary', namespaces):
            # 获取name元素
            name_element = field_binary.find('pds:name', namespaces)
            if name_element is not None:
                field_name = name_element.text
                print(f"Field Name: {field_name}")
            else:
                print("Name element not found.")

            # 获取field_location元素
            field_location_element = field_binary.find('pds:field_location', namespaces)
            if field_location_element is not None:
                field_location = int(field_location_element.text)
                print(f"field_location: {field_location}")
            else:
                print("field_location_element not found.")

            # 获取data_type元素
            data_type_element = field_binary.find('pds:data_type', namespaces)
            if data_type_element is not None:
                data_type = data_type_element.text
                print(f"data_type: {data_type}")
            else:
                print("data_type_element not found.")

            # 获取field_length元素
            field_length_element = field_binary.find('pds:field_length', namespaces)
            if field_length_element is not None:
                field_length = int(field_length_element.text)
                print(f"Field_length: {field_length}")
            else:
                print("field_length_element not found.")

        group_field = record_binary.find('pds:Group_Field_Binary', namespaces)
        if group_field is not None:
            group_name = group_field.find('pds:name', namespaces).text
            repetitions = int(group_field.find('pds:repetitions', namespaces).text)
            fields = int(group_field.find('pds:fields', namespaces).text)
            groups = int(group_field.find('pds:groups', namespaces).text)
            group_location = int(group_field.find('pds:group_location', namespaces).text)
            group_length = int(group_field.find('pds:group_length', namespaces).text)
            group_field_binary =group_field.find('pds:Field_Binary', namespaces)
            group_field_length =int(group_field_binary.find('pds:field_length', namespaces).text)
            # 准备存储解析后的数据
            science_data = np.zeros(repetitions, dtype=np.float32)
            # 遍历group指定的次数
            for i in range(repetitions):
                sample_start = group_location -1 + i * group_field_length# 每个样本大小为4字节
                sample = np.frombuffer(data[sample_start:sample_start + group_field_length], dtype='<f4')  # 小端序
                science_data[i] = sample[0]
            plt.plot(science_data[:])
            plt.show()
        else:
            print("Group_Field_Binary element not found.")
    else:
        print("Record_Binary element not found.")
else:
    print("Table_Binary element not found.")

嫦娥5号的数据解析:

import xml.etree.ElementTree as ET
import numpy as np
import matplotlib.pyplot as plt

def parse_pds4_file(file_path):
    # 加载和解析XML文件
    tree = ET.parse(file_path)
    root = tree.getroot()

    # 处理命名空间
    ns = {
        'pds': 'http://pds.nasa.gov/pds4/pds/v1',
        'sp': 'http://pds.nasa.gov/pds4/sp/v1'
    }

    # 获取File Area信息
    file_area = root.find('pds:File_Area_Observational', ns)
    file = file_area.find('pds:File', ns)
    file_name = file.find('pds:file_name', ns).text

    # 读取二进制数据
    with open(file_name, 'rb') as f:
        data = f.read()

    # 解析科学数据
    # 根据XML中的group_location和group_length
    offset = 311-1
    num_records = 132
    record_length = 12311
    num_samples = 3000
    sample_size = 4  # 每个数据点占用4个字节

    # 初始化数组
    science_data = np.zeros((num_records, num_samples), dtype=np.float32)

    # 读取科学数据
    for i in range(num_records):
        record_start = offset + i * record_length
        for j in range(num_samples):
            sample_start = record_start + j * sample_size
            sample = np.frombuffer(data[sample_start:sample_start + sample_size], dtype='>f4')
            # sample=sample.astype('>f4')
            science_data[i, j] = sample[0]

    # 绘制科学数据
    plt.figure(figsize=(10, 6))
    # plt.imshow(science_data.T, cmap='viridis', aspect='auto')
    # plt.colorbar(label='Data Value')
    # plt.title('Science Data Image')
    # plt.xlabel('X Axis')
    # plt.ylabel('Y Axis')
    # plt.savefig('science_data_image.png')
    plt.plot(science_data[1,:])
    plt.show()

if __name__ == "__main__":
    file_path = 'CE5-L_GRAS_LRPR-A_SCI_N_20201201171559_20201201173326_0001_A.2BL'  # 替换为你的文件路径
    parse_pds4_file(file_path)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇