Hour 12. Using the Gravity and Pressure Sensors


What You’ll Learn in This Hour

Using the gravity sensor

Using the pressure sensor


This hour continues our study of the Android sensors by examining the gravity sensor and pressure sensor, which are two of the more common sensors found in the majority of devices. These sensors might be used for some very interesting games if a creative designer were to put them to use.

Using the Gravity Sensor

The gravity sensor on Android devices reports an algorithmic value based on the linear acceleration sensor. In that regard, it is not a distinct hardware sensor but an algorithm (called a low-pass filter). However, we can still use it to get a decent report on gravity.

The gravity value returned by this sensor is a 3D vector. It represents both the direction and magnitude of gravity. The three values supplied by this sensor represent this vector with an x, y, and z component.

Initializing the Gravity Sensor

To initialize the gravity sensor, create a variable of type Sensor and initialize it with the constant Sensor.TYPE_GRAVITY.

Sensor gravity=null;
gravity = sensors.getDefaultSensor(Sensor.TYPE_GRAVITY);
if (gravity==null) {
    title = "ERROR: NO GRAVITY SENSOR WAS FOUND";
    Log.d("ERROR","Gravity sensor not found");
}

Reading the Gravity Sensor

To read the values reported by the gravity sensor, we use the onSensorChanged() event method as usual. The first three values array indexes provide the x, y, and z components of a 3D vector. This vector represents the direction to the gravity source (usually the center of the Earth). A Float3 works well to record this data for later use in the program.

Float3 data = new Float3(0,0,0);
@Override public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
    case Sensor.TYPE_GRAVITY:
        data.x = event.values[0];
        data.y = event.values[1];
        data.z = event.values[2];
        break;
    }
}

The vector makes sense only if you hold your Android device flat (on a table or in your hand) and then move it from that initial position. From this orientation, the Z axis pointing downward toward the Earth receives actual gravity values that correspond with known gravity. See Table 12.1 for a list of predefined gravity constants that can be used for comparison.

Table 12.1. SensorManager Gravity Constants

Image

Watch Out

Whenever you use the Float2 or Float3 class, be sure to instantiate it by passing zeroes to the constructor! Failure to initialize these values will result in an exception error if the object is ever parsed by BigDecimal (for rounding).


Because the value recorded by the gravity sensor is a 3D vector, you will have to convert this value to 2D screen space to visualize the vector. Screen space involves only an X and Y pair. To convert a 3D coordinate to a 2D coordinate, divide each of X and Y by Z, like so:

U = X / Z
V = Y / Z

U and V represent the resulting screen coordinate of a 3D point. This could be handy later on!

Testing the Gravity Sensor

The following program is called Gravity Sensor Demo in the included source code projects. Figure 12.1 shows the output from this program.

Image

Figure 12.1. The Gravity Sensor Demo program graphs the gravity vector.

package android.program;
import java.math.BigDecimal;
import android.app.Activity;
import android.os.Bundle;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.util.Log;
import android.view.*;

import android.content.pm.ActivityInfo;
import android.renderscript.*;
import android.hardware.*;

public class Game extends Activity implements SensorEventListener {
    DrawView drawView;
    SensorManager sensors;
    Sensor gravity=null;
    String title="Gravity Sensor Demo";
    volatile Float3 data = new Float3(0,0,0);

    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        drawView = new DrawView(this);
        setContentView(drawView);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        //initialize sensors
        sensors = (SensorManager)getSystemService(SENSOR_SERVICE);

