Remote Experimentation with a VNA on Python

Sohum Suthar | Lee Teng @ aps

Info

Understanding the VNA Hierarchy: Channels, Traces, and Ports

Before you begin, refer to the setup docs if needed: Interfacing the ZVA-24 VNA with a Windows PC Using LAN with Python.

Channels

A channel on a VNA represents a complete measurement setup. Each channel defines specific measurement conditions such as frequency range, sweep type, and measurement parameters. Multiple channels can be configured on a VNA, and each channel can have multiple traces.

  • Frequency Range: Defines the start and stop frequencies for the sweep.
  • Sweep Type: Can be linear, logarithmic, segmented, etc.
  • Number of Points: The number of data points in the sweep.
  • Power Level: The output power for the measurement.
  • IF Bandwidth: Intermediate frequency bandwidth, affecting the noise floor and measurement speed.

Example commands:

zva.write('SENS1:FREQ:START 1e9')  # set the start frequency of channel 1 to 1 GHz
zva.write('SENS1:FREQ:STOP 2e9')   # set the stop frequency of channel 1 to 2 GHz
zva.write('SENS1:SWE:POIN 201')    # set the number of points for the sweep in channel 1 to 201
zva.write('SOUR1:POW:LEV -10')     # set the power level for channel 1 to -10 dBm

Traces

A trace is a visual representation of a specific measurement on the VNA's display. Each trace is associated with a particular channel and measurement parameter. Multiple traces can be displayed simultaneously.

Parameters related to traces:

  • Measurement Format: Defines how the data is displayed (e.g., Log Magnitude, Phase, Smith Chart).
  • Trace Math: Operations that can be applied to traces, like addition, subtraction, averaging, etc.

Example commands:

zva.write("CALC1:PAR:DEF 'S11'")  # define the parameter for trace 1 of channel 1 as S11
zva.write("DISP:WIND1:TRAC1:Y:AUTO")  # auto-scale the Y-axis of trace 1 in window 1

Ports

A port on a VNA is a physical connection point where the device under test (DUT) is connected.

Parameters related to ports:

  • Port Number: Identifies the specific port on the VNA (e.g., Port 1, Port 2).
  • Port Extension: Adjustments made to account for delays or mismatches in the test setup.
  • Port Power: Output power level specific to each port.

Example commands:

zva.write('SOUR1:POW:LEV -10')  # set the power level for port 1 to -10 dBm
zva.write('SENS1:CORR:EDEL:TIME 1e-9')  # set the electrical delay for port 1 to 1 ns

Sweep Modes and Segment Overlaps

The Define Segments dialog contains a table to edit the individual segments of the sweep range. Sweep segments may have common points or even overlap. However, overlapping sweep segments might lead to ambiguous behavior with certain functions like marker functions, trace evaluation functions, trace search functions, and band filter functions. Be cautious when using overlapping segments to avoid unexpected results.

Segmented Sweep Setup:

Example commands:

zva.write('SENS1:SWE:TYPE SEGM') # set sweep type to segmented
zva.write('SENS1:SEGM:DEL:ALL') # delete all existing segments
zva.write('SENS1:SEGM1:ADD') # add segment 1
zva.write('SENS1:SEGM1:FREQ:STAR 1e9')
zva.write('SENS1:SEGM1:FREQ:STOP 1.5e9')
zva.write('SENS1:SEGM1:SWE:POIN 201')
zva.write('SENS1:SEGM1:POW -10')
zva.write('SENS1:SEGM1:BAND 1e3')
zva.write('SENS1:SEGM2:ADD') # add segment 2
zva.write('SENS1:SEGM2:FREQ:STAR 1.5e9')
zva.write('SENS1:SEGM2:FREQ:STOP 2e9')
zva.write('SENS1:SEGM2:SWE:POIN 401')
zva.write('SENS1:SEGM2:POW 0')
zva.write('SENS1:SEGM2:BAND 3e3')

Commands Overview

Basic Commands for VNA Operation

Reset the VNA

zva.write('*RST')

Configure Channel

zva.write('SENS1:FREQ:START 1e9') # start frequency 1 GHz
zva.write('SENS1:FREQ:STOP 2e9') # stop frequency 2 GHz
zva.write('SENS1:SWE:POIN 201') # 201 points
zva.write('SOUR1:POW:LEV -10') # power level -10 dBm
zva.write('SENS1:BAND 1e3') # if bandwidth 1 kHz

Define Traces

zva.write("CALC1:PAR:DEF 'Trc1', 'S11'") # define trace 1 as S11
zva.write("CALC1:PAR:DEF 'Trc2', 'S21'") # define trace 2 as S21
zva.write("DISP:WIND1:TRAC1:FEED 'Trc1'") # display trace 1 in window 1
zva.write("DISP:WIND1:TRAC2:FEED 'Trc2'") # display trace 2 in window 1

Start the Sweep

zva.write('INIT1:IMM; *WAI')  # start the sweep and wait for completion

Retrieve Data

s11_data = zva.query('CALC1:DATA? FDATA')  # get S11 data
s21_data = zva.query('CALC1:DATA? FDATA2') # get S21 data

USING THE VNA

Initializing the VNA

To initialize the VNA, you need to establish a connection with the instrument, configure the measurement channels, and set up the necessary parameters. Here’s a detailed step-by-step process:

Reset the VNA

Ensure the VNA is in a known state by resetting it.

zva.write('*RST')

Configure Channel Parameters

Set the frequency range, number of points, power level, and IF bandwidth for the desired channel.

