Main Content

Create a Digital Twin of a TI mmWave Radar

Since R2025a

This example creates a waveform-level model of a TI mmWave radar using the radar's configuration file, and it explains how to leverage custom RF components for simulating real systems in MATLAB® software. You will compare the synthetic data with data collected from the real-world TI radar and demonstrate the strong similarity between the actual sensor and its model.

Introduction

Radar Toolbox™ allows you to collect I/Q data from the TI mmWave radar sensors listed in Installation, Setup, and Supported Boards as demonstrated in I/Q Data Collection and Detection Generation with Texas Instruments (TI) millimeter-wave (mmWave) Radar. This example compliments the real data collection tutorial and shows, step by step, how to create an accurate model of the TI hardware and waveform using the radarTransceiver. This allows you to test your algorithm in custom scenarios without the overhead of running costly experiments.

This example considers the IWR1642BOOST automotive radar paired with a data acquisition board (DCA1000EVM). Together, these boards export raw I/Q samples that can be processed and visualized using the many functions and system objects provided in MATLAB.

Model a Radar Transceiver from the Configuration File

Read the Configuration File

In order to run the TI radar, you must first send the radar board a configuration file that specifies the waveform and the configurable properties of the hardware and software. The format and explanation of the configuration file is described in detail in the MMWAVE SDK User Guide linked in this table. The rest of the example uses the following configuration file shown below.

type("exampleConfig.cfg")
sensorStop
flushCfg
dfeDataOutputMode 1
channelCfg 15 3 0
adcCfg 2 1
adcbufCfg -1 0 1 1 1
profileCfg 0 77 150 7 75 0 0 25 1 400 6250 0 0 30
chirpCfg 0 0 0 0 0 0 0 1
chirpCfg 1 1 0 0 0 0 0 2
frameCfg 0 1 40 0 100 1 0
lowPower 0 1
guiMonitor -1 1 1 0 0 0 1
cfarCfg -1 0 2 8 4 3 0 15 1
cfarCfg -1 1 0 8 4 4 1 15 1
multiObjBeamForming -1 1 0.5
clutterRemoval -1 0
calibDcRangeSig -1 0 -5 8 256
extendedMaxVelocity -1 0
bpmCfg -1 0 0 1
lvdsStreamCfg -1 0 1 0
compRangeBiasAndRxChanPhase 0.0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
measureRangeBiasAndRxChanPhase 0 1.5 0.2
CQRxSatMonitor 0 3 4 95 0
CQSigImgMonitor 0 95 4
analogMonitor 0 0
aoaFovCfg -1 -90 90 -90 90
cfarFovCfg -1 0 0 10.00
cfarFovCfg -1 1 -2.59 2.59
calibData 0 0 0
sensorStart

Many of the lines in the configuration file are mandatory to operate the radar but are not relevant for exporting raw samples from the radar to your computer or for the waveform-level model of the radar. Instead, they configure the post processing that would be done on the radar if you wanted to export detection/measurement level data, similar to what would be exported from the radarDataGenerator. Thus, you can create an accurate model of the radar from just a few lines of the configuration file and the information contained in the radar's datasheets.

The relevant lines in the configuration file are:

Line Header

Description

profileCfg

Determines the waveform and analog to digital converter (ADC) properties

chirpCfg

Determines the variations between each chirp or pulse (if any)

frameCfg

Determines how many chirps are in each frame (also known as a coherent processing interval, CPI) and how many frames are transmitted

For more information, explore the following links:

Link

Description

Link1

MMWAVE SDK User Guide (Configuration File)

Link2

IWR1642 datasheet (Just the Chip)

Link3

IWR1642BOOST datasheet (Evaluation Board)

Parsing the configuration file by referring to the MMWAVE SDK User Guide can be tedious, especially if you are still changing and/or comparing different configurations. To help simplify the process, a helper class is provided to parse the relevant lines in the configuration file.

Parse the configuration with the following helper class.

