WP7:将Accelerometer和Compass数据转换为模拟Motion API

时间:2011-10-02 21:48:45

标签: windows-phone-7 accelerometer windows-phone-7.1 motion

我正在为Windows Phone 7.1(Mango)编写一个小样本应用程序,并希望使用Combined Motion API来显示设备的运动。我需要编写模拟类,以便在使用不支持设备所有传感器的仿真器时测试我的应用程序。

我已经编写了一个简单的模拟类来模拟指南针(它只是模拟旋转设备)和模拟器中实际可用的加速度计。

现在我必须为Motion API编写一个新的模拟对象,但我希望我可以使用罗盘和加速度计的值来计算用于Motion对象的值。不幸的是,我发现没有任何样本可用于进行简单的转换。

有人知道进行此转换的代码示例吗?如果已经有了解决方案,那么我自己也不愿意这样做。

2 个答案:

答案 0 :(得分:1)

进行Mockup工作的一个很好的起点是查看Motion API及其内部工作原理以及API核心使用的参数:

http://msdn.microsoft.com/en-us/library/hh202984%28VS.92%29.aspx

在继续之前,请记住Motion API是复杂的数学模型,它结合了不同手机传感器的输入。您可以通过组合加速度,位置和旋转来找到许多不同的模型来计算运动。您可以在本文中找到一个很好的描述:

http://www.instructables.com/id/Accelerometer-Gyro-Tutorial/

所以实际上,你必须使用上面文章中显示的方程式和函数,然后自己计算这些值。

除了简单的事情之外,这是其他所有事情,但可能就是这样。

我希望我能够帮助你:)让社区知道,如果你已经完成了。我认为一个codeplex项目很适合为windows phone motion API编写一种模拟实用程序。

答案 1 :(得分:1)

我现在再次遇到同样的问题,因为Windows Phone 8已经用完了,我还没有手机可以测试。

WP7 Mock Microsoft.Devices.Sensors.Compass when using the emulator的答案非常相似,我创建了a wrapper class with the same methods as the Motion API。当支持实际的Motion API时,会使用它。否则,在调试模式下,将返回模拟数据,这会改变滚动,俯仰和偏航。


