Top Banner
Custom Equations in PNA-X A Comprehensive Look at Equation Editor Functionality and Tutorial on Writing Custom Equations Dara Sariaslani Application Architect Component Test Division Agilent Technologies
23

Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

Jun 30, 2020

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

Custom Equations in PNA-X

A Comprehensive Look at Equation Editor Functionality and Tutorial on Writing Custom Equations

Dara Sariaslani

Application Architect Component Test Division

Agilent Technologies

Page 2: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

2 Agilent Technologies

Table of Contents

History ........................................................................................................................................................... 3

Equation Editor Basics ................................................................................................................................... 3

What is in the EE dialog? ........................................................................................................................... 4

Writing Custom Equation DLLs ..................................................................................................................... 6

Getting Started .......................................................................................................................................... 6

Understanding How Custom Equations Work .......................................................................................... 9

Using the Arguments Array to Pass Data ................................................................................................ 10

Using Equation Editor’s Special Functions .............................................................................................. 11

Adding a Function to a Custom Equation Project ................................................................................... 12

Examples ..................................................................................................................................................... 15

Example 1 – Normalize to First ............................................................................................................... 15

Example 2 – Unwrapped Phase .............................................................................................................. 17

Example 3 – Taylor Window ................................................................................................................... 20

Page 3: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

3 Agilent Technologies

History The Equation Editor has been a feature in the PNA firmware since May 2006. It initially provided a way for customers to apply a commonly used set of mathematical functions to data available from various channels in the PNA and see the results as traces in their measurement windows. Functions such as sin(), cos(), phase(), mag(), and conj() were included in the equation editor’s original library of functions. The idea was that as measurement designers, we could not think of all the possible measurement scenarios in which our instruments were to be used, but at the same time we wanted to provide the most flexible measurement platform possible to capture the widest swath of the measurement landscape with the PNA-X. The Equation Editor is one of the cornerstones of the PNA’s flexible hardware/software architecture.

Soon after the Equation Editor was introduced, we received requests for new functions that were missing from the original equation library. We honored a number of these requests. Functions such as kfac(), min_hold() and max_hold() were all added in subsequent releases of firmware. However, because the Equation Editor was an integrated part of the PNA firmware, every new function added to the library meant that a new release of the firmware was needed to disseminate the function to people who requested it. Obviously, this could not be a sustainable model of how we wanted to inject additional flexibilities into this platform. There was a need to add new functions to the Equation Editor independent of the PNA’s firmware development/release processes.

The custom equation capability was introduced on July 2008. With this new capability, anyone who can write a C++ DLL, adhering to certain rules and interfaces can add his/her own function(s) to the Equation Editor’s library dynamically and without having to rebuild or reinstall the PNA firmware. While there are still many restrictions on what can actually be done in custom equations (as we will learn in this document), this capability makes the PNA family one of the most customizable measurement instruments in its class.

Equation Editor Basics

The Equation Editor’s (EE) output occupies a trace. In other words, an equation is always assigned to an existing trace and by extension, the equation belongs to a specific channel and a specific window. We refer to this trace as the Equation Trace (ET).

The output of an ET is always a complex array of numbers whose size matches the number of points in the channel that contains the ET.

Page 4: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

4 Agilent Technologies

What is in the EE dialog?

1. The equation’s edit box. This is where you actually type the equation. An equation can either be typed as an expression (ex. S11*S22) or as a full equality (ex. myProd=S11*S22). When you use a name in the left hand side of the equation, that name is used to annotate the resulting ET on the window. If a simple expression is used, then the entire expression is used to annotate the ET in the window.

2. The Trace List. This is the list of all currently existing traces that can be used in this instance of an equation. This brings us to the first EE restriction.

i. An equation can contain references to traces from any existing channels as long as the number of points in those channels match the number of points in the ET’s channel. The Trace list automatically filters out any trace that cannot legally be used in the equation.

ii. To avoid a circular reference, the trace list does not show the trace that has been assigned to the ET (ex. If Tr1 is the ET, then Tr1 does not show up in the trace list).

3. The Channel Parameter List. This is another source of data for an equation. In addition to the existing traces in the instrument state, any parameter that can be measured in the ET’s channel can also be used in the equation.

i. The channel parameter list is measurement class dependent and it is based on the measurement class of the ET’s parent channel. For example if you create the ET in a S-Parameter channel, then the channel parameter list will contain all the S-Parameters, all the measurement and reference receiver parameters, all the possible receiver ratios, and all the DC parameters supported by a standard measurement class channel.

ii. The receiver parameters are expressed in two types of notations, the standard PNA notation for receivers (A, B, C, D, R1, R2, R3, R4) and also the 8510 style notation for receivers (ax and bx). Ratios are expressed as two receiver names

Page 5: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

5 Agilent Technologies

next to each other (ex. A/B with src port set to 1 is expressed as AB_1 or a1b2_1)

iii. In a multiport configuration, the list will only contain the s-parameters representing the PNA’s physical number of ports, but if the box is in multiport mode, you can still address the higher port count s-parameters by directly typing the name of the s-parameter in the equation. For example if you have a 12 port system and want to use the S12_5 parameter in the equation, you can simply type S12_5 in the equation edit box.

iv. Any channel parameter that is referenced in the equation will force the ET’s parent channel to create and perform the necessary sweep(s) to acquire the data for that parameter if that data is not already acquired as part of the channel’s data acquisition list (based on the existing traces and corrections applied).