radarCfg = helperParseRadarConfig("exampleConfig.cfg")
radarCfg = 
  helperParseRadarConfig with properties:

         StartFrequency: 7.7000e+10
          StopFrequency: 7.8875e+10
        CenterFrequency: 7.7938e+10
              Bandwidth: 1.6000e+09
        SampledRampTime: 6.4000e-05
         ChirpCycleTime: 2.2500e-04
             SweepSlope: 2.5000e+13
        SamplesPerChirp: 400
          ADCSampleRate: 6250000
              NumChirps: 80
           NumReceivers: 4
        ActiveReceivers: 4
        NumTransmitters: 2
     ActiveTransmitters: 2
       FramePeriodicity: 0.1000
        RangeResolution: 0.0937
           MaximumRange: 37.4741
       MaximumRangeRate: 2.1370
    RangeRateResolution: 0.1068
                 RxGain: 30
                    PRF: 4.4444e+03

This helper class assumes the use of time division multiplexing (TDM) by alternating between the two transmitting antennas for each chirp. Moreover, the helper class assumes that every transmitted chirp's waveform is identical. As a result, the chirpCfg lines can be ignored. Furthermore, this parser assumes that all receivers are enabled. Modify the parser class to support changing these features as well as other configurable features not contained in the parsed lines.

Once the radar configuration file is parsed, use the helperRadarAndProcessor class to create the accompanying radar transceiver and associated processing blocks, such as the range-Doppler processor and CFAR detector.

addpath("tiRadar")
radarSystem = helperRadarAndProcessor(radarCfg)
radarSystem = 
  helperRadarAndProcessor with properties:

              RadarTransceiver: [1×1 radarTransceiver]
               AntennaNoiseCfg: []
                  VirtualArray: [1×1 phased.ULA]
                            Fc: 7.7938e+10
                            Fs: 1.6000e+09
    NumSamplesDuringPulseWidth: 102400
         RangeDopplerProcessor: [1×1 phased.RangeDopplerResponse]
                RangeProcessor: [1×1 phased.RangeResponse]
                  PhaseShiftBf: [1×1 phased.PhaseShiftBeamformer]
                    TwoDimCFAR: [1×1 phased.CFARDetector2D]

The radar transceiver itself is contained in the RadarTransceiver property of the object as shown immediately below. The next few sections outline how the helperRadarAndProcessor's radarTransceiver System Object™ is designed to match the TI radar.

radarSystem.RadarTransceiver
ans = 
  radarTransceiver with properties:

                Waveform: [1×1 phased.LinearFMWaveform]
             Transmitter: [1×1 phased.Transmitter]
         TransmitAntenna: [1×1 phased.Radiator]
          ReceiveAntenna: [1×1 phased.Collector]
                Receiver: [1×1 phased.Receiver]
      MechanicalScanMode: 'None'
      ElectronicScanMode: 'None'
        MountingLocation: [0 0 0]
          MountingAngles: [0 0 0]
    NumRepetitionsSource: 'Property'
          NumRepetitions: 1
       RangeLimitsSource: 'Property'
             RangeLimits: [0 Inf]
         RangeOutputPort: false
          TimeOutputPort: false

Configure the Antennas

Antenna Arrays

There are two antenna arrays on the IWR1642BOOST, one for transmitting and one for receiving. The transmit antenna array contains two antenna elements spaced two wavelengths apart from each other. The receiving antenna array consists of four antenna elements each spaced half of a wavelength apart. They are each visualized below.

figure;
subplot(1,2,1)
viewArray(radarSystem.RadarTransceiver.TransmitAntenna.Sensor,'ShowLocalCoordinates',false,'ShowAnnotation',false)
title('Tx Array Geometry')
subplot(1,2,2)
viewArray(radarSystem.RadarTransceiver.ReceiveAntenna.Sensor,'ShowLocalCoordinates',false,'ShowAnnotation',false)
title('Rx Array Geometry')

Figure contains 2 axes objects. Hidden axes object 1 with title Tx Array Geometry contains an object of type scatter. Hidden axes object 2 with title Rx Array Geometry contains an object of type scatter.

