Class: Digiproc::DigitalSignal

Inherits:
Object
  • Object
show all
Includes:
Convolvable::InstanceMethods, FourierTransformable
Defined in:
lib/signals/digital_signal.rb

Overview

Class for performing actions on Digital Signals easily

Instance Attribute Summary collapse

Attributes included from FourierTransformable

#fft, #fft_strategy

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Convolvable::InstanceMethods

#acorr, #auto_correlation, #conv, #convolution_strategy, #convolve, #cross_correlation, included, #xcorr

Methods included from FourierTransformable

#fft_angle, #fft_data, #fft_db, #fft_imaginary, #fft_magnitude, #fft_real, included

Constructor Details

#initialize(data:) ⇒ DigitalSignal

Initialize with ‘data`

Raises:

  • (ArgumentError)


40
41
42
43
44
# File 'lib/signals/digital_signal.rb', line 40

def initialize(data: )
    raise ArgumentError.new("Data must be an Array, not a #{data.class}") if not data.is_a? Array
    @data = data
    initialize_modules(Digiproc::FourierTransformable => {time_data: data})
end

Instance Attribute Details

#dataObject

Returns the value of attribute data.



4
5
6
# File 'lib/signals/digital_signal.rb', line 4

def data
  @data
end

Class Method Details

.new_from_eqn(eqn:, size:) ⇒ Object

Construct an instance from a Proc (or a Lambda) and a size ds = Digiproc::DigitalSignal.new_from_eqn(eqn: ->(t){ Math.sin(t) }, size: 100)



10
11
12
13
# File 'lib/signals/digital_signal.rb', line 10

def self.new_from_eqn(eqn: , size: )
    rng = (0...size)
    self.new(data: rng.map{ |n| eqn.call(n) })
end

.new_from_equations(eqns:, ranges:) ⇒ Object

Make a digital signal which is defined by one equation in one range, and another eqn in another range eqn1 = ->(t){ (1 - Math::E ** (-0.08*t)) } eqn2 = ->(t){ Math::E ** (-0.002*(t - 100)) } ds = Digiproc::DigitalSignal.new_from_equations(eqns: [eqn1, eqn2], ranges: [0…100, 100…1000])



20
21
22
23
24
25
26
27
28
29
# File 'lib/signals/digital_signal.rb', line 20

def self.new_from_equations(eqns: , ranges: )
    data = []
    eqns.each_with_index do |eqn, i|
        ranges[i].each do |n|
            data[n] = eqn.call(n)
        end
    end
    data.map!{ |val| val.nil? ? 0 : val }
    self.new(data: data)
end

Instance Method Details

#*(ds) ⇒ Object

Allows multiplication of two digital signal objects Performs element by element multiplicaiton of the data vectors

Raises:

  • (ArgumentError)


81
82
83
84
85
86
# File 'lib/signals/digital_signal.rb', line 81

def *(ds)
    raise ArgumentError.new("Object must have a data property") unless ds.respond_to? :data
    raise ArgumentError.new("Object must have a data array") unless ds.data.respond_to? :times
    self.data.length < ds.data.length ? self.class.new(data:self.data.times(ds.data.take(self.data.length))) : self.class.new(data: ds.data.times(self.data.take(ds.data.length))) 
    # self.data.times ds.data
end

#cross_spectral_density(signal) ⇒ Object

Returns the cross_spectral_density of the digital signal with an incoming signal (accepts an array of numeric data) Returns a Digiproc::FFT object



164
165
166
167
168
# File 'lib/signals/digital_signal.rb', line 164

def cross_spectral_density(signal)
    ft = Digiproc::FFT.new(time_data: self.xcorr(signal))
    ft.calculate
    ft
end

#csd(signal) ⇒ Object

Alias for #cross_spectral_density



172
173
174
# File 'lib/signals/digital_signal.rb', line 172

def csd(signal)
    self.cross_spectral_density(signal)
end

#ds_acorrObject

Alias to #ds_auto_correlation



144
145
146
# File 'lib/signals/digital_signal.rb', line 144

def ds_acorr
    ds_auto_correlation
end

#ds_auto_correlationObject

Performs an autocorrelation of the ‘data` array and retursn a Digiproc::DigitalSignal whose data is the result of the autocorrelation



138
139
140
# File 'lib/signals/digital_signal.rb', line 138

def ds_auto_correlation
    Digiproc::DigitalSignal.new(data: self.auto_correlation)
end

#ds_conv(signal) ⇒ Object

