Borealis experiments are dictionaries in the programming language python, which contain all the information needed for the radar to decipher and operate different experiments. The different options below show a number of different keys possible to make an experiment. Choose your own options and click RUN to see how the different parameters effect the scan pattern or FOV of the radar. More information about Borealis experiments can be found below or in the Borealis documentation.
Choose a pre-made experiment:
Number of Range Gates:
Distance to First Range (km):
Beam Angles (°):
Beam Order:
Tau Spacing (us):
Pulse Sequence:
Pulse Length (us):
Integration Time (ms):
Transmit/Receive Frequency (kHz):
For more indepth information on how to create your own Borealis experiment, you can find the full documentation here.
| Name | Key | Description | normalscan Example |
|---|---|---|---|
| Number of Range Gates | num_ranges | Number of range gates in the field of view of the radar. | 75 |
| Distance to First Range Gate | first_range | Distance from the radar to the first range gate (km) | 180 |
| Beam Angles | beam_angle | The distance in degrees between the center of each of the beams. This is converted into a list of beam directions from the azimuth. | 3.24 |
| Beam Order | beam_order | A list of which beams to transmit and receive on in an experiment, in order. Beams can repeat, and you can use only a subset of any of the beams if required. It is also possible to image using multiple beams at one time in the Borealis software, however this is not widely used and not available in this tool at the moment. | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 |
| Pulse Sequence | pulse_sequence | The pulse sequence timing, given in quantities of tau_spacing. This is used to bild the lag table and should have an optimum number of different lags. Be aware the lag table does not show if your sequence has multiple instances of the same lag. | 0, 14, 22, 24, 27, 31, 42, 43 |
| Tau Spacing | tau_spacing | Increments between pulses in the pulse sequence (us). | 1500 |
| Pulse Length | pulse_len | Length of a single pulse (us). This value also controls the size of the range gates: 100 us for 15 km and 300 us for 45 km. | 300 |
| Integration Time | intt | Duration of an integration (ms), during which the radar will send a pulse sequence and listen on a specific beam. | 3500 |
| Transmit/Receive Frequency | txfreq | The frequency you would like to transmit the pulse on (kHz). | 10,500 |
There are additional keys used in Borealis that are not included in this tool. They are described in the Borealis documentation.
A fundamental aspect of Borealis experiments are the use of slices. Most simple experiments use a single slice, which is represented above in a single set of parameters. However, we can develop more complex experiments using additional slices. For example, the commonly used twofsound mode uses two slices with two different frequencies, one running a full scan of 16 beams first, then the second. Slices can also be used to run experiments near-simultaneously. Two slices can interface either per scan (see twofsound), per integration time on a single beam (one slice runs for specified integration time, then the second), per pulse sequence (where the pulse sequences alternate withint the integration time), and even down to the single pulses, where pulse sequences are run concurrently. More information on slices can be found here.
These values must be written in a way that the software can understand, here is an example of SuperDARN Canada's normalscan, an example of using two slices in twofsound and an example with parameters entered without reading in from a common fields file you can use to make your own python experiment file. The superdarn_common_fields module can be seen on SuperDARN Canada's Borealis GitHub page.
#!/usr/bin/python
import sys
import os
BOREALISPATH = os.environ['BOREALISPATH']
sys.path.append(BOREALISPATH)
import experiments.superdarn_common_fields as scf
from experiment_prototype.experiment_prototype import ExperimentPrototype
class Normalscan(ExperimentPrototype):
def __init__(self, **kwargs):
"""
kwargs:
freq: int
"""
cpid = 151
super(Normalscan, self).__init__(cpid)
if scf.IS_FORWARD_RADAR:
beams_to_use = scf.STD_16_FORWARD_BEAM_ORDER
else:
beams_to_use = scf.STD_16_REVERSE_BEAM_ORDER
if scf.opts.site_id in ["cly", "rkn", "inv"]:
num_ranges = scf.POLARDARN_NUM_RANGES
if scf.opts.site_id in ["sas", "pgr"]:
num_ranges = scf.STD_NUM_RANGES
# default frequency set here
freq = scf.COMMON_MODE_FREQ_1
if kwargs:
if 'freq' in kwargs.keys():
freq = kwargs['freq']
self.printing('Frequency set to {}'.format(freq))
self.add_slice({ # slice_id = 0, there is only one slice.
"pulse_sequence": scf.SEQUENCE_7P,
"tau_spacing": scf.TAU_SPACING_7P,
"pulse_len": scf.PULSE_LEN_45KM,
"num_ranges": num_ranges,
"first_range": scf.STD_FIRST_RANGE,
"intt": 3500, # duration of an integration, in ms
"beam_angle": scf.STD_16_BEAM_ANGLE,
"beam_order": beams_to_use,
"scanbound": [i * 3.5 for i in range(len(beams_to_use))], #1 min scan
"txfreq" : freq, #kHz
"acf": True,
"xcf": True, # cross-correlation processing
"acfint": True, # interferometer acfs
})
##!/usr/bin/python
import os
import sys
import copy
BOREALISPATH = os.environ['BOREALISPATH']
sys.path.append(BOREALISPATH)
from experiment_prototype.experiment_prototype import ExperimentPrototype
import experiments.superdarn_common_fields as scf
class Twofsound(ExperimentPrototype):
def __init__(self, **kwargs):
cpid = 3503
if scf.IS_FORWARD_RADAR:
beams_to_use = scf.STD_16_FORWARD_BEAM_ORDER
else:
beams_to_use = scf.STD_16_REVERSE_BEAM_ORDER
if scf.opts.site_id in ["cly", "rkn", "inv"]:
num_ranges = scf.POLARDARN_NUM_RANGES
if scf.opts.site_id in ["sas", "pgr"]:
num_ranges = scf.STD_NUM_RANGES
tx_freq_1 = scf.COMMON_MODE_FREQ_1
tx_freq_2 = scf.COMMON_MODE_FREQ_2
if kwargs:
if 'freq1' in kwargs.keys():
tx_freq_1 = int(kwargs['freq1'])
if 'freq2' in kwargs.keys():
tx_freq_2 = int(kwargs['freq2'])
slice_1 = { # slice_id = 0, the first slice
"pulse_sequence": scf.SEQUENCE_7P,
"tau_spacing": scf.TAU_SPACING_7P,
"pulse_len": scf.PULSE_LEN_45KM,
"num_ranges": num_ranges,
"first_range": scf.STD_FIRST_RANGE,
"intt": 3500, # duration of an integration, in ms
"beam_angle": scf.STD_16_BEAM_ANGLE,
"beam_order": beams_to_use,
"scanbound" : [i * 3.5 for i in range(len(beams_to_use))],
"txfreq" : tx_freq_1, #kHz
"acf": True,
"xcf": True, # cross-correlation processing
"acfint": True, # interferometer acfs
}
slice_2 = copy.deepcopy(slice_1)
slice_2['txfreq'] = tx_freq_2
list_of_slices = [slice_1, slice_2]
sum_of_freq = 0
for slice in list_of_slices:
sum_of_freq += slice['txfreq']# kHz, oscillator mixer frequency on the USRP for TX
rxctrfreq = txctrfreq = int(sum_of_freq/len(list_of_slices))
super(Twofsound, self).__init__(cpid, txctrfreq=txctrfreq, rxctrfreq=rxctrfreq,
comment_string='Twofsound classic scan-by-scan')
self.add_slice(slice_1)
self.add_slice(slice_2, interfacing_dict={0: 'SCAN'})
#!/usr/bin/python
import sys
import os
BOREALISPATH = os.environ['BOREALISPATH']
sys.path.append(BOREALISPATH)
from experiment_prototype.experiment_prototype import ExperimentPrototype
class Normalscan(ExperimentPrototype):
def __init__(self, **kwargs):
"""
kwargs:
freq: int
"""
cpid = 151
super(Normalscan, self).__init__(cpid)
self.add_slice({ # slice_id = 0, there is only one slice.
"pulse_sequence": [0, 9, 12, 20, 22, 26, 27],
"tau_spacing": 2400, #us
"pulse_len": 300, #us
"num_ranges": 75,
"first_range": 180, # km
"intt": 3500, # duration of an integration, in ms
"beam_angle": [-26.25, -22.75, -19.25, -15.75, -12.25, -8.75,
-5.25, -1.75, 1.75, 5.25, 8.75, 12.25, 15.75, 19.25, 22.75,
26.25] # 3.24 degrees seperation from the azimuth of radar
"beam_order": [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
"scanbound": [i * 3.5 for i in range(len(beams_to_use))], #1 min scan
"txfreq" : 10500, #kHz
"acf": True,
"xcf": True, # cross-correlation processing
"acfint": True, # interferometer acfs
})