• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Accuracy of timestamping streamed data (code included - long)
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Accuracy of timestamping streamed data (code included - long)


  • Subject: Accuracy of timestamping streamed data (code included - long)
  • From: Hank Heijink <email@hidden>
  • Date: Mon, 25 Sep 2006 17:07:32 -0400

Hi everyone,

I'm new to this list, and fairly new to Cocoa (although by now I've worked my way through Hillegass and Dalrymple + same). I'm working on the following problem:

For an application in behavioral science, I'm recording cursor movements on an old (10+ years) and big (3 by 5 feet) Quora digitizing tablet. The tablet streams 6-byte coordinates at a constant rate of about 160 Hz, and I want to get as constant a recording rate as I can get. After saving my data to a file, I find I'm fairly accurate, that is, I've timestamped my coordinates such that they're between 5 and 7.5 ms apart. The standard deviation of the differences in time is about 0.3 ms.

My question is, can I achieve even better accuracy, and if so, how? I'd like a more constant rate - I know the output rate of the tablet is more constant than this, and I'm not sure what causes the variability. My application is built in release mode and it's the only one running. Not sure if background daemons are having an effect and I don't know much about USB timing accuracy, but I guess at this level of accuracy, everything could have an effect...

I hope I'm clear... Any thoughts are much appreciated!

Thanks a lot,
Hank

PS. For the more curious... the tablet is connected to my 2.0GHz MacBook with an RS422 cable that connects to a Keyspan serial-port-to- USB converter.

-----
Here's what I've done (with the relevant code, apologies for the amount). I've taken the naive approach:


In my interface, a button detaches an NSThread using detachNewThreadSelector:toTarget:withObject:, and this is the selector.

- (void)launchThread
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

// Set the priority of this thread to something high to improve timing accuracy
if (![NSThread setThreadPriority:1.0])
NSLog(@"Failed to set priority");

// Initialize a timer with a firing rate of 0.005 seconds (5 ms or 200 Hz) --
// Well over the tablet's streaming frequency of about 160 Hz.
timer = [NSTimer timerWithTimeInterval:0.005
target:self
selector:@selector(updateTabletCursor)
userInfo:nil
repeats:YES];

[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];

while ([runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

[pool release];
}


This is the method that does the work. shouldStopStreaming is set from the interface, recordingArray is an instance variable (NSMutableArray) of self.

- (void)updateTabletCursor
{
	if ([tablet shouldStopStreaming]) {
		[timer invalidate];
		return;
	}

	// Read one coordinate from the tablet
	MVCoordinate *coordinate = [tablet read];
	[self setCurrentCoordinate:coordinate];

	// Do we need to record the movement?
	if ([self isRecording]) {
		[recordingArray addObject:coordinate];
	}
}

This is the read function. The timestamping happens in the init code of the MVCoordinate class by calling mach_absolute_time. It gets converted to nanoseconds when the recordingArray is written to a file.

- (id)read
{
unsigned char buffer[60];
unsigned char *indexInBuffer;
int bytesRead, i;

if ((bytesRead = ReadSerialPort(fileDescriptor, buffer)) <= 0)
return nil;

indexInBuffer = buffer;

// A new coordinate starts with a 1 on the most significant bit of the first byte.
// Search for that byte
for (i = 0; i < bytesRead; i++) {
if (*(indexInBuffer + i) & 0x80) break;
}

return [[[MVCoordinate alloc] initWithRawData:indexInBuffer] autorelease];
}


And finally, the ReadSerialPort function. PortAvailableForReading is a wrapper for select(). The two integer arguments are used to make the timeval struct.

int ReadSerialPort (int fileDescriptor, unsigned char *stringToRead)
{
	ssize_t bytesRead = 0;
	unsigned char buffer[60]; // We're only expecting 6 bytes

	// Have we got anything to read?
	if (!PortAvailableForReading(fileDescriptor, 0, 20000)) {
		fprintf (stderr, "Nothing to read\n");
		return bytesRead;
	} else {
		bytesRead = read (fileDescriptor,
						  buffer,
						  sizeof (buffer));
	}


memcpy(stringToRead, buffer, bytesRead);

	return bytesRead;
}

-----
Hank Heijink
Postdoctoral Research Fellow
Center for Neurobiology and Behavior
Columbia University Medical Center

_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Accuracy of timestamping streamed data (code included - long)
      • From: Scott Ribe <email@hidden>
    • Re: Accuracy of timestamping streamed data (code included - long)
      • From: Jakob Olesen <email@hidden>
    • Re: Accuracy of timestamping streamed data (code included - long)
      • From: Dado Colussi <email@hidden>
    • Re: Accuracy of timestamping streamed data (code included - long)
      • From: Ricky Sharp <email@hidden>
  • Prev by Date: Tracing Cocoa Functions
  • Next by Date: Partially transparent child window
  • Previous by thread: Re: Re: Tracing Cocoa Functions
  • Next by thread: Re: Accuracy of timestamping streamed data (code included - long)
  • Index(es):
    • Date
    • Thread