Skip to content

Commit

Permalink
feat: custom features in fingerprint node
Browse files Browse the repository at this point in the history
  • Loading branch information
Maximvdw committed Jan 13, 2025
1 parent 048080f commit b63deea
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 11 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: |
npm run build
- name: Upload web artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: web
path: |
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
**/docs/out
key: ${{ runner.os }}-docs-v1-${{ github.run_number }}
- name: Publish Documentation
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: docs
path: |
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
<a href="https://github.com/OpenHPS/openhps-fingerprinting/actions/workflows/main.yml" target="_blank">
<img alt="Build Status" src="https://github.com/OpenHPS/openhps-fingerprinting/actions/workflows/main.yml/badge.svg">
</a>
<a href="https://codecov.io/gh/OpenHPS/openhps-fingerprinting">
<img src="https://codecov.io/gh/OpenHPS/openhps-fingerprinting/branch/master/graph/badge.svg"/>
</a>
<a href="https://codeclimate.com/github/OpenHPS/openhps-fingerprinting/" target="_blank">
<img alt="Maintainability" src="https://img.shields.io/codeclimate/maintainability/OpenHPS/openhps-fingerprinting">
</a>
<a href="https://badge.fury.io/js/@openhps%2Ffingerprinting">
<img src="https://badge.fury.io/js/@openhps%2Ffingerprinting.svg" alt="npm version" height="18">
</a>
Expand Down Expand Up @@ -76,6 +70,11 @@ ModelBuilder.create()
.build();
```

### Custom Properties
It is possible to store custom properties in the fingerprint data object. This can be useful for creating features that
contain non-relative features such as sensor values or other data.


## Contributors
The framework is open source and is mainly developed by PhD Student Maxim Van de Wynckel as part of his research towards *Hybrid Positioning and Implicit Human-Computer Interaction* under the supervision of Prof. Dr. Beat Signer.

Expand Down
27 changes: 25 additions & 2 deletions src/nodes/FingerprintingNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ObjectProcessingNode,
ObjectProcessingNodeOptions,
} from '@openhps/core';
import { Fingerprint } from '../data';
import { Fingerprint, RelativeValue } from '../data';
import { FingerprintingOptions, FingerprintService } from '../service/FingerprintService';

/**
Expand Down Expand Up @@ -58,6 +58,22 @@ export class FingerprintingNode<

processObject(dataObject: DataObject, dataFrame: InOut): Promise<DataObject> {
return new Promise((resolve, reject) => {
// Include custom features
if (this.options.features && this.options.features.length > 0) {
this.options.features.forEach((feature) => {
let value: number;
let key: string;
if (typeof feature === 'string') {
key = feature;
value = dataObject[feature];
} else {
key = feature.key;
value = feature.value(dataObject);
}
dataObject.addRelativePosition(new RelativeValue(`__property_${key}`, value));
});
}

if (dataObject.position !== undefined && !this.options.locked) {
this.offlineFingerprinting(dataObject, dataFrame).then(resolve).catch(reject);
} else if (dataObject.relativePositions.length > 0) {
Expand Down Expand Up @@ -137,7 +153,9 @@ export class FingerprintingNode<
}
}

export interface FingerprintingNodeOptions extends ObjectProcessingNodeOptions {
type NumberProperty<T> = keyof { [K in keyof T as T[K] extends number ? K : never]: T[K] };

export interface FingerprintingNodeOptions<T extends DataObject = DataObject> extends ObjectProcessingNodeOptions {
locked?: boolean;
/**
* Fingerprint classifier
Expand All @@ -153,4 +171,9 @@ export interface FingerprintingNodeOptions extends ObjectProcessingNodeOptions {
* This option uses more memory.
*/
serializeContext?: boolean;
/**
* A list of features to include in the fingerprint
* Only features of type number are allowed
*/
features?: (NumberProperty<T> | { key: string; value: (obj: T) => number })[];
}
62 changes: 61 additions & 1 deletion test/specs/node.knn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import {
CallbackSinkNode,
DataFrame,
DataObject,
GraphBuilder,
MemoryDataService,
ModelBuilder,
ModelBuilder,
Pressure,
SerializableMember,
SerializableObject,
} from '@openhps/core';
import {
RelativeRSSI,
Expand Down Expand Up @@ -80,4 +84,60 @@ describe('node knn fingerprinting', () => {
}).catch(done);
});

it('should support custom features', (done) => {
@SerializableObject()
class CustomObject extends DataObject {
@SerializableMember()
pressure: Pressure;
}

ModelBuilder.create()
.addService(new FingerprintService(new MemoryDataService(Fingerprint), {
autoUpdate: true,
}))
.addShape(GraphBuilder.create()
.from("offline")
.via(new FingerprintingNode({
name: "fingerprinting",
features: [
{ key: "pressure", value: (object: CustomObject) => object.pressure.value }
],
}))
.to(new CallbackSinkNode())
)
.addShape(GraphBuilder.create()
.from("online")
.via(new KNNFingerprintingNode({
features: [
{ key: "pressure", value: (object: CustomObject) => object.pressure.value }
],
}))
.to(new CallbackSinkNode())
)
.build().then(m => {
const object = new CustomObject("phone");
object.setPosition(new Absolute2DPosition(1, 1));
object.addRelativePosition(new RelativeRSSI(new RFTransmitterObject("AP_1"), 4));
object.addRelativePosition(new RelativeRSSI(new RFTransmitterObject("AP_2"), 5));
object.addRelativePosition(new RelativeRSSI(new RFTransmitterObject("AP_3"), 6));
object.pressure = new Pressure(1013);
const frame = new DataFrame(object);
const offline = m.findNodeByName("offline");
const online = m.findNodeByName("online");
offline.onceCompleted(frame.uid).then(() => {
const node1 = m.findNodeByName("fingerprinting") as KNNFingerprintingNode<any>;
expect(node1.cache.length).to.be.equal(1);
expect(node1.cache[0].vector.length).to.equal(4);
// Now test online fingerprinting
object.position = undefined;
const frame = new DataFrame(object);
online.onceCompleted(frame.uid).then(() => {
done();
});
online.push(frame);
}).catch(done);
offline.push(frame);
}).catch(done);
});

});

0 comments on commit b63deea

Please sign in to comment.