Why am I getting 0 degrees from magneticField property the whole time? - iphone

I'm trying to get the device's declination from the magnetic North in degrees, by relying solely on the device's magnetometer. This is the code I've written but I just get 0 degrees.. What am I doing wrong ?
CMMotionManager *motionManager;
motionManager = [[CMMotionManager alloc] init];
[motionManager startDeviceMotionUpdates];
CMDeviceMotion *deviceMotion;
deviceMotion = [[CMDeviceMotion alloc] init];
while(!self.stopButtonPressed)
{
double x = motionManager.deviceMotion.magneticField.field.x;
double y = motionManager.deviceMotion.magneticField.field.y;
double degrees = asin(y/sqrt(pow(x, 2.0) + pow(y, 2.0))) * 180.0 / M_PI ;
int degreesRounded = (int)degrees;
NSLog(#"Degrees : %i", degreesRounded);
}

It is likely that the simulator doesn't return normal values for these methods, so you will need to test on a real device.
The CLLocationManager's method, didUpdateHeading: doesn't work on the simulator, so you are probably experiencing something similar here.
Edit:
From the docs:
"The latest sample of device-motion data. (read-only)
#property(readonly) CMDeviceMotion *deviceMotion
Discussion
If no device-motion data is available, the value of this property is nil. An application that is receiving device-motion data after calling startDeviceMotionUpdates periodically checks the value of this property and processes the device-motion data."
Check to see if that property of your motion manager is nil. If it is, then you would get 0 for the magnetic field property.
Edit 2:
Instead of using startDeviceMotionUpdates, you should be using startMagnetometerUpdatesToQueue:. The docs say this:
"Magnetometer. Set the magnetometerUpdateInterval property to specify an update interval. Call the startMagnetometerUpdatesToQueue:withHandler: method, passing a block of type CMMagnetometerHandler. Magnetic-field data is passed into the block as CMMagnetometerData objects."
Docs are here.

What thread is the above code running on? If you're running it on the main thread, you probably won't get see updates to the device motion data over time. Use an NSTimer or similar mechanism to sample the motion over time, so the main thread is free to do other things (like service requests from the core motion sub-system).

Here is what I tested on a real device:
CMMotionManager *myMotionManager= [[CMMotionManager alloc] init];
myMotionManager.deviceMotionUpdateInterval = 1;
[myMotionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical toQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
double x = myMotionManager.deviceMotion.magneticField.field.x;
double y = myMotionManager.deviceMotion.magneticField.field.y;
double z = myMotionManager.deviceMotion.magneticField.field.z;
NSLog(#"Field.x= %f; Field.y = %f; Field.z= %f",x,y,z);
}];

Related

CCMotionManager, accelerometerData, gyroData, magnetometerData return nil

I'm working from Apple's Event Handling Guide for iOS. I'm using the pull method described near startAccelerometerUpdates. The pull method is fine for my purposes since I only need to read from the sensor once.
I'm getting a nil from the calls to accelerometerData, gyroData and magnetometerData in the code below on an iPhone 4, iPad Mini, and iPad Retina. isAccelerometerAvailable,isGyroAvailable, andisMagnetometerAvailableeach returnYES`. Two questions:
How does one determine the result from startAccelerometerUpdates and startGyroUpdates? They are void functions, so can I assume it can never fail? (Otherwise, it would throw or return a BOOL).
How does one get the last error from the call to accelerometerData, gyroData and magnetometerData?
EDIT: I had to call [NSThread sleepForTimeInterval:0.150f] to get data out of the sensor. Anything less and the sensor does not produce valid data.
The sleep adds a third question: how long does one need to pause to ensure the data arrives when the hardware is present?
Thanks in advance.
static CryptoPP::RandomPool pool;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
CryptoPP::SecByteBlock seed(32);
CryptoPP::OS_GenerateRandomBlock(true, seed.data(), seed.size());
pool.IncorporateEntropy(seed.data(), seed.size());
});
// First, send in all the uninitialized data. Then:
// sesnors[0,1,2] use accelerometer, if available
// sesnors[3,4,5] use gyroscope, if available
// sesnors[6,7,8] use magnetometer, if available
CryptoPP::SecBlock<double> sensors(3 * 3);
pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());
CMMotionManager* mgr = [[CMMotionManager alloc] init];
if(mgr) {
[mgr startAccelerometerUpdates];
[mgr startGyroUpdates];
[mgr startMagnetometerUpdates];
[NSThread sleepForTimeInterval:0.150f];
if([mgr isAccelerometerAvailable]) {
CMAccelerometerData* accelData = [mgr accelerometerData];
if(accelData) {
sensors[0] = [accelData acceleration].x;
sensors[1] = [accelData acceleration].y;
sensors[2] = [accelData acceleration].z;
}
}
if([mgr isGyroAvailable]) {
CMGyroData* gyroData = [mgr gyroData];
if(gyroData) {
sensors[3] = [gyroData rotationRate].x;
sensors[4] = [gyroData rotationRate].y;
sensors[5] = [gyroData rotationRate].z;
}
}
if([mgr isMagnetometerAvailable]) {
CMMagnetometerData* magnetData = [mgr magnetometerData];
if(magnetData) {
sensors[6] = [magnetData magneticField].x;
sensors[7] = [magnetData magneticField].y;
sensors[8] = [magnetData magneticField].z;
}
}
pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());
[mgr stopMagnetometerUpdates];
[mgr stopGyroUpdates];
[mgr stopAccelerometerUpdates];
[mgr release], mgr = nil;
}
When you do this:
mgr startAccelerometerUpdates];
[mgr setAccelerometerUpdateInterval:0.01f];
That means you will not start receiving information until 0.01 seconds later, and even then you'll only receive updates if the main thread is idle.
You are not waiting 0.01 seconds before checking for data, you're actually waiting about 0.00000000001 seconds. And I'm not sure, but I think your main thread might need to be idle even if you do wait longer.
I think you should go back to the documentation and have a look how you're supposed to read information from the sensors.
I recommend using blocks to process the motion data:
[mManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
// process data
}];
You could tell the accelerometer to stop immediately after receiving the first value, if you only want it once.

