// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** Measurements represent recordings from sensors at regular intervals, or algorithm outputs derived from such sensor data. The structure works a bit like a csv. There is a header which describes the type and order of the values stored in the data field. When dealing with measurements it is important to consider if the signal you are measuring is a sample an integral signal. Samples and integrals are treated very different mathematically. Samples only make sense to talk about at instants in time, while integrals only make sense to talk about time windows. Heart rate and acceleration are good examples of sample measurements, while steps and are good examples of integral measurements. For sequential integral measurements, one time window ends an instant before the next measurement begins, therefore one only need to add an explicit end to the last measurement (see time_end_utc) Because of these assumptions, non-sequential integral measurements should be broken into separate messages. */ syntax = "proto2"; package pebble.pipeline; option java_package = "com.getpebble.pipeline"; import "common.proto"; message Measurement { required uint32 offset_sec = 1; /// offset since timestamp (in seconds) repeated uint32 data = 2 [packed=true]; } message MeasurementSet { enum HeartRateQuality { NoAccel = 0; OffWrist = 1; NoSignal = 2; Worst = 3; Poor = 4; Acceptable = 5; Good = 6; Excellent = 7; } enum Type { /// types of measurement samples we can take. minor changes to SI units to encode efficiently UnknownEvent = 0; /// Protocol Buffers uses the first enum as the default TimeMS = 1; /// more percise time, in milliseconds VMC = 2; /// Vector Magnitude Counts. Rough measure of total activity. Unitless Steps = 3; /// Accumulated steps. Unitless DistanceCM = 4; /// Distance traveled. In centimeters RestingGCalories = 5; /// Calories burned due to resting metabolic rate. In gram calories ActiveGCalories = 6; /// Calories burned due to activity. In gram calories BPM = 7; /// Heart rate. Beats per minutes RR = 8; /// Heart rate variability. Peak to peak time in ms Orientation = 9; /// Orientation of the watch. Some weird units Light = 10; /// Ambient light. Temperature = 11; /// Temperature of watch. Kelvin HRQuality = 12; /// Quality of heart rate signal, a HeartRateQuality enum } required bytes uuid = 1; /// 16-byte uuid for efficient message size optional User user = 2; required uint32 time_utc = 3; /// time since epoch (in seconds) required sint32 utc_to_local = 4; /// sint32 stores neg number efficiently. need to recast before adding to time_utc, but saves storage optional bytes sensor_settings = 5; /// sensors might have all sorts of different modes or config that might be best represented as a byte blob repeated Type types = 7; /// denotes types and order of packed data in each Sample repeated Measurement measurements = 8; optional uint32 time_end_utc = 9; /// end of last time window if data contains any integral signals }