MATLAB Examples

# Search and Track Scheduling for Multifunction Phased Array Radar

This example requires Phased Array System Toolbox™.

## Contents

Assume the multifunction radar operates at S band and must detect targets with a radar cross section (RCS) of 1 that are between 2 km and 100 km.

```fc = 2e9; % Radar carrier frequency (Hz) c = 3e8; % Propagation speed (m/s) lambda = c/fc; % Radar wavelength (m) maxrng = 100e3; % Maximum range (m) minrng = 2e3; % Minimum range (m) ```

Waveform

To satisfy the range requirement, a linear FM waveform with a 1 MHz bandwidth is used. The waveform can be defined as

```bw = 1e6; fs = 1.5*bw; prf = 1/range2time(maxrng,c); dcycle = 0.1; wav = phased.LinearFMWaveform('SampleRate', fs, ... 'DurationSpecification', 'Duty cycle', 'DutyCycle', dcycle, ... 'PRF', prf, 'SweepBandwidth', bw); ```

The range resolution achievable by the waveform is

```rngres = bw2range(bw,c) ```
```rngres = 150 ```

The multifunction radar is equipped with a phased array that can electronically scan the radar beams in space. Use a 50-by-50 rectangular array with elements separated by half wavelength to achieve a half power beam width of approximately 2 degrees.

```arraysz = 50; ant = phased.URA('Size',arraysz,'ElementSpacing',lambda/2); ant.Element.BackBaffled = true; arraystv = phased.SteeringVector('SensorArray',ant,'PropagationSpeed',c); radiator = phased.Radiator('OperatingFrequency',fc, ... 'PropagationSpeed', c, 'Sensor',ant, 'WeightsInputPort', true); collector = phased.Collector('OperatingFrequency',fc, ... 'PropagationSpeed', c, 'Sensor',ant); beamw = rad2deg(lambda/(arraysz*lambda/2)) ```
```beamw = 2.2918 ```

The detection requirements are used to derive the appropriate transmit power. Assume the noise figure on the receiving preamplifier is 7 dB.

```pd = 0.9; % Probability of detection pfa = 1e-6; % Probability of false alarm snr_min = albersheim(pd, pfa, 1); ampgain = 20; tgtrcs = 1; ant_snrgain = pow2db(arraysz^2); ppower = radareqpow(lambda,maxrng,snr_min,wav.PulseWidth,... 'RCS',tgtrcs,'Gain',ampgain+ant_snrgain); tx = phased.Transmitter('PeakPower',ppower,'Gain',ampgain,'InUseOutputPort',true); rx = phased.ReceiverPreamp('Gain',ampgain,'NoiseFigure',7,'EnableInputPort',true); ```

Signal Processing

The multifunction radar applies a sequence of operations to the received signal, including matched filtering, time varying gain, monopulse, and detection to generate range and angle measurements of the detected targets.

```% matched filter mfcoeff = getMatchedFilter(wav); mf = phased.MatchedFilter('Coefficients',mfcoeff,'GainOutputPort', true); % time varying gain tgrid = unigrid(0,1/fs,1/prf,'[)'); rgates = c*tgrid/2; rngloss = 2*fspl(rgates,lambda); refloss = 2*fspl(maxrng,lambda); tvg = phased.TimeVaryingGain('RangeLoss',rngloss,'ReferenceLoss',refloss); % monopulse monfeed = phased.MonopulseFeed('SensorArray',ant,'PropagationSpeed',c,... 'OperatingFrequency',fc,'SquintAngle',1); monest = getMonopulseEstimator(monfeed); ```

Data processing

The detections are fed into a tracker which performs several operations. The tracker maintains a list of tracks, or estimates of targets states in the area of interest. If a detection cannot be assigned to any track already maintained by the tracker, the tracker initiates a new track. In most cases, it is unclear whether the new track represents a true target or a false one. At first, a track is created with tentative status. If enough evidence is obtained, the track becomes confirmed. Similarly, if no detections are assigned to a track, the track is coasted (predicted without correction), but after a few missed updates, the tracker deletes the track.

The multifunction radar uses a tracker that associates the detections to the tracks using a global nearest neighbor (GNN) algorithm.

```tracker = trackerGNN('FilterInitializationFcn',@initMPARGNN,... 'ConfirmationThreshold',[2 3], 'DeletionThreshold',5,... 'HasDetectableTrackIDsInput',true,'AssignmentThreshold',100,... 'MaxNumTracks',2,'MaxNumSensors',1); ```

