def get_height_filter(self): """ Gets the height filter applied to the readings. The units of this filter are those set by :meth:`set_units` :return: The height filter .. code-block:: python {"minimum": minimum, "maximum": maximum} :rtype: dict """ try: self._send(pack("<BB", 0x12, 0x00)) res = unpack("<BBhh", self._read()) if res[0] == 0x12 and res[1] == 0x01: minimum = units.convert_distance_from_si( self.distance_units, res[2] / 1000) maximum = units.convert_distance_from_si( self.distance_units, res[3] / 1000) return {"minimum": minimum, "maximum": maximum} else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get height filter")
def set_angle_filter(self, minimum, maximum): """ Sets the angle filter to apply to the readings (in degrees). 0 degrees is center, negative angles are to the left of the sensor, positive angles are to the right. :param minimum: The minimum angle (-60 to +60) :type minimum: int :param maximum: The maximum angle (-60 to +60) :type maximum: int """ if not (isinstance(minimum, int) and -55 <= minimum <= 55): raise ValueError( "Angle filter minimum must be an integer between -55 and +55") if not (isinstance(maximum, int) and -55 <= maximum <= 55): raise ValueError( "Angle filter maximum must be an integer between -55 and +55") if maximum < minimum: raise ValueError( "Angle filter maximum must be greater than the minimum") try: self._send(pack("<BBbb", 0x07, 0x02, minimum, maximum)) res = unpack("<BBbb", self._read()) if res[0] == 0x07 and res[1] == 0x01: return True else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get angle filter")
def set_auto_start(self, auto_start): """ Sets the auto start settings. Auto start immediately starts the RadarIQ sensor on power on. :param auto_start: The auto start setting (True or False) :type auto_start: bool """ if not isinstance(auto_start, bool): raise ValueError("Auto start must be a boolean") try: self._send(pack("<BBB", 0x17, 0x02, int(auto_start))) res = unpack("<BBB", self._read()) if res[0] == 0x17 and res[1] == 0x01: if res[2] == int(auto_start): return True else: raise Exception("Auto start did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set auto start")
def set_frame_rate(self, frame_rate): """ Sets the frequency with which to capture frames of data. :param frame_rate: The frame rate frames/second) :type frame_rate: int """ if not isinstance(frame_rate, int): raise ValueError("Frame rate must be an integer") if not 0 <= frame_rate <= 20: raise ValueError("Frame rate must be between 0 and 20 fps") try: self._send(pack("<BBB", 0x04, 0x02, frame_rate)) res = unpack("<BBB", self._read()) if res[0] == 0x04 and res[1] == 0x01: if res[2] == frame_rate: return True else: raise Exception("Frame rate did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set frame rate")
def start(self, samples=0, clear_buffer=True): """ Start to capture data into the queue. To fetch data use get_data() and to stop capture call stop_capture(). When capturing data in a non-continuous mode. The data capture will automatically stop once the number of samples has been received. :param samples: The number of samples to capture (0 = continuous) :type samples: int :param clear_buffer: When set any data currently on the buffer will be cleared before beginning capture :type clear_buffer: bool """ try: if clear_buffer is True: self.connection.emtpy_queue() self.data_queue.empty() self._send(pack("<BBB", 0x64, 0x00, samples)) self.is_capturing = True t = threading.Thread(target=self._get_data_thread) t.start() self.capture_max = samples self.capture_count = 0 except Exception: raise Exception("Failed to start data capture")
def get_radar_application_versions(self): """ Gets the version of the radar applications. :return: The radar application versions :rtype: dict """ try: data = { 'controller': ['', 0, 0, 0], 'application_1': ['', 0, 0, 0], 'application_2': ['', 0, 0, 0], 'application_3': ['', 0, 0, 0], } self._send(pack("<BBB", 0x14, 0x00, 0x01)) res = unpack("<BBB20sBBH", self._read()) # Slot 1 if res[0] == 0x14 and res[1] == 0x01 and res[2] == 1: data['controller'] = [ res[3].decode().rstrip('\x00'), res[4], res[5], res[6] ] self._send(pack("<BBB", 0x14, 0x00, 0x02)) res = unpack("<BBB20sBBH", self._read()) # Slot 2 if res[0] == 0x14 and res[1] == 0x01 and res[2] == 2: data['application_1'] = [ res[3].decode().rstrip('\x00'), res[4], res[5], res[6] ] self._send(pack("<BBB", 0x14, 0x00, 0x03)) res = unpack("<BBB20sBBH", self._read()) # Slot 3 if res[0] == 0x14 and res[1] == 0x01 and res[2] == 3: data['application_2'] = [ res[3].decode().rstrip('\x00'), res[4], res[5], res[6] ] self._send(pack("<BBB", 0x14, 0x00, 0x04)) res = unpack("<BBB20sBBH", self._read()) # Slot 4 if res[0] == 0x14 and res[1] == 0x01 and res[2] == 4: data['application_3'] = [ res[3].decode().rstrip('\x00'), res[4], res[5], res[6] ] return data except Exception as err: raise Exception("Failed to get radar versions")
def stop(self): """ Stops capturing of data. """ try: self._send(pack("<BB", 0x65, 0x00)) except Exception: raise Exception("Failed to stop data capture") self.is_capturing = False
def save(self): """ Saves the settings to the sensor. """ try: self._send(pack("<BB", 0x09, 0x02)) res = unpack("<BB", self._read()) if res[0] == 0x09 and res[1] == 0x01: return True else: raise Exception("Invalid response") except Exception: raise Exception("Failed to save settings")
def get_point_density(self): """ Gets the point density setting. :return: Point density. See `Point Density Options`_ :rtype: int """ try: self._send(pack("<BB", 0x10, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x10 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get point density setting")
def get_moving_filter(self): """ Gets the moving objects filter applied to the readings. :return: moving filter. See `Moving filter`_ :rtype: str """ try: self._send(pack("<BB", 0x08, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x08 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get moving filter")
def get_object_type_mode(self): """ Gets the object type mode from the sensor. :return: The object type mode. See `Object Type Modes`_ :rtype: int """ try: self._send(pack("<BB", 0x16, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x16 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get object type mode")
def scene_calibration(self): """ Calibrate the sensor to remove any near-field objects from the scene. This is useful to: * Remove effects of an enclosure * Hide static objects directly in front of the sensor To use this feature, mount the sensor, ensure that there are no objects within 1m of the sensor then run :meth:`scene_calibration`. Once run the scene calibration will be saved to the sensor """ try: self._send(pack("<BB", 0x15, 0x02)) _ = self._read() except Exception: raise Exception("Failed to perform scene calibration")
def get_auto_start(self): """ Gets the autostart setting. :return: True or False :rtype: bool """ try: self._send(pack("<BB", 0x17, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x17 and res[1] == 0x01: return bool(res[2]) else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get auto start")
def get_mode(self): """ Gets the capture mode from the sensor. :return: The capture mode. See `Capture Modes`_ :rtype: int """ try: self._send(pack("<BB", 0x05, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x05 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get mode")
def get_frame_rate(self): """ Gets the frequency with which to capture frames of data (frames/second) from the sensor. :return: The frame rate as it is set in the sensor :rtype: int """ try: self._send(pack("<BB", 0x04, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x04 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get frame rate")
def get_sensitivity(self): """ Get the point sensitivity setting. :return: Sensitivity setting. See `Sensitivity values`_. :rtype: int """ try: self._send(pack("<BB", 0x11, 0x00)) res = unpack("<BBB", self._read()) if res[0] == 0x11 and res[1] == 0x01: return res[2] else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get sensitivity setting")
def encode(self, src): """ Encodes data from src. Adds header and footer to bytes to start and end of packet respectively. Escapes any header, footer or escape bytes. Does not support messages over 255. :param src Data needing to be encoded :type src: bytes :return: Encoded packet :rtype: bytes """ # Add CRC to the source string (so it can be encoded) crc = self.crc16_ccitt(src) src += pack("<H", crc) src_idx = 0 # Add packet header dest = PACKET_HEAD_BYTES # Loop through data, check for footer bytes in data and escape them while src_idx <= len(src): char = src[src_idx:src_idx + 1] if char == PACKET_HEAD_BYTES: dest += PACKET_ESC_BYTES dest += int_to_bytes(PACKET_HEAD ^ PACKET_XOR) elif char == PACKET_FOOT_BYTES: dest += PACKET_ESC_BYTES dest += int_to_bytes(PACKET_FOOT ^ PACKET_XOR) elif char == PACKET_ESC_BYTES: dest += PACKET_ESC_BYTES dest += int_to_bytes(PACKET_ESC ^ PACKET_XOR) else: dest += char src_idx += 1 # Add the packet footer dest += PACKET_FOOT_BYTES if len(dest) > 255: raise Exception( "Encoded packet is greater than the maximum of 255 bytes") return dest
def get_version(self): """ Gets the version of the hardware and firmware. :return: The sensor version (firmware and hardware) :rtype: dict """ try: self._send(pack("<BB", 0x01, 0x00)) res = unpack("<BBBBHBBH", self._read()) if res[0] == 0x01 and res[1] == 0x01: return {"firmware": list(res[2:5]), "hardware": list(res[5:8])} else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get version")
def get_serial_number(self): """ Gets the serial of the sensor. :return: The sensors serial number :rtype: str """ try: self._send(pack("<BB", 0x02, 0x00)) res = unpack("<BBLL", self._read()) if res[0] == 0x02 and res[1] == 0x01: return "{}-{}".format(res[2], res[3]) else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get serial")
def reset(self, code): """ Resets the sensor. :param code: The reset code. See `Reset codes`_ :type code: int """ if not 0 <= code <= 1: raise ValueError("Invalid reset code") try: self._send(pack("<BBB", 0x03, 0x02, code)) res = unpack("<BB", self._read()) if res[0] == 0x03 and res[1] == 0x01: return True else: raise Exception("Invalid response") except Exception: raise Exception("Failed to reset sensor")
def set_distance_filter(self, minimum, maximum): """ Sets the distance filter applied to the readings. :param minimum: The minimum distance (in units as specified by :meth:`set_units`) :type minimum: number :param maximum: The maximum distance (in units as specified by :meth:`set_units`) :type maximum: number """ minimum = int( units.convert_distance_to_si(self.distance_units, minimum) * 1000) maximum = int( units.convert_distance_to_si(self.distance_units, maximum) * 1000) if not (isinstance(minimum, int) and 0 <= minimum <= 10000): raise ValueError( "Distance filter minimum must be a number between 0 and 10000mm" ) if not (isinstance(maximum, int) and 0 <= maximum <= 10000): raise ValueError( "Distance filter maximum must be a number between 0 and 10000mm" ) if maximum < minimum: raise ValueError( "Distance filter maximum must be greater than the minimum") try: self._send(pack("<BBHH", 0x06, 0x02, minimum, maximum)) res = unpack("<BBHH", self._read()) if res[0] == 0x06 and res[1] == 0x01: if res[2] == minimum and res[3] == maximum: return True else: raise Exception("Distance filter did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set distance filter")
def set_moving_filter(self, moving): """ Sets the moving filter to apply to the readings. :param moving: One of MOVING_BOTH, MOVING_OBJECTS_ONLY :type moving: int """ if not (isinstance(moving, int) and 0 <= moving <= 1): raise ValueError("Moving filter value is invalid") try: self._send(pack("<BBB", 0x08, 0x02, moving)) res = unpack("<BBB", self._read()) if res[0] == 0x08 and res[1] == 0x01: if res[2] == moving: return True else: raise Exception("Moving filter did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set moving filter")
def set_point_density(self, density): """ Sets the point density setting. :param density: The point density to set. See `Point Density Options`_ :type density: int """ if not 0 <= density <= 2: raise ValueError("Invalid point density setting") try: self._send(pack("<BBB", 0x10, 0x02, density)) res = unpack("<BBB", self._read()) if res[0] == 0x10 and res[1] == 0x01: if res[2] == density: return True else: raise Exception("Point density did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set the point density")
def set_mode(self, mode): """ Sets the capture mode for the sensor. :param mode: See `Capture Modes`_ :type mode: int :return: None """ if not 0 <= mode <= 1: raise ValueError("Invalid mode") try: self._send(pack("<BBB", 0x05, 0x02, mode)) res = unpack("<BBB", self._read()) if res[0] == 0x05 and res[1] == 0x01: if res[2] == mode: return True else: raise Exception("Mode did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set mode")
def get_angle_filter(self): """ Gets the angle filter applied to the readings (in degrees). 0 degrees is center, negative angles are to the left of the sensor, positive angles are to the right. :return: The angle filter. .. code-block:: python {"minimum": minimum, "maximum": maximum} :rtype: dict """ try: self._send(pack("<BB", 0x07, 0x00)) res = unpack("<BBbb", self._read()) if res[0] == 0x07 and res[1] == 0x01: return {"minimum": res[2], "maximum": res[3]} else: raise Exception("Invalid response") except Exception: raise Exception("Failed to get angle filter")
def set_sensitivity(self, sensitivity): """ Sets the the sensitivity setting to apply to the readings. :param sensitivity: The sensitivity setting to set. See `Sensitivity values`_. :type sensitivity: int """ if not (isinstance(sensitivity, int) and 0 <= sensitivity <= 9): raise ValueError("Sensitivity must be an integer between 0 and 9") try: self._send(pack("<BBB", 0x11, 0x02, sensitivity)) res = unpack("<BBB", self._read()) if res[0] == 0x11 and res[1] == 0x01: if res[2] == sensitivity: return True else: raise Exception( "Sensitivity setting did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set the sensitivity setting")
def set_object_type_mode(self, mode): """ Sets the object type mode for the sensor. :param mode: See `Object Type Modes`_ :type mode: int :return: None """ if not 0 <= mode <= 1: raise ValueError("Invalid object type mode") try: self._send(pack("<BBB", 0x16, 0x02, mode)) res = unpack("<BBB", self._read()) if res[0] == 0x16 and res[1] == 0x01: if res[2] == mode: return True else: raise Exception( "Object Tracking mode did not set correctly") else: raise Exception("Invalid response") except Exception: raise Exception("Failed to set object tracking mode")