---
title: Yokogawa WT Power Analyzer Automation
description: "Automate Yokogawa WT5000, WT1800R and WT300E power analyzers from Python: numeric item lists, fresh-data sync, integration, harmonics and efficiency maps."
url: https://galoislabs.ai/blog/yokogawa-power-analyzer-automation
author: Alex Hernandez
author_url: https://galoislabs.ai/blog/authors/alex-hernandez
published: "2026-08-08"
topic: Instrument automation
publisher: Galois Labs
---

# Yokogawa WT power analyzer automation in Python: efficiency and standby power

![A bench source-measure instrument drawn in isometric, two test leads running to a single component in a small fixture.](https://galoislabs.ai/blog/figures/bench-2.light.webp)

*FIG. 1 — SOURCE AND MEASURE, ONE DEVICE*

To automate a Yokogawa WT power analyzer from Python, open it with PyVISA over VXI-11, USB or GP-IB, choose what to read with `:NUMeric:NORMal:ITEM` commands, wait for a completed update with `:COMMunicate:WAIT 1`, then read every item with one `:NUMeric:NORMal:VALue?` query. Integration, harmonics and efficiency maps all follow that pattern.

This guide covers the WT5000, the WT1800R (WT1801R to WT1806R) and the WT300E family (WT310E, WT310EH, WT332E and WT333E), with commands from Yokogawa's communication manuals: [IM WT5000-17EN](https://cdn.tmi.yokogawa.com/1/7114/files/IMWT5000-17EN.pdf), [IM WT1801R-17EN](https://cdn.tmi.yokogawa.com/1/10199/files/IMWT1801R-17EN.pdf) and [IM WT310E-17EN](https://cdn.tmi.yokogawa.com/1/6198/files/IMWT310E-17EN.pdf). The WT1800R and WT5000 can both switch to WT1800 and WT1800E command types, so those older units use a closely related command set; check names against your unit's manual. General PyVISA patterns are in [SCPI instrument automation with Python](https://galoislabs.ai/blog/scpi-automation-python).

## How do I connect to a Yokogawa WT analyzer from Python?

All three models speak VXI-11 over Ethernet and document USB and GP-IB. Some interfaces are options, such as Ethernet (/C7) on the WT300E.

| Interface            | WT5000                    | WT1800R        | WT300E         | PyVISA resource string                |
| -------------------- | ------------------------- | -------------- | -------------- | ------------------------------------- |
| Ethernet, VXI-11     | Yes                       | Yes            | Yes            | `TCPIP0::192.168.1.60::inst0::INSTR`  |
| Ethernet, raw socket | Port 10002, LF terminator | Not documented | Not documented | `TCPIP0::192.168.1.60::10002::SOCKET` |
| Ethernet, Modbus/TCP | Port 502                  | Port 502       | Yes            | Not a VISA resource                   |
| USB                  | USB-TMC                   | USB TMC        | USBTMC-USB488  | From `list_resources()`               |
| GP-IB                | Yes                       | Yes            | Yes            | `GPIB0::1::INSTR`                     |
| RS-232               | Not documented            | Not documented | Yes            | `ASRL/dev/ttyUSB0::INSTR`             |

Three notes from the manuals:

- **One controller at a time.** Each Ethernet interface accepts one connection, and every manual warns that commands sent through two interfaces at once are not executed properly. A WTViewerE session and your script cannot share the analyzer.
- **USB drivers.** The WT1800R and WT300E manuals tell you to install Yokogawa's USB TMC driver and not to use USB TMC drivers from other companies, and the WT5000 manual also points to Yokogawa's driver. VXI-11 over Ethernet sidesteps the question; it is a standard protocol that [PyVISA-py](https://pyvisa.readthedocs.io/projects/pyvisa-py/en/latest/installation.html), the pure-Python backend, implements.
- **Raw socket.** The WT5000's socket on port 10002 has no protocol layer, so it carries no device clear. Prefer VXI-11 when you have the choice.

Open the session once and set response formats you can parse:

```python title="wt/session.py"
import pyvisa


class WTError(RuntimeError):
    pass


def open_wt(rm: pyvisa.ResourceManager, resource: str, timeout_ms: int = 10_000):
    """Open a WT analyzer, confirm its identity, and set predictable response formats."""
    wt = rm.open_resource(
        resource, read_termination="\n", write_termination="\n", timeout=timeout_ms
    )
    idn = wt.query("*IDN?").strip()      # e.g. YOKOGAWA,WT5000,<serial>,F<firmware>
    if not idn.startswith("YOKOGAWA,WT"):
        raise WTError(f"{resource} is not a WT analyzer: {idn}")
    wt.write("*CLS")                     # clears the event registers and the error queue
    wt.write(":COMMunicate:REMote ON")
    wt.write(":COMMunicate:HEADer OFF")  # settings queries return data, not ":RATE 500.0E-03"
    wt.write(":NUMeric:FORMat ASCii")
    return wt, idn


def check_errors(wt, limit: int = 16) -> None:
    """Drain :STATus:ERRor? and raise if anything was queued."""
    errors = []
    for _ in range(limit):
        code, _, message = wt.query(":STATus:ERRor?").partition(",")
        if int(code) == 0:
            break
        errors.append((int(code), message.strip().strip('"')))
    if errors:
        raise WTError(str(errors))
```

Two details differ from generic SCPI. Errors come from `:STATus:ERRor?` with positive codes: an unknown command is 113, "Undefined header", not -113. And settings queries carry a header by default (`:RATE?` returns `:RATE 500.0E-03`) until you send `:COMMunicate:HEADer OFF`; query-only commands such as `:NUMeric:NORMal:VALue?` return bare data either way.

## How do numeric item lists work?

A WT analyzer computes dozens of functions per element at each update. Instead of one query per value, you define an ordered list of items (a function, an element and, for harmonics, an order) and read the whole list with one query. `:NUMeric:NORMal:ITEM<x>` sets item x, and `:NUMeric:NORMal:NUMber` sets how many items `:NUMeric:NORMal:VALue?` returns. Out of the box the WT5000 and WT1800R return 15 items and the WT300E 10, so set both the items and the count yourself:

```python title="wt/items.py"
from wt.session import check_errors

Item = tuple[str, int | None]  # (function, element); None for ETA1, MATH and similar


def set_items(wt, items: list[Item]) -> None:
    for n, (function, element) in enumerate(items, start=1):
        target = function if element is None else f"{function},{element}"
        wt.write(f":NUMeric:NORMal:ITEM{n} {target}")
    wt.write(f":NUMeric:NORMal:NUMber {len(items)}")
    check_errors(wt)


def read_items(wt, items: list[Item]) -> dict[str, float]:
    values = wt.query_ascii_values(":NUMeric:NORMal:VALue?")  # NAN and INF parse as floats
    names = [f if e is None else f"{f}-{e}" for f, e in items]
    return dict(zip(names, values))
```

The model differences that matter for item lists:

| Setting                       | WT5000                                 | WT1800R                              | WT300E                                |
| ----------------------------- | -------------------------------------- | ------------------------------------ | ------------------------------------- |
| Elements                      | 1 to 7                                 | 1 to 6                               | 1 to 3                                |
| Numeric items                 | 1 to 1,000                             | 1 to 1,000                           | 1 to 255                              |
| Update interval command       | `:UPDate:RATE`                         | `:RATE`                              | `:RATE`                               |
| Update intervals              | 10 ms to 20 s                          | 50 ms to 20 s, or AUTO               | 100 ms to 20 s, or AUTO               |
| FLOAT byte order              | LSB first by default                   | LSB first by default                 | MSB first, fixed                      |
| Elapsed integration time item | `ITIMe` with element                   | `TIME` with element                  | `TIME`                                |
| Efficiency                    | `:MEASure:EFFiciency:ETA1` to `ETA4`   | `:MEASure:EFFiciency:ETA1` to `ETA4` | `:MATH EFFiciency`, WT332E and WT333E |
| Harmonics                     | Groups `:HARMonics1` and `:HARMonics2` | /G5 or /G6 option                    | /G5 option                            |

`URMS`, `IRMS`, `P`, `S`, `Q`, `LAMBda`, `UDC` and `IDC` exist on all three; each manual's function option list has the rest.

**Check for NAN and INF.** In ASCII format, an item with no data comes back as `NAN`, and an over-range, overflow or computation error comes back as `INF`. Python's `float()` accepts both, so a script that averages blindly carries an over-range into its result. `math.isfinite()` rejects both.

**Switch to FLOAT for long lists.** An ASCII value takes about 13 bytes with its separator; `:NUMeric:FORMat FLOat` sends 4-byte IEEE singles in one IEEE 488.2 block. The WT5000 and WT1800R default to LSB first (`:NUMeric:BYTeorder`) and switch to MSB first under a legacy command type; the WT300E always sends MSB first. NAN becomes 9.91E+37 and INF 9.9E+37:

```python title="float_items.py"
wt.write(":NUMeric:FORMat FLOat")
wt.write(":NUMeric:BYTeorder LSBFirst")  # WT5000 and WT1800R; a WT300E always sends MSB first
values = wt.query_binary_values(
    ":NUMeric:NORMal:VALue?",
    datatype="f",           # IEEE single precision, 4 bytes
    is_big_endian=False,    # True on a WT300E
)
bad = [i for i, v in enumerate(values, start=1) if abs(v) >= 9.9e37]  # no data or over-range
```

[PyVISA's `query_binary_values()`](https://pyvisa.readthedocs.io/en/latest/introduction/rvalues.html) defaults to little-endian 4-byte floats, which matches the WT5000's default by coincidence and misreads a WT300E silently.

## How do I make sure each reading is a new update?

`:NUMeric:NORMal:VALue?` returns whatever the analyzer currently holds, whether or not it has updated since your last query, so a fast loop reads the same update twice. `*OPC?` will not help. On these analyzers, `*OPC`, `*OPC?` and `*WAI` apply only to overlap commands, selected with `:COMMunicate:OPSE`; on the WT5000 that means media access such as loading a setup file, and the WT300E has none, so its `*OPC?` always returns 1. Everything else, measurement queries included, is sequential.

The manuals describe the method instead. Bit 0 of the condition register, UPD, is 1 while measured data is being updated. `:STATus:FILTer1 FALL` makes bit 0 of the extended event register latch when UPD falls, `:STATus:EESR?` reads and clears that register, and `:COMMunicate:WAIT 1` holds the next command until the bit is set:

```python title="wt/sync.py"
from wt.items import Item, read_items


def arm_update_wait(wt) -> None:
    """Latch EESR bit 0 each time a data update finishes (UPD falls from 1 to 0)."""
    wt.write(":STATus:FILTer1 FALL")
    wt.query(":STATus:EESR?")        # reading the register clears it


def read_fresh(wt, items: list[Item]) -> dict[str, float]:
    """Block until the next data update completes, then read the item list once."""
    wt.write(":COMMunicate:WAIT 1")  # the next command waits for EESR bit 0
    data = read_items(wt, items)
    wt.query(":STATus:EESR?")        # clear bit 0 so the next call waits again
    return data
```

The wait happens inside the next query's read, so the VISA timeout must cover a full update interval plus margin; at 20 s, the 10 s timeout in `open_wt()` fails every time. Set the interval with `:UPDate:RATE 500MS` on the WT5000 or `:RATE 500MS` on the WT1800R and WT300E, and size `wt.timeout` from it.

The update interval is also the measurement window: in the WT5000's sync source period average method, each measurement spans whole sync-source periods inside the interval. The [WT5000 features guide](https://cdn.tmi.yokogawa.com/1/7117/files/IMWT5000-01EN.pdf) states the trade-off: a fast rate captures fast load changes, a slow rate suits low-frequency signals. At 50 Hz or 60 Hz, 500 ms to 1 s covers 25 to 60 cycles.

## How do I run an integration measurement?

Integration accumulates watt-hours (`WH`), ampere-hours (`AH`) and their positive and negative parts over a timed window. It is how you measure energy per cycle, or average power on a load that will not sit still. A timed run in normal mode has four steps: reset, set the mode and timer, start, and poll `:INTEGrate:STATe?` until it reports that the timer ran out.

```python title="wt/integrate.py"
import time

from wt.session import WTError, check_errors


def run_integration(wt, hours: int = 0, minutes: int = 10, seconds: int = 0,
                    poll_s: float = 1.0) -> None:
    """Run one timed integration in normal mode; return when the timer expires."""
    wt.write(":INTEGrate:RESet")                               # mode and timer change only when reset
    wt.write(":INTEGrate:MODE NORMal")
    wt.write(f":INTEGrate:TIMer {hours},{minutes},{seconds}")  # TIMer1 on WT5000 and WT1800R
    check_errors(wt)
    wt.write(":INTEGrate:STARt")
    while True:
        state = wt.query(":INTEGrate:STATe?").split(",")[0].strip().upper()
        if state.startswith("TIM"):      # TIMeup: the integration timer ran out
            return
        if not state.startswith("STA"):  # STARt is the only state that means still running
            raise WTError(f"integration ended in state {state}")
        time.sleep(poll_s)
```

Read the result as ordinary items, for example `[("WH", 1), ("AH", 1)]`. Average power over the window is `WH × 3600 / seconds`. Details from the manuals:

- **States.** `:INTEGrate:STATe?` answers RESet, READy (real-time modes only), STARt, STOP, ERRor (abnormal termination from overflow or power failure) or TIMeup. Prefix matching works whether the analyzer answers in short or full form.
- **Timer syntax.** The WT5000 and WT1800R document `:INTEGrate:TIMer<x>` per element; with independent element integration off, the suffix can be omitted and element 1's timer applies to all elements.
- **Ranges on the WT5000.** Autorange is turned off at the start of integration and the range stays fixed, so set the range for the load's peak before you start.
- **The WT300E.** Integration keeps autorange if enabled, and no measurement takes place while the range switches. Ranges, update interval, integration mode and timer change only while integration is reset, hence the reset first. Starting integration turns averaging off, and a reset does not restore it; send `:MEASure:AVERaging:STATe ON` if a later step needs it.

## How do I measure standby power to IEC 62301?

IEC 62301 is the IEC method for measuring standby power. [IEC 62301:2026](https://webstore.iec.ch/en/publication/90194), edition 3.0, *Measurement of standby power for appliances and equipment*, replaced the [2011 edition](https://webstore.iec.ch/en/publication/6789) on May 22, 2026. It covers standby and other non-active modes such as off mode, plus how results are reported, and leaves networked standby to IEC 63474:2026. Its summary of changes makes data logging mandatory and drops the average reading and direct meter reading methods. Regulations and labeling programs cite a specific edition, so confirm which one applies to your product.

The standard, not the analyzer, decides how long to measure, how to judge stability, and what the supply and instrument must meet. Yokogawa's [application note on IEC 62301](https://tmi.yokogawa.com/library/resources/application-notes/iec62301-standards-testing-for-standby-power-measurement/) summarizes the 2011 edition's conditions (supply harmonics and crest factor, ambient temperature, instrument uncertainty and resolution) and the stability judgments its software offers: linear regression, cumulative average and three-section comparison.

What a script owns is the measurement: a fixed range and a timestamped log of every update, which your edition's stability rule then judges. On a WT300E:

```python title="standby.py"
import time

from wt.items import set_items
from wt.session import check_errors
from wt.sync import arm_update_wait, read_fresh


def standby_log(wt, minutes: float = 15, current_range: str = "50E-3") -> list[dict]:
    """Log element 1 at every data update on a WT300E, one row per update."""
    wt.write(":INTEGrate:RESet")                        # ranges change only while integration is reset
    wt.write(":INPut:CURRent:AUTO OFF")
    wt.write(f":INPut:CURRent:RANGe {current_range}")  # 50 mA: a WT310E range at crest factor 3
    check_errors(wt)
    items = [("P", 1), ("URMS", 1), ("IRMS", 1)]
    set_items(wt, items)
    arm_update_wait(wt)
    rows, start = [], time.monotonic()
    while time.monotonic() - start < minutes * 60:
        row = read_fresh(wt, items)
        rows.append({"t_s": time.monotonic() - start, **row})
    return rows  # reject NAN/INF rows, then apply your edition's stability rule
```

Write ranges as plain numbers. In Yokogawa's multiplier table `M` is milli and `MA` is mega, except that `MA` means milliampere for current; `50E-3` needs no such rule. If the standby current arrives in irregular pulses, the [WT300E user's manual](https://cdn.tmi.yokogawa.com/1/6196/files/IMWT310E-01EN.pdf) warns that autorange may not hold a steady range and recommends a fixed one.

> **Evidence, not certification**
>
> A script that follows the method produces a record you can hand to a test lab or an auditor. It supports certification; the program or body that names the standard grants it.

## How do I read harmonics and THD?

On the WT5000, each element belongs to one of two harmonic measurement groups, `:HARMonics1` and `:HARMonics2`, and each group has its own order range and PLL source. The PLL source sets the fundamental, usually the voltage input of the element you measure. THD is an ordinary numeric item (`UTHD`, `ITHD`), and the spectrum of one function comes out as a list: `:NUMeric:LIST:VALue?` returns TOTal, DC, then orders 1 up to `:NUMeric:LIST:ORDer`, as many as 502 values per list item.

```python title="harmonics.py"
from wt.items import read_items, set_items
from wt.session import check_errors
from wt.sync import arm_update_wait


def current_harmonics(wt, element: int = 1, max_order: int = 50) -> dict:
    """WT5000: voltage and current THD plus the current spectrum of one element."""
    wt.write(f":HARMonics1:CONFigure:ELEMent{element} 1")  # element joins group Hrm1
    wt.write(f":HARMonics1:ORDer 1,{max_order}")
    wt.write(f":HARMonics1:PLLSource U{element}")          # fundamental from the voltage
    wt.write(f":NUMeric:LIST:ITEM1 I,{element}")
    wt.write(f":NUMeric:LIST:ORDer {max_order}")
    wt.write(":NUMeric:LIST:SELect ALL")
    wt.write(":NUMeric:LIST:NUMber 1")
    check_errors(wt)
    items = [("UTHD", element), ("ITHD", element)]
    set_items(wt, items)

    arm_update_wait(wt)
    wt.write(":COMMunicate:WAIT 1")  # wait for a completed update
    wt.write(":NUMeric:HOLD ON")     # hold that update so both queries read it
    try:
        thd = read_items(wt, items)
        total, dc, *orders = wt.query_ascii_values(":NUMeric:LIST:VALue? 1")
    finally:
        wt.write(":NUMeric:HOLD OFF")
        wt.query(":STATus:EESR?")
    return {"thd": thd, "i_total": total, "i_dc": dc,
            "i_by_order": dict(enumerate(orders, start=1))}
```

`:NUMeric:HOLD ON` holds all numeric data internally at that moment, the method the manual gives for reading several queries from one update, so the THD items and the list cannot straddle two updates. Harmonics need the /G5 or /G6 option on the WT1800R and /G5 on the WT300E. IEC harmonic measurement is a separate WT5000 option, /G7, with its own `:HARMonics<x>:IEC` commands; the function above is for engineering spectra.

## How do I map efficiency across load points?

Efficiency maps are where scripting pays off: a dozen or more load points, each needing a settled reading of input and output power. Wire the converter input to element 1 and the output to element 2, and let the analyzer compute efficiency. `:MEASure:EFFiciency:ETA1 P2,P1` sets η1 to P2 / P1 × 100 percent, numerator first, and both powers come from the same update.

```python title="efficiency_map.py"
import math
import time
from typing import Protocol

from wt.items import set_items
from wt.session import WTError, check_errors
from wt.sync import arm_update_wait, read_fresh


class Load(Protocol):
    def set_current(self, amps: float) -> None: ...


ITEMS = [("URMS", 1), ("IRMS", 1), ("P", 1),   # element 1: converter input
         ("UDC", 2), ("IDC", 2), ("P", 2),     # element 2: converter output
         ("ETA1", None)]                       # P2 / P1 x 100


def efficiency_map(wt, load: Load, currents: list[float],
                   updates: int = 4, settle_s: float = 2.0) -> list[dict]:
    wt.write(":MEASure:EFFiciency:ETA1 P2,P1")
    check_errors(wt)
    set_items(wt, ITEMS)
    arm_update_wait(wt)
    rows = []
    for amps in currents:
        load.set_current(amps)
        time.sleep(settle_s)                   # let the converter settle
        read_fresh(wt, ITEMS)                  # discard: this update may span the step
        samples = [read_fresh(wt, ITEMS) for _ in range(updates)]
        mean = {k: sum(s[k] for s in samples) / updates for k in samples[0]}
        if not all(math.isfinite(v) for v in mean.values()):
            raise WTError(f"no data or over-range at {amps} A: {mean}")
        mean["eta_from_mean_p"] = 100 * mean["P-2"] / mean["P-1"]
        rows.append({"load_a": amps, "updates": updates, **mean})
    return rows
```

Three choices are deliberate. The update after each step is discarded, because its window can hold samples from both loads. Each point averages a fixed, recorded number of fresh updates. And the script keeps both the mean `ETA1` and a ratio of mean powers; they differ slightly when power moves between updates, and a large gap means the point had not settled. For every point on one range, set fixed ranges for the highest load before the sweep, since autorange can switch between points.

The load side is its own driver; [electronic load automation](https://galoislabs.ai/blog/electronic-load-automation) covers it, and [DC-DC converter validation](https://galoislabs.ai/blog/dc-dc-converter-validation) covers the test plan around the sweep. [Motor drive test automation](https://galoislabs.ai/blog/motor-drive-test-automation) extends the map to speed and torque with the WT5000's motor evaluation option. Record enough to reproduce each point:

| Field                                | Source                 | Why                           |
| ------------------------------------ | ---------------------- | ----------------------------- |
| Load setpoint                        | Load driver            | The map's x-axis              |
| `P-1`, `P-2`, `ETA1`                 | Numeric items          | Result and inputs             |
| `URMS-1`, `IRMS-1`, `UDC-2`, `IDC-2` | Numeric items          | Catch wiring or source faults |
| Ranges, update interval              | Range and rate queries | Reproducibility               |
| Updates averaged                     | Script                 | Sample size                   |
| `*IDN?` string                       | Session start          | Serial number and firmware    |

## How do I run this efficiency map in Galois with Évariste?

Évariste, the agent in the Galois platform, opens from the app sidebar (Ctrl+Shift+E) and works on the real bench through the galois-edge daemon. The task is the same `efficiency_map()` run: same analyzer, load, items and NAN and INF rule.

1. **Find the instruments.** Ask Évariste to "List connected instruments"; it lists them across your team's edges and reads each profile's commands. Check the analyzer's profile for the commands this guide uses: `:MEASure:EFFiciency`, `:NUMeric:NORMal:ITEM`, `:STATus:FILTer1`, `:STATus:EESR?` and `:COMMunicate:WAIT`. For a unit with no profile, or one that lacks any of these, upload its communication manual; Évariste generates a profile for you to review before it is deployed to the edge and bound.
2. **State the objective and limits.** Use your own load points; these suit a 6 A converter:

   > Map the converter's efficiency with the WT5000 and the electronic load. Element 1 is the converter input, element 2 the output; define ETA1 as P2 over P1. Fix ranges for the 6 A full load. Step the load from 0.5 A to 6 A in 0.5 A steps; at each point wait 2 s, discard the first update, then record four fresh updates of URMS-1, IRMS-1, P-1, UDC-2, IDC-2, P-2 and ETA1, using :STATus:FILTer1 FALL and :COMMunicate:WAIT 1 so each read is a new update. Fail any reading that contains NAN or INF.

   To add an efficiency floor from your datasheet, append a line such as "Fail any reading at 2 A or more with ETA1 below 90 percent."
3. **Review the draft.** Évariste returns a draft sequence: setup steps, then a loop over twelve load points, each with a 2 s wait, one discarded read and four recorded reads. A draft does not run until you approve it. Check it against this guide: autorange off, `:STATus:FILTer1 FALL` set once, `:COMMunicate:WAIT 1` before each read and `:STATus:EESR?` after it, the update that spans a load step discarded, NAN and INF failing the step, and each command matching the manual. Edit in conversation or the sequence builder; every change is a new version with a diff. [How to review an AI-generated test plan](https://galoislabs.ai/blog/review-ai-generated-test-plan) has the full checklist.
4. **Run it.** Wire element 1 to the converter input and element 2 to its output, check the source and load against the converter's ratings, then start the run with the DUT serial. The run goes to the bench through galois-edge, and Monitor shows the channels live. Dangerous commands you ask Évariste to send outside the sequence wait for your confirmation.
5. **Read the results.** Each step is recorded with its measured value, limits, pass/fail, raw command and response, instrument, operator, DUT serial and timestamps. With range and update-interval queries added to the draft, that covers the fields table above. Then ask Évariste for each point's mean, whether mean ETA1 agrees with mean P-2 over mean P-1 (the settling check), which steps failed or passed close to a limit, or how this run compares with the last; answers cite the runs. Check its figures against the recorded reads.
6. **Report.** "Generate a test report from the last run" builds a PDF or HTML report from a LaTeX template. In the report editor, add the element wiring, plus the fixed ranges and update interval from the fields table, then share it to Slack.

| Step             | Code path (this guide)                          | Galois with Évariste                        |
| ---------------- | ----------------------------------------------- | ------------------------------------------- |
| Connect          | `open_wt()` checks `*IDN?`, sets formats        | galois-edge matches `*IDN?` to a profile    |
| Driver           | `wt/` modules you maintain                      | Library or generated profile, reviewed      |
| Item list and η1 | `set_items()`, `:MEASure:EFFiciency:ETA1 P2,P1` | Setup steps in the draft                    |
| Fresh data       | `arm_update_wait()`, `read_fresh()`             | EESR filter, wait and clear steps           |
| Sweep            | `efficiency_map()` loop, settle, discard        | Loop over load points with wait and discard |
| Limits           | `math.isfinite()`, `WTError`                    | NAN and INF check, plus any floor you add   |
| Record           | `rows` plus the fields table                    | Per-step run record                         |
| Interpret        | Mean `ETA1` against `eta_from_mean_p`           | Questions over the run, with citations      |
| Report           | Your own script                                 | "Generate a test report from the last run"  |

You no longer write or maintain the driver modules, sweep loop, error handling, logging or report script. The objective, datasheet limits, profile and draft review, approval, wiring and bench safety stay yours. The standby log and harmonics read follow the same flow; [AI test automation for hardware benches](https://galoislabs.ai/blog/ai-test-automation-hardware) explains the draft-and-approve model.

## When are Yokogawa's own tools the better choice?

- **[WTViewerE](https://tmi.yokogawa.com/solutions/products/power-analyzers/power-measurement-application-software/761941-wtviewere-application-software/)** displays waveforms, trends, vectors and harmonic bar graphs, saves CSV, and with the WT5000's /DS option streams waveforms at up to 2 MS/s. For exploratory work it is the faster path.
- **[Power Consumption Measuring Software](https://tmi.yokogawa.com/solutions/products/power-analyzers/power-measurement-application-software/power-consumption-measuring-software/)** sets up IEC 62301 Ed2.0 (2011) and EN 50564:2011 measurements, judges stability and writes the report, on the WT5000, WT1800E, WT1800, WT310E and other models. If you test to the 2011 edition and need a report rather than a pipeline, start there.
- **TMCTL** is the communication library the manuals list for USB and Ethernet control from a PC.

A script earns its place when the analyzer is one instrument in a sequence, with a load, a source and a chamber stepping together and every result landing in one record. The [Galois and PyVISA comparison](https://galoislabs.ai/compare/pyvisa) lays out what the platform adds to plain PyVISA.

## How do I run WT measurements across several benches?

On one bench, the modules above are enough. Across several, the questions become which analyzer is where, who may drive it, and where results go.

Galois is agent-driven test engineering for hardware teams: agents generate tests and instrument drivers, run them on real benches through the open-source galois-edge daemon, and turn the results into reports and a shared engineering record.

galois-edge, the Apache-2.0 daemon, queries each instrument it finds with `*IDN?` and [matches the reply against the profiles it has loaded](https://docs.galoislabs.ai/guides/connecting-instruments/). Galois ships 573 instrument profiles across 135 manufacturers in its [instrument library](https://galoislabs.ai/instruments), including the WT5000, WT1801E and WT310E. An analyzer that does not announce itself over mDNS goes in the daemon's static `LAN_INSTRUMENTS` list as `TCPIP::<address>::INSTR`, and instruments without a profile still accept raw commands. Profiles are data rather than classes, a choice [declarative instrument drivers](https://galoislabs.ai/blog/declarative-instrument-drivers) explains.

Existing scripts move with one line: with [`pyvisa.ResourceManager("@galois")`](https://docs.galoislabs.ai/guides/pyvisa/), calls travel through Galois Cloud to the daemon on the bench PC, and the calls this guide relies on (`write`, `query`, `query_ascii_values`, `query_binary_values` and timeouts) are on the backend's supported list. Agents reach the same instruments as typed MCP tools generated from the profile ([MCP for lab instruments](https://galoislabs.ai/blog/mcp-lab-instruments)). Évariste drafts the same map from a plain-English objective, as [the walkthrough above](https://galoislabs.ai/blog/yokogawa-power-analyzer-automation#how-do-i-run-this-efficiency-map-in-galois-with-évariste) shows. The platform also runs as a dedicated single-tenant cloud or fully on-prem and air-gapped ([deployment options](https://galoislabs.ai/deployment)).

To try the daemon on your own analyzer, start with the [quickstart](https://docs.galoislabs.ai/getting-started/quickstart/).

## Frequently asked questions

### Can PyVISA control a Yokogawa WT5000?

Yes. The WT5000 speaks VXI-11 over Ethernet, so PyVISA opens it as TCPIP0::\<address>::inst0::INSTR with either NI-VISA or the pure-Python PyVISA-py backend. It also documents USB-TMC, GP-IB and a raw TCP socket on port 10002 with a line-feed terminator. Only one interface and one Ethernet connection can be used at a time.

### Why does my Yokogawa power analyzer return the same reading twice?

:NUMeric:NORMal:VALue? returns the data the analyzer currently holds, whether or not it has updated since the last query. To read only new data, send :STATus:FILTer1 FALL and read :STATus:EESR? once, then before each read send :COMMunicate:WAIT 1, which holds the next command until a data update finishes, and read :STATus:EESR? again afterward to clear the event.

### How do I read efficiency from a Yokogawa WT analyzer?

On the WT5000 and WT1800R, :MEASure:EFFiciency:ETA1 P2,P1 defines efficiency 1 as element 2 power over element 1 power, in percent, numerator first. Add ETA1 to the numeric item list and it comes back with every other item. On the WT300E family, efficiency is a MATH function available on the WT332E and WT333E.

### Can I map efficiency on a Yokogawa WT5000 without writing Python?

Yes. In Galois, describe the sweep to Évariste, the agent in the Galois platform, in plain English: the WT5000 and the electronic load, the load points, ETA1 as P2 over P1, four fresh updates at each point and a rule that fails any NAN or INF reading. Évariste drafts a versioned sequence from the analyzer's profile; if the analyzer has no profile, or its profile lacks a command the sweep uses, upload its communication manual and Évariste generates one for you to review before it is deployed to the edge and bound. An engineer approves the draft before it runs on the bench through galois-edge. Each step records its measured value, limits, pass or fail, and raw command and response, and Évariste can check mean ETA1 against mean P-2 over mean P-1 and generate a PDF or HTML test report. Wiring, datasheet limits and bench safety stay yours.

### Which edition of IEC 62301 applies to standby power tests?

IEC 62301:2026, edition 3.0, published on May 22, 2026, and replaced the 2011 edition 2.0. Regulations and labeling programs cite a specific edition, so confirm which one your product is tested against. Yokogawa's Power Consumption Measuring Software is written to the 2011 edition and EN 50564:2011.
