Basti's Scratchpad on the Internet

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))))))
Other posts
comments powered by Disqus