SofaPirate / Plaquette

Object-oriented Arduino library for creative physical computing
https://sofapirate.github.io/Plaquette/
GNU General Public License v3.0
15 stars 4 forks source link

MovingFilter time windows have wrong behavior if not called once-per-step #115

Closed possibly-human closed 2 months ago

possibly-human commented 5 months ago

For objets using a timeWindow (ex: Normalizer, MinMaxScaler...), the timeWindow works according to a rate of 1 update/sampling per step(), and the sampleRate() is not easily modifiable by users.

In certain cases, it would be interesting to be able to set the sample rate. Ex : I want to normalize a signal with a timeWindow of 5 seconds but sampling the signal at a frequency of 5Hz.

Something like "normalizer.setSampleRate(5);" would be ideal.

sofian commented 5 months ago

The issue is more complicated to me than simply allowing for unit-specific sample rates, because that still presumes the unit is called once-per-step. In reality, units can be called:

To make a robust calculation of the filter, I believe we need to keep the notion of sampleRate() (which just concerns the rate at which step() is called) separated from this issue.

sofian commented 5 months ago

When we call put(float) on the fillter, we could calculate how much time has passed since the last call to put(float) which would tell us how to adjust based on the target time window. This will work well if we call put(float) at most once per step.

However, if we call several times in one step, there will be no time difference since the last call (cause the step() function is supposed to all happen in a single time step). So I think we'll need to also preserve some kind of average of all values within a single step() to make some kind of an estimate. Not sure how to handle all of this in the most memory-efficient manner.

sofian commented 5 months ago

We also have to deal with the isStarted() / _isAdaptive mechanism appropriately.

sofian commented 5 months ago

@possibly-human For now I would advise you to precalculate the timeWindow that you need based on sample rate so that we can get things moving. This issue proves to be more complicated than first thought.

Another option is to constantly send the value to the smoother at every frame even tough you use the same value. Eg.

float sensorValue;
...
void step() {
  if (metro)
    mySensor >> sensorValue; // save value
  sensorValue >> smoother;
}
sofian commented 4 months ago

I implemented a fix for Smoother. @possibly-human can you please put it to test and let me know if it works?

possibly-human commented 4 months ago

I implemented a fix for Smoother. @possibly-human can you please put it to test and let me know if it works?

Will do!

possibly-human commented 4 months ago

It works!

Capture d’écran, le 2024-06-09 à 14 44 44
sofian commented 4 months ago

I implemented fixes for MinMaxScaler and Normalizer but needs testing. @possibly-human

possibly-human commented 4 months ago

Seems to work. Not much difference when a normalizer or scaler is called twice in one step. Will keep an eye out for normalizer and scaler behaviour with implemented changes.

Previous version: Previous version

Updated version: Updated

Arduino code: Code