Group all radar components together in a struct for easier reference the simulation loop.

```mfradar.Tx = tx; mfradar.Rx = rx; mfradar.TxAnt = radiator; mfradar.RxAnt = collector; mfradar.Wav = wav; mfradar.RxFeed = monfeed; mfradar.MF = mf; mfradar.TVG = tvg; mfradar.DOA = monest; mfradar.STV = arraystv; mfradar.Tracker = tracker; mfradar.IsTrackerInitialized = false; ```

## Target and Scene Definition

This example assumes the radar is stationary at the origin with two targets in its field of view. One target is departing from the radar and is at a range of around 50 km. The other target is approaching and is 30 km away. Both of them have an RCS of 1 .

```% Define the targets. tgtpos = [29875 49637; 0 4225; 0 0]; tgtvel = [-100 120; 0 100; 0 0]; ntgt = size(tgtpos,2); tgtmotion = phased.Platform('InitialPosition',tgtpos,'Velocity',tgtvel); target = phased.RadarTarget('MeanRCS',tgtrcs*ones(1,ntgt),'OperatingFrequency',fc); ```

The propagation environment is assumed to be free space.

```channel = phased.FreeSpace('SampleRate',fs,'TwoWayPropagation',true,'OperatingFrequency',fc); ```

Group targets and propagation channels together in a struct for easier reference in the simulation loop.

```env.Target = target; env.TargetMotion = tgtmotion; env.Channel = channel; ```

The advantage of using one multifunction radar to perform multiple tasks comes at a price of higher cost and more sophisticated logic. In general, a radar has finite resources to spend on its tasks. If resources are used for tracking tasks, then those resources are not available for searching tasks until the tracking tasks are finished. Hence, a critical component in a multifunction radar is resource management.

The search tasks can be considered as deterministic. In this example, a raster scan is used to cover the desired airspace. If no other tasks exist, the radar will scan the space one angular cell a time. The size of an angular cell is determined by the beam width of the antenna array.

Assume that we will cover a space between -30 to 30 degrees azimuth and 0 to 20 degrees elevation. The angular search grid can be computed using the beam width as

```scanregion = [-30, 30, 0, 20]; azscanspan = diff(scanregion(1:2)); numazscan = ceil(azscanspan/beamw); azscanangles = linspace(scanregion(1),scanregion(2),numazscan); elscanspan = diff(scanregion(3:4)); numelscan = ceil(elscanspan/beamw); elscanangles = linspace(scanregion(3),scanregion(4),numelscan); [elscangrid,azscangrid] = meshgrid(elscanangles,azscanangles); scanangles = [azscangrid(:) elscangrid(:)].'; ```

The beam position grid and target scene are shown below.

```sceneplot = helperMPARTaskPlot('initialize',scanangles,azscanangles,maxrng,beamw,tgtpos); ```

All these search beams will be transmitted one by one sequentially until the entire search area is covered, at which time it will repeat. The searches are performed along the azimuthal direction one elevation angle a time. The search tasks are often contained in a job queue

```searchq = struct('JobType','Search','BeamDirection',num2cell(scanangles,1),... 'Priority',1000,'WaveformIndex',1); current_search_idx = 1; ```

Each job in the queue specifies the job type as well as the pointing direction of the beam. It also contains a priority value for the job. This priority value is determined by the job type but number wise is arbitrary. This example uses a value of 1000 as the priority for search jobs.

```disp(searchq(current_search_idx)) ```
``` JobType: 'Search' BeamDirection: [2x1 double] Priority: 1000 WaveformIndex: 1 ```

Unlike search tasks, track tasks cannot be planned. They are created only when a target is detected by a search task or when the target has already been tracked. Therefore, track tasks are dynamic tasks that get created and executed based on the changing scenario. Tracking tasks are also managed in a job queue.

```trackq(10) = struct('JobType',[],'BeamDirection',[],'Priority',3000,'WaveformIndex',[],... 'Time',[],'Range',[],'TrackID',[]); num_trackq_items = 0; disp(trackq(1)) ```
``` JobType: [] BeamDirection: [] Priority: [] WaveformIndex: [] Time: [] Range: [] TrackID: [] ```

Group search and track queues together in a struct for easier reference in the simulation loop.