v. The channel parameter data that is used in the equation will be corrected data if the current activated calset for the ET’s parent channel can correct the parameter and correction is turned on for the ET.

4. Function Library Selection. In this drop-down you select the library you want to use. Library selection filters the list of functions that are displayed in the function list below.

i. Initially the only choice is “built-in”. This is the integrated factory library that contains the basic functions discussed earlier.

ii. Any additional custom equation DLL that is loaded will also be listed in the drop-down.

iii. The “all” selections shows the combined list of all the functions from all the loaded libraries.

5. Function List. This is where individual functions are listed. i. Function names from custom DLLs are automatically pre-pended with “d_”.

ii. Clicking on a function from the list automatically inserts the call to that function in the equation text area at the current cursor insertion point. Any arguments that are needed to satisfy the function will also be listed in the equation text area.

6. Mathematical Operators. This is a list of operators that the EE understands. The operator “E” is the ten based exponential operator (ex. 1E1 = 10 in real and 20 in LogMag).

7. Store Equation. Pressing this button stores the current equation in the equation text area in the drop-down list associated with the equation text edit control. This can be used to quickly recall often used equations.

i. You must click OK after storing an equation for it to be permanently stored. If you hit cancel after storing an equation, it will be lost.

ii. Up to 10 equations can be stored this way. 8. Delete Equation. This button deletes the selected equation from the stored equation

list. 9. Import Functions. This button brings up a dialog that allows you to either load a custom

equation DLL or remove one that is already loaded. i. Custom equations can be in any directory accessible from the PNA (even

mapped remote shared drives). However, the recommended location for the custom equation DLLs is the “C:\Program Files\Agilent\Network Analyzer\UserFunctions” directory on the PNA.

Page 6: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

6 Agilent Technologies

ii. The latest release of the PNA firmware includes a few custom equation DLLs to supplement the list of built-in functions. They include:

1. Expansion.dll – contains functions such as PAE() and some balanced parameters as well as a zTransfer() and a zReflect() function.

2. EqnErrorTerms.dll – contains some commonly used error terms (such as directivity, load match, source match, …) that can be used in correcting various response measurements. By supplying a channel number, the functions extract the error term data from the calset associated with that channel.

3. BestFit.dll – this library contains an assortment of functions that we have compiled as a result of various customer engagements. You will find functions such as bestfit, DFLP (deviation from linear phase), unwrap (phase unwrapping function), mean, tilt (slope of the bestfit line), and others.

iii. If you attempt to load a properly formatted custom equation dll, the list of functions contained in the dll will be displayed in the load library dialog. However, if the dll is malformed or points to other DLLs currently not found in any of the directories in the PATH environment variable, then an error message will be displayed.

10. Enable Checkbox. An equation is active if the enable checkbox is checked and not grayed out. When the checkbox is grayed out, it means the current equation is not valid (usually some kind of a syntax problem)

Writing Custom Equation DLLs

