Skip to content

Instantly share code, notes, and snippets.

@kristopherjohnson
Last active April 2, 2023 00:54
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kristopherjohnson/0b0442c9b261f44cf19a to your computer and use it in GitHub Desktop.
Save kristopherjohnson/0b0442c9b261f44cf19a to your computer and use it in GitHub Desktop.
Simple implementation of low-pass filter
struct LowPassFilterSignal {
/// Current signal value
var value: Double
/// A scaling factor in the range 0.0..<1.0 that determines
/// how resistant the value is to change
let filterFactor: Double
/// Update the value, using filterFactor to attenuate changes
mutating func update(newValue: Double) {
value = filterFactor * value + (1.0 - filterFactor) * newValue
}
}
@kristopherjohnson
Copy link
Author

Example:

class ViewController: UIViewController {

    // ...

    var smoothGravityX = LowPassFilterSignal(value: 0, filterFactor: 0.85)
    var smoothGravityY = LowPassFilterSignal(value: 0, filterFactor: 0.85)

    func startMotionUpdates() {
        if motionMgr.accelerometerAvailable {
            motionMgr.accelerometerUpdateInterval = 0.01
            motionMgr.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()) {
                [weak self] (data: CMAccelerometerData?, error: NSError?) in

                guard let data = data else { return }
                guard let vc = self else { return }

                // Raw accelerometer data is jittery, so use low-pass filter to smooth it
                vc.smoothGravityX.update(data.acceleration.x)
                vc.smoothGravityY.update(data.acceleration.y)

                vc.onUpdateGravityX(vc.smoothGravityX.value, gravityY: vc.smoothGravityY.value)
            }
        }
    }

    // ...
}

@aaronrogers
Copy link

Good stuff. Do you have value and newValue reversed?

Other examples I've been reading (AccelerometerGraph, CMMotionManager and Low-pass Filter, and Stack Overflow) all have the values reversed.

mutating func update(newValue: Double) {
  value = filterFactor * newValue + (1.0 - filterFactor) * value
}

@uros-gardasevic
Copy link

It's probably the naming that has made you confused. The previous computed value plays the significant role in creation of a new one, hence should be multiplied by higher factor (in our case, where α is picked as a very small number closer to 0, that factor is (1-α)).
Thats the whole point of this filter, to not let new value significantly peak over the current one.

So next to (1-α), where α should number between 0 and 1 but closer to 0, you should have previously computed value.
(1-α) * smoothedValueFromPreviousStep + α * newRawValue

If you look at the equation bellow, you might get better understand where s the motivation coming from.
value * (1-α) + value * α = (1 - α + α) * value = 1 * value = value

Finally, if you take an opposite approach in defining α, where it still resides in [0,1] but closer to 1, then you can revert the naming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment