Sample Programs for Creating and Downloading Waveform Data

Creating I/Q Waveform Data  

Downloading I/Q Waveform Data

Creating I/Q Waveform Data

This section examines the C++ code algorithm for creating and downloading I/Q waveform data by breaking the programming example into functional parts and explaining the code in generic terms. This is done to help you understand the code algorithm in creating and downloading the I and Q data, so you can leverage the concept into your programming environment.  

You can use various programming environments to create Arb waveform data. Generally there are two types:

No matter which programming environment you use to create the waveform data, make sure that the data conforms to the data requirements shown in Waveform Data Requirements. To learn more about I/Q data for the PXB, see Understanding Binary Formatted Waveform Data.

Code Algorithm

There are three steps in the process of creating an I/Q waveform:

  1. Create the I and Q data.

  2. Save the I and Q data to a text file for review.

  3. Interleave the I and Q data to make an I/Q file, and swap the byte order for little endian platforms.

For information on downloading I/Q waveform data to a PXB, refer to Downloading and Extracting Waveform Data.

1. Create I and Q data.

The following lines of code create scaled I and Q data for a sine wave. The I data consists of one period of a sine wave and the Q data consists of one period of a cosine wave.

Line

Code – Create I and Q data

 

1

const int NUMSAMPLES= 512;

2

main(int argc, char* argv[]);

3

