Scan Configuration ================== Scans can be configured with a configuration dictionary using the :class:`~wni.radar.Radar` object. The scan configuration dict is extensive. This documents everything that can be done with the dict. The best way to read this document may be to start with the :ref:`example configuration ` and make changes to it to make the scan do what you need it to do. .. autoclass:: wni.radar.Radar :members: apply_dict_config, apply_default_settings, settings_as_dict, do_vcp .. _configuration-dict: ---------------------- The configuration dict ---------------------- The configuration dict is a nested dict, where each level of nesting controls another part of the system. All aspects of a scan configuration can be controlled through the configuration dict. The dict must be passed in to the :meth:`~wni.radar.Radar.apply_dict_config` method of the :class:`~wni.radar.Radar` object. There are three toplevel keys: :ref:`system_config `, :ref:`scan_settings `, and :ref:`transceiver `. .. _system-config: ############################ System Configuration Options ############################ The ``system_config`` key of :ref:`the configuration dict ` controls the :class:`~wni.system_config.SystemConfig` class. The value is another dict, with the following keys: * ``ch1_ref_cal`` (float [dB]): Reflectivity calibration for channel 1. This value will be added to the calculated power for channel 1. * ``ch2_ref_cal`` (float [dB]): Reflectivity calibration for channel 2. This value will be added to the calculated power for channel 2. * ``dump_to_disk`` (bool): Whether to dump the data to disk on the radar. This is useful to set to ``True`` if you want to collect I/Q data, since that may be too much data to transport over a network. * ``data_dump_directory`` (str): The directory to dump data to. This is only relevant if ``dump_to_disk`` is True. * ``output_iq`` (bool): Whether I/Q data should be output. This is useful in conjunction with ``dump_to_disk``. Remember that setting this to True can cause the radar to generate 80 MB/s of data (that's 288 GB/hr). The following keys affect how the data will be processed: * ``calc_reflectivity`` (bool): Whether to calculte reflectivity. * ``calc_velocity`` (bool): Whether to calculate Doppler velocity. * ``calc_mag_R1`` (bool): Whether to calculate the magnitude of the lag1 autocorrelation. (This is only useful for point targets, not weather.) * ``doppler_processing`` (bool): Whether to calculate the radar products using the `power spectral density `_. This is more processing intensive, but should produce better results. * ``filter_cpu`` (bool): Whether to apply filtering on the CPU. Filtering may also happen on the receive path of the FPGA. If ``filter_cpu`` is True, the FIR taps must be specified in the :ref:`scan settings dict `. * ``decimation`` (int): Decimation to apply on the filtered data. This is only applicable when ``filter_cpu`` is True. * ``do_range_correction`` (bool): Whether to apply range correction. This should always be set to True for weather radars in normal operation. .. _scan-settings-config: ########################## Scan Setting Configuration ########################## The ``scan_settings`` key of :ref:`the configuration dict ` contains settings that control the scan control: things like prt, waveform, receive length, FPGA filter settings, etc. This controls most things about the scan that are *timing* or *waveform* specific. The RF stuff (e.g. center frequency, attenuation, etc.) is controlled by the :ref:`transceiver ` key. There are a few toplevel settings, and then several nested settings. The layout of the configuration dict was chosen to be the same as the hierarchy in the system: we have one FPGA, and in the FPGA there are two mostly-independent **channels**. Each channel has its own transmit path and receive path, and they can be configured independently through this config dict. These are the settings that can be configured. **Usually you probably don't need to change these, and you may want** :ref:`channel config ` **and** :ref:`scan set config ` **below.** This is the most complicated configuration dict, because there are many nested dict fields. See the :ref:`example script ` below for all the nesting. * ``max_tx_amp_length`` (float [μs]): maximum transmit amplifier length before it is considered continuous transmit. Between this and ``max_duty_cycle`` you can ensure that the amplifiers do not get destroyed. * ``max_duty_cycle`` (int, [%]): Maximum duty cycle the amplifiers can support. Must be a number between 0 (cannot transmit at all) and 100 (continuous duty cycle). * ``jitter_min`` (int, [clock cycles]): minimum jitter. Units are clock cycles of a 120 MHz clock. This is the minimum jitter that can be applied, if jitter is enabled (see `scan set config `_) key ``add_jitter``. * ``jitter_mask`` (int): The mask (up to ``0xfff``) to use when reading the pseudorandom number. This can effectively be used to set the maximum jitter, by using a smaller mask: e.g. `0x3ff` will max out at 8.525 microseconds. * ``ch1``, ``ch2``, ``ch*`` (dict): A dict for configuring channel-specific settings. ``ch1`` applies to channel 1, ``ch2`` applies to channel 2, and ``ch*`` applies to both. If ``ch*`` and ``ch1`` or ``ch2`` are both given, ``ch*`` will **always** be applied first. This allows for setting common settings on ``ch*`` and overriding them on each channel. See :ref:`channel config below ` for details on channel configuration options. .. _channel-config: ~~~~~~~~~~~~~~ Channel Config ~~~~~~~~~~~~~~ The channel configuration determine behavior common to all :ref:`scan sets `. These are things in the FPGA like FIR filter, scan scheduler mode, and related settings. The following configuration options may be given: * ``fir_enable`` (bool): Whether the FIR filter in the FPGA is enabled, the default is True. This should usually be set to True. Remember: the FPGA's FIR filter decimates the data by 6. In order to achieve the full data rate of the system, ``fir_enable`` must be True. * ``fir_fpga`` (dict): A dictionary configuring the FIR filter in the FPGA. See :ref:`fir-config` for information on configuring the taps fo the FIR filter in the FPGA. * ``num_sets`` (int): The number of :ref:`scan sets ` that are being used for this channel. Needs to be a number between 1 and 96. .. _scan-start-set: * ``scan_start_set`` (int): The set index which is the start of a scan. This should usually be ``0``. * ``set0``, ``set1``, ... ``set[n]``, ``set*`` (dict): The configuration dict for each :ref:`scan set `. ``set0``, ``set1``, up to ``set[num_sets - 1]`` can be set. If ``set*`` is specified, it will set the scan settings for all sets. You may set ``set*`` for applying common settings to all scan sets and override individual scan sets for more specific settings. * ``scheduler_mode`` (str): The mode of operation of the scan scheduler. The available modes are ``run_while_enabled`` (the default) and ``run_n``. If the mode is run_while_enabled, a scan will run as long as the software trigger is high. If the mode is run_n, a scan will run for exactly ``num_loops`` loops. * ``num_loops`` (int): The number of loops to execute if ``scheduler_mode`` is ``run_n``. If the mode is set to run_while_enabled, this setting is ignored. .. _fir-config: ++++++++++++++++++++++ FPGA FIR configuration ++++++++++++++++++++++ The FPGA FIR filter can be configured with up to 600 arbitrary taps. The FPGA FIR configuration is a dict, with the following keys. The FPGA FIR filter is on the receive path of the FPGA. It operates on the baseband I/Q signals from the `AD9361 transceiver `_. The following keys must be present in the FPGA configuration dict: * ``type`` (str): The type of configuration. The valid values are ``arb``, ``bpf``, or ``matched_filter``. The type of ``arb`` indicates that you are specifying arbitrary FIR taps. If the type is arb, you must specify the following keys must be present: * ``taps`` (numpy.ndarray): A complex numpy array indicating the real and imagniary taps of the complex FIR filter. All values should be between [-1, 1], and they will be scaled appropriately. .. _fir-bpf: The type of ``bpf`` indicates that you are setting up the FIR filter as a band-pass filter. A low-pass filter or high-pass filter are considered special cases of a bandpass filter here. If the type is bpf, the following keys must be present: * ``start`` (float, [MHz]): Start band of the filter. If this is set to 0, the filter will be set up as a low-pass filter. * ``stop`` (float, [MHz]): Stop band of the filter. If this is set to 15, the filter will be set up as a high-pass filter. * ``ntaps`` (int): Number of taps to utilize for the FIR filter. Default is 120 * ``window`` (str): The window to use. Default is ``hamming``. The type of ``matched_filter`` indicates to populate the taps with a `matched filter `_. If the type is matched_filter, the following keys must be present: * ``waveform`` (numpy.ndarray): The transmit waveform. .. _scan-set-config: +++++++++++++++ Scan Set Config +++++++++++++++ Each channel can expose up to 96 scan setting sets. The scan scheduler will loop through the scan sets based on each scan sets ``next_set`` value. The scan scheduler starts with the channel's :ref:`scan_start_set `, and follows ``next_set`` of all scans until it repeats. If there is no set that points back to ``scan_start_set``, the scan will not run. (To run with only a single scan set, set ``scan_start_set`` to 0 and ``next_set`` to 0. The following fields are available on each scan set: * ``prt`` (float [μs]): Pulse repetition time. * ``pulses`` (float [μs]): Number of pulses in this scan set. * ``rx_delay`` (float [μs]): Amount of time to delay the start of the receive window. * ``rx_length`` (float [μs]): Length of time the receive window is valid. * ``add_jitter`` (bool): Whether to insert jitter to the end of this scan set (after all pulses have been fired). * ``next_set`` (int): The scan set id to do next. * ``waveform`` (dict): The transmit waveform. See :ref:`waveform config ` below. * ``fir_config`` (dict): The FIR configuration that will run **on the processing computer**. This is a separate part of filtering from the FPGA FIR coefficients. See :ref:`FIR filter ` below. .. _waveform-config: %%%%%%%% Waveform %%%%%%%% Each scan set can be given its own transmit waveform until the block RAM in the FPGA is exhausted. The waveform configuration is given as a dict, with the following keys: * ``type`` (str): Either ``chirp`` or ``arb``. If type is ``chirp``, a `linear frequency-modulated (LFM) chirp `_ will be generated. In that case, the following keys must be present: * ``nsamples`` (int): Number of samples in the chirp. These are clocked at 30 MHz, so a value of 30 corresponds to a 1 μs pulse width. * ``center`` (float [MHz]): Center frequency of the LFM chirp. This should be a value between [-15, 15]. * ``bandwidth`` (float [MHz]): Bandwith of the chirp in MHz. This should be a value between [0, 15]. .. Note:: You must ensure that the chosen center frequency and bandwidth do not exceed the Nyquist frequency of the system. If you have a center * ``window`` (str): Amplitude window to apply to the I/Q samples. Default is ``hanning``. If type is ``arb``, that indicates an arbitrary waveform. The following keys must be present: * ``iq`` (numpy.ndarray): A complex-valued array of I/Q samples to be transmitted. * ``scale`` (float): An additional multiplier to apply to the I/Q samples. If ``iq`` is valued between [-1, 1], then scale should usually be set to ``0.25``. .. Warning:: The spuriuous transmit frequency performs much better if the DACs do not approach saturation. Therefore, ``scale`` should usually be provided as 0.25 if ``iq`` is valued between -1 and 1. .. _fir-filter-config: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Processing Computer FIR Filter %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% The processing computer can do additional filtering of the receive data. This is useful (read: **necessary**) when the FPGA FIR filter is set up as a bandpass filter. Then the processing computer can/should be set up to do pulse compression. The keys for FIR configuration are as follows: * ``type`` (str): The type of filter. Can be one of three values: ``arb``, ``bpf``, or ``matched_filter``. If ``type`` is ``arb``, that specifies that the taps will be arbitrarily specified. The following keys must be present: * ``taps`` (numpy.ndarray): The complex-valued taps as a numpy array. If ``type`` is ``bpf``, the keys are the same as specified for a :ref:`bpf on the FPGA FIR filter `. If ``type`` is ``matched_filter``, no additional keys are required. .. _transceiver-config: ######################### Transceiver Configuration ######################### The ``transceiver`` key of :ref:`the configuration dict ` contains settings that control the `AD9361 transceiver `_. These are things like Tx attenuation, Rx gain, radar center frequency, and calibration options. The following keys may be provided: * ``tx_frequency`` (float [Hz]): The transmit center frequency of the radar. * ``rx_frequency`` (float [Hz]): The receive center frequency of the radar. * ``ch1_rx_rf_gain``, ``ch2_rx_rf_gain`` (float [dB]): Receive gain of the transceiver. This effectively moves around the dynamic range of the receiver. It should be set low enough that there is no saturation. The two channels can be set to different values. * ``ch1_tx_attenuation``, ``ch2_tx_attenuation`` (int mdB): The transmit attenuation of teh transceiver. This should be set to apply the correct power to the transmitter. NB: The units are milli-decibels (mdB), which is weird. Don't accidentally be off by three orders of magnitude. * ``rf_loopback`` (bool): Whether the radar should be in an internal RF loopback mode. This prevents the RF signal from going to the frontend. * ``digital_loopback`` (bool): Whether the transceiver should loop the digital Tx data directly into the digital Rx path. * ``rx_rf_bandwidth`` (float [Hz]): The expected baseband bandwith of the receive signal. * ``tx_rf_bandwidth`` (float [Hz]): The expected baseband bandwith of the transmit signal. * ``tx_sampling_freq`` (float [Hz]): The transmit sampling frequency. If this is provided, it should *always* be set to 30 Mhz (30e6), unless you know what you are doing and understand all the implications. (This clock goes into the FPGA, and anything greater than 30 MHz will not meet timing; anything less will throw off things as well.) ################# VCP Configuration ################# The radar supports simple VCP patterns with the method :meth:`~wni.radar.Radar.do_vcp`. The dict contains the following keys: * ``type`` (str): The type of volume coverage pattern. The only supported type currently is ``"ppi"``. * ``value`` (list of dict): A list of azimuth speeds and elevation angles to point at for each revolution. Each entry in the list should contain settings for azimuth rotation speed and elevation angle. Each list entry will last a single revolution. After the last entry, the scan will start over with the first one. Example VCP settings: .. code-block:: python3 # run a single azimuth speed (10 degrees per second) and single elevation # (4 degrees) forever settings = { "vcp": [ {"az_speed": 20, "el": 4} ] } # Keep a single azimuth speed, but change elevation angles. The settings # here will cause the elevation to cycle from [0, 4, 8, 12, 8, 4, 0, 4, 8, # 12, 8, 4, ...] forever. settings = { "vcp": [ {"az_speed": 10, "el": 0}, {"az_speed": 10, "el": 4}, {"az_speed": 10, "el": 8}, {"az_speed": 10, "el": 12}, {"az_speed": 10, "el": 8}, {"az_speed": 10, "el": 4}, ] } .. _example-config: ########################## Example configuration dict ########################## .. literalinclude:: example_config.py :language: python