Creating & managing signals
Signals are represented by array-like data types with a series (time, frequency or space domain) per column. The SampledSignal
data type wraps an array and carries sampling rate as metadata with it. This allows the API to be used without having to specify sampling rate at each call. However, if a user prefers to use other array-like data types, the sampling rate may be provided as a fs
keyword argument for API calls that require sampling rate.
All code examples in this manual assume you have imported SignalAnalysis
and SignalAnalysis.Units
:
using SignalAnalysis, SignalAnalysis.Units
The SignalAnalysis.Units
package re-exports commonly used units (s
, ms
, Hz
, kHz
, etc) from Unitful.jl
. In addition, a variable 𝓈
is always exported and is an alias for the s
unit from SignalAnalysis.Units
. This allows indexing of signals using time in seconds:
# create a 1-second signal sampled at 20 kHz
x = signal(randn(20000), 20kHz)
# get a signal segment from 0.25 to 0.5 seconds:
y = x[0.25:0.5𝓈]
Creating and wrapping signals
Signals wrapped in a SampledSignal
data type may be easily created using signal()
:
x = signal(data, fs)
Properties such as frame rate (sampling rate), number of channels, etc may be accessed using the SignalBase
API. The signals can be treated as arrays, but carry sampling rate metadata with them. While most operations infer metadata from the input signal, some operations may be unable to automatically infer the frame rate of the output signal. We provide some handy wrappers around common DSP.jl
functions to aid with rate inference:
# create a 1-second signal sampled at 20 kHz
y = signal(randn(20000), 20kHz)
# use DSP.filt() to filter a signal but retainin sampling rate
y = sfilt(lpf, y)
# use DSP.filtfilt() to filter a signal but retainin sampling rate
y = sfiltfilt(lpf, y)
# use DSP.resample() to resample a signal and infer sampling rate
y = sresample(y, 2//3)
API reference
Base.Colon
— Method(:)(start::Unitful.Time, stop::Unitful.Time)
Generates a time range index for a signal.
Examples:
julia> x = signal(randn(2000), 8kHz)
julia> x[0.2𝓈:0.201𝓈]
SampledSignal @ 8000.0 Hz, 9-element Array{Float64,1}:
-0.08671384898800058
-0.665143340284631
-0.3955367460364236
1.2386430598616671
-0.4882254309443194
-1.080437097803303
0.8209785486953832
1.3477512734963886
-0.27722340584395494
Base.Iterators.partition
— Methodpartition(x::SampledSignal, n; step=n, flush=true)
Iterates over the signal x
, n
samples at a time, with a step size of step
. If flush
is enabled, the last partition may be smaller than n
samples.
When applied to a multichannel signal x
, each partition contains samples from all channels.
Examples:
julia> x = signal(collect(1:10), 1.0);
julia> collect(partition(x, 5))
2-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
julia> collect(partition(x, 5; step=2))
5-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
[1, 2, 3, 4, 5]
[3, 4, 5, 6, 7]
[5, 6, 7, 8, 9]
[7, 8, 9, 10]
[9, 10]
julia> collect(partition(x, 5; step=2, flush=false))
3-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
[1, 2, 3, 4, 5]
[3, 4, 5, 6, 7]
[5, 6, 7, 8, 9]
julia> x = signal(hcat(collect(1:10), collect(11:20)), 1.0);
julia> collect(partition(x, 5))[1]
5×2 view(::Array{Int64,2}, 1:5, :) with eltype Int64:
1 11
2 12
3 13
4 14
5 15
Base.Libc.time
— Methodtime(i, x::SampledSignal)
Gets the time of the i
th sample in the signal. The index i
can be a range or an array of indices.
SignalAnalysis.analytic
— Methodanalytic(s)
Converts a signal to analytic representation. The conversion preserves energy, i.e., to convert back to a real signal while conserving energy, multiply by √2.
SignalAnalysis.domain
— Methoddomain(x)
Returns the domain of the signal.
SignalAnalysis.isanalytic
— Methodisanalytic(s)
Checks if a signal is analytic.
SignalAnalysis.issamerate
— Methodissamerate(x, y)
Checks if two signals have the same sampling rate. If the sampling rate of a signal is unknown (because it is not a SampledSignal
), it is assumed to have the same rate as the other signal.
SignalAnalysis.padded
— Methodpadded(s::AbstractArray{T, 2}, padding; delay, fill) -> Any
Generates a padded view of a signal with optional delay/advance.
SignalAnalysis.padded
— Methodpadded(s::AbstractArray{T, 1}, padding; delay, fill) -> Any
Generates a padded view of a signal with optional delay/advance.
SignalAnalysis.samerateas
— Methodsamerateas(x, y)
Create a signal with samples y
and sampling rate same as signal x
.
Examples:
julia> x = signal(randn(100), 8kHz)
julia> y = samerateas(x, randn(5))
SampledSignal @ 8000.0 Hz, 5-element Vector{Float64}:
-0.3053704876108388
-0.5474123820044299
-0.6916442204609657
-0.5185296405433826
-0.4598263144701988
SignalAnalysis.samerateas
— Methodsamerateas(x)
Create a signal with the same sampling rate as signal x
.
Examples:
julia> x = signal(randn(100), 8kHz)
julia> y = samerateas(x)(randn(5))
SampledSignal @ 8000.0 Hz, 5-element Array{Float64,1}:
-0.08671384898800058
-0.665143340284631
-0.3955367460364236
0.8209785486953832
1.3477512734963886
SignalAnalysis.samples
— Methodsamples(s)
Gets the underlying samples in the signal.
SignalAnalysis.signal
— Methodsignal(x::AbstractArray, fs) -> Any
Creates a signal with frame rate fs
.
SignalAnalysis.signal
— Methodsignal(filename::AbstractString; start, nsamples) -> Any
Loads a signal from a WAV file.
SignalAnalysis.signal
— Methodsignal(fs) -> Any
Creates a curried function that takes in an array and creates a signal with sampling rate fs
.
SignalAnalysis.signal
— Methodsignal(
n::Int64,
fs
) -> MetaArrays.MetaArray{Vector{Float64}, SignalAnalysis.SamplingInfo, Float64, 1}
Creates an empty signal of length n
samples, and frame rate fs
.
SignalAnalysis.signal
— Methodsignal(
n::Int64,
ch::Int64,
fs
) -> MetaArrays.MetaArray{Matrix{Float64}, SignalAnalysis.SamplingInfo, Float64, 2}
Creates an empty signal of length n
samples, ch
channels, and frame rate fs
.
SignalAnalysis.signal
— Methodsignal(
x::MetaArrays.MetaArray{var"#s2", SignalAnalysis.SamplingInfo, T, N} where {T, var"#s2", N},
fs
) -> Any
Creates a signal with frame rate fs
. If the original signal's frame rate is the same as fs
, this method simply returns the original signal. Otherwise, it creates a new signal with the specified frame rate and data from the original signal. Do note that this method does not resample the signal.
SignalAnalysis.signal
— Methodsignal(
T::Type,
n::Int64,
fs
) -> MetaArrays.MetaArray{_A, SignalAnalysis.SamplingInfo, _B, 1} where {_A, _B}
Creates an empty signal of type T
, length n
samples, and frame rate fs
.
SignalAnalysis.signal
— Methodsignal(
T::Type,
n::Int64,
ch::Int64,
fs
) -> MetaArrays.MetaArray{_A, SignalAnalysis.SamplingInfo, _B, 2} where {_A, _B}
Creates an empty signal of type T
, length n
samples, ch
channels, and frame rate fs
.
SignalAnalysis.toframe
— Methodtoframe(t, s::SampledSignal)
toframe(t, fs)
Converts time to signal frame number.
Examples:
julia> x = signal(randn(2000), 8kHz);
julia> toframe(0.2𝓈, x)
1601
julia> toframe(0.2𝓈, 8kHz)
1601
julia> toframe([0.2𝓈, 0.201𝓈], x)
2-element Array{Int64,1}:
1601
1609
julia> toframe(0.2:0.201𝓈, x)
1601:1609
julia> toframe((0.2, 0.201), x)
1601:1609
julia> toframe((0.2, 0.201), 8000)
1601:1609
julia> toframe(0.2:0.01:0.3, x)
11-element Array{Int64,1}:
1601
1681
1761
⋮