-
Notifications
You must be signed in to change notification settings - Fork 28
3. Using CARP Mobile Sensing
A CAMS study is configured, deployed, executed, and used in the following steps:
- Define a
StudyProtcol
. - Deploy this protocol to a
DeploymentService
. - Get a study deployment for the phone and start executing this study deployment using a
SmartPhoneClientManager
. - Use the generated
measurements
locally in the app or specify how and where to store or upload it using aDataEndPoint
.
Note that as a mobile sensing framework running on a phone (i.e., a "client" in the CARP Core Architecture), CAMS could be limited to support 3-4. However, to support that an app can create and deploy an application-specific protocol locally on the phone, CAMS also supports 1-2. This allows for local creation, deployment, and execution of study protocols, which in many applications have shown to be useful.
In CAMS, a sensing protocol is configured in a StudyProtocol
.
Below is a simple example of how to set up a protocol that collects step counts, ambient light, screen activity, and battery status from the phone.
// Create a protocol.
final phone = Smartphone();
final protocol = SmartphoneStudyProtocol(
ownerId: 'AB',
name: 'Tracking steps, light, screen, and battery',
dataEndPoint: SQLiteDataEndPoint(),
)
..addPrimaryDevice(phone)
..addTaskControl(
DelayedTrigger(delay: const Duration(seconds: 10)),
BackgroundTask(measures: [
Measure(type: SensorSamplingPackage.STEP_COUNT),
Measure(type: SensorSamplingPackage.AMBIENT_LIGHT),
Measure(type: DeviceSamplingPackage.SCREEN_EVENT),
Measure(type: DeviceSamplingPackage.BATTERY_STATE),
]),
phone,
Control.Start,
);
The above example defines a SmartphoneStudyProtocol
with an ownerId
and name
, which will store data in an SQLite database locally on the phone using a SQLiteDataEndPoint
.
The primary device is a Smartphone
device configuration.
Sampling is configured by adding a TaskControl
configuration, which specifies the trigger, task, device, and control for data sampling. In this example, a DelayedTrigger
starts sampling after 10 seconds using a BackgroundTask
, which will collect four measures on step events, ambient light, screen events, and battery state events. This data sampling should be done by the smartphone (primary device) and when triggered, sampling should start (Control
).
Sampling can be configured in very sophisticated ways, by specifying different types of triggers, tasks, measures, and sampling configurations. See the CAMS domain model for an overview and Appendix A for a list of supported measure types (i.e., what data the framework can collect).
You can write your own DataEndPoint
definitions and corresponding DataManager
s for uploading data to your own data endpoint. See the wiki on how to add a new data manager.
A device deployment specifies how a study protocol is executed on a specific device - in this case, a smartphone.
A StudyProtocol
can be deployed to a DeploymentService
which handles the deployment of protocols for different devices. CAMS comes with a local deployment service (the SmartphoneDeploymentService
) which runs on the phone. This can be used to deploy a protocol and get back a SmartphoneDeployment
, which can be executed on the phone.
...
// Use the on-phone deployment service.
DeploymentService deploymentService = SmartphoneDeploymentService();
// Create a study deployment using the protocol
var status = await deploymentService.createStudyDeployment(protocol);
...
A study deployment for a smartphone (primary device) is handled by a SmartPhoneClientManager
.
...
var studyDeploymentId = ... // any id obtained e.g. from an invitation
var deviceRoleName = ... // the role name of this phone in the protocol;
// Create and configure a client manager for this phone.
SmartPhoneClientManager client = SmartPhoneClientManager();
await client.configure(deploymentService: deploymentService);
// Add the study to the client manager and get a study runtime to
// control this deployment
Study study = await client.addStudy(
studyDeploymentId,
deviceRoleName,
);
SmartphoneDeploymentController? controller = client.getStudyRuntime(study);
// Deploy the study on this phone.
await controller?.tryDeployment();
// Configure the controller.
await controller?.configure();
...
In the code above, the following steps are taken:
- A SmartPhoneClientManager is created and configured by specifying what DeploymentService to use.
- A study is added to the client based on the study deployment id and the role name of this device in the protocol
- The
StudyDeploymentController
, which controls the execution of a study deployment is created - The study is deployed to the controller
- The controller is configured (in this case only default configuration is done)
When a study deployment is deployed to a client and a controller is ready, the controller will execute the study.
controller?.start();
// Print all data events from the study
controller?.measurements.forEach(print);
By calling the start and stop methods on the controller, data sampling is resumed or paused. Data sampling entails that all tasks and measures are triggered as specified in the study protocol, and data is uploaded to the data endpoint specified in the protocol.
The sampled measurements are available from the measurements
stream of the controller.
A controller can permanently be disposed of:
// Once the sampling has to stop, e.g. in a Flutter dispose() method,
// call the controller's dispose method.
controller.dispose();
The above description is a generic description of how CAMS works with a deployment service and how to use this to deploy and get studies. Moreover, the client manager is designed to handle multiple studies, which each is added to it.
However, if you just want to create a single study protocol and execute this locally on the phone, CAMS comes with a set of methods to do this in a very simple manner, as illustrated below.
// Create and configure a client manager for this phone, add the protocol,
// and start sampling data.
SmartPhoneClientManager().configure().then((_) => SmartPhoneClientManager()
.addStudyProtocol(protocol)
.then((_) => SmartPhoneClientManager().start()));
Basically, the protocol can be added to the singleton SmartPhoneClientManager
, which then can be started. Hence, once you have defined the protocol, the whole CAMS runtime infrastructure can be started in one line of code.
Listening to measurements is done on the client manager;
// Listening on the data stream and print them as JSON.
SmartPhoneClientManager()
.measurements
.listen((measurement) => print(toJsonString(measurement)));
Sampling can be started and stopped via the client manager, which also can be disposed when the app is disposed of.
You can access the sampled data in your app via the measurements
stream. For example, the following code would listen to all measurements from the study created above and print it to the Dart console.
controller?.measurements.forEach(print);
The measurements
stream is available both in the controller and the client, where the client aggregates measurements from all the studies it is running.
This measurements
stream is a standard Dart Stream
and you can hence perform all the normal stream (reactive) operations on it. For example, if you only want to listen to light
events, write;
controller?.measurements
.where((measurement) =>
measurement.data.format.toString() ==
SensorSamplingPackage.AMBIENT_LIGHT)
.listen((measurement) => print(measurement));
Or map the data;
// map measurements to JSON and then print
controller?.measurements
.map((measurement) => measurement.toJson())
.listen((json) => print(json));
In general, you can listen to events using the Dart StreamSubscription
;
// subscribe to the stream of measurements
StreamSubscription<Measurement> subscription =
controller!.measurements.listen((Measurement measurement) {
// do something w. the measurement, e.g. print the json
print(const JsonEncoder.withIndent(' ').convert(measurement));
});
The measurements
stream originates from each individual Probe
and you can listen directly to a probe. For example, listening to probes of a certain type, like acceleration;
// Listen to a specific probe(s)
controller.executor
.lookupProbe(CarpDataTypes.ACCELERATION_TYPE_NAME)
.forEach((probe) => probe.measurements.listen(
(measurement) => print(measurement),
));
In general, measurements originate from probes, are aggregated by study controllers, and again aggregated by the client. You can listen to measurements on all levels.
CAMS supports data transformation via two concepts:
-
DataTransformer
which can transform one type ofData
to another type ofData
. -
DataTransformerSchema
which holds a set ofDataTransformer
s that can map from the native CARP namespace to another namespace.
Transformation is configured by specifying which data format is to be used in the data endpoint specified in the study protocol. For example, to transform data into another format like Open mHealth (OMH), the study protocol would be specified like this:
// Create a study protocol storing data in files using the OMH data format
SmartphoneStudyProtocol protocol = SmartphoneStudyProtocol(
ownerId: 'AB',
name: 'Track patient movement',
dataEndPoint: FileDataEndPoint(
bufferSize: 500 * 1000,
zip: true,
encrypt: false,
dataFormat: NameSpace.OMH,
),
);
See also the section on how to add data transformers of your own.
When configuring a study protocol, you can specify which privacy schema to use to protect the collected data. CAMS comes with a default PrivacySchema
which implements privacy protection and anonymization methods on the data being collected. For example, one-way hashing of phone numbers before they are stored.
To use this schema, simply specify this schema when creating a study protocol:
//Configure the controller with the default privacy schema
SmartphoneStudyProtocol protocol = SmartphoneStudyProtocol(
ownerId: 'AB',
name: 'Track patient movement',
privacySchemaName: PrivacySchema.DEFAULT,
);
This default privacy schema can be extended or you can write your own privacy schemas and use these instead.