```jobq.SearchQueue = searchq; jobq.SearchIndex = current_search_idx; jobq.TrackQueue = trackq; jobq.NumTrackJobs = num_trackq_items; ```

Because a tracking job cannot be initialized beforehand, all jobs start as empty jobs for the time being. Once a job is created, it will contain the information such as job type, the direction of the beam, and when to execute. Note that the tracking task has a priority of 3000, which is higher than the priority of 1000 for a search job. This means that when the time is in conflict, the system will execute the tracking job first.

Normally there is a size limit for the queue and in this example it is set to 10.

In this example, for simplicity, the multifunction radar executes only one type of job within a small time period, often referred to as a dwell, but can switch tasks at the beginning of each dwell. Hence, for each dwell, the radar looks at all tasks that are due for execution and picks the one that has the highest priority. Consequently, jobs that get postponed will now have an increased priority and are more likely to be executed in the next dwell.

## Simulation

This section simulates a short run of the multifunction radar system. The entire structure of the multifunction radar simulation can be represented by the following diagram.

The simulation starts with the radar manager, which provides an initial job. Based on this job, the radar transmits the waveform, simulates the echo, and applies signal processing to generate the detection. The detection is processed by a tracker to create tracks for targets. Then the track goes back to the radar manager. Based on the track and the knowledge about the scene, the radar manager schedules the track job and picks the job for the next dwell.

The logic of radar manager operation can be summarized as follows:

1. The radar starts with a search job.
2. If there is a target present in the detection, the radar will schedule a confirmation job in the same direction to ensure that this is not a false alarm. The confirmation task has a higher priority than the search task but not as high as the track task. If the detection gets confirmed, a track is established and a track job is created to be executed after a given revisit time. If the detection is not confirmed, then the original detection is considered as a false alarm and no track is created.
3. For a track job, the radar performs the detection, updates the track and creates a future track job.
4. Based on the priority and time for execution, the radar selects the next job.

Assume a dwell is 10 ms. At the beginning of the simulation, the radar is configured to perform search one beam a time. The following code snippet would run until a detection is found.

```rng(2018); current_time = 0; Npulses = 10; numdwells = 200; dwelltime = 0.01; jobload.num_search_job = zeros(1,numdwells); jobload.num_track_job = zeros(1,numdwells); ```

You can run the example in its entirety to see the plots being dynamically updated during execution. In the top two plots, the color of the beams indicate the types of the current job: red for search, yellow for confirm, and purple for track. The bottom two plots show the true locations (triangle), detections (circle), and tracks (square) of the two targets, respectively. System log is also displayed in the command line to help you understand the system behavior at current moment. Next, we explain details at several critical moments.

The code snippet below simulates the system behavior until it detects the first target. You can see that the simulation loop follows aforementioned system diagram.

```for dwell_idx = 1:14 % Scheduler to provide current job [current_job,jobq] = getCurrentJob(jobq,current_time); % Simulate the received I/Q signal [xsum,xdaz,xdel,mfradar] = generateEcho(mfradar,env,current_job); % Signal processor to extract detection [detection,mfradar] = generateDetection(xsum,xdaz,xdel,mfradar,current_job,current_time); % Radar manager to perform data processing and update track queue [jobq,allTracks,mfradar] = updateTrackAndJob(detection,jobq,mfradar,current_job,current_time,dwelltime); % Visualization helperMPARTaskPlot('update',sceneplot,current_job,maxrng,beamw,tgtpos,allTracks,detection.Measurement); % Update time tgtpos = env.TargetMotion(dwelltime-Npulses/mfradar.Wav.PRF); current_time = current_time+dwelltime; % Record resource allocation if strcmp(current_job.JobType,'Search') jobload.num_search_job(dwell_idx) = 1; else jobload.num_track_job(dwell_idx) = 1; end end ```
```0.000000 sec: Search [-30.000000 0.000000] 0.010000 sec: Search [-27.692308 0.000000] 0.020000 sec: Search [-25.384615 0.000000] 0.030000 sec: Search [-23.076923 0.000000] 0.040000 sec: Search [-20.769231 0.000000] 0.050000 sec: Search [-18.461538 0.000000] 0.060000 sec: Search [-16.153846 0.000000] 0.070000 sec: Search [-13.846154 0.000000] 0.080000 sec: Search [-11.538462 0.000000] 0.090000 sec: Search [-9.230769 0.000000] 0.100000 sec: Search [-6.923077 0.000000] 0.110000 sec: Search [-4.615385 0.000000] 0.120000 sec: Search [-2.307692 0.000000] 0.130000 sec: Search [0.000000 0.000000] Target detected at 29900.000000 m```