CLLocationManager heading issues on iPhone 5

I recently started testing my app on an iPhone 5, and to my alarm, it seems that the CLLocationManager doesn't really work! Although [CLLocationManager headingAvailable] is YES, I don't receive any heading updates at all. Strangely, on an iPhone 4, after 30 or so heading updates, locationManager:didUpdateToHeading: is no longer called. This issue is entirely new. The location manager also returns negative numbers for verticalAccuracy, so I'm assuming the altitude it is invalid. Here's how I'm creating the location manager:
CLLocationManager* locationManager = [[CLLocationManager alloc] init];
if([locationManager respondsToSelector:#selector(disallowDeferredLocationUpdates)]) {
[locationManager disallowDeferredLocationUpdates];
[locationManager setPausesLocationUpdatesAutomatically:NO];
}
locationManager.headingOrientation = CLDeviceOrientationFaceUp;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.headingFilter = -1;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[sharedSingleton setLocationManager:locationManager];
sharedSingleton is just my singleton class that handles some odds and ends, including holding onto a reference to the location manager.
If I need to post any more code let me know. I just don't know what might be causing this strange issue. Thanks!
You need to retain "locationManager" in memory somewhere, either as a property of your object or as an instance variable.
What I belive is happening is that you're creating your location manager, and then your method exits and "locationManager" falls out of scope and is magically released by ARC.
So, instead, do something like this:
in your #implementation:
#property (strong) CLLocationManager * locationManager;
and in your #interface:
self.locationManager = [[CLLocationManager alloc] init];
if([self.locationManager respondsToSelector:#selector(disallowDeferredLocationUpdates)]) {
[self.locationManager disallowDeferredLocationUpdates];
[self.locationManager setPausesLocationUpdatesAutomatically:NO];
}
self.locationManager.headingOrientation = CLDeviceOrientationFaceUp;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.headingFilter = -1;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
You could try a few things. First of all, I don't see a startUpdatingHeading call. Maybe you're doing it somewhere else. You should add the locationManager:didFailWithError: method to the delegate to check for errors, and try returning YES in locationManagerShouldDisplayHeadingCalibration: in case it's a calibration issue.
Seems the solution was obvious and I overlooked it. The delegate was being set to nil just moments after the location manager was started, which explains why on a slower device like the iPhone 4 a few updates were able to come through before the code setting the delegate to nil was run, but on the iPhone 5 it was instantaneous.

Using accelerometer for animation and output?

i'm new to xcode and objective c, so be indulgent :)
i wrote an app where i can move a small ball over the display using the accelerometer.
in addition i want to output the accelerometer data on the display.
the problem is, when i want to output the data the ball won't move.
thanks for help!
xLabel.text = [NSString stringWithFormat:#"x: %f.02", acceleration.x];
yLabel.text = [NSString stringWithFormat:#"y: %f.02", acceleration.y];
zLabel.text = [NSString stringWithFormat:#"z: %f.02", acceleration.z];
Here's the initializing code:
UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer]; //Share reference
accel.delegate = self;
accel.updateInterval = 1.0f / 60.0f;
That may be because you trying to both the tasks on main thread which is not possible simultaneously.
I think you can use Core animation (which uses its own thread to perform its operations) to move object instead of directly changing frame of the object (which requires main thread).

iPad can't find current location

I am displaying current location for iPad in map view for this i am using following code
if ([CLLocationManager locationServicesEnabled])
{
lm = [[CLLocationManager alloc] init];
lm.delegate = self;
lm.desiredAccuracy = kCLLocationAccuracyBest;
lm.distanceFilter = 100.0f;
[lm startUpdatingLocation];
}
and i am calculating lat and longitude and passing it the url as
NSString *urlString = [NSString stringWithFormat:#"http://maps.google.com/maps?z=15&daddr=%##%#,%#&saddr=%##%#,%#",n,lat_New,lng_New,currentLocation,currentLatt,currentLong];
I am not getting the correct direction for initial starting point and ending point for iPad in map view.
Is there anything alternative for showing correct initial and staring point in iPad map view?
Does app store rejects apps for such problems and Pleas suggest me some good alternative for this.
There are couple of things .One simulator cannot give you GPS locations.Secondly Usually gps coordinates returned first time may not be accurate.so try receiving location updates by implementing the delegate.

How to notice a quick forward motion with didAccelerate deprecated?

What i want to do is read the acceleration.y and do something like:
if (acceleration.y > 0.8) {
// Do something
}
As didAccelerate is deprecated I wonder how to get the y-value:
motionManager = [[[CMMotionManager alloc] init] autorelease];
motionManager.accelerometerUpdateInterval = kUpdateInterval;
if (motionManager.accelerometerAvailable) {
[motionManager startAccelerometerUpdates];
}
else {
//this device doesn't have accelerometer notice somewhere??
}
- (void)startAccelerometerUpdates {
// READ Y-VALUE?????
}
I want to use raw accelerometer data so the app also works on 3GS. Is it possible to read the Y-value?
EDIT: the answer below is deprecated, check these posts for the right way.
Old answer:
Use a UIAccelerometer singleton instance for this, for example in your AppDelegate
//in your launching method
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
//delegate method:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
// use the y-property of the acceleration
}
Look at the CMMotionManager class reference and search section named "Handing Motion Updates at Specified Intervals". In the "Accelerometer" bullet, it says
Set the accelerometerUpdateInterval property to specify an update interval. Call the startAccelerometerUpdatesToQueue:withHandler: method, passing in a block of type CMAccelerometerHandler. Accelerometer data is passed into the block as CMAccelerometerData objects.
CMAccelerometerData has a reference to CMAcceleration which is a struct that holds per axis acceleration.