Update the state based on the incoming data
        
        This function updates the state of the DeviceSpec object, giving values for each
        axis [x,y,z,roll,pitch,yaw] in range [-1.0, 1.0]
        The state tuple is only set when all 6 DoF have been read correctly.
        
        The timestamp (in fractional seconds since the start of the program)  is written as element "t"
        
        If callback is provided, it is called on with a copy of the current state tuple.
        If button_callback is provided, it is called only on button state changes with the argument (state, button_state).
        
        Parameters:
            data    The data for this HID event, as returned by the HID callback

def process(self, data):
        """
        Update the state based on the incoming data
        
        This function updates the state of the DeviceSpec object, giving values for each
        axis [x,y,z,roll,pitch,yaw] in range [-1.0, 1.0]
        The state tuple is only set when all 6 DoF have been read correctly.
        
        The timestamp (in fractional seconds since the start of the program)  is written as element "t"
        
        If callback is provided, it is called on with a copy of the current state tuple.
        If button_callback is provided, it is called only on button state changes with the argument (state, button_state).
        
        Parameters:
            data    The data for this HID event, as returned by the HID callback
                        
        """
        button_changed = False        
    
        for name,(chan,b1,b2,flip) in self.mappings.items():
            if data[0] == chan:
                self.dict_state[name] = flip * to_int16(data[b1], data[b2])/float(self.axis_scale)
                
        for button_index, (chan, byte, bit) in enumerate(self.button_mapping):
            if data[0] == chan:
                button_changed = True               
                # update the button vector
                mask = 1<<bit
                self.dict_state["buttons"][button_index] = 1 if (data[byte] & mask) != 0 else 0                                
                                        
        self.dict_state["t"] = high_acc_clock()
        
        # must receive both parts of the 6DOF state before we return the state dictionary
        if len(self.dict_state)==8:
            self.tuple_state = SpaceNavigator(**self.dict_state)
        
        # call any attached callbacks
        if self.callback:
            self.callback(self.tuple_state)                        
        
        # only call the button callback if the button state actually changed
        if self.button_callback and button_changed:            
            self.button_callback(self.tuple_state, self.tuple_state.buttons)