A212 API reference

Contents

A212 API reference#

This page provides an auto-generated summary of Acconeer Exploration Tool’s A212 API.

Configurations#

Session#

class acconeer.exptool.a2.SessionConfig(sensor_configs: list[SensorConfig], update_rate: float | None)#
sensor_configs: list[SensorConfig]#

The list of sensor configs

update_rate: float | None#

The update rate of the server.

A measurement of every sensor config in sensor_configs is made in one update period.

classmethod default() → SessionConfig#

Creates a default SessionConfig

Example:

>>> SessionConfig.default()
SessionConfig(sensor_configs=[SensorConfig(...)], update_rate=None)
property sensor_config: SensorConfig#

Return the sole sensor config.

Raises:

AttributeError – if len(sensor_configs) > 1

validate() → None#

Performs self-validation and validation of its sensor configs

Raises:

ValidationError – If anything is invalid.

Sensor#

class acconeer.exptool.a2.SensorConfig(subsweeps: list[SubsweepConfig], sweeps_per_frame: int, sweep_rate, frame_rate, inter_frame_idle_state, inter_sweep_idle_state, jitter_compensation=True)#
subsweeps: list[SubsweepConfig]#

The list of subsweep configs

sweeps_per_frame: int#

Sets the number of sweeps that will be captured in each frame(measurement). Can be set to 0 if e.g. only temperature measurement is wanted.

sweep_rate: float | None#

The rate for sweeps in a frame (measurement) in Hz.

Must be > 0 or None, where None is interpreted as max sweep rate.

frame_rate: float | None#

The frame rate in Hz.

Must be > 0 or None, where None is interpreted as max frame rate.

inter_frame_idle_state: IdleState#

The inter frame idle state is the state the sensor idles in between each frame.

inter_sweep_idle_state: IdleState#

The inter sweep idle state is the state the sensor idles in between each sweep in a frame.

jitter_compensation: bool#

Enable jitter compensation

property start_point: int#

The starting point of the sweep. The corresponding start in millimeter is approximately start_point * 2.5 mm.

property num_points: int#

The number of data points to measure in a sweep.

property step_length: int#

The number of steps to have between each data point.

property profile: Profile#

Profile to use

Each profile consists of a number of settings for the sensor that configures the TX and RX paths. Lower profiles have higher depth resolution while higher profiles have higher SNR.

property prf: PRF#

Pulse Repetition Frequency

property tx_antenna_mask: int#

The active TX antenna mask

property rx_antenna_mask: int#

The active RX antenna mask

classmethod default() → SensorConfig#

Creates a default SensorConfig

classmethod single_subsweep(*, sweeps_per_frame: int = 1, sweep_rate: float | None = None, frame_rate: float | None = None, inter_frame_idle_state: IdleState = IdleState.ARMED, inter_sweep_idle_state: IdleState = IdleState.ARMED, jitter_compensation: bool = True, **subsweep_parameters: Any) → SensorConfig#

Creates a SensorConfig with a single SubsweepConfig,

The subsweep_parameters are sent directly to SubsweepConfig’s constructor.

Examples:

>>> SensorConfig.single_subsweep()
SensorConfig(subsweeps=[SubsweepConfig(start_point=100, num_points=40, step_length=16, ...)], sweeps_per_frame=1, ...)
>>> SensorConfig.single_subsweep(sweeps_per_frame=3, start_point=20)
SensorConfig(subsweeps=[SubsweepConfig(start_point=20,...)],
             sweeps_per_frame=3,
             ...)
classmethod identical_subsweeps(*, num_subsweeps: int, sweeps_per_frame: int = 1, sweep_rate: float | None = None, frame_rate: float | None = None, inter_frame_idle_state: IdleState = IdleState.ARMED, inter_sweep_idle_state: IdleState = IdleState.ARMED, jitter_compensation: bool = True, **subsweep_parameters: Any) → SensorConfig#

Creates a SensorConfig with num_subsweeps identical subsweeps.

Examples:

>>> SensorConfig.identical_subsweeps(num_subsweeps=2, num_points=37)
SensorConfig(subsweeps=[SubsweepConfig(...,
                                       num_points=37,
                                       ...),
                        SubsweepConfig(...,
                                       num_points=37,
                                       ...)],
             sweeps_per_frame=1,
             ...)
property subsweep: SubsweepConfig#

Get the sole subsweep config.

Raises:

