Example 3. Creating a Butterworth Filter

Instrument:
N1000A
N109x
UXR Scope
Flex Apps:
FlexDCA
FlexRT
Meas. mode:
Scope
Eye
Jitter
TDR
Package License:
L-RND

This Python example creates a Butterworth user operator in the FlexDCA / Python environment. Although not required, it is a good practice to give the XML file and script files the same or similar base filename, but using different file name extensions of course! As the number of your measurements grow, you'll appreciate being able to quickly associate the different XML and script files at a glance.

Color has been added to the following example lines of the XML and script code to make them easier to read.

Because Example 1 showed how to install a user measurement, this example will not repeat the procedure.

Required Files

Butterworth.xml

Copy

Butterworth.xml

<?xml version="1.0" encoding="utf-8"?>
<Function xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      Application="FlexDCA" MinimumSoftwareVersion="5.00.328">
   <Name>Butterworth</Name>
   <Abbreviation>Butter</Abbreviation>
   <Comments>Implements a Butterworth filter.</Comments>
   <Script>Butterworth.py</Script>
   <Icon></Icon>
   <FunctionType>1 Source</FunctionType>
   <Variable Name = "FType" Value = "low"/>
   <Control Name = "N">
      <Integer>
         <Min>1</Min>
         <!--Python Butterworth implementation has problems above 4th order.-->
         <Max>4</Max>
         <Units>None</Units>
         <Default>2</Default>
      </Integer>
   </Control>
   <Control Name = "Fc">
      <Double>
         <Min>10e6</Min>
         <Max>1000e9</Max>
         <Resolution>1000</Resolution>
         <Units>Hertz</Units>
         <Default>7.5e9</Default>
      </Double>
   </Control>
</Function>

Butterworth.py

Copy

Butterworth.py

""" Python 3.9 FlexDCA Butterworth User Operator. 
"""

import math
import numpy as np
from scipy.fftpack import fft, ifft
from scipy.signal import butter, freqz, lfilter

# Expects a dictionary mapping from variable name to value.
def algorithm(inputVars):

    # Initialize
    FiltData = []
    XOrg = 0.0
    XInc = 0.0
    Gain = 1.0
    FilterWidth = 0.0
    FilterDelay = 0.0
    XUnits = 'Same'
    YUnits = 'Same'
    ErrorMsg = ''

    # N, Fc, and FType are additional variables specified in the XML config file
    # for this filter.
    if ('N' not in inputVars) or ('Fc' not in inputVars) or ('FType' not in inputVars):
        return { 'ErrorMsg' : 'Please configure the operator by loading the XML configuration file ' \
                              'rather than loading the Python script directly.' }

    # Gather custom controls
    N = inputVars['N']
    Fc = inputVars['Fc']
    FType = inputVars['FType']

    # Gather system info
    SrcData = inputVars['SrcData']
    XOrg = inputVars['XOrg']
    XInc = inputVars['XInc']
    IsPeriodic = inputVars['IsPeriodic']

    FilterDelay = N * 0.112 / Fc
    FilterWidth = N / Fc

    num_points = len(SrcData)
    if num_points > 0:
        # Create the filter
        Wn = 2 * XInc * Fc
        b, a = butter(N, Wn, FType)

        if IsPeriodic:
            # Perform filtering circularly using FFTs
            fft_src_data = fft(SrcData)
            _unused, H = freqz(b, a, num_points, True)
            FiltData = ifft(fft_src_data * H).real

            # Rotate to remove FilterDelay.
            shift = int(round(FilterDelay / XInc))
            FiltData = np.roll(FiltData, -shift)
        else:
            start_index = int(math.ceil(FilterWidth / XInc))
            if start_index >= num_points:
                ErrorMsg = 'Too few points. Pipeline eats up entire waveform.'
            else:
                # Perform filtering
                FiltData = lfilter(b, a, SrcData)

                # Remove filter delay and pipeline
                FiltData = FiltData[start_index:]
                leading_pipeline = FilterWidth - FilterDelay
                XOrg = XOrg + leading_pipeline

    # Return output variables as dictionary.
    return { 'FiltData' : FiltData,
             'XOrg' : XOrg,
             'XInc' : XInc,
             'Gain' : Gain,
             'FilterWidth' : FilterWidth,
             'FilterDelay' : FilterDelay,
             'XUnits' : XUnits,
             'YUnits' : YUnits,
             'ErrorMsg' : ErrorMsg
           }