You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					140 lines
				
				4.4 KiB
			
		
		
			
		
	
	
					140 lines
				
				4.4 KiB
			| 
								 
											5 months ago
										 
									 | 
							
								#!/usr/bin/env python3
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import time
							 | 
						||
| 
								 | 
							
								import ctypes
							 | 
						||
| 
								 | 
							
								import select
							 | 
						||
| 
								 | 
							
								import threading
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import cereal.messaging as messaging
							 | 
						||
| 
								 | 
							
								from cereal.services import SERVICE_LIST
							 | 
						||
| 
								 | 
							
								from openpilot.common.util import sudo_write
							 | 
						||
| 
								 | 
							
								from openpilot.common.realtime import config_realtime_process, Ratekeeper
							 | 
						||
| 
								 | 
							
								from openpilot.common.swaglog import cloudlog
							 | 
						||
| 
								 | 
							
								from openpilot.common.gpio import gpiochip_get_ro_value_fd, gpioevent_data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from openpilot.system.sensord.sensors.i2c_sensor import Sensor
							 | 
						||
| 
								 | 
							
								from openpilot.system.sensord.sensors.lsm6ds3_accel import LSM6DS3_Accel
							 | 
						||
| 
								 | 
							
								from openpilot.system.sensord.sensors.lsm6ds3_gyro import LSM6DS3_Gyro
							 | 
						||
| 
								 | 
							
								from openpilot.system.sensord.sensors.lsm6ds3_temp import LSM6DS3_Temp
							 | 
						||
| 
								 | 
							
								from openpilot.system.sensord.sensors.mmc5603nj_magn import MMC5603NJ_Magn
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								I2C_BUS_IMU = 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def interrupt_loop(sensors: list[tuple[Sensor, str, bool]], event) -> None:
							 | 
						||
| 
								 | 
							
								  pm = messaging.PubMaster([service for sensor, service, interrupt in sensors if interrupt])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Requesting both edges as the data ready pulse from the lsm6ds sensor is
							 | 
						||
| 
								 | 
							
								  # very short (75us) and is mostly detected as falling edge instead of rising.
							 | 
						||
| 
								 | 
							
								  # So if it is detected as rising the following falling edge is skipped.
							 | 
						||
| 
								 | 
							
								  fd = gpiochip_get_ro_value_fd("sensord", 0, 84)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Configure IRQ affinity
							 | 
						||
| 
								 | 
							
								  irq_path = "/proc/irq/336/smp_affinity_list"
							 | 
						||
| 
								 | 
							
								  if not os.path.exists(irq_path):
							 | 
						||
| 
								 | 
							
								    irq_path = "/proc/irq/335/smp_affinity_list"
							 | 
						||
| 
								 | 
							
								  if os.path.exists(irq_path):
							 | 
						||