Using TDM, the transmit and receive antenna arrays manifest into the following virtual array. More about creating large virtual arrays can be found in Simulate an Automotive 4D Imaging MIMO Radar

figure;
viewArray(radarSystem.VirtualArray)
title('Virtual Array')

Figure contains an axes object. The hidden axes object with title Virtual Array, xlabel x axis (Az 0 El 0) -->, ylabel y axis --> contains 7 objects of type scatter, line, text.

Antenna Elements

Both antenna arrays are made of elements modeled by the phased.CosineAntennaElement system object. This serves as an approximation of the directivity pattern measured and is documented in the IWR1642BOOST datasheet linked above. Other antenna element models are also available in the Phased Array System Toolbox™. For the most precise modeling of the antenna elements' directivity, use the phased.CustomAntennaElement and model the exact gain patterns plotted in the documentation. For the cosine approximation, the directivity pattern near boresight was prioritized in this approximation leading to a mismatch in the peak gain at boresight. You can remove this by changing the exponents of the cosine antenna elements or by adding an amplifier with a negative gain in the transmitter described in the next section. However, with the goal of matching the SNR of the actual device, you can also wait and apply only the net SNR difference with the actual model after all such approximations, if there is any. This comparison is shown in Compare Synthetic and Real-World Data. Display the antenna element patterns below using the pattern method.

element = radarSystem.RadarTransceiver.TransmitAntenna.Sensor.Element;

figure;
az = -180:180;
el = 0;
pattern(element,radarCfg.CenterFrequency,az,el,CoordinateSystem="rectangular",Type='directivity'); 
hold on;
az = 0;
el = -90:90;
pattern(element,radarCfg.CenterFrequency,az,el,CoordinateSystem="rectangular",Type='directivity'); 
legend('Azimuth','Elevation')
title('Single Antenna Element Directivity Pattern')
xlim([-40 40])
ylim([0 20])

Figure contains an axes object. The axes object with title Single Antenna Element Directivity Pattern, xlabel Elevation Angle (degrees), ylabel Directivity (dBi) contains 2 objects of type line. These objects represent Azimuth, Elevation.

Transmitter

The transmitter in the radar transceiver accounts for three main features of the TI radar:

  1. Signal Splitter (for dechirping on the receive end)

  2. Time Division Multiplexing (alternating between elements in the transmit antenna array)

  3. Transmitted Power

These features are implemented using the cascade transmitter/receiver design depicted below.

figure
viewLayout(radarSystem.RadarTransceiver.Transmitter)

Figure contains an axes object. The hidden axes object with title Transmitter Layout contains 15 objects of type line, text.

Signal Splitter

The radar processes reflected signals first by mixing them with the transmitted waveform (instead of matched filtering). To implement this mixing operation, use the signal splitter in the transmitter to feed a copy of the transmitted waveform to the receiver and then to a custom mixer in the receiver.

Time Division Multiplexing

The code in the directory tiRadar implements a custom object that switches the active transmit antenna for each chirp. The logic that accomplishes this is inside the step implementation in helperTDMSwitch.m. The step implementation creates a matrix of zeros of size n elements by the length of the transmitted signal. During simulation, only the active column has a nonzero signal, which results in only one antenna transmitting at a time.

Transmit Power

Lastly, the transmitted power according to the IWR1642 datasheet is 12.5 dBm. Because the waveform objects have an amplitude of one, model the specified transmit power using an amplifier operating in saturation in series with the TDM switch.

transmitPower = radarSystem.RadarTransceiver.Transmitter.Cascade{3}.OPsat % dBm
transmitPower = 
12.5000

Receiver

The modeled receiver accounts for the low noise amplifier (LNA) and the mixing operation.

figure
viewLayout(radarSystem.RadarTransceiver.Receiver)

Figure contains an axes object. The hidden axes object with title Receiver Layout contains 21 objects of type line, text.

Low Noise Amplifier

The first step is to apply the LNA by setting its receiver gain and the noise figure. The receiver gain is defined in the radar configuration class in the RxGain property, and the noise figure is set to 15 dB according to the IWR1642 datasheet. Additionally, according to the IWR1642 documentation, the receiver subsystem can support bandwidths of up to 5 MHz so the sample rate is set accordingly to approximate the noise bandwidth.

