Skip to content

Commit

Permalink
Implement changing freq (interval) property after instance creation
Browse files Browse the repository at this point in the history
lib/sensor.js
Move median calculation function definition outside of the constructor function
Move latest median value storage to closure scope, to provide history between calls
Add read only, test mode only, state property to expose internal instance state information
Use a local (constructor function scope) variable to access the private state properties
Replace (in constructor) references to the private state information with the local variable

test/sensor.js
Add test cases for freq, threshold, id, limit properties
Extend expected instance shape information
Expand testing for analog noise filtering
Expand testing for data and change events
Remove some duplicate tests (data)
Streamline instance shape tests using deep object compare
Require loadah module to get access to cloneDeep method
Add function to create object containing the current instance shape information
Fix test for unexpected properties in sensor instance

Add comments to document the test sequences
Add closing comment for function definitions
Add some line breaks for readability, and lint nit pick.
  • Loading branch information
mMerlin authored and rwaldron committed Jul 19, 2015
1 parent f270365 commit 0d5887b
Show file tree
Hide file tree
Showing 2 changed files with 547 additions and 268 deletions.
112 changes: 56 additions & 56 deletions lib/sensor.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,24 @@ var priv = new Map(),
// Flex Sensor (alias)
"bend"
]
};
},
IS_TEST_MODE = !!process.env.IS_TEST_MODE;

// To reduce noise in sensor readings, sort collected samples
// from high to low and select the value in the center.
function arrayMedian(input) {
var half, len, sorted;
// faster than default comparitor (even for small n)
sorted = input.sort(function(a, b) {
return a - b;
});
len = sorted.length;
half = Math.floor(len / 2);

// If the length is odd, return the midpoint m
// If the length is even, return average of m & m + 1
return len % 2 ? sorted[half] : (sorted[half - 1] + sorted[half]) / 2;
}// ./arrayMedian(input)


/**
Expand All @@ -37,14 +54,15 @@ function Sensor(opts) {
return new Sensor(opts);
}

// var value, last, min, max, samples;
var value, last, min, max, samples, intervalId, eventProcessing;
var value, last, min, max, samples, state, median, intervalId, eventProcessing;

value = null;
min = 1023;
max = 0;
last = -min;
samples = [];
intervalId = null;
median = NaN;

Board.Component.call(
this, opts = Board.Options(opts)
Expand All @@ -62,7 +80,6 @@ function Sensor(opts) {
this.io.pinMode(this.pin, this.mode);

// Sensor instance properties
// this.freq = opts.freq || 25;
this.range = opts.range || [0, 1023];
this.limit = opts.limit || null;
this.threshold = opts.threshold === undefined ? 1 : opts.threshold;
Expand All @@ -81,12 +98,11 @@ function Sensor(opts) {
}.bind(this));

// Throttle
// setInterval(function() {
// TODO: this function should be outside of the Sensor constructor function
// (with appropritate passed arguments), to avoid creating a separate copy
// (of the function) for each Sensor instance.
// TODO: The event (interval) processing function should be outside of the Sensor
// constructor function (with appropriate passed (and bound?) arguments), to
// avoid creating a separate copy (of the function) for each Sensor instance.
eventProcessing = function () {
var err, median, low, high, boundary;
var err, boundary;

err = null;

Expand All @@ -105,37 +121,21 @@ function Sensor(opts) {
return;
}


// To reduce noise in sensor readings, sort collected samples
// from high to low and select the value in the center.
median = (function(input) {
var half, len, sorted;
// faster than default comparitor (even for small n)
sorted = input.sort(function(a, b) {
return a - b;
});
len = sorted.length;
half = Math.floor(len / 2);

// If the length is odd, return the midpoint m
// If the length is even, return average of m & m + 1
return len % 2 ? sorted[half] : (sorted[half - 1] + sorted[half]) / 2;
}(samples));
// Keep the previous calculated value if there were no new readings
if (samples.length > 0) {
// Filter the accumulated sample values to reduce analog reading noise
median = arrayMedian(samples);
}
// @DEPRECATE
this.emit("read", err, median);
// The "read" event has been deprecated in
// favor of a "data" event.
this.emit("data", err, median);
// If the median value for this interval is outside last +/- a threshold
// fire a change event

low = last - this.threshold;
high = last + this.threshold;

// Includes all aliases
// Prevent events from firing if the latest value is the same as (within
// the threshold of) the last value to trigger a change event
if (median < low || median > high) {
// If the filtered (median) value for this interval is at least ± the
// configured threshold from last, fire change events
if (median <= (last - this.threshold) || median >= (last + this.threshold)) {
// Include all aliases
aliases.change.forEach(function(change) {
this.emit(change, err, median);
}, this);
Expand Down Expand Up @@ -164,19 +164,18 @@ function Sensor(opts) {

// Reset samples
samples.length = 0;
// }.bind(this), this.freq);
}.bind(this);
}.bind(this);// ./function eventProcessing()

// Create a "state" entry for privately
// storing the state of the sensor
priv.set(this, {
state = {
booleanBarrier: opts.type === "digital" ? 0 : 512,
scale: null,
// value: 0
value: 0,
freq: opts.freq || 25
});

};
// Put a reference where the prototype methods defined in this file have access
priv.set(this, state);

Object.defineProperties(this, {
raw: {
Expand Down Expand Up @@ -212,19 +211,17 @@ function Sensor(opts) {
},
scaled: {
get: function() {
var mapped, constrain, scale;

scale = priv.get(this).scale;
var mapped, constrain;

if (scale && value !== null) {
if (state.scale && value !== null) {
if (opts.type === "digital") {
// Value is either 0 or 1, use as an index
// to return the scaled value.
return scale[value];
return state.scale[value];
}

mapped = Board.fmap(value, this.range[0], this.range[1], scale[0], scale[1]);
constrain = Board.constrain(mapped, scale[0], scale[1]);
mapped = Board.fmap(value, this.range[0], this.range[1], state.scale[0], state.scale[1]);
constrain = Board.constrain(mapped, state.scale[0], state.scale[1]);

return constrain;
}
Expand All @@ -233,12 +230,10 @@ function Sensor(opts) {
},
freq: {
get: function() {
return priv.get(this).freq;
return state.freq;
},
set: function(newFreq) {
priv.set(this, {
freq: newFreq
});
state.freq = newFreq;
if (intervalId) {
clearInterval(intervalId);
}
Expand All @@ -247,10 +242,6 @@ function Sensor(opts) {
},
value: {
get: function() {
var state;

state = priv.get(this);

if (state.scale) {
this.isScaled = true;
return this.scaled;
Expand All @@ -260,9 +251,18 @@ function Sensor(opts) {
}
}
});
if (IS_TEST_MODE) {
Object.defineProperties(this, {
state: {
get: function() {
return priv.get(this);
}
}
});
}

// Set the freq property only after the get and set functions are defined
this.freq = opts.freq || 25;
this.freq = state.freq;
}

util.inherits(Sensor, events.EventEmitter);
Expand Down
Loading

0 comments on commit 0d5887b

Please sign in to comment.