WP7: преобразование данных акселометра и компаса в Mock API - PullRequest
3 голосов
/ 03 октября 2011

Я пишу небольшой пример приложения для Windows Phone 7.1 (Mango) и хочу использовать API Combined Motion для отображения движения устройства.Мне нужно написать фиктивные классы, чтобы иметь возможность тестировать мое приложение при использовании эмулятора, который не поддерживает все датчики устройства.

Я уже написал простой фиктивный класс для симуляции компаса (он просто имитирует вращениеустройство) и для акселерометра, который фактически доступен в эмуляторе.

Теперь мне нужно написать новый фиктивный объект для API Motion, но я надеюсь, что смогу рассчитать значения, которые используются для объекта Motionиспользуя значения из компаса и акселерометра.К сожалению, я не нашел образца для простого преобразования, которое уже делает это.

Кто-нибудь знает пример кода, который выполняет это преобразование?Как бы это ни было сложно, я бы не хотел делать это сам, если уже есть решение.

Ответы [ 2 ]

1 голос
/ 14 декабря 2012

Я снова столкнулся с той же проблемой, когда Windows Phone 8 вышел, и у меня пока нет телефона для тестирования.

Очень похоже на ответ на WP7 Mock Microsoft.Devices.Sensors.Compass при использовании эмулятора я создал класс-оболочку с теми же методами, что и в Motion API .Когда поддерживается фактический API-интерфейс Motion, он используется.В противном случае в режиме отладки возвращаются фиктивные данные, которые изменяют крен, высоту и рыскание.


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; }
}
1 голос
/ 14 марта 2012

Хорошей отправной точкой для работы над макетом является взгляд на API Motion и его внутреннюю работу, а также на то, какие параметры используются ядром API:

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

Прежде чем продолжить, имейте в виду, что Motion API - это сложная математическая модель, которая объединяет входные сигналы различных телефонных датчиков. Вы можете найти множество различных моделей для расчета движения, комбинируя ускорение, положение и вращение. Одно хорошее описание вы можете найти в этой статье:

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

Таким образом, фактически вы должны использовать уравнения и функции, показанные в статье выше, а затем самостоятельно рассчитать значения.

Это все, кроме простой вещи, но возможно таким образом.

Я надеюсь, что смог вам помочь :) и сообщить сообществу, если вы это сделали. Я думаю, что для проекта codeplex было бы неплохо написать некие насмешливые утилиты для API Windows Phone Motion.

...