class_docstr: Raw object from a Persyst file.
  
  Parameters
  ----------
  fname : path-like
      Path to the Persyst header (.lay) file.
  %(preload)s
  %(verbose)s
  
  See Also
  --------
  mne.io.Raw : Documentation of attributes and methods.
class_docstr_tok: ["Raw","object","from","a","Persyst","file",".","Parameters","--","--","--","--","--","fname",":","path-like","Path","to","the","Persyst","header","(",".lay",")","file",".","%","(","preload",")","s","%","(","verbose",")","s","See","Also","--","--","--","--","mne.io.Raw",":","Documentation","of","attributes","and","methods","."]
code: def __init__(self, fname, preload=False, verbose=None):
      fname = str(_check_fname(fname, "read", True, "fname"))
      logger.info("Loading %s" % fname)
  
      # make sure filename is the Lay file
      if not fname.endswith(".lay"):
          fname = fname + ".lay"
      # get the current directory and Lay filename
      curr_path, lay_fname = op.dirname(fname), op.basename(fname)
      if not op.exists(fname):
          raise FileNotFoundError(
              f"The path you specified, "
              f'"{lay_fname}",does not exist.'
          )
  
      # sections and subsections currently unused
      keys, data, sections = _read_lay_contents(fname)
  
      # these are the section headers in the Persyst file layout
      # Note: We do not make use of "SampleTimes" yet
      fileinfo_dict = OrderedDict()
      channelmap_dict = OrderedDict()
      patient_dict = OrderedDict()
      comments_dict = OrderedDict()
  
      # keep track of total number of comments
      num_comments = 0
  
      # loop through each line in the lay file
      for key, val, section in zip(keys, data, sections):
          if key == "":
              continue
  
          # Make sure key are lowercase for everything, but electrodes.
          # We also do not want to lower-case comments because those
          # are free-form text where casing may matter.
          if key is not None and section not in [
              "channelmap",
              "comments",
          ]:
              key = key.lower()
  
          # FileInfo
          if section == "fileinfo":
              # extract the .dat file name
              if key == "file":
                  dat_fname = op.basename(val)
                  dat_fpath = op.join(curr_path, op.basename(dat_fname))
  
                  # determine if .dat file exists where it should
                  error_msg = (
                      f"The data path you specified "
                      f"does not exist for the lay path, "
                      f"{lay_fname}. Make sure the dat file "
                      f"is in the same directory as the lay "
                      f"file, and the specified dat filename "
                      f"matches."
                  )
                  if not op.exists(dat_fpath):
                      raise FileNotFoundError(error_msg)
              fileinfo_dict[key] = val
          # ChannelMap
          elif section == "channelmap":
              # channel map has <channel_name>=<number> for <key>=<val>
              channelmap_dict[key] = val
          # Patient (All optional)
          elif section == "patient":
              patient_dict[key] = val
          # Comments (turned into mne.Annotations)
          elif section == "comments":
              comments_dict[key] = comments_dict.get(key, list()) + [
                  val
              ]
              num_comments += 1
  
      # get numerical metadata
      # datatype is either 7 for 32 bit, or 0 for 16 bit
      datatype = fileinfo_dict.get("datatype")
      cal = float(fileinfo_dict.get("calibration"))
      n_chs = int(fileinfo_dict.get("waveformcount"))
  
      # Store subject information from lay file in mne format
      # Note: Persyst also records "Physician", "Technician",
      #       "Medications", "History", and "Comments1" and "Comments2"
      #       and this information is currently discarded
      subject_info = _get_subjectinfo(patient_dict)
  
      # set measurement date
      testdate = patient_dict.get("testdate")
      if testdate is not None:
          # TODO: Persyst may change its internal date schemas
          #  without notice
          # These are the 3 "so far" possible datatime storage
          # formats in Persyst .lay
          if "/" in testdate:
              testdate = datetime.strptime(testdate, "%m/%d/%Y")
          elif "-" in testdate:
              testdate = datetime.strptime(testdate, "%d-%m-%Y")
          elif "." in testdate:
              testdate = datetime.strptime(testdate, "%Y.%m.%d")
  
          if not isinstance(testdate, datetime):
              warn(
                  "Cannot read in the measurement date due "
                  "to incompatible format. Please set manually "
                  "for %s " % lay_fname
              )
              meas_date = None
          else:
              testtime = datetime.strptime(
                  patient_dict.get("testtime"), "%H:%M:%S"
              )
              meas_date = datetime(
                  year=testdate.year,
                  month=testdate.month,
                  day=testdate.day,
                  hour=testtime.hour,
                  minute=testtime.minute,
                  second=testtime.second,
                  tzinfo=timezone.utc,
              )
  
      # Create mne structure
      ch_names = list(channelmap_dict.keys())
      if n_chs != len(ch_names):
          raise RuntimeError(
              "Channels in lay file do not "
              "match the number of channels "
              "in the .dat file."
          )  # noqa
      # get rid of the "-Ref" in channel names
      ch_names = [ch.upper().split("-REF")[0] for ch in ch_names]
  
      # get the sampling rate and default channel types to EEG
      sfreq = fileinfo_dict.get("samplingrate")
      ch_types = "eeg"
      info = create_info(ch_names, sfreq, ch_types=ch_types)
      info.update(subject_info=subject_info)
      with info._unlock():
          for idx in range(n_chs):
              # calibration brings to uV then 1e-6 brings to V
              info["chs"][idx]["cal"] = cal * 1.0e-6
          info["meas_date"] = meas_date
  
      # determine number of samples in file
      # Note: We do not use the lay file to do this
      # because clips in time may be generated by Persyst that
      # DO NOT modify the "SampleTimes" section
      with open(dat_fpath, "rb") as f:
          # determine the precision
          if int(datatype) == 7:
              # 32 bit
              dtype = np.dtype("i4")
          elif int(datatype) == 0:
              # 16 bit
              dtype = np.dtype("i2")
          else:
              raise RuntimeError(f"Unknown format: {datatype}")
  
          # allow offset to occur
          f.seek(0, os.SEEK_END)
          n_samples = f.tell()
          n_samples = n_samples // (dtype.itemsize * n_chs)
  
          logger.debug(
              f"Loaded {n_samples} samples " f"for {n_chs} channels."
          )
  
      raw_extras = {
          "dtype": dtype,
          "n_chs": n_chs,
          "n_samples": n_samples,
      }
      # create Raw object
      super().__init__(
          info,
          preload,
          filenames=[dat_fpath],
          last_samps=[n_samples - 1],
          raw_extras=[raw_extras],
          verbose=verbose,
      )
  
      # set annotations based on the comments read in
      onset = np.zeros(num_comments, float)
      duration = np.zeros(num_comments, float)
      description = [""] * num_comments
  
      # loop through comments dictionary, which may contain
      # multiple events for the same "text" annotation
      t_idx = 0
      for _description, event_tuples in comments_dict.items():
          for _onset, _duration in event_tuples:
              # extract the onset, duration, description to
              # create an Annotations object
              onset[t_idx] = _onset
              duration[t_idx] = _duration
              description[t_idx] = _description
              t_idx += 1
      annot = Annotations(onset, duration, description)
      self.set_annotations(annot)
  
file_path: mne-python/mne/io/persyst/persyst.py
imports: import os
  import os.path
  from collections import OrderedDict
  from datetime import datetime, timezone
  import numpy
  from ..._fiff.constants import FIFF
  from ..._fiff.meas_info import create_info
  from ..._fiff.utils import _mult_cal_one
  from ...annotations import Annotations
  from ...utils import _check_fname, fill_doc, logger, verbose, warn
  from ..base import BaseRaw
label_desc: Use image node_id 1 to create a new RawPersyst object from inherited base classes: BaseRaw with example: obj = RawPersyst(fname, preload, verbose)