PCM telemetry data links are used in a wide array of applications in which data from a variety of sensors needs to be passed over a single communication link to a receiver or host application. In networked applications this task may be easily achieved by collecting all data points into an array or other data structure in your application and passing it over Ethernet (TCP or UDP) to the receiving application. In distributed applications that use a radio link or other non-standard computer connection, the task of preparing the data for communication includes combining the data into a single data stream and modulating the stream into a signal suitable for transmission.
Common telemetry applications include data downlinks from an aircraft or spacecraft and streaming data from remote sites in the energy industry such as oil wells and wind turbines.
2. PCM Telemetry Links
A pulse code modulation (PCM) telemetry link consists of a series of pulses that represent data from the sensor channels, as well as some header and synchronization data that allow the receiver to properly decode the data stream. Data from the various channels is multiplexed into one serial data stream. Due to the nature of telemetry applications, the data rate may vary by channel. For example a pressure reading or vibration data may be encoded at 1000 Hz, while a temperature reading only needs to be at passed at 10 Hz. Allowing for variable data rates per channel maximizes the use of the available communication bandwidth for all the data to be transferred.
The data to be transmitted in the telemetry data stream can be represented by a Current Value Table (CVT). The CVT has an entry containing the most recent data value acquired for each channel.
Figure 1: Example of a Current Value Table
The multiplexed channel order and data rate are can be represented in an Index Matrix (IM). The values in the Index Matrix represent different channels, and the rows and columns of the index matrix represent the order in which the channels are encoded in the data stream. By entering a channel number more or less frequently in the index matrix you can configure the rate at which a channel is encoded in the data stream.
The following figure shows a basic IM where each channel is encoded at a uniform rate.
Figure 2: Basic Index Matrix
To better use the bandwidth of our transfer medium we can choose to transfer the temperature data (channels 0-2) more slowly than the rest of the data. In the example illustrated in Figure 3, we include one temperature channel per row, rotating through the temperature channels in each successive row. So you can see that the pressure and voltage channels are transferred at three times the rate of the temperature channels.
Figure 3: Index Matrix with variable data rates
The actual contents of the index matrix can become increasingly complex with mulitple levels of data rates to represent the needs of the actual application. Typical applications may include hundreds of data channels with index matrices on the order of 64 rows * 128 columns (8192 entries).
During the encoding process each row of the index matrix is converted into a subframe of the data stream. A subframe ID is placed in the data stream preceeding the data to identify each subframe. Each subframe ends with a sequence of bits called the frame synch. The subframe ID and frame synch are used by the receiver to synchronize itself with the data stream and verify that each subframe has been received. The following figure shows the index matrix with the additional fields.
Figure 4: Index Matrix with subframe ID and frame synch
As the application generates the data stream, it sequences through each row of the index matrix and adds the appropriate bits of data corresponding to the field in the index matrix. For each data channel it encounters, it retrieves the current channel value from the CVT and adds that value to the data stream.
The encoding type (modulation) and number of bits per subframe ID, channel value and frame synch, are specific to each application. The most common encoding type is pulse code modulation (PCM) in which each data bit is represented by a state or sequence of states on the transmission medium. In the most basic PCM type, NRZ-L (Non-Return to Zero - Level), each data bit is represented by either a high state or low state. Bi--phase modulation, such as Manchester encoding, is more complex using a two state sequence to represent one data bit.
Once the data has been encoded into a bit stream it may go through further modulation for the transmission medium. For example when using a radio link, a bit stream may be converted into a frequency shift key (FSK) signal which is the signal that is sent from the radio transmitter. FSK is a modulation scheme commonly used for data tranfer via radio downlinks and dial-up modems.
Figure 5: Bit stream below with corresponding Frequensy Shift Key signal above
3. LabVIEW FPGA Application
The LabVIEW implementation of the telemetry data stream encoder consists of a number of different tasks within the LabVIEW FPGA VI. Two smaller tasks manage the interface to the Current Value Table and the Index Matrix. Each of these two data structures is stored in the 16 kB of user memory that can be accessed from the FPGA diagram using the Memory Read and Write functions. The third task tasks uses the CVT and IM to generate the telemetry data stream. It parses through the index matrix and reads data from the current value table in order to generate the data stream. Each of these three task runs independently on the FPGA
Figure 6: Block diagram of the LabVIEW FPGA VI
In order to generate the data stream at very high bit rates we use a single cycle timed loop (SCTL) for the implementation of the Data Stream Generator task. However, in order to use a SCTL for the encoding of the data stream we need to limit the application to using only 16 kB of memory that can be accessed directly from the SCTL. Other memory options such as FIFOs, lookup tables and the memory extension utility can not be used inside of a SCTL. To match the requirements of a particular application we decided to reserve 1000 addresses (2000 bytes) of the user memory for the current value table and use a 64x64 index matrix. The index matrix uses 4096 addresses (8192 bytes) of the user memory. Each data channel value in the CVT has 2 bytes and each value of the index matrix also occupies two bytes to be able to reference the 1000 possible channels we may use.
If an application does not require a high data stream rate, you can use a regular While loop instead of the SCTL for the data stream generator and then access the total memory available on your device using the memory extension utility.
4. Current Value Table and Index Matrix
For this application we have only implemented a write interface from the host application to the Current Value Table and Index Matrix. The host application is not able to read back the current data in these data structures, but this functionality can be added with some extra code.
Values in the Current Value Table are updated from the host application by setting the address within the CVT (CVT Index), the new value, and then toggling a Boolean control to update the value. The new value is written to the appropriate address in the user memory using the Memory Write function. These values may be updated randomly and sporadically while the application is running. Instead of updating these values from the host application, you can add some code on the FPGA to update them directly based on different inputs such as analog and digital signals, PWM inputs or quadrature signals.
The values in the Index Matrix are normally written only once at the beginning of the application or when the configuration of the data stream is changed. To improve the performance of this configuration process, the code is optimized to index through the IM with each write operation. The host application first applies a reset to the interface which resets the internal address register. Then consecutive values of the index matrix are passed to the interface code and written to the user memory. The address space of the Index Matrix is offset by 1000 from the start of the user memory so this offset is applied to the address before writting data to the IM.
Figure 7: LabVIEW FPGA code to update the Current Value Table and Index Matrix
5. Bit Stream Encoding
The data stream generator is implemented using a single cycle timed loop. The basic operation of the generator is to read a channel number from the index matrix, read the value of the corresponding channel from the current value table and then output the value as part of the bit stream. Data subframes are formed from each row of the index matrix by pre-pending a subframe ID and appending a Frame Synch at the end of each row.
To implement these steps in the Single Cycle Timed Loop we are using a simple state machine that steps through the different parts of this sequence. The following diagram shows the order of states within the state machine. A Wait/Update Ouput state is used to control the timing of the state machine and update the physical digital line. This state is called whenever a state is ready to update the digital output line and before the state machine goes to the next major step in the sequence. Constants are used in the diagram to control the number of times the Wait step loops on itself before moving onto the next step and thereby controls the bit rate of the data stream.
The following diagrams show the different steps of the state machine.
The Idle state (figure 9) resets the output lines and waits for the data stream to be enabled from the host application.
Figure 9: Data Stream Generator and Idle State
When the data stream is enabled the state machine transitions to the Subframe ID state (figure 10), which generates the subframe ID bit sequence. The subframe index is stored in a shift register as a 16-bit integer. The Subframe ID state sequentially selects each of the 16 bits to be generated on the digital output. As the last bit is generated the Subframe ID state increments the subframe index. The Subframe ID state calls the Wait State (described below), which updates the digital output for each bit value. The Wait state returns to the Subframe ID state until all 16 bits have been generated.
Figure 10: Subframe ID state
The Wait state (figure 11) is a core piece of the state machine and is called by a number of other states. It serves two purposes; to update the digital output for each bit of data and to add a delay for controlling the bit rate. The Wait state is called once per data bit. The next state is passed to the Wait state by the state calling it. In figure 10 you can see that the next state will be the Subframe ID until the SF ID sequence is complete. At that point, the next state is the Data-Read Index state.
The Wait state counter is initialized from the calling state and controls the number of times the Wait state loops on itself. In this example there are 40 iterations of the single cycle timed loop between updates of the digital output to generate a bit rate of 1 Megabit/s.
After half the iterations of the Wait state, the clock output is updated. On the last iteration of the Wait state, the clock and data lines are updated with the most recent bit passed to the Wait state. Then the Wait state calls the next state.
Figure 11: Wait and Update Digital output state
After the subframe ID has been generated on the output, the state machine generates the row of channel values selected by the Index Matrix. For this purpose the current position in the Index Matrix is read to retrieve the channel number. Then the value of the channel is read from the Current Value Table. Lastly we need to index through each of the bits in this value and pass them to the Wait state to generate on the digital outputs. After generating the last bit of a value we increment the current position in the Index Matrix and repeat this process.
Due to the optimization routines in the Single Cycle Timed Loop we can only use one Memory Read function inside of the SCTL. However for the task described in the previous paragraph we need to read two different values from the user memory (Index Matrix and Current Value Table). Therefore we need to split this task into multiple states of our state machine (figure 12, 13, and 14). The Read Memory function is placed outside of the Case structure (see figure 11) so that it can be used by any state and pass the value it reads to the following state. The first two states in this task determine the memory addresses to read for the Index Matrix and Current Value table. The last of the three states manages the indexing of the bits in the channel value to be generated and passes them to the Wait state. It also handles incrementing the current position in the Index Matrix. The size of the Index Matrix (64x64) is encoded in this state using constants for the row and column limits.
Figure 12: Data - Read Index Matrix state
Figure 13: Data - Read Current Value Table state
Figure 14: Data - Output state
After a row of values has been generated the state machine goes to the Frame Synch state (figure 15). This state generates a set sequence of bits which are added at the end of each row of data to allow the receiver of the telemetry stream to synchronize itself with the stream and verify proper transmission. In this example we are using a sequence of 24 bits which is contained in a numeric constant (see figure 9) on the diagram. This state indexes through the 24 bits of the constant passing them to the Wait state to be generated on the digital output.
After the Frame Synch is complete the state machine goes back to the Subframe ID state or the Idle state depending on the Stream Enable Boolean.
Figure 15: Frame Synch state
6. Host Application
The host interface of the PCM Telemetry FPGA VI is used to configure the Index Matrix and to update the data in the Current Value Table. The following figures show a very simple host application. In the first diagram the Index Matrix is initialized and the data in the Current Value Table is reset to zeros. The 64x64 Index Matrix is assigned values 0 - 63 for each row. Therefore the data in each subframe will be identical on the output and only values for channels 0-63 will be used.
Figure 16: Host Application - Initialization of the Index Matrix and Current Value Table reset
The second frame of the sequence runs continuously until the host application is stopped. Using the Stream Enable control of the FPGA VI interface the data stream is turned on and off. Values in the Current Value Table are updated by specifying a channel (CVT Index) and value for the CVT and then toggling the CVT Update control.
Figure 17: Host Application - Updates of the Current Value Table
Using the example shown in this document a developer can quickly customize the code for their own PCM telemetry application. Depending on the needs of a specific application a developer may choose to divide the 16 kB of memory (8192 * 16-bit addresses) between the Current Value Table and Index Matrix in other ways. In this example we have allocated enough memory space for the Current Value Table to store 1000 16-bit channels. To be able to reference all 1000 channels each entry in the Index Matrix must be a 16-bit value. This limits the number of entries in the Index Matrix to 7192 and we choose to define a 64*64 index matrix (4096 elements). In applications with channel counts less than 256, you can use 8-bit addresses in the Index Matrix, doubling the possible number of entries in the Index Matrix. We then have enough memory for a significantly larger Index Matrix. With 256 channels (512 bytes of channel data), we have enough memory for 15872 entries in the index matrix.
The output of this example is a pure digital PCM bit stream that can be further modulated or encoded using a range of techniques including bi-phase (Manchester) encoding pulse width modulation (PWM) or frequency shift keying (FSK).