aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/sca/trace/process.py
blob: 2499df43f2ccecff6dc9c03356b4f08de3257c70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""
This module provides functions for sample-wise processing of single traces.
"""
from typing import cast

import numpy as np
from public import public

from .trace import Trace


@public
def absolute(trace: Trace) -> Trace:
    """
    Apply absolute value to samples of `trace`.

    :param trace:
    :return:
    """
    return trace.with_samples(np.absolute(trace.samples))


@public
def invert(trace: Trace) -> Trace:
    """
    Invert(negate) the samples of `trace`.

    :param trace:
    :return:
    """
    return trace.with_samples(np.negative(trace.samples))


@public
def threshold(trace: Trace, value) -> Trace:
    """
    Map samples of the `trace` to 1 if they are above `value` or to 0.

    :param trace:
    :param value:
    :return:
    """
    result_samples = trace.samples.copy()
    result_samples[result_samples <= value] = 0
    result_samples[np.nonzero(result_samples)] = 1
    return trace.with_samples(result_samples)


def _rolling_window(samples: np.ndarray, window: int) -> np.ndarray:
    shape = samples.shape[:-1] + (samples.shape[-1] - window + 1, window)
    strides = samples.strides + (samples.strides[-1],)
    return np.lib.stride_tricks.as_strided(samples, shape=shape, strides=strides)  # type: ignore[attr-defined]


@public
def rolling_mean(trace: Trace, window: int) -> Trace:
    """
    Compute the rolling mean of `trace` using `window`. Shortens the trace by `window` - 1.

    :param trace:
    :param window:
    :return:
    """
    return trace.with_samples(
        cast(
            np.ndarray,
            np.mean(_rolling_window(trace.samples, window), -1).astype(
                dtype=trace.samples.dtype, copy=False
            ),
        )
    )


@public
def offset(trace: Trace, offset) -> Trace:
    """
    Offset samples of `trace` by `offset`, sample-wise (Adds `offset` to all samples).

    :param trace:
    :param offset:
    :return:
    """
    return trace.with_samples(trace.samples + offset)


def _root_mean_square(trace: Trace):
    return np.sqrt(np.mean(np.square(trace.samples)))


@public
def recenter(trace: Trace) -> Trace:
    """
    Subtract the root mean square of the `trace` from its samples, sample-wise.

    :param trace:
    :return:
    """
    around = _root_mean_square(trace)
    return offset(trace, -around)


@public
def normalize(trace: Trace) -> Trace:
    """
    Normalize a `trace` by subtracting its mean and dividing by its standard deviation.

    :param trace:
    :return:
    """
    return trace.with_samples(
        (trace.samples - np.mean(trace.samples)) / np.std(trace.samples)
    )


@public
def normalize_wl(trace: Trace) -> Trace:
    """
    Normalize a `trace` by subtracting its mean and dividing by a multiple (= `len(trace)`) of its standard deviation.

    :param trace:
    :return:
    """
    return trace.with_samples(
        (trace.samples - np.mean(trace.samples))
        / (np.std(trace.samples) * len(trace.samples))
    )