Differential IMD Measurement for PNA-X Pro with Two Internal Combiners


This Python example performs a differential IMD measurement (input 1 and 2 and output 2 and 4), using two internal combiners (requires Option 471, 479, 481, or 489) and Option CB2 for the combiner on the second test port.

See Also

Python Basics

"""

This example is done for PNAX-Pro for differential IMD (input 1 and 2 and output 2 and 4) using internal combiners (requires options 471 or 479 or 481 or 489)   and CB2 for the combiner on the second test port.

"""

def Mbox(title, text, style):

    return ctypes.windll.user32.MessageBoxW(0, text, title, style)

    ##  Styles:

    ##  0 : OK

    ##  1 : OK | Cancel

    ##  2 : Abort | Retry | Ignore

    ##  3 : Yes | No | Cancel

    ##  4 : Yes (6)| No (7)

    ##  5 : Retry | Cancel

    ##  6 : Cancel | Try Again | Continue

   

import numpy as np

import pyvisa as visa

from win32com.client import *

import ctypes # An included library with Python install.

import time

import matplotlib.pyplot as plt

import pyvisa

rm = pyvisa.ResourceManager()

rm.list_resources()

ressource_list = rm.list_resources()

address = "SOCKET"

if address =="INSTR":

    PNA_X = rm.open_resource('TCPIP0::localhost::inst0::INSTR',write_termination='\n',read_termination='\n',timeout=20000)

    IDN_PNAX=PNA_X.query("*IDN?")

    print(IDN_PNAX)

   

if address =="SOCKET":

    PNA_X = rm.open_resource('TCPIP0::localhost::5025::SOCKET',write_termination='\n',read_termination='\n',timeout=20000)

    IDNPNA=PNA_X.query("*IDN?")

    print(IDNPNA)

PNA_X.write('OUTP OFF')

ActiveChannel=1

channelOne=1

answePreset=Mbox('','Do a Preset?',1)

if answePreset==1:

    PNA_X.write("SYST:FPR")

    PNA_X.write("*OPC")

Mbox('','Configuration of Diff IMD measurements ',0)

InputPort_diffP1=1

InputPort_diffN1=2

InputPort_diffP2=3

InputPort_diffN2=4

# Define option groups based on your constraints

mutually_exclusive_group = {"471", "479", "481", "489"}

mandatory_option = "CB2"

# This part is just to check the options for the PNAX

try:

    # Query instrument options

    opt_string = PNA_X.query("*OPT?").strip()

   

    # Clean and convert the string into a Python set for efficient comparison

    installed_options = {opt.strip().replace('"', '') for opt in opt_string.split(',')}

   

    # 1. Verify the mandatory CB2 option

    has_cb2 = mandatory_option in installed_options

   

    # 2. Use set intersection to find which exclusive options are installed

    found_exclusive_options = mutually_exclusive_group.intersection(installed_options)

    has_exactly_one_exclusive = len(found_exclusive_options) == 1

   

    # Display diagnostic results

    print("--- PNA-X Configuration Analysis ---")

    print(f"Detected options: {', '.join(installed_options)}")

    print(f"Mandatory option CB2: {' Present' if has_cb2 else ' Missing'}")

    print(f"Exclusive options found: {list(found_exclusive_options)}")

   

    # Final constraints validation

    if has_cb2 and has_exactly_one_exclusive:

        print(f"\n VALID Configuration: CB2 is present and only option {list(found_exclusive_options)} is installed.")

        Mbox('',f'\n VALID Configuration: CB2 is present and only option {list(found_exclusive_options)} is installed.',0)

    else:

        print("\n INVALID Configuration:")

        if not has_cb2:

            print("   - The mandatory option CB2 is missing.")

            Mbox('',f"\n INVALID Configuration:""   - The mandatory option CB2 is missing.",0)

        if len(found_exclusive_options) == 0:

            print(f"   - No option from the group {list(mutually_exclusive_group)} was found (exactly one required).")

            Mbox('',f"\n INVALID Configuration:"f"   - No option from the group {list(mutually_exclusive_group)} was found (exactly one required).",0)

        elif len(found_exclusive_options) > 1:

            print(f"   - Conflict! Multiple exclusive options detected simultaneously: {list(found_exclusive_options)}")

except pyvisa.VisaIOError as e:

    print(f"VISA Communication Error: {e}")

#Create DIQ channel

#Channel1

PNA_X.write("DISP:WIND ON")

PNA_X.write("DISPlay:WINDow"+str(channelOne)+":TITLe:DATA 'RF frequency sweep DIFF IMD'")

PNA_X.write("DISPlay:WINDow"+str(channelOne)+":TITLe:STATe 1")

PNA_X.write("CALC1:MEAS1:DEF 'IPwrF1:Differential I/Q'") #Create DIQ Trace

