function radioTime = runRBDSReceiver(radioTime, userInput, viewer, inGenCode)
%RUNRBDSRECEIVER Run RBDS receiver

%   Copyright 2016-2018 The MathWorks, Inc.

persistent rbdsParam sigSrc fmBroadcastDemod player datalinkDecoder sessionDecoder sigSrcType

% see if filename changed:
fname1 = '';
fname2 = '';
if ~isempty(sigSrcType) && sigSrcType == ExampleSourceType.Captured 
  [~, fname1] = fileparts(userInput.SignalFilename);
  [~, fname2] = fileparts(sigSrc.Filename);
end
fileNameChanged = ~strcmp(fname1, fname2);

% (re)create objects:
if isempty(rbdsParam) || ...
    userInput.SignalSourceType ~= sigSrcType || ...
    ( userInput.SignalSourceType == ExampleSourceType.Captured && fileNameChanged )
  
  % First time call, or source changed from/to file to/from radio, or a
  % different filename needs to be read now. Recreate sources:
  [rbdsParam, sigSrc] = helperRBDSConfig(userInput);
  sigSrcType = userInput.SignalSourceType;
  
  fmBroadcastDemod = comm.FMBroadcastDemodulator(...
    'SampleRate',         rbdsParam.FrontEndSampleRate, ...
    'FrequencyDeviation', rbdsParam.FrequencyDeviation, ...
    'FilterTimeConstant', rbdsParam.FilterTimeConstant, ...
    'AudioSampleRate',    rbdsParam.AudioSampleRate, ...
    'Stereo', true);

  datalinkDecoder = RBDSDataLinkDecoder();
  sessionDecoder  = RBDSSessionDecoder();
  rtID = '4BD7';
  registerODA(sessionDecoder, rtID, @RadioTextPlusMainGroup, @RadioTextPlus3A);
end

if xor(inGenCode, datalinkDecoder.RunInGenCode)
  release(datalinkDecoder);
  datalinkDecoder.RunInGenCode = inGenCode;
end

if userInput.PlaybackAudio && isempty(player)
  % Create audio player
  player = audioDeviceWriter('SampleRate', rbdsParam.AudioSampleRate);
end

if xor(userInput.OpenScopes, rbdsParam.OpenScopes)
  rbdsParam.OpenScopes = userInput.OpenScopes;
end

% reset objects at sim start:
if radioTime == 0
  clear RBDSPhyDecoder RBDSCostasLoop; % clear their persistent variables
  reset(sigSrc);
  reset(datalinkDecoder);
  reset(sessionDecoder);
end

% frequency changed, reset viewer and Layer2/Layer3 state:
if (userInput.SignalSourceType == ExampleSourceType.RTLSDRRadio || ...
    userInput.SignalSourceType == ExampleSourceType.PlutoSDRRadio) && ...
   (sigSrc.CenterFrequency ~= userInput.CenterFrequency)
    sigSrc.CenterFrequency = userInput.CenterFrequency;
    reset(datalinkDecoder);
    reset(sessionDecoder);
    reset(viewer);
  return;
end

% MAIN LOOP:
if radioTime <= userInput.Duration

  rcv = sigSrc();

  % Demodulate FM broadcast signals and play the decoded audio
  if userInput.PlaybackAudio
    audioSig = fmBroadcastDemod(rcv);
    player(audioSig);
  end

  % Process physical layer (Layer #1)
  if ~inGenCode
    bitsPHY = RBDSPhyDecoder(rcv, rbdsParam, rbdsParam.OpenScopes);
  else
    fields = {'isSourceRadio', 'isSourcePlutoSDR', 'Duration', 'LogData', 'LogFileName', 'OpenScopes'};
    rbdsParamPhy = rmfield(rbdsParam, fields);
    bitsPHY = RBDSPhyDecoder_mex(rcv, rbdsParamPhy, rbdsParam.OpenScopes);
  end

  % Process data-link layer (Layer #2)
  [enabled, iw1, iw2, iw3, iw4] = datalinkDecoder(bitsPHY);

  % Process session and presentation layer (Layer #3)
  outStruct = sessionDecoder(enabled, iw1, iw2, iw3, iw4);

  % View results packet contents (Data Viewer)
  update(viewer, outStruct);

  radioTime = radioTime + rbdsParam.FrameDuration;
  
else
  release(sigSrc);
  if userInput.PlaybackAudio
    release(player);
  end
end
