Адаптивное отслеживание маневрирующих целей с помощью управляемого радара

Этот пример показывает, как использовать управление радиолокационными ресурсами для эффективного отслеживания нескольких маневрирующих целей. Отслеживание маневрирующих целей требует, чтобы радар пересматривал цели чаще, чем отслеживание не маневрирующих целей. Фильтр «Взаимодействие нескольких моделей» (IMM) оценивает, когда цель маневрирует. Эта оценка помогает управлять временем пересмотра радара и, следовательно, улучшает отслеживание. Этот пример использует Radar Toolbox™ для модели радара и Sensor Fusion and Tracking Toolbox™ для отслеживания.

Введение

Многофункциональные радары могут искать цели, подтверждать новые треки и пересматривать треки, чтобы обновить состояние. Для выполнения этих функций многофункциональным радаром часто управляет менеджер ресурсов, который создает радиолокационные задачи для поиска, подтверждения и отслеживания. Эти задачи планируются в соответствии с приоритетом и временем, так что на каждом временном шаге многофункциональный радар может указывать свой луч в желаемом направлении. Пример Search and Track Scheduling for Multifunction Phased Array Radar показывает многофункциональный радар с фазированной решеткой, управляемый менеджером ресурсов.

В этом примере мы распространяем пример Адаптивного слежения за маневрирующими целями с управляемым радаром на случай нескольких маневрирующих целей. Существует два противоречивых требования к радару, используемому для отслеживания маневрирующих целей:

  1. Количество целей и их начальное местоположение обычно не известны заранее. Поэтому радар должен постоянно искать интересующую область, чтобы найти цели. Кроме того, радар должен обнаружить и установить дорожку на каждой цели, как только он войдет в зону покрытия радара.

  2. Период времени маневрирования цели неизвестен в усовершенствование. Если известно, что цели не маневрируют, радар может пересматривать цели нечасто. Однако, поскольку начало и конец маневра неизвестно, радар должен пересматривать каждую дорожку достаточно часто, чтобы иметь возможность распознавать, когда маневр начинается и заканчивается.

Радар должен сбалансироваться между обеспечением достаточного количества лучей с центром на сопровождаемых целях и оставлением достаточного времени для поиска новых целей. Один из подходов состоит в том, чтобы просто определить скорость повторного просмотра для каждой отслеживаемой цели независимо от ее статуса маневрирования и оставить оставшееся время для поиска новой цели. Эта схема управления радаром иногда упоминается как Активное Отслеживание [1]. Когда больше целей становится отслеживаемым, радар может или выполнять меньше задач поиска или он может отслеживать каждую цель реже. Очевидно, что если количество целей велико, радар может быть перегружен.

Активное отслеживание обрабатывает все дорожки одинаково, что делает его основанным на режиме алгоритмом управления ресурсами. Более сложный способ управления радаром основан на свойствах каждой дорожки. Например, используйте свойства дорожки, такие как размер ковариации неопределенности состояния, маневрирует ли дорожка и насколько быстро она движется к активу, который защищает сайт радара. Когда такие свойства используются, управление ресурсами радара упоминается как Адаптивное Отслеживание [1].

В этом примере вы сравниваете результаты Active Tracking и Adaptive Tracking, когда радар адаптируется на основе предполагаемого маневра пути.

Задайте сценарий и радиолокационную модель

Вы задаете сценарий и радар со частотой обновления 20 Гц, что означает, что радар имеет 20 лучей в секунду, выделенных для поиска, подтверждения или отслеживания. Вы загружаете траектории бенчмарка, используемые в примере Benchmark Trajectories for Multi-Object Tracking (Sensor Fusion and Tracking Toolbox). Существует шесть траекторий бенчмарка, и вы задаете траекторию для каждой. Из рисунка ниже шесть платформ следуют неманеврирующим ногам, перемежающимся маневрирующими ногами. Траектории можно просмотреть на рисунке ниже.

% Create scenario
updateRate = 20;
scenario = trackingScenario('UpdateRate',updateRate);
% Add the benchmark trajectories
load('BenchmarkTrajectories.mat','-mat');
platform(scenario,'Trajectory',v1Trajectory);
platform(scenario,'Trajectory',v2Trajectory);
platform(scenario,'Trajectory',v3Trajectory);
platform(scenario,'Trajectory',v4Trajectory);
platform(scenario,'Trajectory',v5Trajectory);
platform(scenario,'Trajectory',v6Trajectory);

