Wouldn't it be nice if generating, transmitting, and recording a real-world QPSK signal were easier than deriving its bit error rate (BER) under additive white Gaussian noise (AWGN)? Here at Basis Functional, we sure thought so, which is why we designed the Renni SDR to make this kind of wireless task easier than other SDRs on the market. In this tutorial, we're going to walk through some example Matlab code to help you do just that!
To complete the transmission from source to sink, let's walk through a standard sequence of steps with a few extras thrown in:
- Set parameters like sample rate, center frequency, etc.
- Generate and convert a random bit sequence into QPSK symbols
- Oversample to set the sample rate using a zero-order hold
- Apply a square root raised cosine to maintain the overall power level between transmission and reception and also minimize intersymbol interference
- Write samples to a Midas Blue file (type 1000 complex) for transmission
- Transmit over-the-air via the Renni SDR clearSPEAK application
For all of the details, be sure to examine at the entire Matlab script in all its glory.
For this example, we're going to generate a 65,536 baseband samples of a QPSK signal at 2.42 GHz with a cyclic prefix of 16 samples. Here's some of those settings:
desired_samples = 65536;
upsample_ratio = 8;
fs = 4e6;
cf = 2420e6;
alpha = 0.5;
cp_len = 16;
Note that alpha is a RRC tuning parameter on [0,1].
2. Bits to QPSK symbols
The next step is to generate twice as many random integers on [0,1] as the number of samples desired.
rbits = randi([0, 1], 1, 2*desired_samples);
Then, convert them into a complex number taking on one of four possible values (+/-1 +/- 1j).
rsymbols = 800*(2*(complex(rbits(1:2:end)-0.5, rbits(2:2:end)-0.5)));
Note that the 800 scaling factor is added to properly load the 12-bit digital-to-analog converter (DAC).
3. Oversample and ZOH
Next, create a zero-order hold buffer of the correct length, add the cyclic prefix, and combine to form the zero-order hold symbols set.
zoh_symbols = zeros(1, upsample_ratio*(desired_samples+cp_len));
cp = rsymbols(end-cp_len+1:end);
zoh_symbols(1:upsample_ratio:end) = [cp, rsymbols];
4. Generate the RRC
We're going to rely on a script from another blog for this. Here's the function and here's our call:
rcos = GenerateRaisedCosine(upsample_ratio, alpha);
At this point, we also compute the filter delay that the RRC introduces (1/2 of its length minus one) and filter the symbols.
filter_delay = round((length(rcos)-1)/2);
filtered_symbols = filter(rcos, 1, [zoh_symbols, zeros(1, filter_delay)]);
half_cp_len_upsampled = upsample_ratio * cp_len / 2;
Finally, we maintain phase continuity by removing the symbols at the front that get clobbered by the filter delay and cyclic prefix, while removing the cyclic prefix symbols from the back as well.
filtered_symbols(1:filter_delay+half_cp_len_upsampled) = ;
% remove back cp
filtered_symbols(end-half_cp_len_upsampled+1:end) = ;
5. Write BLUE file
A BLUE file is a specific filetype that is supported by a DSP software called Midas. Renni can also write out to regular binary files, but for the purposes of this demo, we're showing off a capability that many SDRs do not support. Our call references another Basis Functional Matlab file in our GitHub.
WriteBlueFile(file_name, filtered_symbols.', fs, format, cf);
The script also includes some additional plots to assist in visualization and evaluation.
Finally, it's time to transmit. Renni's clearSPEAK application takes over at this point. All you need is to fire up a Renni and use it's intuitive web-based interface to load the Blue file and transmit the waveform over the air. Renni automatically pulls in the recorded data parameters, such as the center frequency and sample rate, to make it easy to transmit. Because of the phase continuity, you can tell Renni to transmit in a loop and the signal will maintain the correct properties for as long as you care to transmit.
For any comments or questions on this tutorial, please reach out to email@example.com