PNA_X.write("DISP:WIND1 ON") #Create Window 1 on turn state on

PNA_X.write("DISP:MEAS1:FEED 1") #Dislay the Trace 1 in window 1

PNA_X.write("CALC"+str(channelOne)+":PAR:MNUM 1,FAST") #Select trace 1

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLoIn-','a{InputPort_diffP1}_F2'")#Create a new DIQ parameters with custom equation

PNA_X.write("CALC"+str(channelOne)+":CUST:MOD 'PwrMainLoIn-'")# Modify the measurement 1 (Trace 1 ) with the new parameters

PNA_X.write("SENSe"+str(channelOne)+":SWEep:MODE HOLD")# Put sweep to Hold

 

# Put all port to combiner mode

PNA_X.write(f"SENS{channelOne}:PATH:CONF:ELEM 'Port1Bypass','combiner'")

PNA_X.write(f"SENS{channelOne}:PATH:CONF:ELEM 'Port2Bypass','combiner'")

PNA_X.write(f"SENS{channelOne}:PATH:CONF:ELEM 'Port3Bypass','combiner'")

PNA_X.write(f"SENS{channelOne}:PATH:CONF:ELEM 'Port4Bypass','combiner'")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP1}:POW:ATT:AUTO 0,'Port {InputPort_diffP1}';*OPC") # to avoid warning message phase control has disabled auto attenuation on controlled port

user_RFstartfreq=500e6 #First tone start freq

user_RFstopfreq=2e9 #First tone stop freq

user_RFdeltafreq=10e6 #Second tone will be First tone + delta frequency

user_IFBW=1000

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG1:STAR "+str(user_RFdeltafreq))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG1:STOP "+str(user_RFdeltafreq))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG1:IFBW "+str(user_IFBW))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG:ADD")# Add new segment

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG2:STAR "+str(user_RFstartfreq))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG2:STOP "+str(user_RFstopfreq))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG2:IFBW "+str(user_IFBW))

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG:ADD")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG3:COUP:STATe ON")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG3:COUP:ID 2")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG3:COUP:OFFS 1")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG3:COUP:UCONvert 1")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG:ADD") #2f1-f2

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG4:COUP:STATe ON")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG4:COUP:ID 2")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG4:COUP:OFFS 3")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG4:COUP:UCONvert 0")

PNA_X.write("SENSe"+str(channelOne)+":DIQ:FREQuency:RANGe4:COUPle:MULTiplier 2")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG:ADD") #2f2-f1

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG5:COUP:STATe ON")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG5:COUP:ID 3")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG5:COUP:OFFS 2")

PNA_X.write("SENS"+str(channelOne)+":DIQ:FREQ:RANG5:COUP:UCONvert 0")

PNA_X.write("SENSe"+str(channelOne)+":DIQ:FREQuency:RANGe5:COUPle:MULTiplier 2")

#InputPort_diffP1=1

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP1}:POW:ATT:AUTO 0,'Port {InputPort_diffP1}';*OPC") # to avoid warning message phase control has disabled auto attenuation on controlled port

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP1}:RANG 2,'Port {InputPort_diffP1}';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP1}:STAT AUTO,'Port {InputPort_diffP1}';*OPC")

PNA_X.write("SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffP1)+":PHASe:STARt 0;*OPC")

PNA_X.write("SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffP1)+":PHASe:STOP 0;*OPC")

#InputPort_diffN1=2

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN1}:PHAS:REF 'Port {InputPort_diffP1}';*OPC")    

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN1}:POW:ATT:AUTO 0,'Port {InputPort_diffN1}';*OPC") # to avoid warning message phase control has disabled auto attenuation on controlled port

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN1}:RANG 2,'Port {InputPort_diffN1}';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN1}:STAT AUTO,'Port {InputPort_diffN1}';*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN1)+":PHASe:STARt 180;*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN1)+":PHASe:STOP 180;*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN1)+":PHAS:STAT CONT;*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN1}:PHAS:PAR 'a2/a1'")

#InputPort_diffP2=3 Port 1 Src3

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP2+2}:POW:ATT:AUTO 0,'Port 1 Src3';*OPC") # to avoid warning message phase control has disabled auto attenuation on controlled port

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP2+2}:RANG 3, 'Port 1 Src3';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffP2+2}:STAT AUTO,'Port 1 Src3';*OPC")

PNA_X.write("SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffP2+2)+":PHASe:STARt 0, 'Port 1 Src3';*OPC")

PNA_X.write("SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffP2+2)+":PHASe:STOP 0, 'Port 1 Src3';*OPC")

#InputPort_diffN2=4 Port 2 Src4

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN2+2}:PHAS:REF 'Port 1 Src3','Port 2 Src4';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN2+2}:POW:ATT:AUTO 0,'Port 2 Src4';*OPC") # to avoid warning message phase control has disabled auto attenuation on controlled port

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN2+2}:RANG 3,'Port 2 Src4';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN2+2}:STAT AUTO,'Port 2 Src4';*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN2+2)+":PHASe:STARt 180,'Port 2 Src4';*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN2+2)+":PHASe:STOP 180,'Port 2 Src4';*OPC")

