Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

Magnetometer

A magnetometer is a sensor that is similar to a compass. A compass detects the horizontal component of the direction of a magnetic field. Magnetometers detect both the directional components and the strength of a magnetic field.

Magnetometers in BlackBerry devices are calibrated to sense the magentic field of the earth. They are designed to measure and remove interference caused by local magnetic fields so that only the magentic field of the earth is measured. You can test the calibration quality of a magnetometer and trigger a new calibration when necessary.

An obvious use of a magnetometer is to create a digital compass by creating a UI that presents the directional magnetometer data. Because a magnetometer includes additonal information about the magnetic field, such as the field strength, you can use it for many other purposes. For example, magnetometers are used in metal detectors, geological exploration tools, stud and pipe finders, and applications that require precise inclination measurements.

Magnetometer data

There are two ways to retrieve data from the magnetometer. You can create and register a listener, or you can poll for magnetometer data. In both cases, you create a MagnetometerSensor.Channel class that acts as the bridge between your application and the magnetometer. The data provided by the magnetometer is encapsulated in a MagnetometerData class.

The following table lists the types of information contained in a MagnetometerData object and the methods to retrieve that information.

Type of information

Methods

Time when data was captured

getTimeStamp()

Magnetometer calibration quality

getCalibrationQuality()

Magnetic field strength

getFieldStrength()

Angle between magnetic north and device axes

getDirection(), getDirectionBack(), getDirectionFront(), getDirectionLeft(), getDirectionRight(), getDirectionBottom(), getDirectionTop()

Angle between horizontal plane and magnetic field vector (dip angle)

getInclination()

OpenGL-compatible rotation matrix that represents a rotation from world coordinates to current device orientation

getRotationMatrix()

Raw magnetometer data (┬ÁT)

getMagnetometerData()

Here's a code sample that retrieves magnetometer data with a listener:

import net.rim.device.api.system.MagnetometerChannelConfig;
import net.rim.device.api.system.MagnetometerData;
import net.rim.device.api.system.MagnetometerListener;
import net.rim.device.api.system.MagnetometerSensor;
import net.rim.device.api.system.MagnetometerSensor.Channel;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.UiApplication;

//Create the application framework by extending the UiApplication class.
//In main(), create an instance of the new application class and invoke 
//enterEventDispatcher() to enable the application to receive events. 
//In the application constructor, invoke pushScreen() to display the 
//custom screen for the application. (The DataScreen class represents 
//the custom screen.)
public class MagListener extends UiApplication 
{
    private DataScreen _scrData;
    
    public static void main(String[] args)
    {
        MagListener app = new MagListener();
        app.enterEventDispatcher();
    }
    
    public MagListener()
    {
        _scrData = new DataScreen();
        pushScreen(_scrData);
    }
    
}

//Indicate that the application class implements the 
//MagnetometerListener interface. Provide an implementation of the 
//onData() method as specified by the interface. Invoke 
//displayData() to display the magnetometer data on the screen.
//In the application constructor, invoke openChannel() to create a 
//Channel object that provides a bridge between your app and the 
//magnetometer over which data can flow. Invoke addMagnetometerListener()
//to add your application class as a magnetometer listener, which causes 
//onData() to be called periodically with the latest 
//MagnetometerData objects.
public class MagListener extends UiApplication 
implements MagnetometerListener
{
    private DataScreen _scrData;
    
    public static void main(String[] args)
    {
        MagListener app = new MagListener();
        app.enterEventDispatcher();
    }
    
    public MagListener()
    {
        _scrData = new DataScreen();
        pushScreen(_scrData);

        Channel  magChannel = MagnetometerSensor.openChannel(this);
        magChannel.addMagnetometerListener(this);
    }

    public void onData(MagnetometerData magData)
    {
        _scrData.displayData(magData);
    }}

//Create the custom screen for the application by extending the 
//MainScreen class. In the screen constructor, create a 
//LabelField and add it to the screen. Provide an implementation of 
//displayData() to retrieve the magnetic field strength from incoming 
//MagnetometerData objects and display the result by setting the text 
//of the LabelField.
class DataScreen extends MainScreen
{
    LabelField _labelMagStrength;
    
    public DataScreen()
    {
        _labelMagStrength = new LabelField("No data yet.");
        add(_labelMagStrength);
    }
    
    void displayData(MagnetometerData magData)
    {
        _labelMagStrength.setText(Float.toString(magData.getFieldStrength()));
    }
    
}

Here's a code sample that retrieves magnetometer data with polling:

Polling too frequently can significantly reduce a smartphone's battery power level.

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.system.Application;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.system.MagnetometerListener;
import net.rim.device.api.system.MagnetometerChannelConfig;
import net.rim.device.api.system.MagnetometerData;
import net.rim.device.api.system.MagnetometerSensor;
import net.rim.device.api.system.MagnetometerSensor.Channel;
import net.rim.device.api.ui.component.Dialog;

//Create the application framework by extending the UiApplication class.
public class MagPoll extends UiApplication implements Runnable
{
    private DataScreen _scrData;
    private boolean bRunning = true;
    
//In main(), create an instance of the new application class and invoke
//enterEventDispatcher() to enable the application to receive events.
//In the application constructor, invoke pushScreen() to display the 
//custom screen for the application.
    public static void main(String[] args)
    {
        MagPoll app = new MagPoll();
        app.enterEventDispatcher();
    }
    
    public MagPoll()
//The DataScreen class represents the custom screen.
    {
        _scrData = new DataScreen();
        pushScreen(_scrData);
        Thread poller = new Thread(this);
        poller.start();
    }
    