AttributeError – if there is more than 1 subsweep.

>>> config = SensorConfig.identical_subsweeps(num_subsweeps=2)
>>> config.subsweep
Traceback (most recent call last):
 ...
AttributeError: SensorConfig has too many subsweeps ...
property num_subsweeps: int#

Get the number of subsweeps

property frame_shape: tuple[int, int]#

The shape of the data array produced when measuring with this config

validate() → None#

Performs self-validation and validation of its subsweep configs

Raises:

ValidationError – If anything is invalid.

Subsweep#

class acconeer.exptool.a2.SubsweepConfig(*, start_point=100, num_points=40, step_length=16, profile=Profile.PROFILE_3, prf=PRF.PRF_20_8_MHZ, tx_antenna_mask=7, rx_antenna_mask=15, hwaas_max=50, hwaas_min=1, hwaas_exponent=0, receiver_gain=10, enable_tx=True)#
start_point: int#

The starting point of the sweep. The corresponding start in millimeter is approximately start_point * 2.5 mm.

num_points: int#

The number of data points to measure in a sweep.

step_length: int#

The number of steps to have between each data point.

profile: Profile#

Profile to use

Each profile consists of a number of settings for the sensor that configures the TX and RX paths. Lower profiles have higher depth resolution while higher profiles have higher SNR.

prf: PRF#

Pulse Repetition Frequency

tx_antenna_mask: int#

The active TX antenna mask

rx_antenna_mask: int#

The active RX antenna mask

hwaas_max: int#

Maximum Hardware accelerated average samples

If hwaas_exponent is set to 0, this value will be used for all points in the sweep.

hwaas_min: int#

Minimum Hardware accelerated average samples

Limits the minimum number of HWAAS used for one point. When the number of HWAAS is scaled with the distance, this value limits the minimum number of HWAAS used for one point

hwaas_exponent: int#

Hardware accelerated average samples exponent

The hwaas_exponent parameter controls how HWAAS is scaled with respect to distance during a sweep. The scaling is determined by the formula:

max(hwaas_min, hwaas_max * (d_n / d_end) ^ hwaas_exponent)

where d_n is the distance at point n and d_end is the distance at the last point in the sweep. HWAAS-scaling compensates for the energy loss of the propagating signal as it travels to and from the target.

Commonly used values:

4: Use this for targets with a general shape. This accounts for the highest degree of energy dilution. 2: Use this for large planar targets, such as flat surfaces, where energy spreads less. 0: Use this to maintain constant HWAAS throughout the sweep. No scaling is applied, and HWAAS remains unchanged.

receiver_gain: int#

Receiver gain

enable_tx: bool#

Enable tx

property num_channels: int#

The number of channels based tx_antenna_mask and rx_antenna_mask

property end_point: int#

The end point measured by this subsweep config

property hwaas_points: NDArray[int64]#

The hwaas values used for each point.

validate() → None#

Performs self-validation

Raises:

ValidationError – If anything is invalid.

Parameter enums#

class acconeer.exptool.a2.Profile#
PROFILE_1 = 1#
PROFILE_2 = 2#
PROFILE_3 = 3#
PROFILE_4 = 4#
PROFILE_5 = 5#
PROFILE_6 = 6#
PROFILE_7 = 7#
class acconeer.exptool.a2.PRF(*values)#

Pulse Repetition Frequency

Pulse Repetition Frequency, PRF, is the frequency at which pulses are sent out from the radar system. The measurement time is approximately proportional to the PRF. The higher the PRF, the shorter the measurement time.

This parameter sets the Maximum Measurable Distance, MMD, that can be achieved. MMD is the maximum value for the end point, i.e.,the start point + (number of points * step length). For example, an MMD of 7.0 m means that the range cannot be set further out than 7.0 m.

It also sets the Maximum Unambiguous Range, MUR, that can be achieved. MUR is the maximum distance at which an object can be located to guarantee that its reflection corresponds to the most recent transmitted pulse. Objects farther away than the MUR may fold into the measured range. For example, with a MUR of 11.5 m, an object at 13.5 m could become visible at 2 m.

PRF Setting

PRF

MMD

MUR

PRF_5_3_MHZ

5.3 MHz

25.0 m

28.0 m

PRF_8_3_MHZ

8.3 MHz

15.0 m

18.0 m

PRF_12_5_MHZ

12.5 MHz

9.0 m

12.0 m