LNA = radarSystem.RadarTransceiver.Receiver.Cascade{1};
rxGain = LNA.Gain
rxGain = 
30
noiseFigure = LNA.NF
noiseFigure = 
15
receiverNoisebandwidth = LNA.SampleRate
receiverNoisebandwidth = 
5000000

Mixer

The mixer performs the dechirping necessary to compute the range of the target reflections. Dechirping refers to the mixing of the incoming reflected signal with the transmitted signal. This is an alternative to matched filtering and allows for a lower bandwidth receiver than matched filtering would enable. The mixer mixes the amplified received signal with a copy of the transmitted waveform.

Waveform

The TI radar transmits a linear frequency modulated waveform. While both the phased.LinearFMWaveform and phased.FMCWWaveform create such waveforms, only the LFM waveform System Object models duty cycles less than 100%. According to the configuration, there is a chirp every 225 microseconds while the sampled ramp time is only 64 microseconds, yielding a duty cycle of about 28%. In other words, the waveform is only active approximately 28% of the total PRI. This does not account for the over sweep [1] present in the waveform.

PRI = 1/radarCfg.PRF
PRI = 
2.2500e-04
sampledRampTime = radarCfg.SampledRampTime
sampledRampTime = 
6.4000e-05
dutyCycle = sampledRampTime/PRI
dutyCycle = 
0.2844

Given this waveform design, configure the LFM waveform with a ramp time equal to the sampled ramp time and with the rest of the PRI without any signal transmission as shown in the figure below.

figure
stft(radarSystem.RadarTransceiver.Waveform(),radarSystem.Fs,'FrequencyRange','twosided')

Figure contains an axes object. The axes object with title Short-Time Fourier Transform, xlabel Time (μs), ylabel Frequency (GHz) contains an object of type image.

Sampling Rate

The real TI hardware includes an analog front end that mixes down the signal to an intermediate frequency (IF) proportional to the reflections' ranges. This allows the hardware to sample at a rate much lower than the bandwidth of the waveform. In this software simulation, we construct a digital representation of the waveform and thus need to sample this waveform at its bandwidth. Thus, the LFM waveform, which has a bandwidth of 1.6 GHz must be sampled at 1.6 GHz. Only after creating the high-bandwidth digital waveform and receiving target reflections can it be mixed down to its IF frequencies. However, we can still simulate data sampled at the proper rate (one that matches the actual radar hardware) by downsampling the IF signal after the mixing operation using the downsampleIF method. You demonstrate this in the next section.

Number of Chirps/Pulses

The number of chirps per frame can be configured in three separate places. The correct place to configure this property depends on the nature of the application and is described below.

  • When analyzing the waveform object by itself, set the number of chirps in the waveform system object to the desired value. For example, in phased.LinearFMWaveform, modify the NumPulses property.

  • In most general cases when using a radarTransceiver, set the radarTransceiver NumRepetitions property to the desired number of chirps. This is applicable when the velocity is constant within a frame or CPI.

  • When using the radarTransceiver with targets that need to be moved or modified between chirps such as the backscatterPedestrian and backscatterBicyclist, keep the radarTransceiver NumRepetitions property equal to one. Then step the radar transceiver in a for-loop where each iteration transmits and receives one chirp. Set the number of for loop iterations to the desired number of chirps.

Data Processing

To demonstrate how the model of the IWR1642BOOST senses point targets, create a point target with a position and velocity. A point target's motion-induced phase shift is automatically accounted for between chirps so set the radar transceiver's number of repetitions to the number of chirps.

release(radarSystem.RadarTransceiver.Waveform) % Due to earlier code in example
% Use with target that does not need manual move/update between chirps
radarSystem.RadarTransceiver.NumRepetitions = radarCfg.NumChirps; 
tgt1 = struct('Position', [7 -2 0], 'Velocity', [1.5 .5 0]);

