192 lines
6.5 KiB
Python
192 lines
6.5 KiB
Python
#
|
|
# Authors: The MNE-Python contributors.
|
|
# License: BSD-3-Clause
|
|
# Copyright the MNE-Python contributors.
|
|
|
|
import os
|
|
import re
|
|
|
|
import numpy as np
|
|
|
|
from ...utils import _pl, _soft_import
|
|
|
|
|
|
def _extract(tags, filepath=None, obj=None):
|
|
"""Extract info from XML."""
|
|
_soft_import("defusedxml", "reading EGI MFF data")
|
|
from defusedxml.minidom import parse
|
|
|
|
if obj is not None:
|
|
fileobj = obj
|
|
elif filepath is not None:
|
|
fileobj = parse(filepath)
|
|
else:
|
|
raise ValueError("There is not object or file to extract data")
|
|
infoxml = dict()
|
|
for tag in tags:
|
|
value = fileobj.getElementsByTagName(tag)
|
|
infoxml[tag] = []
|
|
for i in range(len(value)):
|
|
infoxml[tag].append(value[i].firstChild.data)
|
|
return infoxml
|
|
|
|
|
|
def _get_gains(filepath):
|
|
"""Parse gains."""
|
|
_soft_import("defusedxml", "reading EGI MFF data")
|
|
from defusedxml.minidom import parse
|
|
|
|
file_obj = parse(filepath)
|
|
objects = file_obj.getElementsByTagName("calibration")
|
|
gains = dict()
|
|
for ob in objects:
|
|
value = ob.getElementsByTagName("type")
|
|
if value[0].firstChild.data == "GCAL":
|
|
data_g = _extract(["ch"], obj=ob)["ch"]
|
|
gains.update(gcal=np.asarray(data_g, dtype=np.float64))
|
|
elif value[0].firstChild.data == "ICAL":
|
|
data_g = _extract(["ch"], obj=ob)["ch"]
|
|
gains.update(ical=np.asarray(data_g, dtype=np.float64))
|
|
return gains
|
|
|
|
|
|
def _get_ep_info(filepath):
|
|
"""Get epoch info."""
|
|
_soft_import("defusedxml", "reading EGI MFF data")
|
|
from defusedxml.minidom import parse
|
|
|
|
epochfile = filepath + "/epochs.xml"
|
|
epochlist = parse(epochfile)
|
|
epochs = epochlist.getElementsByTagName("epoch")
|
|
keys = ("first_samps", "last_samps", "first_blocks", "last_blocks")
|
|
epoch_info = {key: list() for key in keys}
|
|
for epoch in epochs:
|
|
ep_begin = int(epoch.getElementsByTagName("beginTime")[0].firstChild.data)
|
|
ep_end = int(epoch.getElementsByTagName("endTime")[0].firstChild.data)
|
|
first_block = int(epoch.getElementsByTagName("firstBlock")[0].firstChild.data)
|
|
last_block = int(epoch.getElementsByTagName("lastBlock")[0].firstChild.data)
|
|
epoch_info["first_samps"].append(ep_begin)
|
|
epoch_info["last_samps"].append(ep_end)
|
|
epoch_info["first_blocks"].append(first_block)
|
|
epoch_info["last_blocks"].append(last_block)
|
|
# Don't turn into ndarray here, keep native int because it can deal with
|
|
# huge numbers (could use np.uint64 but it's more work)
|
|
return epoch_info
|
|
|
|
|
|
def _get_blocks(filepath):
|
|
"""Get info from meta data blocks."""
|
|
binfile = os.path.join(filepath)
|
|
n_blocks = 0
|
|
samples_block = []
|
|
header_sizes = []
|
|
n_channels = []
|
|
sfreq = []
|
|
# Meta data consists of:
|
|
# * 1 byte of flag (1 for meta data, 0 for data)
|
|
# * 1 byte of header size
|
|
# * 1 byte of block size
|
|
# * 1 byte of n_channels
|
|
# * n_channels bytes of offsets
|
|
# * n_channels bytes of sigfreqs?
|
|
with open(binfile, "rb") as fid:
|
|
fid.seek(0, 2) # go to end of file
|
|
file_length = fid.tell()
|
|
block_size = file_length
|
|
fid.seek(0)
|
|
position = 0
|
|
while position < file_length:
|
|
block = _block_r(fid)
|
|
if block is None:
|
|
samples_block.append(samples_block[n_blocks - 1])
|
|
n_blocks += 1
|
|
fid.seek(block_size, 1)
|
|
position = fid.tell()
|
|
continue
|
|
block_size = block["block_size"]
|
|
header_size = block["header_size"]
|
|
header_sizes.append(header_size)
|
|
samples_block.append(block["nsamples"])
|
|
n_blocks += 1
|
|
fid.seek(block_size, 1)
|
|
sfreq.append(block["sfreq"])
|
|
n_channels.append(block["nc"])
|
|
position = fid.tell()
|
|
|
|
if any([n != n_channels[0] for n in n_channels]):
|
|
raise RuntimeError("All the blocks don't have the same amount of channels.")
|
|
if any([f != sfreq[0] for f in sfreq]):
|
|
raise RuntimeError("All the blocks don't have the same sampling frequency.")
|
|
if len(samples_block) < 1:
|
|
raise RuntimeError("There seems to be no data")
|
|
samples_block = np.array(samples_block)
|
|
signal_blocks = dict(
|
|
n_channels=n_channels[0],
|
|
sfreq=sfreq[0],
|
|
n_blocks=n_blocks,
|
|
samples_block=samples_block,
|
|
header_sizes=header_sizes,
|
|
)
|
|
return signal_blocks
|
|
|
|
|
|
def _get_signalfname(filepath):
|
|
"""Get filenames."""
|
|
_soft_import("defusedxml", "reading EGI MFF data")
|
|
from defusedxml.minidom import parse
|
|
|
|
listfiles = os.listdir(filepath)
|
|
binfiles = list(
|
|
f for f in listfiles if "signal" in f and f[-4:] == ".bin" and f[0] != "."
|
|
)
|
|
all_files = {}
|
|
infofiles = list()
|
|
for binfile in binfiles:
|
|
bin_num_str = re.search(r"\d+", binfile).group()
|
|
infofile = "info" + bin_num_str + ".xml"
|
|
infofiles.append(infofile)
|
|
infobjfile = os.path.join(filepath, infofile)
|
|
infobj = parse(infobjfile)
|
|
if len(infobj.getElementsByTagName("EEG")):
|
|
signal_type = "EEG"
|
|
elif len(infobj.getElementsByTagName("PNSData")):
|
|
signal_type = "PNS"
|
|
all_files[signal_type] = {
|
|
"signal": f"signal{bin_num_str}.bin",
|
|
"info": infofile,
|
|
}
|
|
if "EEG" not in all_files:
|
|
raise FileNotFoundError(
|
|
"Could not find any EEG data in the %d file%s found in %s:\n%s"
|
|
% (len(infofiles), _pl(infofiles), filepath, "\n".join(infofiles))
|
|
)
|
|
return all_files
|
|
|
|
|
|
def _block_r(fid):
|
|
"""Read meta data."""
|
|
if np.fromfile(fid, dtype=np.dtype("i4"), count=1).item() != 1: # not meta
|
|
return None
|
|
header_size = np.fromfile(fid, dtype=np.dtype("i4"), count=1).item()
|
|
block_size = np.fromfile(fid, dtype=np.dtype("i4"), count=1).item()
|
|
hl = int(block_size / 4)
|
|
nc = np.fromfile(fid, dtype=np.dtype("i4"), count=1).item()
|
|
nsamples = int(hl / nc)
|
|
np.fromfile(fid, dtype=np.dtype("i4"), count=nc) # sigoffset
|
|
sigfreq = np.fromfile(fid, dtype=np.dtype("i4"), count=nc)
|
|
depth = sigfreq[0] & 0xFF
|
|
if depth != 32:
|
|
raise ValueError("I do not know how to read this MFF (depth != 32)")
|
|
sfreq = sigfreq[0] >> 8
|
|
count = int(header_size / 4 - (4 + 2 * nc))
|
|
np.fromfile(fid, dtype=np.dtype("i4"), count=count) # sigoffset
|
|
block = dict(
|
|
nc=nc,
|
|
hl=hl,
|
|
nsamples=nsamples,
|
|
block_size=block_size,
|
|
header_size=header_size,
|
|
sfreq=sfreq,
|
|
)
|
|
return block
|