As expected, the radar gets a detection when the radar beam illuminates the target, as shown in the figure. When this happens, the radar sends a confirmation beam immediately to make sure it is not a false detection.

Next section shows the result for the confirmation job. To simplify the code, we have combined the simulation loop into a system simulation function.

```[mfradar,env,jobq,jobload,current_time,tgtpos] = MPARSimRun(... mfradar,env,jobq,jobload,current_time,dwelltime,sceneplot,maxrng,beamw,tgtpos,15,15); ```
`0.140000 sec: Confirm [-0.000586 -0.000034] Created track 1 at 29900.000000 m`

Note that the figure now shows the confirmation beam. Once the detection is confirmed, a track is established for the target and a track job is scheduled to execute after a short interval.

This process repeats for every detected target until the revisit time, at which point the multifunction radar stops the search sequence and performs the track task again.

```[mfradar,env,jobq,jobload,current_time,tgtpos] = MPARSimRun(... mfradar,env,jobq,jobload,current_time,dwelltime,sceneplot,maxrng,beamw,tgtpos,16,25); ```
```0.150000 sec: Search [2.307692 0.000000] 0.160000 sec: Search [4.615385 0.000000] Target detected at 49900.000000 m 0.170000 sec: Confirm [4.881676 0.000739] Created track 2 at 49900.000000 m 0.180000 sec: Search [6.923077 0.000000] 0.190000 sec: Search [9.230769 0.000000] 0.200000 sec: Search [11.538462 0.000000] 0.210000 sec: Search [13.846154 0.000000] 0.220000 sec: Search [16.153846 0.000000] 0.230000 sec: Search [18.461538 0.000000] 0.240000 sec: Track [-0.000399 0.000162] Track 1 at 29900.000000 m```

Here the simulation stops at a track beam. The zoomed-in figures around the two targets show how the tracks are updated based on the detection and measurements. Note that a track job for the next revisit is also added to the job queue during the execution of a track job.

Such process repeats for each dwell. The simulation below runs the radar system for a 2-second period. Note that after a while, the second target is detected beyond 50 km. Based on this information, the radar manager reduces how often the system needs to track the second target so that more resources can be freed up for other more urgent needs.

```[mfradar,env,jobq,jobload,current_time,tgtpos] = MPARSimRun(... mfradar,env,jobq,jobload,current_time,dwelltime,sceneplot,maxrng,beamw,tgtpos,26,numdwells); ```
```0.250000 sec: Search [20.769231 0.000000] 0.260000 sec: Search [23.076923 0.000000] 0.270000 sec: Track [4.882892 -0.000030] Track 2 at 49900.000000 m 0.280000 sec: Search [25.384615 0.000000] 0.290000 sec: Search [27.692308 0.000000] 0.340000 sec: Track [0.001390 0.000795] Track 1 at 29900.000000 m 0.370000 sec: Track [4.895153 0.000529] Track 2 at 49900.000000 m 0.440000 sec: Track [0.000284 0.000446] Track 1 at 29900.000000 m 0.470000 sec: Track [4.909764 -0.000394] Track 2 at 49900.000000 m 0.540000 sec: Track [0.000455 -0.000130] Track 1 at 29800.000000 m 0.570000 sec: Track [4.921876 -0.000210] Track 2 at 49900.000000 m 0.640000 sec: Track [0.000181 -0.000020] Track 1 at 29800.000000 m 0.670000 sec: Track [4.932942 -0.000988] Track 2 at 49900.000000 m 0.740000 sec: Track [0.000348 0.000212] Track 1 at 29800.000000 m 0.770000 sec: Track [4.944255 -0.001073] Track 2 at 49900.000000 m 0.840000 sec: Track [0.000171 -0.000125] Track 1 at 29800.000000 m 0.870000 sec: Track [4.954431 -0.000943] Track 2 at 50000.000000 m 0.940000 sec: Track [0.000296 -0.000288] Track 1 at 29800.000000 m 1.040000 sec: Track [0.000108 -0.000147] Track 1 at 29800.000000 m 1.140000 sec: Track [-0.000096 -0.000179] Track 1 at 29800.000000 m 1.240000 sec: Track [-0.000110 -0.000315] Track 1 at 29800.000000 m 1.340000 sec: Track [-0.000291 -0.000515] Track 1 at 29800.000000 m 1.370000 sec: Track [5.005679 -0.000877] Track 2 at 50000.000000 m 1.440000 sec: Track [-0.000191 -0.000592] Track 1 at 29800.000000 m 1.540000 sec: Track [-0.000140 -0.000787] Track 1 at 29700.000000 m 1.640000 sec: Track [0.000069 -0.000600] Track 1 at 29700.000000 m 1.740000 sec: Track [-0.000001 -0.000714] Track 1 at 29700.000000 m 1.840000 sec: Track [0.000030 -0.000686] Track 1 at 29700.000000 m 1.870000 sec: Track [5.057762 0.000107] Track 2 at 50100.000000 m 1.940000 sec: Track [0.000067 -0.000511] Track 1 at 29700.000000 m```

