diff --git a/java/src/org/wiigee/control/Wiigee.java b/java/src/org/wiigee/control/Wiigee.java new file mode 100644 index 0000000..41af532 --- /dev/null +++ b/java/src/org/wiigee/control/Wiigee.java @@ -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+")"); + } + +} diff --git a/java/src/org/wiigee/control/WiimoteDeviceDiscovery.java b/java/src/org/wiigee/control/WiimoteDeviceDiscovery.java new file mode 100644 index 0000000..bdc611f --- /dev/null +++ b/java/src/org/wiigee/control/WiimoteDeviceDiscovery.java @@ -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 devices; + private boolean isInquiring; + private final Object lock; + + public WiimoteDeviceDiscovery(Object lock) { + super(); + this.lock = lock; + this.devices = new Vector(); + } + + 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 getDiscoveredWiimotes() throws IOException { + Vector wiimotes = new Vector(); + + for (int i = 0; i < devices.size(); i++) { + wiimotes.add(new Wiimote(devices.elementAt(i).getBluetoothAddress(), true, true)); + } + + return wiimotes; + } +} diff --git a/java/src/org/wiigee/control/WiimoteWiigee.java b/java/src/org/wiigee/control/WiimoteWiigee.java new file mode 100644 index 0000000..aa56bac --- /dev/null +++ b/java/src/org/wiigee/control/WiimoteWiigee.java @@ -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 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 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); + } + } +} diff --git a/java/src/org/wiigee/device/Device.java b/java/src/org/wiigee/device/Device.java new file mode 100644 index 0000000..796b952 --- /dev/null +++ b/java/src/org/wiigee/device/Device.java @@ -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 accfilters = new Vector(); + + // Listeners, receive generated events + protected Vector accelerationlistener = new Vector(); + protected Vector buttonlistener = new Vector(); + + // 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); + } + } +} diff --git a/java/src/org/wiigee/device/Wiimote.java b/java/src/org/wiigee/device/Wiimote.java new file mode 100644 index 0000000..d9fa086 --- /dev/null +++ b/java/src/org/wiigee/device/Wiimote.java @@ -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 rotfilters = new Vector(); + + // 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 = new Vector(); + protected Vector rotationListener = new Vector(); + + // 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; + } +} diff --git a/java/src/org/wiigee/device/WiimoteStreamer.java b/java/src/org/wiigee/device/WiimoteStreamer.java new file mode 100644 index 0000000..b8ba764 --- /dev/null +++ b/java/src/org/wiigee/device/WiimoteStreamer.java @@ -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 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(); + + try { + while (running) { + // connection has data and we're ready. + + byte[] b = this.getRaw(); // blocks application + + // Log.write(""); + + // debug output + /* for(int i=0; ireference[0]+this.sensivity || + vector[1]reference[1]+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; + } + +} diff --git a/java/src/org/wiigee/filter/Filter.java b/java/src/org/wiigee/filter/Filter.java new file mode 100644 index 0000000..e97d4a9 --- /dev/null +++ b/java/src/org/wiigee/filter/Filter.java @@ -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(); + +} diff --git a/java/src/org/wiigee/filter/HighPassFilter.java b/java/src/org/wiigee/filter/HighPassFilter.java new file mode 100644 index 0000000..54a6946 --- /dev/null +++ b/java/src/org/wiigee/filter/HighPassFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/filter/IdleStateFilter.java b/java/src/org/wiigee/filter/IdleStateFilter.java new file mode 100644 index 0000000..b816e0b --- /dev/null +++ b/java/src/org/wiigee/filter/IdleStateFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/filter/LowPassFilter.java b/java/src/org/wiigee/filter/LowPassFilter.java new file mode 100644 index 0000000..2c3e012 --- /dev/null +++ b/java/src/org/wiigee/filter/LowPassFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/filter/MotionDetectFilter.java b/java/src/org/wiigee/filter/MotionDetectFilter.java new file mode 100644 index 0000000..b13124a --- /dev/null +++ b/java/src/org/wiigee/filter/MotionDetectFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/filter/RotationResetFilter.java b/java/src/org/wiigee/filter/RotationResetFilter.java new file mode 100644 index 0000000..48f568e --- /dev/null +++ b/java/src/org/wiigee/filter/RotationResetFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/filter/RotationThresholdFilter.java b/java/src/org/wiigee/filter/RotationThresholdFilter.java new file mode 100644 index 0000000..972ff58 --- /dev/null +++ b/java/src/org/wiigee/filter/RotationThresholdFilter.java @@ -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; + } + +} diff --git a/java/src/org/wiigee/logic/Gesture.java b/java/src/org/wiigee/logic/Gesture.java new file mode 100644 index 0000000..2d1976a --- /dev/null +++ b/java/src/org/wiigee/logic/Gesture.java @@ -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 data; + + /** + * Create an empty Gesture. + */ + public Gesture() { + this.data = new Vector(); + } + + /** + * Make a deep copy of another Gesture object. + * + * @param original Another Gesture object + */ + public Gesture(Gesture original) { + this.data = new Vector(); + Vector 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 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 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