hal/cocoa/smslib.mm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * smslib.m
michael@0 3 *
michael@0 4 * SMSLib Sudden Motion Sensor Access Library
michael@0 5 * Copyright (c) 2010 Suitable Systems
michael@0 6 * All rights reserved.
michael@0 7 *
michael@0 8 * Developed by: Daniel Griscom
michael@0 9 * Suitable Systems
michael@0 10 * http://www.suitable.com
michael@0 11 *
michael@0 12 * Permission is hereby granted, free of charge, to any person obtaining a
michael@0 13 * copy of this software and associated documentation files (the
michael@0 14 * "Software"), to deal with the Software without restriction, including
michael@0 15 * without limitation the rights to use, copy, modify, merge, publish,
michael@0 16 * distribute, sublicense, and/or sell copies of the Software, and to
michael@0 17 * permit persons to whom the Software is furnished to do so, subject to
michael@0 18 * the following conditions:
michael@0 19 *
michael@0 20 * - Redistributions of source code must retain the above copyright notice,
michael@0 21 * this list of conditions and the following disclaimers.
michael@0 22 *
michael@0 23 * - Redistributions in binary form must reproduce the above copyright
michael@0 24 * notice, this list of conditions and the following disclaimers in the
michael@0 25 * documentation and/or other materials provided with the distribution.
michael@0 26 *
michael@0 27 * - Neither the names of Suitable Systems nor the names of its
michael@0 28 * contributors may be used to endorse or promote products derived from
michael@0 29 * this Software without specific prior written permission.
michael@0 30 *
michael@0 31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
michael@0 32 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
michael@0 33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
michael@0 34 * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
michael@0 35 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
michael@0 36 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
michael@0 37 * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
michael@0 38 *
michael@0 39 * For more information about SMSLib, see
michael@0 40 * <http://www.suitable.com/tools/smslib.html>
michael@0 41 * or contact
michael@0 42 * Daniel Griscom
michael@0 43 * Suitable Systems
michael@0 44 * 1 Centre Street, Suite 204
michael@0 45 * Wakefield, MA 01880
michael@0 46 * (781) 665-0053
michael@0 47 *
michael@0 48 */
michael@0 49
michael@0 50 #import <IOKit/IOKitLib.h>
michael@0 51 #import <sys/sysctl.h>
michael@0 52 #import <math.h>
michael@0 53 #import "smslib.h"
michael@0 54
michael@0 55 #pragma mark Internal structures
michael@0 56
michael@0 57 // Represents a single axis of a type of sensor.
michael@0 58 typedef struct axisStruct {
michael@0 59 int enabled; // Non-zero if axis is valid in this sensor
michael@0 60 int index; // Location in struct of first byte
michael@0 61 int size; // Number of bytes
michael@0 62 float zerog; // Value meaning "zero g"
michael@0 63 float oneg; // Change in value meaning "increase of one g"
michael@0 64 // (can be negative if axis sensor reversed)
michael@0 65 } axisStruct;
michael@0 66
michael@0 67 // Represents the configuration of a type of sensor.
michael@0 68 typedef struct sensorSpec {
michael@0 69 const char *model; // Prefix of model to be tested
michael@0 70 const char *name; // Name of device to be read
michael@0 71 unsigned int function; // Kernel function index
michael@0 72 int recordSize; // Size of record to be sent/received
michael@0 73 axisStruct axes[3]; // Description of three axes (X, Y, Z)
michael@0 74 } sensorSpec;
michael@0 75
michael@0 76 // Configuration of all known types of sensors. The configurations are
michael@0 77 // tried in order until one succeeds in returning data.
michael@0 78 // All default values are set here, but each axis' zerog and oneg values
michael@0 79 // may be changed to saved (calibrated) values.
michael@0 80 //
michael@0 81 // These values came from SeisMaCalibrate calibration reports. In general I've
michael@0 82 // found the following:
michael@0 83 // - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs
michael@0 84 // are different (and in one case two axes are swapped)
michael@0 85 // - PowerBooks and iBooks all have sensors centered on 0, and reading
michael@0 86 // 50-53 steps per gravity (but with differing polarities!)
michael@0 87 // - PowerBooks and iBooks of the same model all have the same axis polarities
michael@0 88 // - PowerBook and iBook access methods are model- and OS version-specific
michael@0 89 //
michael@0 90 // So, the sequence of tests is:
michael@0 91 // - Try model-specific access methods. Note that the test is for a match to the
michael@0 92 // beginning of the model name, e.g. the record with model name "MacBook"
michael@0 93 // matches computer models "MacBookPro1,2" and "MacBook1,1" (and ""
michael@0 94 // matches any model).
michael@0 95 // - If no model-specific record's access fails, then try each model-independent
michael@0 96 // access method in order, stopping when one works.
michael@0 97 static const sensorSpec sensors[] = {
michael@0 98 // ****** Model-dependent methods ******
michael@0 99 // The PowerBook5,6 is one of the G4 models that seems to lose
michael@0 100 // SMS access until the next reboot.
michael@0 101 {"PowerBook5,6", "IOI2CMotionSensor", 21, 60, {
michael@0 102 {1, 0, 1, 0, 51.5},
michael@0 103 {1, 1, 1, 0, -51.5},
michael@0 104 {1, 2, 1, 0, -51.5}
michael@0 105 }
michael@0 106 },
michael@0 107 // The PowerBook5,7 is one of the G4 models that seems to lose
michael@0 108 // SMS access until the next reboot.
michael@0 109 {"PowerBook5,7", "IOI2CMotionSensor", 21, 60, {
michael@0 110 {1, 0, 1, 0, 51.5},
michael@0 111 {1, 1, 1, 0, 51.5},
michael@0 112 {1, 2, 1, 0, 51.5}
michael@0 113 }
michael@0 114 },
michael@0 115 // Access seems to be reliable on the PowerBook5,8
michael@0 116 {"PowerBook5,8", "PMUMotionSensor", 21, 60, {
michael@0 117 {1, 0, 1, 0, -51.5},
michael@0 118 {1, 1, 1, 0, 51.5},
michael@0 119 {1, 2, 1, 0, -51.5}
michael@0 120 }
michael@0 121 },
michael@0 122 // Access seems to be reliable on the PowerBook5,9
michael@0 123 {"PowerBook5,9", "PMUMotionSensor", 21, 60, {
michael@0 124 {1, 0, 1, 0, 51.5},
michael@0 125 {1, 1, 1, 0, -51.5},
michael@0 126 {1, 2, 1, 0, -51.5}
michael@0 127 }
michael@0 128 },
michael@0 129 // The PowerBook6,7 is one of the G4 models that seems to lose
michael@0 130 // SMS access until the next reboot.
michael@0 131 {"PowerBook6,7", "IOI2CMotionSensor", 21, 60, {
michael@0 132 {1, 0, 1, 0, 51.5},
michael@0 133 {1, 1, 1, 0, 51.5},
michael@0 134 {1, 2, 1, 0, 51.5}
michael@0 135 }
michael@0 136 },
michael@0 137 // The PowerBook6,8 is one of the G4 models that seems to lose
michael@0 138 // SMS access until the next reboot.
michael@0 139 {"PowerBook6,8", "IOI2CMotionSensor", 21, 60, {
michael@0 140 {1, 0, 1, 0, 51.5},
michael@0 141 {1, 1, 1, 0, 51.5},
michael@0 142 {1, 2, 1, 0, 51.5}
michael@0 143 }
michael@0 144 },
michael@0 145 // MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes.
michael@0 146 {"MacBookPro2,1", "SMCMotionSensor", 5, 40, {
michael@0 147 {1, 0, 2, 0, 251},
michael@0 148 {1, 2, 2, 0, -251},
michael@0 149 {1, 4, 2, 0, -251}
michael@0 150 }
michael@0 151 },
michael@0 152 // MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07.
michael@0 153 // NOTE! The 17" machines have the signs of their X and Y axes reversed
michael@0 154 // from this calibration, but there's no clear way to discriminate between
michael@0 155 // the two machines.
michael@0 156 {"MacBookPro3,1", "SMCMotionSensor", 5, 40, {
michael@0 157 {1, 0, 2, 0, -251},
michael@0 158 {1, 2, 2, 0, 251},
michael@0 159 {1, 4, 2, 0, -251}
michael@0 160 }
michael@0 161 },
michael@0 162 // ... specs?
michael@0 163 {"MacBook5,2", "SMCMotionSensor", 5, 40, {
michael@0 164 {1, 0, 2, 0, -251},
michael@0 165 {1, 2, 2, 0, 251},
michael@0 166 {1, 4, 2, 0, -251}
michael@0 167 }
michael@0 168 },
michael@0 169 // ... specs?
michael@0 170 {"MacBookPro5,1", "SMCMotionSensor", 5, 40, {
michael@0 171 {1, 0, 2, 0, -251},
michael@0 172 {1, 2, 2, 0, -251},
michael@0 173 {1, 4, 2, 0, 251}
michael@0 174 }
michael@0 175 },
michael@0 176 // ... specs?
michael@0 177 {"MacBookPro5,2", "SMCMotionSensor", 5, 40, {
michael@0 178 {1, 0, 2, 0, -251},
michael@0 179 {1, 2, 2, 0, -251},
michael@0 180 {1, 4, 2, 0, 251}
michael@0 181 }
michael@0 182 },
michael@0 183 // This is speculative, based on a single user's report. Looks like the X and Y axes
michael@0 184 // are swapped. This is true for no other known Appple laptop.
michael@0 185 {"MacBookPro5,3", "SMCMotionSensor", 5, 40, {
michael@0 186 {1, 2, 2, 0, -251},
michael@0 187 {1, 0, 2, 0, -251},
michael@0 188 {1, 4, 2, 0, -251}
michael@0 189 }
michael@0 190 },
michael@0 191 // ... specs?
michael@0 192 {"MacBookPro5,4", "SMCMotionSensor", 5, 40, {
michael@0 193 {1, 0, 2, 0, -251},
michael@0 194 {1, 2, 2, 0, -251},
michael@0 195 {1, 4, 2, 0, 251}
michael@0 196 }
michael@0 197 },
michael@0 198 // ****** Model-independent methods ******
michael@0 199 // Seen once with PowerBook6,8 under system 10.3.9; I suspect
michael@0 200 // other G4-based 10.3.* systems might use this
michael@0 201 {"", "IOI2CMotionSensor", 24, 60, {
michael@0 202 {1, 0, 1, 0, 51.5},
michael@0 203 {1, 1, 1, 0, 51.5},
michael@0 204 {1, 2, 1, 0, 51.5}
michael@0 205 }
michael@0 206 },
michael@0 207 // PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8
michael@0 208 // under OS X 10.4.*
michael@0 209 {"", "IOI2CMotionSensor", 21, 60, {
michael@0 210 {1, 0, 1, 0, 51.5},
michael@0 211 {1, 1, 1, 0, 51.5},
michael@0 212 {1, 2, 1, 0, 51.5}
michael@0 213 }
michael@0 214 },
michael@0 215 // PowerBook5,8 , PowerBook5,9 under OS X 10.4.*
michael@0 216 {"", "PMUMotionSensor", 21, 60, {
michael@0 217 // Each has two out of three gains negative, but it's different
michael@0 218 // for the different models. So, this will be right in two out
michael@0 219 // of three axis for either model.
michael@0 220 {1, 0, 1, 0, -51.5},
michael@0 221 {1, 1, 1, -6, -51.5},
michael@0 222 {1, 2, 1, 0, -51.5}
michael@0 223 }
michael@0 224 },
michael@0 225 // All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15")
michael@0 226 // is Kionix KXM52-1050 three-axis accelerometer chip. Data is at
michael@0 227 // http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models
michael@0 228 // that use this are:
michael@0 229 // MacBook1,1
michael@0 230 // MacBook2,1
michael@0 231 // MacBook3,1
michael@0 232 // MacBook4,1
michael@0 233 // MacBook5,1
michael@0 234 // MacBook6,1
michael@0 235 // MacBookAir1,1
michael@0 236 // MacBookPro1,1
michael@0 237 // MacBookPro1,2
michael@0 238 // MacBookPro4,1
michael@0 239 // MacBookPro5,5
michael@0 240 {"", "SMCMotionSensor", 5, 40, {
michael@0 241 {1, 0, 2, 0, 251},
michael@0 242 {1, 2, 2, 0, 251},
michael@0 243 {1, 4, 2, 0, 251}
michael@0 244 }
michael@0 245 }
michael@0 246 };
michael@0 247
michael@0 248 #define SENSOR_COUNT (sizeof(sensors)/sizeof(sensorSpec))
michael@0 249
michael@0 250 #pragma mark Internal prototypes
michael@0 251
michael@0 252 static int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector);
michael@0 253 static float getAxis(int which, int calibrated);
michael@0 254 static int signExtend(int value, int size);
michael@0 255 static NSString *getModelName(void);
michael@0 256 static NSString *getOSVersion(void);
michael@0 257 static BOOL loadCalibration(void);
michael@0 258 static void storeCalibration(void);
michael@0 259 static void defaultCalibration(void);
michael@0 260 static void deleteCalibration(void);
michael@0 261 static int prefIntRead(NSString *prefName, BOOL *success);
michael@0 262 static void prefIntWrite(NSString *prefName, int prefValue);
michael@0 263 static float prefFloatRead(NSString *prefName, BOOL *success);
michael@0 264 static void prefFloatWrite(NSString *prefName, float prefValue);
michael@0 265 static void prefDelete(NSString *prefName);
michael@0 266 static void prefSynchronize(void);
michael@0 267 // static long getMicroseconds(void);
michael@0 268 float fakeData(NSTimeInterval time);
michael@0 269
michael@0 270 #pragma mark Static variables
michael@0 271
michael@0 272 static int debugging = NO; // True if debugging (synthetic data)
michael@0 273 static io_connect_t connection; // Connection for reading accel values
michael@0 274 static int running = NO; // True if we successfully started
michael@0 275 static unsigned int sensorNum = 0; // The current index into sensors[]
michael@0 276 static const char *serviceName; // The name of the current service
michael@0 277 static char *iRecord, *oRecord; // Pointers to read/write records for sensor
michael@0 278 static int recordSize; // Size of read/write records
michael@0 279 static unsigned int function; // Which kernel function should be used
michael@0 280 static float zeros[3]; // X, Y and Z zero calibration values
michael@0 281 static float onegs[3]; // X, Y and Z one-g calibration values
michael@0 282
michael@0 283 #pragma mark Defines
michael@0 284
michael@0 285 // Pattern for building axis letter from axis number
michael@0 286 #define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z")
michael@0 287 // Name of configuration for given axis' zero (axis specified by integer)
michael@0 288 #define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)]
michael@0 289 // Name of configuration for given axis' oneg (axis specified by integer)
michael@0 290 #define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)]
michael@0 291 // Name of "Is calibrated" preference
michael@0 292 #define CALIBRATED_NAME (@"Calibrated")
michael@0 293 // Application domain for SeisMac library
michael@0 294 #define APP_ID ((CFStringRef)@"com.suitable.SeisMacLib")
michael@0 295
michael@0 296 // These #defines make the accelStartup code a LOT easier to read.
michael@0 297 #undef LOG
michael@0 298 #define LOG(message) \
michael@0 299 if (logObject) { \
michael@0 300 [logObject performSelector:logSelector withObject:message]; \
michael@0 301 }
michael@0 302 #define LOG_ARG(format, var1) \
michael@0 303 if (logObject) { \
michael@0 304 [logObject performSelector:logSelector \
michael@0 305 withObject:[NSString stringWithFormat:format, var1]]; \
michael@0 306 }
michael@0 307 #define LOG_2ARG(format, var1, var2) \
michael@0 308 if (logObject) { \
michael@0 309 [logObject performSelector:logSelector \
michael@0 310 withObject:[NSString stringWithFormat:format, var1, var2]]; \
michael@0 311 }
michael@0 312 #define LOG_3ARG(format, var1, var2, var3) \
michael@0 313 if (logObject) { \
michael@0 314 [logObject performSelector:logSelector \
michael@0 315 withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \
michael@0 316 }
michael@0 317
michael@0 318 #pragma mark Function definitions
michael@0 319
michael@0 320 // This starts up the accelerometer code, trying each possible sensor
michael@0 321 // specification. Note that for logging purposes it
michael@0 322 // takes an object and a selector; the object's selector is then invoked
michael@0 323 // with a single NSString as argument giving progress messages. Example
michael@0 324 // logging method:
michael@0 325 // - (void)logMessage: (NSString *)theString
michael@0 326 // which would be used in accelStartup's invocation thusly:
michael@0 327 // result = accelStartup(self, @selector(logMessage:));
michael@0 328 // If the object is nil, then no logging is done. Sets calibation from built-in
michael@0 329 // value table. Returns ACCEL_SUCCESS for success, and other (negative)
michael@0 330 // values for various failures (returns value indicating result of
michael@0 331 // most successful trial).
michael@0 332 int smsStartup(id logObject, SEL logSelector) {
michael@0 333 io_iterator_t iterator;
michael@0 334 io_object_t device;
michael@0 335 kern_return_t result;
michael@0 336 sms_acceleration accel;
michael@0 337 int failure_result = SMS_FAIL_MODEL;
michael@0 338
michael@0 339 running = NO;
michael@0 340 debugging = NO;
michael@0 341
michael@0 342 NSString *modelName = getModelName();
michael@0 343
michael@0 344 LOG_ARG(@"Machine model: %@\n", modelName);
michael@0 345 LOG_ARG(@"OS X version: %@\n", getOSVersion());
michael@0 346 LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION);
michael@0 347
michael@0 348 for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) {
michael@0 349
michael@0 350 // Set up all specs for this type of sensor
michael@0 351 serviceName = sensors[sensorNum].name;
michael@0 352 recordSize = sensors[sensorNum].recordSize;
michael@0 353 function = sensors[sensorNum].function;
michael@0 354
michael@0 355 LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n",
michael@0 356 serviceName, function, recordSize);
michael@0 357
michael@0 358 NSString *targetName = [NSString stringWithCString:sensors[sensorNum].model
michael@0 359 encoding:NSMacOSRomanStringEncoding];
michael@0 360 LOG_ARG(@" Comparing model name to target \"%@\": ", targetName);
michael@0 361 if ([targetName length] == 0 || [modelName hasPrefix:targetName]) {
michael@0 362 LOG(@"success.\n");
michael@0 363 } else {
michael@0 364 LOG(@"failure.\n");
michael@0 365 // Don't need to increment failure_result.
michael@0 366 continue;
michael@0 367 }
michael@0 368
michael@0 369 LOG(@" Fetching dictionary for service: ");
michael@0 370 CFMutableDictionaryRef dict = IOServiceMatching(serviceName);
michael@0 371
michael@0 372 if (dict) {
michael@0 373 LOG(@"success.\n");
michael@0 374 } else {
michael@0 375 LOG(@"failure.\n");
michael@0 376 if (failure_result < SMS_FAIL_DICTIONARY) {
michael@0 377 failure_result = SMS_FAIL_DICTIONARY;
michael@0 378 }
michael@0 379 continue;
michael@0 380 }
michael@0 381
michael@0 382 LOG(@" Getting list of matching services: ");
michael@0 383 result = IOServiceGetMatchingServices(kIOMasterPortDefault,
michael@0 384 dict,
michael@0 385 &iterator);
michael@0 386
michael@0 387 if (result == KERN_SUCCESS) {
michael@0 388 LOG(@"success.\n");
michael@0 389 } else {
michael@0 390 LOG_ARG(@"failure, with return value 0x%x.\n", result);
michael@0 391 if (failure_result < SMS_FAIL_LIST_SERVICES) {
michael@0 392 failure_result = SMS_FAIL_LIST_SERVICES;
michael@0 393 }
michael@0 394 continue;
michael@0 395 }
michael@0 396
michael@0 397 LOG(@" Getting first device in list: ");
michael@0 398 device = IOIteratorNext(iterator);
michael@0 399
michael@0 400 if (device == 0) {
michael@0 401 LOG(@"failure.\n");
michael@0 402 if (failure_result < SMS_FAIL_NO_SERVICES) {
michael@0 403 failure_result = SMS_FAIL_NO_SERVICES;
michael@0 404 }
michael@0 405 continue;
michael@0 406 } else {
michael@0 407 LOG(@"success.\n");
michael@0 408 LOG(@" Opening device: ");
michael@0 409 }
michael@0 410
michael@0 411 result = IOServiceOpen(device, mach_task_self(), 0, &connection);
michael@0 412
michael@0 413 if (result != KERN_SUCCESS) {
michael@0 414 LOG_ARG(@"failure, with return value 0x%x.\n", result);
michael@0 415 IOObjectRelease(device);
michael@0 416 if (failure_result < SMS_FAIL_OPENING) {
michael@0 417 failure_result = SMS_FAIL_OPENING;
michael@0 418 }
michael@0 419 continue;
michael@0 420 } else if (connection == 0) {
michael@0 421 LOG_ARG(@"'success', but didn't get a connection (return value was: 0x%x).\n", result);
michael@0 422 IOObjectRelease(device);
michael@0 423 if (failure_result < SMS_FAIL_CONNECTION) {
michael@0 424 failure_result = SMS_FAIL_CONNECTION;
michael@0 425 }
michael@0 426 continue;
michael@0 427 } else {
michael@0 428 IOObjectRelease(device);
michael@0 429 LOG(@"success.\n");
michael@0 430 }
michael@0 431 LOG(@" Testing device.\n");
michael@0 432
michael@0 433 defaultCalibration();
michael@0 434
michael@0 435 iRecord = (char*) malloc(recordSize);
michael@0 436 oRecord = (char*) malloc(recordSize);
michael@0 437
michael@0 438 running = YES;
michael@0 439 result = getData(&accel, true, logObject, logSelector);
michael@0 440 running = NO;
michael@0 441
michael@0 442 if (result) {
michael@0 443 LOG_ARG(@" Failure testing device, with result 0x%x.\n", result);
michael@0 444 free(iRecord);
michael@0 445 iRecord = 0;
michael@0 446 free(oRecord);
michael@0 447 oRecord = 0;
michael@0 448 if (failure_result < SMS_FAIL_ACCESS) {
michael@0 449 failure_result = SMS_FAIL_ACCESS;
michael@0 450 }
michael@0 451 continue;
michael@0 452 } else {
michael@0 453 LOG(@" Success testing device!\n");
michael@0 454 running = YES;
michael@0 455 return SMS_SUCCESS;
michael@0 456 }
michael@0 457 }
michael@0 458 return failure_result;
michael@0 459 }
michael@0 460
michael@0 461 // This starts up the library in debug mode, ignoring the actual hardware.
michael@0 462 // Returned data is in the form of 1Hz sine waves, with the X, Y and Z
michael@0 463 // axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
michael@0 464 // "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
michael@0 465 // Z axes centered on 1 (calibrated) or 256 (uncalibrated).
michael@0 466 // Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
michael@0 467 int smsDebugStartup(id logObject, SEL logSelector) {
michael@0 468 LOG(@"Starting up in debug mode\n");
michael@0 469 debugging = YES;
michael@0 470 return SMS_SUCCESS;
michael@0 471 }
michael@0 472
michael@0 473 // Returns the current calibration values.
michael@0 474 void smsGetCalibration(sms_calibration *calibrationRecord) {
michael@0 475 int x;
michael@0 476
michael@0 477 for (x = 0; x < 3; x++) {
michael@0 478 calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]);
michael@0 479 calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]);
michael@0 480 }
michael@0 481 }
michael@0 482
michael@0 483 // Sets the calibration, but does NOT store it as a preference. If the argument
michael@0 484 // is nil then the current calibration is set from the built-in value table.
michael@0 485 void smsSetCalibration(sms_calibration *calibrationRecord) {
michael@0 486 int x;
michael@0 487
michael@0 488 if (!debugging) {
michael@0 489 if (calibrationRecord) {
michael@0 490 for (x = 0; x < 3; x++) {
michael@0 491 zeros[x] = calibrationRecord->zeros[x];
michael@0 492 onegs[x] = calibrationRecord->onegs[x];
michael@0 493 }
michael@0 494 } else {
michael@0 495 defaultCalibration();
michael@0 496 }
michael@0 497 }
michael@0 498 }
michael@0 499
michael@0 500 // Stores the current calibration values as a stored preference.
michael@0 501 void smsStoreCalibration(void) {
michael@0 502 if (!debugging)
michael@0 503 storeCalibration();
michael@0 504 }
michael@0 505
michael@0 506 // Loads the stored preference values into the current calibration.
michael@0 507 // Returns YES if successful.
michael@0 508 BOOL smsLoadCalibration(void) {
michael@0 509 if (debugging) {
michael@0 510 return YES;
michael@0 511 } else if (loadCalibration()) {
michael@0 512 return YES;
michael@0 513 } else {
michael@0 514 defaultCalibration();
michael@0 515 return NO;
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 // Deletes any stored calibration, and then takes the current calibration values
michael@0 520 // from the built-in value table.
michael@0 521 void smsDeleteCalibration(void) {
michael@0 522 if (!debugging) {
michael@0 523 deleteCalibration();
michael@0 524 defaultCalibration();
michael@0 525 }
michael@0 526 }
michael@0 527
michael@0 528 // Fills in the accel record with calibrated acceleration data. Takes
michael@0 529 // 1-2ms to return a value. Returns 0 if success, error number if failure.
michael@0 530 int smsGetData(sms_acceleration *accel) {
michael@0 531 NSTimeInterval time;
michael@0 532 if (debugging) {
michael@0 533 usleep(1500); // Usually takes 1-2 milliseconds
michael@0 534 time = [NSDate timeIntervalSinceReferenceDate];
michael@0 535 accel->x = fakeData(time)/5;
michael@0 536 accel->y = fakeData(time - 1)/5;
michael@0 537 accel->z = fakeData(time - 2)/5 + 1.0;
michael@0 538 return true;
michael@0 539 } else {
michael@0 540 return getData(accel, true, nil, nil);
michael@0 541 }
michael@0 542 }
michael@0 543
michael@0 544 // Fills in the accel record with uncalibrated acceleration data.
michael@0 545 // Returns 0 if success, error number if failure.
michael@0 546 int smsGetUncalibratedData(sms_acceleration *accel) {
michael@0 547 NSTimeInterval time;
michael@0 548 if (debugging) {
michael@0 549 usleep(1500); // Usually takes 1-2 milliseconds
michael@0 550 time = [NSDate timeIntervalSinceReferenceDate];
michael@0 551 accel->x = fakeData(time) * 256 / 5;
michael@0 552 accel->y = fakeData(time - 1) * 256 / 5;
michael@0 553 accel->z = fakeData(time - 2) * 256 / 5 + 256;
michael@0 554 return true;
michael@0 555 } else {
michael@0 556 return getData(accel, false, nil, nil);
michael@0 557 }
michael@0 558 }
michael@0 559
michael@0 560 // Returns the length of a raw block of data for the current type of sensor.
michael@0 561 int smsGetBufferLength(void) {
michael@0 562 if (debugging) {
michael@0 563 return 0;
michael@0 564 } else if (running) {
michael@0 565 return sensors[sensorNum].recordSize;
michael@0 566 } else {
michael@0 567 return 0;
michael@0 568 }
michael@0 569 }
michael@0 570
michael@0 571 // Takes a pointer to accelGetRawLength() bytes; sets those bytes
michael@0 572 // to return value from sensor. Make darn sure the buffer length is right!
michael@0 573 void smsGetBufferData(char *buffer) {
michael@0 574 IOItemCount iSize = recordSize;
michael@0 575 IOByteCount oSize = recordSize;
michael@0 576 kern_return_t result;
michael@0 577
michael@0 578 if (debugging || running == NO) {
michael@0 579 return;
michael@0 580 }
michael@0 581
michael@0 582 memset(iRecord, 1, iSize);
michael@0 583 memset(buffer, 0, oSize);
michael@0 584 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
michael@0 585 const size_t InStructSize = recordSize;
michael@0 586 size_t OutStructSize = recordSize;
michael@0 587 result = IOConnectCallStructMethod(connection,
michael@0 588 function, // magic kernel function number
michael@0 589 (const void *)iRecord,
michael@0 590 InStructSize,
michael@0 591 (void *)buffer,
michael@0 592 &OutStructSize
michael@0 593 );
michael@0 594 #else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
michael@0 595 result = IOConnectMethodStructureIStructureO(connection,
michael@0 596 function, // magic kernel function number
michael@0 597 iSize,
michael@0 598 &oSize,
michael@0 599 iRecord,
michael@0 600 buffer
michael@0 601 );
michael@0 602 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
michael@0 603
michael@0 604 if (result != KERN_SUCCESS) {
michael@0 605 running = NO;
michael@0 606 }
michael@0 607 }
michael@0 608
michael@0 609 // This returns an NSString describing the current calibration in
michael@0 610 // human-readable form. Also include a description of the machine.
michael@0 611 NSString *smsGetCalibrationDescription(void) {
michael@0 612 BOOL success;
michael@0 613 NSMutableString *s = [[NSMutableString alloc] init];
michael@0 614
michael@0 615 if (debugging) {
michael@0 616 [s release];
michael@0 617 return @"Debugging!";
michael@0 618 }
michael@0 619
michael@0 620 [s appendString:@"---- SeisMac Calibration Record ----\n \n"];
michael@0 621 [s appendFormat:@"Machine model: %@\n",
michael@0 622 getModelName()];
michael@0 623 [s appendFormat:@"OS X build: %@\n",
michael@0 624 getOSVersion()];
michael@0 625 [s appendFormat:@"SeisMacLib version %s, record %d\n \n",
michael@0 626 SMSLIB_VERSION, sensorNum];
michael@0 627 [s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n",
michael@0 628 serviceName, function, recordSize];
michael@0 629 if (prefIntRead(CALIBRATED_NAME, &success) && success) {
michael@0 630 [s appendString:@"Calibration values (from calibration):\n"];
michael@0 631 } else {
michael@0 632 [s appendString:@"Calibration values (from defaults):\n"];
michael@0 633 }
michael@0 634 [s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]];
michael@0 635 [s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]];
michael@0 636 [s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]];
michael@0 637 [s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]];
michael@0 638 [s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]];
michael@0 639 [s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]];
michael@0 640 [s appendString:@"---- End Record ----\n"];
michael@0 641 return s;
michael@0 642 }
michael@0 643
michael@0 644 // Shuts down the accelerometer.
michael@0 645 void smsShutdown(void) {
michael@0 646 if (!debugging) {
michael@0 647 running = NO;
michael@0 648 if (iRecord) free(iRecord);
michael@0 649 if (oRecord) free(oRecord);
michael@0 650 IOServiceClose(connection);
michael@0 651 }
michael@0 652 }
michael@0 653
michael@0 654 #pragma mark Internal functions
michael@0 655
michael@0 656 // Loads the current calibration from the stored preferences.
michael@0 657 // Returns true iff successful.
michael@0 658 BOOL loadCalibration(void) {
michael@0 659 BOOL thisSuccess, allSuccess;
michael@0 660 int x;
michael@0 661
michael@0 662 prefSynchronize();
michael@0 663
michael@0 664 if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) {
michael@0 665 // Calibrated. Set all values from saved values.
michael@0 666 allSuccess = YES;
michael@0 667 for (x = 0; x < 3; x++) {
michael@0 668 zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess);
michael@0 669 allSuccess &= thisSuccess;
michael@0 670 onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess);
michael@0 671 allSuccess &= thisSuccess;
michael@0 672 }
michael@0 673 return allSuccess;
michael@0 674 }
michael@0 675
michael@0 676 return NO;
michael@0 677 }
michael@0 678
michael@0 679 // Stores the current calibration into the stored preferences.
michael@0 680 static void storeCalibration(void) {
michael@0 681 int x;
michael@0 682 prefIntWrite(CALIBRATED_NAME, 1);
michael@0 683 for (x = 0; x < 3; x++) {
michael@0 684 prefFloatWrite(ZERO_NAME(x), zeros[x]);
michael@0 685 prefFloatWrite(ONEG_NAME(x), onegs[x]);
michael@0 686 }
michael@0 687 prefSynchronize();
michael@0 688 }
michael@0 689
michael@0 690
michael@0 691 // Sets the calibration to its default values.
michael@0 692 void defaultCalibration(void) {
michael@0 693 int x;
michael@0 694 for (x = 0; x < 3; x++) {
michael@0 695 zeros[x] = sensors[sensorNum].axes[x].zerog;
michael@0 696 onegs[x] = sensors[sensorNum].axes[x].oneg;
michael@0 697 }
michael@0 698 }
michael@0 699
michael@0 700 // Deletes the stored preferences.
michael@0 701 static void deleteCalibration(void) {
michael@0 702 int x;
michael@0 703
michael@0 704 prefDelete(CALIBRATED_NAME);
michael@0 705 for (x = 0; x < 3; x++) {
michael@0 706 prefDelete(ZERO_NAME(x));
michael@0 707 prefDelete(ONEG_NAME(x));
michael@0 708 }
michael@0 709 prefSynchronize();
michael@0 710 }
michael@0 711
michael@0 712 // Read a named floating point value from the stored preferences. Sets
michael@0 713 // the success boolean based on, you guessed it, whether it succeeds.
michael@0 714 static float prefFloatRead(NSString *prefName, BOOL *success) {
michael@0 715 float result = 0.0f;
michael@0 716
michael@0 717 CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName,
michael@0 718 APP_ID);
michael@0 719 // If there isn't such a preference, fail
michael@0 720 if (ref == NULL) {
michael@0 721 *success = NO;
michael@0 722 return result;
michael@0 723 }
michael@0 724 CFTypeID typeID = CFGetTypeID(ref);
michael@0 725 // Is it a number?
michael@0 726 if (typeID == CFNumberGetTypeID()) {
michael@0 727 // Is it a floating point number?
michael@0 728 if (CFNumberIsFloatType((CFNumberRef)ref)) {
michael@0 729 // Yup: grab it.
michael@0 730 *success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result);
michael@0 731 } else {
michael@0 732 // Nope: grab as an integer, and convert to a float.
michael@0 733 long num;
michael@0 734 if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) {
michael@0 735 result = num;
michael@0 736 *success = YES;
michael@0 737 } else {
michael@0 738 *success = NO;
michael@0 739 }
michael@0 740 }
michael@0 741 // Or is it a string (e.g. set by the command line "defaults" command)?
michael@0 742 } else if (typeID == CFStringGetTypeID()) {
michael@0 743 result = (float)CFStringGetDoubleValue((CFStringRef)ref);
michael@0 744 *success = YES;
michael@0 745 } else {
michael@0 746 // Can't convert to a number: fail.
michael@0 747 *success = NO;
michael@0 748 }
michael@0 749 CFRelease(ref);
michael@0 750 return result;
michael@0 751 }
michael@0 752
michael@0 753 // Writes a named floating point value to the stored preferences.
michael@0 754 static void prefFloatWrite(NSString *prefName, float prefValue) {
michael@0 755 CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault,
michael@0 756 kCFNumberFloatType,
michael@0 757 &prefValue);
michael@0 758 CFPreferencesSetAppValue((CFStringRef)prefName,
michael@0 759 cfFloat,
michael@0 760 APP_ID);
michael@0 761 CFRelease(cfFloat);
michael@0 762 }
michael@0 763
michael@0 764 // Reads a named integer value from the stored preferences.
michael@0 765 static int prefIntRead(NSString *prefName, BOOL *success) {
michael@0 766 Boolean internalSuccess;
michael@0 767 CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName,
michael@0 768 APP_ID,
michael@0 769 &internalSuccess);
michael@0 770 *success = internalSuccess;
michael@0 771
michael@0 772 return result;
michael@0 773 }
michael@0 774
michael@0 775 // Writes a named integer value to the stored preferences.
michael@0 776 static void prefIntWrite(NSString *prefName, int prefValue) {
michael@0 777 CFPreferencesSetAppValue((CFStringRef)prefName,
michael@0 778 (CFNumberRef)[NSNumber numberWithInt:prefValue],
michael@0 779 APP_ID);
michael@0 780 }
michael@0 781
michael@0 782 // Deletes the named preference values.
michael@0 783 static void prefDelete(NSString *prefName) {
michael@0 784 CFPreferencesSetAppValue((CFStringRef)prefName,
michael@0 785 NULL,
michael@0 786 APP_ID);
michael@0 787 }
michael@0 788
michael@0 789 // Synchronizes the local preferences with the stored preferences.
michael@0 790 static void prefSynchronize(void) {
michael@0 791 CFPreferencesAppSynchronize(APP_ID);
michael@0 792 }
michael@0 793
michael@0 794 // Internal version of accelGetData, with logging
michael@0 795 int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector) {
michael@0 796 IOItemCount iSize = recordSize;
michael@0 797 IOByteCount oSize = recordSize;
michael@0 798 kern_return_t result;
michael@0 799
michael@0 800 if (running == NO) {
michael@0 801 return -1;
michael@0 802 }
michael@0 803
michael@0 804 memset(iRecord, 1, iSize);
michael@0 805 memset(oRecord, 0, oSize);
michael@0 806
michael@0 807 LOG_2ARG(@" Querying device (%u, %d): ",
michael@0 808 sensors[sensorNum].function, sensors[sensorNum].recordSize);
michael@0 809
michael@0 810 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
michael@0 811 const size_t InStructSize = recordSize;
michael@0 812 size_t OutStructSize = recordSize;
michael@0 813 result = IOConnectCallStructMethod(connection,
michael@0 814 function, // magic kernel function number
michael@0 815 (const void *)iRecord,
michael@0 816 InStructSize,
michael@0 817 (void *)oRecord,
michael@0 818 &OutStructSize
michael@0 819 );
michael@0 820 #else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
michael@0 821 result = IOConnectMethodStructureIStructureO(connection,
michael@0 822 function, // magic kernel function number
michael@0 823 iSize,
michael@0 824 &oSize,
michael@0 825 iRecord,
michael@0 826 oRecord
michael@0 827 );
michael@0 828 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
michael@0 829
michael@0 830 if (result != KERN_SUCCESS) {
michael@0 831 LOG(@"failed.\n");
michael@0 832 running = NO;
michael@0 833 return result;
michael@0 834 } else {
michael@0 835 LOG(@"succeeded.\n");
michael@0 836
michael@0 837 accel->x = getAxis(0, calibrated);
michael@0 838 accel->y = getAxis(1, calibrated);
michael@0 839 accel->z = getAxis(2, calibrated);
michael@0 840 return 0;
michael@0 841 }
michael@0 842 }
michael@0 843
michael@0 844 // Given the returned record, extracts the value of the given axis. If
michael@0 845 // calibrated, then zero G is 0.0, and one G is 1.0.
michael@0 846 float getAxis(int which, int calibrated) {
michael@0 847 // Get various values (to make code cleaner)
michael@0 848 int indx = sensors[sensorNum].axes[which].index;
michael@0 849 int size = sensors[sensorNum].axes[which].size;
michael@0 850 float zerog = zeros[which];
michael@0 851 float oneg = onegs[which];
michael@0 852 // Storage for value to be returned
michael@0 853 int value = 0;
michael@0 854
michael@0 855 // Although the values in the returned record should have the proper
michael@0 856 // endianness, we still have to get it into the proper end of value.
michael@0 857 #if (BYTE_ORDER == BIG_ENDIAN)
michael@0 858 // On PowerPC processors
michael@0 859 memcpy(((char *)&value) + (sizeof(int) - size), &oRecord[indx], size);
michael@0 860 #endif
michael@0 861 #if (BYTE_ORDER == LITTLE_ENDIAN)
michael@0 862 // On Intel processors
michael@0 863 memcpy(&value, &oRecord[indx], size);
michael@0 864 #endif
michael@0 865
michael@0 866 value = signExtend(value, size);
michael@0 867
michael@0 868 if (calibrated) {
michael@0 869 // Scale and shift for zero.
michael@0 870 return ((float)(value - zerog)) / oneg;
michael@0 871 } else {
michael@0 872 return value;
michael@0 873 }
michael@0 874 }
michael@0 875
michael@0 876 // Extends the sign, given the length of the value.
michael@0 877 int signExtend(int value, int size) {
michael@0 878 // Extend sign
michael@0 879 switch (size) {
michael@0 880 case 1:
michael@0 881 if (value & 0x00000080)
michael@0 882 value |= 0xffffff00;
michael@0 883 break;
michael@0 884 case 2:
michael@0 885 if (value & 0x00008000)
michael@0 886 value |= 0xffff0000;
michael@0 887 break;
michael@0 888 case 3:
michael@0 889 if (value & 0x00800000)
michael@0 890 value |= 0xff000000;
michael@0 891 break;
michael@0 892 }
michael@0 893 return value;
michael@0 894 }
michael@0 895
michael@0 896 // Returns the model name of the computer (e.g. "MacBookPro1,1")
michael@0 897 NSString *getModelName(void) {
michael@0 898 char model[32];
michael@0 899 size_t len = sizeof(model);
michael@0 900 int name[2] = {CTL_HW, HW_MODEL};
michael@0 901 NSString *result;
michael@0 902
michael@0 903 if (sysctl(name, 2, &model, &len, NULL, 0) == 0) {
michael@0 904 result = [NSString stringWithFormat:@"%s", model];
michael@0 905 } else {
michael@0 906 result = @"";
michael@0 907 }
michael@0 908
michael@0 909 return result;
michael@0 910 }
michael@0 911
michael@0 912 // Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)")
michael@0 913 NSString *getOSVersion(void) {
michael@0 914 NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:
michael@0 915 @"/System/Library/CoreServices/SystemVersion.plist"];
michael@0 916 NSString *versionString = [dict objectForKey:@"ProductVersion"];
michael@0 917 NSString *buildString = [dict objectForKey:@"ProductBuildVersion"];
michael@0 918 NSString *wholeString = [NSString stringWithFormat:@"%@ (build %@)",
michael@0 919 versionString, buildString];
michael@0 920 return wholeString;
michael@0 921 }
michael@0 922
michael@0 923 // Returns time within the current second in microseconds.
michael@0 924 // long getMicroseconds() {
michael@0 925 // struct timeval t;
michael@0 926 // gettimeofday(&t, 0);
michael@0 927 // return t.tv_usec;
michael@0 928 //}
michael@0 929
michael@0 930 // Returns fake data given the time. Range is +/-1.
michael@0 931 float fakeData(NSTimeInterval time) {
michael@0 932 long secs = lround(floor(time));
michael@0 933 int secsMod3 = secs % 3;
michael@0 934 double angle = time * 10 * M_PI * 2;
michael@0 935 double mag = exp(-(time - (secs - secsMod3)) * 2);
michael@0 936 return sin(angle) * mag;
michael@0 937 }
michael@0 938

mercurial