PRF_15_8_MHZ

15.8 MHz

6.6 m

9.6 m

PRF_20_8_MHZ

20.8 MHz

4.2 m

7.2 m

PRF_27_7_MHZ

27.7 MHz

2.4 m

5.4 m

PRF_5_3_MHZ = 1#
PRF_8_3_MHZ = 2#
PRF_12_5_MHZ = 3#
PRF_15_8_MHZ = 4#
PRF_20_8_MHZ = 5#
PRF_27_7_MHZ = 6#
property frequency: int#

Pulse repetition frequency in Hz

class acconeer.exptool.a2.IdleState(*values)#

Idle state

READY = 0#
ARMED = 1#

Entities#

RSS representations#

class acconeer.exptool.a2.Result(...)#

Result

Represents the RSS processing_result.

frame: NDArray[complex128]#

Frame data in a complex float data format

tick: int#

Server tick when the server got the interrupt from the sensor

data_saturated: list[bool]#

Indication of sensor data being saturated, can cause data corruption. Lower the receiver gain if this indication is set.

measured_sweep_rate: float | None#

Sweep rate measured by RSS. This value might differ from the configured sweep rate since the configured sweep rate represents the maximum requested sweep rate, whereas the measured sweep rate reflects the actual rate achieved during operation, which may be lower if the sensor cannot keep up with the requested speed.

If the configuration includes only a single sweep per frame, the measured sweep rate will be None.

frame_delayed: bool#

Indication of a delayed frame.

A frame is considered delayed when it could not be measured at the specified frame rate. This means that only a set frame rate can produce delayed frames. If the frame rate is uncapped, the frame cannot be delayed.

The frame rate might need to be lowered if this indication is set.

calibration_temperature: int#

The temperature of previously performed calibration

calibration_needed: bool#

Whether the current calibration has become invalid.

Recalibration is performed by setting up a new session (see acconeer.exptool.a2.Client.setup_session()).

seconds_since_server_start(ticks_per_second: int) → float#

Return how many seconds the server has been running before producing this result

seconds_since_tick(ticks_per_second: int, tick: int) → float#

Return how many seconds has elapsed between the specified server tick and this result.

Note that the specified tick needs to be less than this Result’s tick.

property int16_complex_frame: NDArray[Any]#

Frame data in the original data format (complex int16)

get_subframe(config: SensorConfig, subsweep_index: int, channel_indexes: Container[int] = ALL) → NDArray[complex128]#

Get the list of subframes (data collected by a subsweep) given a SensorConfig

The channel index is Tx-major

Examples:

>>> r = Result(frame=np.array([[0.+0.j, 1.+1.j, 2.+2.j, 3.+3.j],
...                            [4.+4.j, 5.+5.j, 6.+6.j, 7.+7.j]]),
...            temperature=21, tick=0, data_saturated=[False],
...            measured_sweep_rate=7.35, frame_delayed=False,
...            calibration_temperature=25, calibration_needed=False)
...
>>> config = SensorConfig.single_subsweep(num_points=4, tx_antenna_mask=0b001, rx_antenna_mask=0b0001)
>>> r.get_subframe(config, 0)
array([[0.+0.j, 1.+1.j, 2.+2.j, 3.+3.j],
       [4.+4.j, 5.+5.j, 6.+6.j, 7.+7.j]])
>>> config = SensorConfig.identical_subsweeps(num_subsweeps=2, num_points=2, tx_antenna_mask=0b001, rx_antenna_mask=0b0001)
>>> r.get_subframe(config, 0)
array([[0.+0.j, 1.+1.j],
       [4.+4.j, 5.+5.j]])
>>> r.get_subframe(config, 1)
array([[2.+2.j, 3.+3.j],
       [6.+6.j, 7.+7.j]])

It’s also possible to extract only the data of specified channels using channel_indexes:

>>> config_2_channels = SensorConfig.identical_subsweeps(num_subsweeps=2,
...                                                      num_points=1,
...                                                      tx_antenna_mask=0b001,
...                                                      rx_antenna_mask=0b0011)
...
>>> r.get_subframe(config_2_channels, 0, channel_indexes=[1])
array([[1.+1.j],
       [5.+5.j]])
>>> r.get_subframe(config_2_channels, 1, channel_indexes=[1])
array([[3.+3.j],
       [7.+7.j]])

Other#

