Tuesday, January 6, 2009

Use the Mac's accelerometer in the iPhone Simulator


The accelerometer is not simulated in the current iPhone SDK (for iPhone OS 2.2).

Otto Chrons has posted a
neat package that sends accelerometer data from an iPhone or iPod Touch to an application running in the iPhone simulator. This package is in two parts: a ‘server’ that runs on your iPhone hardware and a ‘client’ in the form of source code that you can drop into your project (adding just an #import statement).

This works really well, except when:
1) you don’t have an iPhone or iPod Touch, or
2) you’re not registered in the Standard or Enterprise iPhone Development programme; I wasn’t registered at the time, and so I had no way to push Otto’s server application to my iPod Touch.

But then it occurred to me that my MacBook did have an accelerometer. If only that could serve data to the fake accelerometer in the simulator... So I looked, and providentially enough,
UniMotion hove into view.

So I browsed the source for the two packages for a bit, with a view to nefarious smash-and-grab, but by more happy chance it turned out that the relevant pieces in both were modular enough to fit as they were, with a little python for glue.

Otto’s client expects a string with timestamp and x,y,z readings over UDP, and UniMotion’s
motion is kind enough to spew x, y and z readings to standard output, though they’re not oriented in the same directions as those in the iPhone SDK’s UIAcceleration.

On my late 2006 MacBook,
motion emits x,y,z with a positive magnitude to the left, towards me and downwards, respectively. UIAcceleration expects readings to be positive to the right, away from me and upwards, respectively.

So all I needed to do was to grab
motion’s output (with the -f flag for acceleration in units of G), multipy each value by -1, add a timestamp and broadcast over UDP on the given port in the format that Otto’s client expects. I did need to run calibrate in the UniMotion suite first.

Here’s my python glue:


import sys, socket, time, traceback

kCFAbsoluteTimeIntervalSince1970 = 978307200.0 # from CFDate.c

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('',0))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while 1:
  try:
    line = sys.stdin.readline()[:-1] # read line and strip EOL
    fields = line.split() # split around space character
    floatfields = map(float, fields) # convert to floats

    # transform co-ordinate system, from SMS- to UIAcceleration-format,
    # for my macbook (late 2006)
    x, y, z = map(lambda x: -1 * x, floatfields)

    # change epoch to be compatible with CFAbsoluteTimeGetCurrent()
    currentTime = time.time() - kCFAbsoluteTimeIntervalSince1970

    accdata = ','.join(map(str,('ACC: 0',currentTime,x,y,z)))

    sock.sendto(accdata, ('<broadcast>', 10552))
    # print '% .2f % .2f % .2f' % (x, y, z)
  except (ValueError, KeyboardInterrupt):
    sock.close()
    sys.exit(-1)
  except:
    traceback.print_exc()


I call this
sendaccsim.py and run it as:
$ ./motion -f 17 | python sendaccsim.py

It’s just a bit awkward swinging the Mac around like a tea tray.

21 comments:

AB said...

I thought you would fiddle with it, when did u decide to poke and peek ;)

Unknown said...

haha.. I don't know how you serve your guests tea, but hopefully it's not on them :)

Nice post... look forward to reading more.

tomjensen said...

When you say you needed to run calibrate, what exactly did you do with it? I can't understand what to do with it, and I'm not able to get it running on my machine

Thanks for posting this up.

Karan Vasudeva said...

'calibrate' will run in a loop, printing out current orientation values to stdout and expecting scale and offset calibration values on stdin in every iteration.

For the Mac lying on a surface as horizontal as you can arrange, you should expect the three values in the f: field to be as close as possible to 0.00, 0.00 and -1.00 respectively, as you tweak calibration values.

When you exit, these values are saved to ~/Library/Preferences/com.ramsayl.UniMotion.plist (I think) and subsequently are picked up by 'motion'.

daveola said...