| 
								 | 
							
								    sudo_write('1\n', irq_path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  offset = time.time_ns() - time.monotonic_ns()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  poller = select.poll()
							 | 
						||
| 
								 | 
							
								  poller.register(fd, select.POLLIN | select.POLLPRI)
							 | 
						||
| 
								 | 
							
								  while not event.is_set():
							 | 
						||
| 
								 | 
							
								    events = poller.poll(100)
							 | 
						||
| 
								 | 
							
								    if not events:
							 | 
						||
| 
								 | 
							
								      cloudlog.error("poll timed out")
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    if not (events[0][1] & (select.POLLIN | select.POLLPRI)):
							 | 
						||
| 
								 | 
							
								      cloudlog.error("no poll events set")
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    dat = os.read(fd, ctypes.sizeof(gpioevent_data)*16)
							 | 
						||
| 
								 | 
							
								    evd = gpioevent_data.from_buffer_copy(dat)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cur_offset = time.time_ns() - time.monotonic_ns()
							 | 
						||
| 
								 | 
							
								    if abs(cur_offset - offset) > 10 * 1e6:  # ms
							 | 
						||
| 
								 | 
							
								      cloudlog.warning(f"time jumped: {cur_offset} {offset}")
							 | 
						||
| 
								 | 
							
								      offset = cur_offset
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ts = evd.timestamp - cur_offset
							 | 
						||
| 
								 | 
							
								    for sensor, service, interrupt in sensors:
							 | 
						||
| 
								 | 
							
								      if interrupt:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								          evt = sensor.get_event(ts)
							 | 
						||
| 
								 | 
							
								          if not sensor.is_data_valid():
							 | 
						||
| 
								 | 
							
								            continue
							 | 
						||
| 
								 | 
							
								          msg = messaging.new_message(service, valid=True)
							 | 
						||
| 
								 | 
							
								          setattr(msg, service, evt)
							 | 
						||
| 
								 | 
							
								          pm.send(service, msg)
							 | 
						||
| 
								 | 
							
								        except Sensor.DataNotReady:
							 | 
						||
| 
								 | 
							
								          pass
							 | 
						||
| 
								 | 
							
								        except Exception:
							 | 
						||
| 
								 | 
							
								          cloudlog.exception(f"Error processing {service}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def polling_loop(sensor: Sensor, service: str, event: threading.Event) -> None:
							 | 
						||
| 
								 | 
							
								  pm = messaging.PubMaster([service])
							 | 
						||
| 
								 | 
							
								  rk = Ratekeeper(SERVICE_LIST[service].frequency, print_delay_threshold=None)
							 | 
						||
| 
								 | 
							
								  while not event.is_set():
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								      evt = sensor.get_event()
							 | 
						||
| 
								 | 
							
								      if not sensor.is_data_valid():
							 | 
						||
| 
								 | 
							
								        continue
							 | 
						||
| 
								 | 
							
								      msg = messaging.new_message(service, valid=True)
							 | 
						||
| 
								 | 
							
								      setattr(msg, service, evt)
							 | 
						||
| 
								 | 
							
								      pm.send(service, msg)
							 | 
						||
| 
								 | 
							
								    except Exception:
							 | 
						||
| 
								 | 
							
								      cloudlog.exception(f"Error in {service} polling loop")
							 | 
						||
| 
								 | 
							
								    rk.keep_time()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def main() -> None:
							 | 
						||
| 
								 | 
							
								  config_realtime_process([1, ], 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  sensors_cfg = [
							 | 
						||
| 
								 | 
							
								    (LSM6DS3_Accel(I2C_BUS_IMU), "accelerometer", True),
							 | 
						||
| 
								 | 
							
								    (LSM6DS3_Gyro(I2C_BUS_IMU), "gyroscope", True),
							 | 
						||
| 
								 | 
							
								    (LSM6DS3_Temp(I2C_BUS_IMU), "temperatureSensor", False),
							 | 
						||
| 
								 | 
							
								    (MMC5603NJ_Magn(I2C_BUS_IMU), "magnetometer", False),
							 | 
						||
| 
								 | 
							
								  ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Initialize sensors
							 | 
						||
| 
								 | 
							
								  exit_event = threading.Event()
							 | 
						||
| 
								 | 
							
								  threads = [
							 | 
						||
| 
								 | 
							
								    threading.Thread(target=interrupt_loop, args=(sensors_cfg, exit_event), daemon=True)
							 | 
						||
| 
								 | 
							
								  ]
							 | 
						||
| 
								 | 
							
								  for sensor, service, interrupt in sensors_cfg:
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								      sensor.init()
							 | 
						||
| 
								 | 
							
								      if not interrupt:
							 | 
						||
| 
								 | 
							
								        # Start polling thread for sensors without interrupts
							 | 
						||
| 
								 | 
							
								        threads.append(threading.Thread(
							 | 
						||
| 
								 | 
							
								          target=polling_loop,
							 | 
						||
| 
								 | 
							
								          args=(sensor, service, exit_event),
							 | 
						||
| 
								 | 
							
								          daemon=True
							 | 
						||
| 
								 | 
							
								        ))
							 | 
						||
| 
								 | 
							
								    except Exception:
							 | 
						||
| 
								 | 
							
								      cloudlog.exception(f"Error initializing {service} sensor")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  try:
							 | 
						||
| 
								 | 
							
								    for t in threads:
							 | 
						||
| 
								 | 
							
								      t.start()
							 | 
						||
| 
								 | 
							
								    while any(t.is_alive() for t in threads):
							 | 
						||
| 
								 | 
							
								      time.sleep(1)
							 | 
						||
| 
								 | 
							
								  except KeyboardInterrupt:
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								  finally:
							 | 
						||
| 
								 | 
							
								    exit_event.set()
							 | 
						||
| 
								 | 
							
								    for t in threads:
							 | 
						||
| 
								 | 
							
								      if t.is_alive():
							 | 
						||
| 
								 | 
							
								        t.join()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for sensor, _, _ in sensors_cfg:
							 | 
						||
| 
								 | 
							
								      try:
							 | 
						||
| 
								 | 
							
								        sensor.shutdown()
							 | 
						||
| 
								 | 
							
								      except Exception:
							 | 
						||
| 
								 | 
							
								        cloudlog.exception("Error shutting down sensor")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if __name__ == "__main__":
							 | 
						||
| 
								 | 
							
								  main()
							 |