<强> MotionWrapper

    /// <summary>
    /// Provides Windows Phone applications information about the device’s orientation and motion.
    /// </summary>
    public class MotionWrapper //: SensorBase<MotionReading> // No public constructors, nice one.
    {
        private Motion motion;

        public event EventHandler<SensorReadingEventArgs<MockMotionReading>> CurrentValueChanged;

        #region Properties
        /// <summary>
        /// Gets or sets the preferred time between Microsoft.Devices.Sensors.SensorBase<TSensorReading>.CurrentValueChanged events.
        /// </summary>
        public virtual TimeSpan TimeBetweenUpdates
        {
            get
            {
                return motion.TimeBetweenUpdates;
            }
            set
            {
                motion.TimeBetweenUpdates = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the device on which the application is running supports the sensors required by the Microsoft.Devices.Sensors.Motion class.
        /// </summary>
        public static bool IsSupported
        {
            get
            {
#if(DEBUG)
                return true;
#else
                return Motion.IsSupported;
#endif

            }
        }
        #endregion

        #region Constructors
        protected MotionWrapper()
        {
        }

        protected MotionWrapper(Motion motion)
        {
            this.motion = motion;
            this.motion.CurrentValueChanged += motion_CurrentValueChanged;
        }
        #endregion

        /// <summary>
        /// Get an instance of the MotionWrappper that supports the Motion API
        /// </summary>
        /// <returns></returns>
        public static MotionWrapper Instance()
        {
#if(DEBUG)
            if (!Motion.IsSupported)
            {
                return new MockMotionWrapper();
            }
#endif
            return new MotionWrapper(new Motion());
        }

        /// <summary>
        /// The value from the underlying Motion API has changed. Relay it on within a MockMotionReading.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
        {
            var f = new SensorReadingEventArgs<MockMotionReading>();
            f.SensorReading = new MockMotionReading(e.SensorReading);
   RaiseValueChangedEvent(sender, f);
        }

        protected void RaiseValueChangedEvent(object sender, SensorReadingEventArgs<MockMotionReading> e)
        {
            if (CurrentValueChanged != null)
            {
                CurrentValueChanged(this, e);
            }
        }

        /// <summary>
        /// Starts acquisition of data from the sensor.
        /// </summary>
        public virtual void Start()
        {
            motion.Start();
        }

        /// <summary>
        /// Stops acquisition of data from the sensor.
        /// </summary>
        public virtual void Stop()
        {
            motion.Stop();
        }
    }

MockMotionWrapper

    /// <summary>
    /// Provides Windows Phone applications mock information about the device’s orientation and motion.
    /// </summary>
    public class MockMotionWrapper : MotionWrapper
    {
        /// <summary>
        /// Use a timer to trigger simulated data updates.
        /// </summary>
        private DispatcherTimer timer;

        private MockMotionReading lastCompassReading = new MockMotionReading(true);

        #region Properties
        /// <summary>
        /// Gets or sets the preferred time between Microsoft.Devices.Sensors.SensorBase<TSensorReading>.CurrentValueChanged events.
        /// </summary>
        public override TimeSpan TimeBetweenUpdates
        {
            get
            {
                return timer.Interval;
            }
            set
            {
                timer.Interval = value;
            }
        }
        #endregion

        #region Constructors
        public MockMotionWrapper()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(30);
            timer.Tick += new EventHandler(timer_Tick);
        }
        #endregion

        void timer_Tick(object sender, EventArgs e)
        {
            var reading = new Microsoft.Devices.Sensors.SensorReadingEventArgs<MockMotionReading>();
            lastCompassReading = new MockMotionReading(lastCompassReading);
            reading.SensorReading = lastCompassReading;

            //if (lastCompassReading.HeadingAccuracy > 20)
            //{
            //    RaiseValueChangedEvent(this, new CalibrationEventArgs());
            //}

            RaiseValueChangedEvent(this, reading);
        }

        /// <summary>
        /// Starts acquisition of data from the sensor.
        /// </summary>
        public override void Start()
        {
            timer.Start();
        }

        /// <summary>
        /// Stops acquisition of data from the sensor.
        /// </summary>
        public override void Stop()
        {
            timer.Stop();
        }

    }

MockMotionReading

//Microsoft.Devices.Sensors.MotionReading
/// <summary>
/// Contains information about the orientation and movement of the device.
/// </summary>
public struct MockMotionReading : Microsoft.Devices.Sensors.ISensorReading
{
    public static bool RequiresCalibration = false;

    #region Properties
    /// <summary>
    /// Gets the attitude (yaw, pitch, and roll) of the device, in radians.
    /// </summary>
    public MockAttitudeReading Attitude { get; internal set; }

    /// <summary>
    ///  Gets the linear acceleration of the device, in gravitational units.
    /// </summary>
    public Vector3 DeviceAcceleration { get; internal set; }

    /// <summary>
    /// Gets the rotational velocity of the device, in radians per second.
    /// </summary>
    public Vector3 DeviceRotationRate { get; internal set; }

    /// <summary>
    /// Gets the gravity vector associated with the Microsoft.Devices.Sensors.MotionReading.
    /// </summary>
    public Vector3 Gravity { get; internal set; }

    /// <summary>
    /// Gets a timestamp indicating the time at which the accelerometer reading was
    ///     taken. This can be used to correlate readings across sensors and provide
    ///     additional input to algorithms that process raw sensor data.
    /// </summary>
    public DateTimeOffset Timestamp { get; internal set; }
    #endregion

    #region Constructors

    /// <summary>
    /// Initialize an instance from an actual MotionReading
    /// </summary>
    /// <param name="cr"></param>
    public MockMotionReading(MotionReading cr)
        : this()
    {
        this.Attitude = new MockAttitudeReading(cr.Attitude);
        this.DeviceAcceleration = cr.DeviceAcceleration;
        this.DeviceRotationRate = cr.DeviceRotationRate;
        this.Gravity = cr.Gravity;
        this.Timestamp = cr.Timestamp;
    }

    /// <summary>
    /// Create an instance initialized with testing data
    /// </summary>
    /// <param name="test"></param>
    public MockMotionReading(bool test) 
        : this()
    {
        float pitch = 0.01f;
        float roll = 0.02f;
        float yaw = 0.03f;

        this.Attitude = new MockAttitudeReading()
        {
            Pitch = pitch,
            Roll = roll,
            Yaw = yaw,
            RotationMatrix = Matrix.CreateFromYawPitchRoll(yaw, pitch, roll),  
            Quaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll), 

            Timestamp = DateTimeOffset.Now
        };

        // TODO: pull data from the Accelerometer
        this.Gravity = new Vector3(0, 0, 1f);
    }

    /// <summary>
    /// Create a new mock instance based on the previous mock instance
    /// </summary>
    /// <param name="lastCompassReading"></param>
    public MockMotionReading(MockMotionReading lastCompassReading)
        : this()
    {
        // Adjust the pitch, roll, and yaw as required.

        // -90 to 90 deg
        float pitchDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Pitch) - 0.5f;
        //pitchDegrees = ((pitchDegrees + 90) % 180) - 90;

        // -90 to 90 deg
        float rollDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Roll);
        //rollDegrees = ((rollDegrees + 90) % 180) - 90;

        // 0 to 360 deg
        float yawDegrees = MathHelper.ToDegrees(lastCompassReading.Attitude.Yaw) + 0.5f;
        //yawDegrees = yawDegrees % 360;

        float pitch = MathHelper.ToRadians(pitchDegrees);
        float roll = MathHelper.ToRadians(rollDegrees);
        float yaw = MathHelper.ToRadians(yawDegrees);

        this.Attitude = new MockAttitudeReading()
        {
            Pitch = pitch,
            Roll = roll,
            Yaw = yaw,
            RotationMatrix = Matrix.CreateFromYawPitchRoll(yaw, pitch, roll),
            Quaternion = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll),

            Timestamp = DateTimeOffset.Now
        };

        this.DeviceAcceleration = lastCompassReading.DeviceAcceleration;
        this.DeviceRotationRate = lastCompassReading.DeviceRotationRate;
        this.Gravity = lastCompassReading.Gravity;
        Timestamp = DateTime.Now;

    }
    #endregion



}

