aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVR/Src/OVR_Sensor2Impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'LibOVR/Src/OVR_Sensor2Impl.cpp')
-rw-r--r--LibOVR/Src/OVR_Sensor2Impl.cpp1128
1 files changed, 1128 insertions, 0 deletions
diff --git a/LibOVR/Src/OVR_Sensor2Impl.cpp b/LibOVR/Src/OVR_Sensor2Impl.cpp
new file mode 100644
index 0000000..fa5d6e9
--- /dev/null
+++ b/LibOVR/Src/OVR_Sensor2Impl.cpp
@@ -0,0 +1,1128 @@
+/************************************************************************************
+
+Filename : OVR_Sensor2Impl.cpp
+Content : DK2 sensor device specific implementation.
+Created : January 21, 2013
+Authors : Lee Cooper
+
+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_Sensor2Impl.h"
+#include "OVR_SensorImpl_Common.h"
+#include "OVR_Sensor2ImplUtil.h"
+#include "Kernel/OVR_Alg.h"
+
+//extern FILE *SF_LOG_fp;
+
+namespace OVR {
+
+//-------------------------------------------------------------------------------------
+// ***** Oculus Sensor2-specific packet data structures
+
+enum {
+ Sensor2_VendorId = Oculus_VendorId,
+ Sensor2_ProductId = 0x0021,
+
+ Sensor2_BootLoader = 0x1001,
+
+ Sensor2_DefaultReportRate = 1000, // Hz
+};
+
+
+// Messages we care for
+enum Tracker2MessageType
+{
+ Tracker2Message_None = 0,
+ Tracker2Message_Sensors = 11,
+ Tracker2Message_Unknown = 0x100,
+ Tracker2Message_SizeError = 0x101,
+};
+
+
+struct Tracker2Sensors
+{
+ UInt16 LastCommandID;
+ UByte NumSamples;
+ UInt16 RunningSampleCount; // Named 'SampleCount' in the firmware docs.
+ SInt16 Temperature;
+ UInt32 SampleTimestamp;
+ TrackerSample Samples[2];
+ SInt16 MagX, MagY, MagZ;
+ UInt16 FrameCount;
+ UInt32 FrameTimestamp;
+ UByte FrameID;
+ UByte CameraPattern;
+ UInt16 CameraFrameCount; // Named 'CameraCount' in the firmware docs.
+ UInt32 CameraTimestamp;
+
+ Tracker2MessageType Decode(const UByte* buffer, int size)
+ {
+ if (size < 64)
+ return Tracker2Message_SizeError;
+
+ LastCommandID = DecodeUInt16(buffer + 1);
+ NumSamples = buffer[3];
+ RunningSampleCount = DecodeUInt16(buffer + 4);
+ Temperature = DecodeSInt16(buffer + 6);
+ SampleTimestamp = DecodeUInt32(buffer + 8);
+
+ // Only unpack as many samples as there actually are.
+ UByte iterationCount = (NumSamples > 1) ? 2 : NumSamples;
+
+ for (UByte i = 0; i < iterationCount; i++)
+ {
+ UnpackSensor(buffer + 12 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ);
+ UnpackSensor(buffer + 20 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ);
+ }
+
+ MagX = DecodeSInt16(buffer + 44);
+ MagY = DecodeSInt16(buffer + 46);
+ MagZ = DecodeSInt16(buffer + 48);
+
+ FrameCount = DecodeUInt16(buffer + 50);
+
+ FrameTimestamp = DecodeUInt32(buffer + 52);
+ FrameID = buffer[56];
+ CameraPattern = buffer[57];
+ CameraFrameCount = DecodeUInt16(buffer + 58);
+ CameraTimestamp = DecodeUInt32(buffer + 60);
+
+ return Tracker2Message_Sensors;
+ }
+};
+
+struct Tracker2Message
+{
+ Tracker2MessageType Type;
+ Tracker2Sensors Sensors;
+};
+
+// 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 Tracker2Sensors& update, UByte sampleNumber)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float ax = (float)sample.AccelX;
+ float ay = (float)sample.AccelY;
+ float az = (float)sample.AccelZ;
+
+ return Vector3f(ax, ay, az) * 0.0001f;
+}
+
+
+Vector3f MagFromBodyFrameUpdate(const Tracker2Sensors& update)
+{
+ return Vector3f( (float)update.MagX, (float)update.MagY, (float)update.MagZ) * 0.0001f;
+}
+
+Vector3f EulerFromBodyFrameUpdate(const Tracker2Sensors& update, UByte sampleNumber)
+{
+ const TrackerSample& sample = update.Samples[sampleNumber];
+ float gx = (float)sample.GyroX;
+ float gy = (float)sample.GyroY;
+ float gz = (float)sample.GyroZ;
+
+ return Vector3f(gx, gy, gz) * 0.0001f;
+}
+
+bool Sensor2DeviceImpl::decodeTracker2Message(Tracker2Message* message, UByte* buffer, int size)
+{
+ memset(message, 0, sizeof(Tracker2Message));
+
+ if (size < 4)
+ {
+ message->Type = Tracker2Message_SizeError;
+ return false;
+ }
+
+ switch (buffer[0])
+ {
+ case Tracker2Message_Sensors:
+ message->Type = message->Sensors.Decode(buffer, size);
+ break;
+
+ default:
+ message->Type = Tracker2Message_Unknown;
+ break;
+ }
+
+ return (message->Type < Tracker2Message_Unknown) && (message->Type != Tracker2Message_None);
+}
+
+//-------------------------------------------------------------------------------------
+// ***** Sensor2Device
+
+Sensor2DeviceImpl::Sensor2DeviceImpl(SensorDeviceCreateDesc* createDesc)
+ : SensorDeviceImpl(createDesc),
+ LastNumSamples(0),
+ LastRunningSampleCount(0),
+ FullCameraFrameCount(0),
+ LastCameraTime("C"),
+ LastFrameTime("F"),
+ LastSensorTime("S"),
+ LastFrameTimestamp(0)
+{
+ // 15 samples ok in min-window for DK2 since it uses microsecond clock.
+ TimeFilter = SensorTimeFilter(SensorTimeFilter::Settings(15));
+
+ pCalibration = new SensorCalibration(this);
+}
+
+Sensor2DeviceImpl::~Sensor2DeviceImpl()
+{
+ delete pCalibration;
+}
+
+void Sensor2DeviceImpl::openDevice()
+{
+
+ // Read the currently configured range from sensor.
+ SensorRangeImpl sr(SensorRange(), 0);
+
+ if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize))
+ {
+ sr.Unpack();
+ sr.GetSensorRange(&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;
+ }
+ Coordinates = Coord_HMD; // TODO temporary to force it behave
+
+ // Read/Apply sensor config.
+ setCoordinateFrame(Coordinates);
+ setReportRate(Sensor2_DefaultReportRate);
+ setOnboardCalibrationEnabled(false);
+
+ // Must send DK2 keep-alive. Set Keep-alive at 10 seconds.
+ KeepAliveMuxReport keepAlive;
+ keepAlive.CommandId = 0;
+ keepAlive.INReport = 11;
+ keepAlive.Interval = 10 * 1000;
+
+ // Device creation is done from background thread so we don't need to add this to the command queue.
+ KeepAliveMuxImpl keepAliveImpl(keepAlive);
+ GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize);
+
+ // Read the temperature data from the device
+ pCalibration->Initialize();
+}
+
+bool Sensor2DeviceImpl::SetTrackingReport(const TrackingReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTrackingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setTrackingReport(const TrackingReport& data)
+{
+ TrackingImpl ci(data);
+ return GetInternalDevice()->SetFeatureReport(ci.Buffer, TrackingImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetTrackingReport(TrackingReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getTrackingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getTrackingReport(TrackingReport* data)
+{
+ TrackingImpl ci;
+ if (GetInternalDevice()->GetFeatureReport(ci.Buffer, TrackingImpl::PacketSize))
+ {
+ ci.Unpack();
+ *data = ci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetDisplayReport(const DisplayReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setDisplayReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setDisplayReport(const DisplayReport& data)
+{
+ DisplayImpl di(data);
+ return GetInternalDevice()->SetFeatureReport(di.Buffer, DisplayImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetDisplayReport(DisplayReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getDisplayReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getDisplayReport(DisplayReport* data)
+{
+ DisplayImpl di;
+ if (GetInternalDevice()->GetFeatureReport(di.Buffer, DisplayImpl::PacketSize))
+ {
+ di.Unpack();
+ *data = di.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetMagCalibrationReport(const MagCalibrationReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setMagCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setMagCalibrationReport(const MagCalibrationReport& data)
+{
+ MagCalibrationImpl mci(data);
+ return GetInternalDevice()->SetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getMagCalibrationReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getMagCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getMagCalibrationReport(MagCalibrationReport* data)
+{
+ MagCalibrationImpl mci;
+ if (GetInternalDevice()->GetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize))
+ {
+ mci.Unpack();
+ *data = mci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetPositionCalibrationReport(const PositionCalibrationReport& data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setPositionCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setPositionCalibrationReport(const PositionCalibrationReport& data)
+{
+ UByte version = GetDeviceInterfaceVersion();
+ if (version < 5)
+ {
+ PositionCalibrationImpl_Pre5 pci(data);
+ return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize);
+ }
+
+ PositionCalibrationImpl pci(data);
+ return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetPositionCalibrationReport(PositionCalibrationReport* data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getPositionCalibrationReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getPositionCalibrationReport(PositionCalibrationReport* data)
+{
+ UByte version = GetDeviceInterfaceVersion();
+ if (version < 5)
+ {
+ PositionCalibrationImpl_Pre5 pci;
+ if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize))
+ {
+ pci.Unpack();
+ *data = pci.Settings;
+ return true;
+ }
+
+ return false;
+ }
+
+ PositionCalibrationImpl pci;
+ if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize))
+ {
+ pci.Unpack();
+ *data = pci.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::GetAllPositionCalibrationReports(Array<PositionCalibrationReport>* data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ PositionCalibrationReport pc;
+ bool result = GetPositionCalibrationReport(&pc);
+ if (!result)
+ return false;
+
+ int positions = pc.NumPositions;
+ data->Clear();
+ data->Resize(positions);
+
+ for (int i = 0; i < positions; i++)
+ {
+ result = GetPositionCalibrationReport(&pc);
+ if (!result)
+ return false;
+ OVR_ASSERT(pc.NumPositions == positions);
+
+ (*data)[pc.PositionIndex] = pc;
+ // IMU should be the last one
+ OVR_ASSERT(pc.PositionType == (pc.PositionIndex == positions - 1) ?
+ PositionCalibrationReport::PositionType_IMU : PositionCalibrationReport::PositionType_LED);
+ }
+ return true;
+}
+
+bool Sensor2DeviceImpl::SetCustomPatternReport(const CustomPatternReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setCustomPatternReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setCustomPatternReport(const CustomPatternReport& data)
+{
+ CustomPatternImpl cpi(data);
+ return GetInternalDevice()->SetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetCustomPatternReport(CustomPatternReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getCustomPatternReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getCustomPatternReport(CustomPatternReport* data)
+{
+ CustomPatternImpl cpi;
+ if (GetInternalDevice()->GetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize))
+ {
+ cpi.Unpack();
+ *data = cpi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetManufacturingReport(const ManufacturingReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setManufacturingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setManufacturingReport(const ManufacturingReport& data)
+{
+ ManufacturingImpl mi(data);
+ return GetInternalDevice()->SetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetManufacturingReport(ManufacturingReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getManufacturingReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getManufacturingReport(ManufacturingReport* data)
+{
+ ManufacturingImpl mi;
+ if (GetInternalDevice()->GetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize))
+ {
+ mi.Unpack();
+ *data = mi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetLensDistortionReport(const LensDistortionReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setLensDistortionReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setLensDistortionReport(const LensDistortionReport& data)
+{
+ LensDistortionImpl ui(data);
+ return GetInternalDevice()->SetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetLensDistortionReport(LensDistortionReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getLensDistortionReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getLensDistortionReport(LensDistortionReport* data)
+{
+ LensDistortionImpl ui;
+ if (GetInternalDevice()->GetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize))
+ {
+ ui.Unpack();
+ *data = ui.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetUUIDReport(const UUIDReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setUUIDReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setUUIDReport(const UUIDReport& data)
+{
+ UUIDImpl ui(data);
+ return GetInternalDevice()->SetFeatureReport(ui.Buffer, UUIDImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetUUIDReport(UUIDReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getUUIDReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getUUIDReport(UUIDReport* data)
+{
+ UUIDImpl ui;
+ if (GetInternalDevice()->GetFeatureReport(ui.Buffer, UUIDImpl::PacketSize))
+ {
+ ui.Unpack();
+ *data = ui.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetKeepAliveMuxReport(const KeepAliveMuxReport& data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setKeepAliveMuxReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setKeepAliveMuxReport(const KeepAliveMuxReport& data)
+{
+ KeepAliveMuxImpl kami(data);
+ return GetInternalDevice()->SetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetKeepAliveMuxReport(KeepAliveMuxReport* data)
+{
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getKeepAliveMuxReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getKeepAliveMuxReport(KeepAliveMuxReport* data)
+{
+ KeepAliveMuxImpl kami;
+ if (GetInternalDevice()->GetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize))
+ {
+ kami.Unpack();
+ *data = kami.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::SetTemperatureReport(const TemperatureReport& data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return setTemperatureReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTemperatureReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::setTemperatureReport(const TemperatureReport& data)
+{
+ TemperatureImpl ti(data);
+ return GetInternalDevice()->SetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize);
+}
+
+bool Sensor2DeviceImpl::GetTemperatureReport(TemperatureReport* data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getTemperatureReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getTemperatureReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::GetAllTemperatureReports(Array<Array<TemperatureReport> >* data)
+{
+ Lock::Locker lock(&IndexedReportLock);
+
+ TemperatureReport t;
+ bool result = GetTemperatureReport(&t);
+ if (!result)
+ return false;
+
+ int bins = t.NumBins, samples = t.NumSamples;
+ data->Clear();
+ data->Resize(bins);
+ for (int i = 0; i < bins; i++)
+ (*data)[i].Resize(samples);
+
+ for (int i = 0; i < bins; i++)
+ for (int j = 0; j < samples; j++)
+ {
+ result = GetTemperatureReport(&t);
+ if (!result)
+ return false;
+ OVR_ASSERT(t.NumBins == bins && t.NumSamples == samples);
+
+ (*data)[t.Bin][t.Sample] = t;
+ }
+ return true;
+}
+
+bool Sensor2DeviceImpl::getTemperatureReport(TemperatureReport* data)
+{
+ TemperatureImpl ti;
+ if (GetInternalDevice()->GetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize))
+ {
+ ti.Unpack();
+ *data = ti.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+bool Sensor2DeviceImpl::GetGyroOffsetReport(GyroOffsetReport* data)
+{
+ // direct call if we are already on the device manager thread
+ if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId())
+ {
+ return getGyroOffsetReport(data);
+ }
+
+ bool result;
+ if (!GetManagerImpl()->GetThreadQueue()->
+ PushCallAndWaitResult(this, &Sensor2DeviceImpl::getGyroOffsetReport, &result, data))
+ {
+ return false;
+ }
+
+ return result;
+}
+
+bool Sensor2DeviceImpl::getGyroOffsetReport(GyroOffsetReport* data)
+{
+ GyroOffsetImpl goi;
+ if (GetInternalDevice()->GetFeatureReport(goi.Buffer, GyroOffsetImpl::PacketSize))
+ {
+ goi.Unpack();
+ *data = goi.Settings;
+ return true;
+ }
+
+ return false;
+}
+
+void Sensor2DeviceImpl::onTrackerMessage(Tracker2Message* message)
+{
+ if (message->Type != Tracker2Message_Sensors)
+ return;
+
+ const float sampleIntervalTimeUnit = (1.0f / 1000.f);
+ double scaledSampleIntervalTimeUnit = sampleIntervalTimeUnit;
+ Tracker2Sensors& s = message->Sensors;
+
+ double absoluteTimeSeconds = 0.0;
+
+ if (SequenceValid)
+ {
+ UInt32 runningSampleCountDelta;
+
+ if (s.RunningSampleCount < LastRunningSampleCount)
+ {
+ // The running sample count on the device rolled around the 16 bit counter
+ // (expect to happen about once per minute), so RunningSampleCount
+ // needs a high word increment.
+ runningSampleCountDelta = ((((int)s.RunningSampleCount) + 0x10000) - (int)LastRunningSampleCount);
+ }
+ else
+ {
+ runningSampleCountDelta = (s.RunningSampleCount - LastRunningSampleCount);
+ }
+
+ absoluteTimeSeconds = LastSensorTime.TimeSeconds;
+ scaledSampleIntervalTimeUnit = TimeFilter.ScaleTimeUnit(sampleIntervalTimeUnit);
+
+ // If we missed a small number of samples, replicate the last sample.
+ if ((runningSampleCountDelta > LastNumSamples) && (runningSampleCountDelta <= 254))
+ {
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.NumSamples * scaledSampleIntervalTimeUnit;
+ sensors.TimeDelta = (float) ((runningSampleCountDelta - LastNumSamples) * scaledSampleIntervalTimeUnit);
+ sensors.Acceleration = LastAcceleration;
+ sensors.RotationRate = LastRotationRate;
+ sensors.MagneticField = LastMagneticField;
+ sensors.Temperature = LastTemperature;
+
+ pCalibration->Apply(sensors);
+ HandlerRef.Call(sensors);
+ }
+ }
+ }
+ else
+ {
+ LastAcceleration = Vector3f(0);
+ LastRotationRate = Vector3f(0);
+ LastMagneticField= Vector3f(0);
+ LastTemperature = 0;
+ SequenceValid = true;
+ }
+
+ LastNumSamples = s.NumSamples;
+ LastRunningSampleCount = s.RunningSampleCount;
+
+ if (HandlerRef.HasHandlers())
+ {
+ MessageBodyFrame sensors(this);
+ UByte iterations = s.NumSamples;
+
+ if (s.NumSamples > 2)
+ {
+ iterations = 2;
+ sensors.TimeDelta = (float) ((s.NumSamples - 1) * scaledSampleIntervalTimeUnit);
+ }
+ else
+ {
+ sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit;
+ }
+
+ for (UByte i = 0; i < iterations; i++)
+ {
+ sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledSampleIntervalTimeUnit;
+ sensors.Acceleration = AccelFromBodyFrameUpdate(s, i);
+ sensors.RotationRate = EulerFromBodyFrameUpdate(s, i);
+ sensors.MagneticField= MagFromBodyFrameUpdate(s);
+ sensors.Temperature = s.Temperature * 0.01f;
+
+ pCalibration->Apply(sensors);
+ HandlerRef.Call(sensors);
+
+ // TimeDelta for the last two sample is always fixed.
+ sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit;
+ }
+
+ // Send pixel read only when frame timestamp changes.
+ if (LastFrameTimestamp != s.FrameTimestamp)
+ {
+ MessagePixelRead pixelRead(this);
+ // Prepare message for pixel read
+ pixelRead.PixelReadValue = s.FrameID;
+ pixelRead.RawFrameTime = s.FrameTimestamp;
+ pixelRead.RawSensorTime = s.SampleTimestamp;
+ pixelRead.SensorTimeSeconds = LastSensorTime.TimeSeconds;
+ pixelRead.FrameTimeSeconds = LastFrameTime.TimeSeconds;
+
+ HandlerRef.Call(pixelRead);
+ LastFrameTimestamp = s.FrameTimestamp;
+ }
+
+ UInt16 lowFrameCount = (UInt16) FullCameraFrameCount;
+ // Send message only when frame counter changes
+ if (lowFrameCount != s.CameraFrameCount)
+ {
+ // check for the rollover in the counter
+ if (s.CameraFrameCount < lowFrameCount)
+ FullCameraFrameCount += 0x10000;
+ // update the low bits
+ FullCameraFrameCount = (FullCameraFrameCount & ~0xFFFF) | s.CameraFrameCount;
+
+ MessageExposureFrame vision(this);
+ vision.CameraPattern = s.CameraPattern;
+ vision.CameraFrameCount = FullCameraFrameCount;
+ vision.CameraTimeSeconds = LastCameraTime.TimeSeconds;
+
+ HandlerRef.Call(vision);
+ }
+
+ LastAcceleration = sensors.Acceleration;
+ LastRotationRate = sensors.RotationRate;
+ LastMagneticField= sensors.MagneticField;
+ LastTemperature = sensors.Temperature;
+
+ //LastPixelRead = pixelRead.PixelReadValue;
+ //LastPixelReadTimeStamp = LastFrameTime;
+ }
+ else
+ {
+ if (s.NumSamples != 0)
+ {
+ UByte i = (s.NumSamples > 1) ? 1 : 0;
+ LastAcceleration = AccelFromBodyFrameUpdate(s, i);
+ LastRotationRate = EulerFromBodyFrameUpdate(s, i);
+ LastMagneticField = MagFromBodyFrameUpdate(s);
+ LastTemperature = s.Temperature * 0.01f;
+ }
+ }
+}
+
+// Helper function to handle wrap-around of timestamps from Tracker2Message and convert them
+// to system time.
+// - Any timestamps that didn't increment keep their old system time.
+// - This is a bit tricky since we don't know which one of timestamps has most recent time.
+// - The first timestamp must be the IMU one; we assume that others can't be too much ahead of it
+
+void UpdateDK2Timestamps(SensorTimeFilter& tf,
+ SensorTimestampMapping** timestamps, UInt32 *rawValues, int count)
+{
+ int updateIndices[4];
+ int updateCount = 0;
+ int i;
+ double now = Timer::GetSeconds();
+
+ OVR_ASSERT(count <= sizeof(updateIndices)/sizeof(int));
+
+ // Update timestamp wrapping for any values that changed.
+ for (i = 0; i < count; i++)
+ {
+ UInt32 lowMks = (UInt32)timestamps[i]->TimestampMks; // Low 32-bits are raw old timestamp.
+
+ if (rawValues[i] != lowMks)
+ {
+ if (i == 0)
+ {
+ // Only check for rollover in the IMU timestamp
+ if (rawValues[i] < lowMks)
+ {
+ LogText("Timestamp %d rollover, was: %u, now: %u\n", i, lowMks, rawValues[i]);
+ timestamps[i]->TimestampMks += 0x100000000;
+ }
+ // Update the low bits
+ timestamps[i]->TimestampMks = (timestamps[i]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i];
+ }
+ else
+ {
+ // Take the high bits from the main timestamp first (not a typo in the first argument!)
+ timestamps[i]->TimestampMks =
+ (timestamps[0]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i];
+ // Now force it into the reasonable range around the expanded main timestamp
+ if (timestamps[i]->TimestampMks > timestamps[0]->TimestampMks + 0x1000000)
+ timestamps[i]->TimestampMks -= 0x100000000;
+ else if (timestamps[i]->TimestampMks + 0x100000000 < timestamps[0]->TimestampMks + 0x1000000)
+ timestamps[i]->TimestampMks += 0x100000000;
+ }
+
+ updateIndices[updateCount] = i;
+ updateCount++;
+ }
+ }
+
+
+ // TBD: Simplify. Update indices should no longer be needed with new TimeFilter accepting
+ // previous values.
+ // We might want to have multi-element checking time roll-over.
+
+ static const double mksToSec = 1.0 / 1000000.0;
+
+ for (int i = 0; i < updateCount; i++)
+ {
+ SensorTimestampMapping& ts = *timestamps[updateIndices[i]];
+
+ ts.TimeSeconds = tf.SampleToSystemTime(((double)ts.TimestampMks) * mksToSec,
+ now, ts.TimeSeconds, ts.DebugTag);
+ }
+}
+
+
+void Sensor2DeviceImpl::OnInputReport(UByte* pData, UInt32 length)
+{
+ bool processed = false;
+ if (!processed)
+ {
+ Tracker2Message message;
+ if (decodeTracker2Message(&message, pData, length))
+ {
+ processed = true;
+
+ // Process microsecond timestamps from DK2 tracker.
+ // Mapped and raw values must correspond to one another in each array.
+ // IMU timestamp must be the first one!
+ SensorTimestampMapping* tsMaps[3] =
+ {
+ &LastSensorTime,
+ &LastCameraTime,
+ &LastFrameTime
+ };
+ UInt32 tsRawMks[3] =
+ {
+ message.Sensors.SampleTimestamp,
+ message.Sensors.CameraTimestamp,
+ message.Sensors.FrameTimestamp
+ };
+ // Handle wrap-around and convert samples to system time for any samples that changed.
+ UpdateDK2Timestamps(TimeFilter, tsMaps, tsRawMks, sizeof(tsRawMks)/sizeof(tsRawMks[0]));
+
+ onTrackerMessage(&message);
+
+ /*
+ if (SF_LOG_fp)
+ {
+ static UInt32 lastFrameTs = 0;
+ static UInt32 lastCameraTs = 0;
+
+ if ((lastFrameTs != message.Sensors.FrameTimestamp) ||
+ (lastCameraTs = message.Sensors.CameraTimestamp))
+ fprintf(SF_LOG_fp, "msg cameraTs: 0x%X frameTs: 0x%X sensorTs: 0x%X\n",
+ message.Sensors.CameraTimestamp, message.Sensors.FrameTimestamp,
+ message.Sensors.SampleTimestamp);
+
+ lastFrameTs = message.Sensors.FrameTimestamp;
+ lastCameraTs = message.Sensors.CameraTimestamp;
+ }
+ */
+
+#if 0
+ // Checks for DK2 firmware bug.
+ static unsigned SLastSampleTime = 0;
+ if ((SLastSampleTime > message.Sensors.SampleTimestamp) && message.Sensors.SampleTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Sample Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastSampleTime <= message.Sensors.SampleTimestamp);
+ }
+ SLastSampleTime = message.Sensors.SampleTimestamp;
+
+ static unsigned SLastCameraTime = 0;
+ if ((SLastCameraTime > message.Sensors.CameraTimestamp) && message.Sensors.CameraTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Camera Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastCameraTime <= message.Sensors.CameraTimestamp);
+ }
+ SLastCameraTime = message.Sensors.CameraTimestamp;
+
+ static unsigned SLastFrameTime = 0;
+ if ((SLastFrameTime > message.Sensors.FrameTimestamp) && message.Sensors.FrameTimestamp > 1000000 )
+ {
+ fprintf(SF_LOG_fp, "*** Frame Timestamp Wrap! ***\n");
+ OVR_ASSERT (SLastFrameTime <= message.Sensors.FrameTimestamp);
+ }
+ SLastFrameTime = message.Sensors.FrameTimestamp;
+#endif
+ }
+ }
+}
+
+double Sensor2DeviceImpl::OnTicks(double tickSeconds)
+{
+
+ if (tickSeconds >= NextKeepAliveTickSeconds)
+ {
+ // Must send DK2 keep-alive. Set Keep-alive at 10 seconds.
+ KeepAliveMuxReport keepAlive;
+ keepAlive.CommandId = 0;
+ keepAlive.INReport = 11;
+ keepAlive.Interval = 10 * 1000;
+
+ // Device creation is done from background thread so we don't need to add this to the command queue.
+ KeepAliveMuxImpl keepAliveImpl(keepAlive);
+ GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize);
+
+ // Emit keep-alive every few seconds.
+ double keepAliveDelta = 3.0; // Use 3-second interval.
+ NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta;
+ }
+ return NextKeepAliveTickSeconds - tickSeconds;
+}
+
+} // namespace OVR