This commit is contained in:
2011-02-04 13:01:56 +00:00
parent 6ff0592e4f
commit df91eab859
40 changed files with 5199 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.control;
import org.wiigee.util.Log;
/**
* The mother of all classes. :-) It's just used as parent class
* to print version information and later on maybe dynamic configuring
* of the whole wiimote system... detecting plugins and devices automatically
* maybe. :)
*
* @author Benjamin 'BePo' Poppinga
*/
public class Wiigee {
protected static String version = "1.5.6";
protected static String releasedate = "20090817";
protected Wiigee() {
Log.write("This is wiigee version "+version+" ("+releasedate+")");
}
}

View File

@@ -0,0 +1,103 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.control;
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import org.wiigee.util.Log;
import org.wiigee.device.Wiimote;
public class WiimoteDeviceDiscovery implements DiscoveryListener {
private Vector<RemoteDevice> devices;
private boolean isInquiring;
private final Object lock;
public WiimoteDeviceDiscovery(Object lock) {
super();
this.lock = lock;
this.devices = new Vector<RemoteDevice>();
}
public void deviceDiscovered(RemoteDevice newdevice, DeviceClass devclass) {
Log.write("Device discovered: " + newdevice.getBluetoothAddress() + " - ");
// add the device to the vector
if (!devices.contains(newdevice) &&
devclass.getMajorDeviceClass() == 1280 &&
devclass.getMinorDeviceClass() == 4) {
Log.write("Is a Wiimote!");
devices.addElement(newdevice);
} else {
Log.write("Is NOT a Wiimote OR you're using an incompatible Bluetooth Stack.");
}
}
public void inquiryCompleted(int discType) {
switch (discType) {
case WiimoteDeviceDiscovery.INQUIRY_COMPLETED:
Log.write("Inquiry completed.");
break;
case WiimoteDeviceDiscovery.INQUIRY_ERROR:
Log.write("Inquiry error.");
break;
case WiimoteDeviceDiscovery.INQUIRY_TERMINATED:
Log.write("Inquiry terminated.");
break;
}
synchronized (this.lock) {
this.lock.notify();
}
}
public void serviceSearchCompleted(int arg0, int arg1) {
// not necessary
}
public void servicesDiscovered(int arg0, ServiceRecord[] arg1) {
// not necessary
}
public boolean isInquirying() {
return this.isInquiring;
}
public Vector<Wiimote> getDiscoveredWiimotes() throws IOException {
Vector<Wiimote> wiimotes = new Vector<Wiimote>();
for (int i = 0; i < devices.size(); i++) {
wiimotes.add(new Wiimote(devices.elementAt(i).getBluetoothAddress(), true, true));
}
return wiimotes;
}
}

View File

@@ -0,0 +1,210 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.control;
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import org.wiigee.util.Log;
import org.wiigee.device.Wiimote;
import org.wiigee.event.AccelerationListener;
import org.wiigee.event.GestureListener;
import org.wiigee.filter.Filter;
// Singleton
public class WiimoteWiigee extends Wiigee {
protected static String pluginversion = "1.5.6";
protected static String pluginreleasedate = "20090817";
private static final Object lock = new Object();
private Vector<Wiimote> devices;
public WiimoteWiigee() {
super();
String stack;
String stackVersion;
String l2capFeature;
String bluecoveVersion;
Log.write("This is wiigee-plugin-wiimote version " + pluginversion + " (" + pluginreleasedate + ")");
// Bluecove
bluecoveVersion = LocalDevice.getProperty("bluecove");
if(!bluecoveVersion.equals("")) {
stack = LocalDevice.getProperty("bluecove.stack");
stackVersion = LocalDevice.getProperty("bluecove.stack.version");
Log.write("You are using the "+stack+" Bluetooth stack (Version "+stackVersion+")");
l2capFeature = LocalDevice.getProperty("bluecove.feature.l2cap");
Log.write("L2CAP supported: "+l2capFeature);
if(l2capFeature.equals("true")) {
Log.write("wiigee: found a supported stack!");
// set min id for Bluecove
Log.write(Log.DEBUG, "JSR82 PSM Minimum Restriction -- OFF", null);
System.setProperty("bluecove.jsr82.psm_minimum_off", "true");
}
} else {
Log.write("No Bluecove Library detected - trying anyway...");
}
}
/**
* Automatically discovers Wiimotes nearby and connects to
* the first responding Wiimote visible. For multiple managed
* instances of the Wiimote, please use getDevices().
*
* @return First visible Wiimote. Null otherwise.
* @throws java.io.IOException
*/
public Wiimote getDevice() throws IOException {
this.devices = this.discoverWiimotes();
if(this.devices.size()>0) {
return devices.elementAt(0);
}
return null;
}
/**
* Returns an array of all discovered Wiimotes. The count
* of Devices depends on your computers bluetooth capabilities.
* Usually up to 7 Wiimotes/Devices can be connected.
*
* @return Array of discovered wiimotes or null if
* none discoverd.
*/
public Wiimote[] getDevices() throws IOException {
this.devices = this.discoverWiimotes();
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).setLED(i + 1);
}
Wiimote[] out = new Wiimote[this.devices.size()];
for (int i = 0; i < this.devices.size(); i++) {
out[i] = this.devices.elementAt(i);
}
if(out.length>0) {
return out;
}
return null;
}
/**
* Discover the wiimotes around the bluetooth host and
* make them available public via getWiimotes method.
*
* @return Array of discovered wiimotes.
*/
private Vector<Wiimote> discoverWiimotes() throws IOException {
WiimoteDeviceDiscovery deviceDiscovery = new WiimoteDeviceDiscovery(lock);
LocalDevice localDevice = LocalDevice.getLocalDevice();
Log.write("Your Computers Bluetooth MAC: " + localDevice.getBluetoothAddress());
Log.write("Starting device inquiry...");
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
discoveryAgent.startInquiry(DiscoveryAgent.GIAC, deviceDiscovery);
try {
synchronized (lock) {
lock.wait();
}
} catch (InterruptedException e) {
Log.write("Problems during device discovery.");
e.printStackTrace();
}
Log.write("Device discovery completed!");
return deviceDiscovery.getDiscoveredWiimotes();
}
/**
* Returns the number of wiimotes discovered.
*
* @return Number of wiimotes discovered.
*/
public int getNumberOfDevices() {
if(this.devices!=null) {
return this.devices.size();
}
return 0;
}
/**
* Sets the Trainbutton for all wiimotes;
*
* @param b Button encoding, see static Wiimote values
*/
public void setTrainButton(int b) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).setTrainButton(b);
}
}
/**
* Sets the Recognitionbutton for all wiimotes;
*
* @param b Button encoding, see static Wiimote values
*/
public void setRecognitionButton(int b) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).setRecognitionButton(b);
}
}
/**
* Sets the CloseGesturebutton for all wiimotes;
*
* @param b Button encoding, see static Wiimote values
*/
public void setCloseGestureButton(int b) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).setCloseGestureButton(b);
}
}
public void addDeviceListener(AccelerationListener listener) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).addAccelerationListener(listener);
}
}
public void addGestureListener(GestureListener listener) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).addGestureListener(listener);
}
}
public void addAccelerationFilter(Filter filter) {
for (int i = 0; i < this.devices.size(); i++) {
this.devices.elementAt(i).addAccelerationFilter(filter);
}
}
}

View File

@@ -0,0 +1,234 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.device;
import java.io.IOException;
import java.util.Vector;
import org.wiigee.logic.*;
import org.wiigee.event.*;
import org.wiigee.filter.*;
/**
* Abstract representation of a device with very basic functionalities. This
* class should be derived from, if anybody plans to add a new class of devices,
* like Wiimote or AndroidDevice does. This class mainly consist of filter
* management, recognition control and core event control.
*
* @author Benjamin 'BePo' Poppinga
*/
public class Device {
// Fixed number values.
public static final int MOTION = 0;
// Buttons for action coordination
protected int recognitionbutton;
protected int trainbutton;
protected int closegesturebutton;
// Functional
protected boolean accelerationEnabled;
// Filters, can filter the data stream
protected Vector<Filter> accfilters = new Vector<Filter>();
// Listeners, receive generated events
protected Vector<AccelerationListener> accelerationlistener = new Vector<AccelerationListener>();
protected Vector<ButtonListener> buttonlistener = new Vector<ButtonListener>();
// Processing unit to analyze the data
protected ProcessingUnit processingunit = new TriggeredProcessingUnit();
public Device(boolean autofiltering) {
if (autofiltering) {
this.addAccelerationFilter(new IdleStateFilter());
this.addAccelerationFilter(new MotionDetectFilter(this));
this.addAccelerationFilter(new DirectionalEquivalenceFilter());
}
this.addAccelerationListener(this.processingunit);
this.addButtonListener(this.processingunit);
}
/**
* Adds a Filter for processing the acceleration values.
* @param filter The Filter instance.
*/
public void addAccelerationFilter(Filter filter) {
this.accfilters.add(filter);
}
/**
* Resets all the accfilters, which are resetable.
* Sometimes they have to be resettet if a new gesture starts.
*/
public void resetAccelerationFilters() {
for (int i = 0; i < this.accfilters.size(); i++) {
this.accfilters.elementAt(i).reset();
}
}
/**
* Adds an AccelerationListener to the Device. Everytime an acceleration
* on the Device is performed the AccelerationListener would receive
* an event of this action.
*
* @param listener The Listener.
*/
public void addAccelerationListener(AccelerationListener listener) {
this.accelerationlistener.add(listener);
}
/**
* Adds a ButtonListener to the Device. Everytime a Button has been
* pressed or released, the Listener would be notified about this via
* the corresponding Events.
*
* @param listener The Listener.
*/
public void addButtonListener(ButtonListener listener) {
this.buttonlistener.add(listener);
}
/**
* Adds a GestureListener to the Device. Everytime a gesture
* is performed the GestureListener would receive an event of
* this gesture.
*
* @param listener The Listener.
*/
public void addGestureListener(GestureListener listener) {
this.processingunit.addGestureListener(listener);
}
public int getRecognitionButton() {
return this.recognitionbutton;
}
public void setRecognitionButton(int b) {
this.recognitionbutton = b;
}
public int getTrainButton() {
return this.trainbutton;
}
public void setTrainButton(int b) {
this.trainbutton = b;
}
public int getCloseGestureButton() {
return this.closegesturebutton;
}
public void setCloseGestureButton(int b) {
this.closegesturebutton = b;
}
public ProcessingUnit getProcessingUnit() {
return this.processingunit;
}
public void setAccelerationEnabled(boolean enabled) throws IOException {
this.accelerationEnabled = enabled;
}
public void loadGesture(String filename) {
this.processingunit.loadGesture(filename);
}
public void saveGesture(int id, String filename) {
this.processingunit.saveGesture(id, filename);
}
// ###### Event-Methoden
/** Fires an acceleration event.
* @param vector Consists of three values:
* acceleration on X, Y and Z axis.
*/
public void fireAccelerationEvent(double[] vector) {
for (int i = 0; i < this.accfilters.size(); i++) {
vector = this.accfilters.get(i).filter(vector);
// cannot return here if null, because of time-dependent accfilters
}
// don't need to create an event if filtered away
if (vector != null) {
// calculate the absolute value for the accelerationevent
double absvalue = Math.sqrt((vector[0] * vector[0]) +
(vector[1] * vector[1]) + (vector[2] * vector[2]));
AccelerationEvent w = new AccelerationEvent(this,
vector[0], vector[1], vector[2], absvalue);
for (int i = 0; i < this.accelerationlistener.size(); i++) {
this.accelerationlistener.get(i).accelerationReceived(w);
}
}
} // fireaccelerationevent
/** Fires a button pressed event.
* @param button
* Integer value of the pressed button.
*/
public void fireButtonPressedEvent(int button) {
ButtonPressedEvent w = new ButtonPressedEvent(this, button);
for (int i = 0; i < this.buttonlistener.size(); i++) {
this.buttonlistener.get(i).buttonPressReceived(w);
}
if (w.isRecognitionInitEvent() || w.isTrainInitEvent()) {
this.resetAccelerationFilters();
}
}
/** Fires a button released event.
*/
public void fireButtonReleasedEvent(int button) {
ButtonReleasedEvent w = new ButtonReleasedEvent(this, button);
for (int i = 0; i < this.buttonlistener.size(); i++) {
this.buttonlistener.get(i).buttonReleaseReceived(w);
}
}
/**
* Fires a motion start event.
*/
public void fireMotionStartEvent() {
MotionStartEvent w = new MotionStartEvent(this);
for (int i = 0; i < this.accelerationlistener.size(); i++) {
this.accelerationlistener.get(i).motionStartReceived(w);
}
}
/**
* Fires a motion stop event.
*/
public void fireMotionStopEvent() {
MotionStopEvent w = new MotionStopEvent(this);
for (int i = 0; i < this.accelerationlistener.size(); i++) {
this.accelerationlistener.get(i).motionStopReceived(w);
}
}
}

View File

