def open_file(self, bin_file):
     """Open the binary file and read until it gets device info packet
     Args:
         bin_file (str): Path to binary file
         """
     self.parser = Parser(callback=self.process, mode='file')
     self.is_connected = True
     self.parser.start_reading(filename=bin_file)
Exemple #2
0
 def start(self, device_name=None, mac_address=None):
     """Start streaming from Explore device
     Args:
         device_name (str): Explore device name in form of <Explore_####>
         mac_address (str): MAC address of Explore device
     """
     self.parser = Parser(callback=self.process, mode='device')
     self.parser.start_streaming(device_name, mac_address)
     self.is_connected = True
     self._device_configurator = DeviceConfiguration(bt_interface=self.parser.stream_interface)
     self.subscribe(callback=self._device_configurator.update_ack, topic=TOPICS.cmd_ack)
     self.subscribe(callback=self._device_configurator.update_cmd_status, topic=TOPICS.cmd_status)
Exemple #3
0
def bin2csv(bin_file, do_overwrite=False, out_dir=None):

    """Binary to CSV file converter.
    This function converts the given binary file to ExG and ORN csv files.

    Args:
        bin_file (str): Binary file full address
        out_dir (str): Output directory (if None, uses the same directory as binary file)
        do_overwrite (bool): Overwrite if files exist already

    Returns:

    """
    head_path, full_filename = os.path.split(bin_file)
    filename, extension = os.path.splitext(full_filename)
    assert os.path.isfile(bin_file), "Error: File does not exist!"
    assert extension == '.BIN', "File type error! File extension must be BIN."
    if out_dir is None:
        out_dir = head_path + '/'

    eeg_out_file = out_dir + filename + '_eeg.csv'
    orn_out_file = out_dir + filename + '_orn.csv'
    marker_out_file = out_dir + filename + '_marker.csv'

    if not do_overwrite:
        assert not os.path.isfile(eeg_out_file), eeg_out_file + " already exists!"
        assert not os.path.isfile(orn_out_file), orn_out_file + " already exists!"
        assert not os.path.isfile(marker_out_file), marker_out_file + " already exists!"

    with open(bin_file, "rb") as f_bin, open(eeg_out_file, "w") as f_eeg, open(orn_out_file, "w") as f_orn, \
        open(marker_out_file, "w") as f_marker:
        parser = Parser(fid=f_bin)
        f_orn.write('TimeStamp,ax,ay,az,gx,gy,gz,mx,my,mz\n')
        # f_orn.write('hh:mm:ss, mg/LSB, mg/LSB, mg/LSB, mdps/LSB, mdps/LSB, mdps/LSB,'
        #             ' mgauss/LSB, mgauss/LSB, mgauss/LSB\n')
        f_eeg.write('TimeStamp,ch1,ch2,ch3,ch4,ch5,ch6,ch7,ch8\n')

        csv_eeg = csv.writer(f_eeg, delimiter=',')
        csv_orn = csv.writer(f_orn, delimiter=',')
        csv_marker = csv.writer(f_marker, delimiter=',')

        print("Converting...")
        while True:
            try:
                parser.parse_packet(mode='record', csv_files=(csv_eeg, csv_orn, csv_marker))
            except ValueError:
                print("Binary file ended suddenly! Conversion finished!")
                break
 def read_device_info(self, bin_file):
     self.parser = Parser(callback=self.process, mode='file')
     self.parser.read_device_info(bin_file)
