Main Content

Radar Scenario Tutorial

This example shows how to construct and visualize a simple radar scenario programmatically using the radarScenario, theaterPlot, and radarDataGenerator objects.

Scenario Setup

To begin, create an empty radar scenario. All scenario properties have default values. The scenario does not contain any platform by default.

scenario = radarScenario
scenario = 

  radarScenario with properties:

      IsEarthCentered: 0
           UpdateRate: 10
       SimulationTime: 0
             StopTime: Inf
     SimulationStatus: NotStarted
            Platforms: {}
       SurfaceManager: [1x1 radar.scenario.SurfaceManager]
    AtmosphereManager: [1x1 radar.scenario.AtmosphereManager]

Adding Platforms

A scenario is comprised of objects, called platforms, upon which you may mount sensors and emitters. To add a platform, use the platform object function. Here you create a simple tower.

tower = platform(scenario)
tower = 

  Platform with properties:

       PlatformID: 1
          ClassID: 0
         Position: [0 0 0]
      Orientation: [0 0 0]
       Dimensions: [1x1 struct]
       Trajectory: [1x1 kinematicTrajectory]
    PoseEstimator: [1x1 insSensor]
         Emitters: {}
          Sensors: {}
       Signatures: {[1x1 rcsSignature]}

Platform Identification

When you first construct a platform, it has a PlatformID, which is a unique identifier you can use to identify the platform. The scenario assigns platform identifiers in the order that the platforms are created. You can specify a ClassID to denote the platform's classification. For example, here you use a 3 to denote a tower.

tower.ClassID = 3;

Platform Signatures

Sensors can detect platforms. For radar sensors, a relevant signature is the radar cross-section (RCS). By default a uniform RCS signature is used:

tower.Signatures{1}
ans = 

  rcsSignature with properties:

    EnablePolarization: 0
               Pattern: [2x2 double]
               Azimuth: [-180 180]
             Elevation: [2x1 double]
             Frequency: [0 1.0000e+20]
      FluctuationModel: Swerling0

Load predefined cylinder RCS data and use the data to define the RCS of the tower platform.

load('RCSCylinderExampleData.mat','cylinder');

tower.Signatures{1} = rcsSignature('Pattern', cylinder.RCSdBsm, ...
        'Azimuth', cylinder.Azimuth, 'Elevation', cylinder.Elevation, ...
        'Frequency', cylinder.Frequency);

Platform Dimensions

By default, platforms have no dimensions and are modeled as point targets. You may optionally specify the length, width, and height to denote the extent of the object, along with an offset of the body frame origin from its from its geometric center. You can specify platform dimensions using the Dimensions property.

You specify the dimensions of the tower like this:

tower.Dimensions = struct( ...
    'Length', 10, ...
    'Width', 10, ...
    'Height', 60, ...
    'OriginOffset', [0 0 30]);

The tower has a length, width, and height of 10, 10, and 60 meters. The origin offset of [0 0 30] indicates that its body frame origin (rotational center) is 30 meters in the positive z-direction of the platform's local body axis.

Platform Trajectories

You can obtain a platform's current position and orientation through its Position and Orientation properties. You can obtain more information about platforms using the scenario's platformPoses method:

scenario.platformPoses
ans = 

  struct with fields:

         PlatformID: 1
            ClassID: 3
           Position: [0 0 0]
           Velocity: [0 0 0]
       Acceleration: [0 0 0]
        Orientation: [1x1 quaternion]
    AngularVelocity: [0 0 0]

You can specify a platform's position and orientation over time using its Trajectory property. You can specify the trajectory of a platform using a kinematicTrajectory, waypoinTrajectory, or geoTrajectory object.

By default, a platform consists of a static kineticTrajectory that whose body axis is perfectly centered and aligned with the scenario axes:

tower.Trajectory
ans = 

  kinematicTrajectory with properties:

               SampleRate: 100
                 Position: [0 0 0]
              Orientation: [1x1 quaternion]
                 Velocity: [0 0 0]
       AccelerationSource: 'Input'
    AngularVelocitySource: 'Input'

To obtain a pitch angle of 4 degrees, you use the Orientation property of the trajectory object. Specify the orientation using a quaternion obtained from Euler angles.

tYaw = 0;
tPitch = 4;
tRoll = 0;
tower.Trajectory.Orientation = quaternion([tYaw tPitch tRoll],'eulerd','zyx','frame');

Axes Conventions

Most examples in Radar Toolbox, use a "North-East-Down" convention. This means that the x-axis points towards north, the y-axis points toward east, and the z-axis points downwards. Also, the x-, y-, and z- directions of the local body frame are the forward, right, and downward directions, respectively.

Visualizing a Scenario

The theaterPlot object provides an interface to plot objects dynamically in a 3-D scene. You may use standard MATLAB axes plotting methods to add or remove annotations to the theater plot's axes, which you can obtain via its Parent property.

Use a platformPlotter to plot platforms.

As expected, the tower is centered at the origin in NED coordinates with a pitch of 4 degrees.

tPlot = theaterPlot('XLim',[-50 50],'YLim',[-50 50],'ZLim',[-100 0]);
pPlotter = platformPlotter(tPlot,'DisplayName','tower');

pose = platformPoses(scenario);
towerPose = pose(1);
towerDims = tower.Dimensions;

plotPlatform(pPlotter, towerPose.Position, towerDims, towerPose.Orientation);
set(tPlot.Parent,'YDir','reverse', 'ZDir','reverse');
view(tPlot.Parent, -37.5, 30)

Adding Sensors to Platforms

To add a radar sensor to the platform, you can add a radarDataGenerator object on the platform by using its Sensors property.