@@ -0,0 +1,602 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.device;
import java.io.IOException;
import java.util.Random;
import java.util.Vector;
import javax.bluetooth.L2CAPConnection;
import javax.microedition.io.Connector;
import org.wiigee.event.*;
import org.wiigee.filter.Filter;
import org.wiigee.util.Log;
/**
* This class represents the basic functions of the wiimote.
* If you want your wiimote to e.g. vibrate you'll do this here.
*
* @author Benjamin 'BePo' Poppinga
*/
public class Wiimote extends Device {
// Fixed number values.
public static final int BUTTON_2 = 0x0001;
public static final int BUTTON_1 = 0x0002;
public static final int BUTTON_B = 0x0004;
public static final int BUTTON_A = 0x0008;
public static final int BUTTON_MINUS = 0x0010;
public static final int BUTTON_HOME = 0x0080;
public static final int BUTTON_LEFT = 0x0100;
public static final int BUTTON_RIGHT = 0x0200;
public static final int BUTTON_DOWN = 0x0400;
public static final int BUTTON_UP = 0x0800;
public static final int BUTTON_PLUS = 0x1000;
// Reports
public static final byte CMD_SET_REPORT = 0x52;
// IR Modes
public static final byte IR_MODE_STANDARD = 0x01;
public static final byte IR_MODE_EXTENDED = 0x03;
// Modes / Channels
public static final byte MODE_BUTTONS = 0x30;
public static final byte MODE_BUTTONS_ACCELERATION = 0x31;
public static final byte MODE_BUTTONS_ACCELERATION_INFRARED = 0x33;
// Bluetooth-adress as string representation
private String btaddress;
// LED encoded as byte
byte ledencoding;
// Filters, can filter the data stream
protected Vector<Filter> rotfilters = new Vector<Filter>();
// control connection, send commands to wiimote
private L2CAPConnection controlCon;
// receive connection, receive answers from wiimote
private L2CAPConnection receiveCon;
// Listeners, receive generated events
protected Vector<InfraredListener> infraredlistener = new Vector<InfraredListener>();
protected Vector<RotationListener> rotationListener = new Vector<RotationListener>();
// keep track of the orientation
private double pitch = 0.0;
private double roll = 0.0;
private double yaw = 0.0;
// Functional
private boolean vibrating;
private boolean calibrated;
private boolean infraredEnabled;
private WiimoteStreamer wms;
private boolean wiiMotionPlusEnabled;
/**
* Creates a new wiimote-device with a specific bluetooth mac-adress.
*
* @param btaddress
* String representation of the mac-adress e.g. 00191D68B57C.
* @param autofiltering
* If set the wiimote would automatically add the IdleStateFilter.
* @param autoconnect
* If set the wiimote would automatically be connected.
*/
public Wiimote(String btaddress, boolean autofiltering, boolean autoconnect) throws IOException {
super(autofiltering);
this.btaddress = this.removeChar(btaddress, ':');
this.vibrating = false;
this.setCloseGestureButton(Wiimote.BUTTON_HOME);
this.setRecognitionButton(Wiimote.BUTTON_B);
this.setTrainButton(Wiimote.BUTTON_A);
// automatic connect enabled
if (autoconnect) {
this.connect();
this.calibrateAccelerometer();
this.streamData(true);
this.setLED(1);
this.setAccelerationEnabled(true);
}
}
/**
* Creates the two needed connections to send and receive commands
* to and from the wiimote-device.
*
*/
public void connect() throws IOException {
this.controlCon = (L2CAPConnection) Connector.open("btl2cap://" +
this.btaddress + ":11;authenticate=false;encrypt=false;master=false",
Connector.WRITE); // 11
this.receiveCon = (L2CAPConnection) Connector.open("btl2cap://" +
this.btaddress + ":13;authenticate=false;encrypt=false;master=false",
Connector.READ); // 13
}
/**
* Disconnects the wiimote and closes the two connections.
*/
public void disconnect() {
this.vibrating = false;
try {
this.controlCon.close();
this.receiveCon.close();
Log.write("Disconnected wiimote.");
} catch (Exception e) {
Log.write("Failure during disconnect of wiimote.");
}
}
/**
* @return
* Receiving data connection
*/
public L2CAPConnection getReceiveConnection() {
return this.receiveCon;
}
/**
* This method makes the Wiimote-Class reacting to incoming data.
* For just controlling and sending commands to the wiimote
* (vibration, LEDs, ...) it's not necessary to call this method.
*
* @param value
* true, if the class should react to incoming data.
* false, if you only want to send commands to wiimote and
* only the control-connection is used.
*/
public void streamData(boolean value) {
if (value == true) {
if (this.wms == null) {
this.wms = new WiimoteStreamer(this);
}
wms.start();
} else if (this.wms != null) {
wms.stopThread();
}
}
/**
* The added Listener will be notified about detected infrated
* events.
*
* @param listener The Listener to be added.
*/
public void addInfraredListener(InfraredListener listener) {
this.infraredlistener.add(listener);
}
/**
* The added Listener will be notified about detected orientation
* changes.
*
* @param listener The Listener to be added.
*/
public void addRotationListener(RotationListener listener) {
this.rotationListener.add(listener);
}
/**
* Adds a filter to process the rotation speed data of the
* wiimote with an attached Wii Motion Plus.
*
* @param filter The Filter to be added.
*/
public void addRotationFilter(Filter filter) {
this.rotfilters.add(filter);
}
/**
* Resets all filters which are applied to the rotation data
* from the Wii Motion Plus. Also resets _all_ determined orientation
* angles, which should be extended with a consideration of other
* external datas - maybe irda events.
*/
public void resetRotationFilters() {
this.yaw = 0.0;
this.pitch = 0.0;
this.roll = 0.0;
for (int i = 0; i < this.rotfilters.size(); i++) {
this.rotfilters.elementAt(i).reset();
}
}
/**
* Write data to a register inside of the wiimote.
*
* @param offset The memory offset, 3 bytes.
* @param data The data to be written, max. 16 bytes.
* @throws IOException
*/
public void writeRegister(byte[] offset, byte[] data) throws IOException {
byte[] raw = new byte[23];
raw[0] = CMD_SET_REPORT;
raw[1] = 0x16; // Write channel
raw[2] = 0x04; // Register
for (int i = 0; i < offset.length; i++) {
raw[3 + i] = offset[i];
}
raw[6] = (byte) data.length;
for (int i = 0; i < data.length; i++) {
raw[7 + i] = data[i];
}
this.sendRaw(raw);
}
/**
* Makes the Wiimote respond the data of an register. The wiimotestreamer
* doesn't react to the reponse yet.
*
* @param offset The memory offset.
* @param size The size which has to be read out.
* @throws IOException
*/
public void readRegister(byte[] offset, byte[] size) throws IOException {
byte[] raw = new byte[8];
raw[0] = CMD_SET_REPORT;
raw[1] = 0x17; // Read channel
raw[2] = 0x04; // Register
for (int i = 0; i < offset.length; i++) {
raw[3 + i] = offset[i];
}
for (int i = 0; i < size.length; i++) {
raw[6 + i] = size[i];
}
this.sendRaw(raw);
}
/**
* Reads data out of the EEPROM of the wiimote.
* At the moment this method is only used to read out the
* calibration data, so the wiimotestreamer doesn't react for
* every answer on this request.
*
* @param offset The memory offset.
* @param size The size.
* @throws IOException
*/
public void readEEPROM(byte[] offset, byte[] size) throws IOException {
byte[] raw = new byte[8];
raw[0] = CMD_SET_REPORT;
raw[1] = 0x17; // Read channel
raw[2] = 0x00; // EEPROM
for (int i = 0; i < offset.length; i++) {
raw[3 + i] = offset[i];
}
for (int i = 0; i < size.length; i++) {
raw[6 + i] = size[i];
}
this.sendRaw(raw);
}
/**
* Sends pure hexdata to the wiimote. If you want your wiimote
* to vibrate use sendRaw(new byte[] {0x52, 0x13, 0x01}). For other raw-commands use
* the specific wiki-sites around the web (wiili.org, wiibrew.org, ...)
* @param raw
* byte representation of an command
*/
public void sendRaw(byte[] raw) throws IOException {
if (this.controlCon != null) {
this.controlCon.send(raw);
try {
Thread.sleep(100l);
} catch (InterruptedException e) {
System.out.println("sendRaw() interrupted");
}
}
}
/**
* Enables one or more LEDs, where the value could be between 0 and 8.
* If value=1 only the left LED would light up, for value=2 the second
* led would light up, for value=3 the first and second led would light up,
* and so on...
*
* @param value Between 0 and 8, indicating which LEDs should light up
* @throws IOException
*/
public void setLED(int value) throws IOException {
if (value < 16 && value > 0) {
byte tmp = (byte) value;
this.ledencoding = (byte) (tmp << 4);
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x11, this.ledencoding});
} else {
// Random LED change :)
this.setLED(new Random().nextInt(16));
}
}
/**
* Updates the report channel according to the choosen
* functions that are enabled (acceleration, irda, ...).
*
*/
private void updateReportChannel() throws IOException {
if(!accelerationEnabled
&& !wiiMotionPlusEnabled
&& !infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x00, 0x30});
}
else if(accelerationEnabled
&& !wiiMotionPlusEnabled
&& !infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x04, 0x31});
}
else if(!accelerationEnabled
&& wiiMotionPlusEnabled
&& !infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x00, 0x32});
}
else if(accelerationEnabled
&& wiiMotionPlusEnabled
&& !infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x04, 0x35});
}
else if(accelerationEnabled
&& !wiiMotionPlusEnabled
&& infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x04, 0x33});
}
else if(accelerationEnabled
&& wiiMotionPlusEnabled
&& infraredEnabled) {
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x04, 0x37});
}
else {
// default channel - fallback to button only.
Log.write("Invalid Value Configuration: Fallback to Buttons only.");
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x12, 0x00, 0x30});
}
}
/**
* Initializes the calibration of the accerlerometer. This is done once
* per each controller in program lifetime.
*
* @throws IOException
*/
private void calibrateAccelerometer() throws IOException {
this.readEEPROM(new byte[]{0x00, 0x00, 0x20}, new byte[]{0x00, 0x07});
this.calibrated = true;
}
/**
* Activates the acceleration sensor. You have to call the
* streamData(true) method to react to this acceleration data.
* Otherwise the wiimote would send data the whole time and
* nothing else would happen.
*
*/
@Override
public void setAccelerationEnabled(boolean enabled) throws IOException {
super.setAccelerationEnabled(enabled);
if(enabled) {
Log.write("Enabling ACCELEROMETER...");
this.accelerationEnabled = true;
if (!this.calibrated) {
this.calibrateAccelerometer();
}
} else {
Log.write("Disabling ACCELEROMETER...");
this.accelerationEnabled = false;
}
// change channel dynamically
this.updateReportChannel();
}
/**
* Enables or disables the infrared camera of the wiimote with
* the default values.
*
* @param e Should the Infrared Camera be enabled.
* @throws IOException In case of a connection error.
*/
public void setInfraredCameraEnabled(boolean enabled) throws IOException {
this.setInfraredCameraEnabled(enabled, Wiimote.IR_MODE_STANDARD);
}
/**
* Enables the infrared camera in front of the wiimote to track
* IR sources in the field of view of the camera. This could be used
* to a lot of amazing stuff. Using this Mode could slow down the
* recognition of acceleration gestures during the increased data
* size transmitted.
*
* @param e Should the Infrared Camera be enabled.
* @param infraredMode The choosen Infrared Camera Mode.
* @throws IOException In case of a connection error.
*
*/
public void setInfraredCameraEnabled(boolean enabled, byte infraredMode) throws IOException {
if(enabled) {
Log.write("Enabling INFRARED CAMERA...");
this.infraredEnabled = true;
//write 0x04 to output 0x13
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x13, 0x04});
// write 0x04 to output 0x1a
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x1a, 0x04});
// write 0x08 to reguster 0xb00030
this.writeRegister(new byte[]{(byte) 0xb0, 0x00, 0x30}, new byte[]{0x08});
// write sensivity block 1 to register 0xb00000
this.writeRegister(new byte[]{(byte) 0xb0, 0x00, 0x00}, new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x90, 0x00, (byte) 0x41});
// write sensivity block 2 to register 0xb0001a
this.writeRegister(new byte[]{(byte) 0xb0, 0x00, (byte) 0x1a}, new byte[]{0x40, 0x00});
// write ir-mode to register 0xb00033
this.writeRegister(new byte[]{(byte) 0xb0, 0x00, 0x33}, new byte[]{infraredMode});
} else {
Log.write("Disabling INFRARED CAMERA...");
this.infraredEnabled = false;
}
// change channel dynamically
this.updateReportChannel();
}
/**
* To enable the Wii Motion Plus extension. The wiimote will further get
* every other information, like acceleration, infrared camera (loss of precision)
* and button presses.
*
* @throws java.io.IOException
*/
public void setWiiMotionPlusEnabled(boolean enabled) throws IOException {
if(enabled) {
Log.write("Enabling WII MOTION PLUS..");
this.wiiMotionPlusEnabled = true;
// write 0x04 to 0x04a600fe to get wii m+ data within extension reports
this.writeRegister(new byte[]{(byte) 0xa6, 0x00, (byte) 0xfe}, new byte[]{0x04});
} else {
Log.write("Disabling WII MOTION PLUS..");
this.wiiMotionPlusEnabled = false;
}
// change channel dynamically
this.updateReportChannel();
}
/**
* With this method you gain access over the vibrate function of
* the wiimote. You got to try which time in milliseconds would
* fit your requirements.
*
* @param milliseconds Time the wiimote would approx. vibrate.
*/
public void vibrateForTime(long milliseconds) throws IOException {
try {
if (!vibrating) {
this.vibrating = true;
byte tmp = (byte) (this.ledencoding | 0x01);
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x11, tmp});
Thread.sleep(milliseconds);
this.sendRaw(new byte[]{CMD_SET_REPORT, 0x11, this.ledencoding});
this.vibrating = false;
}
} catch (InterruptedException e) {
System.out.println("WiiMoteThread interrupted.");
}
}
public double getPitch() {
return this.pitch;
}
public double getYaw() {
return this.yaw;
}
public double getRoll() {
return this.roll;
}
/**
* Fires a infrared event, containig coordinate pairs (x,y) and a
* size of the detected IR spot.
*
* @param coordinates X and Y display coordinates.
* @param size The size of the spot.
*/
public void fireInfraredEvent(int[][] coordinates, int[] size) {
InfraredEvent w = new InfraredEvent(this, coordinates, size);
for (int i = 0; i < this.infraredlistener.size(); i++) {
this.infraredlistener.get(i).infraredReceived(w);
}
}
/**
* Fires the current relative orientation of the Wiimote to
* all RotationListeners.
*
* @param yaw Orientation around Z axis.
* @param roll Orientation around Y axis.
* @param pitch Orientation around X axis.
*/
public void fireRotationEvent(double pitch, double roll, double yaw) {
this.pitch = pitch;
this.roll = roll;
this.yaw = yaw;
RotationEvent w = new RotationEvent(this, pitch, roll, yaw);
for (int i = 0; i < this.rotationListener.size(); i++) {
this.rotationListener.elementAt(i).rotationReceived(w);
}
}
/**
* If a Wii Motion Plus is attached and activated properly this
* event could be fired within every change of orientation of the
* device. The orientation is not used to do gesture recognition,
* yet.
*
* @param vector The rotational speed vector, containing:
* phi - Rotational speed of x axis (pitch)
* theta - Rotational speed of y axis (roll)
* psi - Rotational speed of z axis (yaw)
*/
public void fireRotationSpeedEvent(double[] vector) {
for (int i = 0; i < this.rotfilters.size(); i++) {
vector = this.rotfilters.get(i).filter(vector);
// cannot return here if null, because of time-dependent filters
}
if (vector != null) {
RotationSpeedEvent w = new RotationSpeedEvent(this, vector[0], vector[1], vector[2]);
for (int i = 0; i < this.rotationListener.size(); i++) {
this.rotationListener.elementAt(i).rotationSpeedReceived(w);
}
// calculate new orientation with integration
// do not store new global values here, since they
// need regular updates only depended on acceleration values.
double tyaw = this.yaw + vector[0] * 0.01;
double troll = this.roll + vector[1] * 0.01;
double tpitch = this.pitch + vector[2] * 0.01;
this.fireRotationEvent(tpitch, troll, tyaw);
}
}
// ###### Hilfsmethoden
// TODO
private String removeChar(String s, char c) {
String r = "";
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != c) {
r += s.charAt(i);
}
}
return r;
}
}

