Documentation : Hardware | Software | Tutorials    Wardrobe : Jackets | Applications
Papers : Notes | Proposals | Research    Administration : Stock | Minutes | Progress

Wearable Application Framework

2003.02.10

This document describes the application framework used in our more current projects like Millennium Square or A Walk in the Wireless Woods that we took to ISWC in Seattle in October 2002.

Motivation

As more and more applications were created for the Mark II CyberJacket it became clear that these applications shared many of the same qualities:

  1. Several different devices (accelerometers, gps, ultrasonics, compass, etc) can be used for a particular application and not all applications use the same devices. To ease the creation of new wearable applications it would be sensible to make the code for each device as modular as possible.
  2. All applications have a real time constraint to output to a particular device. For all of our applications thus far this has been the audio device (/dev/dsp) but, in the general case, it could be anything from a hand-held display to a set of LEDs on the jacket. All applications, therefore, are governed by a real-time loop with timing that depends on the frequency of ouput.

To provide the maximum re-use of code and ease for the programmer a framework was created that exploits these similarities between applications.

The Framework

The code for the framework is in the CVS repository in Applications/Streaming/Framework/. The files framework.h and framework.c are what we examine here.

Within framework.h a structure called the WearableDevice is defined. It ecapsulates all of the general qualites of a wearable device. Here is its definition:

// Device type flags
#define WDT_INPUT  1
#define WDT_OUTPUT 2

typedef struct {

  // File descriptor
  int fd;

  // Device type flag - constructed by bitwise ORing WDT defines.
  int type;

  // Constructor - routine that initializes the device.  Returns a valid
  // file descriptor for the device on success, -1 otherwise.
  int (*constructor)();

  // Input handling function - handles input from the device.  Will be NULL
  // for output only devices.
  void (*inputHandler)();

  // Output building function - routine that builds data for output to the
  // device.  Will be NULL for input only devices.
  void (*outputBuilder)();

  // Output execution function - function that outputs data to the device.
  void (*output)();

  // Destructor - routine that destroys all of the devices related data.
  // close() is called on the file descriptor for the device immediately
  // after this function is called (so there is no need to do so).
  void (*destructor)();

} WearableDevice;

The structure assumes that all devices will have a file descriptor (so that the application can communicate with it) and that it will have a type: input (WDT_INPUT), output (WDT_INPUT) or both (WDT_INPUT | WDT_OUPUT). The function pointers are there to allow the programmer to define what the application does on certain events. These events are as follows:

  1. Construction (all devices). When the application is initialised the (*constructor)() function is called. The code defined for this function should open and initialise the deivce and return its file descriptor.
  2. Input Handling (input devices). When the file descriptor for the device is flagged (*inputHandler)() is called. It is assumed that this function will handle the input waiting on this device.
  3. Output Building (output devices). Before data is output to an output device its content must be prepared. (*outputBuilder)() is used to do just that. Preparing spatialised sound buffefs for output is a good example of how to use this function.
  4. Output (output devices). (*output)() should output the data prepared in (*outputBuilder)() to the device.
  5. Destruction (all devices). When the application terminates (*desctructor)() is called on all devices. This function should close all open file descriptors and free any allocated memory related to the device.

These events are governed by the main program, defined in framework.c. Before I outline the operation of the main program, however, there is a global variable and a few functions that the programmer must be aware of. Here is a snippet of code from framework.c:

int appFinished = 0;    // To control exiting of the real-time loop

// Function that sets the period of the real-time loop
extern void appSetLoopTime(Fraction *timeForOutput);

// Function that registers all of the application devices
extern void appRegisterDevices();

Exiting the program is achieved by setting the global variable appFinished to something other than 0. appSetLoopTime() sets the period of the real-time loop for the application. The period is usually configured to accomadate the output devices' timing constraints. appRegisterDevices() must be defined by the programmer to create and initialise the function pointers of all of the WearableDevice structures required by the application. A function called:

void appRegisterDevice(WearableDevice *wd);

defined in framework.h must be used within appRegisterDevices() to register each device/structure within the framework.

Pseudo code for the operation of the main program is as follows:


main()

  appRegisterDevices()
  for each device registered call constructor()
  appSetLoopTime()

  while not appFinished {

    for each device registered call outputBuilder()

    while output is not required (calculated using looptime)
      for each device with input waiting call inputHandler()

    for each output device call output()
  }

  for each output device call destructor()

The flow of the program follows that of most real-time applications. By defining it in a seperate framework like we have done here, the programmer need only focus on writing input and output handling code for each device in the application. Example applications using this framework can be found in the CVS repository in Applications/Streaming/IswcDemo/ or Applications/Streaming/MillSq1/.


This page last updated February 11, 2003
The material displayed is provided 'as is'. Contents may not be reused without prior permission.
For problems or questions regarding this web contact Cliff Randell.