code: def _get_hdr_info(hdr_fname, eog, misc, scale):
      """Extract all the information from the header file.
  
      Parameters
      ----------
      hdr_fname : str
          Raw EEG header to be read.
      eog : list of str
          Names of channels that should be designated EOG channels. Names should
          correspond to the header file.
      misc : list or tuple of str | 'auto'
          Names of channels or list of indices that should be designated
          MISC channels. Values should correspond to the electrodes in the
          header file. If 'auto', units in header file are used for inferring
          misc channels. Default is ``'auto'``.
      scale : float
          The scaling factor for EEG data. Unless specified otherwise by
          header file, units are in microvolts. Default scale factor is 1.
  
      Returns
      -------
      %(info_not_none)s
      data_fname : str
          Path to the binary data file.
      fmt : str
          The format of the binary data file.
      order : str
          Orientation of the binary data.
      n_samples : int
          Number of data points in the binary data file.
      mrk_fname : str
          Path to the marker file.
      montage : DigMontage
          Coordinates of the channels, if present in the header file.
      orig_units : dict
          Dictionary mapping channel names to their units as specified in
          the header file. Example: {'FC1': 'nV'}
      """
      scale = float(scale)
      ext = op.splitext(hdr_fname)[-1]
      ahdr_format = ext == ".ahdr"
      if ext not in (".vhdr", ".ahdr"):
          raise OSError(
              "The header file must be given to read the data, "
              "not a file with extension '%s'." % ext
          )
  
      settings, cfg, cinfostr, info = _aux_hdr_info(hdr_fname)
      info._unlocked = True
  
      order = cfg.get(cinfostr, "DataOrientation")
      if order not in _orientation_dict:
          raise NotImplementedError(
              "Data Orientation %s is not supported" % order
          )
      order = _orientation_dict[order]
  
      data_format = cfg.get(cinfostr, "DataFormat")
      if data_format == "BINARY":
          fmt = cfg.get("Binary Infos", "BinaryFormat")
          if fmt not in _fmt_dict:
              raise NotImplementedError(
                  "Datatype %s is not supported" % fmt
              )
          fmt = _fmt_dict[fmt]
      else:
          if order == "C":  # channels in rows
              raise NotImplementedError(
                  "BrainVision files with ASCII data in "
                  "vectorized order (i.e. channels in rows"
                  ") are not supported yet."
              )
          fmt = {
              key: cfg.get("ASCII Infos", key)
              for key in cfg.options("ASCII Infos")
          }
  
      # locate EEG binary file and marker file for the stim channel
      path = op.dirname(hdr_fname)
      data_fname = op.join(path, cfg.get(cinfostr, "DataFile"))
      mrk_fname = op.join(path, cfg.get(cinfostr, "MarkerFile"))
  
      # Try to get measurement date from marker file
      # Usually saved with a marker "New Segment", see BrainVision documentation
      regexp = r"^Mk\d+=New Segment,.*,\d+,\d+,-?\d+,(\d{20})$"
      with open(mrk_fname) as tmp_mrk_f:
          lines = tmp_mrk_f.readlines()
  
      for line in lines:
          match = re.findall(regexp, line.strip())
          # Always take first measurement date we find
          if match:
              date_str = match[0]
              info["meas_date"] = _str_to_meas_date(date_str)
              break
      else:
          info["meas_date"] = None
  
      # load channel labels
      nchan = cfg.getint(cinfostr, "NumberOfChannels")
      if ahdr_format:
          # add one fake channel for ahdr format
          nchan += 1
      n_samples = None
      if order == "C":
          try:
              n_samples = cfg.getint(cinfostr, "DataPoints")
          except configparser.NoOptionError:
              warn(
                  "No info on DataPoints found. Inferring number of "
                  "samples from the data file size."
              )
              with open(data_fname, "rb") as fid:
                  fid.seek(0, 2)
                  n_bytes = fid.tell()
                  n_samples = n_bytes // _fmt_byte_dict[fmt] // nchan
  
      ch_names = [""] * nchan
      cals = np.empty(nchan)
      ranges = np.empty(nchan)
      cals.fill(np.nan)
      ch_dict = dict()
      misc_chs = dict()
      orig_units = dict()
      for chan, props in cfg.items("Channel Infos"):
          n = int(re.findall(r"ch(\d+)", chan)[0]) - 1
          props = props.split(",")
  
          # default to µV, following the BV specs; the unit is only allowed to be
          # something else if explicitly stated (cf. EEGLAB export below)
          if len(props) < 4:
              # deal with older files, which have no unit property
              props += ("µV",)
          elif props[3] == "":
              # deal with files where the unit property is simply empty, which
              # are created e.g. by PyCorder
              props[3] = "µV"
  
          name, _, resolution, unit = props[:4]
          # in BrainVision, commas in channel names are encoded as "\1"
          name = name.replace(r"\1", ",")
          ch_dict[chan] = name
          ch_names[n] = name
          if resolution == "":
              if not unit:  # For truncated vhdrs (e.g. EEGLAB export)
                  resolution = 0.000001
              else:
                  resolution = (
                      1.0  # for files with units specified, but not res
                  )
          unit = unit.replace(
              "\xc2", ""
          )  # Remove unwanted control characters
          orig_units[
              name
          ] = unit  # Save the original units to expose later
          cals[n] = float(resolution)
          ranges[n] = _unit_dict.get(unit, 1) * scale
          if unit not in ("V", "mV", "µV", "uV", "nV"):
              misc_chs[name] = (
                  FIFF.FIFF_UNIT_CEL
                  if unit == "C"
                  else FIFF.FIFF_UNIT_NONE
              )
      if ahdr_format:
          ch_dict[_AHDR_CHANNEL_NAME] = _AHDR_CHANNEL_NAME
          ch_names[-1] = _AHDR_CHANNEL_NAME
          orig_units[_AHDR_CHANNEL_NAME] = "V"
          cals[-1] = 1.0
          ranges[-1] = 1
  
      misc = list(misc_chs.keys()) if misc == "auto" else misc
  
      # create montage: 'Coordinates' section in VHDR/AHDR file corresponds to
      # "BVEF" BrainVision Electrode File. The data are based on BrainVision
      # Analyzer coordinate system: Defined between standard electrode positions:
      # X-axis from T7 to T8, Y-axis from Oz to Fpz, Z-axis orthogonal from
      # XY-plane through Cz, fit to a sphere if idealized (when radius=1),
      # specified in mm
      montage = None
      if cfg.has_section("Coordinates"):
          montage_pos = list()
          montage_names = list()
          to_misc = list()
          # Go through channels
          for ch in cfg.items("Coordinates"):
              ch_name = ch_dict[ch[0]]
              montage_names.append(ch_name)
              # 1: radius, 2: theta, 3: phi
              rad, theta, phi = (float(c) for c in ch[1].split(","))
              pol = np.deg2rad(theta)
              az = np.deg2rad(phi)
              # Coordinates could be "idealized" (spherical head model)
              if rad == 1:
                  # scale up to realistic head radius: *1000 to convert m to mm
                  rad *= HEAD_SIZE_DEFAULT * 1000
              pos = _sph_to_cart(np.array([[rad, az, pol]]))[0]
              if (pos == 0).all() and ch_name not in list(eog) + misc:
                  to_misc.append(ch_name)
              montage_pos.append(pos)
          # Make a montage, normalizing from BrainVision units "mm" to "m", the
          # unit used for montages in MNE
          montage_pos = np.array(montage_pos) / 1e3
          montage = make_dig_montage(
              ch_pos=dict(zip(montage_names, montage_pos)),
              coord_frame="head",
          )
          if len(to_misc) > 0:
              misc += to_misc
              warn(
                  f"No coordinate information found for channels {to_misc}. Setting "
                  "channel types to misc. To avoid this warning, set channel types "
                  "explicitly."
              )
  
      if np.isnan(cals).any():
          raise RuntimeError("Missing channel units")
  
      # Attempts to extract filtering info from header. If not found, both are
      # set to zero.
      settings = settings.splitlines()
      idx = None
  
      if "Channels" in settings:
          idx = settings.index("Channels")
          settings = settings[idx + 1 :]
          hp_col, lp_col = 4, 5
          for idx, setting in enumerate(settings):
              if re.match(r"#\s+Name", setting):
                  break
              else:
                  idx = None
  
      # If software filters are active, then they override the hardware setup
      # But we still want to be able to double check the channel names
      # for alignment purposes, we keep track of the hardware setting idx
      idx_amp = idx
      filter_list_has_ch_name = True
  
      if "S o f t w a r e  F i l t e r s" in settings:
          idx = settings.index("S o f t w a r e  F i l t e r s")
          for idx, setting in enumerate(settings[idx + 1 :], idx + 1):
              if re.match(r"#\s+Low Cutoff", setting):
                  hp_col, lp_col = 1, 2
                  filter_list_has_ch_name = False
                  warn(
                      "Online software filter detected. Using software "
                      "filter settings and ignoring hardware values"
                  )
                  break
              else:
                  idx = idx_amp
  
      if idx:
          lowpass = []
          highpass = []
  
          # for newer BV files, the unit is specified for every channel
          # separated by a single space, while for older files, the unit is
          # specified in the column headers
          divider = r"\s+"
          if "Resolution / Unit" in settings[idx]:
              shift = 1  # shift for unit
          else:
              shift = 0
  
          # Extract filter units and convert from seconds to Hz if necessary.
          # this cannot be done as post-processing as the inverse t-f
          # relationship means that the min/max comparisons don't make sense
          # unless we know the units.
          #
          # For reasoning about the s to Hz conversion, see this reference:
          # `Ebersole, J. S., & Pedley, T. A. (Eds.). (2003).
          # Current practice of clinical electroencephalography.
          # Lippincott Williams & Wilkins.`, page 40-41
          header = re.split(r"\s\s+", settings[idx])
          hp_s = "[s]" in header[hp_col]
          lp_s = "[s]" in header[lp_col]
  
          for i, ch in enumerate(ch_names, 1):
              if (
                  ahdr_format
                  and i == len(ch_names)
                  and ch == _AHDR_CHANNEL_NAME
              ):
                  break
              # double check alignment with channel by using the hw settings
              if idx == idx_amp:
                  line_amp = settings[idx + i]
              else:
                  line_amp = settings[idx_amp + i]
              assert line_amp.find(ch) > -1
  
              # Correct shift for channel names with spaces
              # Header already gives 1 therefore has to be subtracted
              if filter_list_has_ch_name:
                  ch_name_parts = re.split(divider, ch)
                  real_shift = shift + len(ch_name_parts) - 1
              else:
                  real_shift = shift
  
              line = re.split(divider, settings[idx + i])
              highpass.append(line[hp_col + real_shift])
              lowpass.append(line[lp_col + real_shift])
  
          if len(highpass) == 0:
              pass
          elif len(set(highpass)) == 1:
              if highpass[0] in ("NaN", "Off"):
                  pass  # Placeholder for future use. Highpass set in _empty_info
              elif highpass[0] == "DC":
                  info["highpass"] = 0.0
              else:
                  info["highpass"] = float(highpass[0])
                  if hp_s:
                      # filter time constant t [secs] to Hz conversion: 1/2*pi*t
                      info["highpass"] = 1.0 / (
                          2 * np.pi * info["highpass"]
                      )
  
          else:
              heterogeneous_hp_filter = True
              if hp_s:
                  # We convert channels with disabled filters to having
                  # highpass relaxed / no filters
                  highpass = [
                      float(filt)
                      if filt not in ("NaN", "Off", "DC")
                      else np.inf
                      for filt in highpass
                  ]
                  info["highpass"] = np.max(
                      np.array(highpass, dtype=np.float64)
                  )
                  # Conveniently enough 1 / np.inf = 0.0, so this works for
                  # DC / no highpass filter
                  # filter time constant t [secs] to Hz conversion: 1/2*pi*t
                  info["highpass"] = 1.0 / (
                      2 * np.pi * info["highpass"]
                  )
  
                  # not exactly the cleanest use of FP, but this makes us
                  # more conservative in *not* warning.
                  if (
                      info["highpass"] == 0.0
                      and len(set(highpass)) == 1
                  ):
                      # not actually heterogeneous in effect
                      # ... just heterogeneously disabled
                      heterogeneous_hp_filter = False
              else:
                  highpass = [
                      float(filt)
                      if filt not in ("NaN", "Off", "DC")
                      else 0.0
                      for filt in highpass
                  ]
                  info["highpass"] = np.min(
                      np.array(highpass, dtype=np.float64)
                  )
                  if (
                      info["highpass"] == 0.0
                      and len(set(highpass)) == 1
                  ):
                      # not actually heterogeneous in effect
                      # ... just heterogeneously disabled
                      heterogeneous_hp_filter = False
  
              if heterogeneous_hp_filter:
                  warn(
                      "Channels contain different highpass filters. "
                      "Lowest (weakest) filter setting (%0.2f Hz) "
                      "will be stored." % info["highpass"]
                  )
  
          if len(lowpass) == 0:
              pass
          elif len(set(lowpass)) == 1:
              if lowpass[0] in ("NaN", "Off", "0"):
                  pass  # Placeholder for future use. Lowpass set in _empty_info
              else:
                  info["lowpass"] = float(lowpass[0])
                  if lp_s:
                      # filter time constant t [secs] to Hz conversion: 1/2*pi*t
                      info["lowpass"] = 1.0 / (
                          2 * np.pi * info["lowpass"]
                      )
  
          else:
              heterogeneous_lp_filter = True
              if lp_s:
                  # We convert channels with disabled filters to having
                  # infinitely relaxed / no filters
                  lowpass = [
                      float(filt)
                      if filt not in ("NaN", "Off", "0")
                      else 0.0
                      for filt in lowpass
                  ]
                  info["lowpass"] = np.min(
                      np.array(lowpass, dtype=np.float64)
                  )
                  try:
                      # filter time constant t [secs] to Hz conversion: 1/2*pi*t
                      info["lowpass"] = 1.0 / (
                          2 * np.pi * info["lowpass"]
                      )
  
                  except ZeroDivisionError:
                      if len(set(lowpass)) == 1:
                          # No lowpass actually set for the weakest setting
                          # so we set lowpass to the Nyquist frequency
                          info["lowpass"] = info["sfreq"] / 2.0
                          # not actually heterogeneous in effect
                          # ... just heterogeneously disabled
                          heterogeneous_lp_filter = False
                      else:
                          # no lowpass filter is the weakest filter,
                          # but it wasn't the only filter
                          pass
              else:
                  # We convert channels with disabled filters to having
                  # infinitely relaxed / no filters
                  lowpass = [
                      float(filt)
                      if filt not in ("NaN", "Off", "0")
                      else np.inf
                      for filt in lowpass
                  ]
                  info["lowpass"] = np.max(
                      np.array(lowpass, dtype=np.float64)
                  )
  
                  if np.isinf(info["lowpass"]):
                      # No lowpass actually set for the weakest setting
                      # so we set lowpass to the Nyquist frequency
                      info["lowpass"] = info["sfreq"] / 2.0
                      if len(set(lowpass)) == 1:
                          # not actually heterogeneous in effect
                          # ... just heterogeneously disabled
                          heterogeneous_lp_filter = False
  
              if heterogeneous_lp_filter:
                  # this isn't clean FP, but then again, we only want to provide
                  # the Nyquist hint when the lowpass filter was actually
                  # calculated from dividing the sampling frequency by 2, so the
                  # exact/direct comparison (instead of tolerance) makes sense
                  if info["lowpass"] == info["sfreq"] / 2.0:
                      nyquist = ", Nyquist limit"
                  else:
                      nyquist = ""
                  warn(
                      "Channels contain different lowpass filters. "
                      "Highest (weakest) filter setting (%0.2f Hz%s) "
                      "will be stored." % (info["lowpass"], nyquist)
                  )
  
      # Creates a list of dicts of eeg channels for raw.info
      logger.info("Setting channel info structure...")
      info["chs"] = []
      for idx, ch_name in enumerate(ch_names):
          if ch_name in eog or idx in eog or idx - nchan in eog:
              kind = FIFF.FIFFV_EOG_CH
              coil_type = FIFF.FIFFV_COIL_NONE
              unit = FIFF.FIFF_UNIT_V
          elif ch_name in misc or idx in misc or idx - nchan in misc:
              kind = FIFF.FIFFV_MISC_CH
              coil_type = FIFF.FIFFV_COIL_NONE
              if ch_name in misc_chs:
                  unit = misc_chs[ch_name]
              else:
                  unit = FIFF.FIFF_UNIT_NONE
          elif ch_name == "STI 014":
              kind = FIFF.FIFFV_STIM_CH
              coil_type = FIFF.FIFFV_COIL_NONE
              unit = FIFF.FIFF_UNIT_NONE
          else:
              kind = FIFF.FIFFV_EEG_CH
              coil_type = FIFF.FIFFV_COIL_EEG
              unit = FIFF.FIFF_UNIT_V
          info["chs"].append(
              dict(
                  ch_name=ch_name,
                  coil_type=coil_type,
                  kind=kind,
                  logno=idx + 1,
                  scanno=idx + 1,
                  cal=cals[idx],
                  range=ranges[idx],
                  loc=np.full(12, np.nan),
                  unit=unit,
                  unit_mul=FIFF.FIFF_UNITM_NONE,
                  coord_frame=FIFF.FIFFV_COORD_HEAD,
              )
          )
  
      info._unlocked = False
      info._update_redundant()
      return (
          info,
          data_fname,
          fmt,
          order,
          n_samples,
          mrk_fname,
          montage,
          orig_units,
      )
  
imports: import configparser
  import os
  import os.path
  import re
  from datetime import datetime, timezone
  from io import StringIO
  import numpy
  from ..._fiff.constants import FIFF
  from ..._fiff.meas_info import _empty_info
  from ..._fiff.utils import _mult_cal_one, _read_segments_file
  from ...annotations import Annotations, read_annotations
  from ...channels import make_dig_montage
  from ...defaults import HEAD_SIZE_DEFAULT
  from ...transforms import _sph_to_cart
  from ...utils import _DefaultEventParser, fill_doc, logger, verbose, warn
  from ..base import BaseRaw
label_desc: Use image node_id 7 for calling a global function with example usage: _get_hdr_info(hdr_fname, eog, misc, scale) and returns: info,  data_fname,  fmt,  order,  n_samples,  mrk_fname,  montage,  orig_units