forked from JonHub/Filters
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FilterTwoPole.cpp
executable file
·208 lines (173 loc) · 5.93 KB
/
FilterTwoPole.cpp
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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include "FilterTwoPole.h"
#ifdef ARDUINO_SAM_DUE
#define ARM_FLOAT
#endif
// The driven, damped harmonic oscillator equation is:
// http://en.wikipedia.org/wiki/Harmonic_oscillator)
//
// a + 2*ζ*ω0*v + ω0²*x = F(t)/m
//
// where the quality factor is related to the damping factor by
//
// Q = ½ζ
//
// It is useful to normalize the force to the spring constant,
// so a force of F will result in the oscillator resting at a position x=F
// The allows the oscillator to be used as a lowpass filter,
// where the position X functions as the output voltage V.
// Setting a=v=0, and solving for m, gives
//
// m = 1/w0
//
// and the final equation is
//
// a + 2*ζ*ω0*v + ω0²*x = ω0²*F(t)
//
// for determining the energy, you must know the effective spring constant, which is
// found from the equation (for undampened, where w0 = w)
//
// w0² = k/m
// w0 = k
//
// this makes the energy
//
// E = 0.5*k*x² + 0.5*m*v²
// = 0.5*w0*x² + 0.5*v²/w0
//
//
// Filter types (such as Bessel or Butterworth) are defined by
// specific quality factors ... the quality factor also defines a
// relationship between f0 (the undamped resonance frequency)
// and the -3 dB frequency of the filter
//
// For a Butterworth filter, these values are
// (see figures 8.26 and 8.32 Bessel)
// (See Analog Devices note AN-649)
// http://www.analog.com/static/imported-files/application_notes/447952852AN-649_0.pdf
// http://www.analog.com/library/analogDialogue/archives/43-09/EDCh%208%20filter.pdf)
//
// Butterworth
// F_0=1
// F_(-3dB)=1
// Q=1/√2
//
// Bessel
// F_0=1.2754
// F_(-3dB)=1
// Q=1/√3
//
// (note – ramp time for a Bessel filter is about 1/(2F_0 ) )
FilterTwoPole::FilterTwoPole( float frequency0, float qualityFactor, float xInit ) {
X = xInit; // start it some arbitrary position
Vprev = 0; // initially stopped
IsHighpass = false; // by default, a normal oscillator
setQ( qualityFactor );
setFrequency0( frequency0 );
LastTimeUS = micros();
}
void FilterTwoPole::setQ( float qualityFactor ) {
// zero will result in divide by zero, upper value keeps it stable
qualityFactor = constrain( qualityFactor, 1e-3, 1e3 );
Q = qualityFactor;
}
void FilterTwoPole::setFrequency0( float f ) {
W0 = TWO_PI*abs(f);
}
void FilterTwoPole::setAsFilter( OSCILLATOR_TYPE ft, float frequency3db, float initialValue ) {
// if this is a highpass filter, set to invert the transfer function on the output
//if( ft == HIGHPASS_BESSEL || ft == HIGHPASS_BUTTERWORTH ) {
// IsHighpass = true;
//}
//else {
IsHighpass = false;
//}
X = initialValue;
if( ft == LOWPASS_BESSEL ) {
setFrequency0( frequency3db * 1.28 );
setQ( 0.5774 );
}
//else if( ft == HIGHPASS_BESSEL ) {
// setFrequency0( frequency3db * 1.28 );
// setQ( 0.5774 );
//}
else if( ft == LOWPASS_BUTTERWORTH ) {
// set as butterworth
setFrequency0( frequency3db );
setQ( 0.7071 );
}
//else if( ft == HIGHPASS_BUTTERWORTH ) {
// set as butterworth
// setFrequency0( frequency3db );
// setQ( 0.7071 );
//}
}
float FilterTwoPole::input( float drive ) {
Fprev = drive; // needed when using filter as a highpass
long now = micros(); // get current time
float dt = 1e-6*float(now - LastTimeUS); // find dt
LastTimeUS = now; // save the last time
// constrain the dt
// if input has not been called frequently enough
// the velocity and position can fly off to extremly high values
// ... constraining the dt effectively "pauses" the motion during delays in updating
// note this will result in an incorrect answer, but if dt is too large
// the answer will be incorrect, regardless.
dt = constrain( dt, 0, 1.0/W0 );
float A = sq(W0)*drive - W0/Q*Vprev - sq(W0)*X; // *** compute acceleration
float V = Vprev + A * dt; // step velocity
Vavg = .5*(V+Vprev);
X += Vavg * dt; // step position, using average V to reduce error
// (trapezoidal integration)
Vprev = V; // save the last V
// normally, this returns output
// use it here to figure out how to return highpass
//return Q/W0*Vavg;
return output();
}
float FilterTwoPole::output() {
// if( IsHighpass )
// return Fprev-X-Q/W0*Vavg; // this is almost correct ...
// else
return X; // the filtered value (position of oscillator)
}
// as a measure for the energy of the oscillator, returns the maxium amplitude
float FilterTwoPole::getMaxAmp() {
// first, calculate the energy
// E = 0.5*w0*x² + 0.5*v²/w0
float E = 0.5 * W0 * sq(X) + 0.5 * sq(Vprev) / W0;
// calculate use this to calculate max amplitude
// E = 0.5*w0*x²
// x = sqrt(2*E/w0)
#ifdef ARM_FLOAT
return sqrtf(2.0*E/W0);
#else
return sqrt(2.0*E/W0);
#endif
}
void FilterTwoPole::print() {
Serial.print(" X: "); Serial.print( X );
Serial.print(" Vprev: "); Serial.print( Vprev );
Serial.println("");
}
void FilterTwoPole::test() {
float updateInterval = .1;
float nextupdateTime = 1e-6*float(micros());
float inputValue = 0;
FilterTwoPole osc( 0.2, 4, 0);
while( true ) {
float now = 1e-6*float(micros());
// switch input values on a 20 second cycle
if( round(now/50.0)-(now/50.0) < 0 )
inputValue = 100;
else
inputValue = 150;
osc.input(inputValue);
analogWrite(10,osc.output() ); // hardcoded the dial pin
if( now > nextupdateTime ) {
nextupdateTime += updateInterval;
Serial.print("inputValue: "); Serial.print( inputValue );
Serial.print("\t output: "); Serial.print( osc.output() );
Serial.println();
}
}
}