Skip to content

AEM and Energy Measurement

Espen Hovland edited this page Jun 5, 2026 · 2 revisions

AEM & Energy Measurement

The Advanced Energy Monitor (AEM) measures the current and voltage delivered to the target device, letting you profile its power consumption. PyCommander exposes AEM in two complementary ways:

  • AemStream — a Pythonic, unbounded stream of live measurements. Best for continuous monitoring and ad-hoc capture.
  • Adapter.analyzeEnergyUsage() — a fixed-window capture that returns a structured analysis (distribution, clustering, period detection, signal characteristics). Best for one-shot profiling.

AEM is only available on Silicon Labs adapters; it does not work with generic J-Link adapters.

There is also the raw commander.aem suite (calibrate, dump, measure, analyze) if you want the underlying commands.


Streaming with AemStream

AemStream opens a long-running AEM capture and yields AemMeasurement objects as they arrive. Each measurement carries timestamp_us, current_ma, voltage_v, and a computed power_mw (current_ma * voltage_v). There is no limit on how long you can stream, and you can stop at any time.

Constructor

AemStream(
    serial_number=...,            # or ip_address / serial_port
    datarate_hz=None,             # desired output rate
    duration_s=None,              # optional fixed duration
    triggerabove_ma=None,         # start capturing when current rises above
    triggerbelow_ma=None,         # start capturing when current drops below
    triggertimeout_s=None,        # give up waiting for the trigger after N s
    pretrigger_ms=None,           # include data from before the trigger
    calibrate=False,              # calibrate before capturing
)

Like the underlying CLI, the stream supports simple triggers and a configurable data-output rate.

Context-manager syntax (recommended)

Entering the with block opens the connection and starts capture; leaving it closes the connection cleanly.

from pycommander import AemStream

# A 10-second stream at 100 Hz
with AemStream(serial_number="440055955", datarate_hz=100, duration_s=10) as aem_stream:
    for measurement in aem_stream:
        print(measurement)

Manual syntax

If you don't use the context manager, call open() to start and close() to gracefully stop.

from pycommander import AemStream

aem_stream = AemStream(serial_number="440055955", datarate_hz=100)
aem_stream.open()

for measurement in aem_stream:
    # do something with measurement.current_ma, measurement.power_mw, ...
    if some_condition:
        break

aem_stream.close()

Behavior notes

  • Iteration blocks waiting for the next sample while the process is alive, and raises StopIteration once the capture process ends (e.g. when duration_s elapses). Unparseable lines are skipped.
  • open() twice raises RuntimeError — close first, or use the context manager.
  • Iterating before open() raises RuntimeError.
  • Clean shutdown. close() asks the capture process to stop politely (Ctrl-C / SIGINT), then escalates to terminate and finally kill if it does not exit promptly. This is handled by the subprocess Runner (see Architecture-and-Packages).

Fixed-window analysis with analyzeEnergyUsage

When you want a structured summary rather than a raw stream, use Adapter.analyzeEnergyUsage():

from pycommander import Adapter

adapter = Adapter(serial_number="440055955")

result = adapter.analyzeEnergyUsage(
    duration_s=10,
    get_distribution=True,
    cluster_states=True,
    detect_period=True,
)

print(result.signal_characteristics.min_current_ma)
print(result.signal_characteristics.max_current_ma)
print(result.clustering.unique_states)
if result.period_detection.result.is_periodic:
    print("Period (ms):", result.period_detection.result.period_ms)

At least one of the three analyses must be enabled:

Analysis Flag Produces
Distribution get_distribution=True a current/time histogram (AemDistribution)
Clustering cluster_states=True Bayesian-blocks power-state clustering (AemClustering)
Period detection detect_period=True periodicity analysis (AemPeriodDetection) — needs ≥ ~5 periods captured

The return value is an AemAnalysisResult; its nested dataclasses are documented in full on the Data-Types page. Each analysis field is None if you did not request it.


Which one should I use?

Goal Use
Watch power live / capture for an indefinite time AemStream
Log time-series to a CSV/TXT file commander.aem.dump
Get a single average current reading commander.aem.measure
Profile states / periodicity over a fixed window Adapter.analyzeEnergyUsage()

Clone this wiki locally