我认为我认为测试设备自动化很棒,这是不言而喻的。如果您已经掌握了基础知识并了解了一些状态报告的复杂性,那么您就可以很好地驾驶几乎任何测试设备。请注意,我几乎说过——那是因为我们都必须面对的最后一个障碍——乐器的怪癖。
虽然 SCPI 是一个标准,但并非所有制造商或设备都会以相同的方式实现它,有些甚至会不遗余力地做出一些不同。在这篇文章中,我将介绍我之前在 2014 年评测过的泰克 PA1000 功率分析仪,其传统归功于 Voltech PM1000+。一般来说,市场上的功率分析仪并不多,但这款分析仪通过使用 PWRVIEW 软件,具有符合 IEC 62301 Ed.2 的待机功耗认证功能。
可悲的是,泰克似乎并没有对这一系列仪器给予太多关注,因为该软件最后一次更新是在 2016 年,并且在 Windows 10 上运行时拒绝与仪器连接。因此,我有一台 Windows 7 机器,只是为了不经常运行该软件。
话虽这么说,PA1000 确实能够通过 USB、GPIB 或以太网进行远程控制,因此对于简单的日志记录,最好只编写我们自己的软件。
你说古怪吗?
深入研究它,我认为为 PA1000 编写一些软件会很简单,而且在大多数情况下确实如此。然而,它需要掌握 PA1000 远程控制实现的一些怪癖。
首先要考虑的是,该设备的以太网连接不符合 LXI。这意味着没有漂亮的 Web 界面,也没有 VXI-11 协议支持。任何数据传输都严格通过套接字传输,尽管 SCPI 直接端口为 5025。虽然这意味着您会丢失一些GPIB总线信号仿真,但这本身并不是什么大问题。
例外情况是它将响应所有与 CR 的通信。这是一些基于套接字的仪器表达的常见特征,但通常对于这些仪器来说,它是 LF(即“\n”),并且此响应并不适用于所有通信。
为此编码有点棘手,因为仪器似乎几乎在发送的每个命令上都会返回 CR,但如果有响应,它就会跟随,然后被 LF 终止。目前一个简单的解决方法是将每个命令作为查询发送,但可能有一种更优雅的方法来解决这个问题。我还没有深入研究数据包级别来找出实际来回发送的内容......但让它工作对我来说已经足够了。
手册中也说明了下一个怪癖......
至少他们足够友善地警告我们,这个单位需要在每个命令上使用“根”:指示符。这通常没有必要,但这个乐器很挑剔。手册中还有另一部分说它也不接受分号分隔的命令——这对我来说不是问题,因为无论如何我通常都会在单独的行上分隔命令。
(看似)最后一个怪癖是该单位没有能力报告命令执行状态——没有 *OPC?相反,设置命令需要 0.5 秒的手动延迟——流量控制在 TCP 世界中并不真正“存在”,我可以确认向设备发送大量测量选择命令会导致它们被忽略(可能是因为缓冲区溢出损坏)。也许以太网是由一个“简单”的以太网微控制器实现的,该微控制器正在解码流并通过 UART 将其发送到主机。更烦人的是,设备在发送 *RST 后需要 5-10 秒才能恢复正常——这在手册中也有说明。
一旦我们掌握了所有这些,我们就可以担心实际读取 PA1000 的结果了。幸运的是,分析器会指示 DSR 寄存器中何时有一组新数据可用,该数据集是使用 DSE 寄存器启用的。
该手册提供了这段伪代码,让您了解如何实现这一点,但是,使用它们自己的寄存器约定(不属于标准 SCPI 状态模型)有点不寻常。然而,没有什么是无法解决的。
通读手册,有一些区域也存在拼写错误和不正确的措辞(例如,应该是电流的电压),但没有考虑所有可能性,在实际需要之前不太可能发现它们。
代码
注意到这些怪癖,我决定编写自己的代码,我称之为 pa1000-logger-v1,可以作为 ZIP 文件下载。代码列表如下 - 使用风险由您自行承担,不作任何明示或暗示的保证,并且我对您可能遭受的任何损害不承担任何责任,无论它们如何发生。根据需要随意修改和重复使用。
# Tektronix PA1000 and pyvisa Logger Example # by Gough Lui (goughlui.com) - May 2021 import pyvisa import time import os resource_manager = pyvisa.ResourceManager() # Change VISA Resource String to match your device ins_pa1000 = resource_manager.open_resource("TCPIP0::192.168.80.4::5025::SOCKET") ins_pa1000.read_termination = "\n" # termination for Ethernet Only! ins_pa1000.write_termination = "\n" def pa1000_configure(cmd) : ins_pa1000.query(cmd) # Commands sent as Queries because of instrument response "\n" if cmd.upper() == "*RST" : time.sleep(10) # delay recommended by manual for reset else : time.sleep(0.5) # delay recommended by manual for configure print("Available: " + ins_pa1000.query("*IDN?")) print("Setting Up - PA1000") pa1000_configure("*RST") # Set Up the PA1000 for Power Quality - See Manual for All Measurements # VLT, AMP, WAT, VAS, VAR, FRQ, PWF, VPK+, VPK-, APK+, APK-, VDC, ADC, VCF # VDF, ADF, IMP, RES, REA, HR, WHR, VAH, VRH, AHR, VRNG, ARNG, VHM, AHM pa1000_configure(":SEL:CLR") # Start by clearing all selections pa1000_configure(":SEL:VLT") pa1000_configure(":SEL:FRQ") pa1000_configure(":SEL:VCF") pa1000_configure(":SEL:VHM") # Select Voltage Harmonics pa1000_configure(":HMX:VLT:SEQ 0") # Select Odd and Even pa1000_configure(":HMX:VLT:RNG 50") # Report to 50th Harmonic pa1000_configure(":RNG:VLT AUT") # Set Auto Range - or use value (1-10) - see manual pa1000_configure(":FSR:VLT") # Frequency Source from Voltage pa1000_configure(":BDW 0") # HF Filter BW Selection 1MHz pa1000_configure(":INP:FILT:LPAS 0") # Disable Low-Pass Filter pa1000_configure(":BLK:DIS") # Disable Blanking of Low Values pa1000_configure(":AVG 0") # Disable Averaging pa1000_configure(":SYST:ZERO 1") # Enable Auto-Zero #pa1000_configure(":RNG:AMP AUT") # for current measurement range #pa1000_configure(":SHU:INT") # Choice of shunts for current measurements #pa1000_configure(":SHU:INT1A") #pa1000_configure(":SHU:EXT") pa1000_configure(":DSE 2") # Data Status Enable for New Measurement while True : try: logname=input("Ready to Log - Enter Log Filename: ") if len(logname)==0 : raise # Append extension if necessary if "." not in logname[-4::] : logname=logname+".csv" if os.path.isfile(logname) : raise except: print("Invalid name or file already exists. Try again.") continue break f=open(logname,"w") input("Press ENTER to start, CTRL-C to abort") measvals=ins_pa1000.query(":FRF?").split(",")[2::] # Retrieve list of measurements f.write("Time,") i=0 while i < len(measvals)-1 : f.write(measvals[i]+",") i=i+1 f.write(measvals[i]+"\n") # Print a header row ins_pa1000.query(":DSR?") # Clear flag to grab next reading logcnt = 0 try: while True: if ins_pa1000.query(":DSR?") == "2" : # Read Data Status Register for New Data f.write(str(time.time())+","+str(ins_pa1000.query(":FRD?"))+"\n") f.flush() logcnt=logcnt+1 print("Logged: "+str(logcnt)+" samples\r",end="") time.sleep(0.1) except KeyboardInterrupt: pass print("") print("Aborted. Cleaning Up!") f.close() ins_pa1000.close()
在这段代码中,我编写了一个简单的pa1000_configure函数,该函数将配置命令作为查询发送,并根据命令进行必要的延迟。注释了一些配置命令,它们指示了一些用于电流测量的有用配置——在本例中,我重点介绍了电能质量参数。
采用文件名,验证它不为空,附加扩展名并检查具有相同名称的现有文件。日志记录从使用 :FRF? 读出列标题 - 但前两个字段(计数)与标题无关,因此我们放弃它们。但是我们确实添加了一个时间标头值,因为我从 Python 打印出来了。
值得注意的是,轮询 DSR 寄存器的代码有 0.1 秒的延迟 - 这是为了减少进出 PA1000 的流量,PA1000 最多只能每 0.5 秒返回一次结果。如果您想要更准确的计时,请删除相应的 time.sleep(0.1) 行。
在记录期间,代码会打印记录的样本数,但不会打印样本数据,因为如果您选择将电压和电流谐波设置为 50 次谐波,则该装置可能会返回数百列,因为它将返回每个列的幅度和相位。在此代码中,使用 CTRL-C 中止将正常关闭代码。
结果
毫不奇怪,自从我编写了代码以来,它似乎对我来说工作得很好。当然,我已经尝试灵活地使用它,使其能够记录所有可能的参数选择,但不能保证。
当设备没有有效的电压输入时,谐波数据将以三个破折号的形式返回,以指示“空白”显示,而不是像其他一些设备那样的 NaN 或“虚拟”值(即非常大或小的东西)。我想这只是 PA1000 的另一个“怪癖”。
结论
虽然由于标准化,自动化仪器应该很容易,但并非所有仪器都会以相同的方式遵循标准。因此,最后一个障碍有时是不可避免的——解决乐器自身的怪癖。泰克 PA1000 的以太网接口似乎更像是一个“附加组件”,它不符合 LXI 标准,并且在响应每次通信时都带有“\r”和任何信息后跟最后的“\n”方面存在一些怪癖。还有硬编码延迟的要求,以确保仪器有时间处理命令并且无法识别命令何时完成(即没有*OPC?命令都必须以 的 “根”为前缀 : 即使这通常不是必需的,并且不支持命令串联。
因此,使用该仪器需要阅读一些手册并严格遵循它,以确保您获得正确的结果。这涉及到处的一些解决方法,有一些令人头疼的问题,但我似乎最终取得了胜利。
对于日志记录任务,我现在使用 this 而不是 PWRVIEW。备用能量鉴定我仍然使用 PWRVIEW 运行漂亮的图表,并且因为限值检查和 Ulim/Ures 计算是自动完成的——但这并不是说你不能编写自己的。