Error using xcode timer - xcode

I've been using Xcode to implement a timer and run calculations off the time. However every so often the timer will skip an interation, which throws off all the calculations.
- (void)Time
{
if (running==false) return;
NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsed = currentTime - startTime;
int mins = (int) (elapsed / 60.0);
elapsed -= mins* 60;
int seconds = (int) (elapsed);
elapsed -= seconds;
int fraction = elapsed * 100.0;
beepTime = [self Dec:seconds+elapsed];
[self mod];
label.text= [NSString stringWithFormat:#"%u:%02u.%.01u",mins,seconds,fraction/10];
[self performSelector:#selector(Time) withObject:nil afterDelay:0.01];
}
Here is a small sample of some logs
2012-08-14 20:25:04.659 RT 8.470000 BP 50.710000 MT 8.360000
2012-08-14 20:25:04.671 RT 8.470000 BP 50.720000 MT 8.370000
2012-08-14 20:25:04.682 RT 8.470000 BP 50.740000 MT 8.390000
note that BP 50.730000 and MT 8.380000 have been skipped.
Any suggestions?
Cheers!

If you read the documentation on -performSelector:withObject:afterDelay: it makes no guarantees that the message is sent exactly after 0.01 seconds delay. Instead it says that the message is put to event queue after 0.01 seconds and fired whenever it is its turn.
You might be better off by using NSTimer's method + scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:.
Eventhough timer works also in the current run loop similarly than performSelector, atleast it wouldn't execute in 0.01 + (time it took to execute -Time method body).
Note that you need to call that NSTimer method only once and use its returned value to invalidate the timer.

Related

lose RTC after restart (no battery)

in the ESP-IDF doc about RTC, it says:
This timer allows time keeping in various sleep modes, and can also persist time keeping across any resets
I added the below lines to my main function to test it, but every time after rebooting the ESP(not a hard reset), Each time the code inside the if block is executed.
struct timeval current_time;
// printf("seconds : %ld\nmicro seconds : %ld", current_time.tv_sec, current_time.tv_usec);
struct timeval d_time;
d_time.tv_sec = 1000;
d_time.tv_usec = 120;
struct timezone tz;
gettimeofday(&current_time, NULL);
if (current_time.tv_sec < 100) {
tz.tz_minuteswest = 210;
tz.tz_dsttime = DST_NONE;
settimeofday(&d_time, &tz);
}
so, what should I do to make it work, I couldn't the problem.
thank you.

dispatch_get_main_queue() doesn't run new async jobs smoothly

let dispatchGroup = dispatch_group_create()
let now = DISPATCH_TIME_NOW
for i in 0..<1000 {
dispatch_group_enter(dispatchGroup)
// Do some async tasks
let delay = dispatch_time(now, Int64(Double(i) * 0.1 * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue(), {
print(i)
dispatch_group_leave(dispatchGroup)
})
}
The print statement can print first 15-20 numbers smoothly, however, when i goes larger, the print statement prints stuff in a sluggish way. I had more complicated logic inside dispatch_after and I noticed the processing was very sluggish, that's why I wrote this test.
Is there a buffer size or other properties that I can configure? It seems dispatch_get_main_queue() doesn't work well with bigger number of async tasks.
Thanks in advance!
The problem isn't dispatch_get_main_queue(). (You'll notice the same behavior if you use a different queue.) The problem rests in dispatch_after().
When you use dispatch_after, it creates a dispatch timer with a leeway of 10% of the start/when. See the Apple github libdispatch source. The net effect is that when these timers (start ± 10% leeway) overlap, it may start coalescing them. When they're coalesced, they'll appear to fire in a "clumped" manner, a bunch of them firing immediately right after another and then a little delay before it gets to the next bunch.
There are a couple of solutions, all entailing the retirement of the series of dispatch_after calls:
You can build timers manually, forcing DispatchSource.TimerFlag.strict to disable coalescing:
let group = DispatchGroup()
let queue = DispatchQueue.main
let start = CACurrentMediaTime()
os_log("start")
for i in 0 ..< 1000 {
group.enter()
let timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue) // use `.strict` to avoid coalescing
timer.setEventHandler {
timer.cancel() // reference timer so it has strong reference until the handler is called
os_log("%d", i)
group.leave()
}
timer.schedule(deadline: .now() + Double(i) * 0.1)
timer.resume()
}
group.notify(queue: .main) {
let elapsed = CACurrentMediaTime() - start
os_log("all done %.1f", elapsed)
}
Personally, I dislike that reference to timer inside the closure, but you need to keep some strong reference to it until the timer fires, and GCD timers release the block (avoiding strong reference cycle) when the timer is canceled/finishes. This is inelegant solution, IMHO.
It is more efficient to just schedule single repeating timer that fires every 0.1 seconds:
var timer: DispatchSourceTimer? // note this is property to make sure we keep strong reference
func startTimer() {
let queue = DispatchQueue.main
let start = CACurrentMediaTime()
var counter = 0
// Do some async tasks
timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
timer!.setEventHandler { [weak self] in
guard counter < 1000 else {
self?.timer?.cancel()
self?.timer = nil
let elapsed = CACurrentMediaTime() - start
os_log("all done %.1f", elapsed)
return
}
os_log("%d", counter)
counter += 1
}
timer!.schedule(deadline: .now(), repeating: 0.05)
timer!.resume()
}
This not only solves the coalescing problem, but it also is more efficient.
For Swift 2.3 rendition, see previous version of this answer.

Reading chunks of audio with ringbuffer

I would like to analyze chunks of audio data of one second. For this purpose I implemented an audio unit that fills a ringbuffer (TPCircularBuffer by Michael Tyson). In another file I try to read chunks of one second using a NStimer. Unfortunately, I receive errors with consuming these data.
The buffer is filled in a kAudioOutputUnitProperty_SetInputCallback and works fine
Device * THIS = (__bridge Device *)inRefCon;
// Render audio into buffer
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = 2;
bufferList.mBuffers[0].mData = NULL;
bufferList.mBuffers[0].mDataByteSize = inNumberFrames * sizeof(SInt16) * 2;
CheckError(AudioUnitRender(THIS -> rioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, &bufferList), "AudioUnitRender");
// Put audio into circular buffer
TPCircularBufferProduceBytes(&circBuffer, bufferList.mBuffers[0].mData, inNumberFrames * 2 * sizeof(SInt16));
To read one second of samples I implemented te following code:
- (void)initializeTimer {
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:YES];
}
- (void) timerFired:(NSTimer *)theTimer {
NSLog(#"Reading %i second(s) from ring",1);
int32_t availableBytes;
SInt16 *tail = TPCircularBufferTail(&circBuffer, &availableBytes);
int availableSamples = availableBytes / sizeof(SInt16);
NSLog(#"Available samples %i", availableSamples);
for (int i = 0; i < availableSamples; i++) {
printf("%i\n",tail[i]);
}
TPCircularBufferConsume(&circBuffer, sizeof(SInt16) * availableBytes);
}
However, when I run this code the number of samples is printed but then I receive the following error:
Assertion failed: (buffer->fillCount >= 0), function TPCircularBufferConsume, file …/TPCircularBuffer.h, line 142.
Unfortunately, I don’t know what is going wrong with consuming the data. The buffer length is set to samplerate * 2 to be long enough.
I would be very happy if someone knows what is going wrong here.
Your circular buffer isn't long enough. You don't check that the available size is positive before emptying, and the time all your print statements take let the buffer get over-filled.
Make the buffer at least 2X larger than your timer, check before emptying, empty the buffer before printing, and use far fewer print statements.

Making a timer in Arduino whilst checking for input

I need to wait for a period of time while checking whether a button is pressed (so whether an input is HIGH or LOW).
The delay function is annoying to use for this because it cannot check whether something is happening while being delayed, so it would have to wait for 1 ms, check, wait, check, wait, check etc...
Can you help me with the coding I would need to check and pause for a set amount of time, at the same time?
You can realize that with a second condition-controlled loop.
If you want to wait in each arduino main loop as an example for 20 seconds and execute in this time span further code you can do this as follows:
unsigned long startTime = millis(); // Number of milliseconds since the program started (unsigned long)
unsigned long intervalTime = 20000UL; // equals 20 seconds
int buttonPin = 3; // used button pin
void loop()
{
while(millis() - startTime < intervalTime){
if(digitalRead(buttonPin)==HIGH){
//...
}
else {
//...
}
}
//...
}

CADisplaylink code fires even when display doesn't refresh

Note: as of iOS7 this problem may only manifest in the simulator -- still testing.
I have an implementation of CADisplayLink, and I need the code to run if and only if the display actually refreshes
This doesn't happen.
Here's a very simple test program:
I start the display link running; in the first frame aLabel should display "WordFlash"; for the next 19 frames it should display "--------" and for the next 100 it should be blank; then the cycle should repeat.
Occasionally (say 1 in 8 times), and unpredictably, the screen won't refresh to display "WordFlash" though the code has indeed fired (as the counter has advanced). I need the counter to advance only if "WordFlash" has successfully displayed for exactly 1 frame.
Any ideas? I am totally stumped.
Note: This display-refresh-skipping seems to happen without correlation to the time it takes the device to execute the simple code (as in the NSLogged time-to-execute the code can be identical in two different cycles while only one cycle has successfully flashed the word.
#import "HomePwnerViewController.h"
#interface HomePwnerViewController ()
#end
#implementation HomePwnerViewController {
int counter;
UILabel *aLabel;
}
- (void)viewDidLoad
{
[super viewDidLoad];
aLabel = [[UILabel alloc] initWithFrame:self.view.frame];
[self.view addSubview:aLabel];
aLabel.alpha = 1;
aLabel.text = #"";
aLabel.textAlignment = NSTextAlignmentCenter;
[aLabel setFont:[UIFont fontWithName:#"Courier" size:50]];
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(testWordFlashMethod) userInfo:nil repeats:NO];
}
- (void) displaySensitiveProcedure: (CADisplayLink *) sender {
CFTimeInterval startTime = CACurrentMediaTime();
if (counter == 0)
aLabel.text = #"FlashWord";
else if (counter < 20)
aLabel.text = #"---------";
else
aLabel.text = #"";
CFTimeInterval endTime = CACurrentMediaTime();
if (counter == 0)
NSLog(#"frame time %f frame length %f ratio %f", endTime - startTime, sender.duration, (endTime - startTime)/sender.duration);
counter++;
if (counter == 120)
counter = 0;
}
- (void) testWordFlashMethod {
CADisplayLink *DL = [CADisplayLink displayLinkWithTarget:self selector:#selector(displaySensitiveProcedure:)];
DL.frameInterval = 1;
counter = 0;
[DL addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Much obliged,
b
If I were to guess, I'd say that animating a UILabel on screen refresh is not the best way to do things. In my experience changing the text on them can take a bit of time to render. If you want to draw a dash across the screen, you might be better served by using Core Graphics API's, or a CALayer based drawing approach.
Your displaySensitiveProcedure is probably taking more than a single frame (16.7ms) to update the screen, which makes it skip a frame. If you consistently take more than that (say 20ms), you can have the DisplayLink fire less frequently to get smooth animation at a slower framerate - see the frameInterval property. I would suggest timing your callback by doing something like this:
- (void) displaySensitiveProcedure: (CADisplayLink *) sender {
CFTimeInterval startTime = CACurrentMediaTime();
... <code> ...
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(#"frame time: %f", endTime - startTime);
}

Resources