As mentioned earlier, customers can extend the capabilities of the EE by creating their own function libraries in the form of a DLL. The DLL must conform to certain rules and must provide a specific interface in order for it to be recognized by the PNA firmware as an equation library. At the PNA service website (http://na.tm.agilent.com/pna/apps/applications.htm) we provide two different templates of a C++ DLL that you can start with to build your own function library. One template is optimized for use with the full version of the Microsoft Visual Studio development environment. The second template is optimized for the Express (i.e. free) edition of Visual Studio. The express edition version was provided for those who do not have a license for a full version of Visual Studio and it is the one we will be using for developing our examples in this course. The main difference between the full and the express edition is that in the express version, MFC and ATL libraries are not supported. MFC is not needed in building custom equation DLLs, but lack of ATL means that function libraries built using the express edition of visual studio cannot have functions that rely on the PNA’s COM interface.

(Note: enterprising students may be able to find creative workarounds for including the PNA’s COM interface in their equation DLLs, built using the express edition, but that topic will not be covered here)

Getting Started 1. Download and install Visual Studio Express on your development PC:

a. Go to: http://www.microsoft.com/express/Downloads/ b. Pick the Visual C++ 2010 Express link

Page 7: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

7 Agilent Technologies

c. Install VS Express by running the vc_web.exe file you downloaded in step 1b.

2. Download and install the VC++ redistributable package on the target PNA a. Go to link: VC++ redistributable package and download vcredist_x86.exe to your PNA. b. Install the redistribution package.

3. Download the equation example template for VS Express from http://na.tm.agilent.com/pna/apps/applications.htm

4. Unpack the zip file and copy the EqEditorExample folder to the VS projects folder on your development PC. This would typically be under “C:\Documents and Settings\[user name]\My Documents\Visual Studio 2010\Projects”.

5. Start the Visual C++ Express a. Click on “Open Project…”

Page 8: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

8 Agilent Technologies

b. Brows to the EqEditorExample folder and open the solution file (.sln)

c. The solution explorer should look like this:

d. In the toolbar, click the build solution button:

e. Should see this message when the build process is complete:

6. In the “…\Projects\EqEditorExample\Release” directory find the EqEditorExample.dll file and

copy it to the PNA’s UserFunctions directory. 7. In the PNA go to the “Marker/Analysis->Analysis->Equation Editor…” menu and once the EE

dialog is up, click on the “Import Functions” button.

Page 9: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

9 Agilent Technologies

8. Brows to the EqEditorExample.dll and open it. You should see the following:

9. If you see the function “addThreeNumbers()” listed then you have a valid custom equation DLL. 10. [Optional Exercise] – Experiment with creating an equation trace using the addThreeNumbers()

function.

Understanding How Custom Equations Work

A custom equation is a function that has the following signature:

COMPLEXNUMBER myFunctionName(COMPLEXNUMBER* arguments)

“COMPLEXNUMBER” is a specific data type that is defined in the ComplexNumber.h and ComplexNumber.cpp files. These files are included in the template. One element of the ComplexNumber class is a data structure that represents a complex number that can be expressed as a coordinate on the complex plane:

Where “a” is the real component of the number and “b” is the imaginary component. The following code segment defines this structure: struct ComplexNumber

{

double re;

double im;

ComplexNumber():re(0),im(0){};

ComplexNumber(double x, double y):re(x),im(y){};

};

typedef struct ComplexNumber COMPLEXNUMBER;

Any variable that is defined as a COMPLEXNUMBER will have a “.re” and “.im” component. The second element of the ComplexNumber class is a set of functions that provide mathematical operations on arguments of type COMPLEXNUMBER. They are:

Page 10: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

10 Agilent Technologies

extern "C" __declspec(dllexport) COMPLEXNUMBER addCN(COMPLEXNUMBER lhs, COMPLEXNUMBER rhs);

extern "C" __declspec(dllexport) COMPLEXNUMBER subCN(COMPLEXNUMBER lhs, COMPLEXNUMBER rhs);

extern "C" __declspec(dllexport) COMPLEXNUMBER multCN(COMPLEXNUMBER lhs, COMPLEXNUMBER rhs);

extern "C" __declspec(dllexport) COMPLEXNUMBER divCN(COMPLEXNUMBER lhs, COMPLEXNUMBER rhs);

extern "C" __declspec(dllexport) double magCN(COMPLEXNUMBER num);

So, now if we re-examine the signature of a custom equation function

COMPLEXNUMBER myFunctionName(COMPLEXNUMBER* arguments)1

We can see that this function accepts an array of COMPLEXNUMBER values as input and returns a single COMPLEXNUMBER as output. The important point here is that this is the only valid signature for a custom equation function. In other words, any function that you write, regardless of the number and type of input arguments that it needs and regardless of the function it performs, has to conform to this signature. While this may seem very restrictive, we will see later how the single arguments array can be used to pass all kinds of information to our functions. The second notable fact about the function signature is the return type of a single COMPLEXNUMBER. This may seem odd at first glance since we noted earlier that equation results are represented as traces in the PNA. How can a single COMPLEXNUMBER return value produce a trace worth of data on the screen? The answer is in how the PNA firmware actually calls a custom equation function once it is activated in a channel. When an ET is active, the functions that make up the equation are called once for every stimulus point in the parent channel. In other words, if the ET is in a channel with 201 points, then the functions in the equation will be called 201 times during a complete sweep. So any custom equation has to be constructed such that it produces a single COMPLEXNUMBER result appropriate for the corresponding stimulus point at the time that is called. This concept will become clearer once we analyze the example functions.

Using the Arguments Array to Pass Data The following example will illustrate the mechanism for using the arguments array to pass different types of data to the custom equation function. Example Function: COMPLEXNUMBER myFunc(int numPoints, int sweepIndex, double* xAxisArray, COMPLEXNUMBER* traceData)

In order for this function to work as a custom equation, it must conform to the standard signature:

COMPLEXNUMBER myFunctionName(COMPLEXNUMBER* arguments) A complex number array can be represented as:

Index: 0 1 2 … n

Re Im Re Im Re Im Re Im Re Im

1 In C++ an array is denoted by the data type followed by a *. This type of a variable is commonly known as a

pointer and the array variable name is the pointer to the first element of a list in memory that contains the array data. Array elements can be accessed by using the “*+” operator and in C++, array indices always start at 0 (ex. To access the 2

nd element in the arguments array above, one would write arguments[1])

Page 11: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

11 Agilent Technologies

For our example function to conform, its input arguments have to fit the model above. When our equation is called by the PNA firmware, the data that we have requested will be packaged in the arguments complex array in the following format (assume that the ET’s parent channel has 11 points with a start frequency of 1GHz and a stop frequency of 2Ghz, and the trace is R channel power going from -25 dBm to 0 dBm):

INDEX 0 1 2 … 12 13 … 23

Variable numPoints SweepIndex xAxisArray[0] xAxisArray[10] traceData[0] traceData[10]

Data 11 0 n 0 1e9 0 2e9 0 -25 0 0 0

The implementation of the example function will look something like this:

COMPLEXNUMBER myFunc(COMPLEXNUMBER* arguments)

{

//Declare all the neccessary variables

int numPoints;

int sweepIndex;

COMPLEXNUMBER* xAxisArray;

COMPLEXNUMBER* traceData;

COMPLEXNUMBER result;

//Parse the arguments array into its constituent pieces

numPoints = (int) arguments[0].re;

sweepIndex = (int) arguments[1].re;

xAxisArray = arguments + 3;

traceData = xAxisArray + numPoints;

//Convert the compelex x-axis and y-axis data into double array

std::vector<double> xVals(numPoints);

std::vector<double> data(numPoints);

for(int i = 0; i < numPoints; i++)

{

xVals[i] = xAxisArray[i].re;

data[i] = traceData[i].re;

}

//Do usefull things here and compute the complex result

return result;

}

Using Equation Editor’s Special Functions In the last section we learned how the packages data into the arguments array and how that data can then be unpack in the custom equation code to hydrate the specific variables that the function needs. We now need to learn how to tell the PNA what information we need in our functions. To understand how to fill the arguments array with different types of information we must first examine some special functions available in the built-in library that are designed to help in passing data into custom equations.

Page 12: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

12 Agilent Technologies

Equation Editor Special Functions

Function Description getNumPoints() This function is designed to return the number of points in the ET’s parent

channel. It is useful in parsing out the arguments array by knowing the size of trace data arrays. This function takes no arguments.

traceDataArray() This function is designed to return the actual complex trace data for any parameter or trace name that is given to it. It takes an argument that can either be a parameter name like ‘S11’ or a trace name like ‘Tr3’.

xAxisArray() This function is designed to return the x-axis stimulus values in a complex array. The x-axis values will be in the real part of the complex array. This function takes no arguments.

xAxisIndex() This function returns the index of the current stimulus point at the time the custom equation function is called. This function takes no arguments.

xAxisValue() This function returns the x-axis value of the current stimulus point at the time the custom equation function is called. This function takes no arguments.

These are functions in the built-in equation editor library and as such can be used inside of any other equation as arguments to other functions or in the right hand side of mathematical expressions. To complete the example from the previous section, we can now show what the syntax of the call to our function would look like in the equation editor dialog:

myEqTrace=myFunc(getNumPoints(), xAxisIndex(), xAxisArray(), traceDataArray(R1_1))

The important thing to note here is that any argument that is passed into a custom equation has to either resolve to a single numeric value or an array of complex numbers. As of today, there are no easy techniques to pass character or string data into a custom equation function, although enterprising students can recognize the fact that there is a numerical code for characters and that realization can lead to some interesting implementations.

Adding a Function to a Custom Equation Project

The final piece of the puzzle is the actual process of adding a custom equation function to our DLL project and we will illustrate that through the first of the three real life examples that will be discussed in this paper. Our first example is a function that will normalize the input trace data by the first point of the sweep. We will call it normalizeTo1st. Our function will need two pieces of information, the index of each sweep point that results in a call to the function and the actual trace data array.

In C++, the declaration of a function is always placed in the .h (or the header) file and the implementation or the body of the function is placed in the .cpp file. In the template that we downloaded and compiled earlier, there is an EqEditorExample.h and an EqEditorExample.cpp file. Let us first add the declaration for the normalizeTo1st function to the .h file. The .h file contains the following:

Page 13: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

13 Agilent Technologies

#include "ComplexNumber.h"

//this function holds critical information about each function

extern "C" __declspec(dllexport) int listOfFunctions(char** const stringOfNames);

//example function that adds three numbers

extern "C" __declspec(dllexport) COMPLEXNUMBER addThreeNumbers(COMPLEXNUMBER* arguments);

//TODO: replace 'myFunctionName' with the name of your function

//extern "C" __declspec(dllexport) COMPLEXNUMBER myFunctionName(COMPLEXNUMBER* arguments);

There are three parts to the .h file that are highlighted in green, blue and gray. The first two parts, green and blue, contain required code that should not be altered. The gray portion of the header file is where we will do our customization and add the function declarations needed. The template comes with one working example functions that adds 3 numbers and another example declaration that is commented out. We will replace both of these with the declaration for first example function.

#include "ComplexNumber.h"

//this function holds critical information about each function

extern "C" __declspec(dllexport) int listOfFunctions(char** const stringOfNames);

//List of custom equation DLL's function declarations

extern "C" __declspec(dllexport) COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments);

