<a id="api-statistics"></a>

# Statistics API

The statistics API consists of the statistics data structure,
which is provided periodically to registered callbacks.
The data structure contains the following top-level keys:

- **time**: The time information which includes:
  - **range**: The (start, stop) time range for this data structure.
  - **delta**: The total duration which is equal to (stop - start).
  - **samples**: The total number of samples combined into this data.
- **signals**: The signal values over the previous statistics time window.
  The keys include **current**, **voltage**, and **power**.  Each key
  contains a map with keys:
  - **µ**: The mean (average) value.
  - **σ2**: The variance value.
  - **min**: The minimum value.
  - **max**: The maximum value.
  - **p2p**: The peak-to-peak value = (**max** - **min**)
  - **∫**: The integrated value, only for **current** and **power**.
- **accumulators**: The integrated charge and energy values.
- **source**: Where the statistics were computed.  The v1 backend
  provides **sensor** (on the instrument) or **host** (by the host
  driver from the full-rate sample stream).  The legacy v0 backend
  provides **sensor** or **stream_buffer**.

## Statistics source

The model and the scan config select where the statistics are computed
by default.
[`Device.statistics_source`](https://docs.joulescope.com/en/software/pyjoulescope/api/device.md#joulescope.v1.device.Device.statistics_source)
reports the default source for a device.

| Model   | scan config                | Default source                                                                                     |
|---------|----------------------------|----------------------------------------------------------------------------------------------------|
| JS110   | ‘auto’, ‘ignore’,<br/>None | host: computed by the host driver from the<br/>2 Msps sample stream, which requires<br/>streaming. |
| JS110   | ‘off’                      | sensor: computed on the instrument at 2 Hz,<br/>without the standard deviation.                    |
| JS220   | any                        | sensor: computed on the instrument.                                                                |
| JS320   | any                        | sensor: computed on the instrument.                                                                |

The `source` argument of `statistics_callback_register` selects the
source for that callback: ‘sensor’, ‘host’, or the legacy v0 name
‘stream_buffer’ for ‘host’.  The JS110 supports both sources, even at the
same time.  The JS220 and JS320 only compute statistics on the
instrument, so they ignore ‘host’ and issue a DeprecationWarning once.

## Statistics on your thread

Statistics callbacks run on the USB thread, so they must return quickly
and must not call back into the device.  Most scripts instead want the
statistics on their own thread.  Use
[`Device.statistics_get`](https://docs.joulescope.com/en/software/pyjoulescope/api/device.md#joulescope.v1.device.Device.statistics_get)
or [`Device.statistics_iter`](https://docs.joulescope.com/en/software/pyjoulescope/api/device.md#joulescope.v1.device.Device.statistics_iter),
which block until the next value arrives:

```default
import joulescope

with joulescope.scan_require_one(config='auto') as device:
    device.parameter_set('reduction_frequency', '2 Hz')
    for stats in device.statistics_iter(count=20):  # 10 seconds
        i = stats['signals']['current']['µ']['value']
        print(f'{i:.9f} A')
```

The first call starts buffering, so consecutive calls return consecutive
values without gaps.  Both wait 2 seconds for each value by default and
raise TimeoutError if none arrives.  `statistics_iter` without a count
continues until the device closes.

## Example

Here is an example statistics data structure:

```default
{
  "time": {
    "range": {"value": [29.975386, 29.999424], "units": "s"},
    "delta": {"value": 0.024038, "units": "s"},
    "samples": {"value": 48076, "units": "samples"}
  },
  "signals": {
    "current": {
      "µ": {"value": 0.000299379503657111, "units": "A"},
      "σ2": {"value": 2.2021878912979553e-12, "units": "A"},
      "min": {"value": 0.00029360855114646256, "units": "A"},
      "max": {"value": 0.0003051375679206103, "units": "A"},
      "p2p": {"value": 1.1529016774147749e-05, "units": "A"},
      "∫": {"value": 0.008981212667119223, "units": "C"}
    },
    "voltage": {
      "µ": {"value": 2.99890387873055,"units": "V"},
      "σ2": {"value": 1.0830626821348923e-06, "units": "V"},
      "min": {"value": 2.993824005126953, "units": "V"},
      "max": {"value": 3.002903699874878, "units": "V"},
      "p2p": {"value": 0.009079694747924805, "units": "V"}
    },
    "power": {
      "µ": {"value": 0.000897810357252683, "units": "W"},
      "σ2": {"value": 1.9910494110256852e-11, "units": "W"},
      "min": {"value": 0.0008803452947176993, "units": "W"},
      "max": {"value": 0.0009152597631327808, "units": "W"},
      "p2p": {"value": 3.49144684150815e-05, "units": "W"},
      "∫": {"value": 0.026933793578814716, "units": "J"}
    },
    "current_range": {
      "µ": {"value": 4.0, "units": ""},
      "σ2": {"value": 0.0, "units": ""},
      "min": {"value": 4.0, "units": ""},
      "max": {"value": 4.0, "units": ""},
      "p2p": {"value": 0.0, "units": ""}
    },
    "current_lsb": {
      "µ": {"value": 0.5333222397870035, "units": ""},
      "σ2": {"value": 0.24889270730539995, "units": ""},
      "min": {"value": 0.0, "units": ""},
      "max": {"value": 1.0, "units": ""},
      "p2p": {"value": 1.0, "units": ""}
    },
    "voltage_lsb": {
      "µ": {"value": 0.5333430401863711, "units": ""},
      "σ2": {"value": 0.24889309698100895, "units": ""},
      "min": {"value": 0.0, "units": ""},
      "max": {"value": 1.0, "units": ""},
      "p2p": {"value": 1.0, "units": ""}
    }
  },
  "accumulators": {
    "charge": {"value": 0.0, "units": "C"},
    "energy": {"value": 0.0, "units": "J"}
  },
  "source": "sensor"
}
```
