A first example on real hardware

This section refers several commands provided by the device drivers. Use the Software and Device Drivers page as a reference while working through this example.

Alright, let the fun begin. Let's start with the simplest possible test: transmit a waveform from SDR node1, and receive it on node2. Set up two SDR nodes facing each other, with a few feet spacing between them. Open MATLAB, and navigate to the Release_revA_v1.0 directory.

Open the connection to the SDRs

Run the script open_sdr.m, which has been shown below.

node1 = sdr();node1 = node1.zcu111_open('', 'trx-0002', 101);node1.tx_blob = zeros(4, 8192);node1.zcu111_wr_data_blob;node1.zcu111_rd_data_blob();node1 = node1.piradio_load_cal_factors();
node2 = sdr();node2 = node2.zcu111_open('', 'trx-0003', 102);node2.tx_blob = zeros(4, 8192);node2.zcu111_wr_data_blob;node2.zcu111_rd_data_blob();node2 = node2.piradio_load_cal_factors();

This script creates two sdr objects called node1 and node2. The zcu111_open function is used to open all the required sockets to the SDR. tx_blob is initialized such that the TX waveform for all 4 channels is zeros of length 8192. Note that the zcu111_open function accepts three parameters: a) the IP address of the node (this was set while loading the SD card); b) the name of the transceiver board (this is written on the transceiver board using a label); and c) the figure number in MATLAB that is dedicated to seeing what this particular node is transmitting and receiving. So, node1 has an IP address of, has a name of trx-0002, and uses MATLAB figure 101 to show what it is up to. zcu111_wr_data_blob is used to write tx_blob to the FPGA, thereby ensuring that the baseband signals to all 4 TX chains is the NULL waveform. zcu111_rd_data_blob is used to make a dummy read to clear the receive side buffers (this is not strictly required). Finally, piradio_load_cal_factors is used to load the calibration factors from file; this will be explained in detail in a separate section on calibration, but ignore this for now.

Configure the Pi-Radio Transceiver Board

Now that the SDRs have been opened, the next step is to configure the Pi-Radio transceiver board (i.e., the LMX2595 LO chip, the HMC6300 up-converters, and the HMC6301 down-converters) on the SDRs. Run the config_lmx_hmc.m script. this configures the Pi-Radio transceiver board on node1 and node2; only the relevant parts corresponding to node1 have been annotated and shown below.

% 1. Power down the LMX and HMC chipsnode1 = node1.piradio_hmc6300_pdn();node1 = node1.piradio_hmc6301_pdn();node1 = node1.piradio_lmx_config('pdn');

% 2. Configure the clocking. Do this twice to make sure the LMX2595 is working correctly.node1 = node1.piradio_lmx_config('58ghz');node1 = node1.piradio_lmx_config('58ghz');
% 3. Configure the 6300 and 6301. Set attenutations appropriately.node1 = node1.piradio_hmc6300_config(9, 'hmc6300_registers');node1 = node1.piradio_hmc6301_config(9, 'hmc6301_registers');node1 = node1.piradio_hmc6300_set_att(0, 0);node1 = node1.piradio_hmc6301_set_att(18, 0, 0);

% 4. Make sure that the node is transmitting nothing. Do a dummy read.node1.tx_blob = zeros(4,8192);node1 = node1.zcu111_wr_data_blob();node1 = node1.zcu111_rd_data_blob();

Refer to the previous section on "Software and Device Drivers" for more information about the various commands issued. In step 1, the LMX and HMC chips are powered down. In step 2, the LMX chip is powered on and configured to provide an LO signal corresponding to an RF center frequency of F =58 GHz. Note that for a desired RF center frequency of F GHz, the LMX is configured to provide an LO of (F/3.5) GHz; the HMC chips will use this external LO to generate the correct LO at F GHz (look at the HMC6300 and HMC6301 datasheets for more information). In step 3, the HMC chips are configured (i.e., powered on) using a default register values. The TX and RF attenuations are also set appropriately. Finally, in step 4, the TX baseband buffer on the RFSoC is cleared (set to all 0s), and a dummy read is performed to clear the RX-side buffers (this step is not strictly required).

Transmit a waveform from one SDR and receive it on another

Run the script simple_test, which uses one SDR as the TX and another SDR as the RX. This script transmits a single tone from each TX channel sequentially; in each case, the RX will capture the waveforms on all 4 channels, and we will analyze them. The important parts of simple_test.m are shown below.

% 1. Which is the TX and which is the RX node?node_tx = node1;node_rx = node2; % 2. Initialize the TX waveformN = 8192;scToUse = 500;tx_fd = zeros(1, N);tx_fd(N/2 + 1 + scToUse) = 1+1i;tx_fd = fftshift(tx_fd);tx_td = 1*N*N*ifft(tx_fd); % 3. Transmit the waveform from TX channel txIndex. All other channels are% silent. In each case, receive 10 times, and see what we get on each RX% channel.iterations = 10;for txIndex = 1:4 node_tx.tx_blob = zeros(4, N); node_tx.tx_blob(txIndex, :) = tx_td; node_tx.zcu111_wr_data_blob(); for iter = 1:iterations node_rx = node_rx.zcu111_rd_data_blob(); % Add your own code to process rx_blob (size 4 by 8192) endend