Fantastic - this was just what I needed to start development before getting a developer license. Thanks!

Unknown said...

I am also facing the problem of shift of choti-I matra one position right than its normal position on an linux based ebmedded board with Firefox 3.0.1. Any suggestions will be greatly appreciated.

Unknown said...

We mentioned this method in our IAQ (Infrequently Answered Questions) on our website: http://www.appalbum.com/developers/iaq.html#q5

JMathews said...

This is a very cool trick thanks. But a note, this only works if the simulated orientation of the phone is portrait. For example, if you're in landscape then X & Y on the accelerometer are swapped.

JMathews said...

The other thing that's a little strange is how much network activity this creates. It reminds me of the Doom 1.0 days, lots of broadcasting all over the network!

Karan Vasudeva said...

:) I guess you could reduce the accelerometer sampling rate.

JMathews said...

It seems unnecessary that this script be sending anything at all on the network let alone broadcasting.

If you make these changes the "traffic" won't leave the local machine.

#sock.bind(('',0))
sock.bind(('',10522))
#sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

# sock.sendto(accdata, ('{broadcast}', 10552))
sock.sendto(accdata, ('localhost', 10552))

Karan Vasudeva said...

Yes, I agree that that would make more sense!

Unknown said...

Will it be possible to use macbook's multi-touch pad to send multi-touch event to iphone simulator?

Karan Vasudeva said...

I guess so. You could try the code here:

http://www.steike.com/code/multitouch/

I haven't been able to run it myself because my MacBook doesn't have a multitouch trackpad. I tried!

Anonymous said...

Hello Mr Karan Vasudeva,
I am also looking to do this on my mac. Unfortunately I am very new to Mac. I tried to copy and save the python code and try to run it, but errors are appearing.

Here is what I did:
1. Downloaded the unimotion and extracted to a folder.
2. Run Terminal and type make at the unimotion folder (ie, cd etc etc etc, then "make").
3. After it creates a binary folder (bin), i copy the sendaccsim.py file into the motion folder. The type ./motion -f 17 | python sendaccsim.py and enter

But an error came up:
File "sendaccsim.py", line 12
SyntaxError: Non-ASCII character '\xc2' in file sendaccsim.py on line 12, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

What is wrong? Am I missing something?

Unknown said...
This comment has been removed by a blog administrator.
Ben Weiss said...

Okay, I finally got this working after a bit of fiddling.

Copying and pasting the python code into a text file resulted in some inadvertent hard-spaces (option-space). You can search and replace these hard-space characters with ordinary spaces, and that fixes the non-ASCII character issue. I also had to do some re-indenting to make the python file run correctly.

Then I had to tweak the makefiles because they were formatted:

all: blablabla

when they need to be (with a carriage return and a tab):

all:
blablabla

At this point the makefile seems to build things correctly, although I still get the following:

make -C unimotion
../Frameworks/libUniMotion.dylib ../bin/libUniMotion.dylib
../Frameworks/libUniMotion.dylib: ../Frameworks/libUniMotion.dylib: cannot execute binary file
make[1]: *** [all] Error 126
make: *** [sub-unimotion] Error 2

Anyway, then I put sendaccsim.py in the 'bin' directory next to motion and libUniMotion.dylib, navigate to that directory, and run the script as described. I temporarily commented in the printf in the python script, to verify that it was running correctly. I also applied JMathews' changes.

Then I downloaded the linked accelerator-simulator code, and spliced it into my iPhone application project as described. Then run the iPhone app in the simulator. Voila, it worked!

Ben Weiss said...
This comment has been removed by the author.
Ben Weiss said...

blogger killed the formatting in my post. The format in the makefiles needs to be:

all:
[tab] blablabla

Karan Vasudeva said...

Thanks, Ben. I'll remember to quote code in a pre-formatted span in future.

Larry said...

Very, very helpful. Was really wondering how to simulate the accelerometer on just a MacBook. Thanks.