Class: SPCore::FrequencyDomain
- Inherits:
-
Object
- Object
- SPCore::FrequencyDomain
- Includes:
- Hashmake::HashMakeable
- Defined in:
- lib/spcore/analysis/frequency_domain.rb
Overview
if specified in :fft_format (see FFT_FORMATS for valid values).
Constant Summary collapse
- FFT_COMPLEX_VALUED =
:complexValued
- FFT_MAGNITUDE_LINEAR =
:magnitudeLinear
- FFT_MAGNITUDE_DECIBEL =
:magnitudeDecibel
- FFT_FORMATS =
valid values to give for the :fft_format key.
[ FFT_COMPLEX_VALUED, FFT_MAGNITUDE_LINEAR, FFT_MAGNITUDE_DECIBEL ]
- ARG_SPECS =
define how the class is to be instantiated by hash.
{ :time_data => arg_spec_array(:reqd => true, :type => Numeric), :sample_rate => arg_spec(:reqd => true, :type => Numeric, :validator => ->(a){ a > 0 }), :fft_format => arg_spec(:reqd => false, :type => Symbol, :default => FFT_MAGNITUDE_DECIBEL, :validator => ->(a){FFT_FORMATS.include?(a)}) }
- GCD =
:gcd
- WINDOW =
:window
- HARMONIC_SERIES_APPROACHES =
[ GCD, WINDOW ]
Instance Attribute Summary collapse
-
#fft_format ⇒ Object
readonly
Returns the value of attribute fft_format.
-
#fft_full ⇒ Object
readonly
Returns the value of attribute fft_full.
-
#fft_half ⇒ Object
readonly
Returns the value of attribute fft_half.
-
#sample_rate ⇒ Object
readonly
Returns the value of attribute sample_rate.
-
#time_data ⇒ Object
readonly
Returns the value of attribute time_data.
Instance Method Summary collapse
-
#freq_to_idx(freq) ⇒ Object
Convert an FFT frequency bin to the corresponding FFT output index.
-
#harmonic_series(opts = {}) ⇒ Object
Find the strongest harmonic series among the given peak data.
-
#idx_to_freq(idx) ⇒ Object
Convert an FFT output index to the corresponding frequency bin.
-
#initialize(args) ⇒ FrequencyDomain
constructor
A new instance of FrequencyDomain.
-
#peaks ⇒ Object
Find frequency peak values.
Constructor Details
#initialize(args) ⇒ FrequencyDomain
Returns a new instance of FrequencyDomain.
30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 30 def initialize args hash_make args, FrequencyDomain::ARG_SPECS @fft_full = FFT.forward @time_data @fft_half = @fft_full[0...(@fft_full.size / 2)] case(@fft_format) when FFT_MAGNITUDE_LINEAR @fft_half = @fft_half.map {|x| x.magnitude } when FFT_MAGNITUDE_DECIBEL @fft_half = @fft_half.map {|x| Gain.linear_to_db x.magnitude } # in decibels end end |
Instance Attribute Details
#fft_format ⇒ Object (readonly)
Returns the value of attribute fft_format.
28 29 30 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 28 def fft_format @fft_format end |
#fft_full ⇒ Object (readonly)
Returns the value of attribute fft_full.
28 29 30 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 28 def fft_full @fft_full end |
#fft_half ⇒ Object (readonly)
Returns the value of attribute fft_half.
28 29 30 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 28 def fft_half @fft_half end |
#sample_rate ⇒ Object (readonly)
Returns the value of attribute sample_rate.
28 29 30 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 28 def sample_rate @sample_rate end |
#time_data ⇒ Object (readonly)
Returns the value of attribute time_data.
28 29 30 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 28 def time_data @time_data end |
Instance Method Details
#freq_to_idx(freq) ⇒ Object
Convert an FFT frequency bin to the corresponding FFT output index
49 50 51 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 49 def freq_to_idx(freq) return (freq * @fft_full.size) / @sample_rate.to_f end |
#harmonic_series(opts = {}) ⇒ Object
Find the strongest harmonic series among the given peak data.
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 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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 72 def harmonic_series opts = {} defaults = { :n_peaks => 8, :min_freq => 40.0, :approach => WINDOW } opts = defaults.merge(opts) n_peaks = opts[:n_peaks] min_freq = opts[:min_freq] approach = opts[:approach] raise ArgumentError, "n_peaks is < 1" if n_peaks < 1 peaks = self.peaks if peaks.empty? return [] end max_freq = peaks.keys.max max_idx = freq_to_idx(max_freq) sorted_pairs = peaks.sort_by {|f,m| m} top_n_pairs = sorted_pairs.reverse[0, n_peaks] candidate_series = [] case approach when GCD for n in 1..n_peaks combinations = top_n_pairs.combination(n).to_a combinations.each do |combination| freq_indices = combination.map {|pair| freq_to_idx(pair[0]) } fund_idx = multi_gcd freq_indices if fund_idx >= freq_to_idx(min_freq) series = [] idx = fund_idx while idx <= max_idx freq = idx_to_freq(idx) #if peaks.has_key? freq series.push freq #end idx += fund_idx end candidate_series.push series end end end when WINDOW # look for a harmonic series top_n_pairs.each do |pair| f_base = pair[0] min_idx_base = freq_to_idx(f_base) - 0.5 max_idx_base = min_idx_base + 1.0 harmonic_series = [ f_base ] target = 2 * f_base min_idx = 2 * min_idx_base max_idx = 2 * max_idx_base while target < max_freq f_l = idx_to_freq(min_idx.floor) f_h = idx_to_freq(max_idx.ceil) window = f_l..f_h candidates = peaks.select {|actual,magn| window.include?(actual) } if candidates.any? min = candidates.min_by {|actual,magn| (actual - target).abs } harmonic_series.push min[0] else break end target += f_base min_idx += min_idx_base max_idx += max_idx_base end candidate_series.push harmonic_series end else raise ArgumentError, "#{approach} approach is not supported" end strongest_series = candidate_series.max_by do |harmonic_series| sum = 0 harmonic_series.each do |f| if peaks.has_key?(f) sum += peaks[f]**2 else m = @fft_half[freq_to_idx(f)].magnitude sum += m**2 end end sum end return strongest_series end |
#idx_to_freq(idx) ⇒ Object
Convert an FFT output index to the corresponding frequency bin
44 45 46 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 44 def idx_to_freq(idx) return (idx * @sample_rate.to_f) / @fft_full.size end |
#peaks ⇒ Object
Find frequency peak values.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/spcore/analysis/frequency_domain.rb', line 54 def peaks # map positive maxima to indices positive_maxima = Features.positive_maxima(@fft_half) freq_peaks = {} positive_maxima.keys.sort.each do |idx| freq = idx_to_freq(idx) freq_peaks[freq] = positive_maxima[idx] end return freq_peaks end |