code: def compute_head_pos(
      info,
      chpi_locs,
      dist_limit=0.005,
      gof_limit=0.98,
      adjust_dig=False,
      verbose=None,
  ):
      """Compute time-varying head positions.
  
      Parameters
      ----------
      %(info_not_none)s
      %(chpi_locs)s
          Typically obtained by :func:`~mne.chpi.compute_chpi_locs` or
          :func:`~mne.chpi.extract_chpi_locs_ctf`.
      dist_limit : float
          Minimum distance (m) to accept for coil position fitting.
      gof_limit : float
          Minimum goodness of fit to accept for each coil.
      %(adjust_dig_chpi)s
      %(verbose)s
  
      Returns
      -------
      quats : ndarray, shape (n_pos, 10)
          The ``[t, q1, q2, q3, x, y, z, gof, err, v]`` for each fit.
  
      See Also
      --------
      compute_chpi_locs
      extract_chpi_locs_ctf
      read_head_pos
      write_head_pos
  
      Notes
      -----
      .. versionadded:: 0.20
      """
      _check_chpi_param(chpi_locs, "chpi_locs")
      _validate_type(info, Info, "info")
      hpi_dig_head_rrs = _get_hpi_initial_fit(
          info, adjust=adjust_dig, verbose="error"
      )
      n_coils = len(hpi_dig_head_rrs)
      coil_dev_rrs = apply_trans(
          invert_transform(info["dev_head_t"]), hpi_dig_head_rrs
      )
      dev_head_t = info["dev_head_t"]["trans"]
      pos_0 = dev_head_t[:3, 3]
      last = dict(
          quat_fit_time=-0.1,
          coil_dev_rrs=coil_dev_rrs,
          quat=np.concatenate(
              [rot_to_quat(dev_head_t[:3, :3]), dev_head_t[:3, 3]]
          ),
      )
      del coil_dev_rrs
      quats = []
      for fit_time, this_coil_dev_rrs, g_coils in zip(
          *(chpi_locs[key] for key in ("times", "rrs", "gofs"))
      ):
          use_idx = np.where(g_coils >= gof_limit)[0]
  
          #
          # 1. Check number of good ones
          #
          if len(use_idx) < 3:
              gofs = ", ".join(f"{g:0.2f}" for g in g_coils)
              warn(
                  f"{_time_prefix(fit_time)}{len(use_idx)}/{n_coils} "
                  "good HPI fits, cannot determine the transformation "
                  f"({gofs} GOF)!"
              )
              continue
  
          #
          # 2. Fit the head translation and rotation params (minimize error
          #    between coil positions and the head coil digitization
          #    positions) iteratively using different sets of coils.
          #
          this_quat, g, use_idx = _fit_chpi_quat_subset(
              this_coil_dev_rrs, hpi_dig_head_rrs, use_idx
          )
  
          #
          # 3. Stop if < 3 good
          #
  
          # Convert quaterion to transform
          this_dev_head_t = _quat_to_affine(this_quat)
          est_coil_head_rrs = apply_trans(
              this_dev_head_t, this_coil_dev_rrs
          )
          errs = np.linalg.norm(
              hpi_dig_head_rrs - est_coil_head_rrs, axis=1
          )
          n_good = ((g_coils >= gof_limit) & (errs < dist_limit)).sum()
          if n_good < 3:
              warn(
                  _time_prefix(fit_time)
                  + "{}/{} good HPI fits, cannot "
                  "determine the transformation ({} mm/GOF)!".format(
                      n_good,
                      n_coils,
                      ", ".join(
                          f"{1000 * e:0.1f}::{g:0.2f}"
                          for e, g in zip(errs, g_coils)
                      ),
                  )
              )
              continue
  
          # velocities, in device coords, of HPI coils
          dt = fit_time - last["quat_fit_time"]
          vs = tuple(
              1000.0
              * np.linalg.norm(
                  last["coil_dev_rrs"] - this_coil_dev_rrs, axis=1
              )
              / dt
          )
          logger.info(
              _time_prefix(fit_time)
              + (
                  "%s/%s good HPI fits, movements [mm/s] = "
                  + " / ".join(["% 8.1f"] * n_coils)
              )
              % ((n_good, n_coils) + vs)
          )
  
          # Log results
          # MaxFilter averages over a 200 ms window for display, but we don't
          for ii in range(n_coils):
              if ii in use_idx:
                  start, end = " ", "/"
              else:
                  start, end = "(", ")"
              log_str = (
                  "    "
                  + start
                  + "{0:6.1f} {1:6.1f} {2:6.1f} / "
                  + "{3:6.1f} {4:6.1f} {5:6.1f} / "
                  + "g = {6:0.3f} err = {7:4.1f} "
                  + end
              )
              vals = np.concatenate(
                  (
                      1000 * hpi_dig_head_rrs[ii],
                      1000 * est_coil_head_rrs[ii],
                      [g_coils[ii], 1000 * errs[ii]],
                  )
              )
              if len(use_idx) >= 3:
                  if ii <= 2:
                      log_str += "{8:6.3f} {9:6.3f} {10:6.3f}"
                      vals = np.concatenate(
                          (vals, this_dev_head_t[ii, :3])
                      )
                  elif ii == 3:
                      log_str += "{8:6.1f} {9:6.1f} {10:6.1f}"
                      vals = np.concatenate(
                          (vals, this_dev_head_t[:3, 3] * 1000.0)
                      )
              logger.debug(log_str.format(*vals))
  
          # resulting errors in head coil positions
          d = np.linalg.norm(last["quat"][3:] - this_quat[3:])  # m
          r = _angle_between_quats(last["quat"][:3], this_quat[:3]) / dt
          v = d / dt  # m/s
          d = 100 * np.linalg.norm(
              this_quat[3:] - pos_0
          )  # dis from 1st
          logger.debug(
              f"    #t = {fit_time:0.3f}, #e = {100 * errs.mean():0.2f} cm, #g = {g:0.3f}"
              f", #v = {100 * v:0.2f} cm/s, #r = {r:0.2f} rad/s, #d = {d:0.2f} cm"
          )
          logger.debug(
              "    #t = {:0.3f}, #q = {} ".format(
                  fit_time, " ".join(map("{:8.5f}".format, this_quat))
              )
          )
  
          quats.append(
              np.concatenate(
                  (
                      [fit_time],
                      this_quat,
                      [g],
                      [errs[use_idx].mean()],
                      [v],
                  )
              )
          )
          last["quat_fit_time"] = fit_time
          last["quat"] = this_quat
          last["coil_dev_rrs"] = this_coil_dev_rrs
      quats = np.array(quats, np.float64)
      quats = np.zeros((0, 10)) if quats.size == 0 else quats
      return quats
  