View File

@@ -0,0 +1,400 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.device;
import java.io.IOException;
import java.util.EventObject;
import java.util.Vector;
import javax.bluetooth.L2CAPConnection;
import org.wiigee.util.Log;
/**
* This class listens to data sended by the wiimote and generates specific
* events for acceleration, buttonpress, ...
*
* @author Benjamin 'BePo' Poppinga
*
*/
public class WiimoteStreamer extends Thread {
private boolean running;
private int buttonstate;
private double x0, x1, y0, y1, z0, z1;
private double psi0, theta0, phi0;
private boolean wmpcalibrated;
private int calibrationcounter;
private Vector<double[]> calibrationsequence;
private Wiimote wiimote;
private L2CAPConnection receiveCon;
private EventObject lastevent;
protected WiimoteStreamer(Wiimote wiimote) {
this.wiimote = wiimote;
this.receiveCon = wiimote.getReceiveConnection();
this.buttonstate = 0;
Log.write("WiimoteStreamer initialized...");
}
@Override
public void run() {
Log.write("WiimoteStreamer running...");
this.running = true;
this.calibrationcounter = 0;
this.calibrationsequence = new Vector<double[]>();
try {
while (running) {
// connection has data and we're ready.
byte[] b = this.getRaw(); // blocks application
// Log.write("");
// debug output
/* for(int i=0; i<b.length; i++) {
* System.out.print((int)b[i]&0xFF); if(i==input.length-1) {
* System.out.println(""); } else { System.out.print(":"); } }
*/
if((b[1] & 0xFF) == 0x31) {
this.handleButtonData(new byte[] { b[2], b[3] });
this.handleAccelerationData(new byte[] { b[4], b[5], b[6] });
//Log.write("0x31: Button + Acc");
}
else if ((b[1] & 0xFF) == 0x33) {
this.handleButtonData(new byte[] { b[2], b[3] });
this.handleAccelerationData(new byte[]{b[4], b[5], b[6]});
this.handleInfraredData(new byte[]{b[7], b[8], b[9],
b[10], b[11], b[12],
b[13], b[14], b[15],
b[16], b[17], b[18]});
//Log.write("0x33: Button + Acc + Irda");
}
else if ((b[1] & 0xFF) == 0x37) {
this.handleButtonData(new byte[] { b[2], b[3] });
this.handleAccelerationData(new byte[]{b[4], b[5], b[6]});
this.handleInfraredData(
new byte[]{b[7], b[8], b[9], b[10], b[11], b[12],
b[13], b[14], b[15], b[16]});
this.handleWiiMotionPlusData(
new byte[]{b[17], b[18], b[19], b[20], b[21], b[22]});
//Log.write("0x37: Button + Acc + Ext");
}
else if ((b[1] & 0xFF) == 0x21) {
this.handleButtonData(new byte[] { b[2], b[3] });
// calibration data
if (((b[5] & 0xFF) == 0x00) && ((b[6] & 0xFF) == 0x20)) {
this.handleCalibrationData(
new byte[]{b[7], b[8], b[9], b[11], b[12], b[13]});
// Log.write("0x21: Calibration result");
} else {
this.handleRawDataAnswer(
new byte[]{b[5], b[6]},
new byte[]{b[7], b[8], b[9], b[10], b[11], b[12],
b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20],
b[21], b[22]
});
// Log.write("0x21: Raw data answer");
}
}
else if ((b[1] & 0xFF) == (byte) 0x3d) {
Log.write("0x3D: Ext only");
}
else {
Log.write("Unknown data retrieved.");
this.printBytes(b);
}
} // while(running)
} catch (IOException e) {
Log.write("Streamer: Connection to Wiimote lost.");
this.running = false;
}
}
/**
* Handles calibration Data
*
* @param data An array of bytes, containing the data.
*/
private void handleCalibrationData(byte[] data) {
this.x0 = data[0] & 0xFF;
this.y0 = data[1] & 0xFF;
this.z0 = data[2] & 0xFF;
this.x1 = data[3] & 0xFF;
this.y1 = data[4] & 0xFF;
this.z1 = data[5] & 0xFF;
Log.write("Autocalibration successful!");
}
/**
* Handles a raw data answer: It just prints it out.
*
* @param offset The offset of the maybe read data.
* @param data The data itself.
*/
private void handleRawDataAnswer(byte[] offset, byte[] data) {
String out = "";
String[] o = this.byte2hex(offset);
String[] d = this.byte2hex(data);
out += "READ " + o[0] + "" + o[1] + ": ";
for (int i = 0; i < d.length; i++) {
out += d[i] + " ";
}
Log.write(out);
}
/**
* Handles the Wii Motion Plus data to generate orientation
* events on the Wiimote.
*
* @param data The data containing the raw rotation speeds.
*/
private void handleWiiMotionPlusData(byte[] data) {
// fixed values until calibration procedure is known
//int psi0 = 8265;
//int theta0 = 7963;
//int phi0 = 7923;
//this.printBytes(new byte[]{ data[3], data[4], data[5]});
int psiL = (data[0] & 0xFF);
int thetaL = (data[1] & 0xFF);
int phiL = (data[2] & 0xFF);
// cut two lower bits of UPPER values, shift right
int psiU = ((data[3] & 0xFC) << 6);
int thetaU = ((data[4] & 0xFC) << 6);
int phiU = ((data[5] & 0xFC) << 6);
// add the two values
int psiRAW = psiU + psiL;
int thetaRAW = thetaU + thetaL;
int phiRAW = phiU + phiL;
// average of 50 samples for calibration
if (!this.wmpcalibrated) {
if (this.calibrationcounter++ < 50) {
this.calibrationsequence.add(new double[]{psiRAW, thetaRAW, phiRAW});
} else {
this.calibrateWiiMotionPlus();
}
} else { // is calibrated
// calculate degrees per second movement
double psi = (double) (psiRAW - psi0) / 20.0;
double theta = (double) (thetaRAW - theta0) / 20.0;
double phi = (double) (phiRAW - phi0) / 20.0;
this.wiimote.fireRotationSpeedEvent(new double[]{-psi, -theta, -phi});
}
}
/**
* Build an average of the presaved rotation sequences to have a reference
* value for 'not rotating'. This is a kind of calibration until the original
* calibration procedure for the wii motion plus is known.
*/
private void calibrateWiiMotionPlus() {
for (int i = 0; i < this.calibrationsequence.size(); i++) {
this.psi0 += this.calibrationsequence.elementAt(i)[0];
this.theta0 += this.calibrationsequence.elementAt(i)[1];
this.phi0 += this.calibrationsequence.elementAt(i)[2];
}
this.psi0 /= this.calibrationsequence.size();
this.theta0 /= this.calibrationsequence.size();
this.phi0 /= this.calibrationsequence.size();
this.wmpcalibrated = true;
Log.write("Wii Motion Plus calibrated manually!");
}
/**
* Handles the raw infrared data, containing the spot positions
* and the intensities of the spots. At the moment only the
* 12 Byte irda raw data is supported.
*
* @param data 12 Bytes of raw irda data.
*/
private void handleInfraredData(byte[] data) {
int[][] coordinates = new int[4][2];
int[] size = new int[] { 1, 1, 1, 1 };
int j = 0;
// normal mode
if(data.length == 10) {
for(int i=0; i<10; i+=5) { // for each IR fife-byte segment
int tailX1 = data[i] & 0xFF;
int tailY1 = data[i+1] & 0xFF;
int preY1 = (data[i+2] & 0xC0) << 2;
int preX1 = (data[i+2] & 0x30) << 4;
int preY2 = (data[i+2] & 0x0C) << 6;
int preX2 = (data[i+2] & 0x03) << 8;
int tailX2 = data[i+3] & 0xFF;
int tailY2 = data[i+4] & 0xFF;
coordinates[j][0] = tailX1 + preX1;
coordinates[j][1] = tailY1 + preY1;
coordinates[j+1][0] = tailX2 + preX2;
coordinates[j+1][1] = tailY2 + preY2;
j+=2;
}
} else if (data.length == 12) { // extended mode
for (int i = 0; i < 12; i += 3) { // for each IR byte
// triple
int tailX = data[i] & 0xFF;
int tailY = data[i + 1] & 0xFF;
int preY = (data[i + 2] & 0xC0) << 2;
int preX = (data[i + 2] & 0x30) << 4;
coordinates[j][0] = tailX + preX;
coordinates[j][1] = tailY + preY;
size[j++] = (data[i + 2] & 0x0F);
}
}
this.wiimote.fireInfraredEvent(coordinates, size);
}
/**
* Handles the retrieved acceleration data and fires
* corresponding Acceleration Event on the Device.
*
* @param data
*/
private void handleAccelerationData(byte[] data) {
// convert to int.
int xraw = (data[0] & 0xFF);
int yraw = (data[1] & 0xFF);
int zraw = (data[2] & 0xFF);
// calculate acceleration with calibration data.
double x = (double) (xraw - x0) / (double) (x1 - x0);
double y = (double) (yraw - y0) / (double) (y1 - y0);
double z = (double) (zraw - z0) / (double) (z1 - z0);
this.wiimote.fireAccelerationEvent(new double[]{x, y, z});
}
/**
* Decodes the two bytes with the button press and release bits.
*
* @param a First button byte.
* @param b Second button byte.
*/
private void handleButtonData(byte[] data) {
byte first = (byte)(data[0] & 0xFF);
byte second = (byte)(data[1] & 0xFF);
int newbuttons = (first<<8) + second;
int delta = this.buttonstate ^ newbuttons; // XOR
int shift = 0x0001;
while(shift<0x1000) {
if(shift!=0x0020 && shift!=0x0040) { // reserved bytes
if((delta&shift)==shift) { // change detected
if((newbuttons&shift)==shift) { // press detected
this.wiimote.fireButtonPressedEvent(shift);
} else { // release detected
this.wiimote.fireButtonReleasedEvent(shift);
}
}
}
shift<<=1;
}
this.buttonstate = newbuttons;
}
/**
* Gets 23 bytes out of the Receive Connection Stream.
*
* @return The 23 retrieved bytes.
* @throws java.io.IOException
*/
private byte[] getRaw() throws IOException {
byte[] b = new byte[23];
this.receiveCon.receive(b);
return b;
}
/**
* Stops this thread.
*/
protected void stopThread() {
this.running = false;
}
/**
* true if thread is running.
* @return true if thread is running, false otherwise.
*/
protected boolean isRunning() {
return this.running;
}
/**
* Prints a byte stream as hex string.
* @param b
*/
private void printBytes(byte[] b) {
String out = "";
String[] s = this.byte2hex(b);
for (int i = 0; i < s.length; i++) {
out += " " + s[i];
}
Log.write(out);
}
/**
* Converts a byte array to a string array.
*
* @param b
* @return
*/
private String[] byte2hex(byte[] b) {
String[] out = new String[b.length];
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (stmp.length() == 1) {
out[n] = ("0" + stmp).toUpperCase();
} else {
out[n] = stmp.toUpperCase();
}
}
return out;
}
}

View File

@@ -0,0 +1,79 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.*;
/**
* This Event would be generated if an acceleration has been detected.
* It contains information about the force applied to the device in each
* direction (x, y, z). Further it contains the absolute value of this
* vector and the source which generated this event (Device).
*
* @author Benjamin 'BePo' Poppinga
*
*/
public class AccelerationEvent extends EventObject {
double X, Y, Z;
double absvalue;
/**
* Create an AccelerationEvent with a specific source,
* all the three acceleration values and the calculated absolute
* value.
*
* @param source The source which has been accelerated (Wiimote).
* @param X The value of acceleration in the x direction.
* @param Y The value of acceleration in the y direction.
* @param Z The value of acceleration in the z direction.
* @param absvalue The absolute value of this acceleration vector.
*/
public AccelerationEvent(Device source, double X, double Y, double Z, double absvalue) {
super(source);
this.X=X;
this.Y=Y;
this.Z=Z;
this.absvalue=absvalue;
}
public double getX() {
return X;
}
public double getY() {
return Y;
}
public double getZ() {
return Z;
}
public double getAbsValue() {
return absvalue;
}
}

View File

@@ -0,0 +1,65 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventListener;
/**
* This interface has to be implemented if the application should react
* to pure acceleration data. This could be useful if you want to e.g.
* graphically display the acceleration data in your application.
*
* @author Benjamin 'BePo' Poppinga
*/
public interface AccelerationListener extends EventListener {
/**
* This method would be called if a Device source has been accelerated.
*
* @param event The acceleration representation as an event.
*/
public abstract void accelerationReceived(AccelerationEvent event);
/**
* This method would be called if a Device is in idle state and then a
* motion starts or if a Device is in motion and then the motion stops and
* the Device is in idle state.
*
* @param event This is the event which contains if the Wiimote is now
* in motion or not.
*/
public abstract void motionStartReceived(MotionStartEvent event);
/**
* This method would be called if a Device is in motion and then the motion
* stops and the Device is in idle state.
*
* @param event This is the event which contains if the Device is now
* in motion or not.
*/
public abstract void motionStopReceived(MotionStopEvent event);
}

