Skip to content

signals

Signal generation.

Functions:

Name Description
lfm

Generate samples of a linear frequency modulated chirp.

lpm

Generate samples of a linear period modulated chirp.

lfm

lfm(time, f_start, f_stop, duration, invalid=0.0)

Generate samples of a linear frequency modulated chirp.

The instantaneous frequency of the chirp varies linearly from the start frequency to the stop frequency over the duration of the chirp. The value of the signal is given by

\[ s(t) = \begin{cases} \sin(2\pi f_a t + \pi K t^2) & t \leq 0 \leq T, \\ v & \text{otherwise}, \end{cases} \]

where \(f_a\) is the start frequency, \(K = (f_b - f_a)/T\) is the chirp rate, \(f_b\) is the stop frequency, \(T\) is the duration and \(v\) is the invalid value passed to this function. The instantaneous frequency is

\[ f(t) = \begin{cases} f_a + Kt & t \leq0 \leq T, \\ 0 & \text{otherwise}. \end{cases} \]

Parameters:

Name Type Description Default
time ArrayLike

The times in seconds since the start of the chirp to generate samples for.

required
f_start float

The start frequency of the chirp.

required
f_stop float

The stop frequency of the chirp.

required
duration float

The duration of the chirp in seconds.

required
invalid float

The value to use in the output for times before or after the chirp.

0.0

Returns:

Name Type Description
chirp DataArray

The samples of the chirp at the requested times. If a non-DataArray value was given for the time input, the output DataArray will have a dimension named time with the input times as coordinates.

Source code in src/airsonar/processing/signals.py
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
def lfm(
    time: ArrayLike,
    f_start: float,
    f_stop: float,
    duration: float,
    invalid: float = 0.0,
) -> xr.DataArray:
    r"""Generate samples of a linear frequency modulated chirp.

    The instantaneous frequency of the chirp varies linearly from the start frequency to
    the stop frequency over the duration of the chirp. The value of the signal is given
    by

    $$
    s(t) = \begin{cases}
        \sin(2\pi f_a t + \pi K t^2)  & t \leq 0 \leq T, \\
                        v               & \text{otherwise},
    \end{cases}
    $$

    where $f_a$ is the start frequency, $K = (f_b - f_a)/T$ is the chirp rate, $f_b$ is
    the stop frequency, $T$ is the duration and $v$ is the invalid value passed to this
    function. The instantaneous frequency is

    $$
    f(t) = \begin{cases}
        f_a + Kt  & t \leq0 \leq T, \\
            0     & \text{otherwise}.
    \end{cases}
    $$

    Parameters
    ----------
    time
        The times in seconds since the start of the chirp to generate samples for.
    f_start
        The start frequency of the chirp.
    f_stop
        The stop frequency of the chirp.
    duration
        The duration of the chirp in seconds.
    invalid
        The value to use in the output for times before or after the chirp.

    Returns
    -------
    chirp : xr.DataArray
        The samples of the chirp at the requested times. If a non-DataArray value was
        given for the `time` input, the output DataArray will have a dimension named
        `time` with the input times as coordinates.

    """
    # Wrap time into a DataArray.
    if not isinstance(time, xr.DataArray):
        time = np.atleast_1d(time)
        if time.ndim != 1:
            raise ValueError("multi-dimensional times must be given as a DataArray")

        time = xr.DataArray(time, coords=[("time", time)])

    # Evaluate for all times and then clip to the duration.
    K = (f_stop - f_start) / duration
    signal: xr.DataArray = np.sin((2 * np.pi * f_start * time) + (np.pi * K * time**2))  # type:ignore[assignment]
    return signal.where((time >= 0) & (time <= duration), other=invalid)

lpm

lpm(time, f_start, f_stop, duration, invalid=0.0)

Generate samples of a linear period modulated chirp.