imports: import copy
  import itertools
  from functools import partial
  import numpy
  from scipy.linalg import orth
  from scipy.optimize import fmin_cobyla
  from scipy.spatial.distance import cdist
  from ._fiff.constants import FIFF
  from ._fiff.meas_info import Info, _simplify_info
  from ._fiff.pick import _picks_to_idx, pick_channels, pick_channels_regexp, pick_info, pick_types
  from ._fiff.proj import Projection, setup_proj
  from .channels.channels import _get_meg_system
  from .cov import compute_whitener, make_ad_hoc_cov
  from .dipole import _make_guesses
  from .event import find_events
  from .fixes import jit
  from .forward import _concatenate_coils, _create_meg_coils, _magnetic_dipole_field_vec
  from .io import BaseRaw
  from .io.ctf.trans import _make_ctf_coord_trans_set
  from .io.kit.constants import KIT
  from .io.kit.kit import RawKIT
  from .preprocessing.maxwell import _get_mf_picks_fix_mags, _prep_mf_coils, _regularize_out, _sss_basis
  from .transforms import _angle_between_quats, _fit_matched_points, _quat_to_affine, als_ras_trans, apply_trans, invert_transform, quat_to_rot, rot_to_quat
  from .utils import ProgressBar, _check_fname, _check_option, _on_missing, _pl, _validate_type, _verbose_safe_false, logger, use_log_level, verbose, warn
label_desc: Use image node_id 24 for calling a global function with example usage: compute_head_pos(info, chpi_locs, dist_limit, gof_limit, adjust_dig, verbose) and returns: quats