View File

@@ -0,0 +1,84 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.Device;
/**
* An ActionStartEvent is an Event where other different events can
* be derived from, if they can be considered as an event actually starting
* a process like e.g. Training, Recognition, ...
*
* @author Benjamin 'BePo' Poppinga
*/
public class ActionStartEvent extends EventObject {
protected boolean trainbutton;
protected boolean recognitionbutton;
protected boolean closegesturebutton;
public ActionStartEvent(Device source) {
super(source);
}
/**
* Is true if this button press has been done by the
* individual defined RecognitionButton which has to be
* set during initialization of a Wiimote.
*
* @return Is this button press initiated by the recognition button.
* @see device.Wiimote#setRecognitionButton(int) setRecognitionButton()
*/
public boolean isRecognitionInitEvent() {
return this.recognitionbutton;
}
/**
* Is true if this button press has been done by the
* individual defined TrainButton which has to be
* set during initialization of a Wiimote.
*
* @return Is this button pres initiated by the training button.
* @see device.Wiimote#setTrainButton(int) setTrainButton()
*/
public boolean isTrainInitEvent() {
return this.trainbutton;
}
/**
* Is true if this button press has been done by the
* individual defined CloseGestureButton which has to be
* set during initialization of a Wiimote.
*
* @return Is this button press initiated by the close gesture button.
* @see device.Wiimote#setCloseGestureButton(int) setCloseGestureButton()
*/
public boolean isCloseGestureInitEvent() {
return this.closegesturebutton;
}
}

View File

@@ -0,0 +1,43 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.Device;
/**
* An ActionStopEvent is an Event where other different events can
* be derived from, if they can be considered as an event actually stopping
* a process like e.g. Training, Recognition, ...
*
* @author Benjamin 'BePo' Poppinga
*/
public class ActionStopEvent extends EventObject {
public ActionStopEvent(Device source) {
super(source);
}
}

View File

@@ -0,0 +1,54 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventListener;
/**
* This interface has to be implemented if the application should react
* to button press/releases.
*
* @author Benjamin 'BePo' Poppinga
*/
public interface ButtonListener extends EventListener {
/**
* This method would be called if a Device button has been pressed.
*
* @param event The button representation as an event.
*/
public abstract void buttonPressReceived(ButtonPressedEvent event);
/**
* This method would be called if a Device button has been released.
*
* @param event This is actually a meta-event NOT containing which button
* has been released.
*/
public abstract void buttonReleaseReceived(ButtonReleasedEvent event);
}

View File

@@ -0,0 +1,80 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import org.wiigee.device.*;
/**
*
* This Event would be generated if a button on a wiimote has been
* pressed by user. It contains the source (wiimote) and an integer
* representation of which button has been pressed. Please note that
* there exist enumeration constants in the class, so you don't
* have to use this integer values directly.
*
* @author Benjamin 'BePo' Poppinga
*/
public class ButtonPressedEvent extends ActionStartEvent {
// Fixed number values.
public static final int BUTTON_2 = 1;
public static final int BUTTON_1 = 2;
public static final int BUTTON_B = 3;
public static final int BUTTON_A = 4;
public static final int BUTTON_MINUS = 5;
public static final int BUTTON_HOME = 8;
public static final int BUTTON_LEFT = 9;
public static final int BUTTON_RIGHT = 10;
public static final int BUTTON_DOWN = 11;
public static final int BUTTON_UP = 12;
public static final int BUTTON_PLUS = 13;
int button;
/**
* Create a WiimoteButtonPressedEvent with the Wiimote source whose
* Button has been pressed and the integer representation of the button.
*
* @param source
* @param button
*/
public ButtonPressedEvent(Device source, int button) {
super(source);
this.button=button;
if(source.getRecognitionButton()==button) {
this.recognitionbutton=true;
} else if(source.getTrainButton()==button) {
this.trainbutton=true;
} else if(source.getCloseGestureButton()==button) {
this.closegesturebutton=true;
}
}
public int getButton() {
return this.button;
}
}

View File

@@ -0,0 +1,49 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import org.wiigee.device.*;
/**
*
* This event would be generated if a button was released.
* contains: source (wiimote).
*
* @author Benjamin 'BePo' Poppinga
*/
public class ButtonReleasedEvent extends ActionStopEvent {
int button;
public ButtonReleasedEvent(Device source, int button) {
super(source);
this.button = button;
}
public int getButton() {
return this.button;
}
}

View File

@@ -0,0 +1,71 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import org.wiigee.logic.ProcessingUnit;
/**
* This event would be generated if a gesture has been detected.
* It contains information about the gesture "name" or type,
* the accelerationstreamanalyzer which generated the event (source)
* and the probability calculated from the bayes classifier.
*
* @author Benjamin 'BePo' Poppinga
*/
public class GestureEvent {
int id;
boolean valid;
double probability;
ProcessingUnit analyzer;
/** Create a GestureEvent
*
* @param source The Source, which detected the gesture.
* @param id A gesture ID for identifying a gesture.
* @param probability The Bayes-Classifier calculated probability.
*/
public GestureEvent(ProcessingUnit source, boolean valid, int id, double probability) {
this.analyzer = source;
this.valid = valid;
this.id = id;
this.probability = probability;
}
public int getId() {
return this.id;
}
public boolean isValid() {
return this.valid;
}
public double getProbability() {
return this.probability;
}
public ProcessingUnit getSource() {
return this.analyzer;
}
}

View File

@@ -0,0 +1,47 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventListener;
/**
*
* This is the GestureListener interface which has to be implemented
* by any application which should receive recognized gestures.
*
* @author Benjamin 'BePo' Poppinga
*
*/
public interface GestureListener extends EventListener {
/**
* This method would be called if a gesture has been recognized.
*
* @param event The GestureEvent containing information about
* the recognized gesture.
*/
public abstract void gestureReceived(GestureEvent event);
}

View File

@@ -0,0 +1,69 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.Device;
/**
* An infrared event consists of a set of coordinates, containing values
* from [0, 1024] in width to [0, 768] in height. for each point there is
* a given size and if the detected infrared spot is valid.
*
* @author Benjamin 'BePo' Poppinga
*/
public class InfraredEvent extends EventObject {
protected int[][] coordinates;
protected int[] size;
protected boolean[] valid;
public InfraredEvent(Device source, int[][] coordinates, int[] size) {
super(source);
this.coordinates=coordinates;
this.size=size;
this.valid = new boolean[4];
for(int i=0; i<this.coordinates.length; i++) {
this.valid[i] = (this.coordinates[i][0]<1023 && this.coordinates[i][1]<1023);
}
}
public boolean[] getValids() {
return this.valid;
}
public boolean isValid(int i) {
return this.valid[i];
}
public int[][] getCoordinates() {
return this.coordinates;
}
public int[] getSize() {
return this.size;
}
}

View File

@@ -0,0 +1,40 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventListener;
/**
* Interface to implement if a class should receive InfraredEvents
* via the given method. Therefore implement this Interface and add
* the class as a Listener to the Wiimote.
*
* @author Benjamin 'BePo' Poppinga
*/
public interface InfraredListener extends EventListener {
public abstract void infraredReceived(InfraredEvent event);
}

View File

@@ -0,0 +1,63 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import org.wiigee.device.Device;
/**
* This Event gets fired, if the Device starts to move.
*
* @author Benjamin 'BePo' Poppinga
*/
public class MotionStartEvent extends ActionStartEvent {
public MotionStartEvent(Device source) {
super(source);
if(source.getRecognitionButton()==Device.MOTION) {
this.recognitionbutton=true;
} else if(source.getTrainButton()==Device.MOTION) {
this.trainbutton=true;
} else if(source.getCloseGestureButton()==Device.MOTION) {
this.closegesturebutton=true;
}
}
@Override
public boolean isTrainInitEvent() {
return this.trainbutton;
}
@Override
public boolean isCloseGestureInitEvent() {
return this.closegesturebutton;
}
@Override
public boolean isRecognitionInitEvent() {
return this.recognitionbutton;
}
}

View File

@@ -0,0 +1,42 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import org.wiigee.device.Device;
/**
*
* This event would be generated if a motion stops.
* contains: source.
*
* @author Benjamin 'BePo' Poppinga
*/
public class MotionStopEvent extends ActionStopEvent {
public MotionStopEvent(Device source) {
super(source);
}
}

View File

@@ -0,0 +1,65 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.Device;
/**
* A RotationEvents contains the current relative rotation to the last
* given reset position. If the device has never been resetted before,
* the last position is the Wiimotes initial position. This event contains
* all three angles - pitch, yaw, roll - which are only determined using
* the Wii Motion Plus extension. There wouldn't be a RotationEvent without
* this extension.
*
* @author Benjamin 'BePo' Poppinga
*/
public class RotationEvent extends EventObject {
protected double pitch;
protected double yaw;
protected double roll;
public RotationEvent(Device source, double pitch, double roll, double yaw) {
super(source);
this.pitch = pitch;
this.roll = roll;
this.yaw = yaw;
}
public double getPitch() {
return this.pitch;
}
public double getYaw() {
return this.yaw;
}
public double getRoll() {
return this.roll;
}
}

View File

@@ -0,0 +1,43 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventListener;
/**
* Implement this interface to get informed about events only
* occuring if a Wii Motion Plus is attached. If implemented and added
* as a Listener, the RotationSpeedEvents and RotationEvent would be
* retrieved.
*
* @author Benjamin 'BePo' Poppinga
*/
public interface RotationListener extends EventListener {
public abstract void rotationSpeedReceived(RotationSpeedEvent event);
public abstract void rotationReceived(RotationEvent event);
}

View File

@@ -0,0 +1,60 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.event;
import java.util.EventObject;
import org.wiigee.device.Device;
/**
* The RotationSpeedEvent contains the raw angle velocities - psi, theta, phi.
* This event only occurs, if a Wii Motion Plus is attached.
*
* @author Benjamin 'BePo' Poppinga
*/
public class RotationSpeedEvent extends EventObject {
protected double psi;
protected double theta;
protected double phi;
public RotationSpeedEvent(Device source, double psi, double theta, double phi) {
super(source);
this.psi = psi;
this.theta = theta;
this.phi = phi;
}
public double getPsi() {
return this.psi;
}
public double getTheta() {
return this.theta;
}
public double getPhi() {
return this.phi;
}
}

View File

@@ -0,0 +1,70 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
* This filter removes a vector, if it doesn't differ enough from
* the previously retrieved filter.
*
* @author Benjamin 'BePo' Poppinga
*/
public class DirectionalEquivalenceFilter extends Filter {
private double sensivity;
private double[] reference;
public DirectionalEquivalenceFilter() {
super();
this.reset();
}
public void reset() {
this.sensivity=0.2;
this.reference = new double[] {0.0, 0.0, 0.0};
}
public double[] filterAlgorithm(double[] vector) {
if(vector[0]<reference[0]-this.sensivity ||
vector[0]>reference[0]+this.sensivity ||
vector[1]<reference[1]-this.sensivity ||
vector[1]>reference[1]+this.sensivity ||
vector[2]<reference[2]-this.sensivity ||
vector[2]>reference[2]+this.sensivity) {
this.reference=vector;
return vector;
} else {
return null;
}
}
public void setSensivity(double sensivity) {
this.sensivity=sensivity;
}
public double getSensivity() {
return this.sensivity;
}
}

View File

@@ -0,0 +1,62 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
* Abstract class to give a definition for a general filter.
*
* @author Benjamin 'BePo' Poppinga
*/
public abstract class Filter {
/***
* The actual called method to filter anything. It checks if the vector is
* already set to NULL by another filter and won't process it anymore. If it's
* not NULL it would be forwarded to the actual implemented method - filterAlgorithm().
* @param vector The acceleration vector, encoding: 0/x, 1/y, 2/z
* @return a new, filtered acceleration vector, encoded the same way
*/
public double[] filter(double[] vector) {
if(vector==null) {
return null;
} else {
return filterAlgorithm(vector);
}
}
/***
* A filter receives a triple of acceleration values within the variable 'vector'.
* It's encoded as vector[0]=x, vector[1]=y, vector[2]=z. This is not an object since the
* processing of the filter should be really fast, since every acceleration of the wiimote
* passes the filter.
* @param vector
* @param absvalue
* @return
*/
abstract public double[] filterAlgorithm(double[] vector);
abstract public void reset();
}

View File

@@ -0,0 +1,71 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
*
* This filter removes every acceleration that happens slowly or
* steadily (like e.g. gravity). Remember: It _passes_ acceleration
* with a big variety.
*
* @author Benjamin 'BePo' Poppinga
*/
public class HighPassFilter extends Filter {
private double factor;
private double[] prevAcc;
public HighPassFilter() {
super();
this.factor = 0.1;
this.reset();
}
public HighPassFilter(double factor) {
super();
this.factor = factor;
this.reset();
}
@Override
public void reset() {
this.prevAcc = new double[] {0.0, 0.0, 0.0};
}
@Override
public double[] filterAlgorithm(double[] vector) {
double[] retVal = new double[3];
prevAcc[0] = vector[0] * this.factor + this.prevAcc[0] * (1.0 - this.factor);
prevAcc[1] = vector[1] * this.factor + this.prevAcc[1] * (1.0 - this.factor);
prevAcc[2] = vector[2] * this.factor + this.prevAcc[2] * (1.0 - this.factor);
retVal[0] = vector[0] - prevAcc[0];
retVal[1] = vector[1] - prevAcc[1];
retVal[2] = vector[2] - prevAcc[2];
return retVal;
}
}

View File