zva.write('SENS1:FREQ:START 1e9')  # set the start frequency of channel 1 to 1 GHz
zva.write('SENS1:FREQ:STOP 2e9')   # set the stop frequency of channel 1 to 2 GHz
zva.write('SENS1:SWE:POIN 201')    # set the number of points for the sweep in channel 1 to 201
zva.write('SOUR1:POW:LEV -10')     # set the power level for channel 1 to -10 dBm
zva.write('SENS1:BAND 1e3')        # set the IF bandwidth for channel 1 to 1 kHz

Define Traces

Specify the measurement parameters for the traces.

zva.write("CALC1:PAR:DEF 'Trc1', 'S11'")  # define trace 1 as S11
zva.write("CALC1:PAR:DEF 'Trc2', 'S21'")  # define trace 2 as S21
zva.write("DISP:WIND1:TRAC1:FEED 'Trc1'") # display trace 1 in window 1
zva.write("DISP:WIND1:TRAC2:FEED 'Trc2'") # display trace 2 in window 1

Initiate the Sweep

Start the sweep and wait for it to complete.

zva.write('INIT1:IMM; *WAI')  # start the sweep and wait for completion

Stopping the Experiment

To stop the experiment and safely terminate the connection to the VNA, follow these steps:

Stop Continuous Sweep

Ensure the VNA stops any ongoing continuous sweeps.

zva.write(f'INITiate1:CONTinuous OFF')  # hold sweep for channel 1

Return to Local Control

Set the VNA to local control mode.

zva.write('GTL')  # go to local

Close the Connection

Properly close the connection to the VNA.

zva.close()

See "Example Python Script" for a complete script incorporating everything above.

Advanced Commands for Specific Configurations

Set Sweep Type to Segmented

zva.write('SENS1:SWE:TYPE SEGM') # set sweep type to segmented
zva.write('SENS1:SEGM:DEL:ALL') # delete all existing segments

Add Segments to Sweep

zva.write('SENS1:SEGM1:ADD') # add segment 1
zva.write('SENS1:SEGM1:FREQ:STAR 1e9')
zva.write('SENS1:SEGM1:FREQ:STOP 1.5e9')
zva.write('SENS1:SEGM1:SWE:POIN 201')
zva.write('SENS1:SEGM1:POW -10')
zva.write('SENS1:SEGM1:BAND 1e3')
zva.write('SENS1:SEGM2:ADD') # add segment 2
zva.write('SENS1:SEGM2:FREQ:STAR 1.5e9')
zva.write('SENS1:SEGM2:FREQ:STOP 2e9')
zva.write('SENS1:SEGM2:SWE:POIN 401')
zva.write('SENS1:SEGM2:POW 0')
zva.write('SENS1:SEGM2:BAND 3e3')

Software Configuration

Reading Information Using Python

Install the necessary packages in your Python environment:

python -m pip install -U pyvisa numpy

python -m pip install -U pyvisa-py

Example Python Script

This script initializes the VNA, performs a sweep, and saves the data.

import os
import numpy as np
import pyvisa
import skrf as rf

vna_address = 'TCPIP0::your-instrument-address::inst0::INSTR'
experiment_name = 'vna_test'

with pyvisa.ResourceManager('@py').open_resource(vna_address) as zva:
    channels = [1]
    meas = []
    frequencies = []
    measurements = []
    trace_definitions = []


    for ch in channels:
        trace_names_and_defs = zva.query_ascii_values(f'CALCulate{ch}:PARameter:CATalog?', converter='s', separator='\n')[0][1:-1].split(',')
        trace_names = trace_names_and_defs[::2]
        trace_defs = trace_names_and_defs[1::2]
        zva.write(f'SYSTem:DISPlay:UPDate ON')
        zva.write(f'INITiate{ch}:CONTinuous OFF') # hold sweep
        zva.write(f'INITiate{ch}:IMMediate:SCOPE SINGLE') # select channel to sweep
        zva.write(f'INITiate{ch}:IMMediate; *WAI') # wait for sweep to finish
        all_data = []


        for trace_name in trace_names:
            zva.write(f'CALCulate{ch}:PARameter:SELect {trace_name}') # select active trace
            data = zva.query_ascii_values(f'CALCulate{ch}:DATA? SDATA', container=np.array).reshape((-1,2)) # get data
            data = data[:,0] + data[:,1]*1j # construct back to complex number
            all_data.append(data)


        meas.append(all_data)
        meas = np.array(meas)
        f = zva.query_ascii_values(f'CALCulate{ch}:DATA:STIMulus?', container=np.array) # get frequency (x-axis)


        frequencies.append(f)
        measurements.append(meas)
        trace_definitions.append(trace_defs)
        zva.write(f'INITiate{ch}:CONTinuous ON') # resume sweep in continuous mode


    zva.write('GTL') # go to local
    zva.close()


    scrape_vna.save_touchstone(bypass=None, freqs = frequencies, meas = measurements, trace_definitions = trace_definitions)

Program Flow

3

Code Summary

  1. Initialization: Connects to the VNA and retrieves its identification information.
  2. Data Collection: Performs a sweep, collects measurement data, and stores it in variables.
  3. Data Export: Saves the collected data into Touchstone files for further analysis.

Using the Script

  • Ensure you have the necessary Python packages installed.
  • Modify the vna_address to match your VNA's IP address.
  • Run the script to perform the sweep and save the data.

Additional Resources

For a detailed setup guide, refer to the Interfacing the ZVA-24 VNA with a Windows PC Using LAN with Python.