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.
		
		
		
		
		
			
		
			
				
					
					
						
							108 lines
						
					
					
						
							2.6 KiB
						
					
					
				
			
		
		
	
	
							108 lines
						
					
					
						
							2.6 KiB
						
					
					
				"""Utilities for reading real time clocks and keeping soft real time constraints."""
 | 
						|
import os
 | 
						|
import time
 | 
						|
import platform
 | 
						|
import threading
 | 
						|
import subprocess
 | 
						|
import multiprocessing
 | 
						|
 | 
						|
from cffi import FFI
 | 
						|
ffi = FFI()
 | 
						|
ffi.cdef("""
 | 
						|
 | 
						|
typedef int clockid_t;
 | 
						|
struct timespec {
 | 
						|
    long tv_sec;   /* Seconds.  */
 | 
						|
    long tv_nsec;  /* Nanoseconds.  */
 | 
						|
};
 | 
						|
int clock_gettime (clockid_t clk_id, struct timespec *tp);
 | 
						|
 | 
						|
long syscall(long number, ...);
 | 
						|
 | 
						|
"""
 | 
						|
)
 | 
						|
libc = ffi.dlopen(None)
 | 
						|
 | 
						|
 | 
						|
# see <linux/time.h>
 | 
						|
CLOCK_MONOTONIC_RAW = 4
 | 
						|
CLOCK_BOOTTIME = 7
 | 
						|
 | 
						|
if platform.system() != 'Darwin' and hasattr(libc, 'clock_gettime'):
 | 
						|
  c_clock_gettime = libc.clock_gettime
 | 
						|
 | 
						|
  tlocal = threading.local()
 | 
						|
  def clock_gettime(clk_id):
 | 
						|
    if not hasattr(tlocal, 'ts'):
 | 
						|
      tlocal.ts = ffi.new('struct timespec *')
 | 
						|
 | 
						|
    ts = tlocal.ts
 | 
						|
 | 
						|
    r = c_clock_gettime(clk_id, ts)
 | 
						|
    if r != 0:
 | 
						|
      raise OSError("clock_gettime")
 | 
						|
    return ts.tv_sec + ts.tv_nsec * 1e-9
 | 
						|
else:
 | 
						|
  # hack. only for OS X < 10.12
 | 
						|
  def clock_gettime(clk_id):
 | 
						|
    return time.time()
 | 
						|
 | 
						|
def monotonic_time():
 | 
						|
  return clock_gettime(CLOCK_MONOTONIC_RAW)
 | 
						|
 | 
						|
def sec_since_boot():
 | 
						|
  return clock_gettime(CLOCK_BOOTTIME)
 | 
						|
 | 
						|
 | 
						|
def set_realtime_priority(level):
 | 
						|
  if os.getuid() != 0:
 | 
						|
    print("not setting priority, not root")
 | 
						|
    return
 | 
						|
  if platform.machine() == "x86_64":
 | 
						|
    NR_gettid = 186
 | 
						|
  elif platform.machine() == "aarch64":
 | 
						|
    NR_gettid = 178
 | 
						|
  else:
 | 
						|
    raise NotImplementedError
 | 
						|
 | 
						|
  tid = libc.syscall(NR_gettid)
 | 
						|
  return subprocess.call(['chrt', '-f', '-p', str(level), str(tid)])
 | 
						|
 | 
						|
 | 
						|
class Ratekeeper(object):
 | 
						|
  def __init__(self, rate, print_delay_threshold=0.):
 | 
						|
    """Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
 | 
						|
    self._interval = 1. / rate
 | 
						|
    self._next_frame_time = sec_since_boot() + self._interval
 | 
						|
    self._print_delay_threshold = print_delay_threshold
 | 
						|
    self._frame = 0
 | 
						|
    self._remaining = 0
 | 
						|
    self._process_name = multiprocessing.current_process().name
 | 
						|
 | 
						|
  @property
 | 
						|
  def frame(self):
 | 
						|
    return self._frame
 | 
						|
 | 
						|
  @property
 | 
						|
  def remaining(self):
 | 
						|
    return self._remaining
 | 
						|
 | 
						|
  # Maintain loop rate by calling this at the end of each loop
 | 
						|
  def keep_time(self):
 | 
						|
    lagged = self.monitor_time()
 | 
						|
    if self._remaining > 0:
 | 
						|
      time.sleep(self._remaining)
 | 
						|
    return lagged
 | 
						|
 | 
						|
  # this only monitor the cumulative lag, but does not enforce a rate
 | 
						|
  def monitor_time(self):
 | 
						|
    lagged = False
 | 
						|
    remaining = self._next_frame_time - sec_since_boot()
 | 
						|
    self._next_frame_time += self._interval
 | 
						|
    if remaining < -self._print_delay_threshold:
 | 
						|
      print("%s lagging by %.2f ms" % (self._process_name, -remaining * 1000))
 | 
						|
      lagged = True
 | 
						|
    self._frame += 1
 | 
						|
    self._remaining = remaining
 | 
						|
    return lagged
 | 
						|
 | 
						|
 |