Archives

December 2007 の記事

2007/12/27
地球上の座標

地球の上、どこにいるのか緯度と経度でわかります。
例えば GoogleEarth の座標も緯度、経度。

自分が好きな方向に 1m 進んだ場合の位置はを求めるには
緯度方向はほぼ一定
 1度 = 約111km
 1分 = 約1852m
 1秒 = 約30.9m
 1m = 0.000009度
float では足りないので倍精度が必要。
(double の演算ライブラリを全く用意していなかったことにふと気が付く)

経度の変化量は場所によって異なるのと、直進が経度方向に一致しないので、
簡単ではなさそうです。

慣れているベクトルに変換してみます。
角度は度分秒で時間と同じ。度 + 分/60 + 秒/3600
完全な球とみなして極方向を Y、赤道面を X-Z とすると

const double	dtr= _PI / 180.0;
double	rlat= latitude * dtr;
double	rlon= longitude * dtr;
Position.y= sin( rlat );
double	xlength= cos( rlat );
Position.x= xlength * sin( rlon );
Position.z= xlength * cos( rlon );

これで位置が求まるので、接ベクトルを求めれば移動できそうです。
極で矛盾するけど取りあえず

const static double	vup[3]= { 0.0, 1.0, 0.0, };
Cross( tvx, vup, Position );
Normalize( tvx );
Cross( tvy, Position, tvx );

自分の向きの表現は GoogleEarth では方位角度で、0 が北 180 が南と
なるようです。よって

double razimuth= azimuth * dtr;
double rxa= sin( razimuth ) * speed;
double rya= cos( razimuth ) * speed;
Position+= tvx * rxa + tvy * rya;

Position は正規化されているので、speed は 1/地球の半径 倍する
必要あり。
移動したら緯度経度に戻します。

Normalize( Position );
double	rlon= atan2( Position.x, Position.z );
double	xlength= sqrt( Position.x*Position.x + Position.z*Position.z );
double	rlat= atan2( Position.y, xlength );
latitude= rlat * rtd;
longitude= rlon * rtd;

最初に変換してベクトルのまま保持しておいた方がよさそうです。

参考ページ
Wikipedia 緯度
Google Earth COM API を C++ で使う
Google Earth COM API Documentation


以前のエントリ。
2007/10/22 em1keypc 64bit Windows でも 親指シフト
これ以降もプログラムを何度か更新しました。
最新版は下記からどうぞ。

em1keypc 1.31 for PC Win32/Win64
oyayubiwm v1.44

今のところ他の方の 64環境で動いているかわかりませんが、
自分は WindowsVista x64 環境×2 で em1keypc + oyayubiwm を常時
起動しています。(ドッグフードを兼ねて)
64bit といっても特別な操作も設定も必要なく、全く同じバイナリを
起動するだけでよくなっています。


取りあえずパスキー無しの Bluetooth デバイスをプログラムで
ペアリング&追加/接続する方法がわかりました。

WiiリモコンやバランスWiiボードは電源を入れるたびに毎回 sync
しなければなりませんが、これでいちいち
「Bluetooth デバイスの追加ウィザード」を使わなくてよくなります。

(1) 新規デバイスの列挙
 BluetoothFindFirstDevice()
 BluetoothFindNextDevice()
 BluetoothFindDeviceClose()

(2) 対応デバイスかどうか名前で判別
 BLUETOOTH_DEVICE_INFO の szName を参照。

 例えば Wiiコントローラなら
  "Nintendo RVL-CNT-01" (Wiiリモコン)
  "Nintendo RVL-WBC-01" (バランスWiiボード)

(3) 接続済みかどうか判定
 BLUETOOTH_DEVICE_INFO の fConnected が 0 以外ならすでに
 接続中なので無視。

(4) サービスの追加
#include <bthdef.h> // GUID の定義
BluetoothSetServiceState( hradio, &devinfo,
	&HumanInterfaceDeviceServiceClass_UUID,
	BLUETOOTH_SERVICE_ENABLE );

ただし既に一度追加したことがあり、サービスが Enable のままの
デバイスは (4) に失敗します。
BluetoothRemoveDevice() を使って (4) の直前に登録情報を削除して
おくと確実に接続できるようです。

Wiiリモコン・バランスWiiボード 側は、(1) の新規デバイスの検索中
に赤い sync ボタンを押しておきます。

関連リンク
Windows Bluetooth API
バランスWiiボード をPCで使う wiibalancepc v0.07


Bluetooth 関連の API は BluetoothAPIs.h

ヘッダにも詳しく使い方や説明が書かれているため、ヘッダ見た方が
わかりやすいようです。(XP SP2 以降)

//Bluetooth インターフェース検索

#include	<stdio.h>
#include	<windows.h>
#include	<Bthsdpdef.h> // 必要
#include	<BluetoothAPIs.h>

BLUETOOTH_FIND_RADIO_PARAMS	param;
memset( &param, 0, sizeof(BLUETOOTH_FIND_RADIO_PARAMS) );
param.dwSize= sizeof(BLUETOOTH_FIND_RADIO_PARAMS);