class StreamProcessor:
    """Stream processor class"""

    def __init__(self):
        self.parser = None
        self.filters = []
        self.orn_calibrator = None
        self.device_info = {}
        self.old_device_info = {}
        self.imp_calib_info = {}
        self.subscribers = {key: set() for key in TOPICS}  # keys are topics and values are sets of callbacks
        self._device_configurator = None
        self.imp_calculator = None
        self.is_connected = False
        self._is_imp_mode = False
        self.physical_orn = PhysicalOrientation()

    def subscribe(self, callback, topic):
        """Subscribe a function to a topic

        Args:
            callback (function): Callback function to be called when there is a new packet in the topic
            topic (enum 'Topics'): Topic type
        """
        self.subscribers.setdefault(topic, set()).add(callback)

    def unsubscribe(self, callback, topic):
        """Unsubscribe a function from a topic

        Args:
            callback (function): Callback function to be called when there is a new packet in the topic
            topic (enum 'Topics'): Topic type
        """
        self.subscribers.setdefault(topic, set()).discard(callback)

    def start(self, device_name=None, mac_address=None):
        """Start streaming from Explore device

        Args:
            device_name (str): Explore device name in form of <Explore_####>
            mac_address (str): MAC address of Explore device
        """
        if device_name is None:
            device_name = "Explore_" + str(mac_address[-5:-3]) + str(mac_address[-2:])
        self.device_info["device_name"] = device_name
        self.parser = Parser(callback=self.process, mode='device')
        self.parser.start_streaming(device_name, mac_address)
        self.is_connected = True
        self._device_configurator = DeviceConfiguration(bt_interface=self.parser.stream_interface)
        self.subscribe(callback=self._device_configurator.update_ack, topic=TOPICS.cmd_ack)
        self.subscribe(callback=self._device_configurator.update_cmd_status, topic=TOPICS.cmd_status)
        self.orn_initialize(device_name)


    def open_file(self, bin_file):
        """Open the binary file and read until it gets device info packet
        Args:
            bin_file (str): Path to binary file
            """
        self.parser = Parser(callback=self.process, mode='file')
        self.is_connected = True
        self.parser.start_reading(filename=bin_file)

    def read_device_info(self, bin_file):
        self.parser = Parser(callback=self.process, mode='file')
        self.parser.read_device_info(bin_file)

    def stop(self):
        """Stop streaming"""
        self.parser.stop_streaming()
        self.is_connected = False

    def process(self, packet):
        """Process incoming packet

        Args:
            packet (explorepy.packet.Packet): Data packet
        """
        if isinstance(packet, Orientation):
            self.dispatch(topic=TOPICS.raw_orn, packet=packet)
            if self.physical_orn.status == "READY":
                packet = self.physical_orn.calculate(packet=packet)
                self.dispatch(topic=TOPICS.mapped_orn, packet=packet)
        elif isinstance(packet, EEG):
            self.dispatch(topic=TOPICS.raw_ExG, packet=packet)
            if self._is_imp_mode:
                packet_imp = self.imp_calculator.measure_imp(packet=packet)
                self.dispatch(topic=TOPICS.imp, packet=packet_imp)
            self.apply_filters(packet=packet)
            self.dispatch(topic=TOPICS.filtered_ExG, packet=packet)
        elif isinstance(packet, DeviceInfo):
            self.old_device_info = self.device_info.copy()
            self.device_info.update(packet.get_info())
            self.dispatch(topic=TOPICS.device_info, packet=packet)
        elif isinstance(packet, CommandRCV):
            self.dispatch(topic=TOPICS.cmd_ack, packet=packet)
        elif isinstance(packet, CommandStatus):
            self.dispatch(topic=TOPICS.cmd_status, packet=packet)
        elif isinstance(packet, Environment):
            self.dispatch(topic=TOPICS.env, packet=packet)
        elif isinstance(packet, EventMarker):
            self.dispatch(topic=TOPICS.marker, packet=packet)
        elif isinstance(packet, CalibrationInfo):
            self.imp_calib_info = packet.get_info()
        elif not packet:
            self.is_connected = False

    def dispatch(self, topic, packet):
        """Dispatch a packet to subscribers

        Args:
            topic (Enum 'Topics'): Topic enum which packet should be sent to
            packet (explorepy.packet.Packet): Data packet
        """
        if self.subscribers:
            for callback in self.subscribers[topic]:
                callback(packet)

    def add_filter(self, cutoff_freq, filter_type):
        """Add filter to the stream
        Args:
            cutoff_freq (Union[float, tuple]): Cut-off frequency (frequencies) for the filter
            filter_type (str): Filter type ['bandpass', 'lowpass', 'highpass', 'notch']
        """
        while not self.device_info:
            print('Waiting for device info packet...')
            time.sleep(.2)
        self.filters.append(ExGFilter(cutoff_freq=cutoff_freq,
                                      filter_type=filter_type,
                                      s_rate=self.device_info['sampling_rate'],
                                      n_chan=self.device_info['adc_mask'].count(1)))

    def apply_filters(self, packet):
        """Apply temporal filters to a packet"""
        for filt in self.filters:
            packet = filt.apply(packet)

    def configure_device(self, cmd):
        """Change device configuration

        Args:
            cmd (explorepy.command.Command): Command to be sent
        """
        if not self.is_connected:
            raise ConnectionError("No Explore device is connected!")
        return self._device_configurator.change_setting(cmd)

    def imp_initialize(self, notch_freq):
        """Activate impedance mode in the device"""
        cmd = ZMeasurementEnable()
        if self.configure_device(cmd):
            self._is_imp_mode = True
            self.imp_calculator = ImpedanceMeasurement(device_info=self.device_info,
                                                       calib_param=self.imp_calib_info,
                                                       notch_freq=notch_freq)
        else:
            raise ConnectionError('Device configuration process failed!')

    def disable_imp(self):
        """Disable impedance mode in the device"""
        cmd = ZMeasurementDisable()
        if self.configure_device(cmd):
            self._is_imp_mode = False
            print("Impedance measurement mode has been disabled.")
            return True
        print("WARNING: Couldn't disable impedance measurement mode. "
              "Please restart your device manually.")
        return False

    def orn_initialize(self, device_name):
        res = self.physical_orn.read_calibre_data(device_name)
        if res:
            self.physical_orn.status = "READY"
        else:
            self.physical_orn.status = "NOT READY"
            print('Calibration data does not exist. If you need physical orientation, calibrate your device first.')

    def set_marker(self, code):
        """Set a marker in the stream"""
        if not isinstance(code, int):
            raise TypeError('Marker code must be an integer!')
        if 0 <= code <= 7:
            raise ValueError('Marker code value is not valid')

        self.process(EventMarker(timestamp=time.time() - self.parser.start_time,
                                 payload=bytearray(struct.pack('<H', code) + b'\xaf\xbe\xad\xde')))

    def compare_device_info(self, new_device_info):
        """Compare a device info dict with the current version

        Args:
            new_device_info (dict): Device info dictionary to be compared with the internal one

        Returns:
            bool: whether they are equal
        """
        assert self.device_info, "The internal device info has not been set yet!"
        if new_device_info['sampling_rate'] != self.old_device_info['sampling_rate']:
            print("Sampling rate has been changed in the file.")
            return False
        if new_device_info['adc_mask'] != self.old_device_info['adc_mask']:
            print("ADC mask has been changed in the file.")
            return False
        return True

    def send_timestamp(self):
        """Send host timestamp to the device"""
        self._device_configurator.send_timestamp()