Low and High Pass Filter Implementation in Clojure
Following is a port of the Apple's low and high pass filter implementation which can be found in AccelerometerFilter.h / .m that in turn is based on the pseudocode algorithms from Wikipedia. By definition a low pass filter will allow low-frequency signals to pass but will reduce the amplitude of high frequency signals, a high pass filter will do the opposite and will allow high-frequency signals to pass and reduce the amplitude of low frequency signals.
To give you an idea, following graph represents accelerometer data captured from my iPhone along the X axis,
(use '[incanter core io charts])
(let [data (read-dataset "capture.txt" :header true)
time (sel data :cols :Timestamp)
acc (sel data :cols :AccelX)]
(view (line-chart time acc) :width 1400 :height 200))
Running the data set through the low pass filter, removes the jittering and gives us smoother values,
(let [filter (low-pass-filter 60 1 true [0])
data (read-dataset "capture.txt" :header true)
time (sel data :cols :Timestamp)
acc (map #(first (low-pass-filter filter [%]))
(sel data :cols :AccelX))]
(view (line-chart time acc) :width 1400 :height 200))
(ns filter.core)
(def k-min-step 0.02)
(def k-noise-attenuation 3.0)
(defn- norm [vs]
(Math/sqrt (apply + (map #(Math/pow % 2) vs))))
(defn- clamp [x min max]
(cond
(> x max) max
(< x min) min
:default x))
(defn low-pass-filter
([rate cut-off-freq adaptive? vals]
(let [dt (/ 1 rate)
rc (/ 1 cut-off-freq)
filter-constant (/ dt (+ dt rc))]
(ref {:filter-constant filter-constant
:vals vals
:adaptive adaptive?})))
([filter input]
(let [{:keys [filter-constant vals adaptive?]} @filter
alpha (if adaptive?
(let [d (clamp (/ (Math/abs (- (norm vals)
(norm input)))
(- k-min-step 1.0))
0.0 1.0)]
(+ (/ (* (- 1.0 d) filter-constant)
k-noise-attenuation)
(* d filter-constant)))
filter-constant)
vals (map #(+ (* %1 alpha) (* %2 (- 1.0 alpha))) input vals)]
(:vals (dosync (alter filter assoc :vals vals))))))
(defn high-pass-filter
([rate cut-off-freq adaptive? vals]
(let [dt (/ 1 rate)
rc (/ 1 cut-off-freq)
filter-constant (/ rc (+ dt rc))]
(ref {:filter-constant filter-constant
:vals vals
:raw-vals vals
:adaptive adaptive?})))
([filter input]
(let [{:keys [filter-constant vals raw-vals adaptive?]} @filter
alpha (if adaptive?
(let [d (clamp (/ (Math/abs (- (norm vals)
(norm input)))
(- k-min-step 1.0))
0.0 1.0)]
(+ (/ (* d filter-constant)
k-noise-attenuation)
(* (- 1.0 d) filter-constant)))
filter-constant)
vals (map #(* alpha (- (+ %2 %1) %3)) input vals raw-vals)]
(:vals (dosync (alter filter assoc :vals vals :raw-vals input))))))