531 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			531 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Authors: The MNE-Python contributors.
 | 
						|
# License: BSD-3-Clause
 | 
						|
# Copyright the MNE-Python contributors.
 | 
						|
 | 
						|
import calendar
 | 
						|
import datetime
 | 
						|
import os.path as op
 | 
						|
 | 
						|
import numpy as np
 | 
						|
from scipy.spatial.distance import cdist
 | 
						|
 | 
						|
from ..._fiff._digitization import DigPoint, _make_dig_points
 | 
						|
from ..._fiff.constants import FIFF
 | 
						|
from ..._fiff.meas_info import _empty_info
 | 
						|
from ..._fiff.utils import _read_segments_file
 | 
						|
from ...transforms import Transform, apply_trans, get_ras_to_neuromag_trans
 | 
						|
from ...utils import _check_fname, logger, verbose, warn
 | 
						|
from ..base import BaseRaw
 | 
						|
from .utils import _load_mne_locs, _read_pos
 | 
						|
 | 
						|
 | 
						|
@verbose
 | 
						|
def read_raw_artemis123(
 | 
						|
    input_fname, preload=False, verbose=None, pos_fname=None, add_head_trans=True
 | 
						|
) -> "RawArtemis123":
 | 
						|
    """Read Artemis123 data as raw object.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    input_fname : path-like
 | 
						|
        Path to the data file (extension ``.bin``). The header file with the
 | 
						|
        same file name stem and an extension ``.txt`` is expected to be found
 | 
						|
        in the same directory.
 | 
						|
    %(preload)s
 | 
						|
    %(verbose)s
 | 
						|
    pos_fname : path-like | None
 | 
						|
        If not None, load digitized head points from this file.
 | 
						|
    add_head_trans : bool (default True)
 | 
						|
        If True attempt to perform initial head localization. Compute initial
 | 
						|
        device to head coordinate transform using HPI coils. If no
 | 
						|
        HPI coils are in info['dig'] hpi coils are assumed to be in canonical
 | 
						|
        order of fiducial points (nas, rpa, lpa).
 | 
						|
 | 
						|
    Returns
 | 
						|
    -------
 | 
						|
    raw : instance of Raw
 | 
						|
        A Raw object containing the data.
 | 
						|
 | 
						|
    See Also
 | 
						|
    --------
 | 
						|
    mne.io.Raw : Documentation of attributes and methods.
 | 
						|
    """
 | 
						|
    return RawArtemis123(
 | 
						|
        input_fname,
 | 
						|
        preload=preload,
 | 
						|
        verbose=verbose,
 | 
						|
        pos_fname=pos_fname,
 | 
						|
        add_head_trans=add_head_trans,
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def _get_artemis123_info(fname, pos_fname=None):
 | 
						|
    """Generate info struct from artemis123 header file."""
 | 
						|
    fname = op.splitext(fname)[0]
 | 
						|
    header = fname + ".txt"
 | 
						|
 | 
						|
    logger.info("Reading header...")
 | 
						|
 | 
						|
    # key names for artemis channel info...
 | 
						|
    chan_keys = [
 | 
						|
        "name",
 | 
						|
        "scaling",
 | 
						|
        "FLL_Gain",
 | 
						|
        "FLL_Mode",
 | 
						|
        "FLL_HighPass",
 | 
						|
        "FLL_AutoReset",
 | 
						|
        "FLL_ResetLock",
 | 
						|
    ]
 | 
						|
 | 
						|
    header_info = dict()
 | 
						|
    header_info["filter_hist"] = []
 | 
						|
    header_info["comments"] = ""
 | 
						|
    header_info["channels"] = []
 | 
						|
 | 
						|
    with open(header) as fid:
 | 
						|
        # section flag
 | 
						|
        # 0 - None
 | 
						|
        # 1 - main header
 | 
						|
        # 2 - channel header
 | 
						|
        # 3 - comments
 | 
						|
        # 4 - length
 | 
						|
        # 5 - filtering History
 | 
						|
        sectionFlag = 0
 | 
						|
        for line in fid:
 | 
						|
            # skip emptylines or header line for channel info
 | 
						|
            if (not line.strip()) or (sectionFlag == 2 and line.startswith("DAQ Map")):
 | 
						|
                continue
 | 
						|
 | 
						|
            # set sectionFlag
 | 
						|
            if line.startswith("<end"):
 | 
						|
                sectionFlag = 0
 | 
						|
            elif line.startswith("<start main header>"):
 | 
						|
                sectionFlag = 1
 | 
						|
            elif line.startswith("<start per channel header>"):
 | 
						|
                sectionFlag = 2
 | 
						|
            elif line.startswith("<start comments>"):
 | 
						|
                sectionFlag = 3
 | 
						|
            elif line.startswith("<start length>"):
 | 
						|
                sectionFlag = 4
 | 
						|
            elif line.startswith("<start filtering history>"):
 | 
						|
                sectionFlag = 5
 | 
						|
            else:
 | 
						|
                # parse header info lines
 | 
						|
                # part of main header - lines are name value pairs
 | 
						|
                if sectionFlag == 1:
 | 
						|
                    values = line.strip().split("\t")
 | 
						|
                    if len(values) == 1:
 | 
						|
                        values.append("")
 | 
						|
                    header_info[values[0]] = values[1]
 | 
						|
                # part of channel header - lines are Channel Info
 | 
						|
                elif sectionFlag == 2:
 | 
						|
                    values = line.strip().split("\t")
 | 
						|
                    if len(values) != 7:
 | 
						|
                        raise OSError(
 | 
						|
                            f"Error parsing line \n\t:{line}\nfrom file {header}"
 | 
						|
                        )
 | 
						|
                    tmp = dict()
 | 
						|
                    for k, v in zip(chan_keys, values):
 | 
						|
                        tmp[k] = v
 | 
						|
                    header_info["channels"].append(tmp)
 | 
						|
                elif sectionFlag == 3:
 | 
						|
                    header_info["comments"] = f"{header_info['comments']}{line.strip()}"
 | 
						|
                elif sectionFlag == 4:
 | 
						|
                    header_info["num_samples"] = int(line.strip())
 | 
						|
                elif sectionFlag == 5:
 | 
						|
                    header_info["filter_hist"].append(line.strip())
 | 
						|
 | 
						|
    for k in [
 | 
						|
        "Temporal Filter Active?",
 | 
						|
        "Decimation Active?",
 | 
						|
        "Spatial Filter Active?",
 | 
						|
    ]:
 | 
						|
        if header_info[k] != "FALSE":
 | 
						|
            warn(f"{k} - set to but is not supported")
 | 
						|
    if header_info["filter_hist"]:
 | 
						|
        warn("Non-Empty Filter history found, BUT is not supported")
 | 
						|
 | 
						|
    # build mne info struct
 | 
						|
    info = _empty_info(float(header_info["DAQ Sample Rate"]))
 | 
						|
 | 
						|
    # Attempt to get time/date from fname
 | 
						|
    # Artemis123 files saved from the scanner observe the following
 | 
						|
    # naming convention 'Artemis_Data_YYYY-MM-DD-HHh-MMm_[chosen by user].bin'
 | 
						|
    try:
 | 
						|
        date = datetime.datetime.strptime(
 | 
						|
            op.basename(fname).split("_")[2], "%Y-%m-%d-%Hh-%Mm"
 | 
						|
        )
 | 
						|
        meas_date = (calendar.timegm(date.utctimetuple()), 0)
 | 
						|
    except Exception:
 | 
						|
        meas_date = None
 | 
						|
 | 
						|
    # build subject info must be an integer (as per FIFF)
 | 
						|
    try:
 | 
						|
        subject_info = {"id": int(header_info["Subject ID"])}
 | 
						|
    except ValueError:
 | 
						|
        subject_info = {"id": 0}
 | 
						|
 | 
						|
    # build description
 | 
						|
    desc = ""
 | 
						|
    for k in ["Purpose", "Notes"]:
 | 
						|
        desc += f"{k} : {header_info[k]}\n"
 | 
						|
    desc += f"Comments : {header_info['comments']}"
 | 
						|
 | 
						|
    info.update(
 | 
						|
        {
 | 
						|
            "meas_date": meas_date,
 | 
						|
            "description": desc,
 | 
						|
            "subject_info": subject_info,
 | 
						|
            "proj_name": header_info["Project Name"],
 | 
						|
        }
 | 
						|
    )
 | 
						|
 | 
						|
    # Channel Names by type
 | 
						|
    ref_mag_names = ["REF_001", "REF_002", "REF_003", "REF_004", "REF_005", "REF_006"]
 | 
						|
 | 
						|
    ref_grad_names = ["REF_007", "REF_008", "REF_009", "REF_010", "REF_011", "REF_012"]
 | 
						|
 | 
						|
    # load mne loc dictionary
 | 
						|
    loc_dict = _load_mne_locs()
 | 
						|
    info["chs"] = []
 | 
						|
    bads = []
 | 
						|
 | 
						|
    for i, chan in enumerate(header_info["channels"]):
 | 
						|
        # build chs struct
 | 
						|
        t = {
 | 
						|
            "cal": float(chan["scaling"]),
 | 
						|
            "ch_name": chan["name"],
 | 
						|
            "logno": i + 1,
 | 
						|
            "scanno": i + 1,
 | 
						|
            "range": 1.0,
 | 
						|
            "unit_mul": FIFF.FIFF_UNITM_NONE,
 | 
						|
            "coord_frame": FIFF.FIFFV_COORD_DEVICE,
 | 
						|
        }
 | 
						|
        # REF_018 has a zero cal which can cause problems. Let's set it to
 | 
						|
        # a value of another ref channel to make writers/readers happy.
 | 
						|
        if t["cal"] == 0:
 | 
						|
            t["cal"] = 4.716e-10
 | 
						|
            bads.append(t["ch_name"])
 | 
						|
        t["loc"] = loc_dict.get(chan["name"], np.zeros(12))
 | 
						|
 | 
						|
        if chan["name"].startswith("MEG"):
 | 
						|
            t["coil_type"] = FIFF.FIFFV_COIL_ARTEMIS123_GRAD
 | 
						|
            t["kind"] = FIFF.FIFFV_MEG_CH
 | 
						|
            # While gradiometer units are T/m, the meg sensors referred to as
 | 
						|
            # gradiometers report the field difference between 2 pick-up coils.
 | 
						|
            # Therefore the units of the measurements should be T
 | 
						|
            # *AND* the baseline (difference between pickup coils)
 | 
						|
            # should not be used in leadfield / forwardfield computations.
 | 
						|
            t["unit"] = FIFF.FIFF_UNIT_T
 | 
						|
            t["unit_mul"] = FIFF.FIFF_UNITM_F
 | 
						|
 | 
						|
        # 3 axis reference magnetometers
 | 
						|
        elif chan["name"] in ref_mag_names:
 | 
						|
            t["coil_type"] = FIFF.FIFFV_COIL_ARTEMIS123_REF_MAG
 | 
						|
            t["kind"] = FIFF.FIFFV_REF_MEG_CH
 | 
						|
            t["unit"] = FIFF.FIFF_UNIT_T
 | 
						|
            t["unit_mul"] = FIFF.FIFF_UNITM_F
 | 
						|
 | 
						|
        # reference gradiometers
 | 
						|
        elif chan["name"] in ref_grad_names:
 | 
						|
            t["coil_type"] = FIFF.FIFFV_COIL_ARTEMIS123_REF_GRAD
 | 
						|
            t["kind"] = FIFF.FIFFV_REF_MEG_CH
 | 
						|
            # While gradiometer units are T/m, the meg sensors referred to as
 | 
						|
            # gradiometers report the field difference between 2 pick-up coils.
 | 
						|
            # Therefore the units of the measurements should be T
 | 
						|
            # *AND* the baseline (difference between pickup coils)
 | 
						|
            # should not be used in leadfield / forwardfield computations.
 | 
						|
            t["unit"] = FIFF.FIFF_UNIT_T
 | 
						|
            t["unit_mul"] = FIFF.FIFF_UNITM_F
 | 
						|
 | 
						|
        # other reference channels are unplugged and should be ignored.
 | 
						|
        elif chan["name"].startswith("REF"):
 | 
						|
            t["coil_type"] = FIFF.FIFFV_COIL_NONE
 | 
						|
            t["kind"] = FIFF.FIFFV_MISC_CH
 | 
						|
            t["unit"] = FIFF.FIFF_UNIT_V
 | 
						|
            bads.append(t["ch_name"])
 | 
						|
 | 
						|
        elif chan["name"].startswith(("AUX", "TRG", "MIO")):
 | 
						|
            t["coil_type"] = FIFF.FIFFV_COIL_NONE
 | 
						|
            t["unit"] = FIFF.FIFF_UNIT_V
 | 
						|
            if chan["name"].startswith("TRG"):
 | 
						|
                t["kind"] = FIFF.FIFFV_STIM_CH
 | 
						|
            else:
 | 
						|
                t["kind"] = FIFF.FIFFV_MISC_CH
 | 
						|
        else:
 | 
						|
            raise ValueError(
 | 
						|
                f'Channel does not match expected channel Types:"{chan["name"]}"'
 | 
						|
            )
 | 
						|
 | 
						|
        # incorporate multiplier (unit_mul) into calibration
 | 
						|
        t["cal"] *= 10 ** t["unit_mul"]
 | 
						|
        t["unit_mul"] = FIFF.FIFF_UNITM_NONE
 | 
						|
 | 
						|
        # append this channel to the info
 | 
						|
        info["chs"].append(t)
 | 
						|
        if chan["FLL_ResetLock"] == "TRUE":
 | 
						|
            bads.append(t["ch_name"])
 | 
						|
 | 
						|
    # HPI information
 | 
						|
    # print header_info.keys()
 | 
						|
    hpi_sub = dict()
 | 
						|
    # Don't know what event_channel is don't think we have it HPIs are either
 | 
						|
    # always on or always off.
 | 
						|
    # hpi_sub['event_channel'] = ???
 | 
						|
    hpi_sub["hpi_coils"] = [dict(), dict(), dict(), dict()]
 | 
						|
    hpi_coils = [dict(), dict(), dict(), dict()]
 | 
						|
    drive_channels = ["MIO_001", "MIO_003", "MIO_009", "MIO_011"]
 | 
						|
    key_base = "Head Tracking %s %d"
 | 
						|
 | 
						|
    # set default HPI frequencies
 | 
						|
    if info["sfreq"] == 1000:
 | 
						|
        default_freqs = [140, 150, 160, 40]
 | 
						|
    else:
 | 
						|
        default_freqs = [700, 750, 800, 40]
 | 
						|
 | 
						|
    for i in range(4):
 | 
						|
        # build coil structure
 | 
						|
        hpi_coils[i]["number"] = i + 1
 | 
						|
        hpi_coils[i]["drive_chan"] = drive_channels[i]
 | 
						|
        this_freq = header_info.pop(key_base % ("Frequency", i + 1), default_freqs[i])
 | 
						|
        hpi_coils[i]["coil_freq"] = this_freq
 | 
						|
 | 
						|
        # check if coil is on
 | 
						|
        if header_info[key_base % ("Channel", i + 1)] == "OFF":
 | 
						|
            hpi_sub["hpi_coils"][i]["event_bits"] = [0]
 | 
						|
        else:
 | 
						|
            hpi_sub["hpi_coils"][i]["event_bits"] = [256]
 | 
						|
 | 
						|
    info["hpi_subsystem"] = hpi_sub
 | 
						|
    info["hpi_meas"] = [{"hpi_coils": hpi_coils}]
 | 
						|
    # read in digitized points if supplied
 | 
						|
    if pos_fname is not None:
 | 
						|
        info["dig"] = _read_pos(pos_fname)
 | 
						|
    else:
 | 
						|
        info["dig"] = []
 | 
						|
 | 
						|
    info._unlocked = False
 | 
						|
    info._update_redundant()
 | 
						|
    # reduce info['bads'] to unique set
 | 
						|
    info["bads"] = list(set(bads))
 | 
						|
    del bads
 | 
						|
    return info, header_info
 | 
						|
 | 
						|
 | 
						|
class RawArtemis123(BaseRaw):
 | 
						|
    """Raw object from Artemis123 file.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    ----------
 | 
						|
    input_fname : path-like
 | 
						|
        Path to the Artemis123 data file (ending in ``'.bin'``).
 | 
						|
    %(preload)s
 | 
						|
    %(verbose)s
 | 
						|
 | 
						|
    See Also
 | 
						|
    --------
 | 
						|
    mne.io.Raw : Documentation of attributes and methods.
 | 
						|
    """
 | 
						|
 | 
						|
    @verbose
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        input_fname,
 | 
						|
        preload=False,
 | 
						|
        verbose=None,
 | 
						|
        pos_fname=None,
 | 
						|
        add_head_trans=True,
 | 
						|
    ):
 | 
						|
        from ...chpi import (
 | 
						|
            _fit_coil_order_dev_head_trans,
 | 
						|
            compute_chpi_amplitudes,
 | 
						|
            compute_chpi_locs,
 | 
						|
        )
 | 
						|
 | 
						|
        input_fname = str(_check_fname(input_fname, "read", True, "input_fname"))
 | 
						|
        fname, ext = op.splitext(input_fname)
 | 
						|
        if ext == ".txt":
 | 
						|
            input_fname = fname + ".bin"
 | 
						|
        elif ext != ".bin":
 | 
						|
            raise RuntimeError(
 | 
						|
                'Valid artemis123 files must end in "txt"' + ' or ".bin".'
 | 
						|
            )
 | 
						|
 | 
						|
        if not op.exists(input_fname):
 | 
						|
            raise RuntimeError(f"{input_fname} - Not Found")
 | 
						|
 | 
						|
        info, header_info = _get_artemis123_info(input_fname, pos_fname=pos_fname)
 | 
						|
 | 
						|
        last_samps = [header_info.get("num_samples", 1) - 1]
 | 
						|
 | 
						|
        super().__init__(
 | 
						|
            info,
 | 
						|
            preload,
 | 
						|
            filenames=[input_fname],
 | 
						|
            raw_extras=[header_info],
 | 
						|
            last_samps=last_samps,
 | 
						|
            orig_format="single",
 | 
						|
            verbose=verbose,
 | 
						|
        )
 | 
						|
 | 
						|
        if add_head_trans:
 | 
						|
            n_hpis = 0
 | 
						|
            for d in info["hpi_subsystem"]["hpi_coils"]:
 | 
						|
                if d["event_bits"] == [256]:
 | 
						|
                    n_hpis += 1
 | 
						|
            if n_hpis < 3:
 | 
						|
                warn(
 | 
						|
                    f"{n_hpis:d} HPIs active. At least 3 needed to perform"
 | 
						|
                    "head localization\n *NO* head localization performed"
 | 
						|
                )
 | 
						|
            else:
 | 
						|
                # Localized HPIs using the 1st 250 milliseconds of data.
 | 
						|
                with info._unlock():
 | 
						|
                    info["hpi_results"] = [
 | 
						|
                        dict(
 | 
						|
                            dig_points=[
 | 
						|
                                dict(
 | 
						|
                                    r=np.zeros(3),
 | 
						|
                                    coord_frame=FIFF.FIFFV_COORD_DEVICE,
 | 
						|
                                    ident=ii + 1,
 | 
						|
                                )
 | 
						|
                                for ii in range(n_hpis)
 | 
						|
                            ],
 | 
						|
                            coord_trans=Transform("meg", "head"),
 | 
						|
                        )
 | 
						|
                    ]
 | 
						|
                coil_amplitudes = compute_chpi_amplitudes(
 | 
						|
                    self, tmin=0, tmax=0.25, t_window=0.25, t_step_min=0.25
 | 
						|
                )
 | 
						|
                assert len(coil_amplitudes["times"]) == 1
 | 
						|
                coil_locs = compute_chpi_locs(self.info, coil_amplitudes)
 | 
						|
                with info._unlock():
 | 
						|
                    info["hpi_results"] = None
 | 
						|
                hpi_g = coil_locs["gofs"][0]
 | 
						|
                hpi_dev = coil_locs["rrs"][0]
 | 
						|
 | 
						|
                # only use HPI coils with localizaton goodness_of_fit > 0.98
 | 
						|
                bad_idx = []
 | 
						|
                for i, g in enumerate(hpi_g):
 | 
						|
                    msg = f"HPI coil {i + 1} - location goodness of fit ({g:0.3f})"
 | 
						|
                    if g < 0.98:
 | 
						|
                        bad_idx.append(i)
 | 
						|
                        msg += " *Removed from coregistration*"
 | 
						|
                    logger.info(msg)
 | 
						|
                hpi_dev = np.delete(hpi_dev, bad_idx, axis=0)
 | 
						|
                hpi_g = np.delete(hpi_g, bad_idx, axis=0)
 | 
						|
 | 
						|
                if pos_fname is not None:
 | 
						|
                    # Digitized HPI points are needed.
 | 
						|
                    hpi_head = np.array(
 | 
						|
                        [
 | 
						|
                            d["r"]
 | 
						|
                            for d in self.info.get("dig", [])
 | 
						|
                            if d["kind"] == FIFF.FIFFV_POINT_HPI
 | 
						|
                        ]
 | 
						|
                    )
 | 
						|
 | 
						|
                    if len(hpi_head) != len(hpi_dev):
 | 
						|
                        raise RuntimeError(
 | 
						|
                            f"number of digitized ({len(hpi_head)}) and active "
 | 
						|
                            f"({len(hpi_dev)}) HPI coils are not the same."
 | 
						|
                        )
 | 
						|
 | 
						|
                    # compute initial head to dev transform and hpi ordering
 | 
						|
                    head_to_dev_t, order, trans_g = _fit_coil_order_dev_head_trans(
 | 
						|
                        hpi_dev, hpi_head
 | 
						|
                    )
 | 
						|
 | 
						|
                    # set the device to head transform
 | 
						|
                    self.info["dev_head_t"] = Transform(
 | 
						|
                        FIFF.FIFFV_COORD_DEVICE, FIFF.FIFFV_COORD_HEAD, head_to_dev_t
 | 
						|
                    )
 | 
						|
 | 
						|
                    # add hpi_meg_dev to dig...
 | 
						|
                    for idx, point in enumerate(hpi_dev):
 | 
						|
                        d = {
 | 
						|
                            "r": point,
 | 
						|
                            "ident": idx + 1,
 | 
						|
                            "kind": FIFF.FIFFV_POINT_HPI,
 | 
						|
                            "coord_frame": FIFF.FIFFV_COORD_DEVICE,
 | 
						|
                        }
 | 
						|
                        self.info["dig"].append(DigPoint(d))
 | 
						|
 | 
						|
                    dig_dists = cdist(hpi_head[order], hpi_head[order])
 | 
						|
                    dev_dists = cdist(hpi_dev, hpi_dev)
 | 
						|
                    tmp_dists = np.abs(dig_dists - dev_dists)
 | 
						|
                    dist_limit = tmp_dists.max() * 1.1
 | 
						|
 | 
						|
                    logger.info(
 | 
						|
                        "HPI-Dig corrregsitration\n"
 | 
						|
                        f"\tGOF : {trans_g:0.3f}\n"
 | 
						|
                        f"\tMax Coil Error : {100 * tmp_dists.max():0.3f} cm\n"
 | 
						|
                    )
 | 
						|
 | 
						|
                else:
 | 
						|
                    logger.info("Assuming Cardinal HPIs")
 | 
						|
                    nas = hpi_dev[0]
 | 
						|
                    lpa = hpi_dev[2]
 | 
						|
                    rpa = hpi_dev[1]
 | 
						|
                    t = get_ras_to_neuromag_trans(nas, lpa, rpa)
 | 
						|
                    with self.info._unlock():
 | 
						|
                        self.info["dev_head_t"] = Transform(
 | 
						|
                            FIFF.FIFFV_COORD_DEVICE, FIFF.FIFFV_COORD_HEAD, t
 | 
						|
                        )
 | 
						|
 | 
						|
                    # transform fiducial points
 | 
						|
                    nas = apply_trans(t, nas)
 | 
						|
                    lpa = apply_trans(t, lpa)
 | 
						|
                    rpa = apply_trans(t, rpa)
 | 
						|
 | 
						|
                    hpi = apply_trans(self.info["dev_head_t"], hpi_dev)
 | 
						|
                    with self.info._unlock():
 | 
						|
                        self.info["dig"] = _make_dig_points(
 | 
						|
                            nasion=nas, lpa=lpa, rpa=rpa, hpi=hpi
 | 
						|
                        )
 | 
						|
                    order = np.array([0, 1, 2])
 | 
						|
                    dist_limit = 0.005
 | 
						|
 | 
						|
                # fill in hpi_results
 | 
						|
                hpi_result = dict()
 | 
						|
 | 
						|
                # add HPI points in device coords...
 | 
						|
                dig = []
 | 
						|
                for idx, point in enumerate(hpi_dev):
 | 
						|
                    dig.append(
 | 
						|
                        {
 | 
						|
                            "r": point,
 | 
						|
                            "ident": idx + 1,
 | 
						|
                            "kind": FIFF.FIFFV_POINT_HPI,
 | 
						|
                            "coord_frame": FIFF.FIFFV_COORD_DEVICE,
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                hpi_result["dig_points"] = dig
 | 
						|
 | 
						|
                # attach Transform
 | 
						|
                hpi_result["coord_trans"] = self.info["dev_head_t"]
 | 
						|
 | 
						|
                # 1 based indexing
 | 
						|
                hpi_result["order"] = order + 1
 | 
						|
                hpi_result["used"] = np.arange(3) + 1
 | 
						|
                hpi_result["dist_limit"] = dist_limit
 | 
						|
                hpi_result["good_limit"] = 0.98
 | 
						|
 | 
						|
                # Warn for large discrepancies between digitized and fit
 | 
						|
                # cHPI locations
 | 
						|
                if hpi_result["dist_limit"] > 0.005:
 | 
						|
                    warn(
 | 
						|
                        "Large difference between digitized geometry"
 | 
						|
                        " and HPI geometry. Max coil to coil difference"
 | 
						|
                        f" is {100.0 * tmp_dists.max():0.2f} cm\n"
 | 
						|
                        "beware of *POOR* head localization"
 | 
						|
                    )
 | 
						|
 | 
						|
                # store it
 | 
						|
                with self.info._unlock():
 | 
						|
                    self.info["hpi_results"] = [hpi_result]
 | 
						|
 | 
						|
    def _read_segment_file(self, data, idx, fi, start, stop, cals, mult):
 | 
						|
        """Read a chunk of raw data."""
 | 
						|
        _read_segments_file(self, data, idx, fi, start, stop, cals, mult, dtype=">f4")
 |