The “extern "C" __declspec(dllexport)” is a C style function declaration that complies with the equation editor’s interface requirements. Note that our normalizeTo1st function conforms to the standard equation editor function signature discussed earlier. This is all that needs to be done in the .h file.

The next step involves creating the necessary bookkeeping in our DLL to tell the PNA what functions are in our DLL and how they are to presented to the user in the equation editor. Let’s look at what is inside the .cpp file.

// EqEditorExample.cpp : Defines the exported functions for the DLL application.

//

#include "stdafx.h"

#include "EqEditorExample.h"

//example function that takes in three numbers and adds them

COMPLEXNUMBER addThreeNumbers(COMPLEXNUMBER* argumentList)

{

COMPLEXNUMBER answer;

COMPLEXNUMBER argument1, argument2, argument3;

argument1 = argumentList[0];

argument2 = argumentList[1];

argument3 = argumentList[2];

answer = addCN(argument1, addCN(argument2, argument3));

return answer;

}

//TODO: return value needs to reflect the number of functions in the DLL

const int NUMBER_OF_FUNCTIONS = 1;

int listOfFunctions(char** const stringOfNames)

{

if(!stringOfNames){

return NUMBER_OF_FUNCTIONS;

}

//function display strings should be under one hundred characters in length

Page 14: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

14 Agilent Technologies

//to use the addThreeNumbers() method, uncomment the following line

strcpy(stringOfNames[0],"addThreeNumbers,3 ,1,2,3");

//TODO: add your function strings here in the format of:

//strcpy(stringOfNames[index],"nameOfYourFunction, numOfArgs[,arg1, arg2, ... , argN]");

return 0;

}