@@ -0,0 +1,86 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
* Filters if the wiimote is not moved in any way. Be careful in using
* this filter together with a HighPassFilter.
*
* @author Benjamin 'BePo' Poppinga
*/
public class IdleStateFilter extends Filter {
private double sensivity;
/**
* Since an acceleration sensor usually provides information even
* if it doesn't move, this filter removes the data if it's in the
* idle state.
*/
public IdleStateFilter() {
super();
this.sensivity = 0.1;
}
@Override
public void reset() {
// not needed
}
@Override
public double[] filterAlgorithm(double[] vector) {
// calculate values needed for filtering:
// absolute value
double absvalue = Math.sqrt((vector[0]*vector[0])+
(vector[1]*vector[1])+(vector[2]*vector[2]));
// filter formulaes and return values
if(absvalue > 1+this.sensivity ||
absvalue < 1-this.sensivity) {
return vector;
} else {
return null;
}
}
/**
* Defines the absolute value when the wiimote should react to acceleration.
* This is a parameter for the first of the two filters: idle state
* filter. For example: sensivity=0.2 makes the wiimote react to acceleration
* where the absolute value is equal or greater than 1.2g. The default value 0.1
* should work well. Only change if you are sure what you're doing.
*
* @param sensivity
* acceleration data values smaller than this value wouldn't be detected.
*/
public void setSensivity(double sensivity) {
this.sensivity = sensivity;
}
public double getSensivity() {
return this.sensivity;
}
}

View File

@@ -0,0 +1,67 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
*
* This filter removes every acceleration that happens fast or
* suddenly (like e.g. a short hit). Remember: It _passes_ acceleration
* with a slight variety.
*
* @author Benjamin 'BePo' Poppinga
*/
public class LowPassFilter extends Filter {
private double factor;
private double[] prevAcc;
public LowPassFilter() {
super();
this.factor = 0.01;
this.reset();
}
public LowPassFilter(double factor) {
super();
this.factor = factor;
this.reset();
}
@Override
public void reset() {
this.prevAcc = new double[] {0.0, 0.0, 0.0};
}
@Override
public double[] filterAlgorithm(double[] vector) {
double[] retVal = new double[3];
retVal[0] = vector[0] * this.factor + this.prevAcc[0] * (1.0 - this.factor);
retVal[1] = vector[1] * this.factor + this.prevAcc[1] * (1.0 - this.factor);
retVal[2] = vector[2] * this.factor + this.prevAcc[2] * (1.0 - this.factor);
this.prevAcc = retVal;
return retVal;
}
}

View File

@@ -0,0 +1,102 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
import org.wiigee.device.Device;
/**
* This filter uses time to determine if the wiimote actually is in motion
* or not. This filter only works together with the IdleStateFilter.
*
* @author Benjamin 'BePo' Poppinga
*/
public class MotionDetectFilter extends Filter {
private int motionchangetime;
private boolean nowinmotion;
private long motionstartstamp;
private Device device;
/***
* Detects wheather the wiimote receives acceleration or not and
* raises an event, if the device starts or stops. This is actual a
* null filter, not manipulating anything. But looks pretty good in
* this datatype since it could be removed easily.
*
* @param wiimote The Wiimote object which is controlled by the filter.
*/
public MotionDetectFilter(Device device) {
super();
this.device=device;
this.reset();
}
public void reset() {
this.motionstartstamp=System.currentTimeMillis();
this.nowinmotion=false;
this.motionchangetime=190;
}
@Override
public double[] filter(double[] vector) {
if(this.nowinmotion &&
(System.currentTimeMillis()-this.motionstartstamp)>=
this.motionchangetime) {
this.nowinmotion=false;
this.device.fireMotionStopEvent();
} // fi
return filterAlgorithm(vector);
}
public double[] filterAlgorithm(double[] vector) {
if(vector!=null) {
this.motionstartstamp=System.currentTimeMillis();
if(!this.nowinmotion) {
this.nowinmotion=true;
this.motionstartstamp=System.currentTimeMillis();
this.device.fireMotionStartEvent();
}
}
return vector;
}
/**
* Defines the time the wiimote has to be in idle state before a new motion change
* event appears. The default value 500ms should work well, only change it if you are sure
* about what you're doing.
* @param time Time in ms
*/
public void setMotionChangeTime(int time) {
this.motionchangetime=time;
}
public int getMotionChangeTime() {
return this.motionchangetime;
}
}

View File

@@ -0,0 +1,68 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
import org.wiigee.device.Wiimote;
import org.wiigee.util.Log;
/**
* Removes rotation events which are for all axis
* under a defined threshold value (which default is 2.0 degrees
* per second TBD).
*
* @author Benjamin 'BePo' Poppinga
*/
public class RotationResetFilter extends Filter {
private Wiimote device;
public RotationResetFilter(Wiimote source) {
super();
this.device = source;
}
@Override
public void reset() {
// nothing to reset here
}
public double[] filterAlgorithm(double[] vector) {
double abs = Math.sqrt(vector[0]*vector[0]+
vector[1]*vector[1]+
vector[2]*vector[2]);
if(abs<=1.05 && abs>=0.95) { // wiimote is idle
//roll = arctan2(ax,sqrt(ay2+az2))
//pitch = arctan2(ay,sqrt(ax2+az2))
double tphi = Math.toDegrees(Math.atan2(vector[0], Math.sqrt(vector[1]*vector[1]+vector[2]*vector[2])));
double ttheta = Math.toDegrees(Math.atan2(vector[1], Math.sqrt(vector[0]*vector[0]+vector[2]*vector[2])));
this.device.fireRotationEvent(tphi, ttheta, this.device.getYaw());
Log.write("reset rotation using acceleration. pitch="+tphi+" roll="+ttheta);
}
return vector;
}
}

View File

@@ -0,0 +1,71 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.filter;
/**
* Removes rotation events which are for all axis
* under a defined threshold value (which default is 2.0 degrees
* per second TBD).
*
* @author Benjamin 'BePo' Poppinga
*/
public class RotationThresholdFilter extends Filter {
private double threshold;
public RotationThresholdFilter() {
super();
this.threshold = 2.0;
}
public RotationThresholdFilter(double threshold) {
super();
this.threshold = threshold;
}
@Override
public void reset() {
// nothing to reset here
}
public double[] filterAlgorithm(double[] vector) {
if(Math.abs(vector[0])>threshold ||
Math.abs(vector[1])>threshold ||
Math.abs(vector[2])>threshold) {
return vector;
} else {
return new double[] { 0.0, 0.0, 0.0 };
}
}
public void setSensivity(double sensivity) {
this.threshold=sensivity;
}
public double getSensivity() {
return this.threshold;
}
}

View File

@@ -0,0 +1,155 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.util.Vector;
import org.wiigee.event.AccelerationEvent;
/**
* This class represents ONE movement trajectory in a
* concrete instance.
*
* @author Benjamin 'BePo' Poppinga
*/
public class Gesture implements Cloneable {
/** Min/MaxAcceleration setup manually? */
private boolean minmaxmanual;
private double minacc;
private double maxacc;
/** The complete trajectory as WiimoteAccelerationEvents
* as a vector. It's a vector because we don't want to
* loose the chronology of the stored events.
*/
private Vector<AccelerationEvent> data;
/**
* Create an empty Gesture.
*/
public Gesture() {
this.data = new Vector<AccelerationEvent>();
}
/**
* Make a deep copy of another Gesture object.
*
* @param original Another Gesture object
*/
public Gesture(Gesture original) {
this.data = new Vector<AccelerationEvent>();
Vector<AccelerationEvent> origin = original.getData();
for (int i = 0; i < origin.size(); i++) {
this.add((AccelerationEvent) origin.get(i));
}
}
/**
* Adds a new acceleration event to this gesture.
*
* @param event The WiimoteAccelerationEvent to add.
*/
public void add(AccelerationEvent event) {
this.data.add(event);
}
/**
* Returns the last acceleration added to this gesture.
*
* @return the last acceleration event added.
*/
public AccelerationEvent getLastData() {
return (AccelerationEvent) this.data.get(this.data.size() - 1);
}
/**
* Returns the whole chronological sequence of accelerations as
* a vector.
*
* @return chronological sequence of accelerations.
*/
public Vector<AccelerationEvent> getData() {
return this.data;
}
/**
* Removes the first element of the acceleration queue of a gesture
*/
public void removeFirstData() {
this.data.remove(0);
}
public int getCountOfData() {
return this.data.size();
}
public void setMaxAndMinAcceleration(double max, double min) {
this.maxacc = max;
this.minacc = min;
this.minmaxmanual = true;
}
public double getMaxAcceleration() {
if(!this.minmaxmanual) {
double maxacc = Double.MIN_VALUE;
for(int i=0; i<this.data.size(); i++) {
if(Math.abs(this.data.get(i).getX()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getX());
}
if(Math.abs(this.data.get(i).getY()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getY());
}
if(Math.abs(this.data.get(i).getZ()) > maxacc) {
maxacc=Math.abs(this.data.get(i).getZ());
}
}
return maxacc;
} else {
return this.maxacc;
}
}
public double getMinAcceleration() {
if(!this.minmaxmanual) {
double minacc = Double.MAX_VALUE;
for(int i=0; i<this.data.size(); i++) {
if(Math.abs(this.data.get(i).getX()) < minacc) {
minacc=Math.abs(this.data.get(i).getX());
}
if(Math.abs(this.data.get(i).getY()) < minacc) {
minacc=Math.abs(this.data.get(i).getY());
}
if(Math.abs(this.data.get(i).getZ()) < minacc) {
minacc=Math.abs(this.data.get(i).getZ());
}
}
return minacc;
} else {
return this.minacc;
}
}
}

View File

@@ -0,0 +1,205 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.util.Vector;
import org.wiigee.event.AccelerationEvent;
import org.wiigee.util.Log;
/**
* This Class units a Quantizer-Component and an Model-Component.
* In this implementation a k-mean-algorithm for quantization and
* a hidden markov model as instance for the model has been used.
*
* @author Benjamin 'BePo' Poppinga
*/
public class GestureModel {
/** The number of states the hidden markov model consists of */
private int numStates;
/** The number of observations for the hmm and k-mean */
private int numObservations;
/** The quantization component */
private Quantizer quantizer;
/** The statistical model, hidden markov model */
private HMM markovmodell;
/** The default probability of this gesturemodel,
* needed for the bayes classifier */
private double defaultprobability;
/** Creates a Unit (Quantizer&Model).
*
* @param id
* int representation of a gesture "name"/class.
*/
public GestureModel() {
this.numStates=8; // n=8 states empirical value
this.numObservations=14; // k=14 observations empirical value
this.markovmodell = new HMM(numStates, numObservations); // init model
this.quantizer = new Quantizer(numStates); // init quantizer
}
/**
* Trains the model to a set of motion-sequences, representing
* different evaluations of a gesture
*
* @param trainsequence a vector of gestures
*/
public void train(Vector<Gesture> trainsequence) {
// summarize all vectors from the different gestures in one
// gesture called sum.
double maxacc=0;
double minacc=0;
Gesture sum = new Gesture();
for(int i=0; i<trainsequence.size(); i++) {
Vector<AccelerationEvent> t = trainsequence.elementAt(i).getData();
// add the max and min acceleration, we later get the average
maxacc+=trainsequence.elementAt(i).getMaxAcceleration();
minacc+=trainsequence.elementAt(i).getMinAcceleration();
// transfer every single accelerationevent of each gesture to
// the new gesture sum
for(int j=0; j<trainsequence.elementAt(i).getData().size(); j++) {
sum.add(t.elementAt(j));
}
}
// get the average and set it to the sum gesture
sum.setMaxAndMinAcceleration(maxacc/trainsequence.size(), minacc/trainsequence.size());
// train the centeroids of the quantizer with this master gesture sum.
this.quantizer.trainCenteroids(sum);
// convert gesture vector to a sequence of discrete values
Vector<int[]> seqs = new Vector<int[]>();
for(int i=0; i<trainsequence.size(); i++) {
seqs.add(this.quantizer.getObservationSequence(trainsequence.elementAt(i)));
}
// train the markov model with this derived discrete sequences
this.markovmodell.train(seqs);
// set the default probability for use with the bayes classifier
this.setDefaultProbability(trainsequence);
}
/**
* Returns the probability that a gesture matches to this
* gesture model.
*
* @param gesture a gesture to test.
* @return probability that the gesture belongs to this gesture
* model.
*/
public double matches(Gesture gesture) {
int[] sequence = quantizer.getObservationSequence(gesture);
return this.markovmodell.getProbability(sequence);
}
/**
* For debug purposes or very technical interested people. :)
*/
public void printMap() {
Log.write("Gesture Quantizer-Map:");
this.quantizer.printMap();
}
/***
* For debug purposes or very technical interested people. :)
* @return
*/
public void print() {
Log.write(Log.DEBUG, "HMM-Print:", this);
this.markovmodell.print();
Log.write(Log.DEBUG, "Quantizer-Print:", this);
this.quantizer.printMap();
}
public int getNumStates() {
return this.numStates;
}
public int getNumObservations() {
return this.numObservations;
}
/**
* Returns the model probability for bayes.
*
* @return
* the model probability
*/
public double getDefaultProbability() {
return this.defaultprobability;
}
/**
* Since the bayes classifier needs a model probability for
* each model this has to be set once after training. As model
* probability the average probability value has been choosen.
*
* TODO: try lowest or highest model probability as alternative
*
* @param defsequence the vector of training sequences.
*/
private void setDefaultProbability(Vector<Gesture> defsequence) {
double prob=0;
for(int i=0; i<defsequence.size(); i++) {
prob+=this.matches(defsequence.elementAt(i));
}
this.defaultprobability=(prob)/defsequence.size();
}
public void setDefaultProbability(double prob) {
this.defaultprobability = prob;
Log.write("def-prob. set to = "+this.defaultprobability);
}
public Quantizer getQuantizer() {
return this.quantizer;
}
public void setQuantizer(Quantizer q) {
this.quantizer = q;
}
public HMM getHMM() {
return this.markovmodell;
}
public void setHMM(HMM hmm) {
this.markovmodell = hmm;
}
}

View File