class acconeer.exptool.a2.Metadata(tick_period: int, sensor_calibration: str | None, update_period: float | None)#
tick_period: int#
sensor_calibration: str | None#
update_period: float | None#
class acconeer.exptool.a2.GroupResult(results: list[Result])#

Group result

Represent the result of an Exploration Server group (multiple results)

results: list[Result]#

List of results

property frame: NDArray#

Frame data in a complex float data format

property tick: int#

Server tick when the server got the interrupt from the sensor

property data_saturated: list#

Indication of sensor data being saturated, can cause data corruption. Lower the receiver gain if this indication is set.

property measured_sweep_rate: float#

Sweep rate measured by RSS. This value might differ from the configured sweep rate since the configured sweep rate represents the maximum requested sweep rate, whereas the measured sweep rate reflects the actual rate achieved during operation, which may be lower if the sensor cannot keep up with the requested speed.

If the configuration includes only a single sweep per frame, the measured sweep rate will be None.

property result: Result#

Retrieves the sole result

Raises:

AttributeError – if there are more than 1 result

class acconeer.exptool.a2.AntennaArray(*values)#

Antenna arrays of the A2 sensor packages, with antenna positions and channel corrections

AP212 ...#
get_correction_scalar(channel_number: int, az: float, el: float = 0.0, *, profile: Profile) → complex#

Get a correction scalar for the given channel, angles (in radians) and profile

Uses chebval2d if the profile’s table is 3D (az, el correction); falls back to chebval (az-only) for a 2D table.

get_nominal_steering_scalar(channel_number: int, az: float, el: float) → complex#

Get a steering scalar for the given channel and angles (in radians)

The channel number is Tx-major.

get_steering_vector(az: float, el: float, rx_mask: int, tx_mask: int, corrected: bool = True, *, profile: Profile) → NDArray[complex128]#

Get the steering vector for all channels, given angles (in radians) and profile

The returned steering vector is indexed by channel index (Tx-major)

See :py:meth:AntennaArray.get_steering_scalar.

get_steering_vectors(az_angles: NDArray[float64], el_angles: NDArray[float64], tx_antenna_mask: int, rx_antenna_mask: int, *, profile: Profile) → NDArray[complex128]#

Calculate steering vectors for all az/el angle combinations, for the given profile.

Returns an array of shape (num_az, num_el, num_channels).

channels(tx_mask: int, rx_mask: int) → dict[int, Channel]#

Get the channels in this antenna array.

The keys in the returned dict are the Tx-major channel numbers. If the AntennaArray does not contain all antennas masked in by the passed masks, those will silently be omitted.

>>> AntennaArray.AP212.channels(rx_mask=0b1111, tx_mask=0b111)
{0: Channel(tx=Pos(x=-7.5, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=2.5)),
 1: Channel(tx=Pos(x=-7.5, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=0.0)),
 2: Channel(tx=Pos(x=-7.5, y=0.0, z=2.5), rx=Pos(x=0.0, y=0.0, z=0.0)),
 3: Channel(tx=Pos(x=-7.5, y=0.0, z=2.5), rx=Pos(x=2.5, y=0.0, z=0.0)),
 4: Channel(tx=Pos(x=7.5, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=2.5)),
 5: Channel(tx=Pos(x=7.5, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=0.0)),
 6: Channel(tx=Pos(x=7.5, y=0.0, z=2.5), rx=Pos(x=0.0, y=0.0, z=0.0)),
 7: Channel(tx=Pos(x=7.5, y=0.0, z=2.5), rx=Pos(x=2.5, y=0.0, z=0.0)),
 8: Channel(tx=Pos(x=0.0, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=2.5)),
 9: Channel(tx=Pos(x=0.0, y=0.0, z=2.5), rx=Pos(x=-2.5, y=0.0, z=0.0)),
 10: Channel(tx=Pos(x=0.0, y=0.0, z=2.5), rx=Pos(x=0.0, y=0.0, z=0.0)),
 11: Channel(tx=Pos(x=0.0, y=0.0, z=2.5), rx=Pos(x=2.5, y=0.0, z=0.0))}

Client#

class acconeer.exptool.a2.Client(*args: Any, **kwargs: Any)#
class ResultFormat(*values)#

Decide the format of the data returned by Client.get_next