Keep in mind that in a NED coordinate system, the "Z" direction points down. Therefore, if you want to mount a radar at the top of the tower, you should set its "z" position to -60 meters.

The radarDataGenerator has the option to report detections in scenario coordinates. Reporting detections in scenario coordinates makes it easier to compare the generated detections with the positions of the objects in the scenario.

towerRadar = radarDataGenerator('SensorIndex', 1, ...
    'UpdateRate', 10, ...
    'MountingLocation', [0 0 -60], ...
    'ScanMode', 'No scanning', ...
    'HasINS', true, ...
    'TargetReportFormat', 'Detections', ...
    'DetectionCoordinates', 'Scenario');

tower.Sensors = towerRadar;

Visualizing Coverage Areas

To see sensor coverages in a scenario, you use a coverage plotter and plot the coverage configuration of the scenario. You can widen the theater plot's range by adjusting the limits of its internal axes:

tPlot.XLimits = [-5000 5000];
tPlot.YLimits = [-5000 5000];
tPlot.ZLimits = [-1000 0];

covPlotter = coveragePlotter(tPlot,'DisplayName','Sensor Coverage');
plotCoverage(covPlotter, coverageConfig(scenario));

Platform Signatures

You can add other platforms in the scenario and adjust parameters that affect how other sensors observe the platforms. You can use an rcsSignature to model what the radar mounted on the tower would see.

The following code creates a helicopter and sets its radar cross section omnidirectionally to a value of 40 dBsm.

helicopter = platform(scenario);
helicopter.Dimensions = struct( ...
    'Length',30, ...
    'Width', .1, ...
    'Height', 7, ...
    'OriginOffset',[0 8 -3.2]);

helicopter.Signatures = rcsSignature( ...
    'Pattern',[40 40; 40 40], ...
    'Elevation',[-90; 90], ...
    'Azimuth',[-180 180]);

You can mount more than one sensor to any platform by placing the sensors in a cell array before assigning to the Sensors property.

helicopter.Sensors = { ...
    radarDataGenerator('SensorIndex', 2, ...
                       'UpdateRate', 20, ...
                       'MountingLocation', [22 0 0], ...
                       'MountingAngles', [-5 0 0], ...
                       'ScanMode', 'No scanning', ...
                       'HasINS', true, ...
                       'TargetReportFormat', 'Detections', ...
                       'DetectionCoordinates', 'Scenario'), ...
    radarDataGenerator('SensorIndex', 3, ...
                       'UpdateRate', 30, ...
                       'MountingLocation', [22 0 0], ...
                       'MountingAngles', [5 0 0], ...
                       'ScanMode', 'No scanning', ...
                       'HasINS', true, ...
                       'TargetReportFormat', 'Detections', ...
                       'DetectionCoordinates', 'Scenario')};

Platform Motion and Animation

You can arrange for the helicopter to cross the path of the radar beam. This shows how to make the helicopter follow a straight 100-meter path at a constant velocity with an elevation of 250 meters for seven seconds:

helicopter.Trajectory = waypointTrajectory([2000 50 -250; 2000 -50 -250],[0 7]);

Platform motion across time is performed by using a while-loop and calling the scenario's advance method.

You can plot all the platforms positions, orientations and dimensions in the loop:

profiles = platformProfiles(scenario);
dimensions = vertcat(profiles.Dimensions);

while advance(scenario)
    poses = platformPoses(scenario);
    positions = vertcat(poses.Position);
    orientations = vertcat(poses.Orientation);
    plotPlatform(pPlotter, positions, dimensions, orientations);
    plotCoverage(covPlotter, coverageConfig(scenario));
    % to animate more slowly uncomment the following line
    % pause(0.01)
end

Detecting platforms

In the example above, you added three radars with different update rates: the tower has an update rate of 10 Hz, and the helicopter had two radars with update rates of 20 Hz and 30 Hz, respectively.

The scenario can be placed into a mode in which the call to advance updates the time of simulation as needed to update each of the sensors it contains. You can achieve this by setting the UpdateRate of the scenario to zero.

scenario.UpdateRate = 0;

To show the simulation time, add a UI control to the figure.

fig = ancestor(tPlot.Parent,'Figure');
timeReadout = uicontrol(fig,'Style','text','HorizontalAlignment','left','Position',[0 0 200 20]);
timeReadout.String = "SimulationTime: " + scenario.SimulationTime;

Now the proper sensor times can be reached. You can use detect to get the detections available by each sensor within the loop. Detections can be shown by constructing a detectionPlotter object.

dPlotter = detectionPlotter(tPlot,'DisplayName','detections');

You can run the same simulation again by restarting it and modifying it to report detections.

The detected positions for a radarDataGenerator can be extracted from the detection Measurement field:

restart(scenario);

while advance(scenario)
    timeReadout.String = "SimulationTime: " + scenario.SimulationTime;
    detections = detect(scenario);

    % extract column vector of measurement positions
    allDetections = [detections{:}];
    if ~isempty(allDetections)
        measurement = cat(2,allDetections.Measurement)';
    else
        measurement = zeros(0,3);
    end

    plotDetection(dPlotter, measurement);

    poses = platformPoses(scenario);
    positions = vertcat(poses.Position);
    orientations = vertcat(poses.Orientation);
    plotPlatform(pPlotter, positions, dimensions, orientations);
    plotCoverage(covPlotter, coverageConfig(scenario));
end

Notice that the update time of simulation increments non-uniformly to the times required by each of the sensors.

Summary

In this example, you learned how to construct and visualize a simple scenario and obtain detections generated by a radar data generator.