def test_sensor_port_in_use(meta: client.SensorInfo) -> None: """Using an unavailable port will not throw.""" sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('localhost', 0)) _, port = sock.getsockname() with closing(sock): with closing(client.Sensor("os.invalid", port, metadata=meta)): pass with closing(client.Sensor("os.invalid", 0, port, metadata=meta)): pass
def test_sensor_port_in_use(default_meta: client.SensorInfo) -> None: """Instantiating clients listening to the same port does not fail.""" with closing(client.Sensor("", 0, 0, metadata=default_meta)) as s1: with closing( client.Sensor("", s1._cli.lidar_port, s1._cli.imu_port, metadata=default_meta)) as s2: assert s2._cli.lidar_port != 0 assert s2._cli.imu_port != 0 assert s2._cli.lidar_port == s1._cli.lidar_port assert s2._cli.imu_port == s1._cli.imu_port
def fetch_metadata(hostname: str) -> None: """Fetch metadata from a sensor and write it to disk. Accurately reconstructing point clouds from a sensor data stream requires access to sensor calibration and per-run configuration like the operating mode and azimuth window. The client API makes it easy to read metadata and write it to disk for use with recorded data streams. Args: hostname: hostname of the sensor """ # [doc-stag-fetch-metadata] with closing(client.Sensor(hostname)) as source: # print some useful info from print("Retrieved metadata:") print(f" serial no: {source.metadata.sn}") print(f" firmware version: {source.metadata.fw_rev}") print(f" product line: {source.metadata.prod_line}") print(f" lidar mode: {source.metadata.mode}") print(f"Writing to: {hostname}.json") # write metadata to disk source.write_metadata(f"{hostname}.json")
def test_scans_closed(meta: client.SensorInfo) -> None: """Check reading from closed scans raises an exception.""" with closing(client.Sensor("os.invalid", 0, 0, metadata=meta)) as source: scans = client.Scans(source) scans.close() with pytest.raises(ValueError): next(iter(scans))
def configure_dual_returns(hostname: str) -> None: """Configure sensor to use dual returns profile given hostname Args: hostname: hostname of the sensor """ config = client.get_config(hostname) if (config.lidar_mode == client.LidarMode.MODE_2048x10) or ( config.lidar_mode == client.LidarMode.MODE_1024x20): print( f"Changing lidar_mode from {str(config.lidar_mode)} to 1024x10 to" " enable to dual returns. Will not persist change.") config.lidar_mode = client.LidarMode.MODE_1024x10 # [doc-stag-config-udp-profile] config.udp_profile_lidar = client.UDPProfileLidar.PROFILE_LIDAR_RNG19_RFL8_SIG16_NIR16_DUAL # [doc-etag-config-udp-profile] try: client.set_config(hostname, config, persist=False, udp_dest_auto=False) except ValueError: print("error: Your sensor does not support dual returns. Please" " check the hardware revision and firmware version vs release" " notes.") return print("Retrieving sensor metadata..") with closing(client.Sensor(hostname)) as source: # print some useful info from print( f"udp profile lidar: {str(source.metadata.format.udp_profile_lidar)}" )
def test_sensor_packet_bad_size(default_meta: client.SensorInfo) -> None: """Check that the client will ignore improperly-sized packets.""" with closing( client.Sensor("", 0, 0, metadata=default_meta, timeout=1.0, _flush_before_read=False)) as source: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(b"hello", ("localhost", source._cli.lidar_port)) with pytest.raises(client.ClientTimeout): next(iter(source))
def test_sensor_packet(default_meta: client.SensorInfo) -> None: """Check that the client will read single properly-sized LEGACY packet.""" with closing( client.Sensor("", 0, 0, metadata=default_meta, timeout=5.0, _flush_before_read=False)) as source: data = np.random.randint(255, size=source._pf.lidar_packet_size, dtype=np.uint8) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(data.tobytes(), ("localhost", source._cli.lidar_port)) packet = next(iter(source)) assert (packet._data == data).all()
def record_pcap(hostname: str, lidar_port: int = 7502, imu_port: int = 7503, n_seconds: int = 10) -> None: """Record data from live sensor to pcap file. Note that pcap files recorded this way only preserve the UDP data stream and not networking information, unlike capturing packets directly from a network interface with tools like tcpdump or wireshark. See the API docs of :py:func:`.pcap.record` for additional options for writing pcap files. Args: hostname: hostname of the sensor lidar_port: UDP port to listen on for lidar data imu_port: UDP port to listen on for imu data n_seconds: max seconds of time to record. (Ctrl-Z correctly closes streams) """ import ouster.pcap as pcap from datetime import datetime # [doc-stag-pcap-record] from more_itertools import time_limited # connect to sensor and record lidar/imu packets with closing(client.Sensor(hostname, lidar_port, imu_port, buf_size=640)) as source: # make a descriptive filename for metadata/pcap files time_part = datetime.now().strftime("%Y%m%d_%H%M%S") meta = source.metadata fname_base = f"{meta.prod_line}_{meta.sn}_{meta.mode}_{time_part}" print(f"Saving sensor metadata to: {fname_base}.json") source.write_metadata(f"{fname_base}.json") print(f"Writing to: {fname_base}.pcap (Ctrl-C to stop early)") source_it = time_limited(n_seconds, source) n_packets = pcap.record(source_it, f"{fname_base}.pcap") print(f"Captured {n_packets} packets")
def plot_imu_z_accel(hostname: str, lidar_port: int = 7502, imu_port: int = 7503, n_seconds: int = 5) -> None: """Plot the z acceleration from the IMU over time Args: hostname: hostname of the sensor imu_port: UDP port to listen on for imu data n_seconds: seconds of time to take a sample over """ import matplotlib.pyplot as plt # type: ignore # [doc-stag-imu-z-accel] from more_itertools import time_limited # connect to sensor and get imu packets within n_seconds source = client.Sensor(hostname, lidar_port, imu_port, buf_size=640) with closing(source): ts, z_accel = zip(*[(p.sys_ts, p.accel[2]) for p in time_limited(n_seconds, source) if isinstance(p, client.ImuPacket)]) # initialize plot fig, ax = plt.subplots(figsize=(12.0, 2)) ax.plot(ts, z_accel) # [doc-etag-imu-z-accel] plt.title("Z Accel from IMU over {} Seconds".format(n_seconds)) ax.set_xticks(np.arange(min(ts), max(ts), step=((max(ts) - min(ts)) / 5))) # add end ticker to x axis ax.set_xticks(list(ax.get_xticks()) + [max(ts)]) ax.set_xlim([min(ts), max(ts)]) ax.set_ylabel("z accel") ax.set_xlabel("timestamp (ns)") ax.ticklabel_format(useOffset=False, style="plain") plt.show()
def test_sensor_timeout(meta: client.SensorInfo) -> None: """Setting a zero timeout reliably raises an exception.""" with closing(client.Sensor("os.invalid", 0, 0, metadata=meta, timeout=0.0)) as source: with pytest.raises(client.ClientTimeout): next(iter(source))
def test_sensor_init(meta: client.SensorInfo) -> None: """Initializing a data stream with metadata makes no network calls.""" with closing(client.Sensor("os.invalid", 0, 0, metadata=meta)): pass
def test_sensor_closed(default_meta: client.SensorInfo) -> None: """Check reading from a closed source raises an exception.""" with closing(client.Sensor("", 0, 0, metadata=default_meta)) as source: source.close() with pytest.raises(ValueError): next(iter(source))
def test_sensor_init(default_meta: client.SensorInfo) -> None: """Initializing a data stream with metadata makes no network calls.""" with closing(client.Sensor("", 0, 0, metadata=default_meta)) as source: assert source._cli.lidar_port != 0 assert source._cli.imu_port != 0