Again, I have highlighted the portions that should not be altered in green and the parts that need to be modified in gray. Below is what the .cpp looks like once we have added our modifications:

#include "stdafx.h"

#include "EqEditorExample.h

//------------------------------------------------------------------------------

// Functions listed in the Equation Editor

//------------------------------------------------------------------------------

COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments)

{

//declare required variables

int ndx; //index of the current point in the sweep

COMPLEXNUMBER* dataArray ; //the complex number array of trace data

COMPLEXNUMBER curVal; //the complex number value of the current data point after normalization

static COMPLEXNUMBER firstVal; //the complex number of the trace's first data point

//parse the generic arguments list into it's constituent parts

ndx = (int) arguments[0].re;

dataArray = arguments + 1;

//find and store the first data point

if (ndx == 0)

{

firstVal = dataArray[ndx];

}

//normalize the current data value to the first by using the complex number divide function

curVal = divCN(dataArray[ndx], firstVal);

return curVal;

}

//TODO: return value needs to reflect the number of functions in the DLL

const int NUMBER_OF_FUNCTIONS = 1;

int listOfFunctions(char** const stringOfNames)

{

if(!stringOfNames){

return NUMBER_OF_FUNCTIONS;

}

//function display strings should be under one hundred characters in length

strcpy(stringOfNames[0],"normalizeTo1st,2 ,xAxisIndex(), traceDataArray(PARAM)");

//TODO: add your function strings here in the format of:

//strcpy(stringOfNames[index],"nameOfYourFunction, numOfArgs[,arg1, arg2, ... , argN]");

return 0;

}

There are 3 steps in modifying the .cpp file.

1. Modify the constant NUMBER_OF_FUNCTIONS to reflect the total number of functions that you will expose in your DLL. In this example we only have one function so far.

2. Populate the stringOfNames array with an entry for your function. The entry should have the following format: “[name of function], [number of arguments], [argument template], […]”.

a. The name must match the function name used in the .h file. In the equation editor dialog, your function will appear as “d_[name of function]”.

b. The number of arguments will tell the PNA how many arguments are going to be in your function prototype.

c. The argument templates are the text that will appear as place holders in the equation editor UI for each of your arguments when the user selects your equation. The number of argument templates that you specify must match the number of arguments value.

2

1

3

Page 15: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

15 Agilent Technologies

3. Write the actual body of the function.

Examples

Example 1 – Normalize to First

We have already examined the mechanics of adding a function to a custom equation DLL using the normalizeTo1st example. In this section we will study the actual construct of the function and use this simple function to point out good practices and unique attributes of custom equation functions. Let’s take a fresh look at the body of the function:

COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments)

{

//declare required variables

int ndx; //index of the current point in the sweep

COMPLEXNUMBER* dataArray ; //the complex number array of trace data

COMPLEXNUMBER curVal; //the complex number value of the current data point after normalization

static COMPLEXNUMBER firstVal; //the complex number of the trace's first data point

//parse the generic arguments list into it's constituent parts

ndx = (int) arguments[0].re;

dataArray = arguments + 1;

//find and store the first data point

if (ndx == 0)

{

firstVal = dataArray[ndx];

}

//normalize the current data value to the first by using the complex number divide function

curVal = divCN(dataArray[ndx], firstVal);

return curVal;

}

The purpose of this function is to normalize every complex data point in the data array to the very first point in the data array. What we have to keep in mind is that this function will be called once for every point in the channel and as a result we have to somehow remember the value of the first point, so that it can be used over and over again, each time the function is called. To accomplish this we need to first declare some variables. The first thing we need to know each time the function is called is the index of the sweep. Secondly we need an array to store the trace data in. The next variable we need is a container to hold the computed result so that it can be returned back to the PNA to be displayed. Finally, we need a special variable to store the value of the first data point. The reason this variable is special is because it must maintain its value even when the function goes out of execution scope. Typically variables that are declared locally within the body of a function have a lifetime limited to the period in which the function has execution control. These variables go out of scope when the function is finished executing and are reinitialized the next time execution flow returns to the function. One way to extend the lifetime of a variable in C++ is to declare it as a “static”. Static variables maintain their value throughout the life of the process (or executable). As a result, proper care must be taken to initialize and/or reset these variables at the appropriate times. In our example the appropriate time to initialize and rest the firstVal variable is whenever a new sweep starts and that is when the ndx is zero.

However, before we can do anything, we must first parse the complex arguments array for the different pieces of information we need. Recall the format of our function template:

strcpy(stringOfNames[0],"normalizeTo1st,2 ,xAxisIndex(), traceDataArray(PARAM)");

Page 16: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

16 Agilent Technologies

The data in the arguments array has two pieces of information. The very first element of the array has the sweep index information and the remainder of the array has the actual complex trace data. Here is how the data is parsed:

//parse the generic arguments list into it's constituent parts

ndx = (int) arguments[0].re;

dataArray = arguments + 1;