% Create visualization
f = figure;
mp = uipanel('Parent',f,'Title','Theater Plot','FontSize',12,...
    'BackgroundColor','white','Position',[.01 .25 .98 .73]);
tax = axes(mp,'ZDir','reverse');

% Visualize scenario
thp = theaterPlot('Parent',tax,'AxesUnits',["km","km","km"],'XLimits',[0 85000],'YLimits',[-45000 70000],'ZLimits',[-10000 1000]);
plp = platformPlotter(thp, 'DisplayName', 'Platforms');
pap = trajectoryPlotter(thp, 'DisplayName', 'Trajectories', 'LineWidth', 1);
dtp = detectionPlotter(thp, 'DisplayName', 'Detections');
cvp = coveragePlotter(thp, 'DisplayName', 'Radar Coverage');
trp = trackPlotter(thp, 'DisplayName', 'Tracks', 'ConnectHistory', 'on', 'ColorizeHistory', 'on');
numPlatforms = numel(scenario.Platforms);
trajectoryPositions = cell(1,numPlatforms);
for i = 1:numPlatforms
    trajectoryPositions{i} = lookupPose(scenario.Platforms{i}.Trajectory,(0:0.1:185));
end
plotTrajectory(pap, trajectoryPositions);
view(tax,3)

Вы задаете радар используя helperManagedRadarDataGenerator объект, который является объектом, унаследованным от radarDataGenerator Системный объект. The helperManagedRadarDataGenerator обеспечивает вероятностную радиолокационную модель и позволяет указывать радар на определенное направление. Эта настройка позволяет менеджеру ресурсов радара планировать радар для поиска, подтверждения и сопровождения целей. Радар моделирует электронные лучи, которые имеют пределы сканирования азимута [-90 60] степеней и пределы повышения [-9,9 0] степеней. Отрицательные углы возвышения означают, что радар указывает луч от горизонта вверх. Установите радар на новой платформе в сценарии.

radar = helperManagedRadarDataGenerator(1, ...
    'ScanMode', 'Electronic', ...
    'UpdateRate', updateRate, ...
    'MountingLocation', [0 0 -15], ...
    'FieldOfView', [3;10], ...
    'AzimuthResolution', 1.5, ...
    'ElectronicAzimuthLimits', [-90 60], ...
    'ElectronicElevationLimits', [-9.9 0], ...
    'HasElevation', true, ...
    'DetectionCoordinates', 'Sensor spherical');
platform(scenario,'Position',[0 0 0],'Sensors',radar);

Задайте трекер

После того, как радар обнаруживает объекты, он подает обнаружения на трекер, который выполняет несколько операций. Трекер поддерживает список треков, которые являются оценками целевых состояний в интересующей области. Если обнаружение не может быть назначено ни одному треку, уже поддерживаемому трекером, трекер инициирует новый трек. В большинстве случаев неясно, представляет ли новая дорожка истинную цель или ложную цель. Сначала создается трек с предварительным статусом. Если получено достаточное количество обнаружений, дорожка становится подтвержденной. Точно так же, если никакие обнаружения не назначены дорожке, дорожка покрывается (прогнозируется без коррекции). Если у трека несколько пропущенных обновлений, трекер удаляет трек.

В этом примере вы используете трекер, который связывает обнаружения с треками с помощью глобального алгоритма ближайшего соседа (GNN). Чтобы отслеживать маневрирующие цели, вы задаете FilterInitializationFcn который инициализирует фильтр Interacting Multiple Model (IMM). The initMPARIMM функция использует две модели движения: модель постоянной скорости и модель постоянной скорости поворота. The trackingIMM (Sensor Fusion and Tracking Toolbox) фильтр отвечает за оценку вероятности каждой модели, к которой можно получить доступ из ее ModelProbabilities свойство. В этом примере вы классифицируете цель как маневрирующую, когда вероятность модели скорости постоянного поворота выше 0,6.

tracker = trackerGNN('FilterInitializationFcn',@initMPARIMM,...
    'ConfirmationThreshold',[2 3], 'DeletionThreshold',[5 5],...
    'HasDetectableTrackIDsInput',true,'AssignmentThreshold',150,...
    'MaxNumTracks',10,'MaxNumSensors',1);