Step 1 initializes the TX as node1, and the RX as node2. Step 2 generates a single tone sinusoid using OFDM with N (i.e., the FFT size) set to 8192. All subcarriers are set to 0, except subcarrier with index scToUse set to 500. The frequency domain waveform is tx_fd, and the time-domain waveform tx_td is obtained through a simple IFFT and some scaling. We assume you are familiar with MATLAB's fft, ifft, and fftshift operations.

In step 3, we pick one TX channel txIndex at a time, and populate tx_blob corresponding to that txIndex; all other channels have their time-domain complex baseband waveforms set to 0. These waveforms are written to the FPGA using zcu111_wr_data_blob. Read the waveforms from the RX node iterations = 10 times using zcu111_rd_data_blob. The received complex baseband time-domain waveform is available in rx_blob, and you can process this in an way that you please. Note that the other loop runs for all 4 TX channels txIndex.

During execution of this script, turn your attention to Fig. 101 and 102 (recall in the open_sdr script, node1 and node2 were configured to plot out certain information in MATLAB figures 101 and 102 respectively). Expand these figures to a larger size, since they contain many sub-plots.

MATLAB figure 101 (corresponding to node1) during the execution of simple_test.The Figure has 4 rows and 4 columns. Row #1 shows the time-domain waveform transmitted by the node (one column for each TX channel). Similarly, row #2 shows the spectrum of the transmitted waveform (linear scale). Row #3 shows the time-domain waveform received by the node; and finally, row #4 shows the received spectrum (there is no signal; only noise). For Row #1 and #2, the columns correspond to TX channels 1-4. For Row #3 and #4, the columns correspond to RX channels 1-4. Note that row #1 and #2 automatically update when zcu111_wr_data_blob is executed on that node; symmetrically, row #3 and #4 automatically update when zcu111_rd_data_blob is executed on that node.

Above is an example of what we might observe in MATLAB figure 101 (corresponding to the TX node, node1) while running simple_test. Looking at row #1, we can see that TX channels 1,2,4 are inactive, while channel 3 is currently active. Using the MATLAB zoom functionality, zoom into this waveform and observe that it is indeed a simplex sinusoid. Similarly, row #2 shows the spectrum of the transmitted waveform. The X-axis is subcarrier index (-4096 through +4095) and the Y-axis is the power in linear scale. Using the MATLAB data tips, observe that the signal is indeed present on subcarrier index +500. Row #3 and #4 correspond to the RX side of node1, so we will ignore these for now.

During execution of simple_test, you would have observed MATLAB figure 102 (corresponding to the RX node, node2) look something like this below.

MATLAB figure 102 (corresponding to node2) during the execution of simple_test. Observe that row #1 and #2 are flat, indicating that nothing is being transmitted from any of the 4 TX channels on node2. However, row #3 that contains the time-domain received complex baseband signal shows that some signal indeed being received by all 4 RX channels on node2. Zoom in to observe the waveforms. Row #4 shows the received spectrum (in dB scale). Use MATLAB data tips to see the dominant peak at subcarrier index +500, with an undesired sideband at subcarrier index -500; we will discuss sidebands and how to calibrate them out in a different section.

Play around with this example

This simple example has shown you how to generate a waveform, transmit it, capture the received waveforms, and visualize them. Try a few different things (and repeat the execution) to get familiar with this simple example. Note that you should run open_sdr and config_lmx_hmc only once, right at the beginning. After that, you can run simple_test as many times as you like. Here are things to try on your own:

  1. Make node2 the TX and node1 the RX.
  2. Transmit and receive using the same node (say, node1) to measure self interference.
  3. Change the frequency of the transmitted baseband signal to a different subcarrier.
  4. Change the power in the transmitted signal by changing the scaling factor (see the code where tx_td is created). Alternatively, you can change the power in tx_fd, prior to the IFFT. In all cases, ensure that the resulting time-domain signals are located (digitally) between -32k and +32k, otherwise they are out of range for the DACs. However, keep them between -20k and +20k to prevent saturation of the mmWave up-converter chips.
  5. Modify the code to simultaneously transmit four different tones, one from each TX channel.
  6. Modify the code to transmit two tones (from one TX channel) instead of just one tone. Observe the intermodulation products. Change the scaling factor and observe the behavior of the intermodulation peaks.
  7. Observe that row #3 contains the received power (measured digitally on the time-domain signals, in dB scale). Deactivate the TX to observe the noise floor. Then reactivate the TX to measure the received power. Calculate SNR. Compare with the SNR in the desired band in frequency domain (using data tips to measure the power in the required subcarriers).

Refer to the page on Software and Device Drivers for more information on the commands used in this example. Most importantly, have fun!

Close the connection to the SDRs

When you've had enough, we need to release the sockets that were previously opened by open_sdr. Doing this is simple: just run the script close_sdr. This script simply calls zcu111_close for node1 and node2. We are done.