The ndx is scalar integer value and therefore it is in the real part of the first element of the COMPLEXNUMBER. The other point to consider is that in the COMPLEXNUMBER structure the real and imaginary parts are declared as doubles, but the index is an integer and therefore we must cast the value of arguments[0].re into an integer before assigning it to ndx. The remaining data in the arguments array is the complex trace data and to make that assignment, we simply use the concept of “pointer math”. Remember that the “arguments” variable is pointer (or reference) to the first element of the array. In other words, the value stored in “arguments” is the actual address of the first element of our complex array. With pointer math, we can reference any element in the array by simply adding an increment to the address of the first element in the array. So, by saying “dataArray = arguments +1”, we are creating a new array pointer whose address is the second element in the arguments array. This type of array pointer manipulation will be used extensively in the rest of the examples and is a valuable concept to learn for anyone wanting to write custom equation DLLs.

Once the arguments array has been parsed we can attend to our remaining tasks. The first task is to initialize the static variable firstVal. This needs to be initialized once at the beginning of every sweep. The first call into our function whenever the sweep starts is always going to be with ndx=0. So, we can initialize firstVal based on this condition.

//find and store the first data point

if (ndx == 0)

{

firstVal = dataArray[ndx];

}

The last remaining task is to normalize each data point to firstVal and return the result.

//normalize the current data value to the first by using the complex number divide function

curVal = divCN(dataArray[ndx], firstVal);

return curVal;

divCN() is one of the methods defined in the ComplexNumber class and it performs complex number division operation.

Page 17: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

17 Agilent Technologies

Example 2 – Unwrapped Phase

For this example we will be computing the unwrapped phase response of the input trace and we will output the result in degree format into the real part of the resulting trace. A logical question would be, why bother with such an equation when we already have a built-in format for unwrapped phase in the PNA firmware. The answer is that such an equation will be useful to anyone who needs to use the unwrapped phase data as a part of another equation. Because the data supplied to the equation editor from the channels are always in complex number format, formatting functions, such as phase or group delay, have no effect on the data passed into the equation editor. So if a computation needs to operate on phase, unwrapped phase, or delay values, another equation editor function is needed to provide those results. This function was in fact requested by a customer who had this exact requirement.

We start again by looking at the .h and .cpp files. In the .h file we add a new function declaration for the unwrap function.

//List of custom equation DLL's function declarations

extern "C" __declspec(dllexport) COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments);

extern "C" __declspec(dllexport) COMPLEXNUMBER unwrap(COMPLEXNUMBER* arguments);

In the .cpp file we modify the listOfFunctions() method and the NUMBER_OF_FNCTIONS constant.

//TODO: return value needs to reflect the number of functions in the DLL

const int NUMBER_OF_FUNCTIONS = 2;

int listOfFunctions(char** const stringOfNames)

{

if(!stringOfNames){

return NUMBER_OF_FUNCTIONS;

}

//function display strings should be under one hundred characters in length

strcpy(stringOfNames[0],"normalizeTo1st,2 ,xAxisIndex(), traceDataArray(PARAM)");

strcpy(stringOfNames[1],"unwrap,3, getNumPoints(),xAxisIndex(),traceDataArray(PARAM)");

//TODO: add your function strings here in the format of:

//strcpy(stringOfNames[index],"nameOfYourFunction, numOfArgs[,arg1, arg2, ... , argN]");

return 0;

}

Our “unwrap” function will have 3 arguments, the number of points in the channel, the sweep index, and the trace data array. The body of the “unwrap” function is defined as follows:

COMPLEXNUMBER unwrap(COMPLEXNUMBER* arguments)

{

int ndx;

int numPts;

COMPLEXNUMBER* trace_data;

COMPLEXNUMBER* unwrappedPhase;

COMPLEXNUMBER c;

c.re = 0;

c.im = 0;

parse_args1(arguments, numPts, ndx, trace_data);

static std::vector<double> phaseResults(numPts);

//this check is to take care of the case when the

//user changes the number of points in the channel.

//because phaseResults is a static vector, not doing

//this will cause the pna to crash.

Page 18: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

18 Agilent Technologies

if (phaseResults.size() != numPts)

{

phaseResults.resize(numPts);

}

//initialize the result vector on the first call

if (ndx == 0)

{

UnwrapPhase(trace_data, phaseResults);

}

//output of the UnwrapPhase function is in radians

//so convert to degrees before returning the result

c.re = phaseResults[ndx] * (180/PI);

return c;

}

In this function, the parsing of the arguments array is a little more involved, so a separate utility function is used to handle the task:

void parse_args1(COMPLEXNUMBER* arguments, int &num_points, int &index, COMPLEXNUMBER *&yvals)

{

num_points = (int)arguments[0].re;

index = (int)arguments[1].re; // extract index

yvals = arguments + 2;

}

Another new concept introduced in this example is the std::vector. This is a built-in type in C++ that can be used in place of arrays. The C/C++ programmers know that creating and managing memory for arrays are somewhat difficult and the std::vector type removes a lot of the complexity of memory management and insulates the programmer from doing things like accessing out of bounds array elements that is the most common cause of crashes in C/C++ based applications. The std::vector is also very handy when the size of the array needs to be dynamically changed during the lifetime of the application.

In this example the result of function is actually an array of data and the result gets computed at the first call into the function for every sweep and those results need to be statically preserved (like the previous example) so that they can be returned one data point at a time. We again accomplish this by doing a static declaration of the std::vector.

static std::vector<double> phaseResults(numPts);