posSelector = [1 0 0 0 0 0; 0 0 1 0 0 0; 0 0 0 0 1 0];

Управление радиолокационными ресурсами

В этом разделе только кратко описывается управление ресурсами радара. Для получения дополнительной информации смотрите пример Адаптивного Отслеживания Маневрирующих Целей с Управляемым Радаром.

Задачи поиска

Задачи поиска присваиваются определенно в этом примере. Растровый скан используется, чтобы закрыть желаемое воздушное пространство. Если других задач не существует, радар сканирует пространство по одной угловой камере за раз. Размер угловой камеры определяется радаром FieldOfView свойство и ограничено радаром ElectronicAzimuthLimits и ElectronicElevatonLimits свойства.

azscanspan   = diff(radar.ElectronicAzimuthLimits);
numazscan    = floor(azscanspan/radar.FieldOfView(1))+1;
azscanangles = linspace(radar.ElectronicAzimuthLimits(1),radar.ElectronicAzimuthLimits(2),numazscan)+radar.MountingAngles(1);
elscanspan   = diff(radar.ElectronicElevationLimits);
numelscan    = floor(elscanspan/radar.FieldOfView(2))+1;
elscanangles = linspace(radar.ElectronicElevationLimits(1),radar.ElectronicElevationLimits(2),numelscan)+radar.MountingAngles(2);
[elscangrid,azscangrid] = meshgrid(elscanangles,azscanangles);  
scanangles   = [azscangrid(:) elscangrid(:)].';
searchq = struct('JobType','Search','BeamDirection',num2cell(scanangles,1),...
    'Priority',1000,'WaveformIndex',1);
current_search_idx = 1;

Отслеживайте задачи

В отличие от задач поиска, отслеживать задачи нельзя заранее планировать. Вместо этого менеджер ресурсов создает задачи подтверждения и отслеживания на основе изменяющегося сценария. Основным различием этого примера от примера Адаптивного слежения за маневрирующими целями с управляемым радаром является то, что JobType для каждой задачи трекинга может быть либо "TrackNonManeuvering" или "TrackManeuvering". Различие между двумя типами задач отслеживания позволяет вам планировать задачи для каждого типа дорожки с различными скоростями повторного просмотра, что делает его адаптивным алгоритмом отслеживания. Подобно задачам поиска, задачи отслеживания также управляются в очереди заданий.

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

Сгруппируйте очереди поиска и отслеживания вместе в структуре для более легкой ссылки в цикле симуляции.

jobq.SearchQueue  = searchq;
jobq.SearchIndex  = current_search_idx;
jobq.TrackQueue   = trackq;
jobq.NumTrackJobs = num_trackq_items;
jobq.PositionSelector = posSelector;
% Keep a reset state of jobq
resetJobQ = jobq;

Планирование задач

В этом примере для простоты многофункциональный радар выполняет только один тип задания в течение небольшого периода времени, часто называемого dwell, но может переключать задачи в начале каждого dwell. Для каждого dwell радар просматривает все задачи, которые подлежат выполнению, и выбирает подтверждение или отслеживает задачу, если их время для запуска пришло. В противном случае радар выбирает задачу поиска. Чтобы контролировать время выполнения задач, вы устанавливаете managerPreferences struct, заданная ниже. Самая высокая частота повторного просмотра, которая равна частоте обновления радара, дается confirm задача, чтобы гарантировать, что пучок подтверждения следует за каждой новой предварительной дорожкой, которая существует. Точно так же можно управлять скоростью пересмотра для неманеврирующих и маневрирующих целей. При этом вы выбираете значения 1Hz и 4 Гц для неманеврирующих и маневрирующих целей соответственно. Поскольку есть шесть целей, в то время как частота обновления радара составляет 20, если цели не маневрируют, радар должен тратить около 70% времени в поисковом режиме и 30% времени в режиме слежения. Когда новые дорожки инициализируются и когда трекер считает, что цели маневрируют, менеджер ресурсов выделяет больше пучков слежения за счет поисковых пучков.

В разделе Анализ результатов показаны результаты других опций.

managerPreferences = struct(...
    'Type', {"Search","Confirm","TrackNonManeuvering","TrackManeuvering"}, ...
    'RevisitRate', {0, updateRate, 1, 4}, ...
    'Priority', {1000, 3000, 1500, 2500});

Запуск сценария