Note the orientation of the y-values in the theater plot below. The axes are oriented so that the positive z-axis points out of the computer screen. Because of this, in the later plots and in the beamforming output, the target will remain on the right as it is here but will have a positive horizontal axis value.

tp = theaterPlot('XLim',[-1 10],'YLim',[-10 10]);
radarPlotter = platformPlotter(tp,'DisplayName','Radar','Marker','^','MarkerFaceColor','k');
targetPlotter = platformPlotter(tp,'DisplayName','Target','Marker','x');
plotPlatform(radarPlotter, [0 0 0])
plotPlatform(targetPlotter, tgt1.Position,tgt1.Velocity)
title('Scene Geometry')
camroll(90)

Figure contains an axes object. The axes object with title Scene Geometry, xlabel X (m), ylabel Y (m) contains 2 objects of type line. One or more of the lines displays its values using only markers These objects represent Radar, Target.

Now, simulate the transmission of the waveform and reception of the reflected signal. Remember, the step function of the radar transceiver returns the already mixed down IF signal from the target. Then, take only the samples during the active portion of the waveform and downsample them accordingly. The size of the signal after downsampling is the number of samples per chirp by the number of channels by the number of chirps per frame.

time = 0;
sig = radarSystem.RadarTransceiver(tgt1,time);
sig = radarSystem.downsampleIF(sig);
size(sig)
ans = 1×3

   400     4    80

Range-Doppler Processing

Use the method boresightRD to compute the range-Doppler response at boresight. This method reformats the datacube so that the channel dimension (dimension 2) is the same size as the virtual array (8 virtual channels). Then, it sums across the virtual channels and computes the range-Doppler response.

% RD processing    
[rgdp,rngBins,dpBins] = radarSystem.boresightRD(sig);

CFAR Detection

Perform 2D-CFAR detection using the findPeaksRangeDoppler method in the radarSystem object. Extend the range-Doppler output using circular padding to allow the detection of targets near the unambiguous Doppler limits. The CFAR parameters are defined in the method Create2DCFAR in the helperRadarAndProcessor class. The detector outputs multiple detections for the point target in the configured scene. The CFAR mask on the right shows the cells tested for a target as well as the cells that returned one. The detections from the CFAR mask are further filtered so that only the strongest peak per range bin is returned. Edit the CFAR parameters and see how the detection results change.

% 2D CFAR
[cfarMask,peakLocations] = radarSystem.findPeaksRangeDoppler(rgdp);
radarSystem.cfarPlotter(rgdp,cfarMask,peakLocations,rngBins,dpBins)

Figure contains 2 axes objects. Axes object 1 with title CFAR Results, xlabel Doppler (m/s), ylabel Range (m) contains 2 objects of type image, scatter. Axes object 2 with title CFAR Mask, xlabel Doppler (m/s), ylabel Range (m) contains an object of type image.

Angle Processing

Process the angle data in the horizontal plane (azimuth) using the getBfRangeAnglePatch defined in the helperRadarAndProcessor class. This custom method returns the beamforming output as well as its projection onto a cartesian coordinate system. Again, note that the convention in the theater plot above is such that the y-values are negative to the right of the radar. As a result, the peak in the beamforming output that is to the right of the origin has a positive y-value.

Moving targets detected by a radar that employs TDM can cause an abrupt phase jump between antennas 4 and 5 in the virtual array. This is because the target has moved slightly between transmissions. Simply concatenating the received signals into a virtual array does not account for the resultant phase jump and can cause error in the angle estimation of the target. Authors in [2] correct for this by upsampling the pulse dimension and then staggering the signals such that received samples alternate in the same way as the transmitting antennas. See the short implementation in the method upsampleAndStagger for more details. Try processing the angle data with and without the TDM correction.

TdmCorrection = true;
[bfOut,cartData,xBounds,yBounds,tgtAngles] = radarSystem.getBfRangeAnglePatch(sig,peakLocations,rngBins,TdmCorrection);

helperPlotBfOutput(bfOut,tgt1,xBounds,yBounds,cartData)

