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))))))