I am trying to create a flight using the DJI SDK via Virtual Sticks. I've worked out how to fly the drone in the direction of a GPS coordinate using the atan2 function to calculate the angle between two GPS Coordinates, then Yaw the drone to that angle and pitch to move in that direction.
I want to re-calculate that compass bearing, yaw and pitch every couple of seconds to account for wind and drift etc but I don't want to run it on the main thread in case it blocks UI. Or a UI interaction causes the timer not to fire and there is a missed calculation.
Should I use dispatch_source_set_timer in conjunction with GCD or is there a better method to achieve this and avoid memory leaks? Sample code below:
code sample taken from another question answer
// Create a dispatch source that'll act as a timer on the concurrent queue
dispatch_source_t dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
// Setup params for creation of a recurring timer
double interval = 2.0;
dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 0);
uint64_t intervalTime = (int64_t)(interval * NSEC_PER_SEC);
dispatch_source_set_timer(dispatchSource, startTime, intervalTime, 0);
// Attach the block you want to run on the timer fire
dispatch_source_set_event_handler(dispatchSource, ^{
// code to calculate bearing and send Virtual Stick commands to yaw and pitch drone
});
// Start the timer
dispatch_resume(dispatchSource);
// ----
// When you want to stop the timer, you need to suspend the source
dispatch_suspend(dispatchSource);
// If on iOS5 and/or using MRC, you'll need to release the source too
dispatch_release(dispatchSource);
Related
Having a rough go here with scheduling disk reads via GCD.
Below is a code snippet to load frames from a file that contains about frameCount=1000 frames. In my initial implementation, I did this from the main thread:
[self readFramesFromFrame:0 toFrame:frameCount];
And here's my method:
-(BOOL)readFramesFromFrame:(NSInteger)startFrame toFrame:(NSInteger)endFrame
{
if (frameCount<=0)
return YES;
__block BOOL endRead;
dispatch_async(diskQueue, ^{
do {
dispatch_async(frameQueue, ^{
for (NSInteger i=startFrame; i<endFrame; i++)
[self readFileFrame:i];
});
// ** BEGIN get next batch
NSInteger newStart = endFrame;
NSInteger newEnd = ((endFrame+highWater) < frameCount) ? endFrame+highWater : frameCount;
if (newStart==frameCount)
endRead=YES;
else
endRead=[self readFramesFromFrame:(NSInteger)newStart toFrame:(NSInteger)newEnd];
// ** END get next batch
} while (!endRead);
});
return YES;
}
However, I don't want to load up the initial run with 1000 frames as it takes too long.
I initially want to only load 20 frames (my highwater amount), so I rejigged the code and made an revised call of:
[self readFramesFromFrame:0 toFrame:(frameCount<highWater) ? frameCount : highWater];
But this still takes too long before I get access to the first frame for processing. I am trying to schedule separate blocks of work rather than one large block of work, but I realize I am still effectively scheduling all frames. No improvement.
Two points of explanation. Firstly, my call to [self readFileFrame:frameNumber] does a dispatch_io_read using a readQueue, that is throttled elsewhere by my downstream processing handlers by calling either dispatch_suspend(readQueue) or dispatch_resume(readQueue). I use a low-water value of 10 frames and a high-water value of 20 frames which suspend/resume the readQueue as appropriate. That is working swimmingly well, but is currently predicated upon a reasonably stuff queue of frames.
Secondly, my call to readFileFrame will produce a valid frame of data via the readQueue thread that is accessed (and displayed) via a separate GCD timer.
I have tried dispatching the code snippet between the comments on "next batch" back on the main queue, but that is a disaster also. I thought that adding an additional wrapping serial private queue frameQueue would help, but no.
If I pretend that frameCount is, say, 50 frames, things work very quickly and swimmingly well -- but I only get 50 frames and no more.
How do I rejig this code snippet so that it lazily reads in batches of frames?
sBefore UIAccelerometer was deprecated from iOS I used the data from x, y and z from this class to calculate pitch, roll and yaw. I also had to do some filtering, but now I see that with the CoreMotion library I can get these data from the CMAttitude class and would really like to use these properties, but somehow I fail to do so.
Now, what I have done is to instantiate
CMMotionManager *motionManager;
CMDeviceMotion *deviceMotion;
CMAttitude *attitude;
...
deviceMotion = motionManager.deviceMotion;
attitude = deviceMotion.attitude;
motionManager.accelerometerUpdateInterval = 0.065; // 65ms
[motionManager startAccelerometerUpdates];
I am able to read x,y and z from motionManager.accelerometerData.acceleration.<x,y or z> but trying to read from attitude.<roll,pitch,yaw> gives me 0.
NSLog(#"Roll: %f", attitude.roll); // = 0
I read out the values in a method triggered by a continous timer each 100ms.
Any ideas on what I`m missing?
In order to use deviceMotion.attitude you have to call [motionManager startDeviceMotionUpdates].
startAccelerometerUpdates provides accelerometer data only like startGyroUpdates will give you gyroData. Note that device motion data is more than just accelerometer and gyro data as both of them will be combined (sensor fusion) to achieve more precision.
I have a bare single-view iOS app with the following in the -viewDidLoad of the view:
dispatch_queue_t q_default;
q_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q_default); //run event handler on the default global queue
dispatch_time_t now = dispatch_walltime(DISPATCH_TIME_NOW, 0);
dispatch_source_set_timer(timer, now, 30ull*NSEC_PER_SEC, 5000ull);
dispatch_source_set_event_handler(timer, ^{
printf("test\n");
});
dispatch_resume(timer);
This is taken directly from the docs (except for a simplified printf() argument). The block is never executed--can someone tell me why??
Additional Information
I was trying this in a larger app to no avail. I then backed out to the barebones app, tried with ARC both on and off, tried this code in -appDidFinishLaunching..., all with no luck. I can surround this code with NSLogs, both of which are printed. I've checked timer--it is not nil.
So, the problem was that I'd lost my reference to timer when the surrounding scope was destroyed. Changing timer to an ivar instead of an automatic variable fixed things...
Per the documentation of dispatch_source_create:
Dispatch sources are created in a suspended state. After creating the
source and setting any desired attributes (for example, the handler or
the context), your application must call dispatch_resume to begin
event delivery.
So your timer never fires because it's suspended. To end its suspension you need to call dispatch_resume.
I want to know what's equivalent to using AVAudioPlayerDelegate's audioPlayerDidFinishPlaying:successfully: method in OpenAL. For example:
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// (code or conditions for when the audio has finished playing...)
}
Generally speaking, OpenAL won't notify you when audio has finished playing, so there's no real equivalent to the AVAudioPlayerDelegate. The easiest way is to simply delay a function/block call by the length of the audio. As an example, you could use libdispatch (aka Grand Central Dispatch) to add a block to a queue after a set amount of time:
dispatch_time_t delay;
dispatch_queue_t queue;
dispatch_block_t block;
uint64_t audio_length_ns = 10000000000; // 10 seconds
delay = dispatch_time(DISPATCH_TIME_NOW, audio_length_ns);
queue = dispatch_get_main_queue();
block = ^{
// Do whatever you need to after the delay
// Maybe check to see if the audio has actually
// finished playing and queue up the block again
// if it hasn't.
};
// Queue up the block for the time after
dispatch_after(delay, queue, block);
The slightly harder way is, as mentioned in the comment inside the block, to check if OpenAL is finished in the block and, if it isn't, to push the block onto the queue again (probably with a shorter delay, especially if you can approximate how much longer it'll be). In general, though, you probably won't need to be spot-on and just being in a decent range of the sound's completion is good enough.
You can also schedule this sort of thing via other methods, like performSelector:withObject:afterDelay:, but that comes down more to your preference as far as API is concerned. The idea is pretty much the same.
I'm looking to create a countdown timer for SMPTE Timecode (HH:MM:SS:FF) on iOS. Basically, it's just a countdown timer with a resolution of 33.33333ms. I'm not so sure NSTimer is accurate enough to be counted on to fire events to create this timer. I would like to fire an event or call a piece of code every time this timer increments/decrements.
I'm new to Objective-C so I'm looking for wisdom from the community. Someone has suggested the CADisplayLink class, looking for some expert advice.
Try CADisplayLink. It fires at the refresh rate (60 fps).
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(timerFired:)];
displayLink.frameInterval = 2;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
This will fire every 2 frames, which is 30 times per seconds, which seems to be what you are after.
Note, that this is tied to video frame processing, so you need to do your work in the callback very quickly.
You basically have no guarantees with either NSTimer or dispatch_after; they schedule code to triggered on the main thread, but if something else takes a long time to execute and blocks the main thread, your timer won't fire.
That said, you can easily avoid blocking the main thread (use only asynchronous I/O) and things should be pretty good.
You don't say exactly what you need to do in the timer code, but if all you need to do is display a countdown, you should be fine as long as you compute the SMPTE time based on the system time, and not the number of seconds you think should have elapsed based on your timer interval. If you do that, you will almost certainly drift and get out of sync with the actual time. Instead, note your start time and then do all the math based on that:
// Setup
timerStartDate = [[NSDate alloc] init];
[NSTimer scheduledTimer...
- (void)timerDidFire:(NSTimer *)timer
{
NSTImeInterval elapsed = [timerStartDate timeIntervalSinceNow];
NSString *smtpeCode = [self formatSMTPEFromMilliseconds:elapsed];
self.label.text = smtpeCode;
}
Now you will display the correct time code no matter how often the timer is fired. (If the timer doesn't fire often enough, the timer won't update, but when it updates it will be accurate. It will never get out of sync.)
If you use CADisplayLink, your method will be called as fast as the display updates. In other words, as fast as it would be useful, but no faster. If you're displaying the time, that's probably the way to go.
If you are targeting iOS 4+, you can use Grand Central Dispatch:
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our code to run
dispatch_after(time, dispatch_get_main_queue(), ^{
// your code to run here...
});
This will call that code after 33.333333ms. If is this going to be a loop sorta deal, you may want to use the dispatch_after_f function instead that uses a function pointer instead of a block:
void DoWork(void *context);
void ScheduleWork() {
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our 'DoWork' function to run
// Here I pass in NULL for the 'context', whatever you set that to will
// get passed to the DoWork function
dispatch_after_f(time, dispatch_get_main_queue(), NULL, &DoWork);
}
void DoWork(void *context) {
// ...
// Do your work here, updating an on screen counter or something
// ...
// Schedule our DoWork function again, maybe add an if statement
// so it eventually stops
ScheduleWork();
}
And then just call ScheduleWork(); when you want to start the timer. For a repeating loop, I personally think this is a little cleaner than the block method above, but for a one time task I definitely prefer the block method.
See the Grand Central Dispatch docs for more info.