Figure contains 2 axes objects. Axes object 1 with title Phaseshift Beamformer Output, xlabel Azimuth Angle (deg), ylabel Magnitude (dB) contains 5 objects of type line, constantline. This object represents Ground Truth. Axes object 2 with title Cartesian Projection, xlabel Y Axis (m), ylabel X Axis (m) contains 2 objects of type image, line. One or more of the lines displays its values using only markers This object represents Ground Truth.

Compare Synthetic and Real-World Data

This section demonstrates the similarity between synthetic data and data collected using the TI IWR1642BOOST. To collect real data, we configured the IWR1642BOOST using the same configuration detailed in the beginning of this example. Then, we had a human move a corner reflector towards the radar. In the collected frame, the corner reflector has a range of 3.9 meters and an azimuth of 5 degrees. The target is also moving at 0.42 m/s towards the radar.

For this comparison, create a similar scene containing a point target with the same position and velocity as observed in the real-world data. Below, we show that when identical processing is applied to real-world and synthetic data, the results are very similar.

Synthetic Data

Define a corner reflector target (tgt2) and generate a synthetic datacube using the digital twin's radarTransceiver.

tgt2 = struct('Position', [3.9*cosd(5) -3.9*sind(5) 0], 'Velocity', [-.42 0 0], ...
    'Signatures',{rcsSignature(Pattern=-10)});
sig = radarSystem.RadarTransceiver(tgt2,time);
sig = radarSystem.downsampleIF(sig);

% Boresight Range-Doppler Processing
rgdpSynthetic = radarSystem.boresightRD(sig);
[M,I] = max(rgdpSynthetic,[],'all'); % find peak in range-Doppler
[rangeIdx,dopIdx] = ind2sub(size(rgdpSynthetic),I);
peakLocation = [rangeIdx; dopIdx];
normalizedMagSynthetic = mag2db(abs(rgdpSynthetic)) - max(mag2db(abs(rgdpSynthetic(:))));

% Angle Processing
polarBfSynthetic = radarSystem.getBfRangeAnglePatch(sig,peakLocation,rngBins,true);

Real-World Data

Load in real-world data collected using the same configuration file discussed above. This data has already been rearranged so that the received signals from transmitter one and transmitter two are concatenated.

realWorldRadarData = load('realDataExample.mat').movingReflectorData;

Remove the static clutter from the real data so that the detected peak is made up primarily from the moving corner reflector. To remove the static clutter, subtract the mean across the slow-time dimension of the datacube.

realWorldRadarData = realWorldRadarData - mean(realWorldRadarData,3);

Range Doppler Processing

Apply the same radar processing as was used for the synthetic data generated from the digital twin by computing the range-Doppler response after summing the received channels. Then, plot the normalized magnitude.

realWorldSigSumChannels = squeeze(sum(realWorldRadarData,2)); % beamform toward boresight
[rgdpRealWorld,rngBins,dpBins] = radarSystem.RangeDopplerProcessor(realWorldSigSumChannels);
normalizedMagRealWorld = mag2db(abs(rgdpRealWorld)) - max(mag2db(abs(rgdpRealWorld(:))));

Plot the normalized magnitude of the range-Doppler response for both the synthetic and the real radar data.

helperCompareRangeDoppler(normalizedMagRealWorld,normalizedMagSynthetic,dpBins,rngBins)

Figure contains 2 axes objects. Axes object 1 with title Range-Doppler Magnitude of Synthetic Data, xlabel Doppler (m/s), ylabel Range (m) contains an object of type image. Axes object 2 with title Range-Doppler Magnitude of Real-World Data, xlabel Doppler (m/s), ylabel Range (m) contains an object of type image.

Test the SNR of the synthetic and real data by comparing the peak value with the median value of the range-Doppler response

syntheticSNR = max(mag2db(abs(rgdpSynthetic(:)))) - median(mag2db(abs(rgdp(:))))
syntheticSNR = 
72.4739
realWorldSNR = max(mag2db(abs(rgdpRealWorld(:)))) - median(mag2db(abs(rgdpRealWorld(:))))
realWorldSNR = 
72.0072