@@ -0,0 +1,314 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.text.DecimalFormat;
import java.util.Vector;
import org.wiigee.util.Log;
/**
* This is a Hidden Markov Model implementation which internally provides
* the basic algorithms for training and recognition (forward and backward
* algorithm). Since a regular Hidden Markov Model doesn't provide a possibility
* to train multiple sequences, this implementation has been optimized for this
* purposes using some state-of-the-art technologies described in several papers.
*
* @author Benjamin 'BePo' Poppinga
*
*/
public class HMM {
/** The number of states */
protected int numStates;
/** The number of observations */
protected int numObservations;
/** The initial probabilities for each state: p[state] */
protected double pi[];
/** The state change probability to switch from state A to
* state B: a[stateA][stateB] */
protected double a[][];
/** The probability to emit symbol S in state A: b[stateA][symbolS] */
protected double b[][];
/**
* Initialize the Hidden Markov Model in a left-to-right version.
*
* @param numStates Number of states
* @param numObservations Number of observations
*/
public HMM(int numStates, int numObservations) {
this.numStates = numStates;
this.numObservations = numObservations;
pi = new double[numStates];
a = new double[numStates][numStates];
b = new double[numStates][numObservations];
this.reset();
}
/**
* Reset the Hidden Markov Model to the initial left-to-right values.
*
*/
private void reset() {
int jumplimit = 2;
// set startup probability
pi[0] = 1;
for(int i=1; i<numStates; i++) {
pi[i] = 0;
}
// set state change probabilities in the left-to-right version
// NOTE: i now that this is dirty and very static. :)
for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) {
if(i==numStates-1 && j==numStates-1) { // last row
a[i][j] = 1.0;
} else if(i==numStates-2 && j==numStates-2) { // next to last row
a[i][j] = 0.5;
} else if(i==numStates-2 && j==numStates-1) { // next to last row
a[i][j] = 0.5;
} else if(i<=j && i>j-jumplimit-1) {
a[i][j] = 1.0/(jumplimit+1);
} else {
a[i][j] = 0.0;
}
}
}
// emission probability
for(int i=0; i<numStates; i++) {
for(int j=0; j<numObservations; j++) {
b[i][j] = 1.0/(double)numObservations;
}
}
}
/**
* Trains the Hidden Markov Model with multiple sequences.
* This method is normally not known to basic hidden markov
* models, because they usually use the Baum-Welch-Algorithm.
* This method is NOT the traditional Baum-Welch-Algorithm.
*
* If you want to know in detail how it works please consider
* my Individuelles Projekt paper on the wiigee Homepage. Also
* there exist some english literature on the world wide web.
* Try to search for some papers by Rabiner or have a look at
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
* application to isolated user-dependent hand gesture recognition".
*
*/
public void train(Vector<int[]> trainsequence) {
double[][] a_new = new double[a.length][a.length];
double[][] b_new = new double[b.length][b[0].length];
// re calculate state change probability a
for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) {
double zaehler=0;
double nenner=0;
for(int k=0; k<trainsequence.size(); k++) {
int[] sequence = trainsequence.elementAt(k);
double[][] fwd = this.forwardProc(sequence);
double[][] bwd = this.backwardProc(sequence);
double prob = this.getProbability(sequence);
double zaehler_innersum=0;
double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) {
zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1];
nenner_innersum+=fwd[i][t]*bwd[i][t];
}
zaehler+=(1/prob)*zaehler_innersum;
nenner+=(1/prob)*nenner_innersum;
} // k
a_new[i][j] = zaehler/nenner;
} // j
} // i
// re calculate emission probability b
for(int i=0; i<b.length; i++) { // zustaende
for(int j=0; j<b[i].length; j++) { // symbole
double zaehler=0;
double nenner=0;
for(int k=0; k<trainsequence.size(); k++) {
int[] sequence = trainsequence.elementAt(k);
double[][] fwd = this.forwardProc(sequence);
double[][] bwd = this.backwardProc(sequence);
double prob = this.getProbability(sequence);
double zaehler_innersum=0;
double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) {
if(sequence[t]==j) {
zaehler_innersum+=fwd[i][t]*bwd[i][t];
}
nenner_innersum+=fwd[i][t]*bwd[i][t];
}
zaehler+=(1/prob)*zaehler_innersum;
nenner+=(1/prob)*nenner_innersum;
} // k
b_new[i][j] = zaehler/nenner;
} // j
} // i
this.a=a_new;
this.b=b_new;
}
/**
* Traditional Forward Algorithm.
*
* @param o the observationsequence O
* @return Array[State][Time]
*
*/
protected double[][] forwardProc(int[] o) {
double[][] f = new double[numStates][o.length];
for (int l = 0; l < f.length; l++) {
f[l][0] = pi[l] * b[l][o[0]];
}
for (int i = 1; i < o.length; i++) {
for (int k = 0; k < f.length; k++) {
double sum = 0;
for (int l = 0; l < numStates; l++) {
sum += f[l][i-1] * a[l][k];
}
f[k][i] = sum * b[k][o[i]];
}
}
return f;
}
/**
* Returns the probability that a observation sequence O belongs
* to this Hidden Markov Model without using the bayes classifier.
* Internally the well known forward algorithm is used.
*
* @param o observation sequence
* @return probability that sequence o belongs to this hmm
*/
public double getProbability(int[] o) {
double prob = 0.0;
double[][] forward = this.forwardProc(o);
// add probabilities
for (int i = 0; i < forward.length; i++) { // for every state
prob += forward[i][forward[i].length - 1];
}
return prob;
}
/**
* Backward algorithm.
*
* @param o observation sequence o
* @return Array[State][Time]
*/
protected double[][] backwardProc(int[] o) {
int T = o.length;
double[][] bwd = new double[numStates][T];
/* Basisfall */
for (int i = 0; i < numStates; i++)
bwd[i][T - 1] = 1;
/* Induktion */
for (int t = T - 2; t >= 0; t--) {
for (int i = 0; i < numStates; i++) {
bwd[i][t] = 0;
for (int j = 0; j < numStates; j++)
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
}
}
return bwd;
}
/**
* Prints everything about this model, including
* all values. For debug purposes or if you want
* to comprehend what happend to the model.
*
*/
public void print() {
DecimalFormat fmt = new DecimalFormat();
fmt.setMinimumFractionDigits(5);
fmt.setMaximumFractionDigits(5);
for (int i = 0; i < numStates; i++)
Log.write(Log.DEBUG, "pi(" + i + ") = " + fmt.format(pi[i]), this);
Log.write(Log.DEBUG, "", this);
for (int i = 0; i < numStates; i++) {
for (int j = 0; j < numStates; j++)
Log.write(Log.DEBUG, "a(" + i + "," + j + ") = "
+ fmt.format(a[i][j]) + " ", this);
Log.write(Log.DEBUG, "", this);
}
Log.write("");
for (int i = 0; i < numStates; i++) {
for (int k = 0; k < numObservations; k++)
Log.write(Log.DEBUG, "b(" + i + "," + k + ") = "
+ fmt.format(b[i][k]) + " ", this);
Log.write(Log.DEBUG, "", this);
}
}
public double[] getPi() {
return this.pi;
}
public void setPi(double[] pi) {
this.pi = pi;
}
public double[][] getA() {
return this.a;
}
public void setA(double[][] a) {
this.a = a;
}
public double[][] getB() {
return this.b;
}
public void setB(double[][] b) {
this.b=b;
}
}

View File

@@ -0,0 +1,449 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.text.*;
import java.util.Vector;
import java.lang.Math;
import org.wiigee.util.Log;
/**
* This is a Hidden Markov Model implementation which internally provides
* the basic algorithms for training and recognition (forward and backward
* algorithm). Since a regular Hidden Markov Model doesn't provide a possibility
* to train multiple sequences, this implementation has been optimized for this
* purposes using some state-of-the-art technologies described in several papers.
*
* @author Benjamin 'BePo' Poppinga
*
*/
public class PreciseHMM {
/** The number of states */
private int numStates;
/** The number of observations */
private int sigmaSize;
/** The initial probabilities for each state: p[state] */
public double pi[];
/** The state change probability to switch from state A to
* state B: a[stateA][stateB] */
public double a[][];
/** The probability to emit symbol S in state A: b[stateA][symbolS] */
public double b[][];
/**
* Initialize the Hidden Markov Model in a left-to-right version.
*
* @param numStates Number of states
* @param sigmaSize Number of observations
*/
public PreciseHMM(int numStates, int sigmaSize) {
this.numStates = numStates;
this.sigmaSize = sigmaSize;
pi = new double[numStates];
a = new double[numStates][numStates];
b = new double[numStates][sigmaSize];
this.reset();
}
/**
* Reset the Hidden Markov Model to the initial left-to-right values.
*
*/
private void reset() {
int jumplimit = 2;
// set startup probability
pi[0] = 1.0;
for(int i=1; i<numStates; i++) {
pi[i] = 0;
}
// set state change probabilities in the left-to-right version
// NOTE: i now that this is dirty and very static. :)
for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) {
if(i==numStates-1 && j==numStates-1) { // last row
a[i][j] = 1.0;
} else if(i==numStates-2 && j==numStates-2) { // next to last row
a[i][j] = 0.5;
} else if(i==numStates-2 && j==numStates-1) { // next to last row
a[i][j] = 0.5;
} else if(i<=j && i>j-jumplimit-1) {
a[i][j] = 1.0/(jumplimit+1);
} else {
a[i][j] = 0.0;
}
}
}
// emission probability
for(int i=0; i<numStates; i++) {
for(int j=0; j<sigmaSize; j++) {
b[i][j] = 1.0/(double)sigmaSize;
}
}
}
/**
* Trains the Hidden Markov Model with multiple sequences.
* This method is normally not known to basic hidden markov
* models, because they usually use the Baum-Welch-Algorithm.
* This method is NOT the traditional Baum-Welch-Algorithm.
*
* If you want to know in detail how it works please consider
* my Individuelles Projekt paper on the wiigee Homepage. Also
* there exist some english literature on the world wide web.
* Try to search for some papers by Rabiner or have a look at
* Vesa-Matti Mäntylä - "Discrete Hidden Markov Models with
* application to isolated user-dependent hand gesture recognition".
*
*/
public void train(Vector<int[]> trainsequence) {
double[][] a_new = new double[a.length][a.length];
double[][] b_new = new double[b.length][b[0].length];
// re calculate state change probability a
for(int i=0; i<a.length; i++) {
for(int j=0; j<a[i].length; j++) {
double zaehler=0;
double nenner=0;
for(int k=0; k<trainsequence.size(); k++) {
//this.reset();
int[] sequence = trainsequence.elementAt(k);
double[] sf = this.calculateScalingFactor(sequence);
double[][] fwd = this.scaledForwardProc(sequence);
double[][] bwd = this.scaledBackwardProc(sequence, sf);
double zaehler_innersum=0;
double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) {
zaehler_innersum+=fwd[i][t]*a[i][j]*b[j][sequence[t+1]]*bwd[j][t+1]*sf[t+1];
nenner_innersum+=fwd[i][t]*bwd[i][t];
}
zaehler+=zaehler_innersum;
nenner+=nenner_innersum;
} // k
a_new[i][j] = zaehler/nenner;
} // j
} // i
// re calculate emission probability b
for(int i=0; i<b.length; i++) { // zustaende
for(int j=0; j<b[i].length; j++) { // symbole
double zaehler=0;
double nenner=0;
for(int k=0; k<trainsequence.size(); k++) {
//this.reset();
int[] sequence = trainsequence.elementAt(k);
double[] sf = this.calculateScalingFactor(sequence);
double[][] fwd = this.scaledForwardProc(sequence);
double[][] bwd = this.scaledBackwardProc(sequence, sf);
double zaehler_innersum=0;
double nenner_innersum=0;
for(int t=0; t<sequence.length-1; t++) {
if(sequence[t]==j) {
zaehler_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
}
nenner_innersum+=fwd[i][t]*bwd[i][t]*sf[t];
}
zaehler+=zaehler_innersum;
nenner+=nenner_innersum;
} // k
b_new[i][j] = zaehler/nenner;
} // j
} // i
this.a=a_new;
this.b=b_new;
}
private double[] calculateScalingFactor(int[] sequence) {
// for all indexing: [state][time]
double[][] fwd = this.forwardProc(sequence); // normal
double[][] help = new double[fwd.length][fwd[0].length];
double[][] scaled = new double[fwd.length][fwd[0].length];
double[] sf = new double[sequence.length];
// ************** BASIS *************
// Basis, fixed t=0
// setup, because needed for further calculations
for(int i=0; i<help.length; i++) {
help[i][0] = fwd[i][0];
}
// setup initial scaled array
double sum0 = 0;
for(int i=0; i<help.length; i++) {
sum0+=help[i][0];
}
for(int i=0; i<scaled.length; i++) {
scaled[i][0] = help[i][0] / sum0;
}
// calculate scaling factor
sf[0] = 1/sum0;
// **************** INDUCTION ***************
// end of fixed t = 0
// starting with t>1 to sequence.length
// induction, further calculations
for(int t=1; t<sequence.length; t++) {
// calculate help
for(int i=0; i<help.length; i++) {
for(int j=0; j<this.numStates; j++) {
help[i][t]+=scaled[j][t-1]*a[j][i]*b[i][sequence[t]];
}
}
double sum = 0;
for(int i=0; i<help.length; i++) {
sum+=help[i][t];
}
for(int i=0; i<scaled.length; i++) {
scaled[i][t] = help[i][t] / sum;
}
// calculate scaling factor
sf[t] = 1 / sum;
} // t
return sf;
} // calculateScalingFactor
/***
* Returns the scaled Forward variable.
* TODO: Maybe try out if the other precalculated method is faster.
* @param sequence
* @return
*/
private double[][] scaledForwardProc(int[] sequence) {
double[][] fwd = this.forwardProc(sequence);
double[][] out = new double[fwd.length][fwd[0].length];
for(int i=0; i<fwd.length; i++) {
for(int t=0; t<sequence.length; t++) {
double sum = 0;
for(int j=0; j<fwd.length; j++) {
sum+=fwd[j][t];
}
out[i][t] = fwd[i][t] / sum;
}
}
return out;
}
private double[][] scaledBackwardProc(int[] sequence, double[] sf) {
double[][] bwd = this.backwardProc(sequence);
double[][] out = new double[bwd.length][bwd[0].length];
for(int i=0; i<bwd.length; i++) {
for(int t=0; t<sequence.length; t++) {
out[i][t]=1;
for(int r=t+1; r<sequence.length; r++) {
out[i][t]*=sf[r]*bwd[i][t];
}
}
}
return out;
}
/**
* Returns the probability that a observation sequence O belongs
* to this Hidden Markov Model without using the bayes classifier.
* Internally the well known forward algorithm is used.
*
* @param o observation sequence
* @return probability that sequence o belongs to this hmm
*/
public double getProbability(int[] o) {
return scaledViterbi(o);
//return sProbability(o);
/*double prob = 0.0;
double[][] forward = this.forwardProc(o);
// add probabilities
for (int i = 0; i < forward.length; i++) { // for every state
prob += forward[i][forward[i].length - 1];
}
return prob;*/
}
public double sProbability(int[] o) {
double prod = 1.0;
double[][] fwd = this.scaledForwardProc(o);
for(int t=0; t<o.length; t++) {
double sum = 0.0;
for(int i=0; i<this.numStates; i++) {
sum+=fwd[i][t];
}
sum = 1/sum;
prod*=sum;
}
return 1/prod;
}
public double scaledViterbi(int[] o) {
double[][] phi = new double[this.numStates][o.length]; //phi[states][oseq]
// init
for(int i=0; i<this.numStates; i++) {
phi[i][0] = Math.log(pi[i]) + Math.log(b[i][o[0]]);
}
// induction
for(int t=1; t<o.length; t++) {
for(int j=0; j<this.numStates; j++) {
double max = Double.NEGATIVE_INFINITY;
for(int i=0; i<this.numStates; i++) {
double val = phi[i][t-1] + Math.log(this.a[i][j]);
if(val>max) {
max = val;
}
}
phi[j][t] = max + Math.log(this.b[j][o[t]]);
}
}
// conclusion
double lp = Double.NEGATIVE_INFINITY;
for(int i=0; i<this.numStates; i++) {
if(phi[i][o.length-1]>lp) {
lp = phi[i][o.length-1];
}
}
//Log.write("log p = "+lp);
//return lp;
// we now have log10(p) calculated, transform to p.
Log.write("prob = "+Math.exp(lp));
return Math.exp(lp);
//return Math.pow(10, lp);
}
/**
* Traditional Forward Algorithm.
*
* @param o the observationsequence O
* @return Array[State][Time]
*
*/
private double[][] forwardProc(int[] o) {
double[][] f = new double[numStates][o.length];
for (int l = 0; l < f.length; l++) {
f[l][0] = pi[l] * b[l][o[0]];
}
for (int i = 1; i < o.length; i++) {
for (int k = 0; k < f.length; k++) {
double sum = 0;
for (int l = 0; l < numStates; l++) {
sum += f[l][i-1] * a[l][k];
}
f[k][i] = sum * b[k][o[i]];
}
}
return f;
}
/**
* Backward algorithm.
*
* @param o observation sequence o
* @return Array[State][Time]
*/
private double[][] backwardProc(int[] o) {
int T = o.length;
double[][] bwd = new double[numStates][T];
/* Basisfall */
for (int i = 0; i < numStates; i++)
bwd[i][T - 1] = 1;
/* Induktion */
for (int t = T - 2; t >= 0; t--) {
for (int i = 0; i < numStates; i++) {
bwd[i][t] = 0;
for (int j = 0; j < numStates; j++)
bwd[i][t] += (bwd[j][t + 1] * a[i][j] * b[j][o[t + 1]]);
}
}
return bwd;
}
/**
* Prints everything about this model, including
* all values. For debug purposes or if you want
* to comprehend what happend to the model.
*
*/
public void print() {
DecimalFormat fmt = new DecimalFormat();
fmt.setMinimumFractionDigits(10);
fmt.setMaximumFractionDigits(10);
for (int i = 0; i < numStates; i++)
Log.write("pi(" + i + ") = " + fmt.format(pi[i]));
Log.write("");
for (int i = 0; i < numStates; i++) {
for (int j = 0; j < numStates; j++)
Log.write("a(" + i + "," + j + ") = "
+ fmt.format(a[i][j]) + " ");
Log.write("");
}
Log.write("");
for (int i = 0; i < numStates; i++) {
for (int k = 0; k < sigmaSize; k++)
Log.write("b(" + i + "," + k + ") = "
+ fmt.format(b[i][k]) + " ");
Log.write("");
}
}
public double[][] getA() {
return this.a;
}
public void setA(double[][] a) {
this.a = a;
}
public double[][] getB() {
return this.b;
}
public void setB(double[][] b) {
this.b=b;
}
}

