Calibration of an SDR, while not being rocket science, still needs to be done carefully. We recommend that you read this paper before running the calibration routines on your SDR nodes.

Traditionally, calibration has relied on super expensive lab equipment like spectrum analyzers and signal generators. Procuring such equipment, especially at the mmWave frequencies, can easily cost you a quarter of a million dollars. Honestly, how many research groups have access to such gear?

To work around this, we have implemented "two node" calibration that does not rely on any expensive equipment. For example, when the TX (RX) array on node1 is being calibrated, node2 is used as a reference receiver (transmitter). This way, two nodes can be calibrated in a pairwise manner. This section does not describe the calibration algorithms in detail (refer to the paper for that), but it does describe how to run the calibration routines.

Array Calibration

To calibrate the TX and RX arrays on both nodes (node1 and node2), simply run the script wideband_twonode_cal.m (make sure you run open_sdr first, in case you haven't already done so). This script has been shown below.

% 1a. Calibrate the TX array on node1node_a = node1;node_b = node2;wideband_tx_twonode_timing_measurement_v4;node1 = node_a;node2 = node_b;clear node_a node_b;
% 1b. Calibrate the TX array on node2node_a = node2;node_b = node1;wideband_tx_twonode_timing_measurement_v4;node2 = node_a;node1 = node_b;clear node_a node_b;
% 2a. Calibrate the RX array on node1node_a = node1;node_b = node2;wideband_rx_twonode_timing_measurement_v4;node1 = node_a;node2 = node_b;clear node_a node_b;
% 2b. Calibrate the RX array on node2node_a = node2;node_b = node1;wideband_rx_twonode_timing_measurement_v4;node2 = node_a;node1 = node_b;clear node_a node_b;
% 3a. Calibrate the per-channel Magnitude on node1 (TX) and node2 (RX)node_a = node1;node_b = node2;wideband_mag_calibration_v2;node2 = node_b;node1 = node_a;clear node_a node_b;
% 3b. Calibrate the per-channel Magnitude on node2 (TX) and node1 (TX)node_a = node2;node_b = node1;wideband_mag_calibration_v2;node1 = node_b;node2 = node_a;clear node_a node_b;
% 4a. Measure the TX self-rotations on node1node2 = node2.piradio_hmc6300_set_att(20,15); % MAX out the attenuation on node2node_a = node1;wideband_self_interference_fd_measurement_v1;node1 = node_a;clear node_a;node2 = node2.piradio_hmc6300_set_att(0,0); % RESTORE the attenuation on node2
% 4b. Measure the TX self-rotations on node2node1 = node1.piradio_hmc6300_set_att(20,15); % MAX out the attenuation on node1node_a = node2;wideband_self_interference_fd_measurement_v1;node2 = node_a;clear node_a;node1 = node1.piradio_hmc6300_set_att(0,0); % RESTORE the attenuation on node1
% 5. Save cal factors to filenode1 = node1.piradio_save_cal_factors();node2 = node2.piradio_save_cal_factors();

In step 1, the script wideband_tx_twonode_timing_measurement_v4 is used to calibrate the per-channel timing offsets and the per-channel LO phase offsets on each TX channel. This script operates on two nodes: node_a as the node under calibration (NUC) and node_b as the reference (REF). Thus, step 1a and 1b calibrate the TX array on node1 and node2 respectively. The paper contains details on why we need to calibrate out the timing and phase offsets, along with the algorithms that are used to do it. You can easily correlate the algorithms listed in the paper with the code in wideband_tx_twonode_timing_measurement_v4.

In step 2, the script wideband_rx_twonode_timing_measurement_v4 is run. This calibrates the RX arrays on both nodes, symmetrically to how step 1 calibrated the TX arrays. During the execution of step 1 and 2, you will observe MATLAB figure 1 displaying some outputs. These outputs are shown and explained in the calibration paper; you can also look at the source code to figure out what's being plotted.

In step 3, the script wideband_mag_calibration_v2 is run. This script measures the differences in conversion gain across the various TX chips in node_a, as well as the differences in conversion gain across the various chips in node_b. These measured factors are then used to calibrate out the per-channel variations in conversion gain. Step 3a and 3b swap which nodes are node_a and node_b, such that by the end of step 3, both node1 and node2 have the magnitudes (i.e., conversion gain) of their TX and RX arrays calibrated. During the execution of this step, you will observe that MATLAB figure 2 shows the progress of the magnitude calibration, as illustrated in the paper.

Let us move on to step 4. We have also implemented some techniques for self-calibration. Once wideband_twonode_cal is run and the calibration factors are known, a set of self-calibration routines can be run to periodically re-calibrate the SDR nodes quickly in-place, without having to move them back into boresight. For this self-calibration to work, we need to measure the self interference system responses in each SDR node. Essentially, this involves measuring the channel from each TX channel on a node to each RX channel on the same node; this is done in step 4. In the discussion about self calibration (the next section), we will talk about how these saved self interference factors are used for self calibration.

Finally in step 5, the measured calibration factors are written to file. These can then be reloaded upon power up, and only self-calibration needs to be rerun.

At this point, the two nodes are fully calibrated. Run the beamforming demonstration to look at the beams. The fact that the beams are correctly formed is proof that the arrays are correctly calibrated.

Self Calibration

When the nodes are turned on and the LMX and HMC chips are configured, we can run the full two node calibration (described above). Once this is done, the beamforming demonstration can show that the calibration has in fact been done correctly. However, when we power cycle the SDR nodes (or rerun config_lmx_hmc), we will observe that the beams are incorrect, indicating that the calibration has gone bad. The reason is that each time the HMC chips are configured, the starting phase of the LO multipliers on the chip has an output phase uncertainly. Specifically, a random subset of the TX and RX chips can flip their phase by 180 degrees, with respect to each other. This is what causes the beams to get spoiled. Further, there are additional smaller phase shifts that can be caused by hardware drifts. These need to be measured, and calibrated out.

One can re-calibrate two nodes by placing them back in boresight and rerunning wideband_twonode_cal, but this can be cumbersome, especially if the nodes are now mounted onto something else (like a drone). So instead, upon power cycling, simply run the script wideband_self_cal.m. This script is shown below.

% Self calibrate node1node2 = node2.piradio_hmc6300_set_att(20,15); % MAX the attenuation on node2node_a = node1;wideband_self_cal_phase_v1;node1 = node_a;clear node_a;node2 = node2.piradio_hmc6300_set_att(0,0); % RESTORE attenuation on node2
% Self calibrate node2node1 = node1.piradio_hmc6300_set_att(20,15); % MAX the attenuation on node1node_a = node2;wideband_self_cal_phase_v1;node2 = node_a;clear node_a;node1 = node1.piradio_hmc6300_set_att(0,0); % RESTORE attenuation on node1

This script calls wideband_self_cal_phase_v1, which measures the system responses from each TX channel on the node to each RX channel on the same node, and compares them against what was measured in step 4 in wideband_twonode_cal. This comparison yields an additional set of calibration factors to revert the node's behavior to how it was when the full calibration (i.e., wideband_twonode_cal) was run. Once this is done, rerun the beamforming demonstration to see that the beams do in fact look correct; this will demonstrate whether the self calibration was performed correctly.

A quick note: The self calibration routines are currently not as stable as we would like them to be. Therefore, we highly recommend running wideband_twonode_cal at every power cycle. To save time, you can create a new script duplicating wideband_twonode_cal, and eliminate step 3 and 4. We are still working on getting the self calibration working in a stable and repeatable manner; once that's done, the code will be pushed to GitHub, and this document will be updated to reflect the changes.

IQ Calibration

Quadrature up-converters and down-converters (such as the Analog Devices HMC6300 and HMC6301) will always have IQ imbalances. These imbalances cause leakage from the desired sideband into the undesired sideband. This lowers the EVM of the signal on both the TX and RX side. IQ calibration therefore needs to be run to measure these imbalances, and digitally correct them out. To do this, simply run the script iq_cal.m. This is shown below.

% Perform IQ Alpha cal on the RX array of node1node_a = node1;node_b = node2;iq_cal_rx_v2;node2 = node_b;node1 = node_a;clear node_a node_b;
% Perform IQ Alpha cal on the RX array of node2node_a = node2;node_b = node1;iq_cal_rx_v2;node1 = node_b;node2 = node_a;clear node_a node_b;% % Perform IQ Alpha cal on the TX array of node1node_a = node1;node_b = node2;iq_cal_tx_v3;node2 = node_b;node1 = node_a;clear node_a node_b;
% Perform IQ Alpha cal on the TX array of node2node_a = node2;node_b = node1;iq_cal_tx_v3;node1 = node_b;node2 = node_a;clear node_a node_b;
node1 = node1.piradio_save_cal_factors();node2 = node2.piradio_save_cal_factors();

Performing IQ calibration relies on the offset LO technique to generate clean sinusoids. Traditionally, generating such clean sinusoids has relied on extremely expensive signal generators. As explained in the calibration paper, we implement a poor man's clean sinusoidal generator using the SDR nodes itself; this is done using the offset LO technique. The main intuition behind it is that the reference transmitter has an LO offset from the intended receiver, such that the unwanted sideband from the transmitter is not detectable by the receiver.

In the script iq_cal, the RX side IQ imbalances are corrected on both nodes. These imbalances include: a) the conversion gain factors on the I and Q channel; and b) the phase difference between the cosine and sine components of the quadrature carrier (this is ideally 90 degrees, but real hardware shows significant variations). Symmetrically, the TX side IQ imbalances are also measured and calibrated out. In these techniques, the power in the undesired sideband (relative to the power in the desired sideband) is used as a metric to see how bad the imbalance is. Conversely, the ratio of the desired sideband to the undesired sideband demonstrates how balanced the IQ mixer is.

You will notice that iq_cal takes > 1 hour to execute, this time being dominated by the TX-side IQ calibration. It is important to perform this calibration at least once. Once this is done, the IQ calibration factors are written to file, and these are reloaded upon power cycling. It is not necessary to rerun iq_cal upon power cycling, since these factors are very stable. Please refer to the calibration paper that explains in detail the causes of IQ imbalances, along with the algorithms to measure and correct them out.