## Resource Distribution Analysis

It is often of interest to see how the radar resource is distributed among different tasks. The following figure shows how the multifunction radar system in this example assigns its resources between search and track.

```L = 10; searchpercent = sum(buffer(jobload.num_search_job,L,L-1,'nodelay'))/L; trackpercent = sum(buffer(jobload.num_track_job,L,L-1,'nodelay'))/L; figure; plot((1:numel(searchpercent))*L*dwelltime,[searchpercent(:) trackpercent(:)]); xlabel('Time (s)'); ylabel('Job Percentage'); title('Resource Distribution between Search and Track'); legend('Search','Track','Location','best'); grid on; ```

The figure suggests that at the beginning of the simulation, all resources are spent on search. Once the targets are detected, the radar resources get split 80% to 20% between search and track, respectively. However, once the second target gets farther away, more resources are freed up for search. The track load increases briefly when the time arrives to track the second target again.

## Summary

This example introduces the concept of resource management and task scheduling in a multifunctional phased array radar system. It is shown that with the resource management component, the radar acts as a closed loop system. Although the multifunction radar in this example only deals with search and track tasks, the concept can be extended to more realistic situations where other functions, such as self-check and communication, are also involved.

## References

[1] Walter Weinstock, Computer Control of a Multifunction Radar, Lecture 11 and 12 in Eli Brookner, Practical Phased Array Antenna Systems, Lex Book, 1997

## Appendices

Below are several helper functions that model the radar resource management workflow.

getCurrentJob

The function getCurrentJob compares the jobs in the search queue and the track queue and selects the job with the highest priority to execute.

```function [currentjob,jobq] = getCurrentJob(jobq,current_time)

searchq   = jobq.SearchQueue;
trackq    = jobq.TrackQueue;
searchidx = jobq.SearchIndex;
num_trackq_items = jobq.NumTrackJobs;

% update search queue index
searchqidx = mod(searchidx-1,numel(searchq))+1;

% find the track job that is due and has the highest priority

% if the track job found has a higher priority, use that as the current job
% and increase the next search job priority since it gets postponed.
% otherwise, the next search job due is the current job.
trackq(m-1) = trackq(m);
end
num_trackq_items = num_trackq_items-1;
searchq(searchqidx).Priority = searchq(searchqidx).Priority+100;
else
currentjob = searchq(searchqidx);
searchidx = searchqidx+1;

end

jobq.SearchQueue  = searchq;
jobq.SearchIndex  = searchidx;
jobq.TrackQueue   = trackq;
jobq.NumTrackJobs = num_trackq_items;

```

generateEcho

The function generateEcho simulates the complex (I/Q) baseband representation of the target echo received at the radar.

```function [xrsint,xrdazint,xrdelint,mfradar] = generateEcho(mfradar,env,current_job)

% Number of pulses and operating frequency
Npulses = 10;

for m = 1:Npulses
% Waveform

% Update target motion
[~,tgtang]      = rangeangle(tgtpos);

% Transmit

% Propagation
xp = env.Target(xp);

% Pulse integration
if m == 1
else
end
end

```

generateDetection

The function generateDetection applies the signal processing techniques on the echo to generate target detection.