A std::vector is declared with a type and in this case we will declare the vector as a double. You can also declare a vector with an initial size, which we also do here by using numPts. A problem that one quickly runs into, when dealing with static arrays in the context of a custom equation, is the problem of users changing the number of points in the channel. Any custom equation code that uses a static array has to be robust enough to deal with the possibility of the number of points in the channel changing from one sweep to the next. This is how this problem is dealt with in this example:

//this check is to take care of the case when the

//user changes the number of points in the channel.

//because phaseResults is a static vector, not doing

//this will cause the pna to crash.

if (phaseResults.size() != numPts)

{

phaseResults.resize(numPts);

}

Page 19: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

19 Agilent Technologies

As you can see resizing an array with std:vector is very easy. As was mentioned earlier, the resulting unwrapped phase array is computed once at the beginning of each sweep.

//initialize the result vector on the first call

if (ndx == 0)

{

UnwrapPhase(trace_data, phaseResults);

}

The utility function UnwrapPhase() does the actual work.

void UnwrapPhase(COMPLEXNUMBER *c, vector<double> &unWrapPhase)

{

double phasePerPoint;

double phaseWrap = 0;

int numPoints = int(unWrapPhase.size());

vector<double> phase(numPoints);

vector<double> uwPhaseD(numPoints);

for (int i = 0; i < numPoints; i++)

{

phase[i] = complex_to_PhaseRad(c[i]);

}

for (int i = 0; i < numPoints; i++)

{

phasePerPoint = (i == 0 ? 0 : (phase[i] - phase[i - 1]));

phaseWrap = (phasePerPoint > PI ? (phaseWrap - (2*PI)) :

(phasePerPoint < -PI ? (phaseWrap + (2*PI)) : phaseWrap));

unWrapPhase[i] = (phase[i] + phaseWrap);

}

return;

}

double complex_to_PhaseRad(COMPLEXNUMBER c)

{

return atan2(c.im, c.re);

}

The only thing left is to convert the results to degrees and return the value.

//output of the UnwrapPhase function is in radians

//so convert to degrees before returning the result

c.re = phaseResults[ndx] * (180/PI);

return c;

Page 20: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

20 Agilent Technologies

Example 3 – Taylor Window

This is another real life example where a user wanted to apply a different windowing function to a time

domain measurement than the standard Kaiser-Beta window that we use. In this case they wanted to

apply a Taylor window. Here is a description of a Taylor Window function according to MathWorks:

“Taylor windows are similar to Chebyshev windows. While a Chebyshev window has the narrowest possible mainlobe for a

specified sidelobe level, a Taylor window allows you to make tradeoffs between the mainlobe width and sidelobe level. The

Taylor distribution avoids edge discontinuities, so Taylor window sidelobes decrease monotonically. Taylor window coefficients

are not normalized. Taylor windows are typically used in radar applications, such as weighting synthetic aperature radar images

and antenna design.”

And this is what it looks like:

EqnEditorExample.h

#include "ComplexNumber.h"

//this function holds critical information about each function

extern "C" __declspec(dllexport) int listOfFunctions(char** const stringOfNames);

//example function that adds three numbers

extern "C" __declspec(dllexport) COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments);

extern "C" __declspec(dllexport) COMPLEXNUMBER unwrap(COMPLEXNUMBER* arguments);

extern "C" __declspec(dllexport) COMPLEXNUMBER Taylor_FullSpan(COMPLEXNUMBER* arguments);

//TODO: replace 'myFunctionName' with the name of your function

//extern "C" __declspec(dllexport) COMPLEXNUMBER myFunctionName(COMPLEXNUMBER* arguments);

EqnEditorExample.cpp

// EqEditorExample.cpp : Defines the exported functions for the DLL application.

//

#include "stdafx.h"

#include "EqEditorExample.h"

#include <vector>

#define PI 3.141592653589793

using namespace std;

//------------------------------------------------------------------------------

// Helper Functions

//------------------------------------------------------------------------------

double complex_to_logmag(COMPLEXNUMBER c)

{

return 20 * log10(magCN(c));

}

double complex_to_mag(COMPLEXNUMBER c)

{

return magCN(c);

}

Page 21: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

21 Agilent Technologies

double complex_to_PhaseRad(COMPLEXNUMBER c)

{

return atan2(c.im, c.re);

}

double complex_to_PhaseDeg(COMPLEXNUMBER c)

{

return atan2(c.im, c.re)* (180/PI);

}

double logmag_to_linear(double d)

{

return pow(10.0, d/20.0);

}

COMPLEXNUMBER magAngle_to_complex(double mag, double angle)

{

COMPLEXNUMBER c;

c.re = mag * cos(angle);

c.im = mag * sin(angle);

return c;

}

void UnwrapPhase(COMPLEXNUMBER *c, vector<double> &unWrapPhase)

{

double phasePerPoint;

double phaseWrap = 0;

int numPoints = int(unWrapPhase.size());

vector<double> phase(numPoints);

vector<double> uwPhaseD(numPoints);

for (int i = 0; i < numPoints; i++)

{

phase[i] = complex_to_PhaseRad(c[i]);

}

for (int i = 0; i < numPoints; i++)

{

phasePerPoint = (i == 0 ? 0 : (phase[i] - phase[i - 1]));

phaseWrap = (phasePerPoint > PI ? (phaseWrap - (2*PI)) :

(phasePerPoint < -PI ? (phaseWrap + (2*PI)) : phaseWrap));

unWrapPhase[i] = (phase[i] + phaseWrap);

}

return;

}