The instantaneous period of the chirp varies linearly over the duration of the chirp. This is the result of the instantaneous frequency varying hyperbolically from the start frequency to the stop frequency over the duration of the chirp (this signal is sometimes known as a hyperbolic frequency modulated chirp. In effect, the signal spends a larger fraction of the chirp duration at lower frequencies.

The parameter \(\tau_0\) is defined as

\[ \tau_0 = \frac{f_b}{f_b - f_a} T, \]

where \(f_a\) is the start frequency, \(f_b\) is the stop frequency and \(T\) is the duration. The starting phase is then \(\phi_0 = 2\pi f_a \tau_0\). The value of the signal is given by

\[ s(t) = \begin{cases} \sin\left(\phi_0 \ln\dfrac{\tau_0 - t}{\tau_0}\right) & t \leq 0 \leq T, \\ v & \text{otherwise}, \end{cases} \]

where \(v\) is the invalid value passed to this function. The instantaneous frequency is

\[ f(t) = \begin{cases} \dfrac{f_a \tau_0}{\tau_0 - t} & t \leq0 \leq T, \\ 0 & \text{otherwise}. \end{cases} \]

Parameters:

Name Type Description Default
time ArrayLike

The times in seconds since the start of the chirp to generate samples for.

required
f_start float

The start frequency of the chirp.

required
f_stop float

The stop frequency of the chirp.

required
duration float

The duration of the chirp in seconds.

required
invalid float

The value to use in the output for times before or after the chirp.

0.0

Returns:

Name Type Description
chirp DataArray

The samples of the chirp at the requested times. If a non-DataArray value was given for the time input, the output DataArray will have a dimension named time with the input times as coordinates.

Source code in src/airsonar/processing/signals.py
 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def lpm(
    time: ArrayLike,
    f_start: float,
    f_stop: float,
    duration: float,
    invalid: float = 0.0,
) -> xr.DataArray:
    r"""Generate samples of a linear period modulated chirp.

    The instantaneous period of the chirp varies linearly over the duration of the
    chirp. This is the result of the instantaneous frequency varying hyperbolically from
    the start frequency to the stop frequency over the duration of the chirp (this
    signal is sometimes known as a *hyperbolic frequency modulated chirp*. In effect,
    the signal spends a larger fraction of the chirp duration at lower frequencies.

    The parameter $\tau_0$ is defined as

    $$
    \tau_0 = \frac{f_b}{f_b - f_a} T,
    $$

    where $f_a$ is the start frequency, $f_b$ is the stop frequency and $T$ is the
    duration. The starting phase is then $\phi_0 = 2\pi f_a \tau_0$. The value of the
    signal is given by

    $$
    s(t) = \begin{cases}
        \sin\left(\phi_0 \ln\dfrac{\tau_0 - t}{\tau_0}\right)  & t \leq 0 \leq T, \\
                        v               & \text{otherwise},
    \end{cases}
    $$


    where $v$ is the invalid value passed to this function. The instantaneous frequency
    is

    $$
    f(t) = \begin{cases}
        \dfrac{f_a \tau_0}{\tau_0 - t}  & t \leq0 \leq T, \\
                        0               & \text{otherwise}.
    \end{cases}
    $$

    Parameters
    ----------
    time
        The times in seconds since the start of the chirp to generate samples for.
    f_start
        The start frequency of the chirp.
    f_stop
        The stop frequency of the chirp.
    duration
        The duration of the chirp in seconds.
    invalid
        The value to use in the output for times before or after the chirp.

    Returns
    -------
    chirp : xr.DataArray
        The samples of the chirp at the requested times. If a non-DataArray value was
        given for the `time` input, the output DataArray will have a dimension named
        `time` with the input times as coordinates.

    """
    # Wrap time into a DataArray.
    if not isinstance(time, xr.DataArray):
        time = np.atleast_1d(time)
        if time.ndim != 1:
            raise ValueError("multi-dimensional times must be given as a DataArray")

        time = xr.DataArray(time, coords=[("time", time)])

    # Evaluate for all times and then clip to the duration.
    tau0 = (f_stop / (f_stop - f_start)) * duration
    phi0 = 2 * np.pi * f_start * tau0
    signal: xr.DataArray = np.sin(phi0 * np.log((tau0 - time) / tau0))  # type:ignore[assignment]
    return signal.where((time >= 0) & (time <= duration), other=invalid)