Во время симуляции радиолокационный луч изображается синим или фиолетовым цветами, представляющими лучи, связанные с поиском и дорожкой, соответственно. Распределение задач между поиском и конкретными треками на последнюю секунду симуляции можно также увидеть с помощью панели «Распределение ресурсов» на панели «Последняя секунда» в нижней части рисунка. На верхнем рисунке можно увидеть историю каждой дорожки и сравнить ее с траекторией.

% Create a radar resource allocation display
rp = uipanel('Parent',f,'Title','Resource Allocation in the Last Second','FontSize',12,...
    'BackgroundColor','white','Position',[.01 0.01 0.98 0.23]);
rax = axes(rp);

% Run the scenario
allocationType = helperAdaptiveTrackingSim(scenario, thp, rax, tracker, resetJobQ, managerPreferences);

Анализ результатов

Анализируйте нагрузку радиолокационной задачи и ее деление между заданиями поиска, подтверждения и отслеживания. График ниже показывает, что в течение большей части времени радар выделяет около 70% заданий поиска и 30% заданий отслеживания, что ожидаемо, когда цели не маневрируют. Когда цели маневрируют, менеджер ресурсов адаптируется, чтобы выделить больше заданий отслеживания. Когда одновременно маневрирует больше трасс, появляется больше заданий слежения, что видно, например, около 700-го временного шага. Задания подтверждения занимают очень мало радарного времени, потому что трекер сконфигурирован для подтверждения треков после двух ассоциаций обнаружения в трех попытках. Поэтому подтверждение или отказ от предварительных дорожек является быстрым.

numSteps = numel(allocationType);
allocationSummary = zeros(3,numSteps);
for i = 1:numSteps
    for jobType = 1:3
        allocationSummary(jobType,i) = sum(allocationType(1,max(1,i-2*updateRate+1):i)==jobType)/min(i-1,2*updateRate);
    end
end
figure;
plot(1:numSteps,allocationSummary(:,1:numSteps))
title('Radar Allocation vs. Time Step');
xlabel('Time Step');
ylabel('Fraction of step in the last two seconds in each mode');
legend('Search','Confirmation','Tracking');
grid on

Теперь вы хотите сравнить результат выше с результатом активного отслеживания с 1Hz частотой повторного просмотра трека. Следующие рисунки показывают результаты отслеживания и график распределения радаров для случая активного отслеживания. Результаты отслеживания показывают, что некоторые треки были потеряны и сломаны, но график распределения радиолокационных ресурсов показывает 70% задачу поиска и 30% отслеживания деления как в случае Адаптивного Отслеживания. Вы можете получить эти результаты, выполнив выборку кода ниже.

% Modify manager preferences to 1Hz revisit rate for both non-maneuvering and maneuvering targets
managerPreferences = struct(...
    'Type', {"Search","Confirm","TrackNonManeuvering","TrackManeuvering"}, ...
    'RevisitRate', {0, updateRate, 1, 1}, ...
    'Priority', {1000, 3000, 1500, 2500});
% Run the scenario
allocationType = helperAdaptiveTrackingSim(scenario, thp, rax, tracker, resetJobQ, managerPreferences);

Очевидно, что для активного отслеживания необходима более высокая скорость пересмотра отслеживания. Два графика ниже показывают, что увеличение скорости пересмотра дорожки до 2Hz улучшает отслеживание маневрирующих целей. Однако стоимость в том, что радар посвящает более 50% своего времени отслеживанию задач, даже когда трассы не маневрируют. Если бы количество целей было больше, радар стал бы перегружен.

Предыдущие результаты показывают, что достаточно пересмотреть маневрирующие цели со скоростью 2Hz. Однако может ли Adaptive Tracking использоваться, чтобы уменьшить скорость повторного рассмотрения неманеврирующих целей, чтобы 0.5Hz? Приведенные ниже графики показывают, что отслеживание все еще хорошо. При такой настройке выделение радара позволяет на 80% -90% времени в поисковом режиме, оставляя радар с способностью искать и отслеживать еще больше целей.

Сводные данные

В этом примере показано, как использовать комбинацию отслеживания и управления радиолокационными ресурсами для адаптации скорости повторного просмотра для маневрирования треков. Адаптивное отслеживание позволяет вам выбрать скорость повторного просмотра, которая подходит для каждого типа цели и статуса маневрирования. В результате радар становится более эффективным и может отслеживать большее количество маневрирующих целей.