AUTO = 1#
FULL = 3#
SOLE = 2#
classmethod open(ip_address: str | None = None, tcp_port: int | None = None, serial_port: str | None = None, usb_device: str | bool | None = None, mock: bool | None = None, override_baudrate: int | None = None, flow_control: bool = True, generation: str | None = 'a2') → Self#

Open a new client

abstractmethod close() → None#

Closes the connection to the host

get_next() → GroupResult | Result#
get_next(*, format: te.Literal[AUTO]) → GroupResult | Result
get_next(*, format: te.Literal[FULL]) → GroupResult
get_next(*, format: te.Literal[SOLE]) → Result

Gets results from the server.

Parameters:

format –

Decides the format of the returned result.

Returns:

See format.

Raises:

ClientError if the session is not started or a sole Result could not be received.

abstractmethod setup_session(config: SessionConfig, calibration: str | None = None) → Metadata#

Sets up the session specified by config

A session is typically setup by performing radar calibration on the Exploration Server.

Sensor calibrations may be reused by reading it from Metadata.sensor_calibration passing it to the calibration parameter.

abstractmethod start_session() → None#

Starts the already set up session.

After this call, the server starts streaming data to the client.

Raises:

ClientError if Client’s session is not set up.

stop_session() → None#

Stops an on-going session

Raises:

ClientError if Client’s session is not started.

property calibration: str#

Returns the calibration string if calibration was performed in setup_session(),

Raises:

ClientError if Client did not provide calibration.

property calibration_provided: bool#

Returns whether a calibration was provided in setup_session.

property client_info: ClientInfo#

The ClientInfo.

property connected: bool#

Whether this Client is connected.

abstract property server_info: ServerInfo#

The ServerInfo

property session_config: SessionConfig#

The SessionConfig this client is setup with

property session_is_setup: bool#

Whether this Client has a session set up.

property session_is_started: bool#

Whether this Client’s session is started.

property tick_period: int#

The theoretical number of ticks between results

Recording#

Recorder#

class acconeer.exptool.a2.H5Recorder(path_or_file: PathOrH5File, attachable: RecorderAttachable[Recorder[SessionConfig, Metadata, GroupResult, ServerInfo]] | None = None, mode: str = 'x', *, _chunk_size: int | None = None, _lib_version: str | None = None, _timestamp: str | None = None, _uuid: str | None = None)#

Recorder writing directly to an HDF5 file

Parameters:
  • path_or_file –

    If a path-like object, by default (depending on mode below) an HDF5 file is created at that path. When stopping, the file is closed.

    If an h5py.File, that file is used as-is. In this case, the file is not closed when stopping.

  • attachable – A Client that the recorder will be attached to. When a recorder is attached to a Client, all metadata and data frames will be recorded.

  • mode – The file mode to use if a path-like object was given for path_or_file. Default value is ‘x’, meaning that we open for exclusive creation, failing if the file already exists.

  • _chunk_size –

    If given, data will be written to file every _chunk_size samples.

    If not given, data will be written at least every 512:th sample, or at least once per second, whichever comes first.

    Setting a small chunk size (e.g. 1) may degrade performance for high sample rates.

    Internal parameter, subject to change.

Record#

class acconeer.exptool.a2.H5Record(file: h5py.File, chunk_size: int = 100, schema: te.ClassVar[type[SessionSchema]] = <class 'acconeer.exptool._core.recording.h5_session_schema.SessionSchema'>)#
file: File#
property client_info: ClientInfo#
property lib_version: str#
property server_info: ServerInfo#
property timestamp: str#
property uuid: str#
close() → None#
require_algo_group(key: str) → Group#
session(session_index: int) → H5SessionRecord#
property num_sessions: int#
property group_results: Iterator[GroupResult]#
property results: Iterator[Result]#
property num_frames: int#
property session_config: SessionConfig#

Open Record#

acconeer.exptool.a2.open_record(path_or_file: PathOrH5File) → H5Record#

Open a record from file

Since this function returns a H5Record, data is not immediately loaded into memory. Rather, data is lazily loaded from the underlying file on demand.

Either a path or an opened file (h5py.File) may be given. If a path is given, the file will be opened by this function, and must be closed again by the user. The recommended way to do this is by using the context manager of the returned persistent record, like:

with a2.open_record("path/to/my/file.h5") as record:
    record.timestamp
Returns:

A H5Record wrapping the given file

Save Record#

acconeer.exptool.a2.save_record(path_or_file: PathOrH5File, record: H5Record) → None#

Save a record to a HDF5 file