EDF存储说明

 EDF文件的结构:

EDF文件包括【文件头信息】和【数据存储区域】,其中,【文件头信息】又分为两部分,【文件头前半部分】和【文件头后半部分】。EDF头文件信息全部使用ascll表示,头文件中每个功能参数描述均使用固定存储长度,相关信息在存储的时候需要左对齐,剩余未使用的空间使用‘ ’(字符空格)补齐;

 

【文件头前半部分】该部分共256字节,记录了采集行为的相关信息,主要包括通道数、采集时间、患者信息、数据总量等信息;

【文件头后半部分】它记录了各个通道的相关参数信息,每个通道的相关参数占用256字节

 

现在我们假设需要存储4个通道的数据,数据通道名称分别是PPG、EEG、ECG、HR,采样频率分别是100Hz、20Hz、3Hz、3Hz,记录时长100秒,存储软件1秒存一次数据。根据这个需求来解释EDF存储格式。

 

【文件头前半部分】:

8字节ASCII码:数据格式版本(0),目前我是用版本0做的实验,版本0存储的是int16类型,低字节在前。存储其他类型,例如float先修改格式版本。

80字节ASCII码:病人信息标识,这个根据实际需求填写,没有需求可以随便填。

80字节ASCII码:记录标识,这个没研究明白,暂时随便填吧。

8字节ASCII码:记录开始日期,顺序为日.月.年,用“.”隔开,例如2023年6月21日:21.03.2023

8字节ASCII码:记录开始时间,顺序为时.分.秒 ,格式要求与日期相同

8字节ASCII码:记录头的字节数 ,此部分记录了文件头数据的总长度,总长度=256+256*通道数量=256+256*4=1280(公式中第一个256是文件头前半部分,第二个256是各个通道的相关参数)

44字节ASCII码:保留

8字节ASCII码:文件中数据记录块数nr,该参数用于解析软件计算edf文件的完整性。

8字节ASCII码:一个数据记录块的记录时间

4字节ASCII码:数据记录的通道数(ns)

 

 

【文件头后半部分】:

16字节ASCII码:ns*标识(如 PPG)

16字节ASCII码:ns*标识(如 EEG)

16字节ASCII码:ns*标识(如 ECG)

16字节ASCII码:ns*标识(如 HR)

 

80 字节ASCII码:传感器类型,如对PPG传感器的一个描述,根据需求写

80 字节ASCII码:传感器类型,如对EEG传感器的一个描述,根据需求写

80 字节ASCII码:传感器类型,如对ECG传感器的一个描述,根据需求写

80 字节ASCII码:传感器类型,如对HR传感器的一个描述,根据需求写

 

8字节ASCII码:物理信号单位,如mV

8字节ASCII码:物理信号单位,如mV

8字节ASCII码:物理信号单位,如%

8字节ASCII码:物理信号单位,如BMP

 

8字节ASCII码:物理信号最小值

8字节ASCII码:物理信号最小值

8字节ASCII码:物理信号最小值

8字节ASCII码:物理信号最小值

 

8字节ASCII码:物理信号最大值

8字节ASCII码:物理信号最大值

8字节ASCII码:物理信号最大值

8字节ASCII码:物理信号最大值

 

8字节ASCII码:数字信号最小值,记录数据超过该值设置的大小,EDFbrowser将显示该值,超出部分被削平,其他通道类似

8字节ASCII码:数字信号最小值

8字节ASCII码:数字信号最小值

8字节ASCII码:数字信号最小值

 

8字节ASCII码:数字信号最大值,记录数据超过该值设置的大小,EDFbrowser将显示该值,超出部分被削平,其他通道类似

8字节ASCII码:数字信号最大值

8字节ASCII码:数字信号最大值

8字节ASCII码:数字信号最大值

 

80字节ASCII码:滤波器信息

80字节ASCII码:滤波器信息

80字节ASCII码:滤波器信息

80字节ASCII码:滤波器信息

 

8字节ASCII码:一个数据记录的采样点数,在一个存储块中本通道的数据个数

8字节ASCII码:一个数据记录的采样点数,在一个存储块中本通道的数据个数

8字节ASCII码:一个数据记录的采样点数,在一个存储块中本通道的数据个数

8字节ASCII码:一个数据记录的采样点数,在一个存储块中本通道的数据个数

 

32字节ASCII码:保留字节

32字节ASCII码:保留字节

32字节ASCII码:保留字节

32字节ASCII码:保留字节

 

数字信号最大最小值与物理信号最大最小值控制波形在EDFbrowser中Y轴的显示范围。我们自己做解析软件可能用不到。

 

 

【数据存储区域】:

第1块存储数据:PPG波形数据+ EEG波形数据+ECG波形数据+HR波形数据

第1块存储数据:PPG波形数据+ EEG波形数据+ECG波形数据+HR波形数据

.

.

.

第n块存储数据:PPG波形数据+ EEG波形数据+ECG波形数据+HR波形数据

 

 

最后给出一个基于QT的例程,无论什么语言,明白了存储原理,存储不是问题。

QString edf_appendData(QString data,int total_len)
{
    QString edf_data;
    edf_data.append(data);
    qDebug()<<edf_data<<data.size();
    for(int i=0;i<(total_len-data.size());i++)
    {
        edf_data.append(' ');
    }
    return edf_data;
}

