Scan Configuration¶
Scans can be configured with a configuration dictionary using the
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 example configuration and make changes to it to make the scan do what you need it
to do.
-
class
wni.radar.
Radar
(settings=None, recv_timeout=10000)[source]¶ A single class to give access to all of the radar.
Parameters: settings (dict) – If settings is given, it must be a mapping, the keys of which are described in the documentation. Scans can be configured by providing a settings map to the
Radar
object. To set the scan configuration, call theapply_dict_config()
method. To get the current settings, callsettings_as_dict()
.-
apply_default_settings
()[source]¶ Apply the default settings.
This is more useful for development than actually using the system, or for getting the system into a known state.
-
apply_dict_config
(settings)[source]¶ Apply configuration of the radar from a dict. See the dict configuration docs for more info.
-
do_vcp
(settings, scan_time=-1, wait=True)[source]¶ Do a Volume Coverage Pattern (vcp).
Parameters: - settings – a dict, with all the same keys as accepted in apply_dict_config, with an extra required key: “vcp”. The value of “vcp” must be a dict that scan_timer.do_vcp() understands.
- block – If True, do not return until the radar is finished with its scan.
-
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
apply_dict_config()
method of the
Radar
object. There are three toplevel keys:
system_config, scan_settings, and transceiver.
System Configuration Options¶
The system_config
key of the configuration dict controls the
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 toTrue
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 ifdump_to_disk
is True.output_iq
(bool): Whether I/Q data should be output. This is useful in conjunction withdump_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. Iffilter_cpu
is True, the FIR taps must be specified in the scan settings dict.decimation
(int): Decimation to apply on the filtered data. This is only applicable whenfilter_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 Setting Configuration¶
The scan_settings
key of 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
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 channel config and scan set config below. This is the most complicated configuration dict, because there are many nested dict fields. See the 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 andmax_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) keyadd_jitter
.jitter_mask
(int): The mask (up to0xfff
) 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, andch*
applies to both. Ifch*
andch1
orch2
are both given,ch*
will always be applied first. This allows for setting common settings onch*
and overriding them on each channel. See channel config below for details on channel configuration options.
Channel Config¶
The channel configuration determine behavior common to all 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 FPGA FIR configuration for information on configuring the taps fo the FIR filter in the FPGA.num_sets
(int): The number of scan sets that are being used for this channel. Needs to be a number between 1 and 96.scan_start_set
(int): The set index which is the start of a scan. This should usually be0
.set0
,set1
, …set[n]
,set*
(dict): The configuration dict for each scan set.set0
,set1
, up toset[num_sets - 1]
can be set. Ifset*
is specified, it will set the scan settings for all sets. You may setset*
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 arerun_while_enabled
(the default) andrun_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 exactlynum_loops
loops.num_loops
(int): The number of loops to execute ifscheduler_mode
isrun_n
. If the mode is set to run_while_enabled, this setting is ignored.
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 arearb
,bpf
, ormatched_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.
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 120window
(str): The window to use. Default ishamming
.
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¶
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 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 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 FIR filter below.
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): Eitherchirp
orarb
.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 ishanning
.
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. Ifiq
is valued between [-1, 1], then scale should usually be set to0.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 ifiq
is valued between -1 and 1.
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
, ormatched_filter
.If
type
isarb
, 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
isbpf
, the keys are the same as specified for a bpf on the FPGA FIR filter.If
type
ismatched_filter
, no additional keys are required.
Transceiver Configuration¶
The transceiver
key of 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
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:
# 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 configuration dict¶
"""
An example script which configures the radar. If you take this, make sure you
change the settings you care about. In particular, the level for transceiver
values ch1_rx_rf_gain, ch1_tx_attenuation, and the ch2 versions of those, will
need to be changed for your system. tx_frequency and rx_frequency will need to
be changed as well.
"""
from wni.radar import Radar
# grab our radar object
radar = Radar()
radar_settings = {
'system_config': {
# whether we want to output all I/Q. Usually this would be False.
'output_iq': False,
# example reflectivity calibration
'ch1_ref_cal': -71.3,
# example reflectivity calibration
'ch2_ref_cal': -72.3,
# Whether to calculate reflectivity
'calc_reflectivity': True,
# Whether to calculate velocity
'calc_velocity': True,
# Whether to calculate magnitude of the lag 1 autocorrelation. Not
# really useful for weather.
'calc_mag_R1': False,
# Whether to dump to disk
'dump_to_disk': False,
# where to dump data if dump_to_disk is True. If dump_to_disk is
# False, this is ignored.
'data_dump_directory': '.',
'filter_cpu': True,
# A decimation of 1 is the same as no decimation
'decimation': 1,
},
'scan_settings': {
'ch*': {
'scheduler_mode': 'run_while_enabled',
# only a single scan set
'num_sets': 1,
# which set to start with
'scan_start_set': 0,
# Enable the FPGA FIR filter
'fir_enable': True,
# define the coefficients
'fir_fpga': {
'type': 'bpf',
'start': 0,
'stop': 8,
'ntaps': 120,
},
'set0': {
# have to point to self
'next_set': 0,
# number of pulses per dwell
'pulses': 100,
# 1 ms (1000 microsecond) prt
'prt': 1000,
# receive for half the time
'rx_length': 500,
# begin receiving immmediately
'rx_delay': 0,
# whether to add jitter to the pulse
'add_jitter': False,
# waveform configuration
'waveform': {
'type': 'chirp',
'center': 3,
'bandwidth': 3,
'window': 'hanning',
'scale': 0.25,
# 300 samples is 10 microseconds
'nsamples': 300,
},
# this is the processing computer FIR filter setup. It's
# different from the ``fir_coef`` specified above.
'fir_config': {
# we can set it as matched filter, easy peasy.
'type': 'matched_filter',
},
},
},
},
'transceiver': {
# transmit and receive at the same frequency (remember to change both,
# not just one!)
'tx_frequency': 9.34E6,
'rx_frequency': 9.34E6,
# rx gain on the two channels in dB. Should be between 0-50
'ch1_rx_rf_gain': 0,
'ch2_rx_rf_gain': 0,
# tx attenuation; remember, this is mdB!!!
'ch1_tx_attenuation': int(70e3),
'ch2_tx_attenuation': int(70e3),
# actually transmit: set loopbacks off
'rf_loopback': False,
'digital_loopback': False,
# set filters on transceiver
'rx_rf_bandwidth': int(20e6),
'tx_rf_bandwidth': int(20e6),
},
}
radar.apply_dict_config(radar_settings)
# now you can run a scan if you want:
radar.trigger(3)