博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Windows Mobile下的重力感应器(Gravitational Sensor)开发
阅读量:4324 次
发布时间:2019-06-06

本文共 10399 字,大约阅读时间需要 34 分钟。

背景

重力感应器(Gravitational Sensor,简称为GSensor),类似于Accelerometer和Tilt Sensor, 用于测量倾斜度的感应器。严格定义来说,Accelerometer和Tilt Sensor是有区别的,Accelerometer可以测量三维,而Tilt Sensor只能测量二维。术语的定义见 和 。

Accelerometer被广泛用于手机等移动设备上,同时用于Wii的手柄上,Wii游戏的移动就是根据Accelerometer测量的数据进行移动的。

简介

重力感应器(Gravitational Sensor, Accelerometer)已经被广泛应用于Windows Mobile设备上,可是由于MS没有官方定义和提供统一的API,为重力感应器的开发带来不便,本文讲述如何在HTC和Samsung设备上进行重力感应器的开发,实现统一访问了GSensor的类库,在实现过程中使用了Singleton,Simple Factory和Observer模式。

原理

根据设备的内在能力,Accelerometer能够测量一维,二维或者三维的重力加速度。关于 Accelerometer的原理可以参考wikipedia的文章,我不详细介绍了,我主要介绍一下软件开发相关的。

图1 源自于《Samsung Mobile Innovator Windows Mobile API Programming Guide》

从上图可以看出重力信息只是和设备本身有关,和设备在相对位置无关。例如设备平放在水平的桌面上,对这长的屏幕前后移动设备(如下图2),Y轴会发生变化。

图2 源自于

另外的情况,设备长的屏幕垂直放(如下图3),上下移动,也是Y轴在发生变化。

图3 源自于

Samsung 的手机可以输入 *#0*# 启动LCD Test 程序来测试Accelerometer的运行状况。

原罪

Windows Mobile 6.5及以下的设备,是没有统一的API操作GSensor,MS一直没有统一包括GSensor在内的所有Sensor的接口(其他Sensors包括Light Sensor,Stylus Sensor等等),甚至连WM7也没有官方答复,关于MS的答复可以参考下面链接

没有统一的API,各个手机硬件厂商都需要开发自己的API,其中以HTC和samsung最为出名。开始的时候,各个厂商都不公开自己的API,导致Windows Mobile的开发人员只能通过反向工程(Reverse Engineering)等非正当手段获取API,哪怕获取了厂商的API,开发的程序也不能同时支持多种硬件设备。幸运的是 封装了一个托管版本的 ,同时支持HTC和Samsung。下面我会介绍如何使用native c++分别调用HTC和Samsung的GSensor API。

Samsung GSensor API

Samsung已经公开了自己的API,可以在 注册下载和安装。里面包含的Samsung官方的GSensor API。 使用Samsung的API需要安装一个Cab。Cab在 C:\Program Files\Samsung Windows Mobile SDK\redist\smi_wm_sdk_redist_1_1_0.cab

GSensor API定义见 C:\Program Files\Samsung Windows Mobile SDK\inc\smiAccelerometer.h

取GVector信息

GVector SamsungGSensor::GetGVector() {
SmiAccelerometerVector accel; if(SmiAccelerometerGetVector(&accel) == SMI_SUCCESS) {
GVector gVector; gVector.x = accel.x; gVector.y = accel.y; gVector.z = accel.z; return gVector; } throw; }

调用SmiAccelerometerGetVector() API取出GVector信息。

订阅GVector信息

Samsung的API提供订阅功能。