Alias to ds_convolve



120
121
122
# File 'lib/signals/digital_signal.rb', line 120

def ds_conv(signal)
    Digiproc::DigitalSignal.new(data: self.conv(signal))
end

#ds_convolve(signal) ⇒ Object

Convolves data with incoming signal, returns a new Digiproc::DigitalSignal whose data is the result of the convolution



114
115
116
# File 'lib/signals/digital_signal.rb', line 114

def ds_convolve(signal)
    Digiproc::DigitalSignal.new(data: self.conv(signal))
end

#ds_cross_correlation(signal) ⇒ Object

Performs cross correlation with an incoming signal, returns a Digiproc::DigitalSignal whose data is the result of the cross correlation



126
127
128
# File 'lib/signals/digital_signal.rb', line 126

def ds_cross_correlation(signal)
    Digiproc::DigitalSignal.new(data: self.cross_correlation(signal))
end

#ds_xcorr(sig) ⇒ Object

Alias for #ds_cross_correlation



132
133
134
# File 'lib/signals/digital_signal.rb', line 132

def ds_xcorr(sig)
    ds_cross_correlation(sig)
end

#i(*n) ⇒ Object

Get data values from @data by index ds = Digiproc::DigitalSignal.new_from_eqn(eqn: ->(t){ Math.sin(t) }, size: 100) vals = ds.i(10..12) # => [-0.5440211108893699, -0.9999902065507035, -0.5365729180004349]



92
93
94
95
96
97
# File 'lib/signals/digital_signal.rb', line 92

def i(*n)
    indices = n.map{ |input| input.respond_to?(:to_a) ? input.to_a : input}
    indices.flatten!
    indices.map!{ |val| self.value_at val }
    return indices.length == 1 ? indices.first : indices
end

#new_from_spectra(fft) ⇒ Object

Make a new digital signal from fft data



34
35
36
# File 'lib/signals/digital_signal.rb', line 34

def new_from_spectra(fft)
    self.new(data: Digiproc::Functions.ifft(fft))
end

#power_spectral_densityObject

Returns the Power Spectral Density (PSD) of the signal by multiplying the signal’s FFT by the conjugate of the FFT (ie squaring the FFT) The result is in the frequency spectrum (as a Digiproc::FFT object)



151
152
153
# File 'lib/signals/digital_signal.rb', line 151

def power_spectral_density
    self.fft * self.fft.conjugate
end

#processObject

Helper method to allow user to process a ds data using a block ds = Digiproc::DigitalSignal.new_from_eqn(eqn: ->(t){ Math.sin(t) }, size: 100) ds.process { |el| el * 10 } # Change signal gain from 1 to 10. Does not change @data, just returns processed array



51
52
53
54
55
56
57
# File 'lib/signals/digital_signal.rb', line 51

def process 
    processed_data = []
    for i in 0...data.length do
        processed_data << (yield data[i])
    end
    processed_data
end

#process!Object

Same as ‘#process` except @data is replaced by the output



61
62
63
64
65
66
67
# File 'lib/signals/digital_signal.rb', line 61

def process!
    processed_data = []
    for i in 0...data.length do
        processed_data << (yield data[i])
    end
    self.data = processed_data
end

#process_in_place!Object

Updates data while processing, allowing recursive processing (ie using prev outputs to create new ones)



71
72
73
74
75
76
# File 'lib/signals/digital_signal.rb', line 71

def process_in_place!
    for i in 0...data.length do 
        self.data[i] = yield data[i]
    end
    self.data
end

#psdObject

Alias to #power_spectral_density



157
158
159
# File 'lib/signals/digital_signal.rb', line 157

def psd
    self.power_spectral_density
end

#to_aObject

Returns data as an array



178
179
180
# File 'lib/signals/digital_signal.rb', line 178

def to_a
    self.data
end

#value_at(n) ⇒ Object

Get the value at a specific data index. If index falls outside of the ‘data` array, return 0 This is useful when simulating a signal multiplied by a unit step which is zero outside the bounds defined in the data array



102
103
104
# File 'lib/signals/digital_signal.rb', line 102

def value_at(n)
    data[n].nil? ? 0 : data[n]
end

#values_between(start, stop) ⇒ Object

Get values in the data array from a start index to a start index (inclusive). Does not turn data outside array into a 0 value



108
109
110
# File 'lib/signals/digital_signal.rb', line 108

def values_between(start,stop)
    data[start, stop - start + 1]
end