Ссылки

[1] Александр Чарлиш, Фолкер Хоффман, Кристоф Деген и Изабель Шлэнджен, «Развитие от Адаптивного до Познавательного Радарного управления Ресурсом», в Космосе IEEE и журнале Electronic Systems, издании 35, № 6, стр 8-19, 1 июня 2020, doi: 10.1109/MAES.2019.2957847.

Вспомогательные функции

getCurrentRadarTask

Возвращает радиолокационную задачу, которая используется для указания радиолокационного луча.

type('getCurrentRadarTask.m')
function [currentjob,jobq] = getCurrentRadarTask(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
readyidx = find([trackq(1:num_trackq_items).Time]<=current_time);
[~,maxpidx] = max([trackq(readyidx).Priority]);
taskqidx = readyidx(maxpidx);

% 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.
if ~isempty(taskqidx) % && trackq(taskqidx).Priority >= searchq(searchqidx).Priority
    currentjob = trackq(taskqidx);
    for m = taskqidx+1:num_trackq_items
        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;

helperAdaptiveRadarSim

Запускает симуляцию

type('helperAdaptiveTrackingSim.m')
function allocationType = helperAdaptiveTrackingSim(scenario, thp, rax, tracker, resetJobQ, managerPreferences)
% Initialize variables
radar = scenario.Platforms{end}.Sensors{1};
updateRate = radar.UpdateRate;
resourceAllocation = nan(1,updateRate);
h = histogram(rax,resourceAllocation,'BinMethod','integers');
xlabel(h.Parent,'TrackID')
ylabel(h.Parent,'Num beams');
numSteps = updateRate * 185;
allocationType = nan(1,numSteps);
currentStep = 1;
restart(scenario);
reset(tracker);
% Return to a reset state of jobq
jobq = resetJobQ;

% Plotters and axes
plp = thp.Plotters(1);
dtp = thp.Plotters(3);
cvp = thp.Plotters(4);
trp = thp.Plotters(5);

% For repeatable results, set the random seed and revert it when done
s = rng(2020);
oc = onCleanup(@() rng(s));

% Main loop
while advance(scenario)
    time = scenario.SimulationTime;
    
    % Update ground truth display
    poses = platformPoses(scenario);
    plotPlatform(plp, reshape([poses.Position],3,[])');
    
    % Point the radar based on the scheduler current job
    [currentJob,jobq] = getCurrentRadarTask(jobq,time);
    currentStep = currentStep + 1;
    if currentStep > updateRate
        resourceAllocation(1:end-1) = resourceAllocation(2:updateRate);
    end
    if strcmpi(currentJob.JobType,'Search')
        detectableTracks = zeros(0,1,'uint32');
        resourceAllocation(min([currentStep,updateRate])) = 0;
        allocationType(currentStep) = 1;
        cvp.Color = [0 0 1]; 
    else
        detectableTracks = currentJob.TrackID;
        resourceAllocation(min([currentStep,updateRate])) = currentJob.TrackID;
        cvp.Color = [1 0 1];
        if strcmpi(currentJob.JobType,'Confirm')
            allocationType(currentStep) = 2;
        else
            allocationType(currentStep) = 3;
        end
    end
    ra = resourceAllocation(~isnan(resourceAllocation));
    h.Data = ra;
    h.Parent.YLim = [0 updateRate];
    h.Parent.XTick = 0:max(ra);
    point(radar, currentJob.BeamDirection);
    plotCoverage(cvp, coverageConfig(scenario));
    
    % Collect detections and plot them
    detections = detect(scenario);
    if isempty(detections)
        meas = zeros(0,3);
    else
        dets = [detections{:}];
        meassph = reshape([dets.Measurement],3,[])';
        [x,y,z] = sph2cart(deg2rad(meassph(1)),deg2rad(meassph(2)),meassph(3));
        meas = (detections{1}.MeasurementParameters.Orientation*[x;y;z]+detections{1}.MeasurementParameters.OriginPosition)';
    end
    plotDetection(dtp, meas);
    
    % Track and plot tracks
    if isLocked(tracker) || ~isempty(detections)
        tracks = tracker(detections, time, detectableTracks);
        pos = getTrackPositions(tracks,jobq.PositionSelector);
        plotTrack(trp,pos,string([tracks.TrackID]));
    end
        
    % Manage resources for next jobs
    jobq = manageResource(detections,jobq,tracker,currentJob,time,managerPreferences);
end

initMPARIMM

Инициализирует взаимодействующий фильтр нескольких моделей, используемый трекером.

type('initMPARIMM.m')
function imm = initMPARIMM(detection)

cvekf = initcvekf(detection);
cvekf.StateCovariance(2,2) = 1e6;
cvekf.StateCovariance(4,4) = 1e6;
cvekf.StateCovariance(6,6) = 1e6;

ctekf = initctekf(detection);
ctekf.StateCovariance(2,2) = 1e6;
ctekf.StateCovariance(4,4) = 1e6;
ctekf.StateCovariance(7,7) = 1e6;
ctekf.ProcessNoise(3,3) = 1e6; % Large noise for unknown angular acceleration

imm = trackingIMM('TrackingFilters', {cvekf;ctekf}, 'TransitionProbabilities', [0.99, 0.99]);

manageResources

Управляет очередью заданий и создает новые задачи на основе результатов отслеживания.

type('manageResource.m')
function jobq = manageResource(detections,jobq,tracker,current_job,current_time,managerPreferences)

trackq           = jobq.TrackQueue;
num_trackq_items = jobq.NumTrackJobs;
if ~isempty(detections)
    detection = detections{1};
else
    detection = [];
end

% 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)
            rng_est = detection.Measurement(3);
            revisit_time = current_time+1;
            allTracks = predictTracksToTime(tracker, 'all', revisit_time);
            
            % A search task can still find a track we already have. Define
            % a confirmation task only if it's a tentative track. There
            % could be more than one if there are false alarms.
            toConfirm = find(~[allTracks.IsConfirmed]);
            numToConfirm = numel(toConfirm);
            for i = 1:numToConfirm
                trackid = allTracks(toConfirm(i)).TrackID;
                job = revisitTrackJob(tracker, trackid, current_time, managerPreferences, 'Confirm');
                num_trackq_items = num_trackq_items+1;
                trackq(num_trackq_items) = job;
            end
        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;
            job = revisitTrackJob(tracker, trackid, current_time, managerPreferences, 'TrackNonManeuvering');
            if ~isempty(job)
                num_trackq_items = num_trackq_items+1;
                trackq(num_trackq_items) = job;
            end
        end

    otherwise % Covers both types of track jobs
        % 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;
            job = revisitTrackJob(tracker, trackid, current_time, managerPreferences, 'TrackNonManeuvering');
            if ~isempty(job)
                num_trackq_items = num_trackq_items+1;
                trackq(num_trackq_items) = job;
            end
        else
            trackid = current_job.TrackID;
            job = revisitTrackJob(tracker, trackid, current_time, managerPreferences, 'TrackNonManeuvering');
            if ~isempty(job)
                num_trackq_items = num_trackq_items+1;
                trackq(num_trackq_items) = job;
            end
        end

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

function job = revisitTrackJob(tracker, trackID, currentTime, managerPreferences, jobType)
types = [managerPreferences.Type];
inTypes = strcmpi(jobType,types);
revisitTime = 1/managerPreferences(inTypes).RevisitRate + currentTime;
predictedTracks = predictTracksToTime(tracker,'All',revisitTime);

% If the track is not dropped, try to revisit it
allTrackIDs = [predictedTracks.TrackID];
if any(trackID == allTrackIDs)
    mdlProbs = getTrackFilterProperties(tracker, trackID, 'ModelProbabilities');
    if mdlProbs{1}(2) > 0.6
        jobType = 'TrackManeuvering';
        inTypes = strcmpi(jobType,types);
        revisitTime = 1/managerPreferences(inTypes).RevisitRate+currentTime;
        predictedTracks = predictTracksToTime(tracker,trackID,revisitTime);
        xpred = predictedTracks.State([1 3 5]);
    else
        xpred = predictedTracks(trackID == allTrackIDs).State([1 3 5]);   
    end
    
    [phipred,thetapred,rpred] = cart2sph(xpred(1),xpred(2),xpred(3));
    job = struct('JobType',jobType,'Priority',3000,...
        'BeamDirection',rad2deg([phipred thetapred]),'WaveformIndex',1,'Time',revisitTime,...
        'Range',rpred,'TrackID',trackID);
    
else
    job = [];
end
end