I've been playing with Core Motion's framework lately and while trying to get more juice to come out of the limited CMAltitude class just stumbled with some weird data at the end of the call. So to recreate if you call up:
import UIKit
import CoreMotion
class ViewController: UIViewController {
let corey = CMAltimeter()
override func viewDidLoad() {
super.viewDidLoad()
self.getter()
}
#objc func getter() {
corey.startRelativeAltitudeUpdates(to: OperationQueue.main, withHandler: { (altitudeData:CMAltitudeData?, error:Error?) in
print(String(describing: altitudeData.unsafelyUnwrapped))
Just for the visual purposes doing this really scratchy thing will respond with:
Altitude -0.589237 Pressure 101093.882812 # 2377.566172
Altitude -0.618303 Pressure 101094.234375 # 2378.602637
Altitude -0.618303 Pressure 101094.234375 # 2379.640150
Altitude -0.620945 Pressure 101094.250000 # 2380.678124
Altitude -0.628872 Pressure 101094.343750 # 2381.714421
What I would like to know is the last part # 2381.714421, it looks like seconds but actually I'm not really sure, when comparing with a timer, and with boottime time_t that number start +4 seconds, after some time of inactivity it drifts and becomes less time than boottime.
Does it drifts away because of app inactivity?
But how come that it starts with more time than even boot-time?
Can anyone explain what's going on?
CMAltitudeData inherits from CMLogItem, this is where the last field, timestamp is coming from. According to the documentation, timestamp should match the amount of time in seconds since the device booted.
However, according to this question on SO the boot time can indeed drift slightly and it seems that timestamp doesn't simply copy time_t at each measurement, but rather it copies it at the first measurement and after that it just increments it based probably on a different Timer. Comparing to a Timer is not a good idea, since Timer runs on a runloop, so it only works while your app is in the foreground and even then it isn't really precise.
Related
I'm looking for a way to turn timestamp found on CoreMotion events into proper, high precision "wall date-time". I want to correlate events on iPhone and Apple Watch, so need to translate those timestamps to the single domain.
I tested several approaches on internet, like this: https://stackoverflow.com/a/53250802. Which gives utterly wrong results.
And this
extension TimeInterval {
static var bootTime = Date().timeIntervalSince1970 - ProcessInfo.processInfo.systemUptime
var unix: TimeInterval {
return (TimeInterval. bootTime + self)
}
}
which seem to work on first sight. My tests shown that over time, there is increasing discrepancy between result unix TimeInterval:
In this picture you can see sessions that gather events. Single session events has the same color. On upper line you see watch events and on lower phone events. We see that it seems like timestamp on watchOS advances slower then on iPhone. This looks like timestamp might be connected to processor cycle count?
My question is: How to translate CoreMotion timestamps into Date in proper fashion both on iPhone and watchOS?
This is actually correct solution.
extension TimeInterval {
static var bootTime = Date().timeIntervalSince1970 - ProcessInfo.processInfo.systemUptime
var unix: TimeInterval {
return (TimeInterval. bootTime + self)
}
}
My problem with lagged timestamps on watchOS was connected to cluttering processing queue, so event handlers was lagged over time more and more. For some reason, MotionManager does not react to stopMotionDeviceUpdates() and I was getting those over and over again, despited the fact, I turned it off. I just tried to process too much data - I increased sensor frequency to 1/100.
I have a program in SWIFT, that detects when a beacon or multiple beacons are out of range and based on
that it will perform something like saving some data into database and etc. Everything works well, however I do get lots of
false positive in a way that "didExitRegion" getting fired and few secs later "didEnterRegion" getting fired while I haven't been moved or etc.
I know this has lot to do with the tuning of the beacons and their qualities, but at this time I have come up with an alternative solution.
So I decided to use NSTimer to see if I am really off range or is it just a false positive that I am getting?
so when didExitRegion is getting fired, I start a NSTimer for 60 secs. If the 60 secs is up and didEnterRegion didn't get fire, then I know
I am really out of range and perform whatever data saving I need to do.
Otherwise if didEnterRegion is called within that 60 secs, then I'll assume it was a false positive and invalidate the
nstimer and not doing anything.
Everything works well as long as I am working with one beacon. The problem I have with timer is when multiple beacons go out of range.
lets say first beacon is out of range, so didExitRegion is getting fire and start the NSTimer
Let say 20 secs later second beacon is off range and again didExitRegion is getting fired and that one starts the NSTimer again.
Now my NStimer is all out of synch and at that time, things are not working correct and the NSTimer continuously start itself when 60 secs
is up and etc.
So my question is what is the work around this solution? How can I keep my nstimer in synch when is called again before is invalidated?
Is there a better way to this solution? Again, I know a better quality beacons can help, but that is not an option for me at this time.
One solution is to keep a dictionary like this:
var pendingExits = Dictionary<CLBeaconRegion,NSDate>()
Each time you get a didExitRegion call:
Only start the NSTimer if the dictionary is empty -- otherwise, assume the timer is already running.
Add the region to the dictionary along with a new NSDate() to set the timestamp of when it was added.
When you get a didEnterRegion callback:
Look for the region in the dictionary. If it is there, remove it.
When the timer goes off:
Look for any entry with a timestamp 60 seconds or more old. For this, remove the region from the dictionary and fire your custom exit logic.
Find the newest remaining timestamp (if any) in the dictionary. Start the timer to go off at that time plus 60 seconds.
SO basically all is in the title. I've searched quite a lot, but didn't find any right solution which doesn't require internet connection.
If the user changes time in settings - i can't find real time since last launch.
I need that for my game, in it for every hour, even when you don't play the game, you get some coins.
If the user changes time in settings - that affect the time in NSDate() and user can cheat with coins.
So save the NSDate() to user defaults on app launch. The next time the app comes to the foreground, or gets launched again, get the current NSDate and subtract the saved date from it. That will give you the number of seconds between the two dates. Calculating hours from seconds is a simple matter of dividing by 3600. – Duncan C just now edit
EDIT:
Note that in newer versions of Swift (starting with Swift 2?) Most Foundation classes were defined as native Swift classes without the NS prefix. For newer versions of swift, replace all occurrences of NSDate with Date in the above.
Also note that in iOS ≥ 7.0, the Calendar has some methods that make this sort of calculation neater and easier. There's a new method dateComponents(_:from:to:) that lets you calculate the difference between 2 dates in whatever units you want. You could use that to calculate the seconds between the 2 dates more cleanly than calculating seconds, as outlined in my original answer. Calendar methods also tend to handle boundary conditions like spanning daylight savings time, leap seconds, etc.
Consider the following Swift 4/5 playground code:
import UIKit
let now = Date()
let randomSeconds = Double.random(in: 100000...3000000)
let later = now + randomSeconds
if let difference = Calendar.current.dateComponents([.second],
from: now,
to: later)
.second {
print(difference)
Try this.
Step 1. When user exits game. Set a NSUserDefault with current time.
Step 2. When app launches, in your appDelagate file, get this value.
Step 3. Calculate diff between and award coins accordingly.
My iOS application relies heavily on GPS and I tried writing a method that helps conserve battery but I am having little success.
I created an NSTimer that fired every 15 seconds. Every time the method was called, it would increment an int time up by 1. If int time reached 20(5 minutes) it would turn off the location updates and set a bool isStopped to true. Every time the method ran and int time was above 20, it would increment another int, int time2, up by 1. If the method was ran and int time2 was equal to 4, it would start the location updates again and set time2 to 0.
Then in the didUpdateLocation: method for the location manager, I have an algorithm that would first check if the bool isStopped was true, if it was true then it would check the new location's horizontal accuracy and make sure it was under 10. Then it would check the newLocation with a location object named coords and check to see if they were greater than 9 meters apart. If they were not, it would stop location updates again and return. If they were, it would continue to another algorithm where it would check the new location object against some arguments. If it passed the coords location object would be set with the new location object, the time and time2 ints would be set to 0, the isStopped would be set to false, and the whole process would start all over again.
In short, after 5 minutes of no location changes, the location updates would be stopped and periodically checked every 1 minute to see if the user had moved at least 10 meters from the previous location that passed all requirements. When the user does move far enough, it starts the process all over again and the user has to not move 10 or more meters for 5 minutes before it starts the periodic checks. The thought behind this is to do only few second checks every minute when the user isn't moving much instead of constantly having the location services running.
Now here's the problem I run into, when the location updates stop. The NSTimer stops running while the app is in the background.
Could I somehow schedule a background task to run the loop between the 1 minute checks? Does anyone have any better ideas? Or any ideas on solutions to this?
A while ago I asked a question that features all the different ways that you can run GPS in the background.
CLLocationManager geo-fencing/startMonitoringForRegion: vs. startMonitoringForSignificantLocationChanges: vs. 10-minute startUpdating calls
You have to do specific things in order to maintain GPS in the background. A NSTimer would not suffice, since you need to register your app to be allowed to keep it running in the background after ten minutes. You minimally have to run the GPS every 10 minutes in the background to keep your app running.
Anyway, the methods in the above post should answer your question.
a relatively simple question that I've not been able to find a clear answer to. My app is more complex, but answering this question will suffice.
Suppose you're writing a stopwatch app. When the user taps "start", the app stores the current date and time in startTime:
startTime = [NSDate date];
When the user tapes "stop", the app stores the current date and time in stopTime:
stopTime = [NSDate date];
The duration is calculated by:
duration = [stopTime timeIntervalSinceDate:startTime];
and is displayed with something like:
[durationLabel setText:[NSString stringWithFormat:#"%1.2f", duration]];
The typical durations that my app is timing range from 2 to 50 seconds. I need accuracy to 1/100th of a second (e.g. 2.86 seconds).
I'm assuming that there is some protocol that iOS devices use to keep their clocks accurate (cellular or NTP sources?). My concern is that between starting and stopping the stopwatch, the clock on the iOS device is updated which can result in a shift of the current date/time either ahead or back. If this were to happen, the duration calculated would be inaccurate.
I've seen a few posts relating to timing methods for purposes of improving code efficiency. Some suggest using mach_time.h functions, which I'm not familiar with. It's not obvious to me which is the best approach to use.
Is it possible to disable iOS from updating the date & time? Is mach_absolute_time() unaffected by iOS clock updates?
Many thanks!
Tim
You are correct in thinking that CFAbsoluteTime and its derivatives (NSDate dateand so on) are potentially skewed by network updates on 'real' time. Add that to the fact that NSTimer has an accuracy of 50-100ms and you have a timer that is not suited to the most critical of time-sensitive operations.
The answer to this problem seems to be CACurrentMediaTime.
It is a member of the Core Animation group, but there shouldn't be any problem integrating it into non-animation based applications.
CACurrentMediaTime is a wrapper of mach_absolute_time() and makes sense of the "mach absolute time unit," which from my understanding is no fun to tinker with. mach_absolute_time() is calculated by running a non-network synced timer since the device was last booted.
There is relatively little information on CACurrentMediaTime but here are some sources and further reading:
Apple's sparse documentation of CACurrentMediaTime
Stack Overflow - NSTimer vs CACurrentMediaTime()
http://bendodsonapps.com/weblog/2013/01/29/ca-current-media-time/
http://blog.spacemanlabs.com/2011/09/all-in-the-timing-keeping-track-of-time-passed-on-ios/
http://forum.sparrow-framework.org/topic/accurate-timer
Note: If you do use CACurrentMediaTime, make sure you include and link the QuartzCore.framework
Check out this here. I would say forget about the current time check and use a precision timer since it won't rely on the current time but instead uses an interval.