void SamsungGSensor::Register() {
SmiAccelerometerCapabilities cap; if( SmiAccelerometerGetCapabilities(&cap) != SMI_SUCCESS) {
throw; } SmiAccelerometerHandler h = &GetVectorHandler; if(SmiAccelerometerRegisterHandler(1000, h) != SMI_SUCCESS) {
throw; } //Execute the task every second. //Start(1000); }

SmiAccelerometerGetCapabilities()函数检查GSensor的情况,SmiAccelerometerRegisterHandler()注册GetVectorHandler处理函数定期取出GVector信息,SmiAccelerometerRegisterHandler()的第一个参数为interval(取数据的间隔),第二个为回调处理函数,该函数只能为static。

void SamsungGSensor::Unregister() {
SmiAccelerometerUnregisterHandler(); //Stop(); }

上面是反注册函数。

void SamsungGSensor::GetVectorHandler(SmiAccelerometerVector accel) {
GVector gVector; gVector.x = accel.x; gVector.y = accel.y; gVector.z = accel.z; SamsungGSensor::GetInstance()->GVectorChanged(gVector); }

这是回调函数,定义如下:

private: static void GetVectorHandler(SmiAccelerometerVector accel);

由于SmiAccelerometerRegisterHandler()注册的回调函数只能是static的,所以我在开发SamsungGSensor的时候不得不把这个类做成Singleton,否则static函数没法取出对象的实例指针了。

运行于Samsung机器的界面。

HTC GSensor API

目前为止(2009年7月),HTC还没有公开Sensor的APIs,所以这些API都是通过反向工程(Reverse Engineering)出来的,使用有风险,自己承担。

API的定义生成和清理

private: // The following PInvokes were ported from the results of the reverse engineering done     // by Scott at scottandmichelle.net.     // Blog post: http://scottandmichelle.net/scott/comments.html?entry=784 typedef HANDLE (WINAPI * PFN_HTCSensorOpen)(DWORD); typedef void (WINAPI * PFN_HTCSensorClose)(HANDLE); typedef DWORD (WINAPI * PFN_HTCSensorGetDataOutput)(HANDLE, PSENSORDATA);     PFN_HTCSensorOpen           pfnHTCSensorOpen;     PFN_HTCSensorClose          pfnHTCSensorClose;     PFN_HTCSensorGetDataOutput  pfnHTCSensorGetDataOutput;

 

#define SENSOR_DLL      L"HTCSensorSDK.dll" HTCGSensor::HTCGSensor(void) {
HMODULE hSensorLib = LoadLibrary(SENSOR_DLL); if (hSensorLib == NULL) {
printf("Unable to load HTC Sensor DLL"); throw; } pfnHTCSensorOpen = (PFN_HTCSensorOpen) GetProcAddress(hSensorLib, L"HTCSensorOpen"); pfnHTCSensorClose = (PFN_HTCSensorClose) GetProcAddress(hSensorLib, L"HTCSensorClose"); pfnHTCSensorGetDataOutput = (PFN_HTCSensorGetDataOutput) GetProcAddress(hSensorLib, L"HTCSensorGetDataOutput"); if (pfnHTCSensorOpen == NULL || pfnHTCSensorClose == NULL || pfnHTCSensorGetDataOutput == NULL) {
printf("Unable to find entry point"); throw; } sensorHandle = NULL; sensorHandle = pfnHTCSensorOpen(HTC_GSensor); } HTCGSensor* HTCGSensor::Create() {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, L"HTC_GSENSOR_SERVICESTART"); if (hEvent == NULL || GetLastError() != ERROR_ALREADY_EXISTS) {
printf("Unable to create Sensor Event"); throw; } SetEvent(hEvent); CloseHandle(hEvent); return new HTCGSensor(); } HTCGSensor::~HTCGSensor(void) {
if(sensorHandle != NULL) {
pfnHTCSensorClose(sensorHandle); sensorHandle = NULL; } HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, L"HTC_GSENSOR_SERVICESTOP"); if (hEvent == NULL || GetLastError() != ERROR_ALREADY_EXISTS) {
printf("Unable to stop Sensor Event"); throw; } SetEvent(hEvent); CloseHandle(hEvent); }

HTCGSensor()构造函数加载DLL和生成函数调用的入口指针。Create()函数启动Sensor。~HTCGSensor()释放资源。

取GVector信息