{

4

short idata [NUMSAMPLES];    

5

short qdata [NUMSAMPLES];    

6

int numsamples = NUMSAMPLES;    

7

for(int index=0; index<numsamples; index++);    

8

{    

9

idata[index]=32768 * sin((2*3.14*index)/numsamples);        

10

qdata[index]=32768 * cos((2*3.14*index)/numsamples);        

11

}    

 

Line

Code Description Create I and Q data–

1

Define the number of waveform points. Note that the maximum number of waveform points that you can set is based on the amount of available memory in the PXB.

2

Define the main function in C++.

4

Create an array to hold the generated I values. The array length equals the number of the waveform points. Note that we define the array as type short, which represents a 16-bit signed integer in most C++ compilers.

5

Create an array to hold the generated Q values (signed 16-bit integers).

6

Define and set a temporary variable, which is used to calculate the I and Q values.

7-11

Create a loop to do the following:

  • Generate and scale the I data (DAC values). This example uses a simple sine equation, where 2*3.14 equals one waveform cycle. Change the equation to fit your application.

  • The array pointer, index, increments from 0–511, creating 512 I data points over one period of the sine waveform.

  • Set the scale of the DAC values in the range of −32768 to 32767, where the values −32768 and 32767 equal full scale negative and positive respectively.

The PXB comes from the factory with I/Q scaling set to 70% ( –3 dB). If you reduce the DAC input values, ensure that you set the PXB scaling to an appropriate setting that accounts for the reduced values.

 

  • Generate and scale the Q data (DAC value). This example uses a simple cosine equation, where 2*3.14 equals one waveform cycle. Change the equation to fit your application.

    • The array pointer, index, increments from 0–511, creating 512 Q data points over one period of the cosine waveform.

    • Set the scale of the DAC values in the range of −32767 to 32768, where the values −32767 and 32768 equal full scale negative and positive respectively.

 

Return to top

2. Save the I/Q data to a text file to review.

The following lines of code export the I and Q data to a text file for validation. After exporting the data, open the file using Microsoft Excel or a similar spreadsheet program, and verify that the I and Q data are correct.

Line

Code – Saving the I/Q Data to a Text File

 

12

char *ofile = "c:\\temp\\iq.txt";

13

FILE *outfile = fopen(ofile, "w");

14

if (outfile==NULL) perror ("Error opening file to write");

15

for(index=0; index<numsamples; index++)

16

{

17

fprintf(outfile, "%d, %d\n", idata[index], qdata[index]);    

18

}

19

fclose(outfile);

 

Line

Code – Description Saving the I/Q Data to a Text File

12

Set the absolute path of a text file to a character variable. In this example, iq.txt is the file name and *ofile is the variable name.

For the file path, some operating systems may not use the drive prefix (’r;c:’ in this example), or may require only a single forward slash (/), or both ("/temp/iq.txt")

13

Open the text file in write format.

14

If the text file does not open, print an error message.

15-18

Create a loop that prints the array of generated I and Q data samples to the text file.

19

Close the text file.

 

Return to top

3. Interleave the I and Q data, and byte swap if using little endian order.

This step has two sets of code:

For more information on byte order, see Little Endian and Big Endian (Byte Order).

The PXB expects data in big endian order.

 

Line

Code – Interleaving and Byte Swapping for Little Endian Order

 

20

char iqbuffer[NUMSAMPLES*4];

21

for (index=0; index<NUMSAMPLES; index++)

22

{

23

short ivalue = idata[index];    

24

short qvalue = qdata[index];    

25

iqbuffer[index*4] = (ivalue >> 8) & 0xFF;         

26

iqbuffer[index*4+1] = ivalue & 0xFF;     

27

iqbuffer[index*4+2] = (qvalue >> 8) & 0xFF;     

28

iqbuffer[index*4+3] = qvalue & 0xFF;     

29

}

30

return 0;

 

Line

Code – Description Interleaving and Byte Swapping for Little Endian Order

20

Define a character array to store the interleaved I and Q data. The character array makes byte swapping easier, since each array location accepts only 8 bits (1 byte). The array size increases by four times to accommodate two bytes of I data and two bytes of Q data.

21-29

Create a loop to do the following:

  • Save the current I data array value to a variable.

In rare instances, a compiler may define short as larger than 16 bits. If this condition exists, replace short with the appropriate object or label that defines a 16-bit integer.

 

  • Save the current Q data array value to a variable.

  • Swap the low bytes (bits 0–7) of the data with the high bytes of the data (done for both the I and Q data), and interleave the I and Q data.

    • shift the data pointer right 8 bits to the beginning of the high byte (ivalue >> 8)

    • AND (boolean) the high I byte with 0xFF to make the high I byte the value to store in the IQ array—(ivalue >> 8) & 0xFF

    • AND (boolean) the low I byte with 0xFF (ivalue & 0xFF) to make the low I byte the value to store in the I/Q array location just after the high byte [index * 4 + 1]

    • Swap the Q byte order within the same loop. Notice that the I and Q data interleave with each loop cycle. This is due to the I/Q array shifting by one location for each I and Q operation
      [index * 4 + n].

 

Line

Code – Interleaving I and Q data for Big Endian Order

 

20

short iqbuffer[NUMSAMPLES*2];

21

for (index=0; index<NUMSAMPLES; index++)

22

{

23

iqbuffer[index*2] = idata[index];     

24

iqbuffer[index*2+1] = qdata[index]; 

25

}

26

return 0;

 

Line

Code – Description Interleaving I and Q data for Big Endian Order

20

Define a 16-bit integer (short) array to store the interleaved I and Q data. The array size increases by two times to accommodate two bytes of I data and two bytes of Q data.

In rare instances, a compiler may define short as larger than 16 bits. If this condition exists, replace short with the appropriate object or label that defines a 16-bit integer.

 

21-25

Create a loop to do the following:

  • Store the I data values to the I/Q array location [index*2].

  • Store the Q data values to the I/Q array location [index*2+1].

 

Return to top

Downloading I/Q Waveform Data

This topic examines methods of downloading I/Q waveform data created in MATLAB (a simulation software) and C++ (an advanced programming language).

Using Advanced Programming Languages

There are two steps in the process of downloading an I/Q waveform:

  1. Open a connection session.

  2. Download the I/Q data.

1. Open a connection session with the PXB.

The following code establishes a LAN connection with the PXB or prints an error message if the session is not opened successfully.

Line

Code – Open a Connection Session

 

1

char* instOpenString ="lan[hostname or IP address]";
//char* instOpenString ="gpib<primary addr>,<secondary addr>";

2

INST id=iopen(instOpenString);

3

if (!id)

4

{

5

fpringf(stderr, "iopen failed (%s)\n", instOpenString);    

6

return -1;    

7

}

 

Line

Code – Description Open a Connection Session

1

Assign the PXB’s LAN hostname, IP address, or GPIB address to a character string.

  • This example uses the Agilent IO library’siopen() SICL function to establish a LAN connection with the PXB. The input argument, lan[hostname or IP address] contains the device, interface, or commander address. Change it to your PXB host name or just set it to the IP address used by your PXB. For example: ”r;lan[999.137.240.9]

  • If you are using GPIB to connect to the PXB, use the commented line in place of the first line. Insert the GPIB address based on your instrument setting, for example ”r;gpib0,19”.

  • For the detailed information about the parameters of the SICL function iopen(), refer to the online ”r;Agilent SICL User’s Guide for Windows.”

2

Open a connection session with the PXB to download the generated I/Q data.

The SICL function iopen() is from the Agilent IO library and creates a session that returns an identifier to id.

  • If iopen() succeeds in establishing a connection, the function returns a valid session id. The valid session id is not viewable, and can only be used by other SICL functions.

  • If iopen() generates an error before making the connection, the session identifier is always set to zero.

This occurs if the connection fails.

  • To use this function in C++, you must include the standard header
    #include <sicl.h> before the main() function.

3-7

If id = 0, the program prints out the error message and exits the program.

 

Return to top

2. Download the I/Q data.

The following code sends the SCPI command and downloads the generated waveform data to the PXB.

Line

Code – Download the I/Q Data

 

8

int bytesToSend;

9

bytesToSend = numsamples*4;

10

char s[20];

11

char cmd[200];

12

sprintf(s, "%d", bytesToSend);

13

sprintf(cmd, ":MEM:DATA\"NVWFM1:FILE1\", #%d%d", strlen(s), bytesToSend);

14

iwrite(id, cmd, strlen(cmd), 0, 0);

15

iwrite(id, iqbuffer, bytesToSend, 0, 0);

16

iwrite(id, "\n", 1, 1, 0);

 

Line

Code – Description Download the I/Q data

8

Define an integer variable (bytesToSend) to store the number of bytes to send to the PXB.

9

Calculate the total number of bytes, and store the value in the integer variable defined in line 8.

In this code, numsamples contains the number of waveform points, not the number of bytes. Because it takes four bytes of data, two I bytes and two Q bytes, to create one waveform point, we have to multiply numsamples by four. This is shown in the following example:

numsamples = 512 waveform points

numsamples X 4 = 2048 (four bytes per point)

bytesToSend = 2048 (numsamples X 4)

For information on setting the number of waveform points, see Create I and Q data.

10

Create a string large enough to hold the bytesToSend value as characters. In this code, string s is set to 20 bytes (20 characters—one character equals one byte).

11

Create a string and set its length (cmd[200]) to hold the SCPI command syntax and parameters. In this code, we define the string length as 200 bytes (200 characters).

12

Store the value of bytesToSend in string s. For example, if bytesToSend = 2048; s = ”2048”

sprintf() is a standard function in C++, which writes string data to a string variable.

13

Store the SCPI command syntax and parameters in the string cmd. The SCPI command prepares the PXB to accept the data.

  • strlen() is a standard function in C++, which returns length of a string.

  • If bytesToSend = 2048, then s = ”r;2048”, strlen(s) = 4, so cmd = :MEM:DATA ”NVWFM1:FILE1\” #42048.

14

Send the SCPI command stored in the string cmd to the PXB, which is represented by the session id.

  • iwrite() is a SICL function in Agilent IO library, which writes the data (block data) specified in the string cmd to the PXB (id).

  • The third argument of iwrite(), strlen(cmd), informs the PXB of the number of bytes in the command string. The PXB parses the string to determine the number of I/Q data bytes it expects to receive.

  • The fourth argument of iwrite(), 0, means there is no END of file indicator for the string. This lets the session remain open, so the program can download the I/Q data.

15

Send the generated waveform data stored in the I/Q array (iqbuffer) to the PXB.

  • iwrite() sends the data specified in iqbuffer to the PXB (session identifier specified in id).

  • The third argument of iwrite(), bytesToSend, contains the length of the iqbuffer in bytes. In this example, it is 2048.

  • The fourth argument of iwrite(), 0, means there is no END of file indicator in the data.

In many programming languages, there are two methods to send SCPI commands and data:

    • Method 1 where the program stops the data download when it encounters the first zero (END indicator) in the data.

    • Method 2 where the program sends a fixed number of bytes and ignores any zeros in the data. This is the method used in our program.

For your programming language, you must find and use the equivalent of method two. Otherwise you may only achieve a partial download of the I and Q data.

16

Send the terminating carriage (\n) as the last byte of the waveform data.

  • iwrite() writes the data ”r;\n” to the PXB (session identifier specified in id).

  • The third argument of iwrite(), 1, sends one byte to the PXB.

  • The fourth argument of iwrite(), 1, is the END of file indicator, which the program uses to terminate the data download.

To verify the waveform data download, see Playing and Verifying a Downloaded Waveform.

 

Return to top