/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#include "sensorevent.h"
#include <inttypes.h>
#include <string.h>
#include "contexthub.h"
#include "log.h"
namespace android {
constexpr float kCompressedSampleRatio(8.0f * 9.81f / 32768.0f);
/* SensorEvent ****************************************************************/
std::unique_ptr<SensorEvent> SensorEvent::FromBytes(
const std::vector<uint8_t>& buffer) {
SensorEvent *sensor_event = nullptr;
SensorType sensor_type = static_cast<SensorType>(
ReadEventResponse::EventTypeFromBuffer(buffer) -
static_cast<uint32_t>(EventType::FirstSensorEvent));
switch (sensor_type) {
case SensorType::Accel:
case SensorType::Gyro:
case SensorType::GyroUncal:
case SensorType::Magnetometer:
case SensorType::MagnetometerUncal:
case SensorType::Orientation:
case SensorType::Gravity:
case SensorType::LinearAccel:
case SensorType::RotationVector:
case SensorType::GeomagneticRotationVector:
case SensorType::GameRotationVector:
sensor_event = new TripleAxisSensorEvent();
break;
case SensorType::Barometer:
case SensorType::Temperature:
case SensorType::AmbientLightSensor:
case SensorType::Proximity:
sensor_event = new SingleAxisSensorEvent();
break;
// TODO: Activity uses a special struct, it should have its own class
case SensorType::Activity:
case SensorType::AnyMotion:
case SensorType::NoMotion:
case SensorType::SignificantMotion:
case SensorType::Flat:
case SensorType::WindowOrientation:
case SensorType::Tilt:
case SensorType::Hall:
case SensorType::HeartRateECG: // Heart rates not implemented, guessing
case SensorType::HeartRatePPG: // data type here...
case SensorType::StepCount:
case SensorType::StepDetect:
case SensorType::Gesture:
case SensorType::DoubleTwist:
case SensorType::DoubleTap:
case SensorType::Vsync:
case SensorType::WristTilt:
sensor_event = new SingleAxisIntSensorEvent();
break;
case SensorType::CompressedAccel:
sensor_event = new CompressedTripleAxisSensorEvent();
break;
default:
LOGW("Can't create SensorEvent for unknown/invalid sensor type %d",
static_cast<int>(sensor_type));
}
if (sensor_event &&
(!sensor_event->Populate(buffer) || !sensor_event->SizeIsValid())) {
LOGW("Couldn't populate sensor event, or invalid size");
delete sensor_event;
sensor_event = nullptr;
}
return std::unique_ptr<SensorEvent>(sensor_event);
}
SensorType SensorEvent::GetSensorType() const {
return static_cast<SensorType>(
GetEventType() - static_cast<uint32_t>(EventType::FirstSensorEvent));
}
/* TimestampedSensorEvent *****************************************************/
uint8_t TimestampedSensorEvent::GetNumSamples() const {
// Perform size check, but don't depend on SizeIsValid since it will call us
if (event_data.size() < (sizeof(struct SensorEventHeader) +
sizeof(struct SensorFirstSample))) {
LOGW("Short/invalid timestamped sensor event; length %zu",
event_data.size());
return 0;
}
const struct SensorFirstSample *first_sample_header =
reinterpret_cast<const struct SensorFirstSample *>(
event_data.data() + sizeof(struct SensorEventHeader));
return first_sample_header->numSamples;
}
uint64_t TimestampedSensorEvent::GetReferenceTime() const {
if (!SizeIsValid()) {
return 0;
}
const struct SensorEventHeader *header =
reinterpret_cast<const struct SensorEventHeader *>(event_data.data());
return header->reference_time;
}
uint64_t TimestampedSensorEvent::GetSampleTime(uint8_t index) const {
const SensorSampleHeader *sample;
uint64_t sample_time = GetReferenceTime();
// For index 0, the sample time is the reference time. For each subsequent
// sample, sum the delta to the previous sample to get the sample time.
for (uint8_t i = 1; i <= index; i++) {
sample = GetSampleAtIndex(index);
sample_time += sample->delta_time;
}
return sample_time;
}
std::string TimestampedSensorEvent::GetSampleTimeStr(uint8_t index) const {
uint64_t sample_time = GetSampleTime(index);
char buffer[32];
snprintf(buffer, sizeof(buffer), "%" PRIu64 ".%06" PRIu64 " ms",
sample_time / 1000000, sample_time % 1000000);
return std::string(buffer);
}
const SensorSampleHeader *TimestampedSensorEvent::GetSampleAtIndex(
uint8_t index) const {
if (index >= GetNumSamples()) {
LOGW("Requested sample at invalid index %u", index);
return nullptr;
}
unsigned int offset = (sizeof(struct SensorEventHeader) +
index * GetSampleDataSize());
return reinterpret_cast<const struct SensorSampleHeader *>(
event_data.data() + offset);
}
std::string TimestampedSensorEvent::ToString() const {
uint8_t num_samples = GetNumSamples();
char buffer[64];
snprintf(buffer, sizeof(buffer),
"Event from sensor %d (%s) with %d sample%s\n",
static_cast<int>(GetSensorType()),
ContextHub::SensorTypeToAbbrevName(GetSensorType()).c_str(),
num_samples, (num_samples != 1) ? "s" : "");
return std::string(buffer) + StringForAllSamples();
}
bool TimestampedSensorEvent::SizeIsValid() const {
unsigned int min_size = (sizeof(struct SensorEventHeader) +
GetNumSamples() * GetSampleDataSize());
if (event_data.size() < min_size) {
LOGW("Got short sensor event with %zu bytes, expected >= %u",
event_data.size(), min_size);
return false;
}
return true;
}
std::string TimestampedSensorEvent::StringForAllSamples() const {
std::string str;
for (unsigned int i = 0; i < GetNumSamples(); i++) {
str += StringForSample(i);
}
return str;
}
/* SingleAxisSensorEvent ******************************************************/
std::string SingleAxisSensorEvent::StringForSample(uint8_t index) const {
const SingleAxisDataPoint *sample =
reinterpret_cast<const SingleAxisDataPoint *>(GetSampleAtIndex(index));
char buffer[64];
snprintf(buffer, sizeof(buffer), " %f @ %s\n",
sample->fdata, GetSampleTimeStr(index).c_str());
return std::string(buffer);
}
uint8_t SingleAxisSensorEvent::GetSampleDataSize() const {
return sizeof(struct SingleAxisDataPoint);
}
/* SingleAxisIntSensorEvent ***************************************************/
std::string SingleAxisIntSensorEvent::StringForSample(uint8_t index) const {
const SingleAxisDataPoint *sample =
reinterpret_cast<const SingleAxisDataPoint *>(GetSampleAtIndex(index));
char buffer[64];
snprintf(buffer, sizeof(buffer), " %d @ %s\n",
sample->idata, GetSampleTimeStr(index).c_str());
return std::string(buffer);
}
/* TripleAxisSensorEvent ******************************************************/
std::string TripleAxisSensorEvent::StringForSample(uint8_t index) const {
const TripleAxisDataPoint *sample =
reinterpret_cast<const TripleAxisDataPoint *>(
GetSampleAtIndex(index));
const struct SensorFirstSample *first_sample =
reinterpret_cast<const struct SensorFirstSample *>(
event_data.data() + sizeof(struct SensorEventHeader));
bool is_bias_sample = first_sample->biasPresent
&& first_sample->biasSample == index;
char buffer[128];
snprintf(buffer, sizeof(buffer), " X:%f Y:%f Z:%f @ %s%s\n",
sample->x, sample->y, sample->z, GetSampleTimeStr(index).c_str(),
is_bias_sample ? " (Bias Sample)" : "");
return std::string(buffer);
}
uint8_t TripleAxisSensorEvent::GetSampleDataSize() const {
return sizeof(struct TripleAxisDataPoint);
}
/* CompressedTripleAxisSensorEvent ********************************************/
std::string CompressedTripleAxisSensorEvent::StringForSample(
uint8_t index) const {
const CompressedTripleAxisDataPoint *sample =
reinterpret_cast<const CompressedTripleAxisDataPoint *>(
GetSampleAtIndex(index));
const struct SensorFirstSample *first_sample =
reinterpret_cast<const struct SensorFirstSample *>(
event_data.data() + sizeof(struct SensorEventHeader));
bool is_bias_sample = first_sample->biasPresent
&& first_sample->biasSample == index;
float x = sample->ix * kCompressedSampleRatio;
float y = sample->iy * kCompressedSampleRatio;
float z = sample->iz * kCompressedSampleRatio;
char buffer[128];
snprintf(buffer, sizeof(buffer), " X:%f Y:%f Z:%f @ %s%s\n",
x, y, z, GetSampleTimeStr(index).c_str(),
is_bias_sample ? " (Bias Sample)" : "");
return std::string(buffer);
}
uint8_t CompressedTripleAxisSensorEvent::GetSampleDataSize() const {
return sizeof(CompressedTripleAxisDataPoint);
}
} // namespace android