PNA_X.write(f"SENS"+str(channelOne)+":DIQ:PORT"+str(InputPort_diffN2+2)+":PHAS:STAT CONT,'Port 2 Src4';*OPC")

PNA_X.write(f"SENS{channelOne}:DIQ:PORT{InputPort_diffN2+2}:PHAS:PAR 'a2/a1','Port 2 Src4';*OPC")

NextTr=2

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLoIn+','a{InputPort_diffN1}_F2'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'PwrMainLoIn+'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHiIn-','a{InputPort_diffP1}_F3'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'PwrMainHiIn-'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHiIn+','a{InputPort_diffN1}_F3'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'PwrMainHiIn+'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLo','(b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)/SQRT(2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHi','(b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)/SQRT(2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'Pwr3Lo','(b{InputPort_diffP2}_F4-b{InputPort_diffN2}_F4)/SQRT(2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'Pwr3Hi','(b{InputPort_diffP2}_F5-b{InputPort_diffN2}_F5)/SQRT(2)'")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IM3Lo','(b{InputPort_diffP2}_F4-b{InputPort_diffN2}_F4)/(b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'IM3Lo'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IM3Hi','(b{InputPort_diffP2}_F5-b{InputPort_diffN2}_F5)/(b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'IM3Hi'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IM3InLo','(a{InputPort_diffP1}_F4-a{InputPort_diffN1}_F4)/(a{InputPort_diffP1}_F2-a{InputPort_diffN1}_F2)'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'IM3InLo'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

NextTr=NextTr+1

PNA_X.write(f"CALC1:MEAS{NextTr}:DEF 'IPwrF1:Differential I/Q'")

PNA_X.write(f"DISP:MEAS{NextTr}:FEED 1")

PNA_X.write(f"CALC{channelOne}:PAR:MNUM {NextTr},FAST")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IM3InHi','(a{InputPort_diffP1}_F5-a{InputPort_diffN1}_F5)/(a{InputPort_diffP1}_F4-a{InputPort_diffN1}_F4)'")

PNA_X.write(f"CALC{channelOne}:CUST:MOD 'IM3InHi'")

PNA_X.write(f"SENS{channelOne}:SWE:MODE HOLD")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'OIP3Lo','(b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)/SQRT(2)*sqrt((b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)/(b{InputPort_diffP2}_F4-b{InputPort_diffN2}_F4))'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'OIP3Hi','(b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)/SQRT(2)*sqrt((b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)/(b{InputPort_diffP2}_F5-b{InputPort_diffN2}_F5))'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IIP3Lo','(a{InputPort_diffP1}_F2-a{InputPort_diffN1}_F2)/SQRT(2)*sqrt((b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)/(b{InputPort_diffP2}_F4-b{InputPort_diffN2}_F4))'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'IIP3Hi','(a{InputPort_diffP1}_F3-a{InputPort_diffN1}_F3)/SQRT(2)*sqrt((b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)/(b{InputPort_diffP2}_F5-b{InputPort_diffN2}_F5))'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLoIn','(a{InputPort_diffP1}_F2-a{InputPort_diffN1}_F2)/SQRT(2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHiIn','(a{InputPort_diffP1}_F3-a{InputPort_diffN1}_F3)/SQRT(2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'ToneGainLo','(b{InputPort_diffP2}_F2-b{InputPort_diffN2}_F2)/(a{InputPort_diffP1}_F2-a{InputPort_diffN1}_F2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'ToneGainHi','(b{InputPort_diffP2}_F3-b{InputPort_diffN2}_F3)/(a{InputPort_diffP1}_F3-a{InputPort_diffN1}_F3)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLoBal','(b{InputPort_diffP2}_F2/b{InputPort_diffN2}_F2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHiBal','(b{InputPort_diffP2}_F3/b{InputPort_diffN2}_F3)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainLoBalIn','(a{InputPort_diffP1}_F2/a{InputPort_diffN1}_F2)'")

PNA_X.write(f"SENS{channelOne}:DIQ:PAR:DEF 'PwrMainHiBalIn','(a{InputPort_diffP1}_F3/a{InputPort_diffN1}_F3)'")

PNA_X.write("sense"+str(channelOne)+":diq:parameter:delete 'IPwrF1'")

PNA_X.write("sense"+str(channelOne)+":diq:parameter:delete 'OPwrF1'")

PNA_X.write("sense"+str(channelOne)+":diq:parameter:delete 'GainF1'")

Mbox('','Done with diff IMD config',0)