GVector HTCGSensor::GetGVector() {
GVector gVector; SENSORDATA data; pfnHTCSensorGetDataOutput(sensorHandle, &data); // HTC's Sensor returns a vector which is around 1000 in length on average.. // but it really depends on how the device is oriented. // When simply face up, my Diamond returns a vector of around 840 in length. // While face down, it returns a vector of around 1200 in length. // The vector direction is fairly accurate, however, the length is clearly not extremely precise. float htcScaleFactor = 1.0 / 1000.0 * 9.8; gVector.x = data.TiltX * htcScaleFactor; gVector.y = data.TiltY * htcScaleFactor; gVector.z = data.Orientation * htcScaleFactor; return gVector; }

订阅GVector信息

由于HTC的API不提供订阅功能,所以我封装了一个ThreadTask(线程任务)类,负责生成一个线程,该线程定期执行任务,在这个场景下定期任务用于取GVector信息。

#include 
class ThreadTask {
public: ThreadTask(); ~ThreadTask(void); private: HANDLE mProcEvent; HANDLE mThreadHnd; DWORD mThreadId; bool mThreadHalt; int mInterval; bool mStarted; public: void ProcessTask(); void Start(int interval); void Stop(); virtual void Process() {}; };

//    Thread methods DWORD WINAPI ProcessThread(void *param) {
if (param) {
ThreadTask* thread = (ThreadTask*)param; thread->ProcessTask(); } return 0; } ThreadTask::ThreadTask() : mProcEvent(INVALID_HANDLE_VALUE), mThreadHnd(NULL), mThreadId(0), mThreadHalt(false), mInterval(0), mStarted(false) {
} ThreadTask::~ThreadTask(void) {
Stop(); } void ThreadTask::Start(int interval) {
if(!mStarted) {
mStarted = true; mInterval = interval; mProcEvent = CreateEvent(NULL, true, false, NULL); // manual reset, initial state reset mThreadHnd = CreateThread(NULL, 0, &ProcessThread, this, CREATE_SUSPENDED, &mThreadId); if (mThreadHnd) {
SetThreadPriority(mThreadHnd,THREAD_PRIORITY_NORMAL); ResumeThread(mThreadHnd); } } } void ThreadTask::Stop() {
if(mStarted) {
mThreadHalt = true; // Signal the event SetEvent(mProcEvent); // Wait for the Thread to Die WaitForSingleObject(mThreadHnd, INFINITE); CloseHandle(mThreadHnd); CloseHandle(mProcEvent); mStarted = false; } } void ThreadTask::ProcessTask() {
while (!mThreadHalt) {
WaitForSingleObject(mProcEvent, mInterval); //INFINITE ResetEvent(mProcEvent); //process by subclass Process(); } }

作为ThreadTask的子类只需要知道interval来启动Thread,然后重写处理定时任务函数(Override Process() )。ThreadTask可以用于Windows Mobile开发下的很多场景下。

void HTCGSensor::Register() {
Start(1000); } void HTCGSensor::Unregister() {
Stop(); } void HTCGSensor::Process() {
GVectorChanged(GetGVector()); }

 

Sensors工厂

Client不需要知道具体那个厂家(HTC or Samsung)的Sensor,只需要调用工厂类生成Sensor类。

class GSensorFactory {
public: static IGSensor* CreateGSensor(); };

IGSensor* GSensorFactory::CreateGSensor() {
try {
return SamsungGSensor::GetInstance(); } catch(...) {
} try {
return HTCGSensor::Create(); } catch(...) {
} return NULL; }

自动生产相应的Sensor的对象。

LRESULT CSensorTesterView::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
gSensor = GSensorFactory::CreateGSensor(); if(NULL == gSensor) {
MessageBox(L"Can not Initialise GSensor."); } return TRUE; }

Client只是调用工厂类生产Sensor对象。

Observer模式

为Client提供一个当GVector发生改变时自动通知更新的功能,这里使用了Observer模式,我使用了一个开源的类 ,这个类具有很多优点,类型安全(type safe),泛型(generic),任意参数类型和任意参数数量,回传Sender的指针等等, 代码在这里下载

class IGSensor; /**  *  Oberver interface for Gravitation Sensor.  *  */ class IGSensorListener {
public: typedef IGSensor notifier_type; virtual void IGSensor_GVectorChanged(IGSensor* gSensor, GVector gVector) {}; };

这是Listener,也就是我们常说的Abstract Observer。需要定义notifier_type和定义回调接口。

/**  *  Interface of Gravitation Sensor.  *  */ class IGSensor : public Notifier
, public ThreadTask {
public: IGSensor(void); ~IGSensor(void); public: virtual GVector GetGVector() = 0; virtual void Register() = 0; virtual void Unregister() = 0; protected: void GVectorChanged(GVector gVector); };
这是Notifier也就是Subject,需要继承 Notifier<IGSensorListener>。

class CSensorTesterView : public Listener
{
public: virtual void IGSensor_GVectorChanged(IGSensor* gSensor, GVector gVector) override; LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); };
LRESULT CSensorTesterView::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {      gSensor->Register();     attachTo(gSensor); return TRUE; }

上面显示的是client类,为了演示把一些Observer模式无关的代码删除掉,完整代码可以下载源代码。client类需要继承public Listener<IGSensorListener>,重写IGSensor_GVectorChanged()函数和调用attachTo()函数进行注册。

 

一个统一访问GSensor的类库和实例代码就完成了。由于没有HTC的机器,如果谁能为我提供一个测试,我会衷心感谢他。

 

关于Mobile Sensors API项目

这个项目还是在起步阶段,当前实现了samsung的重力感应器,我把项目host到 了,我会持续改进,把各种sensors的实现到这个项目中。

由于我手头上没有HTC的机器,如果谁有兴趣可以加入到项目中帮我测试HTC设备,由于加入了Unit Test,测试变得很简单,只需要执行程序,参考测试输出文件就可以了,不需要调试。当然这个测试过程是一个不断迭代的过程,只是Unit Test把子过程简单化了。

源代码:

环境:VS2008 + WM 6 professional SDK + Samsung Windows Mobile SDK

转载于:https://www.cnblogs.com/procoder/archive/2009/07/28/1532592.html

你可能感兴趣的文章
PHP与正则表达式 2 :一些修饰符与preg_match_all
查看>>
压八位高精度 高精操作大全
查看>>
进程、线程、协程和GIL(二)
查看>>
dockerfile语法规则
查看>>
模块的导入
查看>>
《Inside C#》笔记(八) 接口
查看>>
kafka介绍
查看>>
java类加载机制及方法调用
查看>>
《大话西游》:我的意中人是一个盖世英雄
查看>>
iOS 清除xcode缓存和生成文件
查看>>
为什么幻灯片画布不居中
查看>>
flask模板应用-javaScript和CSS中jinja2 --
查看>>
react-native 调用原生方法
查看>>
查看Mac系统所有USB设备信息 解决android studio无法识别真机问题
查看>>
20145238 《信息安全系统设计基础》第2周学习总结
查看>>
android 获取日期
查看>>
HDU-1018 BigNumber(斯特林近似)
查看>>
Excel公式——单元格前加固定字符串
查看>>
BZOJ.4738.[清华集训2016]汽水(点分治 分数规划)
查看>>
testNG框架的四种传参方式
查看>>