磁場(chǎng)傳感器(Magnetic Field Sensors)
磁場(chǎng)傳感器可以用來(lái)檢測(cè)磁場(chǎng)大小,和加速度傳感器一樣,有x、y、z軸三個(gè)方向,單位為uT(microteslas)。磁場(chǎng)傳感器也稱(chēng)為compass(指南針),在uses-feature中使用android.hardware.sensor.compass作為其名字。說(shuō)實(shí)在的,單看磁場(chǎng)數(shù)值也看不出所以然。
說(shuō)個(gè)故事,單位部門(mén)調(diào)整,一撥同事到10樓,這是上下移動(dòng)實(shí)驗(yàn)室的夾層,下面是移動(dòng)網(wǎng)絡(luò)(有4G小基站)實(shí)驗(yàn)室。一聽(tīng)要挪過(guò)去,各個(gè)憂心忡忡,這輻射如何辦?按秘書(shū)的說(shuō)法,怎么辦,涼拌。正好玩磁場(chǎng)傳感器,雖然magnetic不是electromagnetic,電場(chǎng)、磁場(chǎng)相互作用,在具體的都還給老師了。手頭上也沒(méi)有什么能夠進(jìn)行測(cè)試的,就用磁場(chǎng)傳感器測(cè)一測(cè)。這種事情多半都是自己嚇自己,智能手機(jī)將成為摸金校尉的必備工具。
下面是小例子代碼片段:
public class MagneticFieldSensorActivity extends Activity implements SensorEventListener{
。..。..
@Override
protected void onCreate(Bundle savedInstanceState) {
。..。..
sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
。..。.. //注冊(cè)和注銷(xiāo)磁場(chǎng)傳感器監(jiān)聽(tīng)器
private int count = 1;
@Override
public void onSensorChanged(SensorEvent event) {
if( count++ == 20){ //磁場(chǎng)傳感器很敏感,每20個(gè)變化,顯示一次數(shù)值
double value = Math.sqrt(event.values[0]*event.values[0] + event.values[1]*event.values[1]
+event.values[2]*event.values[2]);
String str = String.format(“X:%8.4f , Y:%8.4f , Z:%8.4f 總值為:%8.4f”,
event.values[0],event.values[1],event.values[2],value);
count = 1;
tv.setText(str);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Nothing here
}
}
方位傳感器(Orientation Sensors)
磁場(chǎng)傳感器和加速度傳感器結(jié)合可或者設(shè)備擺放角度,兩者結(jié)合可以獲取方位。這些計(jì)算由Android代勞了,SensorManager提供getRotationMatrix(),獲得轉(zhuǎn)動(dòng)的矩陣,并進(jìn)一步通過(guò)getOrientation()獲得方位矩陣。Android還有方位傳感器(orientation Sensors),不是物理實(shí)體,而是通過(guò)acceleration傳感器和磁場(chǎng)感應(yīng)器來(lái)獲取方位,而在Android2.2開(kāi)始方位傳感器就被deprecated了。
設(shè)備擺放情況通過(guò)azimuth、pitch和roll來(lái)表示。
azimuth即方位角,就是手機(jī)方向和正北的夾角,百度百科這樣描述方位角:是從某點(diǎn)的指北方向線起,依順時(shí)針?lè)较虻侥繕?biāo)方向線之間的水平夾角。pitch和roll可能是引用了航天的術(shù)語(yǔ)。azimuth,pitch和roll分別是z軸、X軸和Y軸的旋轉(zhuǎn)角度。
老方法采用orientation傳感器,azimuth從0~360。pitch是在x軸方向的轉(zhuǎn)動(dòng)角度,其實(shí)就是Y軸和水平面的仰角,范圍為-180~180,正的為朝下(手機(jī)頭低于水平面),負(fù)的為朝上,pitch的方向逆時(shí)針為正。roll是Y軸防線的轉(zhuǎn)動(dòng)角度,實(shí)際就是X軸和水平面的角度,范圍在-90~90,同樣逆時(shí)針為正,右軸高于左軸時(shí)為正,右軸高于左軸時(shí)為負(fù)。
新的方法,azimuth的范圍是-180~180,當(dāng)然我們可以進(jìn)行適當(dāng)?shù)奶幚?,如果小于零,則加360,這樣就可以和orientation的得到的值一樣。需要注意的是pitch的范圍是-90~90,機(jī)頭上翹為負(fù);roll的范圍是-180~180,和老方法相反,右軸高時(shí)為負(fù)。
這兩種方式的數(shù)值可能會(huì)使人有些迷糊,我們?cè)谑褂弥跋炔槲臋n弄清楚就是了。我曾經(jīng)有個(gè)疑惑為何有兩個(gè)數(shù)值的范圍是360度,其中一個(gè)數(shù)值只有180度。想想球面就知道了,以地球儀為例,經(jīng)度范圍是360°,維度范圍是180°,就可以確定球面上的任何一點(diǎn),以球心到該點(diǎn)假設(shè)是手機(jī)中軸線,還有一個(gè)圍繞Y軸360°轉(zhuǎn)的角度的第三維,這就可以確定所有的排放方式。
小例子
下面小例子我們將同時(shí)展現(xiàn)新舊兩種方式。
public class VirtualJax extends Activity implements SensorEventListener{
。.. 。.. //注冊(cè)和注銷(xiāo)傳感器監(jiān)聽(tīng)器,本例涉及三個(gè)傳感器(加速度傳感器、地磁傳感器和方位傳感器),可以用sensorManager.unregister(this)注銷(xiāo)監(jiān)聽(tīng)器所涉及的全部傳感器。
private Sensor accelSensor = null, compassSensor = null, orientSensor = null;
private float[] accelValues = new float[3], compassValues = new float[3],orientValues = new float[3];
private boolean ready = false; //檢查傳感器是否正常工作,即是否同時(shí)具有加速傳感器和磁場(chǎng)傳感器。
private float[] inR = new float[9];
private float[] inclineMatrix = new float[9];
private float[] prefValues = new float[3];
private double mInclination;
private int count = 1;
@SuppressWarnings(“deprecation”) //因?yàn)閛rientaion已經(jīng)不再使用,為了不要顯示warning,加上此標(biāo)識(shí)
@Override
public void onSensorChanged(SensorEvent event) {
//【1】將相關(guān)傳感器的數(shù)值分別讀入accelValues,compassValues(磁力感應(yīng)器的數(shù)值)和orientValues數(shù)組中
switch(event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
for(int i = 0 ; i 《 3 ; i ++){
accelValues[i] = event.values[i];
}
if(compassValues[0] != 0) //如果accelerator和magnetic傳感器都有數(shù)值,設(shè)置為真
ready = true;
break;
case Sensor.TYPE_MAGNETIC_FIELD:
for(int i = 0 ; i 《 3 ; i ++){
compassValues[i] = event.values[i];
}
if(accelValues[2] != 0) //檢查accelerator和magnetic傳感器都有數(shù)值,只是換一個(gè)軸向檢查
ready = true;
break;
case Sensor.TYPE_ORIENTATION:
for(int i = 0 ; i 《 3 ; i ++){
orientValues[i] = event.values[i];
}
break;
}
if(!ready)
return;
//【2】根據(jù)加速傳感器的數(shù)值accelValues[3]和磁力感應(yīng)器的數(shù)值compassValues[3],進(jìn)行矩陣計(jì)算,獲得方位
//【2.1】計(jì)算rotation matrix R(inR)和inclination matrix I(inclineMatrix)
if(SensorManager.getRotationMatrix(inR, inclineMatrix, accelValues, compassValues)){
/* 【2.2】根據(jù)rotation matrix計(jì)算設(shè)備的方位。,范圍數(shù)組:
values[0]: azimuth, rotation around the Z axis.
values[1]: pitch, rotation around the X axis.
values[2]: roll, rotation around the Y axis.*/
SensorManager.getOrientation(inR, prefValues);
//【2.2】根據(jù)inclination matrix計(jì)算磁仰角,地球表面任一點(diǎn)的地磁場(chǎng)總強(qiáng)度的矢量方向與水平面的夾角。
mInclination = SensorManager.getInclination(inclineMatrix);
//【3】顯示測(cè)量值
if(count++ % 100 == 0){
doUpdate(null);
count = 1;
}
}else{
Toast.makeText(this, “無(wú)法獲得矩陣(SensorManager.getRotationMatrix)”, Toast.LENGTH_LONG);
finish();
}
}
//【3】顯示測(cè)量值
public void doUpdate(View v){
if(!ready)
return;
//preValues[0]是方位角,單位是弧度,范圍是-pi到pi,通過(guò)Math.toDegrees()轉(zhuǎn)換為角度
float mAzimuth = (float)Math.toDegrees(prefValues[0]);
/*//糾正為orientation的數(shù)值。
* if(mAzimuth 《 0)
mAzimuth += 360.0;*/
String msg = String.format(“推薦方式: 方位角:%7.3f pitch: %7.3f roll: %7.3f 地磁仰角:%7.3f ”,
mAzimuth,Math.toDegrees(prefValues[1]),Math.toDegrees(prefValues[2]),
Math.toDegrees(mInclination));
nowOne.setText(msg);
msg = String.format(“老方式: 方位角:%7.3f pitch: %7.3f roll: %7.3f”,
orientValues[0],orientValues[1],orientValues[2]);
oldOne.setText(msg);
}
}