MockAttitudeReading

public struct MockAttitudeReading : ISensorReading
{
    public MockAttitudeReading(AttitudeReading attitudeReading) : this()
    {
        Pitch = attitudeReading.Pitch;
        Quaternion = attitudeReading.Quaternion;
        Roll = attitudeReading.Roll;
        RotationMatrix = attitudeReading.RotationMatrix;
        Timestamp = attitudeReading.Timestamp;
        Yaw = attitudeReading.Yaw;
    }

    /// <summary>
    /// Gets the pitch of the attitude reading in radians.
    /// </summary>
    public float Pitch { get; set; }

    /// <summary>
    /// Gets the quaternion representation of the attitude reading.
    /// </summary>
    public Quaternion Quaternion { get; set; }

    /// <summary>
    /// Gets the roll of the attitude reading in radians.
    /// </summary>
    public float Roll { get; set; }

    /// <summary>
    /// Gets the matrix representation of the attitude reading.
    /// </summary>
    public Matrix RotationMatrix { get; set; }

    /// <summary>
    /// Gets a timestamp indicating the time at which the accelerometer reading was
    ///     taken. This can be used to correlate readings across sensors and provide
    ///     additional input to algorithms that process raw sensor data.
    /// </summary>
    public DateTimeOffset Timestamp { get; set; }

    /// <summary>
    /// Gets the yaw of the attitude reading in radians.
    /// </summary>
    public float Yaw { get; set; }
}