```function [detection,mfradar] = generateDetection(xrsint,xrdazint,xrdelint,mfradar,current_job,current_time)

% compute detection threshold
pfa         = 1e-6;
threshold   = npower * db2pow(npwgnthresh(pfa,1,'noncoherent'));
ant_snrgain = pow2db(arraysz^2);
mfgain      = pow2db(norm(mfcoeff)^2);
threshold   = threshold * db2pow(mfgain+2*ant_snrgain);
threshold   = sqrt(threshold);

% matched filtering and time varying gain

% detection in range and angle estimation via monopulse
if any(abs(xrsmf)>threshold)
[~,tgtidx] = findpeaks(abs(xrsmf),'MinPeakHeight',threshold,...
'Sortstr','Descend','NPeaks',1);
rng_est = rgates(tgtidx-(numel(mfcoeff)-1));
% Form the detection object.
measNoise = diag([0.1, 0.1, 150].^2);           % Measurement noise matrix
detection = objectDetection(current_time,...
[ang_est(1);ang_est(2);rng_est], 'MeasurementNoise', measNoise,...
'MeasurementParameters',struct('Frame','spherical', 'HasVelocity', false));
else
detection = objectDetection.empty;
end

if current_time < 0.3 || strcmp(current_job.JobType,'Track')
fprintf('\n%f sec:\t%s\t[%f %f]',current_time,current_job.JobType,current_job.BeamDirection(1),...
current_job.BeamDirection(2));
end

```

updateTrackAndJob

The function updateTrackAndJob performs the tracking on the detection and then pass tracks to the radar manager to update the track task queue.

```function [jobq,allTracks,mfradar] = updateTrackAndJob(detection,jobq,mfradar,current_job,current_time,dwellinterval)

trackq           = jobq.TrackQueue;
num_trackq_items = jobq.NumTrackJobs;

% execute current job
switch current_job.JobType
case 'Search'
% For search job, if there is a detection, establish tentative
% track and schedule a confirmation job
if ~isempty(detection)
ang_est = detection.Measurement(1:2);
rng_est = detection.Measurement(3);
else
end
num_trackq_items = num_trackq_items+1;
trackq(num_trackq_items) = struct('JobType','Confirm','Priority',2000,...
'BeamDirection',ang_est,'WaveformIndex',1,'Time',current_time+dwellinterval,...
'Range',rng_est,'TrackID',allTracks(~[allTracks.IsConfirmed]).TrackID);
if current_time < 0.3 || strcmp(current_job.JobType,'Track')
fprintf('\tTarget detected at %f m',rng_est);
end
else
allTracks = [];
end

case 'Confirm'
% For confirm job, if the detection is confirmed, establish a track
% and create a track job corresponding to the revisit time
if ~isempty(detection)
trackid = current_job.TrackID;

rng_est = detection.Measurement(3);
if rng_est >= 50e3
updateinterval = 0.5;
else
updateinterval = 0.1;
end
revisit_time = current_time+updateinterval;
xpred = predictedTrack.State([1 3 5]);
[phipred,thetapred,rpred] = cart2sph(xpred(1),xpred(2),xpred(3));
num_trackq_items = num_trackq_items+1;
trackq(num_trackq_items) = struct('JobType','Track','Priority',3000,...
'Range',rpred,'TrackID',trackid);
if current_time < 0.3 || strcmp(current_job.JobType,'Track')
fprintf('\tCreated track %d at %f m',trackid,rng_est);
end
else
allTracks = [];
end

case 'Track'
% For track job, if there is a detection, update the track and
% schedule a track job corresponding to the revisit time. If there
% is no detection, predict and schedule a track job sooner so the
% target is not lost.
if ~isempty(detection)
trackid = current_job.TrackID;

rng_est = detection.Measurement(3);
if rng_est >= 50e3
updateinterval = 0.5;
else
updateinterval = 0.1;
end

revisit_time = current_time+updateinterval;
xpred = predictedTrack.State([1 3 5]);
[phipred,thetapred,rpred] = cart2sph(xpred(1),xpred(2),xpred(3));
num_trackq_items = num_trackq_items+1;
trackq(num_trackq_items) = struct('JobType','Track','Priority',3000,...
'Range',rpred,'TrackID',trackid);

if current_time < 0.3 || strcmp(current_job.JobType,'Track')
fprintf('\tTrack %d at %f m',trackid,rng_est);
end
else
trackid = current_job.TrackID;

updateinterval = 0.1;  % revisit sooner
revisit_time = current_time+updateinterval;
xpred = predictedTrack.State([1 3 5]);

[phipred,thetapred,rpred] = cart2sph(xpred(1),xpred(2),xpred(3));
num_trackq_items = num_trackq_items+1;
trackq(num_trackq_items) = struct('JobType','Track','Priority',3000,...