class ProcessingConfiguration(configbase.ProcessingConfig): VERSION = 1 history_length = configbase.IntParameter( label="History length", default_value=100, )
class PowerBinServiceConfig(BaseDenseServiceConfig): _MIN_BIN_SIZE = 0.016 mode = ModeParameter( label="Mode", value=Mode.POWER_BINS, ) bin_count = cb.IntParameter( label="Bin count", default_value=5, limits=(1, None), order=5, help=r""" The number of bins to be used for creating the amplitude over distance histogram. """, ) def check(self): alerts = super().check() if self.range_length < self._MIN_BIN_SIZE: alerts.append(cb.Error("range_interval", "Too short")) elif self.range_length / self.bin_count < self._MIN_BIN_SIZE: alerts.append(cb.Error("bin_count", "Too high for current range")) return alerts
class ProcessingConfig(configbase.ProcessingConfig): VERSION = 1 history_length = configbase.IntParameter( default_value=100, limits=(10, 1000), label="History length", )
class ProcessingConfig(configbase.ProcessingConfig): VERSION = 1 class BackgroundMode(Enum): SUBTRACT = "Subtract" LIMIT = "Limit" show_peak_depths = configbase.BoolParameter( label="Show peak distances", default_value=True, updateable=True, order=-10, ) bg_buffer_length = configbase.IntParameter( default_value=50, limits=(1, 200), label="Background buffer length", order=0, ) bg = configbase.ReferenceDataParameter( label="Background", order=10, ) bg_mode = configbase.EnumParameter( label="Background mode", default_value=BackgroundMode.SUBTRACT, enum=BackgroundMode, updateable=True, order=20, ) history_length = configbase.IntParameter( default_value=100, limits=(10, 1000), label="History length", order=30, )
class ProcessingConfig(configbase.ProcessingConfig): VERSION = 2 history_length = configbase.IntParameter( default_value=100, limits=(10, 1000), label="History length", order=0, ) sf = configbase.FloatParameter( label="Smoothing factor", default_value=None, limits=(0.1, 0.999), decimals=3, optional=True, optional_label="Enable filter", optional_default_set_value=0.9, updateable=True, order=10, )
class PowerBinServiceConfig(BaseDenseServiceConfig): _MIN_BIN_SIZE = 0.016 mode = ModeParameter( label="Mode", value=Mode.POWER_BINS, ) bin_count = cb.IntParameter( label="Bin count", default_value=5, limits=(1, None), order=5, ) def check(self): alerts = super().check() if self.range_length < self._MIN_BIN_SIZE: alerts.append(cb.Error("range_interval", "Too short")) elif self.range_length / self.bin_count < self._MIN_BIN_SIZE: alerts.append(cb.Error("bin_count", "Too high for current range")) return alerts
class SparseServiceConfig(BaseServiceConfig): class SamplingMode(ConfigEnum): A = ("A (less correlation)", 0) B = ("B (more SNR)", 1) mode = ModeParameter( label="Mode", value=Mode.SPARSE, ) sweeps_per_frame = cb.IntParameter( label="Sweeps per frame", default_value=16, limits=(1, 2048), order=50, help=r""" The number of sweeps per frame :math:`N_s`. Must be at least 1, and not greater than 64 when using sampling mode B. """, ) sweep_rate = cb.FloatParameter( label="Sweep rate", unit="Hz", default_value=None, limits=(1, None), decimals=0, optional=True, optional_default_set_value=3000.0, order=40, help=r""" In Sparse, each frame is a collection of several sweeps over the selected distance range (sweeps per frame). The sweep rate :math:`f_s` is the rate at which sweeps are performed, i.e. the rate at which each distance point is scanned. If you set the sweep rate to 4000 Hz and the sweeps per frame to 32, each Sparse data frame will contain 32 sweeps over the selected distance range, where the sweeps are measured at a rate of 4000 Hz. The maximum possible sweep rate... - Is roughly inversely proportional to the number of depth points measured (affected by the **range interval** and **downsampling factor**). - Is roughly inversely proportional to **HW accelerated average samples**. - Depends on the **sampling mode**. Mode A is roughly :math:`4/3 \approx 130\%` slower than mode B with the same configuration. To get the maximum possible rate, leave this value unset and look at the :ref:`sweep rate <sparse-info-sweep-rate>` in the session info (metadata). .. tip:: If you do not need a specific sweep rate, we recommend leaving it unset. """, ) sampling_mode = cb.EnumParameter( label="Sampling mode", enum=SamplingMode, default_value=SamplingMode.B, order=1000, category=cb.Category.ADVANCED, help=r""" The sampling mode changes how the hardware accelerated averaging is done. This may either increase SNR or reduce correlation. *Mode A* is: - optimized for maximal independence of the depth points, giving a higher depth resolution than mode B. - more suitable for applications like gesture recognition, measuring the distance to a movement, and speed measurements. *Mode B* is: - optimized for maximal SNR per unit time spent on measuring. This makes it more energy efficient and suitable for cases where small movements are to be detected over long ranges. - resulting in roughly 3 dB better SNR per unit time than mode A. """, ) def check(self): alerts = super().check() if self.sampling_mode == __class__.SamplingMode.B: if self.sweeps_per_frame > 64: alerts.append( cb.Error("sweeps_per_frame", "Must be < 64 with sampling mode B")) if self.sweep_rate is not None and self.update_rate is not None: max_frame_rate = self.sweep_rate / self.sweeps_per_frame if self.update_rate > max_frame_rate: alerts.append( cb.Error("sweep_rate", "Too low for current update rate")) # Pessimistic buffer size check start_p = int(round(self.range_start / 0.06 - 0.01)) end_p = int(round(self.range_end / 0.06 + 0.01)) sweep_size = (end_p - start_p) // self.downsampling_factor + 1 if sweep_size * self.sweeps_per_frame > 2048: alerts.append(cb.Error("range_interval", "Too long for buffer")) return alerts
class BaseServiceConfig(BaseSessionConfig): class RepetitionMode(ConfigEnum): HOST_DRIVEN = ("Host driven", "on_demand") SENSOR_DRIVEN = ("Sensor driven", "streaming") class Profile(ConfigEnum): PROFILE_1 = ("1 (max resolution)", 1, 0.10) PROFILE_2 = ("2", 2, 0.12) PROFILE_3 = ("3", 3, 0.18) PROFILE_4 = ("4", 4, 0.36) PROFILE_5 = ("5 (max SNR)", 5, 0.60) @property def approx_direct_leakage_length(self): return self.value[2] class PowerSaveMode(ConfigEnum): ACTIVE = ("Active", "active") READY = ("Ready", "ready") SLEEP = ("Sleep", "sleep") OFF = ("Off", "off") range_interval = cb.FloatRangeParameter( label="Range interval", unit="m", default_value=[0.18, 0.78], limits=(-0.7, 7.0), order=10, help=r""" The measured depth range. The start and end values will be rounded to the closest measurement point available. """, ) range_start = cb.get_virtual_parameter_class(cb.FloatParameter)( label="Range start", get_fun=lambda conf: conf.range_interval[0], visible=False, ) range_length = cb.get_virtual_parameter_class(cb.FloatParameter)( label="Range length", get_fun=lambda conf: conf.range_interval[1] - conf.range_interval[0], visible=False, ) range_end = cb.get_virtual_parameter_class(cb.FloatParameter)( label="Range end", get_fun=lambda conf: conf.range_interval[1], visible=False, ) repetition_mode = cb.EnumParameter( label="Repetition mode", enum=RepetitionMode, default_value=RepetitionMode.HOST_DRIVEN, order=1010, category=cb.Category.ADVANCED, help=r""" The RSS supports two different repetition modes. They determine how and when data acquisition occurs. They are: * **On demand / host driven**: The sensor produces data when requested by the application. Hence, the application is responsible for timing the data acquisition. This is the default mode, and may be used with all power save modes. * **Streaming / sensor driven**: The sensor produces data at a fixed rate, given by a configurable accurate hardware timer. This mode is recommended if exact timing between updates is required. The Exploration Tool is capable of setting the update rate also in *on demand (host driven)* mode. Thus, the difference between the modes becomes subtle. This is why *on demand* and *streaming* are called *host driven* and *sensor driven* respectively in Exploration Tool. """, ) update_rate = cb.FloatParameter( label="Update rate", unit="Hz", default_value=None, limits=(0.1, None), decimals=1, optional=True, optional_label="Limit", optional_default_set_value=50.0, order=30, help=r""" The rate :math:`f_f` at which the sensor sends frames to the host MCU. .. attention:: Setting the update rate too high might result in missed data frames. In sparse, the maximum possible update rate depends on the *sweeps per frame* :math:`N_s` and *sweep rate* :math:`f_s`: .. math:: \frac{1}{f_f} > N_s \cdot \frac{1}{f_s} + \text{overhead*} \* *The overhead largely depends on data frame size and data transfer speeds.* """, ) gain = cb.FloatParameter( label="Gain", default_value=0.5, limits=(0.0, 1.0), decimals=2, order=1040, category=cb.Category.ADVANCED, help=r""" The receiver gain used in the sensor. If the gain is too low, objects may not be visible, or it may result in poor signal quality due to quantization errors. If the gain is too high, strong reflections may result in saturated data. We recommend not setting the gain higher than necessary due to signal quality reasons. Must be between 0 and 1 inclusive, where 1 is the highest possible gain. .. note:: When Sensor normalization is active, the change in the data due to changing gain is removed after normalization. Therefore, the data might seen unaffected by changes in the gain, except very high (receiver saturation) or very low (quantization error) gain. Sensor normalization is not available for the Sparse service, but is enabled by default for the other services - Envelope, IQ, and Power Bins. """, ) hw_accelerated_average_samples = cb.IntParameter( label="HW accel. average samples", default_value=10, limits=(1, 63), order=1030, category=cb.Category.ADVANCED, help=r""" Number of samples taken to obtain a single point in the data. These are averaged directly in the sensor hardware - no extra computations are done in the MCU. The time needed to measure a sweep is roughly proportional to the HWAAS. Hence, if there's a need to obtain a higher sweep rate, HWAAS could be decreased. Note that HWAAS does not affect the amount of data transmitted from the sensor over SPI. Must be at least 1 and not greater than 63. """, ) maximize_signal_attenuation = cb.BoolParameter( label="Max signal attenuation", default_value=False, order=2000, category=cb.Category.ADVANCED, help=r""" When measuring in the direct leakage (around 0m), this setting can be enabled to minimize saturation in the receiver. We do not recommend using this setting under normal operation. """, ) profile = cb.EnumParameter( label="Profile", enum=Profile, default_value=Profile.PROFILE_2, order=20, help=r""" The main configuration of all the services are the profiles, numbered 1 to 5. The difference between the profiles is the length of the radar pulse and the way the incoming pulse is sampled. Profiles with low numbers use short pulses while the higher profiles use longer pulses. Profile 1 is recommended for: - measuring strong reflectors, to avoid saturation of the received signal - close range operation (<20 cm), due to the reduced direct leakage Profile 2 and 3 are recommended for: - operation at intermediate distances, (20 cm to 1 m) - where a balance between SNR and depth resolution is acceptable Profile 4 and 5 are recommended for: - for Sparse service only - operation at large distances (>1 m) - motion or presence detection, where an optimal SNR ratio is preferred over a high resolution distance measurement The previous profile Maximize Depth Resolution and Maximize SNR are now profile 1 and 2. The previous Direct Leakage Profile is obtained by the use of the Maximize Signal Attenuation parameter. """, ) downsampling_factor = cb.IntParameter( label="Downsampling factor", default_value=1, limits=(1, None), order=1020, category=cb.Category.ADVANCED, help=r""" The range downsampling by an integer factor. A factor of 1 means no downsampling, thus sampling with the smallest possible depth interval. A factor 2 samples every other point, and so on. In Envelope and IQ, the finest interval is ~0.5 mm. In Power Bins, it is the same but then further downsampled in post-processing. In sparse, it is ~6 cm. The downsampling is performed by skipping measurements in the sensor, and therefore gives lower memory usage, lower power consumption, and lower duty cycle. In sparse, setting a too large factor might result in gaps in the data where moving objects "disappear" between sampling points. In Envelope, IQ, and Power Bins, the factor must be 1, 2, or 4. In sparse, it must be at least 1. Setting a factor greater than 1 might affect the range end point and for IQ and Envelope, also the first point. """, ) tx_disable = cb.BoolParameter( label="Disable TX", default_value=False, order=3000, category=cb.Category.ADVANCED, help=r""" Disable the radio transmitter. If used to measure noise, we recommended also switching off noise level normalization (if applicable). """, ) power_save_mode = cb.EnumParameter( label="Power save mode", enum=PowerSaveMode, default_value=PowerSaveMode.ACTIVE, order=3100, category=cb.Category.ADVANCED, ) def check(self): alerts = [] if self.repetition_mode == __class__.RepetitionMode.SENSOR_DRIVEN: if self.update_rate is None: alerts.append( cb.Error("update_rate", "Must be set when sensor driven")) if self.gain > 0.9: alerts.append( cb.Warning("gain", "Too high gain causes degradation")) if self.range_start < self.profile.approx_direct_leakage_length: alerts.append( cb.Info("range_interval", "Direct leakage might be seen")) return alerts
class ProcessingConfiguration(configbase.ProcessingConfig): VERSION = 1 depth_leak_sample = configbase.FloatParameter( label="Leak sample position", default_value=0.15, limits=(0.05, 0.25), unit="m", logscale=False, decimals=3, updateable=True, order=0, help=("Distance from the sensor for the leak sample position"), ) depth_leak_end = configbase.FloatParameter( label="Leak end position", default_value=0.30, limits=(0.10, 0.50), unit="m", logscale=False, decimals=3, updateable=True, order=1, help= ("Worst case distance from the sensor for the end of leak reflections" ), ) leak_max_amplitude = configbase.FloatParameter( label="Max leak amplitude", default_value=1000, limits=(100, 10000), logscale=True, decimals=0, updateable=True, order=2, help= ("The largest expected amplitude at the leak sample position when there is " "no object above the sensor"), ) detector_queue_target_length = configbase.IntParameter( label="Detector queue length", default_value=3, limits=(1, 10), updateable=True, order=3, help= ("Car detection criterion parameter: " "The number of sweep value pairs (weight, distance) in the detector queue" )) weight_threshold = configbase.FloatParameter( label="Weight threshold", default_value=5, limits=(0.5, 500), logscale=True, decimals=1, updateable=True, order=4, help=("Car detection criterion parameter: " "Minimal value of the weights in the detector queue"), ) weight_ratio_limit = configbase.FloatParameter( label="Weight ratio limit", default_value=3, limits=(1, 10), logscale=True, decimals=2, updateable=True, order=5, help= ("Car detection criterion parameter: " "Maximal ratio between the maximal and the minimal weights in the detector queue" ), ) distance_difference_limit = configbase.FloatParameter( label="Distance limit", default_value=0.1, limits=(0.01, 0.5), logscale=True, decimals=3, updateable=True, order=6, help= ("Car detection criterion parameter: " "Maximal difference between the maximal and minimal distances in the detector queue" ), ) history_length_s = configbase.FloatParameter( label="History length", unit="s", default_value=300, limits=(1, 3600), logscale=True, decimals=0, updateable=True, order=100, help=( "The time interval that is shown in the \"Detection history\" plot" ), ) def check(self): alerts = [] if self.depth_leak_sample >= self.depth_leak_end: alerts.append( configbase.Error("depth_leak_sample", "Must be less than the leak end position")) return alerts def check_sensor_config(self, sensor_config): alerts = [] if sensor_config.update_rate is None: alerts.append(configbase.Error("update_rate", "Must be set")) if not sensor_config.noise_level_normalization: alerts.append( configbase.Error("noise_level_normalization", "Must be set")) if self.depth_leak_sample < sensor_config.range_start \ or self.depth_leak_sample > sensor_config.range_end: alerts.append( configbase.Error( "range_interval", "Leak sample position outside the range interval")) return alerts