View File

@@ -0,0 +1,72 @@
package org.wiigee.logic;
import java.util.Vector;
import org.wiigee.event.AccelerationEvent;
import org.wiigee.event.ButtonPressedEvent;
import org.wiigee.event.ButtonReleasedEvent;
import org.wiigee.event.AccelerationListener;
import org.wiigee.event.ButtonListener;
import org.wiigee.event.GestureEvent;
import org.wiigee.event.GestureListener;
import org.wiigee.event.MotionStartEvent;
import org.wiigee.event.MotionStopEvent;
import org.wiigee.util.Log;
public abstract class ProcessingUnit implements AccelerationListener, ButtonListener {
// Classifier
protected Classifier classifier;
// Listener
private Vector<GestureListener> gesturelistener = new Vector<GestureListener>();
public ProcessingUnit() {
this.classifier = new Classifier();
}
/**
* Add an GestureListener to receive GestureEvents.
*
* @param g
* Class which implements GestureListener interface.
*/
public void addGestureListener(GestureListener g) {
this.gesturelistener.add(g);
}
protected void fireGestureEvent(boolean valid, int id, double probability) {
GestureEvent w = new GestureEvent(this, valid, id, probability);
for (int i = 0; i < this.gesturelistener.size(); i++) {
this.gesturelistener.get(i).gestureReceived(w);
}
}
public abstract void accelerationReceived(AccelerationEvent event);
public abstract void buttonPressReceived(ButtonPressedEvent event);
public abstract void buttonReleaseReceived(ButtonReleasedEvent event);
public abstract void motionStartReceived(MotionStartEvent event);
public abstract void motionStopReceived(MotionStopEvent event);
/**
* Resets the complete gesturemodel. After reset no gesture is known
* to the system.
*/
public void reset() {
if (this.classifier.getCountOfGestures() > 0) {
this.classifier.clear();
Log.write("### Model reset ###");
} else {
Log.write("There doesn't exist any data to reset.");
}
}
// File IO
public abstract void loadGesture(String filename);
public abstract void saveGesture(int id, String filename);
}

View File

@@ -0,0 +1,309 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.util.Vector;
import org.wiigee.event.AccelerationEvent;
import org.wiigee.util.Log;
/**
* This class implements a quantization component. In this case a
* k-mean-algorithm is used. In this case the initial values of the algorithm
* are ordered as two intersected circles, representing an abstract globe with
* k=14 elements. As a special feature the radius of this globe would be
* calculated dynamically before the training of this component.
*
* @author Benjamin 'BePo' Poppinga
*/
public class Quantizer {
/** This is the initial radius of this model. */
private double radius;
/** Number of states from the following Hidden Markov Model */
private int numStates;
/** The representation of the so called Centeroids */
private double[][] map;
/** True, if map is already trained. */
private boolean maptrained;
/**
* Initialize a empty quantizer. The states variable is necessary since some
* algorithms need this value to calculate their values correctly.
*
* @param numStates
* number of hidden markov model states
*/
public Quantizer(int numStates) {
this.numStates = numStates;
this.map = new double[14][3];
this.maptrained = false;
}
/**
* Trains this Quantizer with a specific gesture. This means that the
* positions of the centeroids would adapt to this training gesture. In our
* case this would happen with a summarized virtual gesture, containing all
* the other gestures.
*
* @param gesture
* the summarized virtual gesture
*/
public void trainCenteroids(Gesture gesture) {
Vector<AccelerationEvent> data = gesture.getData();
double pi = Math.PI;
this.radius = (gesture.getMaxAcceleration() + gesture
.getMinAcceleration()) / 2;
Log.write("Using radius: " + this.radius);
// x , z , y
if (!this.maptrained) {
this.maptrained = true;
this.map[0] = new double[] { this.radius, 0.0, 0.0 };
this.map[1] = new double[] { Math.cos(pi / 4) * this.radius, 0.0,
Math.sin(pi / 4) * this.radius };
this.map[2] = new double[] { 0.0, 0.0, this.radius };
this.map[3] = new double[] { Math.cos(pi * 3 / 4) * this.radius,
0.0, Math.sin(pi * 3 / 4) * this.radius };
this.map[4] = new double[] { -this.radius, 0.0, 0.0 };
this.map[5] = new double[] { Math.cos(pi * 5 / 4) * this.radius,
0.0, Math.sin(pi * 5 / 4) * this.radius };
this.map[6] = new double[] { 0.0, 0.0, -this.radius };
this.map[7] = new double[] { Math.cos(pi * 7 / 4) * this.radius,
0.0, Math.sin(pi * 7 / 4) * this.radius };
this.map[8] = new double[] { 0.0, this.radius, 0.0 };
this.map[9] = new double[] { 0.0, Math.cos(pi / 4) * this.radius,
Math.sin(pi / 4) * this.radius };
this.map[10] = new double[] { 0.0,
Math.cos(pi * 3 / 4) * this.radius,
Math.sin(pi * 3 / 4) * this.radius };
this.map[11] = new double[] { 0.0, -this.radius, 0.0 };
this.map[12] = new double[] { 0.0,
Math.cos(pi * 5 / 4) * this.radius,
Math.sin(pi * 5 / 4) * this.radius };
this.map[13] = new double[] { 0.0,
Math.cos(pi * 7 / 4) * this.radius,
Math.sin(pi * 7 / 4) * this.radius };
}
int[][] g_alt = new int[this.map.length][data.size()];
int[][] g = new int[this.map.length][data.size()];
do {
// Derive new Groups...
g_alt = this.copyarray(g);
g = this.deriveGroups(gesture);
// calculate new centeroids
for (int i = 0; i < this.map.length; i++) {
double zaehlerX = 0;
double zaehlerY = 0;
double zaehlerZ = 0;
int nenner = 0;
for (int j = 0; j < data.size(); j++) {
if (g[i][j] == 1) {
zaehlerX += data.elementAt(j).getX();
zaehlerY += data.elementAt(j).getY();
zaehlerZ += data.elementAt(j).getZ();
nenner++;
}
}
if (nenner > 1) { // nur wenn der nenner>0 oder >1??? ist muss
// was
// geaendert werden
// Log.write("Setze neuen Centeroid!");
this.map[i] = new double[] {(zaehlerX / (double) nenner),
(zaehlerY / (double) nenner),
(zaehlerZ / (double) nenner) };
// Log.write("Centeroid: "+i+": "+newcenteroid[0]+":"+newcenteroid[1]);
}
} // new centeroids
} while (!equalarrays(g_alt, g));
// Debug: Printout groups
/*
* for (int i = 0; i < n; i++) { for (int j = 0; j < this.data.size();
* j++) { Log.write(g[i][j] + "|"); } Log.write(""); }
*/
}
/**
* This methods looks up a Gesture to a group matrix, used by the
* k-mean-algorithm (traincenteroid method) above.
*
* @param gesture
* the gesture
*/
public int[][] deriveGroups(Gesture gesture) {
Vector<AccelerationEvent> data = gesture.getData();
int[][] groups = new int[this.map.length][data.size()];
// Calculate cartesian distance
double[][] d = new double[this.map.length][data.size()];
double[] curr = new double[3];
double[] vector = new double[3];
for (int i = 0; i < this.map.length; i++) { // zeilen
double[] ref = this.map[i];
for (int j = 0; j < data.size(); j++) { // spalten
curr[0] = data.elementAt(j).getX();
curr[1] = data.elementAt(j).getY();
curr[2] = data.elementAt(j).getZ();
vector[0] = ref[0] - curr[0];
vector[1] = ref[1] - curr[1];
vector[2] = ref[2] - curr[2];
d[i][j] = Math.sqrt((vector[0] * vector[0])
+ (vector[1] * vector[1]) + (vector[2] * vector[2]));
// Log.write(d[i][j] + "|");
}
// Log.write("");
}
// look, to which group a value belongs
for (int j = 0; j < data.size(); j++) {
double smallest = Double.MAX_VALUE;
int row = 0;
for (int i = 0; i < this.map.length; i++) {
if (d[i][j] < smallest) {
smallest = d[i][j];
row = i;
}
groups[i][j] = 0;
}
groups[row][j] = 1; // guppe gesetzt
}
// Debug output
/*
* for (int i = 0; i < groups.length; i++) { // zeilen for (int j = 0; j
* < groups[i].length; j++) { Log.write(groups[i][j] + "|"); }
* Log.write(""); }
*/
return groups;
}
/**
* With this method you can transform a gesture to a discrete symbol
* sequence with values between 0 and granularity (number of observations).
*
* @param gesture
* Gesture to get the observationsequence to.
*/
public int[] getObservationSequence(Gesture gesture) {
int[][] groups = this.deriveGroups(gesture);
Vector<Integer> sequence = new Vector<Integer>();
// Log.write("Visible symbol sequence: ");
for (int j = 0; j < groups[0].length; j++) { // spalten
for (int i = 0; i < groups.length; i++) { // zeilen
if (groups[i][j] == 1) {
// Log.write(" "+ i);
sequence.add(i);
break;
}
}
}
// die sequenz darf nicht zu kurz sein... mindestens so lang
// wie die anzahl der zustände. weil sonst die formeln nicht klappen.
// english: this is very dirty! it have to be here because if not
// too short sequences would cause an error. i've to think about a
// better resolution than copying the old value a few time.
while (sequence.size() < this.numStates) {
sequence.add(sequence.elementAt(sequence.size() - 1));
// Log.write(" "+sequence.elementAt(sequence.size()-1));
}
// Log.write("");
int[] out = new int[sequence.size()];
for (int i = 0; i < sequence.size(); i++) {
out[i] = sequence.elementAt(i);
}
return out;
}
/**
* Prints out the current centeroids-map. Its for debug or technical
* interests.
*/
public void printMap() {
Log.write(Log.DEBUG, "Centeroids:", this);
for (int i = 0; i < this.map.length; i++) {
Log.write(Log.DEBUG, i + ". :" + this.map[i][0] + ":"
+ this.map[i][1] + ":" + this.map[i][2], this);
}
}
/**
* Function to deepcopy an array.
*/
private int[][] copyarray(int[][] alt) {
int[][] neu = new int[alt.length][alt[0].length];
for (int i = 0; i < alt.length; i++) {
for (int j = 0; j < alt[i].length; j++) {
neu[i][j] = alt[i][j];
}
}
return neu;
}
/**
* Function to look if the two arrays containing the same values.
*/
private boolean equalarrays(int[][] one, int[][] two) {
for (int i = 0; i < one.length; i++) {
for (int j = 0; j < one[i].length; j++) {
if (!(one[i][j] == two[i][j])) {
return false;
}
}
}
return true;
}
public double getRadius() {
return this.radius;
}
public double[][] getHashMap() {
return this.map;
}
public void setUpManually(double[][] map, double radius) {
this.map = map;
this.radius = radius;
}
}