        //try to get linear acceleration sensor
        gravity = sensors.getDefaultSensor(Sensor.TYPE_GRAVITY);
        if (gravity==null) {
            title = "ERROR: NO GRAVITY SENSOR WAS FOUND";
            Log.d("ERROR","Gravity sensor not found");
        }
    }

    @Override public void onSensorChanged(SensorEvent event) {
        switch (event.sensor.getType()) {
        case Sensor.TYPE_GRAVITY:
            data.x = event.values[0];
            data.y = event.values[1];
            data.z = event.values[2];
            break;
        }
    }

    @Override public void onAccuracyChanged(Sensor arg0, int arg1) {
    }

    @Override public void onResume() {
        super.onResume();
        drawView.resume();

        //enable the sensor
        sensors.registerListener(this, gravity,
            SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override public void onPause() {
        super.onPause();
        drawView.pause();

        //disable the sensor
        sensors.unregisterListener(this);
    }

    public class DrawView extends SurfaceView implements Runnable
    {
        Thread gameloop = null;
        SurfaceHolder surface = null;
        volatile boolean running = false;
        AssetManager assets = null;
        Paint paint = new Paint();
        Float2 center = new Float2(0,0);
        Float2 screenCoords = new Float2(0,0);


        //constructor method
        public DrawView(Context context) {
            super(context);
            surface = getHolder();
            assets = context.getAssets();
        }

        public void resume() {
            running = true;
            gameloop = new Thread(this);
            gameloop.start();
        }

        public void pause() {
            running = false;
            while (true) {
                try {
                    gameloop.join();
                }
                catch (InterruptedException e) { }
            }
        }

        //thread run method
        @Override public void run() {

            while (running) {
                if (!surface.getSurface().isValid()) continue;

                //open the canvas for drawing
                Canvas canvas = surface.lockCanvas();

                //erase the text with a big gray box
                paint.setColor(Color.GRAY);
                paint.setStyle(Style.FILL_AND_STROKE);
                canvas.drawRect(new Rect(0,0,300,350), paint);

                //calculate center of screen
                center.x = canvas.getWidth()/2;
                center.y = canvas.getHeight()/2;

                //draw a plain white circle to show the boundary of the data
                paint.setColor(Color.WHITE);
                paint.setStyle(Style.STROKE);
                canvas.drawCircle(center.x, center.y, 200, paint);

                //display sensor info
                int y = 20;
                paint.setColor(Color.WHITE);
                paint.setTextSize(24);
                canvas.drawText(title, 10, y, paint); y+=30;
                canvas.drawText("Vendor: " + gravity.getVendor(),
                    10, y, paint); y+=30;
                canvas.drawText("Model: " + gravity.getName(),
                    10, y, paint); y+=30;
                canvas.drawText("Max range: " + gravity.getMaximumRange(),
                    10, y, paint); y+=30;
                canvas.drawText("Resolution: " + gravity.getResolution(),
                    10, y, paint); y+=30;
                canvas.drawText("Version: " + gravity.getVersion(),
                    10, y, paint); y+=30;

                //draw raw gravity vector data to the screen with green circles
                //(not as useful as the mapped coords but still interesting)
                paint.setColor(Color.GREEN);
                canvas.drawPoint(center.x + data.x*20, center.y + data.y*20,
                    paint);
                canvas.drawText("X: " + toString(round(data.x,3)), 10, y,
                    paint); y+=30;
                canvas.drawText("Y: " + toString(round(data.y,3)), 10, y,
                    paint); y+=30;
                canvas.drawText("Z: " + toString(round(data.z,3)), 10, y,
                    paint); y+=30;

                //convert 3D vector to 2D screen space
                screenCoords.x = data.x / data.z;
                screenCoords.y = data.y / data.z;

                //draw blue circle for vector mapped to screen
                paint.setColor(Color.BLUE);
                canvas.drawCircle(center.x + screenCoords.x, center.y +
                    screenCoords.y, 5, paint);
                canvas.drawText("Vector: " + toString(screenCoords),
                    10, y, paint); y+=30;

                //close the canvas
                surface.unlockCanvasAndPost(canvas);

                try {
                    Thread.sleep(20);
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * Helper methods section (reusable code)
         */
        public String toString(int value) {
            return Integer.toString(value);
        }

        public String toString(float value) {
            return Float.toString(value);
        }

        public String toString(double value) {
            return Double.toString(value);
        }

        public String toString(Float2 value) {
            String s = "X:" + round(value.x) + "," +
                "Y:" + round(value.y);
            return s;
        }

        public String toString(Float3 value) {
            String s = "X:" + round(value.x) + "," +
                "Y:" + round(value.y) + "," +
                "Z:" + round(value.z);
            return s;
        }

        //round to a default 2 decimal places
        public double round(double value) {
            return round(value,2);
        }

        //round a number to any number of decimal places
        public double round(double value, int precision) {
            try {
                BigDecimal bd = new BigDecimal(value);
                BigDecimal rounded = bd.setScale(precision,
                    BigDecimal.ROUND_HALF_UP);
                return rounded.doubleValue();
            }
            catch (Exception e) {
                Log.d("round",e.getMessage());
            }
            return 0;
        }
        /**
         * End of helper methods section
         */
    }
}

Using the Pressure Sensor

The pressure sensor is one of the minor sensors in the Android hardware spec that is not always implemented in every Android device. This is a “hit or miss” sensor that may be included only in specialized devices or high-end models. If there is a waterproof Android camera or watch out there, it would be really fun to write an app that reports underwater pressure!

The pressure sensor returns the atmospheric pressure level in millibars (shorthand: hPa). Only a single value is returned in values[0]. Because this sensor is often not available, we have to ensure that our code handles a null object favorably rather than allowing an exception error to occur.

Initializing the Pressure Sensor

To access the pressure sensor, declare a Sensor variable and initialize it using Sensor.TYPE_PRESSURE, being sure to check for null in the process.

Sensor pressure=null;
if (pressure==null) {
    title = "ERROR: NO PRESSURE SENSOR WAS FOUND";
    Log.d("Sensor Error","No pressure sensor was found");
}

Reading the Pressure Sensor

Accessing the pressure sensor change events in onSensorChanged() involves only looking at the values[0] array index.

float pressValue=0;
@Override public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
    case Sensor.TYPE_PRESSURE:
        pressValue = event.values[0];
        break;
    }
}


By the Way

If you have a particular interest in a certain sensor and want more thorough information, it can be difficult to find the appropriate Android doc with details about specific sensors. Here is the URL to the SensorEvent class that you’ll want to peruse: http://developer.android.com/reference/android/hardware/SensorEvent.html.


Summary

This hour explained how the gravity and pressure sensors work and how to read them. There are still quite a few sensors remaining to be studied, which will be the focus of the next two hours. You will have a good grasp of the most significant Android sensors after completing these hours and will be able to use them for some of the more esoteric or specialized game design ideas.

Q&A

Q. Why do you suppose the gravity sensor returns a 3D vector instead of just a gravity weight value? How could this vector be useful?

A. Answers will vary.

Q. Some sensors, such as proximity and pressure, are not often included in the majority of Android devices. Why do you suppose some sensors are more useful than others, and why might a manufacturer choose one over another?

A. Answers should focus on the use of multisensor chips that include some, but not all, sensor devices.

Workshop

Quiz

1. Which SensorEvent properties are read to get input from the gravity sensor?

2. How do you convert a 3D vector into a 2D screen coordinate?

3. What is the approximate gravity of the Earth in meters per second squared?

Answers

1. values[0], values[1], and values[2]

2. By dividing X and Y each by Z.

3. 9.8

Activities

Modify the gravity demo program so that it displays the current gravity value as a percentage of one of the defined gravity constants (such as GRAVITY_JUPITER).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.234.28