Skip to content
Browse files

Add capturing of motion sensor data.

  • Loading branch information...
1 parent 4f81ab8 commit a6a5bb350eea4ee7a59c9268078825b62a5a8076 @afh afh committed Apr 19, 2011
Showing with 135 additions and 0 deletions.
  1. +4 −0 OnTheSpot.xcodeproj/project.pbxproj
  2. +5 −0 OnTheSpot/RootViewController.h
  3. +126 −0 OnTheSpot/RootViewController.m
View
4 OnTheSpot.xcodeproj/project.pbxproj
@@ -18,6 +18,7 @@
BCB642A8135D7F5000DA6256 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCB642A6135D7F5000DA6256 /* RootViewController.xib */; };
BCBD6725135D8E4D00B25825 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = BCBD6723135D8E4D00B25825 /* Default.png */; };
BCBD6726135D8E4D00B25825 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BCBD6724135D8E4D00B25825 /* Default@2x.png */; };
+ BCBD6728135D94C200B25825 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCBD6727135D94C200B25825 /* CoreMotion.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -37,13 +38,15 @@
BCB642A7135D7F5000DA6256 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/RootViewController.xib; sourceTree = "<group>"; };
BCBD6723135D8E4D00B25825 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
BCBD6724135D8E4D00B25825 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
+ BCBD6727135D94C200B25825 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
BCB64287135D7F5000DA6256 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ BCBD6728135D94C200B25825 /* CoreMotion.framework in Frameworks */,
BCB6428F135D7F5000DA6256 /* UIKit.framework in Frameworks */,
BCB64291135D7F5000DA6256 /* Foundation.framework in Frameworks */,
BCB64293135D7F5000DA6256 /* CoreGraphics.framework in Frameworks */,
@@ -75,6 +78,7 @@
BCB6428D135D7F5000DA6256 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ BCBD6727135D94C200B25825 /* CoreMotion.framework */,
BCB6428E135D7F5000DA6256 /* UIKit.framework */,
BCB64290135D7F5000DA6256 /* Foundation.framework */,
BCB64292135D7F5000DA6256 /* CoreGraphics.framework */,
View
5 OnTheSpot/RootViewController.h
@@ -7,12 +7,17 @@
//
#import <UIKit/UIKit.h>
+#import <CoreMotion/CMMotionManager.h>
@interface RootViewController : UITableViewController
< UINavigationControllerDelegate
, UIImagePickerControllerDelegate
> {
+ @private
+ NSMutableArray* _motionSamples;
+ CMMotionManager* _motionManager;
+ NSTimer* _sampleTimer;
}
- (IBAction)takePicture;
View
126 OnTheSpot/RootViewController.m
@@ -9,12 +9,42 @@
#import "RootViewController.h"
@interface RootViewController()
+@property (nonatomic, retain) NSTimer* sampleTimer;
- (void)storeImage:(UIImage*)anImage withMetaData:(NSDictionary*)metaData;
+- (void)takeMotionSample:(NSTimer*)aTimer;
+- (void)startMotionSampling;
+- (void)stopMotionSampling;
@end
@implementation RootViewController
+@synthesize sampleTimer = _sampleTimer;
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ if ((self = [super initWithCoder:aDecoder])) {
+ _motionManager = [CMMotionManager new];
+ }
+ return self;
+}
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
+ _motionManager = [CMMotionManager new];
+ }
+ return self;
+}
+
+- (id)initWithStyle:(UITableViewStyle)style
+{
+ if ((self = [super initWithStyle:style])) {
+ _motionManager = [CMMotionManager new];
+ }
+ return self;
+}
+
- (void)viewDidLoad
{
[super viewDidLoad];
@@ -143,11 +173,17 @@ - (void)viewDidUnload
- (void)dealloc
{
+ [_motionManager release];
[super dealloc];
}
- (IBAction)takePicture
{
+ // start background motion sampling
+ [self performSelector
+ :@selector(startMotionSampling) withObject:nil];
+
+ // present camera ui
UIImagePickerController *vc = [[UIImagePickerController alloc] init];
vc.delegate = self;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
@@ -162,6 +198,7 @@ - (IBAction)takePicture
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
+ [self stopMotionSampling];
[self.navigationController dismissModalViewControllerAnimated:YES];
NSDictionary* metaData = (NSDictionary*)[info valueForKey:UIImagePickerControllerMediaMetadata];
@@ -171,6 +208,95 @@ - (void)imagePickerController:(UIImagePickerController *)picker
- (void)storeImage:(UIImage*)anImage withMetaData:(NSDictionary*)metaData
{
+ NSDictionary* exifData = [metaData valueForKey:@"{Exif}"];
+ NSAssert(exifData != nil, @"Missing exif data");
+
+ NSDateFormatter* dateFormatter = [NSDateFormatter new];
+ dateFormatter.dateFormat = @"yyyy:MM:dd HH:mm:ss";
+ NSString *dateTimeString = [exifData valueForKey:@"DateTimeOriginal"];
+ NSDate* dateTimeDigitized = [dateFormatter dateFromString:dateTimeString];
+ NSAssert(dateTimeDigitized != nil, @"Missing dateTimeDigitized");
+
+ NSDictionary* motionData = nil;
+ for (NSDictionary* sample in _motionSamples) {
+ // NOTE: for some odd reason using isEqualToDate: never equals true,
+ // even though the dates are the same
+ // also using compare: with NSOrderedSame does not work properly.
+ // The current code is more fault tolerant, since the motion samples
+ // may not have been taken at the exact same time as the picture.
+ NSDate* sampleTimestamp = [sample valueForKey:@"timestamp"];
+ if ([dateTimeDigitized timeIntervalSinceDate:sampleTimestamp] <= 0.0) {
+ motionData = sample;
+ break;
+ }
+ }
+ NSAssert(motionData != nil, @"Missing motion sample for image take at %@", metaData);
+
+ NSMutableDictionary* jsonData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ metaData, @"mediaMetaData"
+ , motionData, @"motionData"
+ , nil];
+ NSLog(@"JSON\n%@", jsonData);
+
+ [dateFormatter release];
// store image in CouchDB
}
+
+- (void)startMotionSampling
+{
+ if (nil == _motionSamples) {
+ _motionSamples = [[NSMutableArray alloc] init];
+ }
+ else {
+ [_motionSamples removeAllObjects];
+ }
+
+ // start motion sensor updates
+ if (!_motionManager.gyroActive && _motionManager.gyroAvailable) {
+ _motionManager.gyroUpdateInterval = 1.0;
+ [_motionManager startGyroUpdates];
+ }
+ if (!_motionManager.deviceMotionActive && _motionManager.deviceMotionAvailable) {
+ _motionManager.deviceMotionUpdateInterval = 1.0;
+ [_motionManager startDeviceMotionUpdates];
+ }
+ if (!_motionManager.accelerometerActive && _motionManager.accelerometerAvailable) {
+ _motionManager.accelerometerUpdateInterval = 1.0;
+ [_motionManager startAccelerometerUpdates];
+ }
+
+ self.sampleTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
+ target:self
+ selector:@selector(takeMotionSample:)
+ userInfo:nil
+ repeats:YES];
+}
+
+- (void)stopMotionSampling
+{
+ [_sampleTimer invalidate];
+ self.sampleTimer = nil;
+
+ if (_motionManager.gyroActive && _motionManager.gyroAvailable) {
+ [_motionManager stopGyroUpdates];
+ }
+ if (_motionManager.deviceMotionActive && _motionManager.deviceMotionAvailable) {
+ [_motionManager stopDeviceMotionUpdates];
+ }
+ if (_motionManager.accelerometerActive && _motionManager.accelerometerAvailable) {
+ [_motionManager stopAccelerometerUpdates];
+ }
+}
+
+- (void)takeMotionSample:(NSTimer*)aTimer
+{
+ NSMutableDictionary* sample = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ [NSDate date], @"timestamp"
+ , _motionManager.gyroData, @"gyroData"
+ , _motionManager.deviceMotion, @"deviceMotion"
+ , _motionManager.accelerometerData, @"accelerometerData"
+ , nil];
+ [_motionSamples addObject:sample];
+}
+
@end

0 comments on commit a6a5bb3

Please sign in to comment.
Something went wrong with that request. Please try again.