View File

@@ -0,0 +1,188 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.logic;
import java.util.Vector;
import org.wiigee.event.*;
import org.wiigee.util.Log;
/**
* This class analyzes the AccelerationEvents emitted from a Wiimote
* and further creates and manages the different models for each type
* of gesture.
*
* @author Benjamin 'BePo' Poppinga
*/
public class TriggeredProcessingUnit extends ProcessingUnit {
// gesturespecific values
private Gesture current; // current gesture
private Vector<Gesture> trainsequence;
// State variables
private boolean learning, analyzing;
public TriggeredProcessingUnit() {
super();
this.learning=false;
this.analyzing=false;
this.current=new Gesture();
this.trainsequence=new Vector<Gesture>();
}
/**
* Since this class implements the WiimoteListener this procedure is
* necessary. It contains the filtering (directional equivalence filter)
* and adds the incoming data to the current motion, we want to train
* or recognize.
*
* @param event The acceleration event which has to be processed by the
* directional equivalence filter and which has to be added to the current
* motion in recognition or training process.
*/
public void accelerationReceived(AccelerationEvent event) {
if(this.learning || this.analyzing) {
this.current.add(event); // add event to gesture
}
}
/**
* This method is from the WiimoteListener interface. A button press
* is used to control the data flow inside the structures.
*
*/
public void buttonPressReceived(ButtonPressedEvent event) {
this.handleStartEvent(event);
}
public void buttonReleaseReceived(ButtonReleasedEvent event) {
this.handleStopEvent(event);
}
public void motionStartReceived(MotionStartEvent event) {
// this.handleStartEvent(event);
}
public void motionStopReceived(MotionStopEvent event) {
// this.handleStopEvent(event);
}
public void handleStartEvent(ActionStartEvent event) {
// TrainButton = record a gesture for learning
if((!this.analyzing && !this.learning) &&
event.isTrainInitEvent()) {
Log.write("Training started!");
this.learning=true;
}
// RecognitionButton = record a gesture for recognition
if((!this.analyzing && !this.learning) &&
event.isRecognitionInitEvent()) {
Log.write("Recognition started!");
this.analyzing=true;
}
// CloseGestureButton = starts the training of the model with multiple
// recognized gestures, contained in trainsequence
if((!this.analyzing && !this.learning) &&
event.isCloseGestureInitEvent()) {
if(this.trainsequence.size()>0) {
Log.write("Training the model with "+this.trainsequence.size()+" gestures...");
this.learning=true;
GestureModel m = new GestureModel();
m.train(this.trainsequence);
// m.print(); // Prints model details after training
this.classifier.addGestureModel(m);
this.trainsequence=new Vector<Gesture>();
this.learning=false;
} else {
Log.write("There is nothing to do. Please record some gestures first.");
}
}
}
public void handleStopEvent(ActionStopEvent event) {
if(this.learning) { // button release and state=learning, stops learning
if(this.current.getCountOfData()>0) {
Log.write("Finished recording (training)...");
Log.write("Data: "+this.current.getCountOfData());
Gesture gesture = new Gesture(this.current);
this.trainsequence.add(gesture);
this.current=new Gesture();
this.learning=false;
} else {
Log.write("There is no data.");
Log.write("Please train the gesture again.");
this.learning=false; // ?
}
}
else if(this.analyzing) { // button release and state=analyzing, stops analyzing
if(this.current.getCountOfData()>0) {
Log.write("Finished recording (recognition)...");
Log.write("Compare gesture with "+this.classifier.getCountOfGestures()+" other gestures.");
Gesture gesture = new Gesture(this.current);
int recognized = this.classifier.classifyGesture(gesture);
if(recognized!=-1) {
double recogprob = this.classifier.getLastProbability();
this.fireGestureEvent(true, recognized, recogprob);
Log.write("######");
Log.write("Gesture No. "+recognized+" recognized: "+recogprob);
Log.write("######");
} else {
this.fireGestureEvent(false, 0, 0.0);
Log.write("######");
Log.write("No gesture recognized.");
Log.write("######");
}
this.current=new Gesture();
this.analyzing=false;
} else {
Log.write("There is no data.");
Log.write("Please recognize the gesture again.");
this.analyzing=false; // ?
}
}
}
@Override
public void loadGesture(String filename) {
GestureModel g = org.wiigee.util.FileIO.readFromFile(filename);
this.classifier.addGestureModel(g);
}
@Override
public void saveGesture(int id, String filename) {
org.wiigee.util.FileIO.writeToFile(this.classifier.getGestureModel(id), filename);
}
}

View File

@@ -0,0 +1,85 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.wiigee.logic;
import java.util.Vector;
/**
*
* @author bepo
*/
public class XHMM extends HMM {
// the temporal values for scaling
int[] currSequence;
double[][] currForward;
double[][] scaledForward;
double[][] currBackward;
double[][] scaledBackward;
double[] currScaling;
double[][] currHelper;
public XHMM(int numStates, int numObservations) {
super(numStates, numObservations);
}
@Override
public void train(Vector<int[]> trainsequence) {
}
private double[][] getScaledForward(int[] sequence) {
double[][] fwd = this.forwardProc(sequence);
double[][] retVal = new double[fwd.length][fwd[0].length];
for(int t=0; t<fwd.length; t++) {
for(int i=0; i<fwd[0].length; i++) {
// build sum
double sum = 0.0;
for(int n=0; n<this.numStates; n++) {
sum += fwd[t][n];
}
retVal[t][i] = fwd[t][i] / sum;
}
}
return retVal;
}
private double[][] getScaledBackward(int[] sequence) {
double[][] fwd = this.forwardProc(sequence);
double[][] bwd = this.backwardProc(sequence);
double[][] retVal = new double[bwd.length][bwd[0].length];
for(int t=0; t<bwd.length; t++) {
for(int i=0; i<bwd[0].length; i++) {
// build sum
double sum = 0.0;
for(int n=0; n<this.numStates; n++) {
sum += fwd[t][n];
}
retVal[t][i] = bwd[t][i] / sum;
}
}
return retVal;
}
private double getScalingDenominator(int t, int[] sequence) {
double retVal = 0.0;
double[][] fwd = this.forwardProc(sequence);
double[][] sfwd = this.getScaledForward(sequence);
double[][] helper = new double[sfwd.length][sfwd[0].length];
if(t==0) {
}
return retVal;
}
}

View File

@@ -0,0 +1,233 @@
/*
* wiigee - accelerometerbased gesture recognition
* Copyright (C) 2007, 2008, 2009 Benjamin Poppinga
*
* Developed at University of Oldenburg
* Contact: wiigee@benjaminpoppinga.de
*
* This file is part of wiigee.
*
* wiigee is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.wiigee.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.wiigee.logic.GestureModel;
import org.wiigee.logic.HMM;
import org.wiigee.logic.Quantizer;
/**
* This is a static class to support saving and loading complete gestures. I've
* choosen not to use some kind of XML, because the big multidimensional arrays
* would cause a huge senseless amount of xml-data. So KISS: Keep It Simple,
* Stupid! ;) Comma-separated-vectors with some control data.
*
* @author Benjamin 'BePo' Poppinga
*
*/
public class FileIO {
public static void writeToFile(GestureModel m, String name) {
try {
// initialize file and get values
BufferedWriter out = new BufferedWriter(new FileWriter(name+".txt"));
int numStates = m.getNumStates();
int numObservations = m.getNumObservations();
double defaultProbability = m.getDefaultProbability();
Quantizer quantizer = m.getQuantizer();
HMM hmm = m.getHMM();
out.write("# numStates:");
out.newLine();
out.write(Integer.toString(numStates));
out.newLine();
out.write("# numObservations:");
out.newLine();
out.write(Integer.toString(numObservations));
out.newLine();
out.write("# defaultProbability:");
out.newLine();
out.write(Double.toString(defaultProbability));
out.newLine();
out.write("# Quantizer: Radius");
out.newLine();
out.write(Double.toString(quantizer.getRadius()));
out.newLine();
out.write("# Quantizer: MAP");
out.newLine();
double[][] map = quantizer.getHashMap();
for(int v=0; v<map.length; v++) {
double[] d = map[v];
out.write(Double.toString(d[0])+", "+Double.toString(d[1])+", "+Double.toString(d[2]));
out.newLine();
}
out.write("# HMM: PI");
out.newLine();
double[] pi = hmm.getPi();
for (int i=0; i<numStates; i++) {
if(i==numStates-1) {
out.write(Double.toString(pi[i]));
out.newLine();
} else {
out.write(Double.toString(pi[i])+", ");
}
}
out.write("# HMM: A");
out.newLine();
double[][] a = hmm.getA();
for(int i=0; i<numStates; i++) {
for(int j=0; j<numStates; j++) {
if(j==numStates-1) {
out.write(Double.toString(a[i][j]));
out.newLine();
} else {
out.write(Double.toString(a[i][j])+", ");
}
}
}
out.write("# HMM: B");
out.newLine();
double[][] b = hmm.getB();
for(int i=0; i<numStates; i++) {
for(int j=0; j<numObservations; j++) {
if(j==numObservations-1) {
out.write(Double.toString(b[i][j]));
out.newLine();
} else {
out.write(Double.toString(b[i][j])+", ");
}
}
}
out.write("# END");
// close file
out.flush();
out.close();
} catch (IOException e) {
System.out.println("Error: Write to File!");
e.printStackTrace();
}
}
public static GestureModel readFromFile(String name) {
try {
// initialize file and create values
BufferedReader in = new BufferedReader(new FileReader(name+".txt"));
int numStates = 0;
int numObservations = 0;
double defaultprobability = 0;
double radius = 0;
double[][] map = new double[numObservations][3];
double[] pi = new double[numStates];
double[][] a = new double[numStates][numStates];
double[][] b = new double[numStates][numObservations];
String line;
int position = 0;
while(in.ready()) {
line = in.readLine();
if(!line.startsWith("#")) { // isn't a comment
switch (position++) {
case 0:
numStates = Integer.parseInt(line);
break;
case 1:
numObservations = Integer.parseInt(line);
break;
case 2:
defaultprobability = Double.parseDouble(line);
break;
case 3:
radius = Double.parseDouble(line);
break;
case 4:
map = new double[numObservations][3];
for(int i=0; i<numObservations; i++) {
String s[] = line.split(", ");
double[] d = new double[] { Double.parseDouble(s[0]),
Double.parseDouble(s[1]),
Double.parseDouble(s[2]) };
map[i] = d;
line = in.ready() ? in.readLine() : "";
}
break;
case 5:
pi = new double[numStates];
String pi_row[] = line.split(", ");
for(int i=0; i<numStates; i++) {
pi[i] = Double.parseDouble(pi_row[i]);
}
break;
case 6:
a = new double[numStates][numStates];
for(int i=0; i<numStates; i++) {
String a_row[] = line.split(", ");
for(int j=0; j<numStates; j++) {
a[i][j] = Double.parseDouble(a_row[j]);
}
line = in.ready() ? in.readLine() : "";
}
break;
case 7:
b = new double[numStates][numObservations];
for(int i=0; i<numStates; i++) {
String b_row[] = line.split(", ");
for(int j=0; j<numObservations; j++) {
b[i][j] = Double.parseDouble(b_row[j]);
}
line = in.ready() ? in.readLine() : "";
}
break;
default:
System.out.println("SWITCH EMPTY!");
break;
}
}
}
GestureModel ret = new GestureModel();
ret.setDefaultProbability(defaultprobability);
Quantizer quantizer = new Quantizer(numStates);
quantizer.setUpManually(map, radius);
ret.setQuantizer(quantizer);
HMM hmm = new HMM(numStates, numObservations);
hmm.setPi(pi);
hmm.setA(a);
hmm.setB(b);
ret.setHMM(hmm);
return ret;
} catch (Exception e) {
System.out.println("Error: Read from File!");
e.printStackTrace();
}
return null; // bad error case
}
}

View File

@@ -0,0 +1,43 @@
package org.wiigee.util;
public class Log {
public static final int OFF = -1;
public static final int NORMAL = 0;
public static final int DEBUG = 1;
public static final int PRINT = 0;
public static final int FILE = 1;
public static int level = Log.NORMAL;
public static int mode = Log.PRINT;
public static void setLevel(int n) {
level = n;
}
public static void write(String s) {
write(Log.NORMAL, s, null);
}
public static void write(String s, Object o) {
write(Log.NORMAL, s, o);
}
public static void write(int n, String s, Object o) {
if(level>=n) {
if(mode==Log.PRINT) {
// console output enabled
if(o!=null) {
System.out.println(o.getClass()+": "+s);
} else {
System.out.println(s);
}
} else if(mode==Log.FILE) {
// file output enabled
// TODO
}
}
}
}