4 * SMSLib Sudden Motion Sensor Access Library
5 * Copyright (c) 2010 Suitable Systems
8 * Developed by: Daniel Griscom
10 * http://www.suitable.com
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the
14 * "Software"), to deal with the Software without restriction, including
15 * without limitation the rights to use, copy, modify, merge, publish,
16 * distribute, sublicense, and/or sell copies of the Software, and to
17 * permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
20 * - Redistributions of source code must retain the above copyright notice,
21 * this list of conditions and the following disclaimers.
23 * - Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimers in the
25 * documentation and/or other materials provided with the distribution.
27 * - Neither the names of Suitable Systems nor the names of its
28 * contributors may be used to endorse or promote products derived from
29 * this Software without specific prior written permission.
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
32 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
34 * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
35 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
36 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
37 * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
39 * For more information about SMSLib, see
40 * <http://www.suitable.com/tools/smslib.html>
44 * 1 Centre Street, Suite 204
50 #import <IOKit/IOKitLib.h>
51 #import <sys/sysctl.h>
55 #pragma mark Internal structures
57 // Represents a single axis of a type of sensor.
58 typedef struct axisStruct {
59 int enabled; // Non-zero if axis is valid in this sensor
60 int index; // Location in struct of first byte
61 int size; // Number of bytes
62 float zerog; // Value meaning "zero g"
63 float oneg; // Change in value meaning "increase of one g"
64 // (can be negative if axis sensor reversed)
67 // Represents the configuration of a type of sensor.
68 typedef struct sensorSpec {
69 const char* model; // Prefix of model to be tested
70 const char* name; // Name of device to be read
71 unsigned int function; // Kernel function index
72 int recordSize; // Size of record to be sent/received
73 axisStruct axes[3]; // Description of three axes (X, Y, Z)
76 // Configuration of all known types of sensors. The configurations are
77 // tried in order until one succeeds in returning data.
78 // All default values are set here, but each axis' zerog and oneg values
79 // may be changed to saved (calibrated) values.
81 // These values came from SeisMaCalibrate calibration reports. In general I've
82 // found the following:
83 // - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs
84 // are different (and in one case two axes are swapped)
85 // - PowerBooks and iBooks all have sensors centered on 0, and reading
86 // 50-53 steps per gravity (but with differing polarities!)
87 // - PowerBooks and iBooks of the same model all have the same axis polarities
88 // - PowerBook and iBook access methods are model- and OS version-specific
90 // So, the sequence of tests is:
91 // - Try model-specific access methods. Note that the test is for a match to the
92 // beginning of the model name, e.g. the record with model name "MacBook"
93 // matches computer models "MacBookPro1,2" and "MacBook1,1" (and ""
94 // matches any model).
95 // - If no model-specific record's access fails, then try each model-independent
96 // access method in order, stopping when one works.
97 static const sensorSpec sensors[] = {
98 // ****** Model-dependent methods ******
99 // The PowerBook5,6 is one of the G4 models that seems to lose
100 // SMS access until the next reboot.
105 {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, -51.5}, {1, 2, 1, 0, -51.5}}},
106 // The PowerBook5,7 is one of the G4 models that seems to lose
107 // SMS access until the next reboot.
112 {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
113 // Access seems to be reliable on the PowerBook5,8
118 {{1, 0, 1, 0, -51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, -51.5}}},
119 // Access seems to be reliable on the PowerBook5,9
124 {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, -51.5}, {1, 2, 1, 0, -51.5}}},
125 // The PowerBook6,7 is one of the G4 models that seems to lose
126 // SMS access until the next reboot.
131 {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
132 // The PowerBook6,8 is one of the G4 models that seems to lose
133 // SMS access until the next reboot.
138 {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
139 // MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes.
144 {{1, 0, 2, 0, 251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, -251}}},
145 // MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07.
146 // NOTE! The 17" machines have the signs of their X and Y axes reversed
147 // from this calibration, but there's no clear way to discriminate between
153 {{1, 0, 2, 0, -251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, -251}}},
159 {{1, 0, 2, 0, -251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, -251}}},
165 {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
171 {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
172 // This is speculative, based on a single user's report. Looks like the X and Y axes
173 // are swapped. This is true for no other known Appple laptop.
178 {{1, 2, 2, 0, -251}, {1, 0, 2, 0, -251}, {1, 4, 2, 0, -251}}},
184 {{1, 0, 2, 0, -251}, {1, 2, 2, 0, -251}, {1, 4, 2, 0, 251}}},
185 // ****** Model-independent methods ******
186 // Seen once with PowerBook6,8 under system 10.3.9; I suspect
187 // other G4-based 10.3.* systems might use this
188 {"", "IOI2CMotionSensor", 24, 60, {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
189 // PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8
191 {"", "IOI2CMotionSensor", 21, 60, {{1, 0, 1, 0, 51.5}, {1, 1, 1, 0, 51.5}, {1, 2, 1, 0, 51.5}}},
192 // PowerBook5,8 , PowerBook5,9 under OS X 10.4.*
197 {// Each has two out of three gains negative, but it's different
198 // for the different models. So, this will be right in two out
199 // of three axis for either model.
201 {1, 1, 1, -6, -51.5},
202 {1, 2, 1, 0, -51.5}}},
203 // All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15")
204 // is Kionix KXM52-1050 three-axis accelerometer chip. Data is at
205 // http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models
206 // that use this are:
218 {"", "SMCMotionSensor", 5, 40, {{1, 0, 2, 0, 251}, {1, 2, 2, 0, 251}, {1, 4, 2, 0, 251}}}};
220 #define SENSOR_COUNT (sizeof(sensors) / sizeof(sensorSpec))
222 #pragma mark Internal prototypes
224 static int getData(sms_acceleration* accel, int calibrated, id logObject, SEL logSelector);
225 static float getAxis(int which, int calibrated);
226 static int signExtend(int value, int size);
227 static NSString* getModelName(void);
228 static NSString* getOSVersion(void);
229 static BOOL loadCalibration(void);
230 static void storeCalibration(void);
231 static void defaultCalibration(void);
232 static void deleteCalibration(void);
233 static int prefIntRead(NSString* prefName, BOOL* success);
234 static void prefIntWrite(NSString* prefName, int prefValue);
235 static float prefFloatRead(NSString* prefName, BOOL* success);
236 static void prefFloatWrite(NSString* prefName, float prefValue);
237 static void prefDelete(NSString* prefName);
238 static void prefSynchronize(void);
239 // static long getMicroseconds(void);
240 float fakeData(NSTimeInterval time);
242 #pragma mark Static variables
244 static int debugging = NO; // True if debugging (synthetic data)
245 static io_connect_t connection; // Connection for reading accel values
246 static int running = NO; // True if we successfully started
247 static unsigned int sensorNum = 0; // The current index into sensors[]
248 static const char* serviceName; // The name of the current service
249 static char *iRecord, *oRecord; // Pointers to read/write records for sensor
250 static int recordSize; // Size of read/write records
251 static unsigned int function; // Which kernel function should be used
252 static float zeros[3]; // X, Y and Z zero calibration values
253 static float onegs[3]; // X, Y and Z one-g calibration values
257 // Pattern for building axis letter from axis number
258 #define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z")
259 // Name of configuration for given axis' zero (axis specified by integer)
260 #define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)]
261 // Name of configuration for given axis' oneg (axis specified by integer)
262 #define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)]
263 // Name of "Is calibrated" preference
264 #define CALIBRATED_NAME (@"Calibrated")
265 // Application domain for SeisMac library
266 #define APP_ID ((CFStringRef) @"com.suitable.SeisMacLib")
268 // These #defines make the accelStartup code a LOT easier to read.
270 #define LOG(message) \
272 [logObject performSelector:logSelector withObject:message]; \
274 #define LOG_ARG(format, var1) \
276 [logObject performSelector:logSelector withObject:[NSString stringWithFormat:format, var1]]; \
278 #define LOG_2ARG(format, var1, var2) \
280 [logObject performSelector:logSelector \
281 withObject:[NSString stringWithFormat:format, var1, var2]]; \
283 #define LOG_3ARG(format, var1, var2, var3) \
285 [logObject performSelector:logSelector \
286 withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \
289 #pragma mark Function definitions
291 // This starts up the accelerometer code, trying each possible sensor
292 // specification. Note that for logging purposes it
293 // takes an object and a selector; the object's selector is then invoked
294 // with a single NSString as argument giving progress messages. Example
296 // - (void)logMessage: (NSString *)theString
297 // which would be used in accelStartup's invocation thusly:
298 // result = accelStartup(self, @selector(logMessage:));
299 // If the object is nil, then no logging is done. Sets calibation from built-in
300 // value table. Returns ACCEL_SUCCESS for success, and other (negative)
301 // values for various failures (returns value indicating result of
302 // most successful trial).
303 int smsStartup(id logObject, SEL logSelector) {
304 io_iterator_t iterator;
306 kern_return_t result;
307 sms_acceleration accel;
308 int failure_result = SMS_FAIL_MODEL;
313 NSString* modelName = getModelName();
315 LOG_ARG(@"Machine model: %@\n", modelName);
316 LOG_ARG(@"OS X version: %@\n", getOSVersion());
317 LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION);
319 for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) {
320 // Set up all specs for this type of sensor
321 serviceName = sensors[sensorNum].name;
322 recordSize = sensors[sensorNum].recordSize;
323 function = sensors[sensorNum].function;
325 LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n", serviceName, function,
328 NSString* targetName = [NSString stringWithCString:sensors[sensorNum].model
329 encoding:NSMacOSRomanStringEncoding];
330 LOG_ARG(@" Comparing model name to target \"%@\": ", targetName);
331 if ([targetName length] == 0 || [modelName hasPrefix:targetName]) {
335 // Don't need to increment failure_result.
339 LOG(@" Fetching dictionary for service: ");
340 CFMutableDictionaryRef dict = IOServiceMatching(serviceName);
346 if (failure_result < SMS_FAIL_DICTIONARY) {
347 failure_result = SMS_FAIL_DICTIONARY;
352 LOG(@" Getting list of matching services: ");
353 result = IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &iterator);
355 if (result == KERN_SUCCESS) {
358 LOG_ARG(@"failure, with return value 0x%x.\n", result);
359 if (failure_result < SMS_FAIL_LIST_SERVICES) {
360 failure_result = SMS_FAIL_LIST_SERVICES;
365 LOG(@" Getting first device in list: ");
366 device = IOIteratorNext(iterator);
370 if (failure_result < SMS_FAIL_NO_SERVICES) {
371 failure_result = SMS_FAIL_NO_SERVICES;
376 LOG(@" Opening device: ");
379 result = IOServiceOpen(device, mach_task_self(), 0, &connection);
381 if (result != KERN_SUCCESS) {
382 LOG_ARG(@"failure, with return value 0x%x.\n", result);
383 IOObjectRelease(device);
384 if (failure_result < SMS_FAIL_OPENING) {
385 failure_result = SMS_FAIL_OPENING;
388 } else if (connection == 0) {
389 LOG_ARG(@"'success', but didn't get a connection (return value was: 0x%x).\n", result);
390 IOObjectRelease(device);
391 if (failure_result < SMS_FAIL_CONNECTION) {
392 failure_result = SMS_FAIL_CONNECTION;
396 IOObjectRelease(device);
399 LOG(@" Testing device.\n");
401 defaultCalibration();
403 iRecord = (char*)malloc(recordSize);
404 oRecord = (char*)malloc(recordSize);
407 result = getData(&accel, true, logObject, logSelector);
411 LOG_ARG(@" Failure testing device, with result 0x%x.\n", result);
416 if (failure_result < SMS_FAIL_ACCESS) {
417 failure_result = SMS_FAIL_ACCESS;
421 LOG(@" Success testing device!\n");
426 return failure_result;
429 // This starts up the library in debug mode, ignoring the actual hardware.
430 // Returned data is in the form of 1Hz sine waves, with the X, Y and Z
431 // axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
432 // "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
433 // Z axes centered on 1 (calibrated) or 256 (uncalibrated).
434 // Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
435 int smsDebugStartup(id logObject, SEL logSelector) {
436 LOG(@"Starting up in debug mode\n");
441 // Returns the current calibration values.
442 void smsGetCalibration(sms_calibration* calibrationRecord) {
445 for (x = 0; x < 3; x++) {
446 calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]);
447 calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]);
451 // Sets the calibration, but does NOT store it as a preference. If the argument
452 // is nil then the current calibration is set from the built-in value table.
453 void smsSetCalibration(sms_calibration* calibrationRecord) {
457 if (calibrationRecord) {
458 for (x = 0; x < 3; x++) {
459 zeros[x] = calibrationRecord->zeros[x];
460 onegs[x] = calibrationRecord->onegs[x];
463 defaultCalibration();
468 // Stores the current calibration values as a stored preference.
469 void smsStoreCalibration(void) {
470 if (!debugging) storeCalibration();
473 // Loads the stored preference values into the current calibration.
474 // Returns YES if successful.
475 BOOL smsLoadCalibration(void) {
478 } else if (loadCalibration()) {
481 defaultCalibration();
486 // Deletes any stored calibration, and then takes the current calibration values
487 // from the built-in value table.
488 void smsDeleteCalibration(void) {
491 defaultCalibration();
495 // Fills in the accel record with calibrated acceleration data. Takes
496 // 1-2ms to return a value. Returns 0 if success, error number if failure.
497 int smsGetData(sms_acceleration* accel) {
500 usleep(1500); // Usually takes 1-2 milliseconds
501 time = [NSDate timeIntervalSinceReferenceDate];
502 accel->x = fakeData(time) / 5;
503 accel->y = fakeData(time - 1) / 5;
504 accel->z = fakeData(time - 2) / 5 + 1.0;
507 return getData(accel, true, nil, nil);
511 // Fills in the accel record with uncalibrated acceleration data.
512 // Returns 0 if success, error number if failure.
513 int smsGetUncalibratedData(sms_acceleration* accel) {
516 usleep(1500); // Usually takes 1-2 milliseconds
517 time = [NSDate timeIntervalSinceReferenceDate];
518 accel->x = fakeData(time) * 256 / 5;
519 accel->y = fakeData(time - 1) * 256 / 5;
520 accel->z = fakeData(time - 2) * 256 / 5 + 256;
523 return getData(accel, false, nil, nil);
527 // Returns the length of a raw block of data for the current type of sensor.
528 int smsGetBufferLength(void) {
531 } else if (running) {
532 return sensors[sensorNum].recordSize;
538 // Takes a pointer to accelGetRawLength() bytes; sets those bytes
539 // to return value from sensor. Make darn sure the buffer length is right!
540 void smsGetBufferData(char* buffer) {
541 IOItemCount iSize = recordSize;
542 IOByteCount oSize = recordSize;
543 kern_return_t result;
545 if (debugging || running == NO) {
549 memset(iRecord, 1, iSize);
550 memset(buffer, 0, oSize);
551 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
552 const size_t InStructSize = recordSize;
553 size_t OutStructSize = recordSize;
555 IOConnectCallStructMethod(connection,
556 function, // magic kernel function number
557 (const void*)iRecord, InStructSize, (void*)buffer, &OutStructSize);
558 #else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
559 result = IOConnectMethodStructureIStructureO(connection,
560 function, // magic kernel function number
561 iSize, &oSize, iRecord, buffer);
562 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
564 if (result != KERN_SUCCESS) {
569 // This returns an NSString describing the current calibration in
570 // human-readable form. Also include a description of the machine.
571 NSString* smsGetCalibrationDescription(void) {
573 NSMutableString* s = [[NSMutableString alloc] init];
577 return @"Debugging!";
580 [s appendString:@"---- SeisMac Calibration Record ----\n \n"];
581 [s appendFormat:@"Machine model: %@\n", getModelName()];
582 [s appendFormat:@"OS X build: %@\n", getOSVersion()];
583 [s appendFormat:@"SeisMacLib version %s, record %d\n \n", SMSLIB_VERSION, sensorNum];
584 [s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n", serviceName, function,
586 if (prefIntRead(CALIBRATED_NAME, &success) && success) {
587 [s appendString:@"Calibration values (from calibration):\n"];
589 [s appendString:@"Calibration values (from defaults):\n"];
591 [s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]];
592 [s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]];
593 [s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]];
594 [s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]];
595 [s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]];
596 [s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]];
597 [s appendString:@"---- End Record ----\n"];
601 // Shuts down the accelerometer.
602 void smsShutdown(void) {
605 if (iRecord) free(iRecord);
606 if (oRecord) free(oRecord);
607 IOServiceClose(connection);
611 #pragma mark Internal functions
613 // Loads the current calibration from the stored preferences.
614 // Returns true iff successful.
615 BOOL loadCalibration(void) {
616 BOOL thisSuccess, allSuccess;
621 if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) {
622 // Calibrated. Set all values from saved values.
624 for (x = 0; x < 3; x++) {
625 zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess);
626 allSuccess &= thisSuccess;
627 onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess);
628 allSuccess &= thisSuccess;
636 // Stores the current calibration into the stored preferences.
637 static void storeCalibration(void) {
639 prefIntWrite(CALIBRATED_NAME, 1);
640 for (x = 0; x < 3; x++) {
641 prefFloatWrite(ZERO_NAME(x), zeros[x]);
642 prefFloatWrite(ONEG_NAME(x), onegs[x]);
647 // Sets the calibration to its default values.
648 void defaultCalibration(void) {
650 for (x = 0; x < 3; x++) {
651 zeros[x] = sensors[sensorNum].axes[x].zerog;
652 onegs[x] = sensors[sensorNum].axes[x].oneg;
656 // Deletes the stored preferences.
657 static void deleteCalibration(void) {
660 prefDelete(CALIBRATED_NAME);
661 for (x = 0; x < 3; x++) {
662 prefDelete(ZERO_NAME(x));
663 prefDelete(ONEG_NAME(x));
668 // Read a named floating point value from the stored preferences. Sets
669 // the success boolean based on, you guessed it, whether it succeeds.
670 static float prefFloatRead(NSString* prefName, BOOL* success) {
673 CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName, APP_ID);
674 // If there isn't such a preference, fail
679 CFTypeID typeID = CFGetTypeID(ref);
681 if (typeID == CFNumberGetTypeID()) {
682 // Is it a floating point number?
683 if (CFNumberIsFloatType((CFNumberRef)ref)) {
685 *success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result);
687 // Nope: grab as an integer, and convert to a float.
689 if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) {
696 // Or is it a string (e.g. set by the command line "defaults" command)?
697 } else if (typeID == CFStringGetTypeID()) {
698 result = (float)CFStringGetDoubleValue((CFStringRef)ref);
701 // Can't convert to a number: fail.
708 // Writes a named floating point value to the stored preferences.
709 static void prefFloatWrite(NSString* prefName, float prefValue) {
710 CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &prefValue);
711 CFPreferencesSetAppValue((CFStringRef)prefName, cfFloat, APP_ID);
715 // Reads a named integer value from the stored preferences.
716 static int prefIntRead(NSString* prefName, BOOL* success) {
717 Boolean internalSuccess;
718 CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName, APP_ID, &internalSuccess);
719 *success = internalSuccess;
724 // Writes a named integer value to the stored preferences.
725 static void prefIntWrite(NSString* prefName, int prefValue) {
726 CFPreferencesSetAppValue((CFStringRef)prefName, (CFNumberRef)[NSNumber numberWithInt:prefValue],
730 // Deletes the named preference values.
731 static void prefDelete(NSString* prefName) {
732 CFPreferencesSetAppValue((CFStringRef)prefName, NULL, APP_ID);
735 // Synchronizes the local preferences with the stored preferences.
736 static void prefSynchronize(void) { CFPreferencesAppSynchronize(APP_ID); }
738 // Internal version of accelGetData, with logging
739 int getData(sms_acceleration* accel, int calibrated, id logObject, SEL logSelector) {
740 IOItemCount iSize = recordSize;
741 IOByteCount oSize = recordSize;
742 kern_return_t result;
748 memset(iRecord, 1, iSize);
749 memset(oRecord, 0, oSize);
751 LOG_2ARG(@" Querying device (%u, %d): ", sensors[sensorNum].function,
752 sensors[sensorNum].recordSize);
754 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
755 const size_t InStructSize = recordSize;
756 size_t OutStructSize = recordSize;
758 IOConnectCallStructMethod(connection,
759 function, // magic kernel function number
760 (const void*)iRecord, InStructSize, (void*)oRecord, &OutStructSize);
761 #else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
762 result = IOConnectMethodStructureIStructureO(connection,
763 function, // magic kernel function number
764 iSize, &oSize, iRecord, oRecord);
765 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
767 if (result != KERN_SUCCESS) {
772 LOG(@"succeeded.\n");
774 accel->x = getAxis(0, calibrated);
775 accel->y = getAxis(1, calibrated);
776 accel->z = getAxis(2, calibrated);
781 // Given the returned record, extracts the value of the given axis. If
782 // calibrated, then zero G is 0.0, and one G is 1.0.
783 float getAxis(int which, int calibrated) {
784 // Get various values (to make code cleaner)
785 int indx = sensors[sensorNum].axes[which].index;
786 int size = sensors[sensorNum].axes[which].size;
787 float zerog = zeros[which];
788 float oneg = onegs[which];
789 // Storage for value to be returned
792 // Although the values in the returned record should have the proper
793 // endianness, we still have to get it into the proper end of value.
794 #if (BYTE_ORDER == BIG_ENDIAN)
795 // On PowerPC processors
796 memcpy(((char*)&value) + (sizeof(int) - size), &oRecord[indx], size);
798 #if (BYTE_ORDER == LITTLE_ENDIAN)
799 // On Intel processors
800 memcpy(&value, &oRecord[indx], size);
803 value = signExtend(value, size);
806 // Scale and shift for zero.
807 return ((float)(value - zerog)) / oneg;
813 // Extends the sign, given the length of the value.
814 int signExtend(int value, int size) {
818 if (value & 0x00000080) value |= 0xffffff00;
821 if (value & 0x00008000) value |= 0xffff0000;
824 if (value & 0x00800000) value |= 0xff000000;
830 // Returns the model name of the computer (e.g. "MacBookPro1,1")
831 NSString* getModelName(void) {
833 size_t len = sizeof(model);
834 int name[2] = {CTL_HW, HW_MODEL};
837 if (sysctl(name, 2, &model, &len, NULL, 0) == 0) {
838 result = [NSString stringWithFormat:@"%s", model];
846 // Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)")
847 NSString* getOSVersion(void) {
848 NSDictionary* dict = [NSDictionary
849 dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
850 NSString* versionString = [dict objectForKey:@"ProductVersion"];
851 NSString* buildString = [dict objectForKey:@"ProductBuildVersion"];
852 NSString* wholeString = [NSString stringWithFormat:@"%@ (build %@)", versionString, buildString];
856 // Returns time within the current second in microseconds.
857 // long getMicroseconds() {
859 // gettimeofday(&t, 0);
863 // Returns fake data given the time. Range is +/-1.
864 float fakeData(NSTimeInterval time) {
865 long secs = lround(floor(time));
866 int secsMod3 = secs % 3;
867 double angle = time * 10 * M_PI * 2;
868 double mag = exp(-(time - (secs - secsMod3)) * 2);
869 return sin(angle) * mag;