summaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/OVR_SensorImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src/OVR_SensorImpl.cpp')
-rw-r--r--LibOVR/Src/OVR_SensorImpl.cpp1165
1 files changed, 1165 insertions, 0 deletions
diff --git a/LibOVR/Src/OVR_SensorImpl.cpp b/LibOVR/Src/OVR_SensorImpl.cpp
new file mode 100644
index 0000000..91ae7e0
--- /dev/null
+++ b/LibOVR/Src/OVR_SensorImpl.cpp
@@ -0,0 +1,1165 @@
+/************************************************************************************
+
+Filename : OVR_SensorImpl.cpp
+Content : Oculus Sensor device implementation.
+Created : March 7, 2013
+Authors : Lee Cooper, Dov Katz
+
+Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+http://www.oculusvr.com/licenses/LICENSE-3.1
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+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 "OVR_SensorImpl.h"
+#include "OVR_Sensor2Impl.h"
+#include "OVR_SensorImpl_Common.h"
+#include "OVR_JSON.h"
+#include "OVR_Profile.h"
+#include "Kernel/OVR_Alg.h"
+#include <time.h>
+
+// HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo.
+
+#include "Kernel/OVR_Timer.h"
+
+//extern FILE *SF_LOG_fp;
+
+namespace OVR {
+
+using namespace Alg;
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor-specific packet data structures
+
+enum {
+ Sensor_VendorId = Oculus_VendorId,
+ Sensor_Tracker_ProductId = Device_Tracker_ProductId,
+ Sensor_Tracker2_ProductId = Device_Tracker2_ProductId,
+ Sensor_KTracker_ProductId = Device_KTracker_ProductId,
+
+ Sensor_BootLoader = 0x1001,
+
+ Sensor_DefaultReportRate = 500, // Hz
+ Sensor_MaxReportRate = 1000 // Hz
+};
+
+
+// Messages we care for
+enum TrackerMessageType
+{
+ TrackerMessage_None = 0,
+ TrackerMessage_Sensors = 1,
+ TrackerMessage_Unknown = 0x100,
+ TrackerMessage_SizeError = 0x101,
+};
+
+
+struct TrackerSensors
+{
+ UByte SampleCount;
+ UInt16 Timestamp;
+ UInt16 LastCommandID;
+ SInt16 Temperature;
+
+ TrackerSample Samples[3];
+
+ SInt16 MagX, MagY, MagZ;
+
+ TrackerMessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 62)
+ return TrackerMessage_SizeError;
+
+ SampleCount = buffer[1];
+ Timestamp = DecodeUInt16(buffer + 2);
+ LastCommandID = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+
+ //if (SampleCount > 2)
+ // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount));
+
+ // Only unpack as many samples as there actually are
+ int iterationCount = (SampleCount > 2) ? 3 : SampleCount;
+
+ for (int i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 56);
+ MagY = DecodeSInt16(buffer + 58);
+ MagZ = DecodeSInt16(buffer + 60);
+
+ return TrackerMessage_Sensors;
+ }
+};
+
+struct TrackerMessage
+{
+ TrackerMessageType Type;
+ TrackerSensors Sensors;
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDisplayInfoImpl
+SensorDisplayInfoImpl::SensorDisplayInfoImpl()
+ : CommandId(0), DistortionType(Base_None)
+{
+ memset(Buffer, 0, PacketSize);
+ Buffer[0] = 9;
+}
+
+void SensorDisplayInfoImpl::Unpack()
+{
+ CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8);
+ DistortionType = Buffer[3];
+ HResolution = DecodeUInt16(Buffer+4);
+ VResolution = DecodeUInt16(Buffer+6);
+ HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f);
+ VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f);
+ VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f);
+ LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f);
+
+#if 0
+ // These are not well-measured on most devices - probably best to ignore them.
+ OutsideLensSurfaceToScreen[0] = DecodeUInt32(Buffer+24) * (1/1000000.f);
+ OutsideLensSurfaceToScreen[1] = DecodeUInt32(Buffer+28) * (1/1000000.f);
+ // TODO: add spline-based distortion.
+ // TODO: currently these values are all zeros in the HMD itself.
+ DistortionK[0] = DecodeFloat(Buffer+32);
+ DistortionK[1] = DecodeFloat(Buffer+36);
+ DistortionK[2] = DecodeFloat(Buffer+40);
+ DistortionK[3] = DecodeFloat(Buffer+44);
+ DistortionK[4] = DecodeFloat(Buffer+48);
+ DistortionK[5] = DecodeFloat(Buffer+52);
+#else
+ // The above are either measured poorly, or don't have values at all.
+ // To remove the temptation to use them, set them to junk.
+ OutsideLensSurfaceToScreen[0] = -1.0f;
+ OutsideLensSurfaceToScreen[1] = -1.0f;
+ DistortionK[0] = -1.0f;
+ DistortionK[1] = -1.0f;
+ DistortionK[2] = -1.0f;
+ DistortionK[3] = -1.0f;
+ DistortionK[4] = -1.0f;
+ DistortionK[5] = -1.0f;
+#endif
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceFactory
+
+SensorDeviceFactory &SensorDeviceFactory::GetInstance()
+{
+ static SensorDeviceFactory instance;
+ return instance;
+}
+
+void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor)
+{
+
+ class SensorEnumerator : public HIDEnumerateVisitor
+ {
+ // Assign not supported; suppress MSVC warning.
+ void operator = (const SensorEnumerator&) { }
+
+ DeviceFactory* pFactory;
+ EnumerateVisitor& ExternalVisitor;
+ public:
+ SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor)
+ : pFactory(factory), ExternalVisitor(externalVisitor) { }
+
+ virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId)
+ {
+ return pFactory->MatchVendorProduct(vendorId, productId);
+ }
+
+ virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc)
+ {
+
+ if (desc.ProductId == Sensor_BootLoader)
+ { // If we find a sensor in boot loader mode then notify the app
+ // about the existence of the device, but don't allow the app
+ // to create or access the device
+ BootLoaderDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+ return;
+ }
+
+ SensorDeviceCreateDesc createDesc(pFactory, desc);
+ ExternalVisitor.Visit(createDesc);
+
+ // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially
+ // mismatching monitor information (in case wrong EDID is reported by splitter),
+ // or to create a new "virtualized" HMD Device.
+
+ SensorDisplayInfoImpl displayInfo;
+
+ if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+
+ // If we got display info, try to match / create HMDDevice as well
+ // so that sensor settings give preference.
+ if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt)
+ {
+ SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor);
+ }
+ }
+ }
+ };
+
+ //double start = Timer::GetProfileSeconds();
+
+ SensorEnumerator sensorEnumerator(this, visitor);
+ GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator);
+
+ //double totalSeconds = Timer::GetProfileSeconds() - start;
+}
+
+bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const
+{
+ return ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker_ProductId)) ||
+ ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker2_ProductId)) ||
+ ((vendorId == Sensor_VendorId) && (productId == Sensor_KTracker_ProductId));
+}
+
+bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc)
+{
+ if (MatchVendorProduct(desc.VendorId, desc.ProductId))
+ {
+ if (desc.ProductId == Sensor_BootLoader)
+ { // If we find a sensor in boot loader mode then notify the app
+ // about the existence of the device, but don't allow them
+ // to create or access the device
+ BootLoaderDeviceCreateDesc createDesc(this, desc);
+ pdevMgr->AddDevice_NeedsLock(createDesc);
+ return false; // return false to allow upstream boot loader factories to catch the device
+ }
+ else
+ {
+ SensorDeviceCreateDesc createDesc(this, desc);
+ return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDeviceCreateDesc
+
+DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance()
+{
+ if (HIDDesc.ProductId == Sensor_Tracker2_ProductId)
+ {
+ return new Sensor2DeviceImpl(this);
+ }
+
+ return new SensorDeviceImpl(this);
+}
+
+bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const
+{
+ if ((info->InfoClassType != Device_Sensor) &&
+ (info->InfoClassType != Device_None))
+ return false;
+
+ info->Type = Device_Sensor;
+ info->ProductName = HIDDesc.Product;
+ info->Manufacturer = HIDDesc.Manufacturer;
+ info->Version = HIDDesc.VersionNumber;
+
+ if (info->InfoClassType == Device_Sensor)
+ {
+ SensorInfo* sinfo = (SensorInfo*)info;
+ sinfo->VendorId = HIDDesc.VendorId;
+ sinfo->ProductId = HIDDesc.ProductId;
+ sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange();
+ sinfo->SerialNumber = HIDDesc.SerialNumber;
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** SensorDevice
+
+SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : OVR::HIDDeviceImpl<OVR::SensorDevice>(createDesc, 0),
+ Coordinates(SensorDevice::Coord_Sensor),
+ HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default.
+ NextKeepAliveTickSeconds(0),
+ FullTimestamp(0),
+ MaxValidRange(SensorRangeImpl::GetMaxSensorRange()),
+ magCalibrated(false)
+{
+ SequenceValid = false;
+ LastSampleCount = 0;
+ LastTimestamp = 0;
+
+ OldCommandId = 0;
+
+ PrevAbsoluteTime = 0.0;
+
+#ifdef OVR_OS_ANDROID
+ pPhoneSensors = PhoneSensors::Create();
+#endif
+}
+
+SensorDeviceImpl::~SensorDeviceImpl()
+{
+ // Check that Shutdown() was called.
+ OVR_ASSERT(!pCreateDesc->pDevice);
+}
+
+
+// Internal creation APIs.
+bool SensorDeviceImpl::Initialize(DeviceBase* parent)
+{
+ if (HIDDeviceImpl<OVR::SensorDevice>::Initialize(parent))
+ {
+ openDevice();
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&CurrentRange);
+ // Increase the magnetometer range, since the default value is not enough in practice
+ CurrentRange.MaxMagneticField = 2.5f;
+ setRange(CurrentRange);
+ }
+
+ // Read the currently configured calibration from sensor.
+ SensorFactoryCalibrationImpl sc;
+ if (GetInternalDevice()->GetFeatureReport(sc.Buffer, SensorFactoryCalibrationImpl::PacketSize))
+ {
+ sc.Unpack();
+ AccelCalibrationOffset = sc.AccelOffset;
+ GyroCalibrationOffset = sc.GyroOffset;
+ AccelCalibrationMatrix = sc.AccelMatrix;
+ GyroCalibrationMatrix = sc.GyroMatrix;
+ CalibrationTemperature = sc.Temperature;
+ }
+
+ // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default.
+ SensorDisplayInfoImpl displayInfo;
+ if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize))
+ {
+ displayInfo.Unpack();
+ Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ?
+ Coord_HMD : Coord_Sensor;
+ }
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor_DefaultReportRate);
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Load mag calibration
+ MagCalibrationReport report;
+ bool res = GetMagCalibrationReport(&report);
+ if (res && report.Version > 0)
+ {
+ magCalibration = report.Calibration;
+ magCalibrated = true;
+ }
+}
+
+void SensorDeviceImpl::closeDeviceOnError()
+{
+ LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr());
+ NextKeepAliveTickSeconds = 0;
+}
+
+void SensorDeviceImpl::Shutdown()
+{
+ HIDDeviceImpl<OVR::SensorDevice>::Shutdown();
+
+ LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr());
+}
+
+void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+
+ bool processed = false;
+ if (!processed)
+ {
+ TrackerMessage message;
+ if (decodeTrackerMessage(&message, pData, length))
+ {
+ processed = true;
+ onTrackerMessage(&message);
+ }
+ }
+}
+
+double SensorDeviceImpl::OnTicks(double tickSeconds)
+{
+ if (tickSeconds >= NextKeepAliveTickSeconds)
+ {
+ // Use 3-seconds keep alive by default.
+ double keepAliveDelta = 3.0;
+
+ // Set Keep-alive at 10 seconds.
+ SensorKeepAliveImpl skeepAlive(10 * 1000);
+ // OnTicks is called from background thread so we don't need to add this to the command queue.
+ GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta;
+ }
+ return NextKeepAliveTickSeconds - tickSeconds;
+}
+
+bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag)
+{
+ bool result = 0;
+ ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue();
+
+ if (!waitFlag)
+ {
+ return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range);
+ }
+
+ if (!threadQueue->PushCallAndWaitResult(this,
+ &SensorDeviceImpl::setRange,
+ &result,
+ range))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+void SensorDeviceImpl::GetRange(SensorRange* range) const
+{
+ Lock::Locker lockScope(GetLock());
+ *range = CurrentRange;
+}
+
+bool SensorDeviceImpl::setRange(const SensorRange& range)
+{
+ SensorRangeImpl sr(range);
+
+ if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ Lock::Locker lockScope(GetLock());
+ sr.GetSensorRange(&CurrentRange);
+ return true;
+ }
+
+ return false;
+}
+
+void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true);
+}
+
+SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const
+{
+ return Coordinates;
+}
+
+Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe)
+{
+
+ Coordinates = coordframe;
+
+ // Read the original coordinate frame, then try to change it.
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ scfg.SetSensorCoordinates(coordframe == Coord_Sensor);
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+
+ // Re-read the state, in case of older firmware that doesn't support Sensor coordinates.
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD;
+ }
+ else
+ {
+ HWCoordinates = Coord_HMD;
+ }
+ return 0;
+}
+
+void SensorDeviceImpl::SetReportRate(unsigned rateHz)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true);
+}
+
+unsigned SensorDeviceImpl::GetReportRate() const
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ return Sensor_MaxReportRate / (scfg.PacketInterval + 1);
+ }
+ return 0; // error
+}
+
+Void SensorDeviceImpl::setReportRate(unsigned rateHz)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (rateHz > Sensor_MaxReportRate)
+ rateHz = Sensor_MaxReportRate;
+ else if (rateHz == 0)
+ rateHz = Sensor_DefaultReportRate;
+
+ scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset,
+ Matrix4f* AccelMatrix, Matrix4f* GyroMatrix,
+ float* Temperature)
+{
+ *AccelOffset = AccelCalibrationOffset;
+ *GyroOffset = GyroCalibrationOffset;
+ *AccelMatrix = AccelCalibrationMatrix;
+ *GyroMatrix = GyroCalibrationMatrix;
+ *Temperature = CalibrationTemperature;
+}
+
+bool SensorDeviceImpl::IsMagCalibrated()
+{
+ return magCalibrated;
+}
+
+void SensorDeviceImpl::SetOnboardCalibrationEnabled(bool enabled)
+{
+ // Push call with wait.
+ GetManagerImpl()->GetThreadQueue()->
+ PushCall(this, &SensorDeviceImpl::setOnboardCalibrationEnabled, enabled, true);
+}
+
+Void SensorDeviceImpl::setOnboardCalibrationEnabled(bool enabled)
+{
+ // Read the original configuration
+ SensorConfigImpl scfg;
+ if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize))
+ {
+ scfg.Unpack();
+ }
+
+ if (enabled)
+ scfg.Flags |= (SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration);
+ else
+ scfg.Flags &= ~(SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration);
+
+ scfg.Pack();
+
+ GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize);
+ return 0;
+}
+
+void SensorDeviceImpl::AddMessageHandler(MessageHandler* handler)
+{
+ if (handler)
+ SequenceValid = false;
+ DeviceBase::AddMessageHandler(handler);
+}
+
+// Sensor reports data in the following coordinate system:
+// Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down.
+// Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right.
+
+
+// We need to convert it to the following RHS coordinate system:
+// X right, Y Up, Z Back (out of screen)
+//
+Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az);
+ return val * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update,
+ Matrix4f magCalibration,
+ bool convertHMDToSensor = false)
+{
+ float mx = (float)update.MagX;
+ float my = (float)update.MagY;
+ float mz = (float)update.MagZ;
+ // Note: Y and Z are swapped in comparison to the Accel.
+ // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases.
+ Vector3f mag = convertHMDToSensor ? Vector3f(mx, my, -mz) : Vector3f(mx, mz, my);
+ mag *= 0.0001f;
+ // Apply calibration
+ return magCalibration.Transform(mag);
+}
+
+Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber,
+ bool convertHMDToSensor = false)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz);
+ return val * 0.0001f;
+}
+
+bool SensorDeviceImpl::decodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(TrackerMessage));
+
+ if (size < 4)
+ {
+ message->Type = TrackerMessage_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case TrackerMessage_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = TrackerMessage_Unknown;
+ break;
+ }
+
+ return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None);
+}
+
+void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message)
+{
+ if (message->Type != TrackerMessage_Sensors)
+ return;
+
+ const double timeUnit = (1.0 / 1000.0);
+ double scaledTimeUnit = timeUnit;
+ TrackerSensors& s = message->Sensors;
+ // DK1 timestamps the first sample, so the actual device time will be later
+ // by the time we get the message if there are multiple samples.
+ int timestampAdjust = (s.SampleCount > 0) ? s.SampleCount-1 : 0;
+
+ const double now = Timer::GetSeconds();
+ double absoluteTimeSeconds = 0.0;
+
+
+ if (SequenceValid)
+ {
+ unsigned timestampDelta;
+
+ if (s.Timestamp < LastTimestamp)
+ {
+ // The timestamp rolled around the 16 bit counter, so FullTimeStamp
+ // needs a high word increment.
+ FullTimestamp += 0x10000;
+ timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp);
+ }
+ else
+ {
+ timestampDelta = (s.Timestamp - LastTimestamp);
+ }
+ // Update the low word of FullTimeStamp
+ FullTimestamp = ( FullTimestamp & ~0xffff ) | s.Timestamp;
+
+ double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit;
+ absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime);
+ scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit);
+ PrevAbsoluteTime = absoluteTimeSeconds;
+
+ // If we missed a small number of samples, generate the sample that would have immediately
+ // proceeded the current one. Re-use the IMU values from the last processed sample.
+ if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254))
+ {
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.SampleCount * scaledTimeUnit;
+ sensors.TimeDelta = (float)((timestampDelta - LastSampleCount) * scaledTimeUnit);
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ HandlerRef.Call(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+
+ // This is our baseline sensor to host time delta,
+ // it will be adjusted with each new message.
+ FullTimestamp = s.Timestamp;
+
+ double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit;
+ absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime);
+ scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit);
+ PrevAbsoluteTime = absoluteTimeSeconds;
+ }
+
+ LastSampleCount = s.SampleCount;
+ LastTimestamp = s.Timestamp;
+
+ bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD);
+
+#ifdef OVR_OS_ANDROID
+ // LDC - Normally we get the coordinate system from the tracker.
+ // Since KTracker doesn't store it we'll always assume HMD coordinate system.
+ convertHMDToSensor = false;
+#endif
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.SampleCount;
+
+ if (s.SampleCount > 3)
+ {
+ iterations = 3;
+ sensors.TimeDelta = (float)((s.SampleCount - 2) * scaledTimeUnit);
+ }
+ else
+ {
+ sensors.TimeDelta = (float)scaledTimeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledTimeUnit;
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ sensors.MagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor);
+
+#ifdef OVR_OS_ANDROID
+ replaceWithPhoneMag(&(sensors.MagneticField));
+#endif
+ sensors.Temperature = s.Temperature * 0.01f;
+ HandlerRef.Call(sensors);
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = (float)scaledTimeUnit;
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+ }
+ else
+ {
+ UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1);
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor);
+ LastMagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor);
+
+#ifdef OVR_OS_ANDROID
+ replaceWithPhoneMag(&LastMagneticField);
+#endif
+ LastTemperature = s.Temperature * 0.01f;
+ }
+}
+
+
+#ifdef OVR_OS_ANDROID
+
+void SensorDeviceImpl::replaceWithPhoneMag(Vector3f* val)
+{
+
+ // Native calibrated.
+ pPhoneSensors->SetMagSource(PhoneSensors::MagnetometerSource_Native);
+
+ Vector3f magPhone;
+ pPhoneSensors->GetLatestMagValue(&magPhone);
+
+ // Phone value is in micro-Tesla. Convert it to Gauss and flip axes.
+ magPhone *= 10000.0f/1000000.0f;
+
+ Vector3f res;
+ res.x = -magPhone.y;
+ res.y = magPhone.x;
+ res.z = magPhone.z;
+
+ *val = res;
+}
+#endif
+
+const int MAX_DEVICE_PROFILE_MAJOR_VERSION = 1;
+
+// Writes the current calibration for a particular device to a device profile file
+bool SensorDeviceImpl::SetMagCalibrationReport(const MagCalibrationReport &data)
+{
+ // Get device info
+ SensorInfo sinfo;
+ GetDeviceInfo(&sinfo);
+
+ // A named calibration may be specified for calibration in different
+ // environments, otherwise the default calibration is used
+ const char* calibrationName = "default";
+
+ // Generate a mag calibration event
+ JSON* calibration = JSON::CreateObject();
+ // (hardcoded for now) the measurement and representation method
+ calibration->AddStringItem("Version", "2.0");
+ calibration->AddStringItem("Name", "default");
+
+ // time stamp the calibration
+ char time_str[64];
+
+#ifdef OVR_OS_WIN32
+ struct tm caltime;
+ time_t now = time(0);
+ localtime_s(&caltime, &now);
+ strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", &caltime);
+#else
+ struct tm* caltime;
+ time_t now = time(0);
+ caltime = localtime(&now);
+ strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", caltime);
+#endif
+
+ calibration->AddStringItem("Time", time_str);
+
+ // write the full calibration matrix
+ char matrix[256];
+ data.Calibration.ToString(matrix, 256);
+ calibration->AddStringItem("CalibrationMatrix", matrix);
+ // save just the offset, for backwards compatibility
+ // this can be removed when we don't want to support 0.2.4 anymore
+ Vector3f center(data.Calibration.M[0][3], data.Calibration.M[1][3], data.Calibration.M[2][3]);
+ Matrix4f tmp = data.Calibration; tmp.M[0][3] = tmp.M[1][3] = tmp.M[2][3] = 0; tmp.M[3][3] = 1;
+ center = tmp.Inverted().Transform(center);
+ Matrix4f oldcalmat; oldcalmat.M[0][3] = center.x; oldcalmat.M[1][3] = center.y; oldcalmat.M[2][3] = center.z;
+ oldcalmat.ToString(matrix, 256);
+ calibration->AddStringItem("Calibration", matrix);
+
+ String path = GetBaseOVRPath(true);
+ path += "/Devices.json";
+
+ // Look for a preexisting device file to edit
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root)
+ { // Quick sanity check of the file type and format before we parse it
+ JSON* version = root->GetFirstItem();
+ if (version && version->Name == "Oculus Device Profile Version")
+ {
+ int major = atoi(version->Value.ToCStr());
+ if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
+ {
+ // don't use the file on unsupported major version number
+ root->Release();
+ root = NULL;
+ }
+ }
+ else
+ {
+ root->Release();
+ root = NULL;
+ }
+ }
+
+ JSON* device = NULL;
+ if (root)
+ {
+ device = root->GetFirstItem(); // skip the header
+ device = root->GetNextItem(device);
+ while (device)
+ { // Search for a previous calibration with the same name for this device
+ // and remove it before adding the new one
+ if (device->Name == "Device")
+ {
+ JSON* item = device->GetItemByName("Serial");
+ if (item && item->Value == sinfo.SerialNumber)
+ { // found an entry for this device
+ item = device->GetNextItem(item);
+ while (item)
+ {
+ if (item->Name == "MagCalibration")
+ {
+ JSON* name = item->GetItemByName("Name");
+ if (name && name->Value == calibrationName)
+ { // found a calibration of the same name
+ item->RemoveNode();
+ item->Release();
+ break;
+ }
+ }
+ item = device->GetNextItem(item);
+ }
+
+
+ /*
+ this is removed temporarily, since this is a sensor fusion setting, not sensor itself
+ should be moved to the correct place when Brant has finished the user profile implementation
+ // update the auto-mag flag
+ item = device->GetItemByName("EnableYawCorrection");
+ if (item)
+ item->dValue = (double)EnableYawCorrection;
+ else
+ device->AddBoolItem("EnableYawCorrection", EnableYawCorrection);*/
+
+ break;
+ }
+ }
+
+ device = root->GetNextItem(device);
+ }
+ }
+ else
+ { // Create a new device root
+ root = *JSON::CreateObject();
+ root->AddStringItem("Oculus Device Profile Version", "1.0");
+ }
+
+ if (device == NULL)
+ {
+ device = JSON::CreateObject();
+ device->AddStringItem("Product", sinfo.ProductName);
+ device->AddNumberItem("ProductID", sinfo.ProductId);
+ device->AddStringItem("Serial", sinfo.SerialNumber);
+ // removed temporarily, see above
+ //device->AddBoolItem("EnableYawCorrection", EnableYawCorrection);
+
+ root->AddItem("Device", device);
+ }
+
+ // Create and the add the new calibration event to the device
+ device->AddItem("MagCalibration", calibration);
+ return root->Save(path);
+}
+
+// Loads a saved calibration for the specified device from the device profile file
+bool SensorDeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data)
+{
+ data->Version = 0;
+ data->Calibration.SetIdentity();
+
+ // Get device info
+ SensorInfo sinfo;
+ GetDeviceInfo(&sinfo);
+
+ // A named calibration may be specified for calibration in different
+ // environments, otherwise the default calibration is used
+ const char* calibrationName = "default";
+
+ String path = GetBaseOVRPath(true);
+ path += "/Devices.json";
+
+ // Load the device profiles
+ Ptr<JSON> root = *JSON::Load(path);
+ if (root == NULL)
+ return false;
+
+ // Quick sanity check of the file type and format before we parse it
+ JSON* version = root->GetFirstItem();
+ if (version && version->Name == "Oculus Device Profile Version")
+ {
+ int major = atoi(version->Value.ToCStr());
+ if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION)
+ return false; // don't parse the file on unsupported major version number
+ }
+ else
+ {
+ return false;
+ }
+
+ JSON* device = root->GetNextItem(version);
+ while (device)
+ { // Search for a previous calibration with the same name for this device
+ // and remove it before adding the new one
+ if (device->Name == "Device")
+ {
+ JSON* item = device->GetItemByName("Serial");
+ if (item && item->Value == sinfo.SerialNumber)
+ { // found an entry for this device
+
+ JSON* autoyaw = device->GetItemByName("EnableYawCorrection");
+ // as a temporary HACK, return no calibration if EnableYawCorrection is off
+ // this will force disable yaw correction in SensorFusion
+ // proper solution would load the value in the Profile, which SensorFusion can access
+ if (autoyaw && autoyaw->dValue == 0)
+ return true;
+
+ item = device->GetNextItem(item);
+ while (item)
+ {
+ if (item->Name == "MagCalibration")
+ {
+ JSON* calibration = item;
+ JSON* name = calibration->GetItemByName("Name");
+ if (name && name->Value == calibrationName)
+ { // found a calibration with this name
+
+ int major = 0;
+ JSON* version = calibration->GetItemByName("Version");
+ if (version)
+ major = atoi(version->Value.ToCStr());
+
+ if (major > data->Version && major <= 2)
+ {
+ time_t now;
+ time(&now);
+
+ // parse the calibration time
+ //time_t calibration_time = now;
+ JSON* caltime = calibration->GetItemByName("Time");
+ if (caltime)
+ {
+ const char* caltime_str = caltime->Value.ToCStr();
+
+ tm ct;
+ memset(&ct, 0, sizeof(tm));
+
+#ifdef OVR_OS_WIN32
+ struct tm nowtime;
+ localtime_s(&nowtime, &now);
+ ct.tm_isdst = nowtime.tm_isdst;
+ sscanf_s(caltime_str, "%d-%d-%d %d:%d:%d",
+ &ct.tm_year, &ct.tm_mon, &ct.tm_mday,
+ &ct.tm_hour, &ct.tm_min, &ct.tm_sec);
+#else
+ struct tm* nowtime = localtime(&now);
+ ct.tm_isdst = nowtime->tm_isdst;
+ sscanf(caltime_str, "%d-%d-%d %d:%d:%d",
+ &ct.tm_year, &ct.tm_mon, &ct.tm_mday,
+ &ct.tm_hour, &ct.tm_min, &ct.tm_sec);
+#endif
+ ct.tm_year -= 1900;
+ ct.tm_mon--;
+ //calibration_time = mktime(&ct);
+ }
+
+ // parse the calibration matrix
+ JSON* cal = calibration->GetItemByName("CalibrationMatrix");
+ if (!cal)
+ cal = calibration->GetItemByName("Calibration");
+ if (cal)
+ {
+ data->Calibration = Matrix4f::FromString(cal->Value.ToCStr());
+ data->Version = (UByte)major;
+ }
+ }
+ }
+ }
+ item = device->GetNextItem(item);
+ }
+
+ return true;
+ }
+ }
+
+ device = root->GetNextItem(device);
+ }
+
+ return true;
+}
+
+
+bool SensorDeviceImpl::SetSerialReport(const SerialReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setSerialReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool SensorDeviceImpl::setSerialReport(const SerialReport& data)
+{
+ SerialImpl di(data);
+ return GetInternalDevice()->SetFeatureReport(di.Buffer, SerialImpl::PacketSize);
+}
+
+bool SensorDeviceImpl::GetSerialReport(SerialReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getSerialReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool SensorDeviceImpl::getSerialReport(SerialReport* data)
+{
+ SerialImpl di;
+ if (GetInternalDevice()->GetFeatureReport(di.Buffer, SerialImpl::PacketSize))
+ {
+ di.Unpack();
+ *data = di.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace OVR