I have used the following code to calculate the distance when the device moved from one place to another.Is it correct or not please look at my code.
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
NSDate *now = [NSDate date];
NSTimeInterval intervalDate = [now timeIntervalSinceDate:now_prev];
sx = acceleration.x * kFilteringFactor + sx * (1.0 - kFilteringFactor);
sy = acceleration.y * kFilteringFactor + sy * (1.0 - kFilteringFactor);
sz = acceleration.z * kFilteringFactor + sz * (1.0 - kFilteringFactor);
[xLabel setText:[NSString stringWithFormat:#"%.2f",sx]];
[yLabel setText:[NSString stringWithFormat:#"%.2f",sy]];
[zLabel setText:[NSString stringWithFormat:#"%.2f",sz]];
float aValue = sqrtf(sx*sx+sy*sy+sz*sz);
[gLabel setText:[NSString stringWithFormat:#"%.2f g",aValue]];
velX += (sx * intervalDate);
distX += (velX * intervalDate);
velY += (sy * intervalDate);
distY += (velY * intervalDate);
velZ += (sz * intervalDate);
distZ += (velZ * intervalDate);
float distance = sqrtf(distX*distX+distY*distY+distZ*distZ);
[distanceLabel setText:[NSString stringWithFormat:#"%.2f",distance*0.0006213f]];
now_prev = [now retain];
}
There are a lot of reasons why trying to get distance from accelerator data is going to be terribly inefficient.
In order to keep track of your initial location, you need to continuously integrate all acceleration values into a vector, and preferably at a very high sampling rate (big gaps in your calculations will lead to inaccuracies)
The method itself isn't terribly hard to figure out (or google for that matter) ;
This question explains how to do the maths and pseudocode for such an integration approach
Related
Am developing an application to calculate the steps of the human while walking,
I did this using accelerometer but I found that it's not accurate.
So after googling this, I found that using GyroScope sensor is more accurate than the accelerometer.
Any help with a control that might do this as I didn't ever develop using GyroScope, Or a document to read more about GyroScope Sensor.
Thanks in advance for help.
-(void)addAcceleration:(UIAcceleration*)accel
{
x = accel.x;
y = accel.y;
z = accel.z;
}
-(NSString*)name
{
return #"You should not see this";
}
#end
#define kAccelerometerMinStep 5.02
#define kAccelerometerNoiseAttenuation 1.0
double Norm(double x, double y, double z)
{
return sqrt(x * x + y * y + z * z);
}
double Clamp(double v, double min, double max)
{
if(v > max)
return max;
else if(v < min)
return min;
else
return v;
}
// See http://en.wikipedia.org/wiki/Low-pass_filter for details low pass filtering
#implementation LowpassFilter
-(id)initWithSampleRate:(double)rate cutoffFrequency:(double)freq
{
self = [super init];
if(self != nil)
{
double dt = 1.0 / rate;
double RC = 1.0 / freq;
filterConstant = dt / (dt + RC);
}
return self;
}
-(void)addAcceleration:(UIAcceleration*)accel
{
double alpha = filterConstant;
if(adaptive)
{
double d = Clamp(fabs(Norm(x, y, z) - Norm(accel.x, accel.y, accel.z)) / kAccelerometerMinStep - 1.0, 0.0, 1.0);
alpha = (1.0 - d) * filterConstant / kAccelerometerNoiseAttenuation + d * filterConstant;
}
x = accel.x * alpha + x * (1.0 - alpha);
y = accel.y * alpha + y * (1.0 - alpha);
z = accel.z * alpha + z * (1.0 - alpha);
}
-(NSString*)name
{
return adaptive ? #"Adaptive Lowpass Filter" : #"Lowpass Filter";
}
#end
// See http://en.wikipedia.org/wiki/High-pass_filter for details on high pass filtering
#implementation HighpassFilter
-(id)initWithSampleRate:(double)rate cutoffFrequency:(double)freq
{
self = [super init];
if(self != nil)
{
double dt = 1.0 / rate;
double RC = 1.0 / freq;
filterConstant = RC / (dt + RC);
}
return self;
}
-(void)addAcceleration:(UIAcceleration*)accel
{
double alpha = filterConstant;
if(adaptive)
{
double d = Clamp(fabs(Norm(x, y, z) - Norm(accel.x, accel.y, accel.z)) / kAccelerometerMinStep - 1.0, 0.0, 1.0);
alpha = d * filterConstant / kAccelerometerNoiseAttenuation + (1.0 - d) * filterConstant;
}
x = alpha * (x + accel.x - lastX);
y = alpha * (y + accel.y - lastY);
z = alpha * (z + accel.z - lastZ);
lastX = accel.x;
lastY = accel.y;
lastZ = accel.z;
}
-(NSString*)name
{
return adaptive ? #"Adaptive Highpass Filter" : #"Highpass Filter";
}
Use CoreMotion FrameWork.
#import <CoreMotion/CoreMotion.h>
self.motionManager = [[CMMotionManager alloc] init];
//Gyroscope
if([self.motionManager isGyroAvailable])
{
/* Start the gyroscope if it is not active already */
if([self.motionManager isGyroActive] == NO)
{
/* Update us 2 times a second */
[self.motionManager setGyroUpdateInterval:1.0f / 2.0f];
/* Add on a handler block object */
/* Receive the gyroscope data on this block */
[self.motionManager startGyroUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMGyroData *gyroData, NSError *error)
{
NSString *x = [[NSString alloc] initWithFormat:#"%.02f",gyroData.rotationRate.x];
self.gyro_xaxis.text = x;
NSString *y = [[NSString alloc] initWithFormat:#"%.02f",gyroData.rotationRate.y];
self.gyro_yaxis.text = y;
NSString *z = [[NSString alloc] initWithFormat:#"%.02f",gyroData.rotationRate.z];
self.gyro_zaxis.text = z;
}];
}
}
else
{
NSLog(#"Gyroscope not Available!");
}
Read this: http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/Introduction/Introduction.html
Need to use two classes: CMGyrodata and CMMotionManager
Related to your question: How to count steps using an Accelerometer?
Read that, will be a good starting point...
Hope this helps...
On an Android device, I was able to use the accelerometer to count steps without much problem, see my answer here.
I will like to measure the sound volume of the surrounding, not too sure if I am doing the right thing.
I will like to create a VU meter of a range of 0(quiet) to 120(very noisy).
I gotten the Peak and Avg power but are very high in normal quiet environment.
Do give me some pointer.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//creating an audio CAF file in the temporary directory, this isn’t ideal but it’s the only way to get this class functioning (the temporary directory is erased once the app quits). Here we also specifying a sample rate of 44.1kHz (which is capable of representing 22 kHz of sound frequencies according to the Nyquist theorem), and 1 channel (we do not need stereo to measure noise).
NSDictionary* recorderSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt:44100],AVSampleRateKey,
[NSNumber numberWithInt:1],AVNumberOfChannelsKey,
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
nil];
NSError* error;
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recorderSettings error:&error];
//enable measuring
//tell the recorder to start recording:
[recorder record];
if (recorder) {
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: #selector(levelTimerCallback:) userInfo: nil repeats: YES];
} else
{
NSLog(#"%#",[error description]);
}
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
NSLog(#"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);
float tavgPow =[recorder averagePowerForChannel:0] + 120.0;
float tpPow = [recorder peakPowerForChannel:0] + 120.0;
float avgPow = tavgPow;//(float)abs([recorder averagePowerForChannel:0]);
float pPow = tpPow;//(float)abs([recorder peakPowerForChannel:0]);
NSString *tempAvg = [NSString stringWithFormat:#"%0.2f",avgPow];
NSString *temppeak = [NSString stringWithFormat:#"%0.2f",pPow];
[avg setText:tempAvg];
[peak setText:temppeak];
NSLog(#"Average input: %f Peak input: %f Low pass results: %f", avgPow,pPow , lowPassResults);
}
Apple uses a lookup table in their SpeakHere sample that converts from dB to a linear value displayed on a level meter. This is to save device power (I guess).
I also needed this, but didn't think a couple of float calculations every 1/10s (my refresh rate) would cost so much device power. So, instead of building up a table I moulded their code into:
float level; // The linear 0.0 .. 1.0 value we need.
const float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room.
float decibels = [audioRecorder averagePowerForChannel:0];
if (decibels < minDecibels)
{
level = 0.0f;
}
else if (decibels >= 0.0f)
{
level = 1.0f;
}
else
{
float root = 2.0f;
float minAmp = powf(10.0f, 0.05f * minDecibels);
float inverseAmpRange = 1.0f / (1.0f - minAmp);
float amp = powf(10.0f, 0.05f * decibels);
float adjAmp = (amp - minAmp) * inverseAmpRange;
level = powf(adjAmp, 1.0f / root);
}
I'm using an AVAudioRecorder, hence you see getting the dB's with averagePowerForChannel:, but you can fill your own dB value there.
Apple's example used double calculations, which I don't understand because for audio metering float accuracy is more than sufficient, and costs less device power.
Needless to say, you can now scale this calculated level to your 0 .. 120 range with a simple level * 120.0f.
The above code can be sped up when we fix root at 2.0f, by replacing powf(adjAmp, 1.0f / root) with sqrtf(adjAmp); but that's a minor thing, and a very good compiler might be able to do this for us. And I'm almost sure that inverseAmpRange will be calculated once at compile-time.
The formula for converting a linear amplitude to decibels when you want to use 1.0 as your reference (for 0db), is
20 * log10(amp);
So I'm not sure about the intent from looking at your code, but you probably want
float db = 20 * log10([recorder averagePowerForChannel:0]);
This will go from -infinity at an amplitude of zero, to 0db at an amplitude of 1.
If you really need it to go up to between 0 and 120 you can add 120 and use a max function at zero.
So, after the above line:
db += 120;
db = db < 0 ? 0 : db;
The formula you are using appears to be the formula for converting DB to amp, which I think is the opposite of what you want.
Edit: I reread and it seems you may already have the decibel value.
If this is the case, just don't convert to amplitude and add 120.
So Change
double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));
to
double peakPowerForChannel = [recorder averagePowerForChannel:0];
and you should be okay to go.
Actually, the range of decibels is from -160 to 0, but it can go to positive values.(AVAudioRecorder Class Reference - averagePowerForChannel: method)
Then is better write db += 160; instead of db += 120;. Of course you can also put an offset to correct it.
I make a regression model to convert the mapping relation between the wav data generated from NSRecorder and the decibel data from NSRecorder.averagePowerForChannel
NSRecorder.averagePowerForChannel (dB) = -80+6 log2(wav_RMS)
Where wav_RMS is root mean square value of wav data in a short time, i.e. 0.1 sec.
Simply set your maximum and minimum value. Like you getting range of 0-120. If you want range of 0-60. Simply divide value to half to get the half range and so on..
I have posted something similar before and I got some suggestion which I have tried but I am still having problems...thus, will like to ask for any assistance on reviewing this code.
I am actually trying to get the ball object in the code to have a better or smoother animation and to increase in its speed constantly until it attains a maximum speed but right now what I am getting is the ball starting at the specified speed and then its movement becomes erratic as the speed increases and after a few second the ball just moves off the iphone simulator screen.
Can someone help me run this code(please add an image to represent the ball in IB) to see what I am not doing correctly.
Thanks in advance for your help.
.h
#interface ViewController : UIViewController
{
UIImageView *ball;
int speedX;
int speedY;
CGPoint ballMovement;
}
#property (nonatomic, retain) IBOutlet UILabel *scoreLabel;
#property (nonatomic, retain) IBOutlet UIImageView *ball;
- (void)initializeTimer;
- (void)animateBall:(NSTimer *)theTimer;
#end
.m
#implementation ViewController
#synthesize ball;
- (void)dealloc {
[ball release];
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
ball.center = CGPointMake(50,50);
speedX = 1;
speedY = 1;
ballMovement = CGPointMake(speedX,speedY);
[self initializeTimer];
}
- (void)initializeTimer {
float theInterval = 0.1f;
[NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:#selector(animateBall:) userInfo:nil repeats:YES];
}
- (void)animateBall:(NSTimer *)theTimer {
ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);
speedX += 1;
speedY += 1;
NSLog(#"%d,%d", speedX, speedY);
if (speedX <= 1)
speedX ==1;
if (speedY <= 1)
speedY ==1;
if(ball.center.x > 300 || ball.center.x < 20)
speedX = -abs(speedX);
if(ball.center.y > 440 || ball.center.y < 40)
speedY = -abs(speedY);
}
You are not capping the speed anywhere, so how is it supposed to reach a constant speed? From your code, once you pass ball.center.x > 300, you reverse the speed.
The rows
if (speedX <= 1) speedX ==1;
if (speedY <= 1) speedY ==1;
Do nothing, since they don't actually assign a value to speedX / speedY. It looks like this is what you mean:
if (speedX <= 1) speedX = 1;
if (speedY <= 1) speedY = 1;
Even so, this won't actually do what you want. If you want a max speed, then it should be something like this:
// Compact version
speedX = MIN(speedX + 1, TARGET_SPEED_X);
// More like your code:
speedX += 1;
if (speedX > TARGET_SPEED_X) speedX = TARGET_SPEED_X;
If you want the ball to bounce off the bottom, with gravity along the y axis:
- (void)animateBall:(NSTimer *)theTimer {
ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);
speedY += 1;
if(ball.center.x > 300) {
speedX = -abs(speedX);
}
if(ball.center.y > 440)
speedY = -abs(speedY);
}
The main cause of the erratic movement you see is probably because of the bounce off the top/side of the screen. For instance:
if(ball.center.y > 440 || ball.center.y < 40)
speedY = -abs(speedY);
What happens when the ball is speeding towards the top here? Say ball.center.y = 30, speedY = -4.
Your speedY will then stay at -4. Next pass it's -3, then -2, then -1, and then it'll oscillate between -1 and 0. The same happens for speedX eventually.
The question is what sort of movement you are looking for. Your original code had something that looked like constant acceleration in the (1, 1) direction, with a bounce effect in the right and bottom corner, but your description said you wanted acceleration to a maximum speed.
For an accelerating ball with a maximum speed:
#interface ViewController : UIViewController {
UIImageView *ball;
double speedX;
double speedY;
int maxSpeed;
double acceleration;
CGPoint ballMovement;
}
#property (nonatomic, retain) IBOutlet UIImageView *ball;
- (void)initializeTimer;
- (void)animateBall:(NSTimer *)theTimer;
#end
#implementation ViewController
#synthesize ball;
- (void)dealloc {
[ball release];
[super dealloc];
}
- (void)viewDidLoad {
[super viewDidLoad];
ball.center = CGPointMake(50,50);
speedX = 1;
speedY = 1;
maxSpeed = 20;
acceleration = 1;
ballMovement = CGPointMake(speedX,speedY);
[self initializeTimer];
}
- (void)initializeTimer {
float theInterval = 0.1f;
[NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:#selector(animateBall:) userInfo:nil repeats:YES];
}
- (void)animateBall:(NSTimer *)theTimer {
ball.center = CGPointMake(ball.center.x + speedX, ball.center.y + speedY);
// Discover the actual speed.
double actualSpeed = sqrt(speedX * speedX + speedY * speedY);
// Increase the speed by the acceleration (actually acceleration * time interval),
// Cap at max speed
double newSpeed = MIN(acceleration + actualSpeed, maxSpeed);
// Increase velocity uniformly along both x and y axis
// (This example does not properly handle when speedX and speedY both start at zero)
speedX = speedX * newSpeed / actualSpeed;
speedY = speedY * newSpeed / actualSpeed;
// Make sure to bounce
if (ball.center.x > 300) speedX = -abs(speedX);
if (ball.center.x < 20) speedX = abs(speedX);
if (ball.center.y > 440) speedY = -abs(speedY);
if (ball.center.y < 40) speedY = abs(speedY);
}
#end
Acceleration = Speed / Delta_time
So you have to modify the code:
- (void)animateBall:(NSTimer *)theTimer {
ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);
static float acceleration = 1.0f;
speedX += acceleration*theTimer.timeInterval;
speedY += acceleration*theTimer.timeInterval;
NSLog(#"%d,%d", speedX, speedY);
if (speedX <= 1)
speedX ==1;
if (speedY <= 1)
speedY ==1;
if(ball.center.x > 300 || ball.center.x < 20)
speedX = -abs(speedX);
if(ball.center.y > 440 || ball.center.y < 40)
speedY = -abs(speedY);
}
I have a UIImageView setup called needleImageView which i need to rotate 360degrees over 10 seconds the point 20.00 20.00 of the ImageView.
Anybody able to show me sample code for this functionality?
Thanks,
-Code
Here is the logic... Try implementing.
On starting timer
//in header file
fireInterval = 10;
mStartingAngle = 0;
mEndingAngle = 360;
//Implementation
-(void) startTimer
{
mPreviousTime = [NSDate timeIntervalSinceReferenceDate];
}
In the loop
-(void) updateFunction
{
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
//NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
//Mapping values between mStartAngle and mEndAngle
mCurrentAngle = (((timeNow - mPreviousTime) * (mEndingAngle - mStartingAngle)) / (previousTime+fireInterval - mPreviousTime)) + mStartingAngle;
if( mPreviousTime + fireInterval <= timeNow )
{
NSLog(#"10 seconds completed");
mPreviousTime = timeNow;
}
}
I'm having a problem with getDistanceFrom and distanceFromLocation. getDistanceFrom is deprecated iOS 4.1 and distanceFromLocation is not available in 3.1.3, how do I get around this problem.
Has anyone else come across this problem ?
My code at the moment is:
CLLocationDistance kilometers;
if ([currentLocation respondsToSelector: #selector(distanceFromLocation:)])
{
kilometers = [currentLocation distanceFromLocation:previousLocation] / 1000;
}
else
{
//kilometers = [currentLocation getDistanceFrom:previousLocation] / 1000;
kilometers = [currentLocation performSelector: #selector(getDistanceFrom:) withObject: previousLocation] / 1000;
}
Depending on which iOS I compile with I'm getting 'invalid operands to binary' on the lines:
kilometers = [currentLocation distanceFromLocation:previousLocation] / 1000;
kilometers = [currentLocation performSelector: #selector(getDistanceFrom:) withObject: previousLocation] / 1000;
Regards,
Stephen
There's a blog post by Cédric Luthi which has a pretty good solution to this.
In short, you need to enter the following into your main.m and make sure to #import <objc/runtime.h> :
Method getDistanceFrom = class_getInstanceMethod([CLLocation class], #selector(getDistanceFrom:));
class_addMethod([CLLocation class], #selector(distanceFromLocation:), method_getImplementation(getDistanceFrom), method_getTypeEncoding(getDistanceFrom));
You can then use distanceFromLocation: on any OS.
I use my own method. It is MUCH faster and can be used on both iOS 3.x and iOS 4.x.
- (double)distanceFrom:(CLLocationCoordinate2D)locationA to:(CLLocationCoordinate2D)locationB
{
double R = 6368500.0; // in meters
double lat1 = locationA.latitude*M_PI/180.0;
double lon1 = locationA.longitude*M_PI/180.0;
double lat2 = locationB.latitude*M_PI/180.0;
double lon2 = locationB.longitude*M_PI/180.0;
return acos(sin(lat1) * sin(lat2) +
cos(lat1) * cos(lat2) *
cos(lon2 - lon1)) * R;
}