void MainWindow::EDFwriteData(QDataStream &file_write,qint16 *PPG,qint16 *HuXi,qint16 *SPO2,qint16 *HR)
{
    int i;
    for ( i = 0; i < 100; i++)   //PPG每秒100个数,即100Hz
    {
        file_write << PPG[i]; // 写入信号采样值到文件
    }
    for (i = 0; i <20; i++)    //呼吸每秒20个数,即20Hz
    {
        file_write << *(HuXi+i); // 写入信号采样值到文件
        qDebug()<<*(HuXi+i);
    }
    for (i = 0; i <3; i++)    //血氧每秒3个数,即3Hz
    {
        file_write << *(SPO2+i); // 写入信号采样值到文件
    }
    for (i = 0; i <3; i++)  //脉率每秒3个数,即3Hz
    {
        file_write << *(HR+i); // 写入信号采样值到文件
    }
}


void MainWindow::on_pushButton_clicked()
{


    // 创建并打开EDF文件
    QFile file("exampl.edf");
    if (!file.open(QIODevice::WriteOnly))
    {
        // 文件打开失败处理
        return;
    }
    file.write(edf_appendData("0",8).toUtf8());  //数据格式版本
    file.write(edf_appendData("test",80).toUtf8());  //病人信息标识
    file.write(edf_appendData("112233",80).toUtf8());  //记录标识
    file.write(edf_appendData("23.04.21",8).toUtf8());        //记录开始日期,用.分割,日、月、年
    file.write(edf_appendData("08.30.00",8).toUtf8());        //记录开始时间,用.分割,时分秒
    file.write(edf_appendData("1280",8).toUtf8());        //记录头的字节数 *********计算一下长度********
    file.write(edf_appendData("",44).toUtf8());        //保留字段。
    file.write(edf_appendData("100",8).toUtf8());        //记录块数 如果1秒为一个记录块,那记录块数就是记录总时间
    file.write(edf_appendData("1",8).toUtf8());          //每个记录块的时间长度(以秒为单位)。
    file.write(edf_appendData("4",4).toUtf8());           //指示EDF文件中的信号通道数量

    file.write(edf_appendData("PPG",16).toUtf8());        //信号通道描述
    file.write(edf_appendData("EEG",16).toUtf8());       //信号通道描述
    file.write(edf_appendData("ECG",16).toUtf8());       //信号通道描述
    file.write(edf_appendData("HR",16).toUtf8());         //信号通道描述

    file.write(edf_appendData("Private Edition",80).toUtf8());  //传感器类型,即对传感器的描述
    file.write(edf_appendData("+1Kpa~-1Kpa",80).toUtf8());  //传感器类型,即对传感器的描述
    file.write(edf_appendData("XueYang",80).toUtf8());  //传感器类型,即对传感器的描述
    file.write(edf_appendData("MaiLv",80).toUtf8());  //传感器类型,即对传感器的描述

    file.write(edf_appendData("mV",8).toUtf8());        //信号通道单位
    file.write(edf_appendData("Kpa",8).toUtf8());        //信号通道单位
    file.write(edf_appendData("%",8).toUtf8());        //信号通道单位
    file.write(edf_appendData("BPM",8).toUtf8());        //信号通道单位

    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的物理最小值
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的物理最小值
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的物理最小值
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的物理最小值

    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的物理最大值
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的物理最大值
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的物理最大值
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的物理最大值

    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的数字最小值。
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的数字最小值。
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的数字最小值。
    file.write(edf_appendData("0",8).toUtf8());        //每个信号通道的数字最小值。

    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的数字最大值。
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的数字最大值。
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的数字最大值。
    file.write(edf_appendData("1000",8).toUtf8());        //每个信号通道的数字最大值。

    file.write(edf_appendData("LV BO QI 10Hz",80).toUtf8()); //滤波器信息
    file.write(edf_appendData("LV BO QI 20Hz",80).toUtf8()); //滤波器信息
    file.write(edf_appendData("LV BO QI 20Hz",80).toUtf8()); //滤波器信息
    file.write(edf_appendData("LV BO QI 20Hz",80).toUtf8()); //滤波器信息

    file.write(edf_appendData("100",8).toUtf8());       //一个数据记录块的采样点数
    file.write(edf_appendData("20",8).toUtf8());       //一个数据记录块的采样点数
    file.write(edf_appendData("3",8).toUtf8());       //一个数据记录块的采样点数
    file.write(edf_appendData("3",8).toUtf8());       //一个数据记录块的采样点数

    file.write(edf_appendData("",32).toUtf8());        //保留字段。
    file.write(edf_appendData("",32).toUtf8());        //保留字段。
    file.write(edf_appendData("",32).toUtf8());        //保留字段。
    file.write(edf_appendData("",32).toUtf8());        //保留字段。


    // 创建并写入EDF文件头部
    QDataStream out(&file);
    out.setByteOrder(QDataStream::LittleEndian); // 根据EDF文件规范,设置字节顺序  设置大小端

    qint16 PPG_Buf[100]={0},HuXi_Buf[20]={0},SPO2_Buf[3]={0},HR_Buf[3]={0};
    HR_Buf[0]=1;HR_Buf[1]=20;HR_Buf[2]=30;
    for(int i=0;i<100;i++)
        PPG_Buf[i]=i;
    for(int i=0;i<20;i++)
        HuXi_Buf[i]=i;
    for(int i=0;i<3;i++)
        SPO2_Buf[i]=i;
    for(int i=0;i<3;i++)
        HR_Buf[i]=i*10;

    for (int i = 0;i<100;i++)
    {
       EDFwriteData(out,PPG_Buf,HuXi_Buf,SPO2_Buf,HR_Buf);
    }


    // 关闭文件
    file.close();
}

 

Edf文件对字段长度要求非常严格,错一点都不行

 

水平有限,如有疏漏、错误,还望海涵。

posted @ 2023-06-21 16:05  90后大叔666  阅读(169)  评论(0编辑  收藏  举报