// Set the values of the given reference arguments, assuming the following semantics:

// First argument in <arguments> array is number of points in data trace.

// Second argument is (0-based) frequency index.

// Third argument is pointer to first frequency value.

// Fourth argument is pointer to first response value.

void parse_args(COMPLEXNUMBER* arguments, int &order, int &num_points, int &index, COMPLEXNUMBER *&xvals, COMPLEXNUMBER *&yvals)

{

order = (int) arguments[0].re;

num_points = (int)arguments[1].re;

index = (int)arguments[2].re; // extract index

xvals = arguments + 3;

yvals = xvals + num_points;

}

void parse_args1(COMPLEXNUMBER* arguments, int &num_points, int &index, COMPLEXNUMBER *&yvals)

{

num_points = (int)arguments[0].re;

index = (int)arguments[1].re; // extract index

yvals = arguments + 2;

}

//--------------------end of helper functions---------------------------------------

//------------------------------------------------------------------------------

// Functions listed in the Equation Editor

//------------------------------------------------------------------------------

COMPLEXNUMBER normalizeTo1st(COMPLEXNUMBER* arguments)

{

//declare required variables

int ndx; //index of the current point in the sweep

COMPLEXNUMBER* dataArray ; //the complex number array of trace data

COMPLEXNUMBER curVal; //the complex number value of the current data point after normalization

static COMPLEXNUMBER firstVal; //the complex number of the trace's first data point

//parse the generic arguments list into it's constituent parts

ndx = (int) arguments[0].re;

dataArray = arguments + 1;

//find and store the first data point

if (ndx == 0)

{

firstVal = dataArray[ndx];

}

//normalize the current data value to the first by using the complex number divide function

curVal = divCN(dataArray[ndx], firstVal);

Page 22: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

22 Agilent Technologies

return curVal;

}

COMPLEXNUMBER unwrap(COMPLEXNUMBER* arguments)

{

int ndx;

int numPts;

COMPLEXNUMBER* trace_data;

COMPLEXNUMBER* unwrappedPhase;

COMPLEXNUMBER c;

c.re = 0;

c.im = 0;

parse_args1(arguments, numPts, ndx, trace_data);

static std::vector<double> phaseResults(numPts);

//this check is to take care of the case when the

//user changes the number of points in the channel.

//because phaseResults is a static vector, not doing

//this will cause the pna to crash.

if (phaseResults.size() != numPts)

{

phaseResults.resize(numPts);

}

//initialize the result vector on the first call

if (ndx == 0)

{

UnwrapPhase(trace_data, phaseResults);

}

//output of the UnwrapPhase function is in radians

//so convert to degrees before returning the result

c.re = phaseResults[ndx] * (180/PI);

return c;

}

//global taylor window coefficients

static double coef[9] = {0.4627189049, 0.0126816508, 0.0030274356, -0.0017856629, 0.0008841076

-0.0003824318, 0.0001214471, -0.0000041759,-.0000249569};

COMPLEXNUMBER Taylor_FullSpan(COMPLEXNUMBER* arguments)

{

//TODO: add function code here

int NumberOfElements, i, order;

COMPLEXNUMBER *FreqPt, *trace_data, c, Taylor_win;

c.re = 0;

c.im = 0;

parse_args(arguments, order, NumberOfElements, i, FreqPt, trace_data);

double FC, Fhigh, Flow, Value;

double *W;

int j;

W = new double[order];

Fhigh = FreqPt[0].re;

Flow = FreqPt[NumberOfElements-1].re;

FC = (Fhigh + Flow)/2;

Value = (FreqPt[i].re - FC) / (Fhigh - Flow);

for (j = 0; j < order; j++)

{

W[j] = 2 * coef[j] * cos(2 * PI * (j + 1) * Value); // this works in radinas

}

Taylor_win.re = 1;

Taylor_win.im = 0;

for (j = 0; j < order; j++)

{

Taylor_win.re += W[j];

}

c = multCN(trace_data[i] , Taylor_win); //this is a complex multiply function

delete [] W;

return c;

}

//--------------------end of functions listed in the Equation Editor--------------------

//TODO: return value needs to reflect the number of functions in the DLL

const int NUMBER_OF_FUNCTIONS = 3;

int listOfFunctions(char** const stringOfNames)

{

if(!stringOfNames){

return NUMBER_OF_FUNCTIONS;

}

Page 23: Custom Equations in PNA-X - Keysightna.support.keysight.com/pna/help/Custom Equations In PNA.pdf · 2010-12-03 · Writing Custom Equation DLLs As mentioned earlier, customers can

23 Agilent Technologies

//function display strings should be under one hundred characters in length

strcpy(stringOfNames[0],"normalizeTo1st,2 ,xAxisIndex(), traceDataArray(PARAM)");

strcpy(stringOfNames[1],"unwrap,3, getNumPoints(),xAxisIndex(),traceDataArray(PARAM)");

strcpy(stringOfNames[2],"Taylor_FullSpan,5,order, getNumPoints(),xAxisIndex(),xAxisArray(),traceDataArray(PARAM)");

//TODO: add your function strings here in the format of:

//strcpy(stringOfNames[index],"nameOfYourFunction, numOfArgs[,arg1, arg2, ... , argN]");

return 0;

}