This article describes how DSSS sequences are generated on EFR32 devices. It also demonstrates (for multiple configurations and modulation types) how these chip codes can be read out when transmitting a DSSS-coded frame. The DSSS symbols captured with this technique can then be fed directly to a signal generator to emulate an EFR32 transmitter.
In the following sections, we'll address questions like:
Our primary goals are to help you understand DSSS configuration within Simplicity Studio v4, and how, in practice, the resulting DSSS signal can be captured without any laboratory equipment.
Application note AN971: EFR32 Radio Configurator Guide for RAIL in Simplicity Studio v4 provides a great summary on DSSS. Thus, we'll present here only the critical elements as a quick reference. Experienced readers may skip this section, while those curious for additional detail are invited to review AN971.
Direct Sequence Spread Spectrum takes a bit or group of bits and replaces them with a longer chip sequence, in order to impact the spectral properties of the transmitted signal. It implies much higher bit rate (which is called chip rate hereafter), and thus increases bandwidth - hence the name: spread spectrum. At the receive side, the received chip sequence is replaced by the corresponding bit or group of bits.
One of the most essential attributes of DSSS coding is the spreading factor, which is defined as the quotient of the chip rate and the bit rate.
As suggested by the above characteristics, DSSS does not affect the transmission time for the same transmitted packet.
The chip sequences are generated by a given algorithm (which will be covered in a following section) and the Base chip sequence.
The chip sequences should be chosen to have weak correlation (that is, they should look different), so that even if the chip sequences are not demodulated perfectly on the receiver, the corresponding bit values can still be detected. This is how we get the processing gain, which, in practice, will improve the sensitivity.
Note: In theory the processing gain is maximized by increasing spreading factor, but in practice the sensitivity performance degrades for spreading factors higher than 8.
Another advantage of DSSS is immunity against narrowband interferers. This can be explained by the decreased information/frequency factor: even if there are unwanted signals on the occupied bandwidth, the vast majority of the spread spectrum isn't affected by narrowband signal interference.
Also, DSSS can enable the use of relatively inaccurate XOs (crystal oscillators).
Based on the above traits, we can conclude that DSSS is a good fit for PHYs that have relatively low data rates.
The Bitrate field on the radio configurator GUI expects the actual bit rate (as opposed to the chip rate). Note that even when applying DSSS to 4FSK or OQPSK modulation, this field should specify the bitrate (not the symbol rate).
Although the chip rate is automatically calculated by the radio configurator, don't forget to multiply the nominal frequency deviation (specified by the Deviation field) by the DSSS Spreading Factor.
The bit endianness configuration for each part of the packet (preamble, sync word, payload, CRC) is applied on the unencoded bit/bit groups. Therefore, in DSSS encoding cases we say LSCS (Least significant chip sequence) or MSCS (Most significant chip sequence). The bit endianness setting isn't applied on the chip sequences, but on the bits/bit groups of each bytes of the packet before encoding. The chip sequences are always sent out in LSB first order.
Note: don't worry if the above is unclear, we'll further explore its meaning through examples in the second half of this article.
The DSSS Chipping Code Base field expects its value in hexadecimal format. A good selection for Base is one where the generated chip sequences are weakly correlated.
Note: the radio configurator does not warn when an incorrect Base format is selected: it won't check whether all the generated chip sequences are different.
Only predefined combinations of the spreading factor and the chipping code length are accepted by the configurator. For valid combinations, visit AN971.
The Preamble Base Pattern field is irrelevant when DSSS is enabled, as the preamble bits are always substituted with the Base chip sequence.
Sync word search is performed on the already decoded bit stream. Hence, the sync detector block won't look for the DSSS signal format, but the originally-encoded bits themselves.
Note: it may be necessary to tune multiple advanced configuration fields in order to optimize the PHY's performance. You can file a support ticket, and we'll reach out to you with guidance for your particular case.
AN971: EFR32 Radio Configurator Guide for RAIL in Simplicity Studio v4 summarizes how DSSS can be configured. However, it is worthwhile to extend that documentation with a review of practical cases - for example to consider configurations with longer chip sequences. We present multiple such cases below - and also include a script with which you can generate all the chip sequences for a given configuration.
In this section, we show how to use the DOUT and DCLK signals to understand how the payload is encoded into DSSS chip sequences.
RAIL Tutorial: Debugging serves as an excellent tutorial for introducing how such PRS signals can be put out on any GPIO pin.
In the examples that follow, the payload is
0x0F0123456789ABCDEF00005555AAAAFF for all PHYs, just to ensure that all bit-groups show up in the payload. The packet also contains preamble bits, sync word and CRC sequence as detailed in each example.
The DOUT signal represents the deviation's sign (in the case of a phase/frequency modulated signal), and therefore the bits of the packet in the 2(G)FSK case. For OQPSK modulation, it's a little bit problematic to retrieve the chip sequences from the DOUT signal. An algorithm will be presented (below) that addresses this problem.
Note: in 4(G)FSK modulation DOUT doesn't distinguish between the inner and the outer deviations, making the DOUT signal unsuitable to expose the chip sequences.
Note: in MSK modulation, the DOUT signal will look like exactly the same as what is seen when using 2(G)FSK modulation.
First we'll take a look at a built-in config - choose the
868M 2GFSK 38.4Kbps 20K PHY from the
Base Profile. This config doesn't use DSSS coding, but it is the simplest example to show how to interpret DCLK and DOUT signals:
Frame Bit Endian field on the configurator GUI is set to
LSB_FIRST, the payload has been sent out in this order. However, the sync word, the preamble and the CRC are represented as we configured it (these were set to their defaults) on the GUI.
Our logic analyzer software (Saleae) can visualize the parsed datastream by setting up a SPI decoder on the captured signals. If you take a closer look at the payload (after 0xF68D sync word), you'll see that the payload has been sent out in LSB first order as expected.
Note: the DOUT signal is sampled on the rising edge of DCLK.
Now, enable DSSS encoding on this PHY. Set the DSSS Chipping Code Length to 8, DSSS Spreading Factor to 4, and the DSSS Chipping Code Base to 0x4D. Don't forget to multiply the deviation by the spreading factor!
Note: as long as we're observing the transmitter only, there's no need care about the performance of the PHY.
According to AN971, this configuration generates these four chip sequences:
|Bits (bin)||Chip sequence (hex)||Chip sequence (bin)||Operation (takes place on the LSB first ordered representation)||Chip sequence in LSB first order (bin)||Chip sequence in LSB first order (hex)|
|00||4D||0100 1101||Base||1011 0010||B2|
|01||D4||1101 0100||Base right shifted by 4||0010 1011||2B|
|10||B2||1011 0010||Base inverted||0100 1101||4D|
|11||2B||0010 1011||Base inverted and right shifted by 4||1101 0100||D4|
Note: from this point on we'll represent the chip sequences only in LSB first bit order.
Generate the new configuration and build the application. Once the binary is ready, download it to the target device and transmit a packet while the logic analyzer is waiting for a trigger on the GPIO to which DOUT signal is assigned. Once the DOUT and DCLK signals are captured, export the SPI decoder's result and analyze it.
The first striking thing you'll notice is that, though the transmission time is the same as before, there are many more edges on DCLK. The A* Timing Measurement Pair shows a 4x higher frequency for this configuration compared to the default config. This (4) equals the DSSS spreading factor, so you may then suspect that we can directly capture the chipping sequences on DOUT.
That's exactly the case! Amazingly, using only the transmitter we can capture the chip sequences for a given packet.
Taking a closer look at the parsed DOUT capture, we first see 20 copies of 0xB2. This is the Base chip sequence in LSB first order - just as we expect. According to AN971 the Bit/Chip sequence is 2 for this DSSS configuration, so 20 repeated chip sequences is consistent with the preamble configuration length (40 bit).
Then we expect a 16 bit sync word (0xF68D), which is encoded to 32 chips. Take the next 8 bytes and decode them to bit groups.
|Chip sequence [hex]||D4||D4||4D||2B||2B||B2||D4||4D|
How can we explain this behavior? We should first understand how the sync word is represented in the radio's register.
What we set in the configurator GUI is what we would like to see when we transmit the packet w/o DSSS encoding, so it should be represented in the transmission order (from left to right). However, the sync word is always sent out in LSB first order, so the configurator swaps the bits and fits them to the right of the 32-bit wide register. Therefore, if you set up a longer sequence than what you configured as sync word's length, the extra rightmost bits (on the GUI) won't be taken into account.
In the next step the DSSS encoder will fetch up Bit/Chip sequence (2 in this case) long bit groups in each byte and will encode them, keeping the bit groups' endianness.
And as the final step, it will transmit all the chip sequences (per each byte) with the configured byte end bit endianness. This is what we mentioned in the previous chapter, that the bit order settings will applied on the sequences (or on the unencoded bit groups) in each byte, not on the chips in the chip sequences or on only the bits in the payload bytes.
See the steps on the image depicted below:
Take some time to check the same logic on the payload chip sequence below, to completely understand the encoding and transmission order.
If you aren't lost yet, here is one more twist in the story: the CRC is calculated as if it would had been calculated without DSSS encoding, but it is transmitted in the exact same way as we've seen above in the payload's case. Use this algorithm to calculate what will be sent out as CRC:
|Chip sequence [hex]||2B||D4||D4||B2||B2||2B||2B||4D|
For your reference, here is a capture where the payload endianness was set to MSB first:
and its CRC sequence (0x3D94 without DSSS):
While developing this example, we couldn't comprehensively demonstrate that the generator sequence is generated by the algorithm above: we couldn't confirm that the chip sequences should be rotated to the right (and not left), and we're still not sure how the rotation and the inversion operation takes place when increasing the bit groups' value one by one.
Let's now look at a more complicated example, where 16 chip sequences are used.
Set up the following configuration:
In this case there are 16 distinct chip sequences generated, according to the table below:
Note: the Base is already in LSB first order (see the first row).
|Bits (bin)||Operation||Chip sequence in LSB first order (bin)||Chip sequence in LSB first order (hex)|
|0001||Base right shifted by 2||1110010010011011||E49B|
|0010||Base right shifted by 4||1111100100100110||F926|
|0011||Base right shifted by 6||1011111001001001||BE49|
|0100||Base right shifted by 8||0110111110010010||6F92|
|0101||Base right shifted by 10||1001101111100100||9BE4|
|0110||Base right shifted by 12||0010011011111001||26F9|
|0111||Base right shifted by 14||0100100110111110||49BE|
|1001||Base inverted and right shifted by 2||0001101101100100||1B64|
|1010||Base inverted and right shifted by 4||0000011011011001||06D9|
|1011||Base inverted and right shifted by 6||0100000110110110||41B6|
|1100||Base inverted and right shifted by 8||1001000001101101||906D|
|1101||Base inverted and right shifted by 10||0110010000011011||641B|
|1110||Base inverted and right shifted by 12||1101100100000110||D906|
|1111||Base inverted and right shifted by 14||1011011001000001||B641|
Let's again compare actual captured chip sequences to decoded source bits. The Bit/Chip Sequence is now 4 and the preamble is configured to 32 bit length. The sync word is configured to 0x904E and we are using the same payload.
|Chip sequence [hex]||926F|
|Chip sequence [hex]||1B64||926F||F926||49BE|
|Chip sequence [hex]||B641||926F||E49B||926F||BE49||F926||9BE4||6F92||49BE||26F9|
|Chip sequence [hex]||6D90||E49B||49BE||41B6|
I encourage you to try and verify the encoding for this (or any other valid config) on your own, to develop a better understanding and get some practice in.
Now, we'll find out what we can do when OQPSK modulation is used.
It's a bit tricky to get the DSSS chip code base by the transmitter's DOUT signal when using OQPSK modulation; in this case the DOUT signal represents the frequency deviation's sign as well, which won't represent the transmitted bits (or the chip sequences) per se.
Still, we can use the DOUT signal stream if we have an OQPSK signal format. For this task, first gaining some familiarity with how OQPSK modulation works is highly recommended..
Although the DOUT level does not instantaneously indicate what kind of bit is actively being transmitted, all bits/chips can be calculated using both the present level of DOUT (the actual deviation), and its previous value. Let's denote the bits/chips as
C_n and the DOUT signal's level as
n starts from 0. The
n is increased with each rising edge of DCLK.
With these notations
C_n equals to
C_n-1 ^ D_n, if
n % 2 == 0 (so for all even indexed bits/chips) where
^ denotes the XOR operation, and
!(C_n-1 ^ D_n), in case of odd indexes, where
! stands for negation. For
n = 0,
0, and the first bit/chip equals the first deviation's sign.
Similar to the 2FSK case, first we take a look at the payload when the DSSS encoder is disabled.
This is the block diagram for the OQPSK decoder (fed by the first nibble of the preamble):
One can decode the packet using the DOUT signal and the algorithm.
0101over 40 bit)
|Decoded bits [hex]||55||55||55||55||...|
|Decoded bits [hex]||(55)||2D||D4|
|Decoded bits [hex]||(D4)||F0||80||C4||A2||E6||91||D5||B3||F7||00||00||AA||AA||55||55||FF|
|Decoded bits [hex]||(FF)||29||BC|
What if we now apply DSSS on this configuration?
The chip sequences are generated slightly differently than the previous cases where the modulation was not OQPSK. For OQPSK modulation cases, instead of inversion the complex conjugation operation takes place if the bit groups top left bit is
1. See the chip sequences in the table below.
Note: owing to the nature of the OQPSK modulation, inversing all bits in a particular chip sequence would result in the same transmitted signal format except the first chip. Therefore the signal formats would have strong correlation (not desired) if the inversion operation were applied.
|Bits to encode||Operation||Chip sequence in LSB first order (bin)||Chip sequence in LSB first order (hex)|
|0001||Base right shifted by 2||1110101100100101||EB25|
|0010||Base right shifted by 4||1010110010010111||AC97|
|0011||Base right shifted by 6||1011001001011110||B25E|
|0100||Base right shifted by 8||1100100101111010||C97A|
|0101||Base right shifted by 10||0010010111101011||25EB|
|0110||Base right shifted by 12||1001011110101100||97AC|
|0111||Base right shifted by 14||0101111010110010||5EB2|
|1000||Base complex conjugated||1101000001100011||D063|
|1001||Base complex conjugated and right shifted by 2||0100000110001111||418F|
|1010||Base complex conjugated and right shifted by 4||0000011000111101||063D|
|1011||Base complex conjugated and right shifted by 6||0001100011110100||18F4|
|1100||Base complex conjugated and right shifted by 8||0110001111010000||63D0|
|1101||Base complex conjugated and right shifted by 10||1000111101000001||8F41|
|1110||Base complex conjugated and right shifted by 12||0011110100000110||3D06|
|1111||Base complex conjugated and right shifted by 14||1111010000011000||F418|
Again, set up the DSSS configuration, hit the
Generate button, build the example, download, and transmit the same packet while the logic analyzer is listening on the DOUT pin.
We won't detail the OQPSK decoding (you can perform this task, as we demonstrated in the previous example), but will share the results here:
|Chip sequence [hex]||7AC9|
|Chip sequence [hex]||C97A||18F4||18F4||AC97|
|Chip sequence [hex]||F418||7AC9||EB25||7AC9||B25E||AC97||25EB||C97A||5EB2||97AC||418F||D063||18F4||063D|
|Chip sequence [hex]||C97A||418F||8F41||B25E|
In this section, we introduce two helpful python scripts that we've developed:
Note: the scripts are written in Python 3, so you shouldn't run them with Python 2.
The DSSS_sequence_generator.py script expects the DSSS configuration parameters:
It first validates the spreading factor/code length combination according to AN971 (Table 3.5.). There is only one function implemented in this script, which prints out the chip sequences in ascending order. This function is called if the script is ran from a terminal like
The chipping codes are printed out in reversed bit order, just as those that are captured on DOUT.
The OQPSK_decoder.py expects the DOUT signal in hexadecimal format (actually as a python integer variable).
It then calculates the number of nibbles in the whole sequence. This sequence will be calculated by the algorithm defined above. At the end, it prints out the decoded packets in the given format in
CL long chunks.
Note: ensure that the DOUT signal does not start with
0, otherwise the length calculation will be wrong.