Windows Phone – Accelerometer, kiirendusandur
26 April 2012
Windows Phone’i on sisse ehitatud kolmesuunaline kiirendusandur. Alljärgnevas rakenduses kasutatakse punase palli liigutamiseks kahte dimensiooni, kuid värvilised jooned ekraanil iseloomustavad telefoni liikumist kolmes erinevas suunas.
MSDN artikkel: http://msdn.microsoft.com/en-us/library/ff431810(v=vs.92).aspx
Antud kood on internetinäidete põhjal kokku pandud kahest erinevast rakendusest – kolmemõõtmeliste joonte kuvamine ning palli edasi-tagasi sõitmine mööda ekraani.
How accelerometer is working–check it out!
MainPage.xaml
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="AccelerometerTest.MainPage"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid Name="MyGrid" Width="411" Height="768">
<Line x:Name="xLine" X1="200" Y1="400" X2="260" Y2="400" Stroke="Red" StrokeThickness="4" Margin="0"/>
<Line x:Name="yLine" X1="200" Y1="400" X2="200" Y2="340" Stroke="Green" StrokeThickness="4" Margin="0"/>
<Line x:Name="zLine" X1="200" Y1="400" X2="260" Y2="340" Stroke="Blue" StrokeThickness="4" Margin="0"/>
<Rectangle x:Name="Track" Height="80" Stroke="#FFC0C0C0"
RadiusX="40" RadiusY="40" Margin="0" Width="410" />
<Ellipse x:Name="Ball" Width="80" Height="80" Margin="0">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.234,0.255">
<GradientStop Color="White"/>
<GradientStop Color="Red" Offset="0.759"/>
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TranslateTransform x:Name="BallTransform" />
</Ellipse.RenderTransform>
</Ellipse>
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="200" />
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0,50,0,0" Name="textBlock2" Text="TextBlock" VerticalAlignment="Top" Width="200" />
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0,100,0,0" Name="textBlock3" Text="TextBlock" VerticalAlignment="Top" Width="200" />
</Grid>
</phone:PhoneApplicationPage>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="AccelerometerTest.MainPage"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<Grid Name="MyGrid" Width="411" Height="768">
<Line x:Name="xLine" X1="200" Y1="400" X2="260" Y2="400" Stroke="Red" StrokeThickness="4" Margin="0"/>
<Line x:Name="yLine" X1="200" Y1="400" X2="200" Y2="340" Stroke="Green" StrokeThickness="4" Margin="0"/>
<Line x:Name="zLine" X1="200" Y1="400" X2="260" Y2="340" Stroke="Blue" StrokeThickness="4" Margin="0"/>
<Rectangle x:Name="Track" Height="80" Stroke="#FFC0C0C0"
RadiusX="40" RadiusY="40" Margin="0" Width="410" />
<Ellipse x:Name="Ball" Width="80" Height="80" Margin="0">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.234,0.255">
<GradientStop Color="White"/>
<GradientStop Color="Red" Offset="0.759"/>
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<TranslateTransform x:Name="BallTransform" />
</Ellipse.RenderTransform>
</Ellipse>
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="200" />
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0,50,0,0" Name="textBlock2" Text="TextBlock" VerticalAlignment="Top" Width="200" />
<TextBlock Height="50" HorizontalAlignment="Left" Margin="0,100,0,0" Name="textBlock3" Text="TextBlock" VerticalAlignment="Top" Width="200" />
</Grid>
</phone:PhoneApplicationPage>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;
using Microsoft.Phone.Shell;
namespace AccelerometerTest
{
public partial class MainPage : PhoneApplicationPage
{
private const int sum = 25;
private const double gravity = 32.2; // raskusjõud (ft/sec2)
private const double damping = 0.5; // põrketugevus
private const double mul = 128.0; // palli liikumise tundlikkus
private double _sx = 0.0;
private double _sy = 0.0;
private double _vx = 0.0;
private double _vy = 0.0;
private double _ax = 0.0;
private double _ay = 0.0;
private double _time = DateTime.Now.Ticks;
private double width, height;
private double time;
private Accelerometer _accel = new Accelerometer();
Vector3 acceleration;
public MainPage()
{
InitializeComponent();
//järgmised kaks rida on selle jaoks, et telefonile ei tuleks "lukku" peale
PhoneApplicationService phoneAppService = PhoneApplicationService.Current;
phoneAppService.UserIdleDetectionMode = IdleDetectionMode.Disabled;
width = (Application.Current.Host.Content.ActualWidth - Ball.Width) / 2.0;
height = (MyGrid.Height - Ball.Height) / 2.0;
// Start acceleroemeter
_accel.CurrentValueChanged +=
new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(OnReadingChanged);
_accel.Start();
}
private void OnReadingChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
time = e.SensorReading.Timestamp.Ticks;
acceleration = e.SensorReading.Acceleration;
Dispatcher.BeginInvoke(() => UpdateDisplay());
// käivita UI thread
}
private double calculate(double[] k) {
double ysum = 0;
for (int i = 0; i < sum; i++)
ysum += k[i];
return ysum;
}
private void UpdateDisplay()
{
//saadakse kätte kiirendused kolmes suunas
double x = acceleration.X;
double y = acceleration.Y;
double z = acceleration.Z;
//time on suurusjärgus 0,02 ja = (praegune aeg - eelmine aeg) / 10 000 000
double ajavahemik = (time - _time) / 10000000.0;
//ax = ((kiirendus * raskusjõud (1G)) + eelmine kiirendus) / 2
// keskmine kiirendus kahe ajavahemiku vahel
double ax = ((x * gravity) + _ax) / 2.0;
double ay = ((y * gravity) + _ay) / 2.0;
// vx (kiirus) = eelmine keskmine kiirus + (keskmine kiirendus * aeg [ehk hetkel keskmine kiirus])
double vx = _vx + (ax * ajavahemik);
double vy = _vy + (ay * ajavahemik);
//palli uus asukoht teljel = eelmine asukoht + ((((eelmine kiirus + praegune kiirus) / 2 ) * aeg ) * kordaja=128)
//lühemalt: palli uus asukoht = eelmine asukoht + käesoleva asukoht
double sx = _sx + ((((_vx + vx) / 2.0) * ajavahemik) * mul);
double sy = _sy - ((((_vy + vy) / 2.0) * ajavahemik) * mul);
//alljärgnev arvutus tekitab põrkamise
// kui asukoht on väiksem kui -laius. laius on pool ekraanilaiusest - palli läbimõõt
//siis palli asukoht on minimaalne laius ja kiirus on -kiirus * 0,5
//(see on siis kiiruse vähenemine poole võrra aga vastassuunas)
if (sx < -width)
{
sx = -width;
vx = -vx * damping;
}
else if (sx > width)
{
sx = width;
vx = -vx * damping;
}
if (sy < -height)
{
sy = -height;
vy = -vy * damping;
}
else if (sy > height)
{
sy = height;
vy = -vy * damping;
}
// salvesta parameetrid järgmise tsükli tarvis
_time = time;
_ax = ax;
_ay = ay;
_vx = vx;
_vy = vy;
_sx = sx;
_sy = sy;
//tehakse transformX ning omistatakse sellele asukoha väärtus
//tulemus on see, et pall liigub ettenähtud kohale
BallTransform.X = sx;
BallTransform.Y = sy;
//joonistatakse kolmemõõtmelised värvilised jooned
xLine.X2 = xLine.X1 + acceleration.X * 400;
yLine.Y2 = yLine.Y1 - acceleration.Y * 400;
zLine.X2 = zLine.X1 - acceleration.Z * 200;
zLine.Y2 = zLine.Y1 + acceleration.Z * 200;
//kuvatakse kiirendusnumbrid
textBlock1.Text = "X: " + x.ToString("0.000");
textBlock2.Text = "Y: " + y.ToString("0.000");
textBlock3.Text = "Z: " + z.ToString("0.000");
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;
using Microsoft.Phone.Shell;
namespace AccelerometerTest
{
public partial class MainPage : PhoneApplicationPage
{
private const int sum = 25;
private const double gravity = 32.2; // raskusjõud (ft/sec2)
private const double damping = 0.5; // põrketugevus
private const double mul = 128.0; // palli liikumise tundlikkus
private double _sx = 0.0;
private double _sy = 0.0;
private double _vx = 0.0;
private double _vy = 0.0;
private double _ax = 0.0;
private double _ay = 0.0;
private double _time = DateTime.Now.Ticks;
private double width, height;
private double time;
private Accelerometer _accel = new Accelerometer();
Vector3 acceleration;
public MainPage()
{
InitializeComponent();
//järgmised kaks rida on selle jaoks, et telefonile ei tuleks "lukku" peale
PhoneApplicationService phoneAppService = PhoneApplicationService.Current;
phoneAppService.UserIdleDetectionMode = IdleDetectionMode.Disabled;
width = (Application.Current.Host.Content.ActualWidth - Ball.Width) / 2.0;
height = (MyGrid.Height - Ball.Height) / 2.0;
// Start acceleroemeter
_accel.CurrentValueChanged +=
new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(OnReadingChanged);
_accel.Start();
}
private void OnReadingChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
time = e.SensorReading.Timestamp.Ticks;
acceleration = e.SensorReading.Acceleration;
Dispatcher.BeginInvoke(() => UpdateDisplay());
// käivita UI thread
}
private double calculate(double[] k) {
double ysum = 0;
for (int i = 0; i < sum; i++)
ysum += k[i];
return ysum;
}
private void UpdateDisplay()
{
//saadakse kätte kiirendused kolmes suunas
double x = acceleration.X;
double y = acceleration.Y;
double z = acceleration.Z;
//time on suurusjärgus 0,02 ja = (praegune aeg - eelmine aeg) / 10 000 000
double ajavahemik = (time - _time) / 10000000.0;
//ax = ((kiirendus * raskusjõud (1G)) + eelmine kiirendus) / 2
// keskmine kiirendus kahe ajavahemiku vahel
double ax = ((x * gravity) + _ax) / 2.0;
double ay = ((y * gravity) + _ay) / 2.0;
// vx (kiirus) = eelmine keskmine kiirus + (keskmine kiirendus * aeg [ehk hetkel keskmine kiirus])
double vx = _vx + (ax * ajavahemik);
double vy = _vy + (ay * ajavahemik);
//palli uus asukoht teljel = eelmine asukoht + ((((eelmine kiirus + praegune kiirus) / 2 ) * aeg ) * kordaja=128)
//lühemalt: palli uus asukoht = eelmine asukoht + käesoleva asukoht
double sx = _sx + ((((_vx + vx) / 2.0) * ajavahemik) * mul);
double sy = _sy - ((((_vy + vy) / 2.0) * ajavahemik) * mul);
//alljärgnev arvutus tekitab põrkamise
// kui asukoht on väiksem kui -laius. laius on pool ekraanilaiusest - palli läbimõõt
//siis palli asukoht on minimaalne laius ja kiirus on -kiirus * 0,5
//(see on siis kiiruse vähenemine poole võrra aga vastassuunas)
if (sx < -width)
{
sx = -width;
vx = -vx * damping;
}
else if (sx > width)
{
sx = width;
vx = -vx * damping;
}
if (sy < -height)
{
sy = -height;
vy = -vy * damping;
}
else if (sy > height)
{
sy = height;
vy = -vy * damping;
}
// salvesta parameetrid järgmise tsükli tarvis
_time = time;
_ax = ax;
_ay = ay;
_vx = vx;
_vy = vy;
_sx = sx;
_sy = sy;
//tehakse transformX ning omistatakse sellele asukoha väärtus
//tulemus on see, et pall liigub ettenähtud kohale
BallTransform.X = sx;
BallTransform.Y = sy;
//joonistatakse kolmemõõtmelised värvilised jooned
xLine.X2 = xLine.X1 + acceleration.X * 400;
yLine.Y2 = yLine.Y1 - acceleration.Y * 400;
zLine.X2 = zLine.X1 - acceleration.Z * 200;
zLine.Y2 = zLine.Y1 + acceleration.Z * 200;
//kuvatakse kiirendusnumbrid
textBlock1.Text = "X: " + x.ToString("0.000");
textBlock2.Text = "Y: " + y.ToString("0.000");
textBlock3.Text = "Z: " + z.ToString("0.000");
}
}
}