    public void run()
    {
     Channel magChannel = MagnetometerSensor.openChannel(
                Application.getApplication());
     
     while( bRunning ) 
					{
         double direction = 0;
         MagnetometerData data = magChannel.getData();
         synchronized( data ) 
         {
             direction = data.getDirection(
                MagnetometerData.MAGNETOMETER_GET_DIRECTION_TOP);
         }    
         _scrData.displayData(direction);

         try
         {
            Thread.sleep( 500 );
         }
         catch(Exception e)
         {
             //handle exception
         }
     }
     magChannel.close();
    }
}

class DataScreen extends MainScreen
{
    LabelField _labelMagDir;
    
    public DataScreen()
    {
        _labelMagDir = new LabelField("No data yet.");
        add(_labelMagDir);
    }
    
    void displayData(double direction)
    {
        synchronized(UiApplication.getUiApplication().getEventLock())
        {
            _labelMagDir.setText(Double.toString(direction));
        }
    } 
}

Magnetometer coordinate systems

When you work with the magnetometer, you actively use two coordinate systems.

Device coordinate system

The device coordinate system corresponds to the orientation of a BlackBerry device. In the device coordinate system, the X-axis is parallel to the width dimension of the BlackBerry device display. The Y-axis is parallel to the height dimension of the BlackBerry device display. The Z-axis is parallel to the thickness (front to back) of the BlackBerry device.


Diagram illustrating device coordinate system

World coordinate system

The world coordinate system corresponds to the magnetic field of the earth. In the world coordinate system, the Z-axis is normal to the surface of the earth and points towards the sky. The Y-axis is a tangent to the surface of the earth and points towards the magnetic north pole. The X-axis is a tangent to the surface of the earth and is orthogonal to both the Z-axis and Y-axis. The X-axis points appropriately east.

Diagram illustrating magnetic world coordinate system

Checking if a device has a magnetometer

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.system.MagnetometerSensor;

public class MagCheck extends UiApplication
{ 
   public static void main(String[] args)
	  { 
		     MagCheck app = new MagCheck();
		     app.enterEventDispatcher();
	  } 

	  public MagCheck() 
	  { 
		     pushScreen(new OutputScreen());
	  }
}

class OutputScreen extends MainScreen
{ 
	  LabelField _labelMsg = new LabelField("Not set yet");

	  public OutputScreen()

//Call the static isSupported() method and check the returned boolean value.      
	  { 
		     if(MagnetometerSensor.isSupported()) 
		     { 
			        _labelMsg.setText("Magnetometer is supported.");
		     }
		     else
		     {
			        _labelMsg.setText("Magnetometer is not supported.");
		     }
		     add(_labelMsg);
	  }
}

Configuring and retrieving magnetometer sampling frequency

The default magnetometer sensor sampling rate is 20Hz. You can specify a preferred sampling rate by creating a MagChannelConfig and specifying the sampling rate you want. You pass the MagChannelConfig as a parameter when you create the Channel. You have to balance the need for timely data against the increased power requirements of a higher sampling rate. There is no guarantee that you will get your suggested sampling rate.

  1. Retain a reference to the Channel object.

  2. Invoke Channel.getConfig() to retrieve the MagnetometerChannelConfig object.

  3. Invoke MagnetometerChannelConfig.getFrequency() to retrieve the magnetometer sampling frequency.

    import net.rim.device.api.ui.UiApplication;
    import net.rim.device.api.ui.container.MainScreen;
    import net.rim.device.api.ui.component.*;
    import net.rim.device.api.system.capability.DeviceCapability;
    import net.rim.device.api.system.MagnetometerListener;
    import net.rim.device.api.system.MagnetometerChannelConfig;
    import net.rim.device.api.system.MagnetometerData;
    import net.rim.device.api.system.MagnetometerSensor;
    import net.rim.device.api.system.MagnetometerSensor.*;
    
    public class MagChannelConfig extends UiApplication
    {  
        private Channel _magChannel;
        
        public static void main(String[] args)
        {
            MagChannelConfig app = new MagChannelConfig();
            app.enterEventDispatcher();
        }
        
        public MagChannelConfig()
        {
            int freq = 20; // (Hz)
            MagnetometerChannelConfig magChannelConfig = new MagnetometerChannelConfig(freq);
            _magChannel = MagnetometerSensor.openChannel(this,magChannelConfig);
            Dialog.alert(Integer.toString(_magChannel.getConfig().getFrequency()));
            _magChannel.close();
        }
    }

Creating and registering a magnetometer listener

import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.system.MagnetometerListener;
import net.rim.device.api.system.MagnetometerChannelConfig;
import net.rim.device.api.system.MagnetometerData;
import net.rim.device.api.system.MagnetometerSensor;
import net.rim.device.api.system.MagnetometerSensor.*;


public class MagListener extends UiApplication implements MagnetometerListener
{
    private DataScreen _scrData;
    
    public static void main(String[] args)
    {
        MagListener app = new MagListener();
        app.enterEventDispatcher();
    }
    
    public MagListener()
    {
        _scrData = new DataScreen();
        pushScreen(_scrData);
        
        Channel  magChannel = MagnetometerSensor.openChannel(this);
        magChannel.addMagnetometerListener(this);
    }
    
    public void onData(MagnetometerData magData)
    {
        _scrData.displayData(magData);
    }
}



class DataScreen extends MainScreen
{
    LabelField _labelMagStrength;
    
    public DataScreen()
    {
        _labelMagStrength = new LabelField("No data yet.");
        add(_labelMagStrength);
    }
    
    void displayData(MagnetometerData magData)
    {
        _labelMagStrength.setText(Float.toString(magData.getFieldStrength()));
    }
    
}