HANDLE	hradio= NULL;
HBLUETOOTH_RADIO_FIND	hfind= NULL;

if( hfind= BluetoothFindFirstRadio( &param, &hradio ) ){
	do{
		BLUETOOTH_RADIO_INFO	rinfo;
		memset( &rinfo, 0, sizeof(BLUETOOTH_RADIO_INFO) );
		rinfo.dwSize= sizeof(BLUETOOTH_RADIO_INFO);
		BluetoothGetRadioInfo( hradio, &rinfo );

		// rinfo.szName  Local Hostname
		// rinfo.address Local Address

		~

		CloseHandle( hradio );
	}while( BluetoothFindNextRadio( hfind, &hradio ) );
	BluetoothFindRadioClose( hfind );
}

これで hRadio や Local の Address が得られます。

ただ Device 関連の API で hRadio が必要な部分はほぼ全部 NULL を
渡すことができるため、あまり必要ないかもしれません。
NULL だと全部の Bluetooth interface が対象。

//Device の検索
BLUETOOTH_DEVICE_SEARCH_PARAMS	searchparam;
memset( &searchparam, 0, sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS) );
searchparam.dwSize= sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS);
searchparam.fReturnAuthenticated= TRUE;
searchparam.fReturnRemembered= TRUE;
searchparam.fReturnConnected= TRUE;
searchparam.fReturnUnknown= TRUE;
searchparam.fIssueInquiry= FALSE;
searchparam.cTimeoutMultiplier= 0;
searchparam.hRadio= NULL; // all

HBLUETOOTH_DEVICE_FIND	hfind= NULL;
BLUETOOTH_DEVICE_INFO	devinfo;
memset( &devinfo, 0, sizeof(BLUETOOTH_DEVICE_INFO) );
devinfo.dwSize= sizeof(BLUETOOTH_DEVICE_INFO);
if( hfind= BluetoothFindFirstDevice( &searchparam, &devinfo ) ){
	do{
		// devinfo.szName        デバイス名
		// devinfo.Address       アドレス
		// devinfo.fConnected
		// devinfo.fRemembered
		// devinfo.fAuthenticated
	}while( BluetoothFindNextDevice( hfind, &devinfo ) );
	BluetoothFindDeviceClose( hfind );
}

これで Wiiリモコン/バランスWiiボード 等も列挙できます。
新しいデバイスの検出も行う場合は searchparam.fIssueInquiry= TRUE、
searchparam.cTimeoutMultiplier= 10 くらいに。

パスキーは
wchar_t* passkey= ~;
unsigned long passlen= wcslen(passkey)*sizeof(wchar_t);
BluetoothAuthenticateDevice( NULL, NULL, &devinfo, passkey, passlen );

または
BluetoothRegisterForAuthentication() → Callback →
BluetoothSendAuthenticationResponse()
BluetoothUnregisterAuthentication()
の手順で行うようです。Dialog を表示させて手入力も可能。


サービス一覧(enable のものだけ)
BluetoothEnumerateInstalledServices( NULL, &devinfo, &numofbuf, _guidbuf );

サービスの切り替え
BluetoothSetServiceState( hradio, &devinfo, &_guid,
BLUETOOTH_SERVICE_ENABLE or BLUETOOTH_SERVICE_DISABLE );


BluetoothEnumerateInstalledServices() は Enable のものだけ列挙する
ので、EnumerateInstalledServices で取得した GUID に対して
SetServiceState( ~ENABLE ) すると必ずエラーになります。
既に ENABLE なため。DISABLE には使える。


pass key 無しで Device を追加登録する方法がまだわかりません。


参考ページ
MSDN BluetoothFindFirstRadio Function
・VS2008 \Microsoft SDKs\Windows\v6.0A\Include\BluetoothAPIs.h


メモリが安くなったので増設してみました。
2GB×2 で 約 8000円

ちなみに 10月に 1GB×2 を 7000円ほどで買ったばかり・・。

Windows Vista x64 で 8GB きちんと認識しています。

その効果はというと

合計           8190MB
キャッシュ済み 6775MB
空きメモリ       34MB

6.7GB ものメモリがキャッシュで費やされています。

さすが Vista。
メモリを増やせば増やした分だけ、空きがなくなるまで取りあえずメモリを
使ってくれるようです。これがスーパーフェッチの効果でしょうか。


たまに Vista にしたら空きメモリが減ったとか、空きメモリを増やすには
どうしたら良いかと尋ねられることがあります。
だけど特に意識する必要はないと思います。

余剰金が発生しても、何もしないで家においておくのが「空きメモリ」で、
銀行に預けたり資産運用するのが「キャッシュ済み」メモリ。

所持金が減るわけではなく、必要になったらいつでも引き出せますし
しかもバックグラウンドである程度有効に活用されています。


キャッシュに活用してくれるのは良いのですが、その代わりメモリが
埋まるまで、先読みの HDD アクセスが結構長く続くようになりました。


関連エントリ
2007/11/27 プログラマの開発環境
2007/10/22 em1keypc 64bit Windows でも 親指シフト