Angle Processing

Reformat the data such that the channel dimension is set to the number of receive antennas (4) and the chirps from each transmitter are interleaved with each other. Then use the getBfRangeAnglePatch method just as we did for the synthetic data. Note how similar the beamwidth and sidelobe levels are in the real and synthetic data.

realWorldRadarData = helperReformatData(realWorldRadarData);
realWorldBf = radarSystem.getBfRangeAnglePatch(realWorldRadarData,peakLocation,rngBins,true);

figure;
plot(-90:90,mag2db(realWorldBf/max(realWorldBf)))
hold on;
plot(-90:90,mag2db(polarBfSynthetic/max(polarBfSynthetic)),'r')
legend({'Synthetic Data','Real-World Data'},'Location','southeast')
xlabel('Angle (deg)')
ylabel('Normalized Magnitude (dB)')
title('Beamforming Comparison')
axis padded

Figure contains an axes object. The axes object with title Beamforming Comparison, xlabel Angle (deg), ylabel Normalized Magnitude (dB) contains 2 objects of type line. These objects represent Synthetic Data, Real-World Data.

Summary

This example walks you through modeling a TI mmWave radar directly from the radar's configuration file and simulates custom electronic components that model features like TDM and dechirping. It also demonstrates an example processing pipeline for the radar data. Finally, the example shows that the synthetic data matches the real-world data very closely.

References

[1] Scheer, Jim, and William A. Holm. "Principles of modern radar." (2010): 3-4.

[2] Bechter, Jonathan, Fabian Roos, and Christian Waldschmidt. "Compensation of motion-induced phase errors in TDM MIMO radars." IEEE Microwave and Wireless Components Letters 27, no. 12 (2017): 1164-1166.

Helper Functions

function dataOut = helperReformatData(dataIn)
    % put it in format such that there are only 4 channels and the chirps
    % are from alternating transmitters 
    dataOut = zeros(size(dataIn,1),.5*size(dataIn,2),2*size(dataIn,3));
    dataOut(:,:,1:2:end) = dataIn(:,1:4,:);
    dataOut(:,:,2:2:end) = dataIn(:,5:end,:);
end

%% Plotting Helper Functions

function helperPlotBfOutput(bfOut,tgt1,xBounds,yBounds,cartData)
    figure; 
    subplot(2,1,1)
    plot(-90:90,bfOut')
    hold on; 
    gt = xline(-atand(tgt1.Position(2)/tgt1.Position(1)),'r--');
    xlabel('Azimuth Angle (deg)')
    ylabel('Magnitude (dB)')
    title('Phaseshift Beamformer Output')
    legend(gt,'Ground Truth','Location','northwest')
    
    subplot(2,1,2);
    imagesc(xBounds,yBounds,cartData)
    hold on;
    plot(-tgt1.Position(2),tgt1.Position(1),'pentagram','MarkerSize',10,'MarkerFaceColor','w')
    xlabel('Y Axis (m)')
    ylabel('X Axis (m)')
    title('Cartesian Projection')
    axis xy
    legend('Ground Truth')
end

function helperCompareRangeDoppler(normalizedMagRealWorld,normalizedMagSynthetic,dpBins,rngBins)
    f = figure; 
    f.Position(3:4) = [500 500];
    subplot (2,1,1)
    imagesc(dpBins,rngBins,normalizedMagSynthetic);
    xlabel('Doppler (m/s)')
    ylabel('Range (m)')
    title('Range-Doppler Magnitude of Synthetic Data')
    axis xy
    ylim([0,5])
    c = colorbar;
    c.Label.String = 'Normalized Magnitude (dB)';
    subplot(2,1,2)
    imagesc(dpBins,rngBins,normalizedMagRealWorld);
    xlabel('Doppler (m/s)')
    ylabel('Range (m)')
    title('Range-Doppler Magnitude of Real-World Data')
    axis xy
    ylim([0,5])
    c = colorbar;
    c.Label.String = 'Normalized Magnitude (dB)';
end