migrated to hidapi-1.1.jar now libraries are located inside hidapi-1.1.jar also library can load them
@@ -0,0 +1,10 @@
|
||||
syntax:glob
|
||||
*~
|
||||
controltower/.settings/*
|
||||
controltower/target/*
|
||||
controltower/.classpath
|
||||
controltower/.project
|
||||
javadrone-api/.settings/*
|
||||
javadrone-api/target/*
|
||||
javadrone-api/.classpath
|
||||
javadrone-api/.project
|
||||
@@ -0,0 +1,93 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>javadrone</artifactId>
|
||||
<version>${currentVersion}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>controltower</artifactId>
|
||||
<name>controltower</name>
|
||||
<description>Demo navigation program using this API </description>
|
||||
|
||||
<build>
|
||||
<finalName>controltower</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>lib</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.codeminders.controltower.ControlTower</mainClass>
|
||||
<packageName>com.codeminders</packageName>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<mainClass>com.codeminders.controltower.ControlTower</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Built-By>Codeminders</Built-By>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/src.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${artifactId}-full-${currentVersion}</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-eclipse-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>javadrone-api</artifactId>
|
||||
<version>${currentVersion}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.hansolo</groupId>
|
||||
<artifactId>SteelSeries</artifactId>
|
||||
<version>3.9.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
<assembly>
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<unpack>true</unpack>
|
||||
<scope>runtime</scope>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,302 @@
|
||||
<?xml version="1.1" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-64,0,0,2,-28"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
<Properties>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="eu.hansolo.steelseries.extras.Indicator" name="indicator1">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="battery low"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="symbolType" type="eu.hansolo.steelseries.tools.SymbolType" editor="org.netbeans.modules.form.editors.EnumEditor">
|
||||
<Value id="BATTERY"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.extras.Indicator" name="indicator4">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="gyrometers down"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="symbolType" type="eu.hansolo.steelseries.tools.SymbolType" editor="org.netbeans.modules.form.editors.EnumEditor">
|
||||
<Value id="BREAK"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.extras.Horizon" name="horizon1">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="artificial horizon"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[60, 60]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="-20" ipadY="-20" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="180" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="180" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.extras.AirCompass" name="airCompass1">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="plane compass"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[60, 60]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="3" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="-20" ipadY="-20" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="180" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="180" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.extras.Altimeter" name="altimeter1">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="altimeter"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[60, 60]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="-60" ipadY="-60" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="62" max="-2" attributes="0"/>
|
||||
<Component id="led1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="62" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<EmptySpace pref="89" max="32767" attributes="0"/>
|
||||
<Component id="led1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="35" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="eu.hansolo.steelseries.extras.Led" name="led1">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="16" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="16" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="jPanel2">
|
||||
<Properties>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="5" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
|
||||
<SubComponents>
|
||||
<Container class="eu.hansolo.steelseries.extras.Indicator" name="indicator2">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="emergency (click to reset)"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="symbolType" type="eu.hansolo.steelseries.tools.SymbolType" editor="org.netbeans.modules.form.editors.EnumEditor">
|
||||
<Value id="ATTENTION"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="resetDrone"/>
|
||||
</Events>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.extras.Indicator" name="indicator3">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="engine problem"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="symbolType" type="eu.hansolo.steelseries.tools.SymbolType" editor="org.netbeans.modules.form.editors.EnumEditor">
|
||||
<Value id="ENGINE"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="50" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="eu.hansolo.steelseries.gauges.Radial1Vertical" name="batteryGraph">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="battery status"/>
|
||||
<Property name="frame3dEffectVisible" type="boolean" value="true"/>
|
||||
<Property name="threshold" type="double" value="110.0"/>
|
||||
<Property name="title" type="java.lang.String" value="Battery"/>
|
||||
<Property name="trackStartColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" red="ff" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="trackStopColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="ff" red="0" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="trackVisible" type="boolean" value="true"/>
|
||||
<Property name="unitString" type="java.lang.String" value="%"/>
|
||||
</Properties>
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
|
||||
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="-60" ipadY="-60" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="140" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="140" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/*
|
||||
* BottomGaugePanel.java
|
||||
*
|
||||
* Created on 23.05.2011, 14:53:56
|
||||
*/
|
||||
package com.codeminders.controltower;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import com.codeminders.ardrone.NavData;
|
||||
import com.codeminders.ardrone.NavDataListener;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BottomGaugePanel extends javax.swing.JPanel implements NavDataListener {
|
||||
|
||||
private BufferedImage commImage;
|
||||
private AtomicReference<ARDrone> drone = new AtomicReference<ARDrone>();
|
||||
|
||||
/** Creates new form BottomGaugePanel */
|
||||
public BottomGaugePanel() {
|
||||
initComponents();
|
||||
try {
|
||||
commImage = ImageIO.read(getClass().getResourceAsStream("/com/codeminders/controltower/images/gaugeback_bottom2.png"));
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ControlTower.class.getName()).error("{0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDrone(ARDrone drone) {
|
||||
this.drone.set(drone);
|
||||
drone.addNavDataListener(this);
|
||||
}
|
||||
|
||||
private void updateGauges(final NavData data) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
horizon1.setPitch(data.getPitch() * -1);
|
||||
horizon1.setRoll(data.getRoll());
|
||||
altimeter1.setValue(data.getAltitude() * 1000);
|
||||
airCompass1.setValue(data.getYaw());
|
||||
batteryGraph.setValue(data.getBattery());
|
||||
if (data.isBatteryTooLow()) {
|
||||
batteryGraph.setThreshold(0);
|
||||
batteryGraph.setLedBlinking(true);
|
||||
indicator1.setOn(true);
|
||||
} else {
|
||||
batteryGraph.setThreshold(110);
|
||||
batteryGraph.setLedBlinking(false);
|
||||
indicator1.setOn(false);
|
||||
|
||||
}
|
||||
if (data.isUltrasonicSensorDeaf()) {
|
||||
led1.setLedBlinking(true);
|
||||
} else {
|
||||
led1.setLedBlinking(false);
|
||||
}
|
||||
if (data.isEmergency()) {
|
||||
indicator2.setOn(true);
|
||||
} else {
|
||||
indicator2.setOn(false);
|
||||
}
|
||||
if (data.isMotorsDown()) {
|
||||
indicator3.setOn(true);
|
||||
} else {
|
||||
indicator3.setOn(false);
|
||||
}
|
||||
if (data.isGyrometersDown()) {
|
||||
indicator4.setOn(true);
|
||||
} else {
|
||||
indicator4.setOn(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navDataReceived(NavData nd) {
|
||||
updateGauges(nd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
g.drawImage(commImage, 0, 0, getWidth(), getHeight(), null);
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
java.awt.GridBagConstraints gridBagConstraints;
|
||||
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
indicator1 = new eu.hansolo.steelseries.extras.Indicator();
|
||||
indicator4 = new eu.hansolo.steelseries.extras.Indicator();
|
||||
horizon1 = new eu.hansolo.steelseries.extras.Horizon();
|
||||
airCompass1 = new eu.hansolo.steelseries.extras.AirCompass();
|
||||
altimeter1 = new eu.hansolo.steelseries.extras.Altimeter();
|
||||
led1 = new eu.hansolo.steelseries.extras.Led();
|
||||
jPanel2 = new javax.swing.JPanel();
|
||||
indicator2 = new eu.hansolo.steelseries.extras.Indicator();
|
||||
indicator3 = new eu.hansolo.steelseries.extras.Indicator();
|
||||
batteryGraph = new eu.hansolo.steelseries.gauges.Radial1Vertical();
|
||||
|
||||
setOpaque(false);
|
||||
setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
jPanel1.setOpaque(false);
|
||||
jPanel1.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
indicator1.setToolTipText("battery low");
|
||||
indicator1.setFrame3dEffectVisible(true);
|
||||
indicator1.setSymbolType(eu.hansolo.steelseries.tools.SymbolType.BATTERY);
|
||||
|
||||
javax.swing.GroupLayout indicator1Layout = new javax.swing.GroupLayout(indicator1);
|
||||
indicator1.setLayout(indicator1Layout);
|
||||
indicator1Layout.setHorizontalGroup(
|
||||
indicator1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
indicator1Layout.setVerticalGroup(
|
||||
indicator1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jPanel1.add(indicator1, new java.awt.GridBagConstraints());
|
||||
|
||||
indicator4.setToolTipText("gyrometers down");
|
||||
indicator4.setFrame3dEffectVisible(true);
|
||||
indicator4.setSymbolType(eu.hansolo.steelseries.tools.SymbolType.BREAK);
|
||||
|
||||
javax.swing.GroupLayout indicator4Layout = new javax.swing.GroupLayout(indicator4);
|
||||
indicator4.setLayout(indicator4Layout);
|
||||
indicator4Layout.setHorizontalGroup(
|
||||
indicator4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
indicator4Layout.setVerticalGroup(
|
||||
indicator4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
jPanel1.add(indicator4, gridBagConstraints);
|
||||
|
||||
add(jPanel1, new java.awt.GridBagConstraints());
|
||||
|
||||
horizon1.setToolTipText("artificial horizon");
|
||||
horizon1.setFrame3dEffectVisible(true);
|
||||
horizon1.setMinimumSize(new java.awt.Dimension(60, 60));
|
||||
|
||||
javax.swing.GroupLayout horizon1Layout = new javax.swing.GroupLayout(horizon1);
|
||||
horizon1.setLayout(horizon1Layout);
|
||||
horizon1Layout.setHorizontalGroup(
|
||||
horizon1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 180, Short.MAX_VALUE)
|
||||
);
|
||||
horizon1Layout.setVerticalGroup(
|
||||
horizon1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 180, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 2;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.ipadx = -20;
|
||||
gridBagConstraints.ipady = -20;
|
||||
add(horizon1, gridBagConstraints);
|
||||
|
||||
airCompass1.setToolTipText("plane compass");
|
||||
airCompass1.setFrame3dEffectVisible(true);
|
||||
airCompass1.setMinimumSize(new java.awt.Dimension(60, 60));
|
||||
|
||||
javax.swing.GroupLayout airCompass1Layout = new javax.swing.GroupLayout(airCompass1);
|
||||
airCompass1.setLayout(airCompass1Layout);
|
||||
airCompass1Layout.setHorizontalGroup(
|
||||
airCompass1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 180, Short.MAX_VALUE)
|
||||
);
|
||||
airCompass1Layout.setVerticalGroup(
|
||||
airCompass1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 180, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 3;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.ipadx = -20;
|
||||
gridBagConstraints.ipady = -20;
|
||||
add(airCompass1, gridBagConstraints);
|
||||
|
||||
altimeter1.setToolTipText("altimeter");
|
||||
altimeter1.setFrame3dEffectVisible(true);
|
||||
altimeter1.setMinimumSize(new java.awt.Dimension(60, 60));
|
||||
|
||||
javax.swing.GroupLayout led1Layout = new javax.swing.GroupLayout(led1);
|
||||
led1.setLayout(led1Layout);
|
||||
led1Layout.setHorizontalGroup(
|
||||
led1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 16, Short.MAX_VALUE)
|
||||
);
|
||||
led1Layout.setVerticalGroup(
|
||||
led1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 16, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout altimeter1Layout = new javax.swing.GroupLayout(altimeter1);
|
||||
altimeter1.setLayout(altimeter1Layout);
|
||||
altimeter1Layout.setHorizontalGroup(
|
||||
altimeter1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(altimeter1Layout.createSequentialGroup()
|
||||
.addGap(62, 62, 62)
|
||||
.addComponent(led1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(62, Short.MAX_VALUE))
|
||||
);
|
||||
altimeter1Layout.setVerticalGroup(
|
||||
altimeter1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, altimeter1Layout.createSequentialGroup()
|
||||
.addContainerGap(89, Short.MAX_VALUE)
|
||||
.addComponent(led1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(35, 35, 35))
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 4;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.ipadx = -60;
|
||||
gridBagConstraints.ipady = -60;
|
||||
add(altimeter1, gridBagConstraints);
|
||||
|
||||
jPanel2.setOpaque(false);
|
||||
jPanel2.setLayout(new java.awt.GridBagLayout());
|
||||
|
||||
indicator2.setToolTipText("emergency (click to reset)");
|
||||
indicator2.setFrame3dEffectVisible(true);
|
||||
indicator2.setSymbolType(eu.hansolo.steelseries.tools.SymbolType.ATTENTION);
|
||||
indicator2.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseReleased(java.awt.event.MouseEvent evt) {
|
||||
resetDrone(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout indicator2Layout = new javax.swing.GroupLayout(indicator2);
|
||||
indicator2.setLayout(indicator2Layout);
|
||||
indicator2Layout.setHorizontalGroup(
|
||||
indicator2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
indicator2Layout.setVerticalGroup(
|
||||
indicator2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jPanel2.add(indicator2, new java.awt.GridBagConstraints());
|
||||
|
||||
indicator3.setToolTipText("engine problem");
|
||||
indicator3.setFrame3dEffectVisible(true);
|
||||
indicator3.setSymbolType(eu.hansolo.steelseries.tools.SymbolType.ENGINE);
|
||||
|
||||
javax.swing.GroupLayout indicator3Layout = new javax.swing.GroupLayout(indicator3);
|
||||
indicator3.setLayout(indicator3Layout);
|
||||
indicator3Layout.setHorizontalGroup(
|
||||
indicator3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
indicator3Layout.setVerticalGroup(
|
||||
indicator3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 50, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 0;
|
||||
gridBagConstraints.gridy = 1;
|
||||
jPanel2.add(indicator3, gridBagConstraints);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 5;
|
||||
gridBagConstraints.gridy = 0;
|
||||
add(jPanel2, gridBagConstraints);
|
||||
|
||||
batteryGraph.setToolTipText("battery status");
|
||||
batteryGraph.setFrame3dEffectVisible(true);
|
||||
batteryGraph.setThreshold(110.0);
|
||||
batteryGraph.setTitle("Battery");
|
||||
batteryGraph.setTrackStartColor(new java.awt.Color(255, 0, 0));
|
||||
batteryGraph.setTrackStopColor(new java.awt.Color(0, 255, 0));
|
||||
batteryGraph.setTrackVisible(true);
|
||||
batteryGraph.setUnitString("%");
|
||||
|
||||
javax.swing.GroupLayout batteryGraphLayout = new javax.swing.GroupLayout(batteryGraph);
|
||||
batteryGraph.setLayout(batteryGraphLayout);
|
||||
batteryGraphLayout.setHorizontalGroup(
|
||||
batteryGraphLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 140, Short.MAX_VALUE)
|
||||
);
|
||||
batteryGraphLayout.setVerticalGroup(
|
||||
batteryGraphLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 140, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
gridBagConstraints = new java.awt.GridBagConstraints();
|
||||
gridBagConstraints.gridx = 1;
|
||||
gridBagConstraints.gridy = 0;
|
||||
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
|
||||
gridBagConstraints.ipadx = -60;
|
||||
gridBagConstraints.ipady = -60;
|
||||
add(batteryGraph, gridBagConstraints);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void resetDrone(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_resetDrone
|
||||
ARDrone drone=this.drone.get();
|
||||
if(drone!=null){
|
||||
try {
|
||||
drone.clearEmergencySignal();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(BottomGaugePanel.class.getName()).error("", ex);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_resetDrone
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private eu.hansolo.steelseries.extras.AirCompass airCompass1;
|
||||
private eu.hansolo.steelseries.extras.Altimeter altimeter1;
|
||||
private eu.hansolo.steelseries.gauges.Radial1Vertical batteryGraph;
|
||||
private eu.hansolo.steelseries.extras.Horizon horizon1;
|
||||
private eu.hansolo.steelseries.extras.Indicator indicator1;
|
||||
private eu.hansolo.steelseries.extras.Indicator indicator2;
|
||||
private eu.hansolo.steelseries.extras.Indicator indicator3;
|
||||
private eu.hansolo.steelseries.extras.Indicator indicator4;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JPanel jPanel2;
|
||||
private eu.hansolo.steelseries.extras.Led led1;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
<?xml version="1.1" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" value="Controller Mapping"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jLabel3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" pref="85" max="32767" attributes="1"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="valueList" alignment="0" pref="231" max="32767" attributes="0"/>
|
||||
<Component id="commandList" alignment="0" pref="231" max="32767" attributes="0"/>
|
||||
<Component id="jSeparator2" alignment="0" pref="231" max="32767" attributes="0"/>
|
||||
<Component id="jToolBar1" alignment="1" pref="231" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jLabel6" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<Component id="delaySpinner" pref="133" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="durationSpinner" alignment="0" pref="133" max="32767" attributes="0"/>
|
||||
<Component id="frequencySpinner" alignment="0" pref="133" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Component id="commandList" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="valueList" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="jLabel6" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="delaySpinner" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="durationSpinner" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="1" attributes="0">
|
||||
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="frequencySpinner" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jToolBar1" min="-2" pref="25" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="jScrollPane1" pref="227" max="32767" attributes="1"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JList" name="buttonList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
|
||||
<StringArray count="5">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
<StringItem index="1" value="Item 2"/>
|
||||
<StringItem index="2" value="Item 3"/>
|
||||
<StringItem index="3" value="Item 4"/>
|
||||
<StringItem index="4" value="Item 5"/>
|
||||
</StringArray>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="valueChanged" listener="javax.swing.event.ListSelectionListener" parameters="javax.swing.event.ListSelectionEvent" handler="listSelection"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JComboBox" name="commandList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="4">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
<StringItem index="1" value="Item 2"/>
|
||||
<StringItem index="2" value="Item 3"/>
|
||||
<StringItem index="3" value="Item 4"/>
|
||||
</StringArray>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="updateCommandSelection"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="valueList">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="4">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
<StringItem index="1" value="Item 2"/>
|
||||
<StringItem index="2" value="Item 3"/>
|
||||
<StringItem index="3" value="Item 4"/>
|
||||
</StringArray>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSpinner" name="durationSpinner">
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="storeCommandz"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Duration (ms)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel2">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Frequency"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSpinner" name="frequencySpinner">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
|
||||
<SpinnerModel initial="0.0" numberType="java.lang.Float" stepSize="0.1" type="number"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="storeCommandz"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel3">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Button"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JToolBar" name="jToolBar1">
|
||||
<Properties>
|
||||
<Property name="floatable" type="boolean" value="false"/>
|
||||
<Property name="rollover" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JButton" name="jButton4">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="prev"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton4ActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="46" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="21" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="jLabel4">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="1"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="jPanel2">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="45" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="21" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="jButton3">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="next"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton3ActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator3">
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="storeButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="store"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="storeButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JLabel" name="jLabel5">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Commands"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator2">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="jLabel6">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Delay (ms)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSpinner" name="delaySpinner">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
|
||||
<SpinnerModel initial="0" numberType="java.lang.Integer" stepSize="100" type="number"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="storeCommandz"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* ControlConfig.java
|
||||
*
|
||||
* Created on 25.05.2011, 18:06:24
|
||||
*/
|
||||
package com.codeminders.controltower;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone.Animation;
|
||||
import com.codeminders.ardrone.ARDrone.LED;
|
||||
import com.codeminders.controltower.config.AssignableControl;
|
||||
import com.codeminders.controltower.config.AssignableControl.Command;
|
||||
import com.codeminders.controltower.config.AssignableControl.ControllerButton;
|
||||
import com.codeminders.controltower.config.ControlMap;
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.swing.JFileChooser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ControlConfig extends javax.swing.JDialog {
|
||||
|
||||
private ControlMap map;
|
||||
private List<AssignableControl> list;
|
||||
private int index = 0;
|
||||
|
||||
/** Creates new form ControlConfig */
|
||||
public ControlConfig(java.awt.Frame parent, boolean modal, ControlMap map) {
|
||||
super(parent, modal);
|
||||
this.map = map;
|
||||
initComponents();
|
||||
initButtonList();
|
||||
initCommandList();
|
||||
buttonList.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the list of available buttons
|
||||
*/
|
||||
private void initButtonList() {
|
||||
ControllerButton[] buttons = ControllerButton.values();
|
||||
String[] str = new String[buttons.length];
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
ControllerButton button = buttons[i];
|
||||
str[i] = button.name();
|
||||
}
|
||||
buttonList.setListData(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the dropdown for available commands
|
||||
*/
|
||||
private void initCommandList() {
|
||||
commandList.removeAllItems();
|
||||
Command[] buttons = Command.values();
|
||||
commandList.addItem("None");
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
Command button = buttons[i];
|
||||
commandList.addItem(button.name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to animation mode (enable duration)
|
||||
*/
|
||||
private void setAnimMode() {
|
||||
valueList.removeAllItems();
|
||||
Animation[] buttons = Animation.values();
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
Animation button = buttons[i];
|
||||
valueList.addItem(button.name());
|
||||
}
|
||||
valueList.setEnabled(true);
|
||||
durationSpinner.setEnabled(true);
|
||||
frequencySpinner.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to LED mode (enable duration and frequency)
|
||||
*/
|
||||
private void setLEDMode() {
|
||||
valueList.removeAllItems();
|
||||
LED[] buttons = LED.values();
|
||||
for (int i = 0; i < buttons.length; i++) {
|
||||
LED button = buttons[i];
|
||||
valueList.addItem(button.name());
|
||||
}
|
||||
valueList.setEnabled(true);
|
||||
durationSpinner.setEnabled(true);
|
||||
frequencySpinner.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to Default mode (disable duration and frequency)
|
||||
*/
|
||||
private void setDefaultMode() {
|
||||
valueList.removeAllItems();
|
||||
valueList.setEnabled(false);
|
||||
durationSpinner.setEnabled(false);
|
||||
frequencySpinner.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the mode based on the command selection
|
||||
*/
|
||||
private void switchMode() {
|
||||
if (commandList.getSelectedItem() == null || "None".equals((String) commandList.getSelectedItem())) {
|
||||
setDefaultMode();
|
||||
return;
|
||||
}
|
||||
String str = (String) commandList.getSelectedItem();
|
||||
if (str == null) {
|
||||
return;
|
||||
}
|
||||
Command command = Command.valueOf(str);
|
||||
if (command == Command.PLAY_ANIMATION) {
|
||||
setAnimMode();
|
||||
} else if (command == Command.PLAY_LED) {
|
||||
setLEDMode();
|
||||
} else {
|
||||
setDefaultMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI elements based on the selected button / command index
|
||||
*/
|
||||
private void updateButtonSelection() {
|
||||
String controlbutton = (String) buttonList.getSelectedValue();
|
||||
if (controlbutton == null) {
|
||||
resetView();
|
||||
return;
|
||||
}
|
||||
list = map.getControls(ControllerButton.valueOf(controlbutton));
|
||||
jLabel4.setText((index + 1) + "");
|
||||
if (list == null) {
|
||||
list = new LinkedList<AssignableControl>();
|
||||
commandList.setSelectedIndex(0);
|
||||
setDefaultMode();
|
||||
return;
|
||||
}
|
||||
if (list.size() <= index) {
|
||||
commandList.setSelectedIndex(0);
|
||||
setDefaultMode();
|
||||
return;
|
||||
}
|
||||
AssignableControl control = list.get(index);
|
||||
commandList.setSelectedItem(control.getCommand().name());
|
||||
if (control.getCommand() == Command.PLAY_ANIMATION) {
|
||||
setAnimMode();
|
||||
valueList.setSelectedItem(control.getAnim().name());
|
||||
} else if (control.getCommand() == Command.PLAY_LED) {
|
||||
setLEDMode();
|
||||
valueList.setSelectedItem(control.getLed().name());
|
||||
} else {
|
||||
setDefaultMode();
|
||||
}
|
||||
durationSpinner.setValue(control.getDuration());
|
||||
frequencySpinner.setValue(control.getFrequency());
|
||||
delaySpinner.setValue(control.getDelay());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current settings configured in the UI in the command list
|
||||
* for the selected button
|
||||
*/
|
||||
private void storeButtonCommands() {
|
||||
AssignableControl control = null;
|
||||
String controlString = (String) buttonList.getSelectedValue();
|
||||
String commandString = (String) commandList.getSelectedItem();
|
||||
if (controlString == null) {
|
||||
return;
|
||||
}
|
||||
ControllerButton button = ControllerButton.valueOf(controlString);
|
||||
if (button == null) {
|
||||
return;
|
||||
}
|
||||
if ("None".equals(commandString)) {
|
||||
if (list.size() > index) {
|
||||
list.remove(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Command command = Command.valueOf((String) commandList.getSelectedItem());
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
switch(command){
|
||||
case PLAY_ANIMATION:
|
||||
Animation anim = Animation.valueOf((String) valueList.getSelectedItem());
|
||||
control = new AssignableControl(button, anim, (Integer) delaySpinner.getValue(), (Integer) durationSpinner.getValue());
|
||||
break;
|
||||
case PLAY_LED:
|
||||
LED led = LED.valueOf((String) valueList.getSelectedItem());
|
||||
float freq = frequencySpinner.getValue() instanceof Float ? (Float) frequencySpinner.getValue() : (Integer) frequencySpinner.getValue();
|
||||
control = new AssignableControl(button, led, (Integer) delaySpinner.getValue(), freq, (Integer) durationSpinner.getValue());
|
||||
break;
|
||||
case TAKE_SNAPSHOT:
|
||||
case RECORD_VIDEO:
|
||||
control = new AssignableControl(button, command, (Integer) delaySpinner.getValue(), getFile());
|
||||
break;
|
||||
default:
|
||||
control = new AssignableControl(button, command, (Integer) delaySpinner.getValue());
|
||||
break;
|
||||
}
|
||||
list.add(index, control);
|
||||
if (list.size() > index + 1) {
|
||||
list.remove(index + 1);
|
||||
}
|
||||
map.setControls(button, list);
|
||||
}
|
||||
|
||||
private File getFile() {
|
||||
final JFileChooser fc = new JFileChooser();
|
||||
fc.setDialogTitle("Select folder to store data");
|
||||
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
int returnVal = fc.showOpenDialog(this);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION) {
|
||||
return fc.getSelectedFile();
|
||||
//This is where a real application would open the file.
|
||||
} else {
|
||||
return new File("." + File.separator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the UI display
|
||||
*/
|
||||
private void resetView() {
|
||||
setDefaultMode();
|
||||
index = 0;
|
||||
jLabel4.setText("1");
|
||||
commandList.setSelectedIndex(0);
|
||||
durationSpinner.setValue(0);
|
||||
frequencySpinner.setValue(0);
|
||||
delaySpinner.setValue(0);
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
buttonList = new javax.swing.JList();
|
||||
commandList = new javax.swing.JComboBox();
|
||||
valueList = new javax.swing.JComboBox();
|
||||
durationSpinner = new javax.swing.JSpinner();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
frequencySpinner = new javax.swing.JSpinner();
|
||||
jLabel3 = new javax.swing.JLabel();
|
||||
jToolBar1 = new javax.swing.JToolBar();
|
||||
jButton4 = new javax.swing.JButton();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jLabel4 = new javax.swing.JLabel();
|
||||
jPanel2 = new javax.swing.JPanel();
|
||||
jButton3 = new javax.swing.JButton();
|
||||
jSeparator3 = new javax.swing.JToolBar.Separator();
|
||||
storeButton = new javax.swing.JButton();
|
||||
jLabel5 = new javax.swing.JLabel();
|
||||
jSeparator2 = new javax.swing.JSeparator();
|
||||
jLabel6 = new javax.swing.JLabel();
|
||||
delaySpinner = new javax.swing.JSpinner();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("Controller Mapping");
|
||||
|
||||
buttonList.setModel(new javax.swing.AbstractListModel() {
|
||||
String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
|
||||
public int getSize() { return strings.length; }
|
||||
public Object getElementAt(int i) { return strings[i]; }
|
||||
});
|
||||
buttonList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
|
||||
public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
|
||||
listSelection(evt);
|
||||
}
|
||||
});
|
||||
jScrollPane1.setViewportView(buttonList);
|
||||
|
||||
commandList.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
commandList.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
updateCommandSelection(evt);
|
||||
}
|
||||
});
|
||||
|
||||
valueList.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
|
||||
durationSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
storeCommandz(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jLabel1.setText("Duration (ms)");
|
||||
|
||||
jLabel2.setText("Frequency");
|
||||
|
||||
frequencySpinner.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.0f), null, null, Float.valueOf(0.1f)));
|
||||
frequencySpinner.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
storeCommandz(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jLabel3.setText("Button");
|
||||
|
||||
jToolBar1.setFloatable(false);
|
||||
jToolBar1.setRollover(true);
|
||||
|
||||
jButton4.setText("prev");
|
||||
jButton4.setFocusable(false);
|
||||
jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
jButton4.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButton4ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(jButton4);
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 46, Short.MAX_VALUE)
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 21, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jToolBar1.add(jPanel1);
|
||||
|
||||
jLabel4.setText("1");
|
||||
jToolBar1.add(jLabel4);
|
||||
|
||||
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
|
||||
jPanel2.setLayout(jPanel2Layout);
|
||||
jPanel2Layout.setHorizontalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 45, Short.MAX_VALUE)
|
||||
);
|
||||
jPanel2Layout.setVerticalGroup(
|
||||
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 21, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jToolBar1.add(jPanel2);
|
||||
|
||||
jButton3.setText("next");
|
||||
jButton3.setFocusable(false);
|
||||
jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
jButton3.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButton3ActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(jButton3);
|
||||
jToolBar1.add(jSeparator3);
|
||||
|
||||
storeButton.setText("store");
|
||||
storeButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
storeButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(storeButton);
|
||||
|
||||
jLabel5.setText("Commands");
|
||||
|
||||
jLabel6.setText("Delay (ms)");
|
||||
|
||||
delaySpinner.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), null, null, Integer.valueOf(100)));
|
||||
delaySpinner.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
storeCommandz(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel3)
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jLabel5, javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(valueList, javax.swing.GroupLayout.Alignment.LEADING, 0, 231, Short.MAX_VALUE)
|
||||
.addComponent(commandList, javax.swing.GroupLayout.Alignment.LEADING, 0, 231, Short.MAX_VALUE)
|
||||
.addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 231, Short.MAX_VALUE)
|
||||
.addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 231, Short.MAX_VALUE)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
|
||||
.addComponent(jLabel6)
|
||||
.addGap(32, 32, 32)
|
||||
.addComponent(delaySpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(jLabel2))
|
||||
.addGap(12, 12, 12)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(durationSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE)
|
||||
.addComponent(frequencySpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 133, Short.MAX_VALUE))))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel5)
|
||||
.addComponent(jLabel3))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(commandList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(valueList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jLabel6)
|
||||
.addComponent(delaySpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(durationSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(frequencySpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 227, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void listSelection(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_listSelection
|
||||
index = 0;
|
||||
updateButtonSelection();
|
||||
}//GEN-LAST:event_listSelection
|
||||
|
||||
private void updateCommandSelection(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_updateCommandSelection
|
||||
switchMode();
|
||||
}//GEN-LAST:event_updateCommandSelection
|
||||
|
||||
private void storeCommandz(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_storeCommandz
|
||||
// storeButtonCommands();
|
||||
}//GEN-LAST:event_storeCommandz
|
||||
|
||||
private void storeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_storeButtonActionPerformed
|
||||
storeButtonCommands();
|
||||
}//GEN-LAST:event_storeButtonActionPerformed
|
||||
|
||||
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed
|
||||
if (index > 0) {
|
||||
index--;
|
||||
updateButtonSelection();
|
||||
}
|
||||
}//GEN-LAST:event_jButton4ActionPerformed
|
||||
|
||||
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
|
||||
if (index < list.size()) {
|
||||
index++;
|
||||
updateButtonSelection();
|
||||
}
|
||||
}//GEN-LAST:event_jButton3ActionPerformed
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JList buttonList;
|
||||
private javax.swing.JComboBox commandList;
|
||||
private javax.swing.JSpinner delaySpinner;
|
||||
private javax.swing.JSpinner durationSpinner;
|
||||
private javax.swing.JSpinner frequencySpinner;
|
||||
private javax.swing.JButton jButton3;
|
||||
private javax.swing.JButton jButton4;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JLabel jLabel3;
|
||||
private javax.swing.JLabel jLabel4;
|
||||
private javax.swing.JLabel jLabel5;
|
||||
private javax.swing.JLabel jLabel6;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JPanel jPanel2;
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JSeparator jSeparator2;
|
||||
private javax.swing.JToolBar.Separator jSeparator3;
|
||||
private javax.swing.JToolBar jToolBar1;
|
||||
private javax.swing.JButton storeButton;
|
||||
private javax.swing.JComboBox valueList;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="3"/>
|
||||
<Property name="title" type="java.lang.String" value="Control Tower"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jToolBar1" alignment="0" pref="800" max="32767" attributes="1"/>
|
||||
<Component id="videoPanel" alignment="0" pref="800" max="32767" attributes="1"/>
|
||||
<Component id="jPanel2" alignment="0" pref="800" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="videoPanel" pref="508" max="32767" attributes="1"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jPanel2" min="-2" pref="209" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jToolBar1" min="-2" pref="25" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JToolBar" name="jToolBar1">
|
||||
<Properties>
|
||||
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
|
||||
<Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
|
||||
<BevelBorder/>
|
||||
</Border>
|
||||
</Property>
|
||||
<Property name="floatable" type="boolean" value="false"/>
|
||||
<Property name="rollover" type="boolean" value="true"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator5">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="droneStatus">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
|
||||
</Property>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/com/codeminders/controltower/images/drone_off.gif"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="drone status (lit = connected)"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="droneStatusMouseReleased"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="batteryStatus">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Lucida Grande" size="10" style="1"/>
|
||||
</Property>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="0%"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator1">
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="joystickControllerStatus">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
|
||||
</Property>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/com/codeminders/controltower/images/controller_off.png"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="controller status (green = available)"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="joystickControllerStatusMouseReleased"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="keyboardControllerStatus">
|
||||
<Properties>
|
||||
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
|
||||
</Property>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/com/codeminders/controltower/images/keyboard_off.png"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="keyboard status (green = available)"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="keyboardControllerStatusMouseReleased"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="flipSticksCheckbox">
|
||||
<Properties>
|
||||
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
|
||||
<Font name="Lucida Grande" size="10" style="0"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="flip sticks"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="flipSticksCheckboxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="mappingButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="mapping"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="map controller buttons"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="0"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mappingButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator3">
|
||||
</Component>
|
||||
<Container class="javax.swing.JPanel" name="jPanel1">
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="384" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<EmptySpace min="0" pref="21" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
</Container>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator7">
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="instrumentButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/com/codeminders/controltower/images/instruments.gif"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="instruments"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="toggle instruments"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="instrumentButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="configureButton">
|
||||
<Properties>
|
||||
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
|
||||
<Image iconType="3" name="/com/codeminders/controltower/images/objects_039.gif"/>
|
||||
</Property>
|
||||
<Property name="text" type="java.lang.String" value="tuning"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="show drone tuning settings"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
<Property name="horizontalTextPosition" type="int" value="4"/>
|
||||
<Property name="verticalTextPosition" type="int" value="3"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="configureButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JToolBar$Separator" name="jSeparator6">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="videoPanel">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
||||
<Color blue="66" green="66" red="66" type="rgb"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[320, 240]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="jPanel2">
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,757 @@
|
||||
/*
|
||||
* ControlTower.java
|
||||
*
|
||||
* Created on 17.05.2011, 13:41:27
|
||||
*/
|
||||
package com.codeminders.controltower;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import com.codeminders.ardrone.ARDrone.VideoChannel;
|
||||
import com.codeminders.ardrone.DroneStatusChangeListener;
|
||||
import com.codeminders.ardrone.NavData;
|
||||
import com.codeminders.ardrone.NavDataListener;
|
||||
import com.codeminders.ardrone.controllers.AfterGlowController;
|
||||
import com.codeminders.ardrone.controllers.KeyboardController;
|
||||
import com.codeminders.ardrone.controllers.PS3Controller;
|
||||
import com.codeminders.ardrone.controllers.PS3ControllerState;
|
||||
import com.codeminders.ardrone.controllers.PS3ControllerStateChange;
|
||||
import com.codeminders.ardrone.controllers.SonyPS3Controller;
|
||||
import com.codeminders.controltower.config.AssignableControl.ControllerButton;
|
||||
import com.codeminders.controltower.config.ControlMap;
|
||||
import com.codeminders.hidapi.ClassPathLibraryLoader;
|
||||
import com.codeminders.hidapi.HIDDeviceInfo;
|
||||
import com.codeminders.hidapi.HIDDeviceNotFoundException;
|
||||
import com.codeminders.hidapi.HIDManager;
|
||||
|
||||
/**
|
||||
* The central class that represents the main window and also manages the
|
||||
* drone update loop.
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ControlTower extends javax.swing.JFrame implements DroneStatusChangeListener, NavDataListener
|
||||
{
|
||||
|
||||
private static final long READ_UPDATE_DELAY_MS = 5L;
|
||||
private static final long CONNECT_TIMEOUT = 8000L;
|
||||
private static float CONTROL_THRESHOLD = 0.5f;
|
||||
private final ImageIcon droneOn = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/drone_on.gif"));
|
||||
private final ImageIcon droneOff = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/drone_off.gif"));
|
||||
private final ImageIcon controllerOn = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/controller_on.png"));
|
||||
private final ImageIcon keyboradControllerOn = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/keyboard_on.png"));
|
||||
private final ImageIcon controllerOff = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/controller_off.png"));
|
||||
private final ImageIcon keyboradControllerOff = new ImageIcon(
|
||||
getClass().getResource("/com/codeminders/controltower/images/keyboard_off.png"));
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final AtomicBoolean flying = new AtomicBoolean(false);
|
||||
private final AtomicBoolean flipSticks = new AtomicBoolean(false);
|
||||
private final Preferences prefs = Preferences.userNodeForPackage(this.getClass());
|
||||
private ARDrone drone;
|
||||
private final AtomicReference<PS3Controller> dev = new AtomicReference<PS3Controller>();
|
||||
private final VideoPanel video = new VideoPanel();
|
||||
private final DroneConfig droneConfigWindow;
|
||||
private final ControlConfig controlConfigWindow;
|
||||
private final KeyboardControlConfig keyboardControlConfigWindow;
|
||||
private final BottomGaugePanel gauges = new BottomGaugePanel();
|
||||
private final ControlMap controlMap = new ControlMap();
|
||||
|
||||
private static boolean isHIDLibLoaded = false;
|
||||
|
||||
static
|
||||
{
|
||||
isHIDLibLoaded = ClassPathLibraryLoader.loadNativeHIDLibrary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new form ControlTower
|
||||
*/
|
||||
public ControlTower()
|
||||
{
|
||||
setAlwaysOnTop(true);
|
||||
initComponents();
|
||||
droneConfigWindow = new DroneConfig(this, true);
|
||||
keyboardControlConfigWindow = new KeyboardControlConfig(this, true);
|
||||
controlConfigWindow = new ControlConfig(this, true, controlMap);
|
||||
videoPanel.add(video);
|
||||
jPanel2.add(gauges);
|
||||
initController();
|
||||
initDrone();
|
||||
flipSticks.set(prefs.getBoolean("FLIP_STICKS", false));
|
||||
flipSticksCheckbox.setSelected(flipSticks.get());
|
||||
}
|
||||
|
||||
private void initDrone()
|
||||
{
|
||||
try
|
||||
{
|
||||
drone = new ARDrone();
|
||||
}
|
||||
catch(UnknownHostException ex)
|
||||
{
|
||||
Logger.getLogger(ControlTower.class.getName()).error("Error creating drone object!", ex);
|
||||
return;
|
||||
}
|
||||
droneConfigWindow.setDrone(drone);
|
||||
gauges.setDrone(drone);
|
||||
video.setDrone(drone);
|
||||
drone.addStatusChangeListener(this);
|
||||
drone.addNavDataListener(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to find PS3 controller, else creates keyboard controller
|
||||
*/
|
||||
private void initController()
|
||||
{
|
||||
PS3Controller current = dev.get();
|
||||
if(current != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
current.close();
|
||||
}
|
||||
catch(IOException ex)
|
||||
{
|
||||
Logger.getLogger(ControlTower.class.getName()).error("", ex);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
dev.set(findController());
|
||||
}
|
||||
catch(IOException ex)
|
||||
{
|
||||
Logger.getLogger(ControlTower.class.getName()).error("{0}", ex);
|
||||
}
|
||||
if(dev.get() == null)
|
||||
{
|
||||
System.err.println("No suitable controller found! Using keyboard");
|
||||
dev.set(new KeyboardController(this));
|
||||
updateControllerStatus(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.err.println("Gamepad controller found");
|
||||
updateControllerStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static PS3Controller findController() throws IOException
|
||||
{
|
||||
if(!isHIDLibLoaded)
|
||||
return null;
|
||||
|
||||
HIDDeviceInfo[] devs = HIDManager.getInstance().listDevices();
|
||||
if (null != devs) {
|
||||
for(int i = 0; i < devs.length; i++)
|
||||
{
|
||||
System.out.println("" + devs[i]);
|
||||
if(AfterGlowController.isA(devs[i]))
|
||||
{
|
||||
return new AfterGlowController(devs[i]);
|
||||
}
|
||||
if(SonyPS3Controller.isA(devs[i]))
|
||||
{
|
||||
return new SonyPS3Controller(devs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateLoop()
|
||||
{
|
||||
if(running.get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
running.set(true);
|
||||
resetStatus();
|
||||
try
|
||||
{
|
||||
|
||||
drone.addStatusChangeListener(new DroneStatusChangeListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void ready()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.getLogger(getClass().getName()).debug("updateLoop::ready()");
|
||||
System.err.println("Configure");
|
||||
droneConfigWindow.updateDrone();
|
||||
drone.selectVideoChannel(VideoChannel.HORIZONTAL_ONLY);
|
||||
drone.setCombinedYawMode(true);
|
||||
drone.trim();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
drone.changeToErrorState(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.err.println("Connecting to the drone");
|
||||
drone.connect();
|
||||
drone.waitForReady(CONNECT_TIMEOUT);
|
||||
drone.clearEmergencySignal();
|
||||
System.err.println("Connected to the drone");
|
||||
try
|
||||
{
|
||||
PS3ControllerState oldpad = null;
|
||||
while(running.get())
|
||||
{
|
||||
PS3ControllerState pad = dev.get().read();
|
||||
PS3ControllerStateChange pad_change = new PS3ControllerStateChange(oldpad, pad);
|
||||
oldpad = pad;
|
||||
|
||||
if(pad_change.isStartChanged() && pad_change.isStart())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.START);
|
||||
}
|
||||
if(pad_change.isSelectChanged() && pad_change.isSelect())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.SELECT);
|
||||
}
|
||||
if(pad_change.isPSChanged() && pad_change.isPS())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.PS);
|
||||
}
|
||||
if(pad_change.isTriangleChanged() && pad_change.isTriangle())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.TRIANGLE);
|
||||
}
|
||||
if(pad_change.isCrossChanged() && pad_change.isCross())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.CROSS);
|
||||
}
|
||||
if(pad_change.isSquareChanged() && pad_change.isSquare())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.SQUARE);
|
||||
}
|
||||
if(pad_change.isCircleChanged() && pad_change.isCircle())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.CIRCLE);
|
||||
}
|
||||
if(pad_change.isL1Changed() && pad_change.isL1())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.L1);
|
||||
}
|
||||
if(pad_change.isR1Changed() && pad_change.isR1())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.R1);
|
||||
}
|
||||
if(pad_change.isL2Changed() && pad_change.isL2())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.L2);
|
||||
}
|
||||
if(pad_change.isR2Changed() && pad_change.isR2())
|
||||
{
|
||||
controlMap.sendCommand(drone, ControllerButton.R2);
|
||||
}
|
||||
if(flying.get())
|
||||
{
|
||||
// Detecting if we need to move the drone
|
||||
|
||||
int leftX = pad.getLeftJoystickX();
|
||||
int leftY = pad.getLeftJoystickY();
|
||||
|
||||
int rightX = pad.getRightJoystickX();
|
||||
int rightY = pad.getRightJoystickY();
|
||||
|
||||
float left_right_tilt = 0f;
|
||||
float front_back_tilt = 0f;
|
||||
float vertical_speed = 0f;
|
||||
float angular_speed = 0f;
|
||||
|
||||
if(Math.abs(((float) leftX) / 128f) > CONTROL_THRESHOLD)
|
||||
{
|
||||
left_right_tilt = ((float) leftX) / 128f;
|
||||
}
|
||||
|
||||
if(Math.abs(((float) leftY) / 128f) > CONTROL_THRESHOLD)
|
||||
{
|
||||
front_back_tilt = ((float) leftY) / 128f;
|
||||
}
|
||||
|
||||
if(Math.abs(((float) rightX) / 128f) > CONTROL_THRESHOLD)
|
||||
{
|
||||
angular_speed = ((float) rightX) / 128f;
|
||||
}
|
||||
|
||||
if(Math.abs(-1 * ((float) rightY) / 128f) > CONTROL_THRESHOLD)
|
||||
{
|
||||
vertical_speed = -1 * ((float) rightY) / 128f;
|
||||
}
|
||||
|
||||
if(left_right_tilt != 0 || front_back_tilt != 0 || vertical_speed != 0 || angular_speed != 0)
|
||||
{
|
||||
if(flipSticks.get())
|
||||
{
|
||||
drone.move(angular_speed, -1 * vertical_speed, -1 * front_back_tilt, left_right_tilt);
|
||||
}
|
||||
else
|
||||
{
|
||||
drone.move(left_right_tilt, front_back_tilt, vertical_speed, angular_speed);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
drone.hover();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(READ_UPDATE_DELAY_MS);
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
drone.disconnect();
|
||||
}
|
||||
}
|
||||
catch(HIDDeviceNotFoundException hex)
|
||||
{
|
||||
hex.printStackTrace();
|
||||
}
|
||||
catch(Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
resetStatus();
|
||||
running.set(false);
|
||||
}
|
||||
|
||||
private void startUpdateLoop()
|
||||
{
|
||||
Thread thread = new Thread(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
updateLoop();
|
||||
}
|
||||
});
|
||||
thread.setName("ARDrone Control Loop");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the drone status in the UI, queues command to AWT event dispatch thread
|
||||
*
|
||||
* @param available
|
||||
*/
|
||||
private void updateDroneStatus(final boolean available)
|
||||
{
|
||||
java.awt.EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(!available)
|
||||
{
|
||||
droneStatus.setForeground(Color.RED);
|
||||
droneStatus.setIcon(droneOff);
|
||||
}
|
||||
else
|
||||
{
|
||||
droneStatus.setForeground(Color.GREEN);
|
||||
droneStatus.setIcon(droneOn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the controller status in the UI, queues command to AWT event dispatch thread
|
||||
*
|
||||
* @param available
|
||||
*/
|
||||
private void updateControllerStatus(final boolean available)
|
||||
{
|
||||
java.awt.EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(!available)
|
||||
{
|
||||
joystickControllerStatus.setForeground(Color.RED);
|
||||
joystickControllerStatus.setIcon(controllerOff);
|
||||
keyboardControllerStatus.setForeground(Color.GREEN);
|
||||
keyboardControllerStatus.setIcon(keyboradControllerOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
joystickControllerStatus.setForeground(Color.GREEN);
|
||||
joystickControllerStatus.setIcon(controllerOn);
|
||||
keyboardControllerStatus.setForeground(Color.RED);
|
||||
keyboardControllerStatus.setIcon(keyboradControllerOff);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the battery status in the UI, queues command to AWT event dispatch thread
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
private void updateBatteryStatus(final int value)
|
||||
{
|
||||
java.awt.EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
batteryStatus.setText(value + "%");
|
||||
if(value < 15)
|
||||
{
|
||||
batteryStatus.setForeground(Color.RED);
|
||||
}
|
||||
else if(value < 50)
|
||||
{
|
||||
batteryStatus.setForeground(Color.ORANGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
batteryStatus.setForeground(Color.GREEN);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the UI, queues command to AWT event dispatch thread
|
||||
*
|
||||
*/
|
||||
private void resetStatus()
|
||||
{
|
||||
java.awt.EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
droneStatus.setForeground(Color.RED);
|
||||
droneStatus.setIcon(droneOff);
|
||||
batteryStatus.setForeground(Color.RED);
|
||||
batteryStatus.setText("0%");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ready()
|
||||
{
|
||||
Logger.getLogger(getClass().getName()).debug("ready()");
|
||||
updateDroneStatus(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navDataReceived(NavData nd)
|
||||
{
|
||||
Logger.getLogger(getClass().getName()).debug("navDataReceived()");
|
||||
updateBatteryStatus(nd.getBattery());
|
||||
this.flying.set(nd.isFlying());
|
||||
}
|
||||
|
||||
public void setControlThreshold(float sens)
|
||||
{
|
||||
CONTROL_THRESHOLD = sens;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
jToolBar1 = new javax.swing.JToolBar();
|
||||
jSeparator5 = new javax.swing.JToolBar.Separator();
|
||||
droneStatus = new javax.swing.JLabel();
|
||||
batteryStatus = new javax.swing.JLabel();
|
||||
jSeparator1 = new javax.swing.JToolBar.Separator();
|
||||
joystickControllerStatus = new javax.swing.JLabel();
|
||||
keyboardControllerStatus = new javax.swing.JLabel();
|
||||
flipSticksCheckbox = new javax.swing.JCheckBox();
|
||||
mappingButton = new javax.swing.JButton();
|
||||
jSeparator3 = new javax.swing.JToolBar.Separator();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jSeparator7 = new javax.swing.JToolBar.Separator();
|
||||
instrumentButton = new javax.swing.JButton();
|
||||
configureButton = new javax.swing.JButton();
|
||||
jSeparator6 = new javax.swing.JToolBar.Separator();
|
||||
videoPanel = new javax.swing.JPanel();
|
||||
jPanel2 = new javax.swing.JPanel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||
setTitle("Control Tower");
|
||||
|
||||
jToolBar1.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
|
||||
jToolBar1.setFloatable(false);
|
||||
jToolBar1.setRollover(true);
|
||||
jToolBar1.add(jSeparator5);
|
||||
|
||||
droneStatus.setForeground(java.awt.Color.red);
|
||||
droneStatus.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/codeminders/controltower/images/drone_off.gif"))); // NOI18N
|
||||
droneStatus.setToolTipText("drone status (lit = connected)");
|
||||
droneStatus.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseReleased(java.awt.event.MouseEvent evt) {
|
||||
droneStatusMouseReleased(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(droneStatus);
|
||||
|
||||
batteryStatus.setFont(new java.awt.Font("Lucida Grande", 1, 10)); // NOI18N
|
||||
batteryStatus.setForeground(java.awt.Color.red);
|
||||
batteryStatus.setText("0%");
|
||||
jToolBar1.add(batteryStatus);
|
||||
jToolBar1.add(jSeparator1);
|
||||
|
||||
joystickControllerStatus.setForeground(java.awt.Color.red);
|
||||
joystickControllerStatus.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/codeminders/controltower/images/controller_off.png"))); // NOI18N
|
||||
joystickControllerStatus.setToolTipText("controller status (green = available)");
|
||||
joystickControllerStatus.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseReleased(java.awt.event.MouseEvent evt) {
|
||||
joystickControllerStatusMouseReleased(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(joystickControllerStatus);
|
||||
|
||||
keyboardControllerStatus.setForeground(java.awt.Color.red);
|
||||
keyboardControllerStatus.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/codeminders/controltower/images/keyboard_off.png"))); // NOI18N
|
||||
keyboardControllerStatus.setToolTipText("keyboard status (green = available)");
|
||||
keyboardControllerStatus.addMouseListener(new java.awt.event.MouseAdapter() {
|
||||
public void mouseReleased(java.awt.event.MouseEvent evt) {
|
||||
keyboardControllerStatusMouseReleased(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(keyboardControllerStatus);
|
||||
|
||||
flipSticksCheckbox.setFont(new java.awt.Font("Lucida Grande", 0, 10)); // NOI18N
|
||||
flipSticksCheckbox.setText("flip sticks");
|
||||
flipSticksCheckbox.setFocusable(false);
|
||||
flipSticksCheckbox.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
flipSticksCheckbox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
flipSticksCheckboxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(flipSticksCheckbox);
|
||||
|
||||
mappingButton.setText("mapping");
|
||||
mappingButton.setToolTipText("map controller buttons");
|
||||
mappingButton.setFocusable(false);
|
||||
mappingButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
|
||||
mappingButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
mappingButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
mappingButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(mappingButton);
|
||||
jToolBar1.add(jSeparator3);
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 384, Short.MAX_VALUE)
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 21, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jToolBar1.add(jPanel1);
|
||||
jToolBar1.add(jSeparator7);
|
||||
|
||||
instrumentButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/codeminders/controltower/images/instruments.gif"))); // NOI18N
|
||||
instrumentButton.setText("instruments");
|
||||
instrumentButton.setToolTipText("toggle instruments");
|
||||
instrumentButton.setFocusable(false);
|
||||
instrumentButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
instrumentButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
instrumentButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(instrumentButton);
|
||||
|
||||
configureButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/codeminders/controltower/images/objects_039.gif"))); // NOI18N
|
||||
configureButton.setText("tuning");
|
||||
configureButton.setToolTipText("show drone tuning settings");
|
||||
configureButton.setFocusable(false);
|
||||
configureButton.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
|
||||
configureButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
|
||||
configureButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
configureButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
jToolBar1.add(configureButton);
|
||||
jToolBar1.add(jSeparator6);
|
||||
|
||||
videoPanel.setBackground(new java.awt.Color(102, 102, 102));
|
||||
videoPanel.setPreferredSize(new java.awt.Dimension(320, 240));
|
||||
videoPanel.setLayout(new javax.swing.BoxLayout(videoPanel, javax.swing.BoxLayout.LINE_AXIS));
|
||||
|
||||
jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.LINE_AXIS));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 508, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, 209, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void configureButtonActionPerformed(java.awt.event.ActionEvent evt)
|
||||
{//GEN-FIRST:event_configureButtonActionPerformed
|
||||
droneConfigWindow.setLocationRelativeTo(this);
|
||||
droneConfigWindow.setVisible(true);
|
||||
}//GEN-LAST:event_configureButtonActionPerformed
|
||||
|
||||
private void droneStatusMouseReleased(java.awt.event.MouseEvent evt)
|
||||
{//GEN-FIRST:event_droneStatusMouseReleased
|
||||
startUpdateLoop();
|
||||
}//GEN-LAST:event_droneStatusMouseReleased
|
||||
|
||||
private void controllerStatusMouseReleased(java.awt.event.MouseEvent evt)
|
||||
{//GEN-FIRST:event_controllerStatusMouseReleased
|
||||
initController();
|
||||
}//GEN-LAST:event_controllerStatusMouseReleased
|
||||
|
||||
private void instrumentButtonActionPerformed(java.awt.event.ActionEvent evt)
|
||||
{//GEN-FIRST:event_instrumentButtonActionPerformed
|
||||
Dimension dim = jPanel2.getSize();
|
||||
if(dim.getHeight() > 0)
|
||||
{
|
||||
dim.setSize(dim.getWidth(), 0);
|
||||
jPanel2.setPreferredSize(dim);
|
||||
jPanel2.setSize(dim);
|
||||
jPanel2.setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
dim.setSize(dim.getWidth(), 210);
|
||||
jPanel2.setPreferredSize(dim);
|
||||
jPanel2.setSize(dim);
|
||||
jPanel2.setVisible(true);
|
||||
}
|
||||
}//GEN-LAST:event_instrumentButtonActionPerformed
|
||||
|
||||
private void mappingButtonActionPerformed(java.awt.event.ActionEvent evt)
|
||||
{//GEN-FIRST:event_mappingButtonActionPerformed
|
||||
if (keyboardControllerStatus.getForeground() != Color.GREEN) {
|
||||
controlConfigWindow.setLocationRelativeTo(this);
|
||||
controlConfigWindow.setVisible(true);
|
||||
} else {
|
||||
keyboardControlConfigWindow.setLocationRelativeTo(this);
|
||||
keyboardControlConfigWindow.setVisible(true);
|
||||
}
|
||||
}//GEN-LAST:event_mappingButtonActionPerformed
|
||||
|
||||
private void flipSticksCheckboxActionPerformed(java.awt.event.ActionEvent evt)
|
||||
{//GEN-FIRST:event_flipSticksCheckboxActionPerformed
|
||||
flipSticks.set(flipSticksCheckbox.isSelected());
|
||||
prefs.putBoolean("FLIP_STICKS", flipSticks.get());
|
||||
}//GEN-LAST:event_flipSticksCheckboxActionPerformed
|
||||
|
||||
private void keyboardControllerStatusMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_keyboardControllerStatusMouseReleased
|
||||
initController();
|
||||
}//GEN-LAST:event_keyboardControllerStatusMouseReleased
|
||||
|
||||
private void joystickControllerStatusMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_joystickControllerStatusMouseReleased
|
||||
initController();
|
||||
}//GEN-LAST:event_joystickControllerStatusMouseReleased
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[])
|
||||
{
|
||||
final ControlTower tower = new ControlTower();
|
||||
java.awt.EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
tower.setLocationRelativeTo(null);
|
||||
tower.setVisible(true);
|
||||
}
|
||||
});
|
||||
tower.startUpdateLoop();
|
||||
}
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JLabel batteryStatus;
|
||||
private javax.swing.JButton configureButton;
|
||||
private javax.swing.JLabel droneStatus;
|
||||
private javax.swing.JCheckBox flipSticksCheckbox;
|
||||
private javax.swing.JButton instrumentButton;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JPanel jPanel2;
|
||||
private javax.swing.JToolBar.Separator jSeparator1;
|
||||
private javax.swing.JToolBar.Separator jSeparator3;
|
||||
private javax.swing.JToolBar.Separator jSeparator5;
|
||||
private javax.swing.JToolBar.Separator jSeparator6;
|
||||
private javax.swing.JToolBar.Separator jSeparator7;
|
||||
private javax.swing.JToolBar jToolBar1;
|
||||
private javax.swing.JLabel joystickControllerStatus;
|
||||
private javax.swing.JLabel keyboardControllerStatus;
|
||||
private javax.swing.JButton mappingButton;
|
||||
private javax.swing.JPanel videoPanel;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.1" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" value="Configuration"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-57,0,0,1,77"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
|
||||
<Property name="columns" type="int" value="2"/>
|
||||
<Property name="rows" type="int" value="6"/>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JLabel" name="maxAltitudeLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Max Altitude"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="maxAltitude">
|
||||
<Properties>
|
||||
<Property name="maximum" type="int" value="10000"/>
|
||||
<Property name="value" type="int" value="10000"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="updateAlt"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="maxAngleLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Max Angle"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="maxAngle">
|
||||
<Properties>
|
||||
<Property name="value" type="int" value="20"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="updateAngle"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="maxSpeedLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Vertical Speed"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="maxSpeed">
|
||||
<Properties>
|
||||
<Property name="maximum" type="int" value="3000"/>
|
||||
<Property name="value" type="int" value="2000"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="updateSpeed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="maxYawLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Rotation Speed"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="maxYaw">
|
||||
<Properties>
|
||||
<Property name="maximum" type="int" value="611"/>
|
||||
<Property name="minimum" type="int" value="70"/>
|
||||
<Property name="value" type="int" value="200"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="updateYaw"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="controllerDeadzoneLabel">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Controller Deadzone"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSlider" name="controllerDeadzone">
|
||||
<Events>
|
||||
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="updateDeadZone"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="cancelButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Close"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="okButton">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Apply"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* DroneConfig.java
|
||||
*
|
||||
* Created on 17.05.2011, 15:19:40
|
||||
*/
|
||||
package com.codeminders.controltower;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import java.io.IOException;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class DroneConfig extends javax.swing.JDialog {
|
||||
|
||||
private ARDrone drone;
|
||||
private ControlTower tower;
|
||||
private Preferences prefs;
|
||||
|
||||
/** Creates new form DroneConfig */
|
||||
public DroneConfig(ControlTower tower, boolean modal) {
|
||||
super(tower, modal);
|
||||
this.tower = tower;
|
||||
prefs = Preferences.userNodeForPackage(this.getClass());
|
||||
initComponents();
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
private synchronized void saveSettings() {
|
||||
prefs.putInt("control:altitude_max", maxAltitude.getValue());
|
||||
prefs.putInt("control:euler_angle_max", maxAngle.getValue());
|
||||
prefs.putInt("control:control_vz_max", maxSpeed.getValue());
|
||||
prefs.putInt("control:control_yaw", maxYaw.getValue());
|
||||
prefs.putInt("tower:controller_threshold", controllerDeadzone.getValue());
|
||||
try {
|
||||
prefs.flush();
|
||||
} catch (BackingStoreException ex) {
|
||||
Logger.getLogger(DroneConfig.class.getName()).error("Cannot save settings: {0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void loadSettings() {
|
||||
maxAltitude.setValue(prefs.getInt("control:altitude_max", 10000));
|
||||
maxAngle.setValue(prefs.getInt("control:euler_angle_max", 20));
|
||||
maxSpeed.setValue(prefs.getInt("control:control_vz_max", 2000));
|
||||
maxYaw.setValue(prefs.getInt("control:control_yaw", 200));
|
||||
controllerDeadzone.setValue(prefs.getInt("tower:controller_threshold", 50));
|
||||
updateAlt(null);
|
||||
updateAngle(null);
|
||||
updateSpeed(null);
|
||||
updateYaw(null);
|
||||
updateDeadZone(null);
|
||||
}
|
||||
|
||||
public synchronized void setDrone(ARDrone drone) {
|
||||
this.drone = drone;
|
||||
}
|
||||
|
||||
public synchronized void updateDrone() {
|
||||
if (drone == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
drone.setConfigOption("control:altitude_max", maxAltitude.getValue() + "");
|
||||
drone.setConfigOption("control:euler_angle_max", (((float) maxAngle.getValue()) / 100.0f) + "");
|
||||
drone.setConfigOption("control:control_vz_max", maxSpeed.getValue() + "");
|
||||
drone.setConfigOption("control:control_yaw", (((float) maxYaw.getValue()) / 100.0f) + "");
|
||||
tower.setControlThreshold(((float) controllerDeadzone.getValue()) / 100.0f);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(DroneConfig.class.getName()).error("Exception Setting data: {0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
maxAltitudeLabel = new javax.swing.JLabel();
|
||||
maxAltitude = new javax.swing.JSlider();
|
||||
maxAngleLabel = new javax.swing.JLabel();
|
||||
maxAngle = new javax.swing.JSlider();
|
||||
maxSpeedLabel = new javax.swing.JLabel();
|
||||
maxSpeed = new javax.swing.JSlider();
|
||||
maxYawLabel = new javax.swing.JLabel();
|
||||
maxYaw = new javax.swing.JSlider();
|
||||
controllerDeadzoneLabel = new javax.swing.JLabel();
|
||||
controllerDeadzone = new javax.swing.JSlider();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
okButton = new javax.swing.JButton();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("Configuration");
|
||||
getContentPane().setLayout(new java.awt.GridLayout(6, 2));
|
||||
|
||||
maxAltitudeLabel.setText("Max Altitude");
|
||||
getContentPane().add(maxAltitudeLabel);
|
||||
|
||||
maxAltitude.setMaximum(10000);
|
||||
maxAltitude.setValue(10000);
|
||||
maxAltitude.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
updateAlt(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(maxAltitude);
|
||||
|
||||
maxAngleLabel.setText("Max Angle");
|
||||
getContentPane().add(maxAngleLabel);
|
||||
|
||||
maxAngle.setValue(20);
|
||||
maxAngle.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
updateAngle(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(maxAngle);
|
||||
|
||||
maxSpeedLabel.setText("Vertical Speed");
|
||||
getContentPane().add(maxSpeedLabel);
|
||||
|
||||
maxSpeed.setMaximum(3000);
|
||||
maxSpeed.setValue(2000);
|
||||
maxSpeed.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
updateSpeed(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(maxSpeed);
|
||||
|
||||
maxYawLabel.setText("Rotation Speed");
|
||||
getContentPane().add(maxYawLabel);
|
||||
|
||||
maxYaw.setMaximum(611);
|
||||
maxYaw.setMinimum(70);
|
||||
maxYaw.setValue(200);
|
||||
maxYaw.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
updateYaw(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(maxYaw);
|
||||
|
||||
controllerDeadzoneLabel.setText("Controller Deadzone");
|
||||
getContentPane().add(controllerDeadzoneLabel);
|
||||
|
||||
controllerDeadzone.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
updateDeadZone(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(controllerDeadzone);
|
||||
|
||||
cancelButton.setText("Close");
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancelButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(cancelButton);
|
||||
|
||||
okButton.setText("Apply");
|
||||
okButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
okButtonActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
getContentPane().add(okButton);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
|
||||
setVisible(false);
|
||||
}//GEN-LAST:event_cancelButtonActionPerformed
|
||||
|
||||
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
|
||||
updateDrone();
|
||||
saveSettings();
|
||||
}//GEN-LAST:event_okButtonActionPerformed
|
||||
|
||||
private void updateAlt(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateAlt
|
||||
float value = (float) maxAltitude.getValue() / 1000.0f;
|
||||
maxAltitude.setToolTipText(value + "m");
|
||||
maxAltitudeLabel.setText("Max Altitude (" + value + "m)");
|
||||
}//GEN-LAST:event_updateAlt
|
||||
|
||||
private void updateAngle(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateAngle
|
||||
long value = Math.round(Math.toDegrees((float) maxAngle.getValue() / 100.0f));
|
||||
maxAngle.setToolTipText(value + "");
|
||||
maxAngleLabel.setText("Max Angle (" + value + "°)");
|
||||
}//GEN-LAST:event_updateAngle
|
||||
|
||||
private void updateSpeed(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateSpeed
|
||||
float value = (float) maxSpeed.getValue() / 1000.0f;
|
||||
maxSpeed.setToolTipText(value + "m/s");
|
||||
maxSpeedLabel.setText("Vertical Speed (" + value + "m/s)");
|
||||
}//GEN-LAST:event_updateSpeed
|
||||
|
||||
private void updateDeadZone(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateDeadZone
|
||||
float value = (float) controllerDeadzone.getValue();
|
||||
controllerDeadzone.setToolTipText(value + "%");
|
||||
controllerDeadzoneLabel.setText("Controller Deadzone (" + value + "%)");
|
||||
}//GEN-LAST:event_updateDeadZone
|
||||
|
||||
private void updateYaw(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateYaw
|
||||
float value = Math.round(Math.toDegrees((float) maxYaw.getValue() / 100.0f));
|
||||
maxYaw.setToolTipText(value + "°/s");
|
||||
maxYawLabel.setText("Rotation Speed (" + value + "°/s)");
|
||||
}//GEN-LAST:event_updateYaw
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JSlider controllerDeadzone;
|
||||
private javax.swing.JLabel controllerDeadzoneLabel;
|
||||
private javax.swing.JSlider maxAltitude;
|
||||
private javax.swing.JLabel maxAltitudeLabel;
|
||||
private javax.swing.JSlider maxAngle;
|
||||
private javax.swing.JLabel maxAngleLabel;
|
||||
private javax.swing.JSlider maxSpeed;
|
||||
private javax.swing.JLabel maxSpeedLabel;
|
||||
private javax.swing.JSlider maxYaw;
|
||||
private javax.swing.JLabel maxYawLabel;
|
||||
private javax.swing.JButton okButton;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
|
||||
<Properties>
|
||||
<Property name="defaultCloseOperation" type="int" value="2"/>
|
||||
<Property name="title" type="java.lang.String" value="Controller Mapping"/>
|
||||
</Properties>
|
||||
<SyntheticProperties>
|
||||
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
|
||||
</SyntheticProperties>
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" min="-2" pref="314" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jScrollPane1" min="-2" pref="266" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="jTextArea1">
|
||||
<Properties>
|
||||
<Property name="editable" type="boolean" value="false"/>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="text" type="java.lang.String" value="Enter - takeoff
Space - landing

W - tilt left
A - tilt forward
S - tilt backward
D - tilt right

Arrow Up - increase altitude
Arrow Down - decrease altitude
Arrow Left - yaw Left
Arrow Right- yaw Right

R - clear emergency signal
C - change camera view
E - start/stop video
F - take a snapshot"/>
|
||||
</Properties>
|
||||
<AccessibilityProperties>
|
||||
<Property name="AccessibleContext.accessibleName" type="java.lang.String" value="keyMappings"/>
|
||||
<Property name="AccessibleContext.accessibleParent" type="javax.accessibility.Accessible" editor="org.netbeans.modules.form.ComponentChooserEditor">
|
||||
<ComponentRef name="Form"/>
|
||||
</Property>
|
||||
</AccessibilityProperties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* ControlConfig.java
|
||||
*
|
||||
* Created on 25.05.2011, 18:06:24
|
||||
*/
|
||||
package com.codeminders.controltower;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class KeyboardControlConfig extends javax.swing.JDialog {
|
||||
|
||||
private int index = 0;
|
||||
|
||||
/** Creates new form ControlConfig */
|
||||
public KeyboardControlConfig(java.awt.Frame parent, boolean modal) {
|
||||
super(parent, modal);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
|
||||
jScrollPane1 = new javax.swing.JScrollPane();
|
||||
jTextArea1 = new javax.swing.JTextArea();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("Controller Mapping");
|
||||
|
||||
jTextArea1.setEditable(false);
|
||||
jTextArea1.setColumns(20);
|
||||
jTextArea1.setRows(5);
|
||||
jTextArea1.setText("Enter - takeoff\nSpace - landing\n\nW - tilt left\nA - tilt forward\nS - tilt backward\nD - tilt right\n\nArrow Up - increase altitude\nArrow Down - decrease altitude\nArrow Left - yaw Left\nArrow Right- yaw Right\n\nR - clear emergency signal\nC - change camera view\nE - start/stop video\nF - take a snapshot");
|
||||
jScrollPane1.setViewportView(jTextArea1);
|
||||
jTextArea1.getAccessibleContext().setAccessibleName("keyMappings");
|
||||
jTextArea1.getAccessibleContext().setAccessibleParent(this);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 314, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 266, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JScrollPane jScrollPane1;
|
||||
private javax.swing.JTextArea jTextArea1;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.1" encoding="UTF-8" ?>
|
||||
|
||||
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<AuxValues>
|
||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-20,0,0,1,98"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
|
||||
<Property name="columns" type="int" value="6"/>
|
||||
<Property name="rows" type="int" value="4"/>
|
||||
</Layout>
|
||||
</Form>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* VideoPanel.java
|
||||
*
|
||||
* Created on 21.05.2011, 18:42:10
|
||||
*/
|
||||
|
||||
package com.codeminders.controltower;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import com.codeminders.ardrone.DroneVideoListener;
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class VideoPanel extends javax.swing.JPanel implements DroneVideoListener
|
||||
{
|
||||
|
||||
private AtomicReference<BufferedImage> image = new AtomicReference<BufferedImage>();
|
||||
private AtomicBoolean preserveAspect = new AtomicBoolean(true);
|
||||
private BufferedImage noConnection = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
/** Creates new form VideoPanel */
|
||||
public VideoPanel()
|
||||
{
|
||||
initComponents();
|
||||
Graphics2D g2d = (Graphics2D) noConnection.getGraphics();
|
||||
Font f = g2d.getFont().deriveFont(24.0f);
|
||||
g2d.setFont(f);
|
||||
g2d.drawString("No video connection", 40, 110);
|
||||
image.set(noConnection);
|
||||
}
|
||||
|
||||
public void setDrone(ARDrone drone)
|
||||
{
|
||||
drone.addImageListener(this);
|
||||
}
|
||||
|
||||
public void setPreserveAspect(boolean preserve)
|
||||
{
|
||||
preserveAspect.set(preserve);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void frameReceived(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
|
||||
{
|
||||
BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
im.setRGB(startX, startY, w, h, rgbArray, offset, scansize);
|
||||
image.set(im);
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g)
|
||||
{
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
drawDroneImage(g2d, width, height);
|
||||
}
|
||||
|
||||
private void drawDroneImage(Graphics2D g2d, int width, int height)
|
||||
{
|
||||
BufferedImage im = image.get();
|
||||
if(im == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int xPos = 0;
|
||||
int yPos = 0;
|
||||
if(preserveAspect.get())
|
||||
{
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.fill3DRect(0, 0, width, height, false);
|
||||
float widthUnit = ((float) width / 4.0f);
|
||||
float heightAspect = (float) height / widthUnit;
|
||||
float heightUnit = ((float) height / 3.0f);
|
||||
float widthAspect = (float) width / heightUnit;
|
||||
|
||||
if(widthAspect > 4)
|
||||
{
|
||||
xPos = (int) (width - (heightUnit * 4)) / 2;
|
||||
width = (int) (heightUnit * 4);
|
||||
} else if(heightAspect > 3)
|
||||
{
|
||||
yPos = (int) (height - (widthUnit * 3)) / 2;
|
||||
height = (int) (widthUnit * 3);
|
||||
}
|
||||
}
|
||||
if(im != null)
|
||||
{
|
||||
g2d.drawImage(im, xPos, yPos, width, height, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is always
|
||||
* regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed"
|
||||
// desc="Generated Code">//GEN-BEGIN:initComponents
|
||||
private void initComponents()
|
||||
{
|
||||
|
||||
setLayout(new java.awt.GridLayout(4, 6));
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
// End of variables declaration//GEN-END:variables
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
package com.codeminders.controltower.config;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import com.codeminders.ardrone.ARDrone.Animation;
|
||||
import com.codeminders.ardrone.ARDrone.LED;
|
||||
import com.codeminders.ardrone.ARDrone.VideoChannel;
|
||||
import com.codeminders.ardrone.util.FileImageRecorder;
|
||||
import com.codeminders.ardrone.util.FileVideoRecorder;
|
||||
import com.codeminders.ardrone.util.RecordingSuccessCallback;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* This class represents one control mapping for a button and at the same
|
||||
* time implements all control logic for button presses.
|
||||
* @author normenhansen
|
||||
*/
|
||||
public class AssignableControl {
|
||||
|
||||
public enum Command {
|
||||
|
||||
TAKEOFF, LAND, TRIM, CLEAR_EMERGENCY, PLAY_ANIMATION, PLAY_LED, RESET,
|
||||
VIDEO_CYCLE, FRONTAL_CAM, BOTTOM_CAM, BOTTOM_CAM_SMALL, FRONTAL_CAM_SMALL, TAKE_SNAPSHOT, RECORD_VIDEO
|
||||
}
|
||||
|
||||
public enum ControllerButton {
|
||||
|
||||
PS, SELECT, START, LEFT_STICK, RIGHT_STICK, TRIANGLE, CIRCLE, CROSS, SQUARE, L1, L2, R1, R2
|
||||
}
|
||||
|
||||
public enum ControllerAxis {
|
||||
|
||||
LEFT_X, LEFT_Y, RIGHT_X, RIGHT_Y
|
||||
}
|
||||
|
||||
public enum DroneAxis {
|
||||
|
||||
FRONT_BACK, LEFT_RIGHT, UP_DOWN, ROTATE
|
||||
}
|
||||
private ControllerButton button;
|
||||
private Command command;
|
||||
private Animation anim;
|
||||
private LED led;
|
||||
private ControllerAxis controlAxis;
|
||||
private DroneAxis droneAxis;
|
||||
private float frequency;
|
||||
private int duration;
|
||||
private int delay;
|
||||
private String prefString;
|
||||
private static final VideoChannel[] VIDEO_CYCLE = {VideoChannel.HORIZONTAL_ONLY,
|
||||
VideoChannel.VERTICAL_ONLY, VideoChannel.VERTICAL_IN_HORIZONTAL, VideoChannel.HORIZONTAL_IN_VERTICAL};
|
||||
private int video_index = 0;
|
||||
private File recFile;
|
||||
private FileImageRecorder fir;
|
||||
private FileVideoRecorder fvr;
|
||||
|
||||
/**
|
||||
* Creates the control from a string that is stored in the java preferences of this app
|
||||
* @param prefString
|
||||
*/
|
||||
public AssignableControl(String prefString) {
|
||||
String[] strings = prefString.split("/");
|
||||
if (strings.length < 3) {
|
||||
throw new IllegalStateException("preference string malformed");
|
||||
}
|
||||
button = ControllerButton.valueOf(strings[0]);
|
||||
command = Command.valueOf(strings[1]);
|
||||
delay = Integer.parseInt(strings[2]);
|
||||
switch (command) {
|
||||
case PLAY_ANIMATION:
|
||||
anim = Animation.valueOf(strings[3]);
|
||||
duration = Integer.parseInt(strings[4]);
|
||||
break;
|
||||
case PLAY_LED:
|
||||
led = LED.valueOf(strings[3]);
|
||||
frequency = Float.parseFloat(strings[4]);
|
||||
duration = Integer.parseInt(strings[5]);
|
||||
break;
|
||||
case RECORD_VIDEO:
|
||||
case TAKE_SNAPSHOT:
|
||||
try {
|
||||
recFile = new File(strings[3].replace('?', File.separatorChar));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
this.prefString = prefString;
|
||||
}
|
||||
|
||||
public AssignableControl(ControllerButton button, LED led, int delay, float frequency, int duration) {
|
||||
this.command = Command.PLAY_LED;
|
||||
this.delay = delay;
|
||||
this.led = led;
|
||||
this.frequency = frequency;
|
||||
this.duration = duration;
|
||||
prefString = button.name() + "/" + command.name() + "/" + delay + "/" + led.name() + "/" + frequency + "/" + duration;
|
||||
}
|
||||
|
||||
public AssignableControl(ControllerButton button, Animation anim, int delay, int duration) {
|
||||
this.command = Command.PLAY_ANIMATION;
|
||||
this.delay = delay;
|
||||
this.anim = anim;
|
||||
this.duration = duration;
|
||||
prefString = button.name() + "/" + command.name() + "/" + delay + "/" + anim.name() + "/" + duration;
|
||||
}
|
||||
|
||||
public AssignableControl(ControllerButton button, Command command, int delay, File file) {
|
||||
this.command = command;
|
||||
this.delay = delay;
|
||||
this.recFile = file;
|
||||
prefString = button.name() + "/" + command.name() + "/" + delay + "/" + file.getPath().replace(File.separatorChar, '?');
|
||||
}
|
||||
|
||||
public AssignableControl(ControllerButton button, Command command, int delay) {
|
||||
this.command = command;
|
||||
this.delay = delay;
|
||||
prefString = button.name() + "/" + command.name() + "/" + delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the command to the supplied drone
|
||||
* @param drone
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendToDrone(final ARDrone drone) throws IOException {
|
||||
switch (command) {
|
||||
case PLAY_ANIMATION:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending animation command");
|
||||
drone.playAnimation(anim, duration);
|
||||
break;
|
||||
case PLAY_LED:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending LED command");
|
||||
drone.playLED(led, frequency, duration);
|
||||
break;
|
||||
case CLEAR_EMERGENCY:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending clear emergency");
|
||||
drone.clearEmergencySignal();
|
||||
break;
|
||||
case TRIM:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending trim");
|
||||
drone.trim();
|
||||
break;
|
||||
case TAKEOFF:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending takeoff");
|
||||
drone.takeOff();
|
||||
break;
|
||||
case LAND:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending land");
|
||||
drone.land();
|
||||
break;
|
||||
case RESET:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending reset");
|
||||
drone.clearEmergencySignal();
|
||||
drone.trim();
|
||||
break;
|
||||
case VIDEO_CYCLE:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending video cycle");
|
||||
cycleVideoChannel(drone);
|
||||
break;
|
||||
case FRONTAL_CAM:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending front cam");
|
||||
drone.selectVideoChannel(ARDrone.VideoChannel.VERTICAL_ONLY);
|
||||
break;
|
||||
case BOTTOM_CAM:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending bottom cam");
|
||||
drone.selectVideoChannel(ARDrone.VideoChannel.HORIZONTAL_ONLY);
|
||||
break;
|
||||
case BOTTOM_CAM_SMALL:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending bottom cam small");
|
||||
drone.selectVideoChannel(ARDrone.VideoChannel.VERTICAL_IN_HORIZONTAL);
|
||||
break;
|
||||
case FRONTAL_CAM_SMALL:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Sending front cam small");
|
||||
drone.selectVideoChannel(ARDrone.VideoChannel.HORIZONTAL_IN_VERTICAL);
|
||||
break;
|
||||
case TAKE_SNAPSHOT:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Take snapshot");
|
||||
takeSnapshot(drone);
|
||||
break;
|
||||
case RECORD_VIDEO:
|
||||
Logger.getLogger(AssignableControl.class.getName()).debug("Record video");
|
||||
recordVideo(drone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void takeSnapshot(final ARDrone drone) {
|
||||
if (fir == null) {
|
||||
fir = new FileImageRecorder(recFile, 0, "SNAPSHOT-", new RecordingSuccessCallback() {
|
||||
|
||||
@Override
|
||||
public void recordingSuccess(String filename) {
|
||||
try {
|
||||
AudioPlayer.playResource(this.getClass(), "/com/codeminders/controltower/sounds/camera.aif");
|
||||
} catch (Exception ex) {
|
||||
java.util.logging.Logger.getLogger(AssignableControl.class.getName()).log(Level.SEVERE, "{0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordingError(String filename, String err, Throwable ex) {
|
||||
}
|
||||
});
|
||||
drone.addImageListener(fir);
|
||||
}
|
||||
fir.activate();
|
||||
}
|
||||
|
||||
private synchronized void recordVideo(final ARDrone drone) {
|
||||
|
||||
if (fvr == null) {
|
||||
fvr = new FileVideoRecorder(recFile, 0, "VIDEO-", new RecordingSuccessCallback() {
|
||||
|
||||
@Override
|
||||
public void recordingSuccess(String filename) {
|
||||
try {
|
||||
AudioPlayer.playResource(this.getClass(), "/com/codeminders/controltower/sounds/rec_stop.aif");
|
||||
} catch (Exception ex) {
|
||||
java.util.logging.Logger.getLogger(AssignableControl.class.getName()).log(Level.SEVERE, "{0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordingError(String filename, String err, Throwable ex) {
|
||||
}
|
||||
}, 20);
|
||||
drone.addImageListener(fvr);
|
||||
fvr.startRecording();
|
||||
try {
|
||||
AudioPlayer.playResource(this.getClass(), "/com/codeminders/controltower/sounds/rec_start.aif");
|
||||
} catch (Exception ex) {
|
||||
java.util.logging.Logger.getLogger(AssignableControl.class.getName()).log(Level.SEVERE, "{0}", ex);
|
||||
}
|
||||
} else {
|
||||
fvr.finishRecording();
|
||||
fvr = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for VIDEO_CYCLE commands to cycle the video channel
|
||||
* @param drone
|
||||
* @throws IOException
|
||||
*/
|
||||
private void cycleVideoChannel(ARDrone drone) throws IOException {
|
||||
if (++video_index == VIDEO_CYCLE.length) {
|
||||
video_index = 0;
|
||||
}
|
||||
drone.selectVideoChannel(VIDEO_CYCLE[video_index]);
|
||||
}
|
||||
|
||||
public ControllerButton getButton() {
|
||||
return button;
|
||||
}
|
||||
|
||||
public Command getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public Animation getAnim() {
|
||||
return anim;
|
||||
}
|
||||
|
||||
public LED getLed() {
|
||||
return led;
|
||||
}
|
||||
|
||||
public ControllerAxis getControlAxis() {
|
||||
return controlAxis;
|
||||
}
|
||||
|
||||
public DroneAxis getDroneAxis() {
|
||||
return droneAxis;
|
||||
}
|
||||
|
||||
public float getFrequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public int getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public File getRecFile() {
|
||||
return recFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the complete data of this object as a string for storing into java preferences
|
||||
* @return
|
||||
*/
|
||||
public String getPrefString() {
|
||||
return prefString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
package com.codeminders.controltower.config;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
public class AudioPlayer
|
||||
{
|
||||
|
||||
private static final int EXTERNAL_BUFFER_SIZE = 128000;
|
||||
|
||||
public static synchronized void playResource(Class resclass, String resname) throws Exception
|
||||
{
|
||||
play(AudioSystem.getAudioInputStream(new BufferedInputStream(resclass.getResourceAsStream(resname))));
|
||||
}
|
||||
|
||||
public static synchronized void play(AudioInputStream audioInputStream) throws Exception
|
||||
{
|
||||
|
||||
AudioFormat audioFormat = audioInputStream.getFormat();
|
||||
SourceDataLine line = null;
|
||||
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
|
||||
try
|
||||
{
|
||||
line = (SourceDataLine) AudioSystem.getLine(info);
|
||||
line.open(audioFormat);
|
||||
line.start();
|
||||
|
||||
int nBytesRead = 0;
|
||||
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
|
||||
while((nBytesRead = audioInputStream.read(abData, 0, abData.length)) > 0)
|
||||
{
|
||||
line.write(abData, 0, nBytesRead);
|
||||
}
|
||||
} finally
|
||||
{
|
||||
if(line != null)
|
||||
{
|
||||
line.drain();
|
||||
line.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.codeminders.controltower.config;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
import com.codeminders.controltower.config.AssignableControl.ControllerButton;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* This class stores the button mapping and manages
|
||||
* button command sending to the drone
|
||||
* @author normenhansen
|
||||
*/
|
||||
public class ControlMap {
|
||||
|
||||
private Preferences prefs;
|
||||
private HashMap<ControllerButton, List<AssignableControl>> map = new HashMap<ControllerButton, List<AssignableControl>>();
|
||||
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
|
||||
|
||||
public ControlMap() {
|
||||
prefs = Preferences.userNodeForPackage(this.getClass());
|
||||
loadMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the update loop in ControlTower when a button is pressed
|
||||
* @param drone
|
||||
* @param button
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void sendCommand(ARDrone drone, ControllerButton button) throws IOException {
|
||||
List<AssignableControl> commands = map.get(button);
|
||||
if (commands == null) {
|
||||
return;
|
||||
}
|
||||
for (Iterator<AssignableControl> it = commands.iterator(); it.hasNext();) {
|
||||
final AssignableControl assignableCommand = it.next();
|
||||
if (assignableCommand.getDelay() > 0) {
|
||||
delayCommand(assignableCommand, drone);
|
||||
} else {
|
||||
assignableCommand.sendToDrone(drone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command for a drone with its assigned delay using an Executor
|
||||
* @param command
|
||||
* @param drone
|
||||
*/
|
||||
private void delayCommand(final AssignableControl command, final ARDrone drone) {
|
||||
exec.schedule(new Callable<Object>() {
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
command.sendToDrone(drone);
|
||||
return null;
|
||||
}
|
||||
}, command.getDelay(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of control commands that are assigned to a certain button
|
||||
* @param button
|
||||
*/
|
||||
public List<AssignableControl> getControls(ControllerButton button) {
|
||||
return map.get(button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of control commands for a certain button
|
||||
* @param button
|
||||
* @param controls
|
||||
*/
|
||||
public synchronized void setControls(ControllerButton button, List<AssignableControl> controls) {
|
||||
map.put(button, controls);
|
||||
storeMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the mapping from the java preferences
|
||||
*/
|
||||
private void loadMap() {
|
||||
boolean found = false;
|
||||
try {
|
||||
String[] keys = prefs.keys();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String string = keys[i];
|
||||
if (string.startsWith("CONTROL_MAPPING")) {
|
||||
String mapping = prefs.get(string, "");
|
||||
AssignableControl command = new AssignableControl(mapping);
|
||||
List<AssignableControl> commands = map.get(command.getButton());
|
||||
if (commands == null) {
|
||||
commands = new LinkedList<AssignableControl>();
|
||||
map.put(command.getButton(), commands);
|
||||
}
|
||||
commands.add(0, command);
|
||||
Logger.getLogger(ControlMap.class.getName()).debug("Load command:{0} "+command.getPrefString());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} catch (BackingStoreException ex) {
|
||||
Logger.getLogger(ControlMap.class.getName()).error("{0}", ex);
|
||||
}
|
||||
if (!found) {
|
||||
Logger.getLogger(ControlMap.class.getName()).debug("Load default button map");
|
||||
createDefaultMapping();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default mapping in case no settings are stored yet
|
||||
*/
|
||||
private void createDefaultMapping() {
|
||||
map.put(ControllerButton.PS, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.PS).add(new AssignableControl(ControllerButton.PS, AssignableControl.Command.RESET, 0));
|
||||
|
||||
map.put(ControllerButton.START, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.START).add(new AssignableControl(ControllerButton.START, AssignableControl.Command.TAKEOFF, 0));
|
||||
|
||||
map.put(ControllerButton.SELECT, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.SELECT).add(new AssignableControl(ControllerButton.SELECT, AssignableControl.Command.LAND, 0));
|
||||
|
||||
map.put(ControllerButton.TRIANGLE, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.TRIANGLE).add(new AssignableControl(ControllerButton.TRIANGLE, AssignableControl.Command.VIDEO_CYCLE, 0));
|
||||
|
||||
map.put(ControllerButton.SQUARE, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.SQUARE).add(new AssignableControl(ControllerButton.SQUARE, AssignableControl.Command.TAKE_SNAPSHOT, 0));
|
||||
|
||||
map.put(ControllerButton.CIRCLE, new LinkedList<AssignableControl>());
|
||||
map.get(ControllerButton.CIRCLE).add(new AssignableControl(ControllerButton.CIRCLE, AssignableControl.Command.RECORD_VIDEO, 0));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the mapping in the java preferences
|
||||
*/
|
||||
private void storeMap() {
|
||||
clearMap();
|
||||
int i = 0;
|
||||
Collection<List<AssignableControl>> list = map.values();
|
||||
for (Iterator<List<AssignableControl>> it = list.iterator(); it.hasNext();) {
|
||||
List<AssignableControl> list1 = it.next();
|
||||
for (Iterator<AssignableControl> it1 = list1.iterator(); it1.hasNext();) {
|
||||
AssignableControl assignableCommand = it1.next();
|
||||
prefs.put("CONTROL_MAPPING_" + i++, assignableCommand.getPrefString());
|
||||
}
|
||||
}
|
||||
try {
|
||||
prefs.flush();
|
||||
} catch (BackingStoreException ex) {
|
||||
Logger.getLogger(ControlMap.class.getName()).error("{0}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the used preferences from all mappings
|
||||
*/
|
||||
private void clearMap() {
|
||||
String[] keys;
|
||||
try {
|
||||
keys = prefs.keys();
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
String string = keys[i];
|
||||
if (string.startsWith("CONTROL_MAPPING")) {
|
||||
prefs.remove(string);
|
||||
}
|
||||
}
|
||||
} catch (BackingStoreException ex) {
|
||||
Logger.getLogger(ControlMap.class.getName()).error("{0}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Depois Largura: | Altura: | Tamanho: 3.2 KiB |
|
Depois Largura: | Altura: | Tamanho: 3.2 KiB |
|
Depois Largura: | Altura: | Tamanho: 562 B |
|
Depois Largura: | Altura: | Tamanho: 562 B |
|
Depois Largura: | Altura: | Tamanho: 40 KiB |
|
Depois Largura: | Altura: | Tamanho: 622 B |
|
Depois Largura: | Altura: | Tamanho: 267 B |
|
Depois Largura: | Altura: | Tamanho: 266 B |
|
Depois Largura: | Altura: | Tamanho: 405 B |
@@ -0,0 +1,9 @@
|
||||
# Set root logger level to DEBUG and its only appender to A1.
|
||||
log4j.rootLogger=DEBUG, A1
|
||||
|
||||
# A1 is set to be a ConsoleAppender.
|
||||
log4j.appender.A1=org.apache.log4j.ConsoleAppender
|
||||
|
||||
# A1 uses PatternLayout.
|
||||
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.A1.layout.ConversionPattern=%r [%t] %-5p %c{2} %x - %m%n
|
||||
@@ -0,0 +1,83 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>javadrone</artifactId>
|
||||
<version>${currentVersion}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>javadrone-api</artifactId>
|
||||
<name>javadrone-api</name>
|
||||
<description>Java API (without using native code) to control AR.Drone</description>
|
||||
|
||||
<build>
|
||||
<finalName>javadrone-api-${currentVersion}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-eclipse-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<configuration>
|
||||
<downloadSources>true</downloadSources>
|
||||
<downloadSources>true</downloadSources>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>local</id>
|
||||
<name>Local repository</name>
|
||||
<url>file:${basedir}/../lib/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>hidapi</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.16</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,639 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.codeminders.ardrone.NavData.FlyingState;
|
||||
import com.codeminders.ardrone.commands.*;
|
||||
|
||||
public class ARDrone
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
DISCONNECTED, CONNECTING, BOOTSTRAP, DEMO, ERROR, TAKING_OFF, LANDING
|
||||
}
|
||||
|
||||
public enum VideoChannel
|
||||
{
|
||||
HORIZONTAL_ONLY, VERTICAL_ONLY, VERTICAL_IN_HORIZONTAL, HORIZONTAL_IN_VERTICAL
|
||||
}
|
||||
|
||||
public enum Animation
|
||||
{
|
||||
PHI_M30_DEG(0), PHI_30_DEG(1), THETA_M30_DEG(2), THETA_30_DEG(3), THETA_20DEG_YAW_200DEG(4), THETA_20DEG_YAW_M200DEG(
|
||||
5), TURNAROUND(6), TURNAROUND_GODOWN(7), YAW_SHAKE(8), YAW_DANCE(9), PHI_DANCE(10), THETA_DANCE(11), VZ_DANCE(
|
||||
12), WAVE(13), PHI_THETA_MIXED(14), DOUBLE_PHI_THETA_MIXED(15), ANIM_MAYDAY(16);
|
||||
|
||||
private int value;
|
||||
|
||||
private Animation(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum LED
|
||||
{
|
||||
BLINK_GREEN_RED(0), BLINK_GREEN(1), BLINK_RED(2), BLINK_ORANGE(3), SNAKE_GREEN_RED(4), FIRE(5), STANDARD(6), RED(
|
||||
7), GREEN(8), RED_SNAKE(9), BLANK(10), RIGHT_MISSILE(11), LEFT_MISSILE(12), DOUBLE_MISSILE(13), FRONT_LEFT_GREEN_OTHERS_RED(
|
||||
14), FRONT_RIGHT_GREEN_OTHERS_RED(15), REAR_RIGHT_GREEN_OTHERS_RED(16), REAR_LEFT_GREEN_OTHERS_RED(17), LEFT_GREEN_RIGHT_RED(
|
||||
18), LEFT_RED_RIGHT_GREEN(19), BLINK_STANDARD(20);
|
||||
|
||||
private int value;
|
||||
|
||||
private LED(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConfigOption
|
||||
{
|
||||
ACCS_OFFSET("control:accs_offset"), ACCS_GAINS("control:accs_gains"), GYROS_OFFSET("control:gyros_offset"), GYROS_GAINS(
|
||||
"control:gyros_gains"), GYROS110_OFFSET("control:gyros110_offset"), GYROS110_GAINS(
|
||||
"control:gyros110_gains"), GYRO_OFFSET_THR_X("control:gyro_offset_thr_x"), GYRO_OFFSET_THR_Y(
|
||||
"control:gyro_offset_thr_y"), GYRO_OFFSET_THR_Z("control:gyro_offset_thr_z"), PWM_REF_GYROS(
|
||||
"control:pwm_ref_gyros"), CONTROL_LEVEL("control:control_level"), SHIELD_ENABLE("control:shield_enable"), EULER_ANGLE_MAX(
|
||||
"control:euler_angle_max"), ALTITUDE_MAX("control:altitude_max"), ALTITUDE_MIN("control:altitude_min"), CONTROL_TRIM_Z(
|
||||
"control:control_trim_z"), CONTROL_IPHONE_TILT("control:control_iphone_tilt"), CONTROL_VZ_MAX(
|
||||
"control:control_vz_max"), CONTROL_YAW("control:control_yaw"), OUTDOOR("control:outdoor"), FLIGHT_WITHOUT_SHELL(
|
||||
"control:flight_without_shell"), BRUSHLESS("control:brushless"), AUTONOMOUS_FLIGHT(
|
||||
"control:autonomous_flight"), MANUAL_TRIM("control:manual_trim"), INDOOR_EULER_ANGLE_MAX(
|
||||
"control:indoor_euler_angle_max"), INDOOR_CONTROL_VZ_MAX("control:indoor_control_vz_max"), INDOOR_CONTROL_YAW(
|
||||
"control:indoor_control_yaw"), OUTDOOR_EULER_ANGLE_MAX("control:outdoor_euler_angle_max"), OUTDOOR_CONTROL_VZ_MAX(
|
||||
"control:outdoor_control_vz_max"), OUTDOOR_CONTROL_YAW("outdoor_control:control_yaw");
|
||||
|
||||
private String value;
|
||||
|
||||
private ConfigOption(String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
private static final int CMD_QUEUE_SIZE = 64;
|
||||
private State state = State.DISCONNECTED;
|
||||
private Object state_mutex = new Object();
|
||||
|
||||
private static final int NAVDATA_PORT = 5554;
|
||||
private static final int VIDEO_PORT = 5555;
|
||||
// private static final int CONTROL_PORT = 5559;
|
||||
|
||||
private static byte[] DEFAULT_DRONE_IP = { (byte) 192, (byte) 168, (byte) 1, (byte) 1 };
|
||||
|
||||
private InetAddress drone_addr;
|
||||
private DatagramSocket cmd_socket;
|
||||
// private Socket control_socket;
|
||||
|
||||
private CommandQueue cmd_queue = new CommandQueue(CMD_QUEUE_SIZE);
|
||||
|
||||
private NavDataReader nav_data_reader;
|
||||
private VideoReader video_reader;
|
||||
private CommandSender cmd_sender;
|
||||
|
||||
private Thread nav_data_reader_thread;
|
||||
private Thread cmd_sending_thread;
|
||||
private Thread video_reader_thread;
|
||||
|
||||
private boolean combinedYawMode = true;
|
||||
|
||||
private boolean emergencyMode = true;
|
||||
private Object emergency_mutex = new Object();
|
||||
|
||||
private List<DroneStatusChangeListener> status_listeners = new LinkedList<DroneStatusChangeListener>();
|
||||
private List<DroneVideoListener> image_listeners = new LinkedList<DroneVideoListener>();
|
||||
private List<NavDataListener> navdata_listeners = new LinkedList<NavDataListener>();
|
||||
|
||||
public ARDrone() throws UnknownHostException
|
||||
{
|
||||
this(InetAddress.getByAddress(DEFAULT_DRONE_IP));
|
||||
}
|
||||
|
||||
public ARDrone(InetAddress drone_addr)
|
||||
{
|
||||
this.drone_addr = drone_addr;
|
||||
}
|
||||
|
||||
public void addImageListener(DroneVideoListener l)
|
||||
{
|
||||
synchronized(image_listeners)
|
||||
{
|
||||
image_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeImageListener(DroneVideoListener l)
|
||||
{
|
||||
synchronized(image_listeners)
|
||||
{
|
||||
image_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearImageListeners()
|
||||
{
|
||||
synchronized(image_listeners)
|
||||
{
|
||||
image_listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void addStatusChangeListener(DroneStatusChangeListener l)
|
||||
{
|
||||
synchronized(status_listeners)
|
||||
{
|
||||
status_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeStatusChangeListener(DroneStatusChangeListener l)
|
||||
{
|
||||
synchronized(status_listeners)
|
||||
{
|
||||
status_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearStatusChangeListeners()
|
||||
{
|
||||
synchronized(status_listeners)
|
||||
{
|
||||
status_listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void addNavDataListener(NavDataListener l)
|
||||
{
|
||||
synchronized(navdata_listeners)
|
||||
{
|
||||
navdata_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNavDataListener(NavDataListener l)
|
||||
{
|
||||
synchronized(navdata_listeners)
|
||||
{
|
||||
navdata_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearNavDataListeners()
|
||||
{
|
||||
synchronized(navdata_listeners)
|
||||
{
|
||||
navdata_listeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void changeState(State newstate) throws IOException
|
||||
{
|
||||
if(newstate == State.ERROR)
|
||||
changeToErrorState(null);
|
||||
|
||||
synchronized(state_mutex)
|
||||
{
|
||||
if(state != newstate)
|
||||
{
|
||||
log.debug("State changed from " + state + " to " + newstate);
|
||||
state = newstate;
|
||||
|
||||
// We automatically switch to DEMO from bootstrap
|
||||
if(state == State.BOOTSTRAP)
|
||||
sendDemoNavigationData();
|
||||
|
||||
state_mutex.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
if(newstate == State.DEMO)
|
||||
{
|
||||
synchronized(status_listeners)
|
||||
{
|
||||
for(DroneStatusChangeListener l : status_listeners)
|
||||
l.ready();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void changeToErrorState(Exception ex)
|
||||
{
|
||||
synchronized(state_mutex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(state != State.DISCONNECTED)
|
||||
doDisconnect();
|
||||
} catch(IOException e)
|
||||
{
|
||||
// Ignoring exceptions on disconnection
|
||||
}
|
||||
log.debug("State changed from " + state + " to " + State.ERROR + " with exception ", ex);
|
||||
state = State.ERROR;
|
||||
state_mutex.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearEmergencySignal() throws IOException
|
||||
{
|
||||
synchronized(emergency_mutex)
|
||||
{
|
||||
if(isEmergencyMode())
|
||||
cmd_queue.add(new EmergencyCommand());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate drone connection procedure.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void connect() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
cmd_socket = new DatagramSocket();
|
||||
// control_socket = new Socket(drone_addr, CONTROL_PORT);
|
||||
|
||||
cmd_sender = new CommandSender(cmd_queue, this, drone_addr, cmd_socket);
|
||||
cmd_sending_thread = new Thread(cmd_sender);
|
||||
cmd_sending_thread.start();
|
||||
|
||||
nav_data_reader = new NavDataReader(this, drone_addr, NAVDATA_PORT);
|
||||
nav_data_reader_thread = new Thread(nav_data_reader);
|
||||
nav_data_reader_thread.start();
|
||||
|
||||
video_reader = new VideoReader(this, drone_addr, VIDEO_PORT);
|
||||
video_reader_thread = new Thread(video_reader);
|
||||
video_reader_thread.start();
|
||||
|
||||
changeState(State.CONNECTING);
|
||||
|
||||
} catch(IOException ex)
|
||||
{
|
||||
changeToErrorState(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public void disableAutomaticVideoBitrate() throws IOException
|
||||
{
|
||||
cmd_queue.add(new ConfigureCommand("video:bitrate_control_mode", "0"));
|
||||
}
|
||||
|
||||
public void disconnect() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
doDisconnect();
|
||||
} finally
|
||||
{
|
||||
changeState(State.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
private void doDisconnect() throws IOException
|
||||
{
|
||||
if(cmd_queue != null)
|
||||
cmd_queue.add(new QuitCommand());
|
||||
|
||||
if(nav_data_reader != null)
|
||||
nav_data_reader.stop();
|
||||
|
||||
if(video_reader != null)
|
||||
video_reader.stop();
|
||||
|
||||
if(cmd_socket != null)
|
||||
cmd_socket.close();
|
||||
|
||||
// Only the following method can throw an exception.
|
||||
// We call it last, to ensure it won't prevent other
|
||||
// cleanup operations from being completed
|
||||
// control_socket.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the automatic bitrate control of the video stream. Enabling this
|
||||
* configuration will reduce the bandwith used by the video stream under bad
|
||||
* Wi-Fi conditions, reducing the commands latency. Note : Before enabling
|
||||
* this config, make sure that your video decoder is able to handle the
|
||||
* variable bitrate mode !
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void enableAutomaticVideoBitrate() throws IOException
|
||||
{
|
||||
setConfigOption("video:bitrate_control_mode", "1");
|
||||
}
|
||||
|
||||
public void hover() throws IOException
|
||||
{
|
||||
cmd_queue.add(new HoverCommand());
|
||||
}
|
||||
|
||||
public boolean isCombinedYawMode()
|
||||
{
|
||||
return combinedYawMode;
|
||||
}
|
||||
|
||||
public boolean isEmergencyMode()
|
||||
{
|
||||
return emergencyMode;
|
||||
}
|
||||
|
||||
public void land() throws IOException
|
||||
{
|
||||
// TODO: Review of possible race condition
|
||||
cmd_queue.add(new LandCommand());
|
||||
changeState(State.LANDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the drone
|
||||
*
|
||||
* @param left_right_tilt The left-right tilt (aka. "drone roll" or phi
|
||||
* angle) argument is a percentage of the maximum inclination as
|
||||
* configured here. A negative value makes the drone tilt to its
|
||||
* left, thus flying leftward. A positive value makes the drone
|
||||
* tilt to its right, thus flying rightward.
|
||||
* @param front_back_tilt The front-back tilt (aka. "drone pitch" or theta
|
||||
* angle) argument is a percentage of the maximum inclination as
|
||||
* configured here. A negative value makes the drone lower its
|
||||
* nose, thus flying frontward. A positive value makes the drone
|
||||
* raise its nose, thus flying backward. The drone translation
|
||||
* speed in the horizontal plane depends on the environment and
|
||||
* cannot be determined. With roll or pitch values set to 0, the
|
||||
* drone will stay horizontal but continue sliding in the air
|
||||
* because of its inertia. Only the air resistance will then make
|
||||
* it stop.
|
||||
* @param vertical_speed The vertical speed (aka. "gaz") argument is a
|
||||
* percentage of the maximum vertical speed as defined here. A
|
||||
* positive value makes the drone rise in the air. A negative
|
||||
* value makes it go down.
|
||||
* @param angular_speed The angular speed argument is a percentage of the
|
||||
* maximum angular speed as defined here. A positive value makes
|
||||
* the drone spin right; a negative value makes it spin left.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void move(float left_right_tilt, float front_back_tilt, float vertical_speed, float angular_speed)
|
||||
throws IOException
|
||||
{
|
||||
cmd_queue
|
||||
.add(new MoveCommand(combinedYawMode, left_right_tilt, front_back_tilt, vertical_speed, angular_speed));
|
||||
}
|
||||
|
||||
// Callback used by receiver
|
||||
public void navDataReceived(NavData nd)
|
||||
{
|
||||
if(nd.isBatteryTooLow() || nd.isNotEnoughPower())
|
||||
{
|
||||
log.error("Battery pb " + nd.toString());
|
||||
}
|
||||
|
||||
synchronized(emergency_mutex)
|
||||
{
|
||||
emergencyMode = nd.isEmergency();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
synchronized(state_mutex)
|
||||
{
|
||||
if(state != State.CONNECTING && nd.isControlReceived())
|
||||
{
|
||||
log.debug("Control received! ACK!");
|
||||
cmd_queue.add(new ControlCommand(5, 0));
|
||||
}
|
||||
|
||||
if(state == State.TAKING_OFF && nd.getFlyingState() == FlyingState.FLYING)
|
||||
{
|
||||
log.debug("Take off success");
|
||||
cmd_queue.clear(); // Maybe we should just remove
|
||||
// LAND/TAKEOFF comand
|
||||
// instead of nuking the whole queue?
|
||||
changeState(State.DEMO);
|
||||
} else if(state == State.LANDING && nd.getFlyingState() == FlyingState.LANDED)
|
||||
{
|
||||
log.debug("Landing success");
|
||||
cmd_queue.clear(); // Maybe we should just remove
|
||||
// LAND/TAKEOFF comand
|
||||
// instead of nuking the whole queue?
|
||||
changeState(State.DEMO);
|
||||
} else if(state != State.BOOTSTRAP && nd.getMode() == NavData.Mode.BOOTSTRAP)
|
||||
{
|
||||
changeState(State.BOOTSTRAP);
|
||||
} else if(state == State.BOOTSTRAP && nd.getMode() == NavData.Mode.DEMO)
|
||||
{
|
||||
changeState(State.DEMO);
|
||||
}
|
||||
|
||||
if(state != State.CONNECTING && nd.isCommunicationProblemOccurred())
|
||||
{
|
||||
// 50ms communications watchdog has been triggered
|
||||
cmd_queue.add(new KeepAliveCommand());
|
||||
}
|
||||
|
||||
}
|
||||
} catch(IOException e)
|
||||
{
|
||||
log.error("Error changing the state", e);
|
||||
}
|
||||
|
||||
if(state == State.DEMO)
|
||||
{
|
||||
synchronized(navdata_listeners)
|
||||
{
|
||||
for(NavDataListener l : navdata_listeners)
|
||||
l.navDataReceived(nd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playAnimation(int animation_no, int duration) throws IOException
|
||||
{
|
||||
cmd_queue.add(new PlayAnimationCommand(animation_no, duration));
|
||||
}
|
||||
|
||||
public void playAnimation(Animation animation, int duration) throws IOException
|
||||
{
|
||||
cmd_queue.add(new PlayAnimationCommand(animation.getValue(), duration));
|
||||
}
|
||||
|
||||
public void playLED(int animation_no, float freq, int duration) throws IOException
|
||||
{
|
||||
cmd_queue.add(new PlayLEDCommand(animation_no, freq, duration));
|
||||
}
|
||||
|
||||
public void playLED(LED animation, float freq, int duration) throws IOException
|
||||
{
|
||||
cmd_queue.add(new PlayLEDCommand(animation.getValue(), freq, duration));
|
||||
}
|
||||
|
||||
public void selectVideoChannel(VideoChannel c) throws IOException
|
||||
{
|
||||
/*
|
||||
* Current implementation supports 4 different channels : -
|
||||
* ARDRONE_VIDEO_CHANNEL_HORI - ARDRONE_VIDEO_CHANNEL_VERT -
|
||||
* ARDRONE_VIDEO_CHANNEL_LARGE_HORI_SMALL_VERT -
|
||||
* ARDRONE_VIDEO_CHANNEL_LARGE_VERT_SMALL_HORI
|
||||
*
|
||||
* AT command example : AT*CONFIG=605,"video:video_channel","2"
|
||||
*/
|
||||
|
||||
String s;
|
||||
switch(c)
|
||||
{
|
||||
case HORIZONTAL_ONLY: // ARDRONE_VIDEO_CHANNEL_HORI
|
||||
s = "0";
|
||||
break;
|
||||
|
||||
case VERTICAL_ONLY: // ARDRONE_VIDEO_CHANNEL_VERT
|
||||
s = "1";
|
||||
break;
|
||||
|
||||
case VERTICAL_IN_HORIZONTAL: // ARDRONE_VIDEO_CHANNEL_LARGE_HORI_SMALL_VERT
|
||||
s = "2";
|
||||
break;
|
||||
|
||||
case HORIZONTAL_IN_VERTICAL: // ARDRONE_VIDEO_CHANNEL_LARGE_VERT_SMALL_HORI
|
||||
s = "3";
|
||||
break;
|
||||
default:
|
||||
assert (false);
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_queue.add(new ConfigureCommand("video:video_channel", s));
|
||||
}
|
||||
|
||||
public void sendAllNavigationData() throws IOException
|
||||
{
|
||||
setConfigOption("general:navdata_demo", "FALSE");
|
||||
}
|
||||
|
||||
public void sendDemoNavigationData() throws IOException
|
||||
{
|
||||
setConfigOption("general:navdata_demo", "TRUE");
|
||||
}
|
||||
|
||||
public void sendEmergencySignal() throws IOException
|
||||
{
|
||||
synchronized(emergency_mutex)
|
||||
{
|
||||
if(!isEmergencyMode())
|
||||
cmd_queue.add(new EmergencyCommand());
|
||||
}
|
||||
}
|
||||
|
||||
public void setCombinedYawMode(boolean combinedYawMode)
|
||||
{
|
||||
this.combinedYawMode = combinedYawMode;
|
||||
}
|
||||
|
||||
public void setConfigOption(String name, String value) throws IOException
|
||||
{
|
||||
cmd_queue.add(new ConfigureCommand(name, value));
|
||||
}
|
||||
|
||||
public void setConfigOption(ConfigOption option, String value) throws IOException
|
||||
{
|
||||
cmd_queue.add(new ConfigureCommand(option.getValue(), value));
|
||||
}
|
||||
|
||||
public void takeOff() throws IOException
|
||||
{
|
||||
// TODO: review for possible race condition
|
||||
if (!isEmergencyMode()) {
|
||||
cmd_queue.add(new TakeOffCommand());
|
||||
changeState(State.TAKING_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
public void trim() throws IOException
|
||||
{
|
||||
cmd_queue.add(new FlatTrimCommand());
|
||||
}
|
||||
|
||||
// Callback used by VideoReciver
|
||||
public void videoFrameReceived(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
|
||||
{
|
||||
synchronized(image_listeners)
|
||||
{
|
||||
for(DroneVideoListener l : image_listeners)
|
||||
l.frameReceived(startX, startY, w, h, rgbArray, offset, scansize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for drone to switch to demo mode. Throw exception if this not
|
||||
* succeeded within given timeout. Should be called right after connect().
|
||||
*
|
||||
* This is a convenience function. Another way to achieve the same result is
|
||||
* using status change callback.
|
||||
*
|
||||
* @param how_long
|
||||
* @throws IOException
|
||||
*/
|
||||
public void waitForReady(long how_long) throws IOException
|
||||
{
|
||||
long since = System.currentTimeMillis();
|
||||
synchronized(state_mutex)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if((System.currentTimeMillis() - since) >= how_long)
|
||||
{
|
||||
try
|
||||
{
|
||||
disconnect();
|
||||
} catch(IOException e)
|
||||
{
|
||||
}
|
||||
// Timeout, too late
|
||||
throw new IOException("Timeout connecting to ARDrone");
|
||||
} else if(state == State.DEMO)
|
||||
{
|
||||
return; // OK! We are now connected
|
||||
} else if(state == State.ERROR || state == State.DISCONNECTED)
|
||||
{
|
||||
throw new IOException("Connection Error");
|
||||
}
|
||||
|
||||
long p = Math.min(how_long - (System.currentTimeMillis() - since), how_long);
|
||||
if(p > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
state_mutex.wait(p);
|
||||
} catch(InterruptedException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class CommandQueue
|
||||
{
|
||||
private LinkedList<DroneCommand> data;
|
||||
private int maxSize;
|
||||
@SuppressWarnings("unused")
|
||||
private Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
public CommandQueue(int maxSize)
|
||||
{
|
||||
data = new LinkedList<DroneCommand>();
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public synchronized DroneCommand take() throws InterruptedException
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
DroneCommand res = data.pollLast();
|
||||
if(res != null)
|
||||
{
|
||||
// log.debug("[" + data.size() + "] Returning " + res);
|
||||
if(res.isSticky())
|
||||
{
|
||||
int sc = res.incrementStickyCounter();
|
||||
data.addLast(res);
|
||||
if(sc > 1)
|
||||
Thread.sleep(res.getStickyRate());
|
||||
}
|
||||
return res;
|
||||
} else
|
||||
{
|
||||
// log.debug("Waiting for data");
|
||||
wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void add(DroneCommand cmd)
|
||||
{
|
||||
Iterator<DroneCommand> i = data.iterator();
|
||||
int p = cmd.getPriority();
|
||||
int pos = -1;
|
||||
|
||||
while(i.hasNext())
|
||||
{
|
||||
DroneCommand x = i.next();
|
||||
pos++;
|
||||
int xp = x.getPriority();
|
||||
if(xp < p)
|
||||
{
|
||||
// Skipping
|
||||
continue;
|
||||
} else
|
||||
{
|
||||
// Found insertion point.
|
||||
if(!x.replaces(cmd))
|
||||
{
|
||||
// log.debug("[" + data.size() + "] Adding command " +
|
||||
// cmd);
|
||||
data.add(pos, cmd);
|
||||
notify();
|
||||
} else
|
||||
{
|
||||
// log.debug("Replacing duplicate element " + cmd);
|
||||
data.set(pos, cmd);
|
||||
}
|
||||
cmd = null; // inserted
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(cmd != null)
|
||||
{
|
||||
// log.debug("[" + data.size() + "] Adding command " + cmd);
|
||||
data.addLast(cmd);
|
||||
notify();
|
||||
}
|
||||
|
||||
if(data.size() > maxSize)
|
||||
{
|
||||
// TODO: trim
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int size()
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
public synchronized void clear()
|
||||
{
|
||||
data.clear();
|
||||
notify();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.codeminders.ardrone.commands.*;
|
||||
|
||||
public class CommandSender implements Runnable
|
||||
{
|
||||
private static final int CMD_PORT = 5556;
|
||||
|
||||
private CommandQueue cmd_queue;
|
||||
private ARDrone drone;
|
||||
private InetAddress drone_addr;
|
||||
private DatagramSocket cmd_socket;
|
||||
private int sequence = 1;
|
||||
|
||||
private Logger log = Logger.getLogger(getClass().getName());
|
||||
|
||||
public CommandSender(CommandQueue cmd_queue, ARDrone drone, InetAddress drone_addr, DatagramSocket cmd_socket)
|
||||
{
|
||||
this.cmd_queue = cmd_queue;
|
||||
this.drone = drone;
|
||||
this.drone_addr = drone_addr;
|
||||
this.cmd_socket = cmd_socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
try
|
||||
{
|
||||
DroneCommand c = cmd_queue.take();
|
||||
if(c instanceof QuitCommand)
|
||||
{
|
||||
// Terminating
|
||||
break;
|
||||
}
|
||||
|
||||
if(c instanceof ATCommand)
|
||||
{
|
||||
ATCommand cmd = (ATCommand) c;
|
||||
//if(!(c instanceof KeepAliveCommand) && !(c instanceof MoveCommand) && !(c instanceof HoverCommand) && c.getStickyCounter()==0)
|
||||
log.debug("Q[" + cmd_queue.size() + "]Sending AT command " + c);
|
||||
byte[] pdata = cmd.getPacket(sequence++); // TODO: pass
|
||||
// sequence number
|
||||
DatagramPacket p = new DatagramPacket(pdata, pdata.length, drone_addr, CMD_PORT);
|
||||
cmd_socket.send(p);
|
||||
}
|
||||
} catch(InterruptedException e)
|
||||
{
|
||||
// ignoring
|
||||
} catch(IOException e)
|
||||
{
|
||||
drone.changeToErrorState(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class DataReader implements Runnable {
|
||||
|
||||
private static final int RECONNECT_TIMEOUT = 1000;
|
||||
private static final int MAX_TMEOUT = 5;
|
||||
|
||||
protected DatagramChannel channel;
|
||||
ARDrone drone;
|
||||
protected Selector selector;
|
||||
private boolean done;
|
||||
private InetAddress drone_addr;
|
||||
private int data_port;
|
||||
|
||||
private long timeOfLastMessage = 0;
|
||||
private int buffer_size;
|
||||
|
||||
|
||||
public DataReader(ARDrone drone, InetAddress drone_addr, int data_port, int buffer_size) throws ClosedChannelException, IOException {
|
||||
super();
|
||||
this.drone = drone;
|
||||
this.drone_addr = drone_addr;
|
||||
this.data_port = data_port;
|
||||
this.buffer_size = buffer_size;
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect() throws IOException, ClosedChannelException {
|
||||
|
||||
channel = DatagramChannel.open();
|
||||
channel.configureBlocking(false);
|
||||
channel.socket().bind(new InetSocketAddress(data_port));
|
||||
channel.connect(new InetSocketAddress(drone_addr, data_port));
|
||||
|
||||
selector = Selector.open();
|
||||
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
try {
|
||||
if (selector.isOpen())
|
||||
selector.close();
|
||||
} catch (IOException iox)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (!channel.socket().isClosed()) {
|
||||
channel.socket().close();
|
||||
}
|
||||
|
||||
try {
|
||||
if (channel.isConnected())
|
||||
channel.disconnect();
|
||||
} catch (IOException iox)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteBuffer inbuf = ByteBuffer.allocate(buffer_size);
|
||||
done = false;
|
||||
while(!done)
|
||||
{
|
||||
selector.select(MAX_TMEOUT);
|
||||
if(done)
|
||||
{
|
||||
disconnect();
|
||||
break;
|
||||
}
|
||||
Set readyKeys = selector.selectedKeys();
|
||||
Iterator iterator = readyKeys.iterator();
|
||||
|
||||
if (!iterator.hasNext()) {
|
||||
if (timeOfLastMessage > 0 && System.currentTimeMillis() - timeOfLastMessage > RECONNECT_TIMEOUT ) {
|
||||
disconnect();
|
||||
try {
|
||||
connect();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
timeOfLastMessage = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
while(iterator.hasNext())
|
||||
{
|
||||
timeOfLastMessage = System.currentTimeMillis();
|
||||
SelectionKey key = (SelectionKey) iterator.next();
|
||||
iterator.remove();
|
||||
handleReceivedMessageKey(key, inbuf);
|
||||
}
|
||||
}
|
||||
} catch(Exception e)
|
||||
{
|
||||
drone.changeToErrorState(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract void handleReceivedMessageKey(SelectionKey key, ByteBuffer inbuf) throws Exception;
|
||||
|
||||
public void stop()
|
||||
{
|
||||
done = true;
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public class Dimension
|
||||
{
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public Dimension(int width, int height)
|
||||
{
|
||||
super();
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width)
|
||||
{
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height)
|
||||
{
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public abstract class DroneCommand
|
||||
{
|
||||
protected static final int MIN_PRIORITY = 0;
|
||||
protected static final int HIGH_PRIORITY = 50;
|
||||
protected static final int VERY_HIGH_PRIORITY = 90;
|
||||
protected static final int MAX_PRIORITY = 100;
|
||||
protected static final long DEFAULT_STICKY_RATE_MS = 100;
|
||||
|
||||
protected static final String LAND_TAKEOFF_CATEGORY = "takeoffland";
|
||||
|
||||
private int sticky_counter = 0;
|
||||
|
||||
public abstract int getPriority();
|
||||
|
||||
public String getCategory()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSticky()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* For sticky packets indicates how many times it has been sent.
|
||||
*
|
||||
* @return current value
|
||||
*/
|
||||
public int incrementStickyCounter()
|
||||
{
|
||||
return ++sticky_counter;
|
||||
}
|
||||
|
||||
public int getStickyCounter()
|
||||
{
|
||||
return sticky_counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* For sticky packets indicates delay between sending repeated packets;
|
||||
*/
|
||||
public long getStickyRate()
|
||||
{
|
||||
return DEFAULT_STICKY_RATE_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to check if 2 commands are are relaceable by each
|
||||
* other.
|
||||
*
|
||||
* @param cmd
|
||||
* @return true if they are replaceable
|
||||
*/
|
||||
public boolean replaces(DroneCommand cmd)
|
||||
{
|
||||
if(equals(cmd))
|
||||
{
|
||||
return true;
|
||||
} else
|
||||
{
|
||||
String c = getCategory();
|
||||
return c != null && c.equals(cmd.getCategory());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public interface DroneStatusChangeListener
|
||||
{
|
||||
|
||||
/**
|
||||
* This method is called whenever the drone changes from BOOTSTRAP or
|
||||
* ERROR modes to DEMO mode. Could be used for user-supplied initialization
|
||||
*/
|
||||
void ready();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public interface DroneVideoListener
|
||||
{
|
||||
void frameReceived(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize);
|
||||
}
|
||||
@@ -0,0 +1,650 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.codeminders.ardrone.VisionTag.VisionTagType;
|
||||
|
||||
public class NavData
|
||||
{
|
||||
public static enum ControlAlgorithm
|
||||
{
|
||||
EULER_ANGELS_CONTROL, ANGULAR_SPEED_CONTROL
|
||||
}
|
||||
|
||||
public static enum CtrlState
|
||||
{
|
||||
DEFAULT, INIT, LANDED, FLYING, HOVERING, TEST, TRANS_TAKEOFF, TRANS_GOTOFIX, TRANS_LANDING;
|
||||
|
||||
public static CtrlState fromInt(int v) throws NavDataFormatException
|
||||
{
|
||||
switch(v)
|
||||
{
|
||||
case 0:
|
||||
return DEFAULT;
|
||||
case 1:
|
||||
return INIT;
|
||||
case 2:
|
||||
return LANDED;
|
||||
case 3:
|
||||
return FLYING;
|
||||
case 4:
|
||||
return HOVERING;
|
||||
case 5:
|
||||
return TEST;
|
||||
case 6:
|
||||
return TRANS_TAKEOFF;
|
||||
case 7:
|
||||
return TRANS_GOTOFIX;
|
||||
case 8:
|
||||
return TRANS_LANDING;
|
||||
default:
|
||||
throw new NavDataFormatException("Invalid control state " + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static enum FlyingState
|
||||
{
|
||||
FLYING, TAKING_OFF, LANDING, LANDED;
|
||||
|
||||
public static FlyingState fromControlState(CtrlState state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case FLYING:
|
||||
case HOVERING:
|
||||
case TRANS_GOTOFIX:
|
||||
return FlyingState.FLYING;
|
||||
|
||||
case TRANS_TAKEOFF:
|
||||
return FlyingState.TAKING_OFF;
|
||||
|
||||
case TRANS_LANDING:
|
||||
return FlyingState.LANDING;
|
||||
|
||||
default:
|
||||
return FlyingState.LANDED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static enum Mode
|
||||
{
|
||||
BOOTSTRAP, DEMO
|
||||
}
|
||||
|
||||
public static enum NavDataTag
|
||||
{
|
||||
NAVDATA_DEMO_TAG(0), NAVDATA_TIME_TAG(1), NAVDATA_RAW_MEASURES_TAG(2), NAVDATA_PHYS_MEASURES_TAG(3), NAVDATA_GYROS_OFFSETS_TAG(
|
||||
4), NAVDATA_EULER_ANGLES_TAG(5), NAVDATA_REFERENCES_TAG(6), NAVDATA_TRIMS_TAG(7), NAVDATA_RC_REFERENCES_TAG(
|
||||
8), NAVDATA_PWM_TAG(9), NAVDATA_ALTITUDE_TAG(10), NAVDATA_VISION_RAW_TAG(11), NAVDATA_VISION_OF_TAG(12), NAVDATA_VISION_TAG(
|
||||
13), NAVDATA_VISION_PERF_TAG(14), NAVDATA_TRACKERS_SEND_TAG(15), NAVDATA_VISION_DETECT_TAG(16), NAVDATA_WATCHDOG_TAG(
|
||||
17), NAVDATA_ADC_DATA_FRAME_TAG(18), NAVDATA_VIDEO_STREAM_TAG(19), NAVDATA_CKS_TAG(0xFFFF);
|
||||
|
||||
private int value;
|
||||
|
||||
private NavDataTag(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger log = Logger.getLogger(NavData.class.getName());
|
||||
|
||||
public static float byteArrayToFloat(byte[] b, int offset)
|
||||
{
|
||||
return Float.intBitsToFloat(byteArrayToInt(b, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the byte array to an int.
|
||||
*
|
||||
* @param b The byte array
|
||||
* @return The integer
|
||||
*/
|
||||
public static int byteArrayToInt(byte[] b)
|
||||
{
|
||||
return byteArrayToInt(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the byte array to an int starting from the given offset.
|
||||
*
|
||||
* @param b The byte array
|
||||
* @param offset The array offset
|
||||
* @return The integer
|
||||
*/
|
||||
public static int byteArrayToInt(byte[] b, int offset)
|
||||
{
|
||||
int value = 0;
|
||||
for(int i = 3; i >= 0; i--)
|
||||
{
|
||||
int shift = i * 8;
|
||||
value += (b[i + offset] & 0x000000FF) << shift;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the byte array to an int starting from the given offset.
|
||||
*
|
||||
* @param b The byte array
|
||||
* @param offset The array offset
|
||||
* @return The short
|
||||
*/
|
||||
public static int byteArrayToShort(byte[] b, int offset)
|
||||
{
|
||||
return ((b[offset + 1] & 0x000000FF) << 8) + (b[offset] & 0x000000FF);
|
||||
}
|
||||
|
||||
public static NavData createFromData(byte[] buf) throws NavDataFormatException
|
||||
{
|
||||
log.debug("Parsing navdata len=" + buf.length);
|
||||
NavData data = new NavData();
|
||||
data.mode = NavData.Mode.BOOTSTRAP; // Assume we are in bootstrap
|
||||
|
||||
int offset = 0;
|
||||
|
||||
int header = byteArrayToInt(buf, offset);
|
||||
if(header != 0x55667788)
|
||||
throw new NavDataFormatException("Error parsing NavData");
|
||||
offset += 4;
|
||||
|
||||
int state = byteArrayToInt(buf, offset);
|
||||
offset += 4;
|
||||
|
||||
parseState(data, state);
|
||||
|
||||
data.sequence = byteArrayToInt(buf, offset);
|
||||
offset += 4;
|
||||
|
||||
// int vision_flag = byteArrayToInt(buf, offset);
|
||||
offset += 4;
|
||||
|
||||
// Read options
|
||||
while(offset < buf.length)
|
||||
{
|
||||
int option_tag = byteArrayToShort(buf, offset);
|
||||
offset += 2;
|
||||
int option_len = byteArrayToShort(buf, offset);
|
||||
offset += 2;
|
||||
|
||||
if(option_len == 0)
|
||||
throw new NavDataFormatException("Zero-len option with tag " + option_tag);
|
||||
|
||||
// log.debug("At offset " + (offset - 4) + " found option " +
|
||||
// option_tag + " with len=" + option_len);
|
||||
|
||||
if(option_tag == NavDataTag.NAVDATA_DEMO_TAG.getValue())
|
||||
{
|
||||
parseDemoNavData(data, buf, offset);
|
||||
data.mode = NavData.Mode.DEMO;
|
||||
} else if(option_tag == NavDataTag.NAVDATA_CKS_TAG.getValue())
|
||||
{
|
||||
// this is last tag. We do not unpack it yet, but we gracefully
|
||||
// exit if it has been encountered.
|
||||
break;
|
||||
} else if(option_tag == NavDataTag.NAVDATA_VISION_DETECT_TAG.getValue())
|
||||
{
|
||||
List<VisionTag> vtags = parseVisionTags(data, buf, offset);
|
||||
if(vtags != null)
|
||||
data.setVisionTags(vtags);
|
||||
} else
|
||||
{
|
||||
log.warn("Skipping unknown NavData option with tag=" + option_tag);
|
||||
}
|
||||
offset = offset + option_len - 4;
|
||||
}
|
||||
|
||||
// TODO: calculate checksum
|
||||
log.debug("Got Nav data. mode " + data.mode);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static List<VisionTag> parseVisionTags(NavData data, byte[] buf, int offset) throws NavDataFormatException
|
||||
{
|
||||
int nb_detected = byteArrayToInt(buf, offset);
|
||||
offset += 4;
|
||||
|
||||
if(nb_detected!=0)
|
||||
log.debug("" + nb_detected + " vision tags detected");
|
||||
|
||||
if(nb_detected == 0)
|
||||
return null;
|
||||
|
||||
assert (nb_detected > 0);
|
||||
List<VisionTag> res = new ArrayList<VisionTag>(nb_detected);
|
||||
for(int i = 0; i < nb_detected; i++)
|
||||
{
|
||||
int type = byteArrayToInt(buf, offset + 4 * i);
|
||||
int xc = byteArrayToInt(buf, offset + 4 * i + 1 * nb_detected * 4);
|
||||
int yc = byteArrayToInt(buf, offset + 4 * i + 2 * nb_detected * 4);
|
||||
int width = byteArrayToInt(buf, offset + 4 * i + 3 * nb_detected * 4);
|
||||
int height = byteArrayToInt(buf, offset + 4 * i + 4 * nb_detected * 4);
|
||||
int dist = byteArrayToInt(buf, offset + 4 * i + 5 * nb_detected * 4);
|
||||
|
||||
VisionTag vt = new VisionTag(VisionTagType.fromInt(type), new Point(xc, yc), new Dimension(width, height),
|
||||
dist);
|
||||
log.debug("Vision#" + i + " " + vt.toString());
|
||||
res.add(vt);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void parseDemoNavData(NavData data, byte[] buf, int offset) throws NavDataFormatException
|
||||
{
|
||||
data.ctrl_state = CtrlState.fromInt(byteArrayToInt(buf, offset) >> 16);
|
||||
log.debug("Ctrl State " + data.ctrl_state);
|
||||
|
||||
offset += 4;
|
||||
data.battery = byteArrayToInt(buf, offset);
|
||||
offset += 4;
|
||||
data.pitch = byteArrayToFloat(buf, offset) / 1000;
|
||||
offset += 4;
|
||||
data.roll = byteArrayToFloat(buf, offset) / 1000;
|
||||
offset += 4;
|
||||
data.yaw = byteArrayToFloat(buf, offset) / 1000;
|
||||
offset += 4;
|
||||
data.altitude = ((float) byteArrayToInt(buf, offset)) / 1000;
|
||||
offset += 4;
|
||||
data.vx = byteArrayToFloat(buf, offset);
|
||||
offset += 4;
|
||||
data.vy = byteArrayToFloat(buf, offset);
|
||||
offset += 4;
|
||||
data.vz = byteArrayToFloat(buf, offset);
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
private static void parseState(NavData data, int state)
|
||||
{
|
||||
data.flying = (state & 1) != 0;
|
||||
data.videoEnabled = (state & (1 << 1)) != 0;
|
||||
data.visionEnabled = (state & (1 << 2)) != 0;
|
||||
data.controlAlgorithm = (state & (1 << 3)) != 0 ? ControlAlgorithm.ANGULAR_SPEED_CONTROL
|
||||
: ControlAlgorithm.EULER_ANGELS_CONTROL;
|
||||
data.altitudeControlActive = (state & (1 << 4)) != 0;
|
||||
data.userFeedbackOn = (state & (1 << 5)) != 0;
|
||||
data.controlReceived = (state & (1 << 6)) != 0;
|
||||
data.trimReceived = (state & (1 << 7)) != 0;
|
||||
data.trimRunning = (state & (1 << 8)) != 0;
|
||||
data.trimSucceeded = (state & (1 << 9)) != 0;
|
||||
data.navDataDemoOnly = (state & (1 << 10)) != 0;
|
||||
data.navDataBootstrap = (state & (1 << 11)) != 0;
|
||||
data.motorsDown = (state & (1 << 12)) != 0;
|
||||
data.gyrometersDown = (state & (1 << 14)) != 0;
|
||||
data.batteryTooLow = (state & (1 << 15)) != 0;
|
||||
data.batteryTooHigh = (state & (1 << 16)) != 0;
|
||||
data.timerElapsed = (state & (1 << 17)) != 0;
|
||||
data.notEnoughPower = (state & (1 << 18)) != 0;
|
||||
data.angelsOutOufRange = (state & (1 << 19)) != 0;
|
||||
data.tooMuchWind = (state & (1 << 20)) != 0;
|
||||
data.ultrasonicSensorDeaf = (state & (1 << 21)) != 0;
|
||||
data.cutoutSystemDetected = (state & (1 << 22)) != 0;
|
||||
data.PICVersionNumberOK = (state & (1 << 23)) != 0;
|
||||
data.ATCodedThreadOn = (state & (1 << 24)) != 0;
|
||||
data.navDataThreadOn = (state & (1 << 25)) != 0;
|
||||
data.videoThreadOn = (state & (1 << 26)) != 0;
|
||||
data.acquisitionThreadOn = (state & (1 << 27)) != 0;
|
||||
data.controlWatchdogDelayed = (state & (1 << 28)) != 0;
|
||||
data.ADCWatchdogDelayed = (state & (1 << 29)) != 0;
|
||||
data.communicationProblemOccurred = (state & (1 << 30)) != 0;
|
||||
data.emergency = (state & (1 << 31)) != 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void printState(NavData data)
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
sb.append("IsFlying: " + data.isFlying() + "\n");
|
||||
sb.append("IsVideoEnabled: " + data.isVideoEnabled() + "\n");
|
||||
sb.append("IsVisionEnabled: " + data.isVisionEnabled() + "\n");
|
||||
sb.append("controlAlgo: " + data.getControlAlgorithm() + "\n");
|
||||
sb.append("AltitudeControlActive: " + data.isAltitudeControlActive() + "\n");
|
||||
sb.append("IsUserFeedbackOn: " + data.isUserFeedbackOn() + "\n");
|
||||
sb.append("ControlReceived: " + data.isVideoEnabled() + "\n");
|
||||
sb.append("IsTrimReceived: " + data.isTrimReceived() + "\n");
|
||||
sb.append("IsTrimRunning: " + data.isTrimRunning() + "\n");
|
||||
sb.append("IsTrimSucceeded: " + data.isTrimSucceeded() + "\n");
|
||||
sb.append("IsNavDataDemoOnly: " + data.isNavDataDemoOnly() + "\n");
|
||||
sb.append("IsNavDataBootstrap: " + data.isNavDataBootstrap() + "\n");
|
||||
sb.append("IsMotorsDown: " + data.isMotorsDown() + "\n");
|
||||
sb.append("IsGyrometersDown: " + data.isGyrometersDown() + "\n");
|
||||
sb.append("IsBatteryLow: " + data.isBatteryTooLow() + "\n");
|
||||
sb.append("IsBatteryHigh: " + data.isBatteryTooHigh() + "\n");
|
||||
sb.append("IsTimerElapsed: " + data.isTimerElapsed() + "\n");
|
||||
sb.append("isNotEnoughPower: " + data.isNotEnoughPower() + "\n");
|
||||
sb.append("isAngelsOutOufRange: " + data.isAngelsOutOufRange() + "\n");
|
||||
sb.append("isTooMuchWind: " + data.isTooMuchWind() + "\n");
|
||||
sb.append("isUltrasonicSensorDeaf: " + data.isUltrasonicSensorDeaf() + "\n");
|
||||
sb.append("isCutoutSystemDetected: " + data.isCutoutSystemDetected() + "\n");
|
||||
sb.append("isPICVersionNumberOK: " + data.isPICVersionNumberOK() + "\n");
|
||||
sb.append("isATCodedThreadOn: " + data.isATCodedThreadOn() + "\n");
|
||||
sb.append("isNavDataThreadOn: " + data.isNavDataThreadOn() + "\n");
|
||||
sb.append("isVideoThreadOn: " + data.isVideoThreadOn() + "\n");
|
||||
sb.append("isAcquisitionThreadOn: " + data.isAcquisitionThreadOn() + "\n");
|
||||
sb.append("isControlWatchdogDelayed: " + data.isControlWatchdogDelayed() + "\n");
|
||||
sb.append("isADCWatchdogDelayed: " + data.isADCWatchdogDelayed() + "\n");
|
||||
sb.append("isCommunicationProblemOccurred: " + data.isCommunicationProblemOccurred() + "\n");
|
||||
sb.append("IsEmergency: " + data.isEmergency() + "\n");
|
||||
sb.append("CtrlState: " + data.getControlState() + "\n");
|
||||
sb.append("Battery: " + data.getBattery() + "\n");
|
||||
sb.append("Altidtude: " + data.getAltitude() + "\n");
|
||||
sb.append("Pitch: " + data.getPitch() + "\n");
|
||||
sb.append("Roll: " + data.getRoll() + "\n");
|
||||
sb.append("Yaw: " + data.getYaw() + "\n");
|
||||
sb.append("X velocity: " + data.getVx() + "\n");
|
||||
sb.append("Y velocity: " + data.getLongitude() + "\n");
|
||||
sb.append("Z velocity: " + data.getVz() + "\n");
|
||||
sb.append("Vision Tags: " + data.getVisionTags() + "\n");
|
||||
|
||||
log.debug("State: " + sb.toString());
|
||||
}
|
||||
|
||||
protected Mode mode;
|
||||
|
||||
// state flags
|
||||
protected boolean flying;
|
||||
protected boolean videoEnabled;
|
||||
protected boolean visionEnabled;
|
||||
protected ControlAlgorithm controlAlgorithm;
|
||||
protected boolean altitudeControlActive;
|
||||
protected boolean userFeedbackOn; // /TODO better
|
||||
// name
|
||||
protected boolean controlReceived;
|
||||
protected boolean trimReceived;
|
||||
protected boolean trimRunning;
|
||||
protected boolean trimSucceeded;
|
||||
protected boolean navDataDemoOnly;
|
||||
protected boolean navDataBootstrap;
|
||||
protected boolean motorsDown;
|
||||
protected boolean gyrometersDown;
|
||||
protected boolean batteryTooLow;
|
||||
protected boolean batteryTooHigh;
|
||||
protected boolean timerElapsed;
|
||||
protected boolean notEnoughPower;
|
||||
protected boolean angelsOutOufRange;
|
||||
protected boolean tooMuchWind;
|
||||
protected boolean ultrasonicSensorDeaf;
|
||||
protected boolean cutoutSystemDetected;
|
||||
protected boolean PICVersionNumberOK;
|
||||
protected boolean ATCodedThreadOn;
|
||||
protected boolean navDataThreadOn;
|
||||
protected boolean videoThreadOn;
|
||||
protected boolean acquisitionThreadOn;
|
||||
protected boolean controlWatchdogDelayed;
|
||||
protected boolean ADCWatchdogDelayed;
|
||||
protected boolean communicationProblemOccurred;
|
||||
protected boolean emergency;
|
||||
|
||||
// Common nav data
|
||||
protected int sequence;
|
||||
|
||||
// Demo nav data
|
||||
protected CtrlState ctrl_state;
|
||||
protected int battery;
|
||||
protected float altitude;
|
||||
protected float pitch;
|
||||
protected float roll;
|
||||
protected float yaw;
|
||||
protected float vx;
|
||||
protected float vy;
|
||||
protected float vz;
|
||||
|
||||
// Vision tags data
|
||||
protected List<VisionTag> vision_tags;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return value in meters
|
||||
*/
|
||||
public float getAltitude()
|
||||
{
|
||||
return altitude;
|
||||
}
|
||||
|
||||
public int getBattery()
|
||||
{
|
||||
return battery;
|
||||
}
|
||||
|
||||
public ControlAlgorithm getControlAlgorithm()
|
||||
{
|
||||
return controlAlgorithm;
|
||||
}
|
||||
|
||||
public CtrlState getControlState()
|
||||
{
|
||||
return ctrl_state;
|
||||
}
|
||||
|
||||
public float getLongitude()
|
||||
{
|
||||
return vy;
|
||||
}
|
||||
|
||||
public Mode getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return value in degrees
|
||||
*/
|
||||
public float getPitch()
|
||||
{
|
||||
return pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return value in degrees
|
||||
*/
|
||||
public float getRoll()
|
||||
{
|
||||
return roll;
|
||||
}
|
||||
|
||||
public int getSequence()
|
||||
{
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public float getVx()
|
||||
{
|
||||
return vx;
|
||||
}
|
||||
|
||||
public float getVz()
|
||||
{
|
||||
return vz;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return value in degrees
|
||||
*/
|
||||
public float getYaw()
|
||||
{
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public boolean isAcquisitionThreadOn()
|
||||
{
|
||||
return acquisitionThreadOn;
|
||||
}
|
||||
|
||||
public boolean isADCWatchdogDelayed()
|
||||
{
|
||||
return ADCWatchdogDelayed;
|
||||
}
|
||||
|
||||
public boolean isAltitudeControlActive()
|
||||
{
|
||||
return altitudeControlActive;
|
||||
}
|
||||
|
||||
public boolean isAngelsOutOufRange()
|
||||
{
|
||||
return angelsOutOufRange;
|
||||
}
|
||||
|
||||
public boolean isATCodedThreadOn()
|
||||
{
|
||||
return ATCodedThreadOn;
|
||||
}
|
||||
|
||||
public boolean isBatteryTooHigh()
|
||||
{
|
||||
return batteryTooHigh;
|
||||
}
|
||||
|
||||
public boolean isBatteryTooLow()
|
||||
{
|
||||
return batteryTooLow;
|
||||
}
|
||||
|
||||
public boolean isCommunicationProblemOccurred()
|
||||
{
|
||||
return communicationProblemOccurred;
|
||||
}
|
||||
|
||||
public boolean isControlReceived()
|
||||
{
|
||||
return controlReceived;
|
||||
}
|
||||
|
||||
public boolean isControlWatchdogDelayed()
|
||||
{
|
||||
return controlWatchdogDelayed;
|
||||
}
|
||||
|
||||
public boolean isCutoutSystemDetected()
|
||||
{
|
||||
return cutoutSystemDetected;
|
||||
}
|
||||
|
||||
public boolean isEmergency()
|
||||
{
|
||||
return emergency;
|
||||
}
|
||||
|
||||
public boolean isFlying()
|
||||
{
|
||||
return flying;
|
||||
}
|
||||
|
||||
public boolean isGyrometersDown()
|
||||
{
|
||||
return gyrometersDown;
|
||||
}
|
||||
|
||||
public boolean isMotorsDown()
|
||||
{
|
||||
return motorsDown;
|
||||
}
|
||||
|
||||
public boolean isNavDataBootstrap()
|
||||
{
|
||||
return navDataBootstrap;
|
||||
}
|
||||
|
||||
public boolean isNavDataDemoOnly()
|
||||
{
|
||||
return navDataDemoOnly;
|
||||
}
|
||||
|
||||
public boolean isNavDataThreadOn()
|
||||
{
|
||||
return navDataThreadOn;
|
||||
}
|
||||
|
||||
public boolean isNotEnoughPower()
|
||||
{
|
||||
return notEnoughPower;
|
||||
}
|
||||
|
||||
public boolean isPICVersionNumberOK()
|
||||
{
|
||||
return PICVersionNumberOK;
|
||||
}
|
||||
|
||||
public boolean isTimerElapsed()
|
||||
{
|
||||
return timerElapsed;
|
||||
}
|
||||
|
||||
public boolean isTooMuchWind()
|
||||
{
|
||||
return tooMuchWind;
|
||||
}
|
||||
|
||||
public boolean isTrimReceived()
|
||||
{
|
||||
return trimReceived;
|
||||
}
|
||||
|
||||
public boolean isTrimRunning()
|
||||
{
|
||||
return trimRunning;
|
||||
}
|
||||
|
||||
public boolean isTrimSucceeded()
|
||||
{
|
||||
return trimSucceeded;
|
||||
}
|
||||
|
||||
public boolean isUltrasonicSensorDeaf()
|
||||
{
|
||||
return ultrasonicSensorDeaf;
|
||||
}
|
||||
|
||||
public boolean isUserFeedbackOn()
|
||||
{
|
||||
return userFeedbackOn;
|
||||
}
|
||||
|
||||
public boolean isVideoEnabled()
|
||||
{
|
||||
return videoEnabled;
|
||||
}
|
||||
|
||||
public boolean isVideoThreadOn()
|
||||
{
|
||||
return videoThreadOn;
|
||||
}
|
||||
|
||||
public boolean isVisionEnabled()
|
||||
{
|
||||
return visionEnabled;
|
||||
}
|
||||
|
||||
public FlyingState getFlyingState()
|
||||
{
|
||||
return FlyingState.fromControlState(ctrl_state);
|
||||
}
|
||||
|
||||
public List<VisionTag> getVisionTags()
|
||||
{
|
||||
return vision_tags;
|
||||
}
|
||||
|
||||
public void setVisionTags(List<VisionTag> vision_tags)
|
||||
{
|
||||
this.vision_tags = vision_tags;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NavDataFormatException extends Exception
|
||||
{
|
||||
|
||||
public NavDataFormatException(String why)
|
||||
{
|
||||
super(why);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public interface NavDataListener
|
||||
{
|
||||
void navDataReceived(NavData nd);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
|
||||
public class NavDataReader extends DataReader {
|
||||
|
||||
private static final int BUFSIZE = 4096;
|
||||
|
||||
public NavDataReader(ARDrone drone, InetAddress drone_addr, int navdata_port) throws IOException
|
||||
{
|
||||
super(drone, drone_addr, navdata_port, BUFSIZE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
void handleReceivedMessageKey(SelectionKey key, ByteBuffer inbuf) throws Exception
|
||||
{
|
||||
|
||||
if(key.isWritable())
|
||||
{
|
||||
byte[] trigger_bytes = {0x01, 0x00, 0x00, 0x00};
|
||||
ByteBuffer trigger_buf = ByteBuffer.allocate(trigger_bytes.length);
|
||||
trigger_buf.put(trigger_bytes);
|
||||
trigger_buf.flip();
|
||||
channel.write(trigger_buf);
|
||||
channel.register(selector, SelectionKey.OP_READ);
|
||||
} else if(key.isReadable())
|
||||
{
|
||||
inbuf.clear();
|
||||
int len = channel.read(inbuf);
|
||||
byte[] packet = new byte[len];
|
||||
inbuf.flip();
|
||||
inbuf.get(packet, 0, len);
|
||||
|
||||
NavData nd = NavData.createFromData(packet);
|
||||
|
||||
drone.navDataReceived(nd);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public class Point
|
||||
{
|
||||
private int x;
|
||||
private int y;
|
||||
|
||||
public Point(int x, int y)
|
||||
{
|
||||
super();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(int x)
|
||||
{
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getY()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(int y)
|
||||
{
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.*;
|
||||
|
||||
import com.codeminders.ardrone.video.*;
|
||||
|
||||
public class VideoReader extends DataReader {
|
||||
/**
|
||||
* Image data buffer. It should be big enough to hold single full frame
|
||||
* (encoded).
|
||||
*/
|
||||
private static final int BUFSIZE = 100 * 1024;
|
||||
|
||||
|
||||
public VideoReader(ARDrone drone, InetAddress drone_addr, int video_port) throws IOException
|
||||
{
|
||||
super(drone, drone_addr, video_port, BUFSIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleReceivedMessageKey(SelectionKey key, ByteBuffer inbuf)
|
||||
throws Exception {
|
||||
|
||||
if(key.isWritable())
|
||||
{
|
||||
byte[] trigger_bytes = { 0x01, 0x00, 0x00, 0x00 };
|
||||
ByteBuffer trigger_buf = ByteBuffer.allocate(trigger_bytes.length);
|
||||
trigger_buf.put(trigger_bytes);
|
||||
trigger_buf.flip();
|
||||
channel.write(trigger_buf);
|
||||
channel.register(selector, SelectionKey.OP_READ);
|
||||
} else if(key.isReadable())
|
||||
{
|
||||
inbuf.clear();
|
||||
int len = channel.read(inbuf);
|
||||
if(len > 0)
|
||||
{
|
||||
inbuf.flip();
|
||||
final BufferedVideoImage vi = new BufferedVideoImage();
|
||||
vi.addImageStream(inbuf);
|
||||
drone.videoFrameReceived(0, 0, vi.getWidth(), vi.getHeight(), vi.getJavaPixelData(), 0, vi.getWidth());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
|
||||
package com.codeminders.ardrone;
|
||||
|
||||
public class VisionTag
|
||||
{
|
||||
|
||||
public VisionTag(VisionTagType type, Point position, Dimension dimensions, int distance)
|
||||
{
|
||||
super();
|
||||
this.type = type;
|
||||
this.position = position;
|
||||
this.dimensions = dimensions;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Values for the detection type on drone cameras.
|
||||
*/
|
||||
public static enum VisionTagType
|
||||
{
|
||||
CAD_TYPE_HORIZONTL(0), /* Deprecated */
|
||||
CAD_TYPE_VERTICAL(1), /* Deprecated */
|
||||
CAD_TYPE_VISION(2), /* Detection of 2D horizontal tags on drone shells */
|
||||
CAD_TYPE_NONE(3), /* Detection disabled */
|
||||
CAD_TYPE_COCARDE(4), /* Detects a roundel under the drone */
|
||||
CAD_TYPE_ORIENTED_COCARDE(5), /*
|
||||
* Detects an oriented roundel under the
|
||||
* drone
|
||||
*/
|
||||
CAD_TYPE_STRIPE(6), /* Detects a uniform stripe on the ground */
|
||||
CAD_TYPE_H_COCARDE(7), /* Detects a roundel in front of the drone */
|
||||
CAD_TYPE_H_ORIENTED_COCARDE(8), /*
|
||||
* Detects an oriented roundel in front
|
||||
* of the drone
|
||||
*/
|
||||
CAD_TYPE_STRIPE_V(9), CAD_TYPE_MULTIPLE_DETECTION_MODE(10), /*
|
||||
* The drone
|
||||
* uses
|
||||
* several
|
||||
* detections
|
||||
* at the
|
||||
* same time
|
||||
*/
|
||||
CAD_TYPE_NUM(11); /* Number of possible values for CAD_TYPE */
|
||||
|
||||
private int value;
|
||||
|
||||
private VisionTagType(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public static VisionTagType fromInt(int v) throws NavDataFormatException
|
||||
{
|
||||
switch(v)
|
||||
{
|
||||
case 0:
|
||||
return VisionTagType.CAD_TYPE_HORIZONTL;
|
||||
case 1:
|
||||
return VisionTagType.CAD_TYPE_VERTICAL;
|
||||
case 2:
|
||||
return VisionTagType.CAD_TYPE_VISION;
|
||||
case 3:
|
||||
return VisionTagType.CAD_TYPE_NONE;
|
||||
case 4:
|
||||
return VisionTagType.CAD_TYPE_COCARDE;
|
||||
case 5:
|
||||
return VisionTagType.CAD_TYPE_ORIENTED_COCARDE;
|
||||
case 6:
|
||||
return VisionTagType.CAD_TYPE_STRIPE;
|
||||
case 7:
|
||||
return VisionTagType.CAD_TYPE_H_COCARDE;
|
||||
case 8:
|
||||
return VisionTagType.CAD_TYPE_H_ORIENTED_COCARDE;
|
||||
case 9:
|
||||
return VisionTagType.CAD_TYPE_STRIPE_V;
|
||||
case 10:
|
||||
return VisionTagType.CAD_TYPE_MULTIPLE_DETECTION_MODE;
|
||||
case 11:
|
||||
return VisionTagType.CAD_TYPE_NUM;
|
||||
default:
|
||||
throw new NavDataFormatException("Invalid vision tag type " + v);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Type of the detected tag #i ; see the CAD_TYPE enumeration.
|
||||
private VisionTagType type;
|
||||
|
||||
/**
|
||||
* X and Y coordinates of detected 2D-tag #i inside the picture, with (0, 0)
|
||||
* being the top-left corner, and (1000, 1000) the right-bottom corner
|
||||
* regardless the picture resolu- tion or the source camera.
|
||||
*/
|
||||
private Point position;
|
||||
|
||||
/**
|
||||
* Width and height of the detection bounding-box (2D-tag #i), when
|
||||
* applicable.
|
||||
*/
|
||||
private Dimension dimensions;
|
||||
|
||||
/**
|
||||
* Distance from camera to detected 2D-tag #i in centimeters, when
|
||||
* applicable.
|
||||
*/
|
||||
private int distance;
|
||||
|
||||
public VisionTagType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public Point getPosition()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public Dimension getDimensions()
|
||||
{
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
public int getDistance()
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("VisionTag [type=");
|
||||
builder.append(type);
|
||||
builder.append(", position=");
|
||||
builder.append(position);
|
||||
builder.append(", dimensions=");
|
||||
builder.append(dimensions);
|
||||
builder.append(", distance=");
|
||||
builder.append(distance);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import com.codeminders.ardrone.DroneCommand;
|
||||
|
||||
public abstract class ATCommand extends DroneCommand
|
||||
{
|
||||
private String encodeParameter(Object p)
|
||||
{
|
||||
if(p instanceof Integer)
|
||||
return p.toString();
|
||||
|
||||
if(p instanceof Float)
|
||||
return Integer.toString(Float.floatToIntBits((Float) p));
|
||||
|
||||
if(p instanceof String)
|
||||
return "\"" + p + "\"";
|
||||
|
||||
throw new IllegalArgumentException("Unsupported parameter type: " + p.getClass().getName() + " " + p);
|
||||
}
|
||||
|
||||
public String getCommandString(int seq)
|
||||
{
|
||||
return "AT*" + getID() + "=" + seq + getParametersString() + "\r";
|
||||
}
|
||||
|
||||
protected abstract String getID();
|
||||
|
||||
public byte[] getPacket(int seq)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getCommandString(seq).getBytes("ASCII");
|
||||
} catch(UnsupportedEncodingException e)
|
||||
{
|
||||
// never happens
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Object[] getParameters();
|
||||
|
||||
private String getParametersString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for(Object p : getParameters())
|
||||
{
|
||||
sb.append(',').append(encodeParameter(p));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return MIN_PRIORITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getClass().getSimpleName());
|
||||
builder.append(" [ID=");
|
||||
builder.append(getID());
|
||||
builder.append(", param=");
|
||||
builder.append(getParametersString());
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if(obj == null || !(obj instanceof ATCommand))
|
||||
return false;
|
||||
ATCommand o = (ATCommand) obj;
|
||||
return o.getCommandString(0).equals(getCommandString(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class ConfigureCommand extends ATCommand
|
||||
{
|
||||
protected String name;
|
||||
protected String value;
|
||||
|
||||
public ConfigureCommand(String name, String value)
|
||||
{
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "CONFIG";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] { name, value };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class ControlCommand extends ATCommand
|
||||
{
|
||||
protected int arg1;
|
||||
protected int arg2;
|
||||
|
||||
public ControlCommand(int arg1, int arg2)
|
||||
{
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "CTRL";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] { arg1, arg2 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class EmergencyCommand extends RefCommand
|
||||
{
|
||||
public EmergencyCommand()
|
||||
{
|
||||
value |= (1<<8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class FlatTrimCommand extends ATCommand
|
||||
{
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "FTRIM";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] {};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class HoverCommand extends PCMDCommand
|
||||
{
|
||||
public HoverCommand()
|
||||
{
|
||||
super(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class KeepAliveCommand extends ATCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "COMWDG";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return VERY_HIGH_PRIORITY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class LandCommand extends RefCommand
|
||||
{
|
||||
public LandCommand()
|
||||
{
|
||||
// 9th bit set to 0
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return HIGH_PRIORITY;
|
||||
}
|
||||
|
||||
public boolean isSticky()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getCategory()
|
||||
{
|
||||
return LAND_TAKEOFF_CATEGORY;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class MoveCommand extends PCMDCommand
|
||||
{
|
||||
public MoveCommand(boolean combined_yaw_enabled,
|
||||
float left_right_tilt,
|
||||
float front_back_tilt,
|
||||
float vertical_speed,
|
||||
float angular_speed)
|
||||
{
|
||||
super(false);
|
||||
this.combined_yaw_enabled = combined_yaw_enabled;
|
||||
this.left_right_tilt = left_right_tilt;
|
||||
this.front_back_tilt = front_back_tilt;
|
||||
this.vertical_speed = vertical_speed;
|
||||
this.angular_speed = angular_speed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class PCMDCommand extends ATCommand
|
||||
{
|
||||
protected boolean hover;
|
||||
protected boolean combined_yaw_enabled;
|
||||
protected float left_right_tilt;
|
||||
protected float front_back_tilt;
|
||||
protected float vertical_speed;
|
||||
protected float angular_speed;
|
||||
|
||||
|
||||
protected PCMDCommand(boolean hover)
|
||||
{
|
||||
this.hover = hover;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "PCMD";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
if(hover)
|
||||
return new Object[] { 0, 0f, 0f, 0f, 0f };
|
||||
|
||||
// int mode = combined_yaw_enabled ? 1 : 0;
|
||||
int mode = 1;
|
||||
if(combined_yaw_enabled)
|
||||
mode |= (1<<1);
|
||||
|
||||
return new Object[] { mode, left_right_tilt, front_back_tilt, vertical_speed, angular_speed };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class PlayAnimationCommand extends ATCommand
|
||||
{
|
||||
protected int animation_no;
|
||||
protected int duration;
|
||||
|
||||
public PlayAnimationCommand(int animation_no, int duration)
|
||||
{
|
||||
this.animation_no = animation_no;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "ANIM";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] { animation_no, duration };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class PlayLEDCommand extends ATCommand
|
||||
{
|
||||
protected int animation_no;
|
||||
protected float frequency;
|
||||
protected int duration;
|
||||
|
||||
public PlayLEDCommand(int animation_no, float frequency, int duration)
|
||||
{
|
||||
this.animation_no = animation_no;
|
||||
this.frequency = frequency;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "LED";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] { animation_no, frequency, duration };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
import com.codeminders.ardrone.DroneCommand;
|
||||
|
||||
public class QuitCommand extends DroneCommand
|
||||
{
|
||||
@Override
|
||||
public int getPriority()
|
||||
{
|
||||
return MAX_PRIORITY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class RefCommand extends ATCommand
|
||||
{
|
||||
protected int value;
|
||||
|
||||
protected RefCommand()
|
||||
{
|
||||
value |= (1 << 18) | (1 << 20) | (1 << 22) | (1 << 24) | (1 << 28) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getID()
|
||||
{
|
||||
return "REF";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] getParameters()
|
||||
{
|
||||
return new Object[] { value };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.codeminders.ardrone.commands;
|
||||
|
||||
public class TakeOffCommand extends RefCommand
|
||||
{
|
||||
public TakeOffCommand()
|
||||
{
|
||||
value |= (1<<9);
|
||||
}
|
||||
|
||||
public boolean isSticky()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getCategory()
|
||||
{
|
||||
return LAND_TAKEOFF_CATEGORY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
import com.codeminders.hidapi.*;
|
||||
|
||||
/**
|
||||
* "Afterglow" controller for PS3
|
||||
*
|
||||
* @author lord
|
||||
*
|
||||
*/
|
||||
public class AfterGlowController extends PS3Controller
|
||||
{
|
||||
private static final int VENDOR_ID = 3695;
|
||||
private static final int PRODUCT_ID = 25346;
|
||||
|
||||
private static final int BUFSIZE = 32;
|
||||
private static final int EXPECTED_BUFSIZE = 27;
|
||||
|
||||
public static boolean isA(HIDDeviceInfo hidDeviceInfo)
|
||||
{
|
||||
return(hidDeviceInfo.getVendor_id() == VENDOR_ID && hidDeviceInfo.getProduct_id() == PRODUCT_ID);
|
||||
}
|
||||
|
||||
private byte[] buf = new byte[BUFSIZE];
|
||||
|
||||
public AfterGlowController() throws HIDDeviceNotFoundException, IOException
|
||||
{
|
||||
dev = HIDManager.getInstance().openById(VENDOR_ID, PRODUCT_ID, null);
|
||||
dev.enableBlocking();
|
||||
}
|
||||
|
||||
public AfterGlowController(HIDDeviceInfo hidDeviceInfo) throws IOException
|
||||
{
|
||||
dev = hidDeviceInfo.open();
|
||||
dev.enableBlocking();
|
||||
}
|
||||
|
||||
private int joystickCoordConv(byte b)
|
||||
{
|
||||
int v = b < 0 ? b + 256 : b;
|
||||
return v - 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized PS3ControllerState read() throws IOException
|
||||
{
|
||||
int n = dev.read(buf);
|
||||
if(n != EXPECTED_BUFSIZE)
|
||||
{
|
||||
throw new IOException("Received packed with unexpected size " + n);
|
||||
}
|
||||
|
||||
BitSet bs = new BitSet(13);
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if((1 & (buf[0] >> i)) == 1)
|
||||
bs.set(i);
|
||||
}
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
if((1 & (buf[1] >> i)) == 1)
|
||||
bs.set(8 + i);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
boolean square = bs.get(i++);
|
||||
boolean cross = bs.get(i++);
|
||||
boolean circle = bs.get(i++);
|
||||
boolean triangle = bs.get(i++);
|
||||
boolean L1 = bs.get(i++);
|
||||
boolean R1 = bs.get(i++);
|
||||
boolean L2 = bs.get(i++);
|
||||
boolean R2 = bs.get(i++);
|
||||
boolean select = bs.get(i++);
|
||||
boolean start = bs.get(i++);
|
||||
boolean leftJoystickPress = bs.get(i++);
|
||||
boolean rightJoystickPress = bs.get(i++);
|
||||
boolean PS = bs.get(i++);
|
||||
|
||||
int leftJoystickX = joystickCoordConv(buf[3]);
|
||||
int leftJoystickY = joystickCoordConv(buf[4]);
|
||||
int rightJoystickX = joystickCoordConv(buf[5]);
|
||||
int rightJoystickY = joystickCoordConv(buf[6]);
|
||||
|
||||
// TODO: decode HAT switch
|
||||
int hatSwitchLeftRight = 0;
|
||||
int hatSwitchUpDown = 0;
|
||||
|
||||
PS3ControllerState res = new PS3ControllerState(square, cross, circle, triangle, L1, R1, L2, R2, select, start,
|
||||
leftJoystickPress, rightJoystickPress, PS, hatSwitchLeftRight, hatSwitchUpDown, leftJoystickX,
|
||||
leftJoystickY, rightJoystickX, rightJoystickY);
|
||||
|
||||
// System.err.println(res.toString());
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
public class KeyboardController extends PS3Controller implements KeyListener
|
||||
{
|
||||
PS3ControllerState state = new PS3ControllerState();
|
||||
|
||||
public KeyboardController(JFrame frame)
|
||||
{
|
||||
frame.addKeyListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PS3ControllerState read() throws IOException
|
||||
{
|
||||
PS3ControllerState s;
|
||||
synchronized(state)
|
||||
{
|
||||
s = new PS3ControllerState(state);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
synchronized(state)
|
||||
{
|
||||
mapBooleanKey(e, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
synchronized(state)
|
||||
{
|
||||
mapBooleanKey(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void mapBooleanKey(KeyEvent e, boolean value)
|
||||
{
|
||||
int c = e.getKeyCode();
|
||||
switch(c)
|
||||
{
|
||||
case KeyEvent.VK_ENTER:
|
||||
state.start = value;
|
||||
break;
|
||||
case KeyEvent.VK_SPACE:
|
||||
state.select = value;
|
||||
break;
|
||||
case KeyEvent.VK_R:
|
||||
state.PS = value;
|
||||
break;
|
||||
case KeyEvent.VK_C:
|
||||
state.triangle = value;
|
||||
break;
|
||||
case KeyEvent.VK_F:
|
||||
state.square = value;
|
||||
break;
|
||||
case KeyEvent.VK_E:
|
||||
state.circle = value;
|
||||
break;
|
||||
case KeyEvent.VK_W:
|
||||
state.leftJoystickY = value ? -127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_A:
|
||||
state.leftJoystickX = value ? -127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_S:
|
||||
state.leftJoystickY = value ? 127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_D:
|
||||
state.leftJoystickX = value ? 127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_UP:
|
||||
state.rightJoystickY = value ? -127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_LEFT:
|
||||
state.rightJoystickX = value ? -127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
state.rightJoystickY = value ? 127 : 0;
|
||||
break;
|
||||
case KeyEvent.VK_RIGHT:
|
||||
state.rightJoystickX = value ? 127 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.codeminders.hidapi.HIDDevice;
|
||||
|
||||
/**
|
||||
* Base abstract class for supported PS3-compatible USB controllers
|
||||
*
|
||||
* @author lord
|
||||
*
|
||||
*/
|
||||
public abstract class PS3Controller
|
||||
{
|
||||
protected HIDDevice dev;
|
||||
|
||||
protected static void printDelta(byte[] prev, int prev_size, byte[] cur, int cur_size)
|
||||
{
|
||||
if(prev_size != cur_size)
|
||||
{
|
||||
System.err.println("Packet size is different. Prev: " + prev_size + " New: " + cur_size);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < prev_size; i++)
|
||||
{
|
||||
if(prev[i] != cur[i])
|
||||
{
|
||||
System.err.println("Index: " + i + " Prev value: " + Integer.toHexString((int) prev[i])
|
||||
+ " New value: " + Integer.toHexString((int) cur[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
if(dev != null)
|
||||
dev.close();
|
||||
}
|
||||
|
||||
public abstract PS3ControllerState read() throws IOException;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("PS3Controller [");
|
||||
try
|
||||
{
|
||||
builder.append("manufacturer=");
|
||||
builder.append(dev.getManufacturerString());
|
||||
builder.append(", product=");
|
||||
builder.append(dev.getProductString());
|
||||
} catch(IOException e)
|
||||
{
|
||||
builder.append("<invalid>");
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
/**
|
||||
* Data structure describing state of generic PS-3 compatible game controller.
|
||||
*
|
||||
* @author lord
|
||||
*/
|
||||
public class PS3ControllerState
|
||||
{
|
||||
// buttons with pictures
|
||||
protected boolean square;
|
||||
protected boolean cross;
|
||||
protected boolean circle;
|
||||
protected boolean triangle;
|
||||
|
||||
// Front-side buttons
|
||||
protected boolean L1;
|
||||
protected boolean R1;
|
||||
protected boolean L2;
|
||||
protected boolean R2;
|
||||
|
||||
// square small "select" button
|
||||
protected boolean select;
|
||||
// triangular small "start" button
|
||||
protected boolean start;
|
||||
|
||||
// Pressing on joysticks (button)
|
||||
protected boolean leftJoystickPress;
|
||||
protected boolean rightJoystickPress;
|
||||
|
||||
// PS3 button (sometimes labeled as Home on 3rd party models)
|
||||
protected boolean PS;
|
||||
|
||||
// Direction pad (hatswitch)
|
||||
protected int hatSwitchLeftRight;
|
||||
protected int hatSwitchUpDown;
|
||||
|
||||
// Analog joysticks
|
||||
|
||||
protected int leftJoystickX;
|
||||
protected int leftJoystickY;
|
||||
|
||||
protected int rightJoystickX;
|
||||
protected int rightJoystickY;
|
||||
|
||||
public PS3ControllerState(boolean square, boolean cross, boolean circle, boolean triangle, boolean l1, boolean r1,
|
||||
boolean l2, boolean r2, boolean select, boolean start, boolean leftJoystickPress,
|
||||
boolean rightJoystickPress, boolean pS, int hatSwitchLeftRight, int hatSwitchUpDown, int leftJoystickX,
|
||||
int leftJoystickY, int rightJoystickX, int rightJoystickY)
|
||||
{
|
||||
this.square = square;
|
||||
this.cross = cross;
|
||||
this.circle = circle;
|
||||
this.triangle = triangle;
|
||||
L1 = l1;
|
||||
R1 = r1;
|
||||
L2 = l2;
|
||||
R2 = r2;
|
||||
this.select = select;
|
||||
this.start = start;
|
||||
this.leftJoystickPress = leftJoystickPress;
|
||||
this.rightJoystickPress = rightJoystickPress;
|
||||
PS = pS;
|
||||
this.hatSwitchLeftRight = hatSwitchLeftRight;
|
||||
this.hatSwitchUpDown = hatSwitchUpDown;
|
||||
this.leftJoystickX = leftJoystickX;
|
||||
this.leftJoystickY = leftJoystickY;
|
||||
this.rightJoystickX = rightJoystickX;
|
||||
this.rightJoystickY = rightJoystickY;
|
||||
}
|
||||
|
||||
public PS3ControllerState(PS3ControllerState o)
|
||||
{
|
||||
this.square = o.square;
|
||||
this.cross = o.cross;
|
||||
this.circle = o.circle;
|
||||
this.triangle = o.triangle;
|
||||
L1 = o.L1;
|
||||
R1 = o.R1;
|
||||
L2 = o.L2;
|
||||
R2 = o.R2;
|
||||
this.select = o.select;
|
||||
this.start = o.start;
|
||||
this.leftJoystickPress = o.leftJoystickPress;
|
||||
this.rightJoystickPress = o.rightJoystickPress;
|
||||
PS = o.PS;
|
||||
this.hatSwitchLeftRight = o.hatSwitchLeftRight;
|
||||
this.hatSwitchUpDown = o.hatSwitchUpDown;
|
||||
this.leftJoystickX = o.leftJoystickX;
|
||||
this.leftJoystickY = o.leftJoystickY;
|
||||
this.rightJoystickX = o.rightJoystickX;
|
||||
this.rightJoystickY = o.rightJoystickY;
|
||||
|
||||
}
|
||||
|
||||
public PS3ControllerState()
|
||||
{
|
||||
}
|
||||
|
||||
public int getHatSwitchLeftRight()
|
||||
{
|
||||
return hatSwitchLeftRight;
|
||||
}
|
||||
|
||||
public int getHatSwitchUpDown()
|
||||
{
|
||||
return hatSwitchUpDown;
|
||||
}
|
||||
|
||||
public int getLeftJoystickX()
|
||||
{
|
||||
return leftJoystickX;
|
||||
}
|
||||
|
||||
public int getLeftJoystickY()
|
||||
{
|
||||
return leftJoystickY;
|
||||
}
|
||||
|
||||
public int getRightJoystickX()
|
||||
{
|
||||
return rightJoystickX;
|
||||
}
|
||||
|
||||
public int getRightJoystickY()
|
||||
{
|
||||
return rightJoystickY;
|
||||
}
|
||||
|
||||
public boolean isCircle()
|
||||
{
|
||||
return circle;
|
||||
}
|
||||
|
||||
public boolean isCross()
|
||||
{
|
||||
return cross;
|
||||
}
|
||||
|
||||
public boolean isL1()
|
||||
{
|
||||
return L1;
|
||||
}
|
||||
|
||||
public boolean isL2()
|
||||
{
|
||||
return L2;
|
||||
}
|
||||
|
||||
public boolean isLeftJoystickPress()
|
||||
{
|
||||
return leftJoystickPress;
|
||||
}
|
||||
|
||||
public boolean isPS()
|
||||
{
|
||||
return PS;
|
||||
}
|
||||
|
||||
public boolean isR1()
|
||||
{
|
||||
return R1;
|
||||
}
|
||||
|
||||
public boolean isR2()
|
||||
{
|
||||
return R2;
|
||||
}
|
||||
|
||||
public boolean isRightJoystickPress()
|
||||
{
|
||||
return rightJoystickPress;
|
||||
}
|
||||
|
||||
public boolean isSelect()
|
||||
{
|
||||
return select;
|
||||
}
|
||||
|
||||
public boolean isSquare()
|
||||
{
|
||||
return square;
|
||||
}
|
||||
|
||||
public boolean isStart()
|
||||
{
|
||||
return start;
|
||||
}
|
||||
|
||||
public boolean isTriangle()
|
||||
{
|
||||
return triangle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("PS3ControllerState [square=");
|
||||
builder.append(square);
|
||||
builder.append(", cross=");
|
||||
builder.append(cross);
|
||||
builder.append(", circle=");
|
||||
builder.append(circle);
|
||||
builder.append(", triangle=");
|
||||
builder.append(triangle);
|
||||
builder.append(", L1=");
|
||||
builder.append(L1);
|
||||
builder.append(", R1=");
|
||||
builder.append(R1);
|
||||
builder.append(", L2=");
|
||||
builder.append(L2);
|
||||
builder.append(", R2=");
|
||||
builder.append(R2);
|
||||
builder.append(", select=");
|
||||
builder.append(select);
|
||||
builder.append(", start=");
|
||||
builder.append(start);
|
||||
builder.append(", rightJoystickPress=");
|
||||
builder.append(rightJoystickPress);
|
||||
builder.append(", leftJoystickPress=");
|
||||
builder.append(leftJoystickPress);
|
||||
builder.append(", PS=");
|
||||
builder.append(PS);
|
||||
builder.append(", hatSwitchLeftRight=");
|
||||
builder.append(hatSwitchLeftRight);
|
||||
builder.append(", hatSwitchUpDown=");
|
||||
builder.append(hatSwitchUpDown);
|
||||
builder.append(", leftJoystickX=");
|
||||
builder.append(leftJoystickX);
|
||||
builder.append(", leftJoystickY=");
|
||||
builder.append(leftJoystickY);
|
||||
builder.append(", rightJoystickX=");
|
||||
builder.append(rightJoystickX);
|
||||
builder.append(", rightJoystickY=");
|
||||
builder.append(rightJoystickY);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
public class PS3ControllerStateChange extends PS3ControllerState
|
||||
{
|
||||
protected boolean squareChanged;
|
||||
protected boolean crossChanged;
|
||||
protected boolean circleChanged;
|
||||
protected boolean triangleChanged;
|
||||
protected boolean L1Changed;
|
||||
protected boolean R1Changed;
|
||||
protected boolean L2Changed;
|
||||
protected boolean R2Changed;
|
||||
protected boolean selectChanged;
|
||||
protected boolean startChanged;
|
||||
protected boolean leftJoystickPressChanged;
|
||||
protected boolean rightJoystickPressChanged;
|
||||
protected boolean PSChanged;
|
||||
protected int hatSwitchLeftRightChange;
|
||||
protected int hatSwitchUpDownChange;
|
||||
protected int leftJoystickXChange;
|
||||
protected int leftJoystickYChange;
|
||||
protected int rightJoystickXChange;
|
||||
protected int rightJoystickYChange;
|
||||
|
||||
// composite change flags
|
||||
private boolean buttonStateChanged;
|
||||
private boolean hatChanged;
|
||||
private boolean leftJoystickChanged;
|
||||
private boolean rightJoystickChanged;
|
||||
private boolean joysticksChanged;
|
||||
private boolean changed;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param o Old state
|
||||
* @param n New state
|
||||
*/
|
||||
public PS3ControllerStateChange(PS3ControllerState o, PS3ControllerState n)
|
||||
{
|
||||
super(n);
|
||||
|
||||
if(o==null)
|
||||
o=n;
|
||||
|
||||
squareChanged = o.square != n.square;
|
||||
crossChanged = o.cross != n.cross;
|
||||
circleChanged = o.circle != n.circle;
|
||||
triangleChanged = o.triangle != n.triangle;
|
||||
L1Changed = o.L1 != n.L1;
|
||||
R1Changed = o.R1 != n.R1;
|
||||
L2Changed = o.L2 != n.L2;
|
||||
R2Changed = o.R2 != n.R2;
|
||||
selectChanged = o.select != n.select;
|
||||
startChanged = o.start != n.start;
|
||||
leftJoystickPressChanged = o.leftJoystickPress != n.leftJoystickPress;
|
||||
rightJoystickPressChanged = o.rightJoystickPress != n.rightJoystickPress;
|
||||
PSChanged = o.PS != n.PS;
|
||||
|
||||
hatSwitchLeftRightChange = n.hatSwitchLeftRight - o.hatSwitchLeftRight;
|
||||
hatSwitchUpDownChange = n.hatSwitchUpDown - o.hatSwitchUpDown;
|
||||
|
||||
leftJoystickXChange = n.leftJoystickX - o.leftJoystickX;
|
||||
leftJoystickYChange = n.leftJoystickY - o.leftJoystickY;
|
||||
rightJoystickXChange = n.rightJoystickX - o.rightJoystickX;
|
||||
rightJoystickYChange = n.rightJoystickY - o.rightJoystickY;
|
||||
|
||||
buttonStateChanged = squareChanged || crossChanged || circleChanged || triangleChanged || L1Changed
|
||||
|| R1Changed || L2Changed || R2Changed || selectChanged || startChanged || leftJoystickPressChanged
|
||||
|| rightJoystickPressChanged || PSChanged;
|
||||
|
||||
hatChanged = hatSwitchLeftRightChange != 0 || hatSwitchUpDownChange != 0;
|
||||
|
||||
leftJoystickChanged = leftJoystickXChange != 0 || leftJoystickYChange != 0;
|
||||
rightJoystickChanged = rightJoystickXChange != 0 || rightJoystickYChange != 0;
|
||||
|
||||
joysticksChanged = leftJoystickChanged || rightJoystickChanged;
|
||||
|
||||
changed = joysticksChanged || hatChanged || buttonStateChanged;
|
||||
|
||||
}
|
||||
|
||||
public int getHatSwitchLeftRightChange()
|
||||
{
|
||||
return hatSwitchLeftRightChange;
|
||||
}
|
||||
|
||||
public int getHatSwitchUpDownChange()
|
||||
{
|
||||
return hatSwitchUpDownChange;
|
||||
}
|
||||
|
||||
public int getLeftJoystickXChange()
|
||||
{
|
||||
return leftJoystickXChange;
|
||||
}
|
||||
|
||||
public int getLeftJoystickYChange()
|
||||
{
|
||||
return leftJoystickYChange;
|
||||
}
|
||||
|
||||
public int getRightJoystickXChange()
|
||||
{
|
||||
return rightJoystickXChange;
|
||||
}
|
||||
|
||||
public int getRightJoystickYChange()
|
||||
{
|
||||
return rightJoystickYChange;
|
||||
}
|
||||
|
||||
public boolean isButtonStateChanged()
|
||||
{
|
||||
return buttonStateChanged;
|
||||
}
|
||||
|
||||
public boolean isChanged()
|
||||
{
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean isCircleChanged()
|
||||
{
|
||||
return circleChanged;
|
||||
}
|
||||
|
||||
public boolean isCrossChanged()
|
||||
{
|
||||
return crossChanged;
|
||||
}
|
||||
|
||||
public boolean isHatChanged()
|
||||
{
|
||||
return hatChanged;
|
||||
}
|
||||
|
||||
public boolean isJoysticksChanged()
|
||||
{
|
||||
return joysticksChanged;
|
||||
}
|
||||
|
||||
public boolean isL1Changed()
|
||||
{
|
||||
return L1Changed;
|
||||
}
|
||||
|
||||
public boolean isL2Changed()
|
||||
{
|
||||
return L2Changed;
|
||||
}
|
||||
|
||||
public boolean isLeftJoystickChanged()
|
||||
{
|
||||
return leftJoystickChanged;
|
||||
}
|
||||
|
||||
public boolean isLeftJoystickPressChanged()
|
||||
{
|
||||
return leftJoystickPressChanged;
|
||||
}
|
||||
|
||||
public boolean isPSChanged()
|
||||
{
|
||||
return PSChanged;
|
||||
}
|
||||
|
||||
public boolean isR1Changed()
|
||||
{
|
||||
return R1Changed;
|
||||
}
|
||||
|
||||
public boolean isR2Changed()
|
||||
{
|
||||
return R2Changed;
|
||||
}
|
||||
|
||||
public boolean isRightJoystickChanged()
|
||||
{
|
||||
return rightJoystickChanged;
|
||||
}
|
||||
|
||||
public boolean isRightJoystickPressChanged()
|
||||
{
|
||||
return rightJoystickPressChanged;
|
||||
}
|
||||
|
||||
public boolean isSelectChanged()
|
||||
{
|
||||
return selectChanged;
|
||||
}
|
||||
|
||||
public boolean isSquareChanged()
|
||||
{
|
||||
return squareChanged;
|
||||
}
|
||||
|
||||
public boolean isStartChanged()
|
||||
{
|
||||
return startChanged;
|
||||
}
|
||||
|
||||
public boolean isTriangleChanged()
|
||||
{
|
||||
return triangleChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
|
||||
package com.codeminders.ardrone.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.codeminders.hidapi.*;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class SonyPS3Controller extends PS3Controller
|
||||
{
|
||||
private static final int VENDOR_ID = 1356;
|
||||
private static final int PRODUCT_ID = 616;
|
||||
|
||||
private static final int BUFSIZE = 64;
|
||||
private static final int EXPECTED_BUFSIZE = 32;
|
||||
private static final int EXPECTED_BUFSIZE_2 = 49;
|
||||
|
||||
private byte[] buf = new byte[BUFSIZE];
|
||||
|
||||
|
||||
public static boolean isA(HIDDeviceInfo hidDeviceInfo)
|
||||
{
|
||||
return(hidDeviceInfo.getVendor_id() == VENDOR_ID && hidDeviceInfo.getProduct_id() == PRODUCT_ID);
|
||||
}
|
||||
|
||||
public SonyPS3Controller() throws HIDDeviceNotFoundException, IOException
|
||||
{
|
||||
dev = HIDManager.getInstance().openById(VENDOR_ID, PRODUCT_ID, null);
|
||||
if (null != dev) {
|
||||
dev.enableBlocking();
|
||||
} else {
|
||||
throw new HIDDeviceNotFoundException("Device not found");
|
||||
}
|
||||
}
|
||||
|
||||
public SonyPS3Controller(HIDDeviceInfo hidDeviceInfo) throws IOException
|
||||
{
|
||||
dev = hidDeviceInfo.open();
|
||||
if (null != dev) {
|
||||
dev.enableBlocking();
|
||||
} else {
|
||||
throw new HIDDeviceNotFoundException("Device not found");
|
||||
}
|
||||
// dev.close();
|
||||
}
|
||||
|
||||
private int joystickCoordConv(byte b)
|
||||
{
|
||||
int v = b < 0 ? b + 256 : b;
|
||||
return(v - 128);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PS3ControllerState read() throws IOException
|
||||
{
|
||||
int n = dev.read(buf);
|
||||
if(n != EXPECTED_BUFSIZE && n != EXPECTED_BUFSIZE_2)
|
||||
{
|
||||
throw new IOException("Received packed with unexpected size " + n);
|
||||
}
|
||||
|
||||
BitSet bs = new BitSet(24);
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if((1 & (buf[2] >> i)) == 1)
|
||||
bs.set(i);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if((1 & (buf[3] >> i)) == 1)
|
||||
bs.set(8 + i);
|
||||
}
|
||||
for(int i = 0; i < 8; i++)
|
||||
{
|
||||
if((1 & (buf[4] >> i)) == 1)
|
||||
bs.set(16 + i);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
boolean select = bs.get(i++);
|
||||
boolean leftJoystickPress = bs.get(i++);
|
||||
boolean rightJoystickPress = bs.get(i++);
|
||||
boolean start = bs.get(i++);
|
||||
bs.get(i++);
|
||||
bs.get(i++);
|
||||
bs.get(i++);
|
||||
bs.get(i++);
|
||||
boolean L2 = bs.get(i++);
|
||||
boolean R2 = bs.get(i++);
|
||||
boolean R1 = bs.get(i++);
|
||||
boolean L1 = bs.get(i++);
|
||||
boolean triangle = bs.get(i++);
|
||||
boolean circle = bs.get(i++);
|
||||
boolean cross = bs.get(i++);
|
||||
boolean square = bs.get(i++);
|
||||
boolean PS = bs.get(i++);
|
||||
|
||||
int leftJoystickX = joystickCoordConv(buf[6]);
|
||||
int leftJoystickY = joystickCoordConv(buf[7]);
|
||||
int rightJoystickX = joystickCoordConv(buf[8]);
|
||||
int rightJoystickY = joystickCoordConv(buf[9]);
|
||||
|
||||
// TODO: decode HAT switch
|
||||
int hatSwitchLeftRight = 0;
|
||||
int hatSwitchUpDown = 0;
|
||||
|
||||
PS3ControllerState res = new PS3ControllerState(square, cross, circle, triangle, L1, R1, L2, R2, select, start,
|
||||
leftJoystickPress, rightJoystickPress, PS, hatSwitchLeftRight, hatSwitchUpDown, leftJoystickX,
|
||||
leftJoystickY, rightJoystickX, rightJoystickY);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
package com.codeminders.ardrone.examples;
|
||||
|
||||
import com.codeminders.ardrone.ARDrone;
|
||||
|
||||
public class TakeOffAndLand
|
||||
{
|
||||
|
||||
private static final long CONNECT_TIMEOUT = 3000;
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
ARDrone drone;
|
||||
try
|
||||
{
|
||||
// Create ARDrone object,
|
||||
// connect to drone and initialize it.
|
||||
drone = new ARDrone();
|
||||
drone.connect();
|
||||
drone.clearEmergencySignal();
|
||||
|
||||
// Wait until drone is ready
|
||||
drone.waitForReady(CONNECT_TIMEOUT);
|
||||
|
||||
// do TRIM operation
|
||||
drone.trim();
|
||||
|
||||
// Take off
|
||||
System.err.println("Taking off");
|
||||
drone.takeOff();
|
||||
|
||||
// Fly a little :)
|
||||
Thread.sleep(5000);
|
||||
|
||||
// Land
|
||||
System.err.println("Landing");
|
||||
drone.land();
|
||||
|
||||
// Give it some time to land
|
||||
Thread.sleep(2000);
|
||||
|
||||
// Disconnect from the done
|
||||
drone.disconnect();
|
||||
|
||||
} catch(Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
package com.codeminders.ardrone.tools;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.codeminders.ardrone.controllers.*;
|
||||
import com.codeminders.hidapi.ClassPathLibraryLoader;
|
||||
import com.codeminders.hidapi.HIDDeviceInfo;
|
||||
import com.codeminders.hidapi.HIDManager;
|
||||
|
||||
public class ControllerTest
|
||||
{
|
||||
private static final long READ_UPDATE_DELAY_MS = 30L;
|
||||
|
||||
static
|
||||
{
|
||||
ClassPathLibraryLoader.loadNativeHIDLibrary();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
PS3Controller c = findController();
|
||||
if(c == null)
|
||||
{
|
||||
System.err.println("Controller not found");
|
||||
System.exit(1);
|
||||
} else
|
||||
{
|
||||
System.err.println("Controller found "+c);
|
||||
}
|
||||
try
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
|
||||
PS3ControllerState x = c.read();
|
||||
System.err.println(x);
|
||||
try
|
||||
{
|
||||
Thread.sleep(READ_UPDATE_DELAY_MS);
|
||||
} catch(InterruptedException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
} finally
|
||||
{
|
||||
c.close();
|
||||
}
|
||||
} catch(IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static PS3Controller findController() throws IOException
|
||||
{
|
||||
|
||||
HIDDeviceInfo[] devs = HIDManager.getInstance().listDevices();
|
||||
if (null != devs) {
|
||||
for(int i = 0; i < devs.length; i++)
|
||||
{
|
||||
if(AfterGlowController.isA(devs[i]))
|
||||
return new AfterGlowController(devs[i]);
|
||||
if(SonyPS3Controller.isA(devs[i]))
|
||||
return new SonyPS3Controller(devs[i]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
|
||||
package com.codeminders.ardrone.tools;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
import com.codeminders.hidapi.*;
|
||||
|
||||
public class HIDAPITest
|
||||
{
|
||||
private static final long READ_UPDATE_DELAY_MS = 500L;
|
||||
|
||||
static
|
||||
{
|
||||
ClassPathLibraryLoader.loadNativeHIDLibrary();
|
||||
}
|
||||
|
||||
private static final int BUFSIZE = 2048;
|
||||
|
||||
// "Afterglow" controller for PS3
|
||||
// static final int VENDOR_ID = 3695;
|
||||
// static final int PRODUCT_ID = 25346;
|
||||
|
||||
// Sony PLAYSTATION(R)3 Controller
|
||||
static final int VENDOR_ID = 1356;
|
||||
static final int PRODUCT_ID = 616;
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
listDevices();
|
||||
readDevice();
|
||||
}
|
||||
|
||||
private static void readDevice()
|
||||
{
|
||||
HIDDevice dev;
|
||||
try
|
||||
{
|
||||
dev = HIDManager.getInstance().openById(VENDOR_ID, PRODUCT_ID, null);
|
||||
System.err.print("Manufacturer: " + dev.getManufacturerString() + "\n");
|
||||
System.err.print("Product: " + dev.getProductString() + "\n");
|
||||
System.err.print("Serial Number: " + dev.getSerialNumberString() + "\n");
|
||||
try
|
||||
{
|
||||
byte[] buf = new byte[BUFSIZE];
|
||||
BitSet old = null;
|
||||
|
||||
dev.enableBlocking();
|
||||
while(true)
|
||||
{
|
||||
int n = dev.read(buf);
|
||||
if(n != 49)
|
||||
{
|
||||
System.err.println("Unexpected data packet size!");
|
||||
return;
|
||||
}
|
||||
|
||||
BitSet current = arrayToBitSet(buf, n);
|
||||
if(old != null)
|
||||
{
|
||||
printHEX(buf, n);
|
||||
printBitSetDiff(current, old);
|
||||
}
|
||||
old = current;
|
||||
try
|
||||
{
|
||||
Thread.sleep(READ_UPDATE_DELAY_MS);
|
||||
} catch(InterruptedException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
} finally
|
||||
{
|
||||
dev.close();
|
||||
}
|
||||
} catch(IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printBitSetDiff(BitSet current, BitSet old)
|
||||
{
|
||||
if(current.length() != old.length())
|
||||
{
|
||||
System.err.println("BitSet size does not match!");
|
||||
return;
|
||||
}
|
||||
|
||||
BitSet diff = (BitSet) current.clone();
|
||||
diff.xor(old);
|
||||
System.err.println(diff);
|
||||
}
|
||||
|
||||
private static BitSet arrayToBitSet(byte[] buf, int n)
|
||||
{
|
||||
BitSet bs = new BitSet(n * 8);
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
byte b = buf[i];
|
||||
for(int j = 0; j < 8; j++)
|
||||
{
|
||||
if((b & (1 << j)) > 0)
|
||||
bs.set(i * 8 + j);
|
||||
}
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
private static void printHEX(byte[] buf, int n)
|
||||
{
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
int v = buf[i];
|
||||
if(v < 0)
|
||||
v = v + 256;
|
||||
String hs = Integer.toHexString(v);
|
||||
if(v < 16)
|
||||
System.err.print("0");
|
||||
System.err.print(hs + " ");
|
||||
}
|
||||
System.err.println("");
|
||||
}
|
||||
|
||||
private static void listDevices()
|
||||
{
|
||||
String property = System.getProperty("java.library.path");
|
||||
System.err.println(property);
|
||||
|
||||
try
|
||||
{
|
||||
HIDDeviceInfo[] devs = HIDManager.getInstance().listDevices();
|
||||
System.err.println("Devices:\n\n");
|
||||
if (null != devs) {
|
||||
for(int i = 0; i < devs.length; i++)
|
||||
{
|
||||
System.err.println("" + i + ".\t" + devs[i]);
|
||||
System.err.println("---------------------------------------------\n");
|
||||
}
|
||||
}
|
||||
} catch(IOException e)
|
||||
{
|
||||
System.err.println(e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import com.codeminders.ardrone.DroneVideoListener;
|
||||
|
||||
/**
|
||||
* This is convenience base class for DroneVideoListener implementors, which
|
||||
* presents video frames to user as instances of java.awt.image.BufferedImag
|
||||
*
|
||||
* @author lord
|
||||
*/
|
||||
public abstract class BufferedImageVideoListener implements DroneVideoListener
|
||||
{
|
||||
@Override
|
||||
public void frameReceived(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
|
||||
{
|
||||
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
|
||||
image.setRGB(startX, startY, w, h, rgbArray, offset, scansize);
|
||||
imageReceived(image);
|
||||
}
|
||||
|
||||
public abstract void imageReceived(BufferedImage image);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class FileImageRecorder extends ImageVideoRecorderBase
|
||||
{
|
||||
private static final int MAX_SAVING_THREADS = 4;
|
||||
private static final String EXT = ".png";
|
||||
|
||||
private boolean activated;
|
||||
private ExecutorService executor;
|
||||
|
||||
public String getExtension()
|
||||
{
|
||||
return EXT;
|
||||
}
|
||||
|
||||
private class ImageSaver implements Runnable
|
||||
{
|
||||
private BufferedImage image;
|
||||
private FileImageRecorder recorder;
|
||||
|
||||
public ImageSaver(BufferedImage image, FileImageRecorder recorder)
|
||||
{
|
||||
this.image = image;
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
recorder.record(image);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates image recorder. It will record files in given directory with
|
||||
* names like IMAGE-0010.png
|
||||
*
|
||||
* @param base_path - directory where image files will be stored
|
||||
* @param starting_seq - starting sequence number
|
||||
* @param prefix - filename prefix
|
||||
* @param callback - callback object which will be notified on each
|
||||
* success/failure. Could be null.
|
||||
*/
|
||||
public FileImageRecorder(File base_path, int starting_seq, String prefix, RecordingSuccessCallback callback)
|
||||
{
|
||||
super(base_path, starting_seq, prefix, callback);
|
||||
|
||||
this.activated = false;
|
||||
executor = Executors.newFixedThreadPool(MAX_SAVING_THREADS);
|
||||
}
|
||||
|
||||
void record(BufferedImage image)
|
||||
{
|
||||
File f;
|
||||
try
|
||||
{
|
||||
f = openFile();
|
||||
} catch(IOException e)
|
||||
{
|
||||
callback.recordingError(null, "error opening file", e);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
ImageIO.write(image, "png", f);
|
||||
} catch(IOException e)
|
||||
{
|
||||
callback.recordingError(f.getPath(), "error writing file", e);
|
||||
f.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
callback.recordingSuccess(f.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Active recorder. Next image will be saved, after which it will be
|
||||
* automatically deactivated.
|
||||
*/
|
||||
public synchronized void activate()
|
||||
{
|
||||
activated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Active recorder.
|
||||
*/
|
||||
public synchronized void deActivate()
|
||||
{
|
||||
activated = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void imageReceived(BufferedImage image)
|
||||
{
|
||||
if(!activated)
|
||||
return;
|
||||
|
||||
executor.execute(new ImageSaver(image, this));
|
||||
activated = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Video recorder. It records single video file. You can start/stop recording
|
||||
* many times.
|
||||
*
|
||||
* @author lord
|
||||
*
|
||||
*/
|
||||
public class FileVideoRecorder extends ImageVideoRecorderBase implements Runnable
|
||||
{
|
||||
private static final String EXT = ".avi";
|
||||
|
||||
private Queue<BufferedImage> frames_queue = new LinkedList<BufferedImage>();
|
||||
private boolean recording;
|
||||
private boolean done;
|
||||
private MJPEGGenerator generator;
|
||||
|
||||
double frame_rate;
|
||||
|
||||
private int frame_width;
|
||||
private int frame_height;
|
||||
File current_file;
|
||||
|
||||
public FileVideoRecorder(File base_path, int starting_seq, String prefix, RecordingSuccessCallback callback,
|
||||
double frame_rate)
|
||||
{
|
||||
super(base_path, starting_seq, prefix, callback);
|
||||
|
||||
this.frame_rate = frame_rate;
|
||||
|
||||
this.recording = false;
|
||||
this.done = false;
|
||||
this.generator = null;
|
||||
|
||||
Thread thr = new Thread(this);
|
||||
thr.start();
|
||||
}
|
||||
|
||||
public synchronized void startRecording()
|
||||
{
|
||||
recording = true;
|
||||
notify();
|
||||
}
|
||||
|
||||
public synchronized void pauseRecording()
|
||||
{
|
||||
recording = false;
|
||||
notify();
|
||||
}
|
||||
|
||||
public synchronized void finishRecording()
|
||||
{
|
||||
recording = false;
|
||||
done = true;
|
||||
notify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void imageReceived(BufferedImage image)
|
||||
{
|
||||
if(recording)
|
||||
{
|
||||
frames_queue.add(image);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void run()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait();
|
||||
} catch(InterruptedException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if(done)
|
||||
{
|
||||
if(generator != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
generator.finishAVI();
|
||||
callback.recordingSuccess(current_file.getAbsolutePath());
|
||||
} catch(Exception e)
|
||||
{
|
||||
callback.recordingError(current_file.getAbsolutePath(), "Error closing stream", e);
|
||||
}
|
||||
} else
|
||||
{
|
||||
callback.recordingError(null, "Recording have not started yet", null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedImage frame = frames_queue.poll();
|
||||
if(frame == null)
|
||||
continue;
|
||||
|
||||
if(generator == null)
|
||||
{
|
||||
// Lazy init. Using first frame size as default
|
||||
frame_width = frame.getWidth();
|
||||
frame_height = frame.getHeight();
|
||||
try
|
||||
{
|
||||
current_file = openFile();
|
||||
} catch(IOException e1)
|
||||
{
|
||||
callback.recordingError(null, "Error opening file", e1);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
generator = new MJPEGGenerator(current_file, frame_width, frame_height, frame_rate, 0);
|
||||
} catch(Exception e)
|
||||
{
|
||||
callback.recordingError(current_file.getAbsolutePath(), "Error video stream", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(frame.getWidth() != frame_width || frame.getHeight() != frame_height)
|
||||
{
|
||||
// Needs to be resized
|
||||
Image i = frame.getScaledInstance(frame_width, frame_height, Image.SCALE_FAST);
|
||||
generator.addImage(i);
|
||||
} else
|
||||
{
|
||||
generator.addImage(frame);
|
||||
}
|
||||
} catch(Exception e)
|
||||
{
|
||||
callback.recordingError(current_file.getAbsolutePath(), "Error adding frame", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getExtension()
|
||||
{
|
||||
return EXT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
|
||||
public abstract class ImageVideoRecorderBase extends BufferedImageVideoListener
|
||||
{
|
||||
private DecimalFormat format;
|
||||
|
||||
protected File base_path;
|
||||
protected int seq;
|
||||
protected String prefix;
|
||||
protected RecordingSuccessCallback callback;
|
||||
|
||||
public ImageVideoRecorderBase(File base_path, int starting_seq, String prefix, RecordingSuccessCallback callback)
|
||||
{
|
||||
this.base_path = base_path;
|
||||
this.seq = starting_seq;
|
||||
this.prefix = prefix;
|
||||
this.callback = callback;
|
||||
|
||||
format = new java.text.DecimalFormat("0000");
|
||||
}
|
||||
|
||||
public abstract String getExtension();
|
||||
|
||||
protected File openFile() throws IOException
|
||||
{
|
||||
while(seq < 9999)
|
||||
{
|
||||
String fname = generateFileName();
|
||||
File f = new File(base_path, fname);
|
||||
if(f.createNewFile())
|
||||
return f;
|
||||
seq++;
|
||||
continue;
|
||||
}
|
||||
throw new IOException("Filename space is exhausted. Could not create file");
|
||||
}
|
||||
|
||||
private String generateFileName()
|
||||
{
|
||||
return prefix + format.format(new Integer(seq)) + getExtension();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* MJPEGGenerator.java
|
||||
*
|
||||
* Created on April 17, 2006, 11:48 PM
|
||||
*
|
||||
* To change this template, choose Tools | Options and locate the template under
|
||||
* the Source Creation and Management node. Right-click the template and choose
|
||||
* Open. You can then make changes to the template in the Source Editor.
|
||||
*/
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author monceaux
|
||||
*/
|
||||
public class MJPEGGenerator
|
||||
{
|
||||
/*
|
||||
* Info needed for MJPEG AVI
|
||||
*
|
||||
* - size of file minus "RIFF & 4 byte file size"
|
||||
*/
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
double framerate = 0;
|
||||
int numFrames = 0;
|
||||
File aviFile = null;
|
||||
FileOutputStream aviOutput = null;
|
||||
FileChannel aviChannel = null;
|
||||
|
||||
long riffOffset = 0;
|
||||
long aviMovieOffset = 0;
|
||||
|
||||
AVIIndexList indexlist = null;
|
||||
|
||||
/** Creates a new instance of MJPEGGenerator */
|
||||
public MJPEGGenerator(File aviFile, int width, int height, double framerate, int numFrames) throws Exception
|
||||
{
|
||||
this.aviFile = aviFile;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.framerate = framerate;
|
||||
this.numFrames = numFrames;
|
||||
aviOutput = new FileOutputStream(aviFile);
|
||||
aviChannel = aviOutput.getChannel();
|
||||
|
||||
RIFFHeader rh = new RIFFHeader();
|
||||
aviOutput.write(rh.toBytes());
|
||||
aviOutput.write(new AVIMainHeader().toBytes());
|
||||
aviOutput.write(new AVIStreamList().toBytes());
|
||||
aviOutput.write(new AVIStreamHeader().toBytes());
|
||||
aviOutput.write(new AVIStreamFormat().toBytes());
|
||||
aviOutput.write(new AVIJunk().toBytes());
|
||||
aviMovieOffset = aviChannel.position();
|
||||
aviOutput.write(new AVIMovieList().toBytes());
|
||||
indexlist = new AVIIndexList();
|
||||
}
|
||||
|
||||
public void addImage(Image image) throws Exception
|
||||
{
|
||||
byte[] fcc = new byte[] { '0', '0', 'd', 'b' };
|
||||
byte[] imagedata = writeImageToBytes(image);
|
||||
int useLength = imagedata.length;
|
||||
long position = aviChannel.position();
|
||||
int extra = (useLength + (int) position) % 4;
|
||||
if(extra > 0)
|
||||
useLength = useLength + extra;
|
||||
|
||||
indexlist.addAVIIndex((int) position, useLength);
|
||||
|
||||
aviOutput.write(fcc);
|
||||
aviOutput.write(intBytes(swapInt(useLength)));
|
||||
aviOutput.write(imagedata);
|
||||
if(extra > 0)
|
||||
{
|
||||
for(int i = 0; i < extra; i++)
|
||||
aviOutput.write(0);
|
||||
}
|
||||
imagedata = null;
|
||||
}
|
||||
|
||||
public void finishAVI() throws Exception
|
||||
{
|
||||
byte[] indexlistBytes = indexlist.toBytes();
|
||||
aviOutput.write(indexlistBytes);
|
||||
aviOutput.close();
|
||||
long size = aviFile.length();
|
||||
RandomAccessFile raf = new RandomAccessFile(aviFile, "rw");
|
||||
raf.seek(4);
|
||||
raf.write(intBytes(swapInt((int) size - 8)));
|
||||
raf.seek(aviMovieOffset + 4);
|
||||
raf.write(intBytes(swapInt((int) (size - 8 - aviMovieOffset - indexlistBytes.length))));
|
||||
raf.close();
|
||||
}
|
||||
|
||||
// public void writeAVI(File file) throws Exception
|
||||
// {
|
||||
// OutputStream os = new FileOutputStream(file);
|
||||
//
|
||||
// // RIFFHeader
|
||||
// // AVIMainHeader
|
||||
// // AVIStreamList
|
||||
// // AVIStreamHeader
|
||||
// // AVIStreamFormat
|
||||
// // write 00db and image bytes...
|
||||
// }
|
||||
|
||||
public static int swapInt(int v)
|
||||
{
|
||||
return (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00);
|
||||
}
|
||||
|
||||
public static short swapShort(short v)
|
||||
{
|
||||
return (short) ((v >>> 8) | (v << 8));
|
||||
}
|
||||
|
||||
public static byte[] intBytes(int i)
|
||||
{
|
||||
byte[] b = new byte[4];
|
||||
b[0] = (byte) (i >>> 24);
|
||||
b[1] = (byte) ((i >>> 16) & 0x000000FF);
|
||||
b[2] = (byte) ((i >>> 8) & 0x000000FF);
|
||||
b[3] = (byte) (i & 0x000000FF);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static byte[] shortBytes(short i)
|
||||
{
|
||||
byte[] b = new byte[2];
|
||||
b[0] = (byte) (i >>> 8);
|
||||
b[1] = (byte) (i & 0x000000FF);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private class RIFFHeader
|
||||
{
|
||||
public byte[] fcc = new byte[] { 'R', 'I', 'F', 'F' };
|
||||
public int fileSize = 0;
|
||||
public byte[] fcc2 = new byte[] { 'A', 'V', 'I', ' ' };
|
||||
public byte[] fcc3 = new byte[] { 'L', 'I', 'S', 'T' };
|
||||
public int listSize = 200;
|
||||
public byte[] fcc4 = new byte[] { 'h', 'd', 'r', 'l' };
|
||||
|
||||
public RIFFHeader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(fileSize)));
|
||||
baos.write(fcc2);
|
||||
baos.write(fcc3);
|
||||
baos.write(intBytes(swapInt(listSize)));
|
||||
baos.write(fcc4);
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIMainHeader
|
||||
{
|
||||
/*
|
||||
*
|
||||
* FOURCC fcc; DWORD cb; DWORD dwMicroSecPerFrame; DWORD
|
||||
* dwMaxBytesPerSec; DWORD dwPaddingGranularity; DWORD dwFlags; DWORD
|
||||
* dwTotalFrames; DWORD dwInitialFrames; DWORD dwStreams; DWORD
|
||||
* dwSuggestedBufferSize; DWORD dwWidth; DWORD dwHeight; DWORD
|
||||
* dwReserved[4];
|
||||
*/
|
||||
|
||||
public byte[] fcc = new byte[] { 'a', 'v', 'i', 'h' };
|
||||
public int cb = 56;
|
||||
public int dwMicroSecPerFrame = 0; // (1
|
||||
// /
|
||||
// frames
|
||||
// per
|
||||
// sec)
|
||||
// *
|
||||
// 1,000,000
|
||||
public int dwMaxBytesPerSec = 10000000;
|
||||
public int dwPaddingGranularity = 0;
|
||||
public int dwFlags = 65552;
|
||||
public int dwTotalFrames = 0; // replace
|
||||
// with
|
||||
// correct
|
||||
// value
|
||||
public int dwInitialFrames = 0;
|
||||
public int dwStreams = 1;
|
||||
public int dwSuggestedBufferSize = 0;
|
||||
public int dwWidth = 0; // replace
|
||||
// with
|
||||
// correct
|
||||
// value
|
||||
public int dwHeight = 0; // replace
|
||||
// with
|
||||
// correct
|
||||
// value
|
||||
public int[] dwReserved = new int[4];
|
||||
|
||||
public AVIMainHeader()
|
||||
{
|
||||
dwMicroSecPerFrame = (int) ((1.0 / framerate) * 1000000.0);
|
||||
dwWidth = width;
|
||||
dwHeight = height;
|
||||
dwTotalFrames = numFrames;
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(cb)));
|
||||
baos.write(intBytes(swapInt(dwMicroSecPerFrame)));
|
||||
baos.write(intBytes(swapInt(dwMaxBytesPerSec)));
|
||||
baos.write(intBytes(swapInt(dwPaddingGranularity)));
|
||||
baos.write(intBytes(swapInt(dwFlags)));
|
||||
baos.write(intBytes(swapInt(dwTotalFrames)));
|
||||
baos.write(intBytes(swapInt(dwInitialFrames)));
|
||||
baos.write(intBytes(swapInt(dwStreams)));
|
||||
baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
|
||||
baos.write(intBytes(swapInt(dwWidth)));
|
||||
baos.write(intBytes(swapInt(dwHeight)));
|
||||
baos.write(intBytes(swapInt(dwReserved[0])));
|
||||
baos.write(intBytes(swapInt(dwReserved[1])));
|
||||
baos.write(intBytes(swapInt(dwReserved[2])));
|
||||
baos.write(intBytes(swapInt(dwReserved[3])));
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIStreamList
|
||||
{
|
||||
public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' };
|
||||
public int size = 124;
|
||||
public byte[] fcc2 = new byte[] { 's', 't', 'r', 'l' };
|
||||
|
||||
public AVIStreamList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(size)));
|
||||
baos.write(fcc2);
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIStreamHeader
|
||||
{
|
||||
/*
|
||||
* FOURCC fcc; DWORD cb; FOURCC fccType; FOURCC fccHandler; DWORD
|
||||
* dwFlags; WORD wPriority; WORD wLanguage; DWORD dwInitialFrames; DWORD
|
||||
* dwScale; DWORD dwRate; DWORD dwStart; DWORD dwLength; DWORD
|
||||
* dwSuggestedBufferSize; DWORD dwQuality; DWORD dwSampleSize; struct {
|
||||
* short int left; short int top; short int right; short int bottom; }
|
||||
* rcFrame;
|
||||
*/
|
||||
|
||||
public byte[] fcc = new byte[] { 's', 't', 'r', 'h' };
|
||||
public int cb = 64;
|
||||
public byte[] fccType = new byte[] { 'v', 'i', 'd', 's' };
|
||||
public byte[] fccHandler = new byte[] { 'M', 'J', 'P', 'G' };
|
||||
public int dwFlags = 0;
|
||||
public short wPriority = 0;
|
||||
public short wLanguage = 0;
|
||||
public int dwInitialFrames = 0;
|
||||
public int dwScale = 0; // microseconds
|
||||
// per
|
||||
// frame
|
||||
public int dwRate = 1000000; // dwRate
|
||||
// /
|
||||
// dwScale
|
||||
// =
|
||||
// frame
|
||||
// rate
|
||||
public int dwStart = 0;
|
||||
public int dwLength = 0; // num
|
||||
// frames
|
||||
public int dwSuggestedBufferSize = 0;
|
||||
public int dwQuality = -1;
|
||||
public int dwSampleSize = 0;
|
||||
public int left = 0;
|
||||
public int top = 0;
|
||||
public int right = 0;
|
||||
public int bottom = 0;
|
||||
|
||||
public AVIStreamHeader()
|
||||
{
|
||||
dwScale = (int) ((1.0 / framerate) * 1000000.0);
|
||||
dwLength = numFrames;
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(cb)));
|
||||
baos.write(fccType);
|
||||
baos.write(fccHandler);
|
||||
baos.write(intBytes(swapInt(dwFlags)));
|
||||
baos.write(shortBytes(swapShort(wPriority)));
|
||||
baos.write(shortBytes(swapShort(wLanguage)));
|
||||
baos.write(intBytes(swapInt(dwInitialFrames)));
|
||||
baos.write(intBytes(swapInt(dwScale)));
|
||||
baos.write(intBytes(swapInt(dwRate)));
|
||||
baos.write(intBytes(swapInt(dwStart)));
|
||||
baos.write(intBytes(swapInt(dwLength)));
|
||||
baos.write(intBytes(swapInt(dwSuggestedBufferSize)));
|
||||
baos.write(intBytes(swapInt(dwQuality)));
|
||||
baos.write(intBytes(swapInt(dwSampleSize)));
|
||||
baos.write(intBytes(swapInt(left)));
|
||||
baos.write(intBytes(swapInt(top)));
|
||||
baos.write(intBytes(swapInt(right)));
|
||||
baos.write(intBytes(swapInt(bottom)));
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIStreamFormat
|
||||
{
|
||||
/*
|
||||
* FOURCC fcc; DWORD cb; DWORD biSize; LONG biWidth; LONG biHeight; WORD
|
||||
* biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage;
|
||||
* LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD
|
||||
* biClrImportant;
|
||||
*/
|
||||
|
||||
public byte[] fcc = new byte[] { 's', 't', 'r', 'f' };
|
||||
public int cb = 40;
|
||||
public int biSize = 40; // same
|
||||
// as
|
||||
// cb
|
||||
public int biWidth = 0;
|
||||
public int biHeight = 0;
|
||||
public short biPlanes = 1;
|
||||
public short biBitCount = 24;
|
||||
public byte[] biCompression = new byte[] { 'M', 'J', 'P', 'G' };
|
||||
public int biSizeImage = 0; // width
|
||||
// x
|
||||
// height
|
||||
// in
|
||||
// pixels
|
||||
public int biXPelsPerMeter = 0;
|
||||
public int biYPelsPerMeter = 0;
|
||||
public int biClrUsed = 0;
|
||||
public int biClrImportant = 0;
|
||||
|
||||
public AVIStreamFormat()
|
||||
{
|
||||
biWidth = width;
|
||||
biHeight = height;
|
||||
biSizeImage = width * height;
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(cb)));
|
||||
baos.write(intBytes(swapInt(biSize)));
|
||||
baos.write(intBytes(swapInt(biWidth)));
|
||||
baos.write(intBytes(swapInt(biHeight)));
|
||||
baos.write(shortBytes(swapShort(biPlanes)));
|
||||
baos.write(shortBytes(swapShort(biBitCount)));
|
||||
baos.write(biCompression);
|
||||
baos.write(intBytes(swapInt(biSizeImage)));
|
||||
baos.write(intBytes(swapInt(biXPelsPerMeter)));
|
||||
baos.write(intBytes(swapInt(biYPelsPerMeter)));
|
||||
baos.write(intBytes(swapInt(biClrUsed)));
|
||||
baos.write(intBytes(swapInt(biClrImportant)));
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIMovieList
|
||||
{
|
||||
public byte[] fcc = new byte[] { 'L', 'I', 'S', 'T' };
|
||||
public int listSize = 0;
|
||||
public byte[] fcc2 = new byte[] { 'm', 'o', 'v', 'i' };
|
||||
|
||||
// 00db size jpg image data ...
|
||||
|
||||
public AVIMovieList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(listSize)));
|
||||
baos.write(fcc2);
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIIndexList
|
||||
{
|
||||
public byte[] fcc = new byte[] { 'i', 'd', 'x', '1' };
|
||||
public int cb = 0;
|
||||
public List<AVIIndex> ind = new ArrayList<AVIIndex>();
|
||||
|
||||
public AVIIndexList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void addAVIIndex(AVIIndex ai)
|
||||
{
|
||||
ind.add(ai);
|
||||
}
|
||||
|
||||
public void addAVIIndex(int dwOffset, int dwSize)
|
||||
{
|
||||
ind.add(new AVIIndex(dwOffset, dwSize));
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
cb = 16 * ind.size();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(cb)));
|
||||
for(int i = 0; i < ind.size(); i++)
|
||||
{
|
||||
AVIIndex in = (AVIIndex) ind.get(i);
|
||||
baos.write(in.toBytes());
|
||||
}
|
||||
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIIndex
|
||||
{
|
||||
public byte[] fcc = new byte[] { '0', '0', 'd', 'b' };
|
||||
public int dwFlags = 16;
|
||||
public int dwOffset = 0;
|
||||
public int dwSize = 0;
|
||||
|
||||
public AVIIndex(int dwOffset, int dwSize)
|
||||
{
|
||||
this.dwOffset = dwOffset;
|
||||
this.dwSize = dwSize;
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(dwFlags)));
|
||||
baos.write(intBytes(swapInt(dwOffset)));
|
||||
baos.write(intBytes(swapInt(dwSize)));
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private class AVIJunk
|
||||
{
|
||||
public byte[] fcc = new byte[] { 'J', 'U', 'N', 'K' };
|
||||
public int size = 1808;
|
||||
public byte[] data = new byte[size];
|
||||
|
||||
public AVIJunk()
|
||||
{
|
||||
Arrays.fill(data, (byte) 0);
|
||||
}
|
||||
|
||||
public byte[] toBytes() throws Exception
|
||||
{
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(fcc);
|
||||
baos.write(intBytes(swapInt(size)));
|
||||
baos.write(data);
|
||||
baos.close();
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] writeImageToBytes(Image image) throws Exception
|
||||
{
|
||||
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Graphics2D g = bi.createGraphics();
|
||||
g.drawImage(image, 0, 0, width, height, null);
|
||||
ImageIO.write(bi, "jpg", baos);
|
||||
baos.close();
|
||||
bi = null;
|
||||
g = null;
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
package com.codeminders.ardrone.util;
|
||||
|
||||
public interface RecordingSuccessCallback
|
||||
{
|
||||
|
||||
/**
|
||||
* This method will be called if file was successfully recorded
|
||||
*
|
||||
* @param filename - full path to recorded file
|
||||
*/
|
||||
public void recordingSuccess(String filename);
|
||||
|
||||
/**
|
||||
* This method will be called if file recording failed
|
||||
*
|
||||
* @param filename - file name we attempted to record. Could be null.
|
||||
* @param err - error message. Could be null.
|
||||
* @param ex - throwable, which caused the error. Could be null.
|
||||
*/
|
||||
public void recordingError(String filename, String err, Throwable ex);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,987 @@
|
||||
|
||||
package com.codeminders.ardrone.video;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
// Copyright (C) 2007-2011, PARROT SA, all rights reserved.
|
||||
|
||||
// DISCLAIMER
|
||||
// The APIs is provided by PARROT and contributors "AS IS" and any express or
|
||||
// implied warranties, including, but not limited to, the implied warranties of
|
||||
// merchantability
|
||||
// and fitness for a particular purpose are disclaimed. In no event shall PARROT
|
||||
// and contributors be liable for any direct, indirect, incidental, special,
|
||||
// exemplary, or
|
||||
// consequential damages (including, but not limited to, procurement of
|
||||
// substitute goods or services; loss of use, data, or profits; or business
|
||||
// interruption) however
|
||||
// caused and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of the use
|
||||
// of this
|
||||
// software, even if advised of the possibility of such damage.
|
||||
|
||||
// Author : Daniel Schmidt
|
||||
// Publishing date : 2010-01-06
|
||||
// based on work by : Wilke Jansoone
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// - Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions, the disclaimer and the original author of the source
|
||||
// code.
|
||||
// - Neither the name of the PixVillage Team, nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
|
||||
public class BufferedVideoImage
|
||||
{
|
||||
private static final int BLOCK_WIDTH = 8;
|
||||
private static final int CIF_WIDTH = 88;
|
||||
private static final int CIG_HEIGHT = 72;
|
||||
|
||||
private static final int VGA_WIDTH = 160;
|
||||
private static final int VGA_HEIGHT = 120;
|
||||
|
||||
private static final int TABLE_QUANTIZATION_MODE = 31;
|
||||
|
||||
private static final int FIX_0_298631336 = 2446;
|
||||
private static final int FIX_0_390180644 = 3196;
|
||||
private static final int FIX_0_541196100 = 4433;
|
||||
private static final int FIX_0_765366865 = 6270;
|
||||
private static final int FIX_0_899976223 = 7373;
|
||||
private static final int FIX_1_175875602 = 9633;
|
||||
private static final int FIX_1_501321110 = 12299;
|
||||
private static final int FIX_1_847759065 = 15137;
|
||||
private static final int FIX_1_961570560 = 16069;
|
||||
private static final int FIX_2_053119869 = 16819;
|
||||
private static final int FIX_2_562915447 = 20995;
|
||||
private static final int FIX_3_072711026 = 25172;
|
||||
|
||||
private static final int BITS = 13;
|
||||
private static final int PASS1_BITS = 1;
|
||||
private static final int F1 = BITS - PASS1_BITS - 1;
|
||||
private static final int F2 = BITS - PASS1_BITS;
|
||||
private static final int F3 = BITS + PASS1_BITS + 3;
|
||||
|
||||
/**
|
||||
* 176px x 144px
|
||||
*/
|
||||
private static final int CIF = 1;
|
||||
|
||||
/**
|
||||
* 320px x 240px
|
||||
*/
|
||||
private static final int QVGA = 2;
|
||||
|
||||
private static final short[] ZIGZAG_POSITIONS = new short[] { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
|
||||
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29,
|
||||
22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, };
|
||||
|
||||
// Cfr. Handbook of Data Compression - Page 529
|
||||
// David Salomon
|
||||
// Giovanni Motta
|
||||
|
||||
private static final short[] QUANTIZER_VALUES = new short[] { 3, 5, 7, 9, 11, 13, 15, 17, 5, 7, 9, 11, 13,
|
||||
15, 17, 19, 7, 9, 11, 13, 15, 17, 19, 21, 9, 11, 13, 15, 17, 19, 21, 23, 11, 13, 15, 17, 19, 21, 23, 25,
|
||||
13, 15, 17, 19, 21, 23, 25, 27, 15, 17, 19, 21, 23, 25, 27, 29, 17, 19, 21, 23, 25, 27, 29, 31 };
|
||||
|
||||
static byte[] CLZLUT = new byte[] { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
private static final int[] CROMA_QUADRANT_OFFSETS = new int[] { 0, 4, 32, 36 };
|
||||
|
||||
private short[] dataBlockBuffer = new short[64];
|
||||
|
||||
private uint streamField;
|
||||
private int streamFieldBitIndex;
|
||||
private int streamIndex;
|
||||
private int sliceCount;
|
||||
private boolean pictureComplete;
|
||||
private int pictureFormat;
|
||||
private int resolution;
|
||||
private int pictureType;
|
||||
private int quantizerMode;
|
||||
private int frameIndex;
|
||||
private int sliceIndex;
|
||||
private int blockCount;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
/**
|
||||
* Length of one row of pixels in the destination image in bytes.
|
||||
*/
|
||||
private int pixelRowSize;
|
||||
private ByteBuffer imageStream;
|
||||
private ImageSlice imageSlice;
|
||||
private uint[] pixelData;
|
||||
private int[] javaPixelData;
|
||||
|
||||
public void addImageStream(ByteBuffer stream)
|
||||
{
|
||||
imageStream = stream;
|
||||
processStream();
|
||||
}
|
||||
|
||||
private void alignStreamData()
|
||||
{
|
||||
int alignedLength;
|
||||
int actualLength;
|
||||
|
||||
actualLength = streamFieldBitIndex;
|
||||
|
||||
if(actualLength > 0)
|
||||
{
|
||||
alignedLength = (actualLength & ~7);
|
||||
if(alignedLength != actualLength)
|
||||
{
|
||||
alignedLength += 0x08;
|
||||
streamField.shiftLeftEquals(alignedLength - actualLength);
|
||||
streamFieldBitIndex = alignedLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void composeImageSlice()
|
||||
{
|
||||
int u, ug, ub;
|
||||
int v, vg, vr;
|
||||
int r, g, b;
|
||||
|
||||
int lumaElementIndex1 = 0;
|
||||
int lumaElementIndex2 = 0;
|
||||
int chromaOffset = 0;
|
||||
|
||||
int dataIndex1 = 0;
|
||||
int dataIndex2 = 0;
|
||||
|
||||
int lumaElementValue1 = 0;
|
||||
int lumaElementValue2 = 0;
|
||||
int chromaBlueValue = 0;
|
||||
int chromaRedValue = 0;
|
||||
|
||||
int[] pixelDataQuadrantOffsets = new int[] { 0, BLOCK_WIDTH, width * BLOCK_WIDTH,
|
||||
(width * BLOCK_WIDTH) + BLOCK_WIDTH };
|
||||
|
||||
int imageDataOffset = (sliceIndex - 1) * width * 16;
|
||||
|
||||
for(MacroBlock macroBlock : imageSlice.MacroBlocks)
|
||||
{
|
||||
for(int verticalStep = 0; verticalStep < BLOCK_WIDTH / 2; verticalStep++)
|
||||
{
|
||||
chromaOffset = verticalStep * BLOCK_WIDTH;
|
||||
lumaElementIndex1 = verticalStep * BLOCK_WIDTH * 2;
|
||||
lumaElementIndex2 = lumaElementIndex1 + BLOCK_WIDTH;
|
||||
|
||||
dataIndex1 = imageDataOffset + (2 * verticalStep * width);
|
||||
dataIndex2 = dataIndex1 + width;
|
||||
|
||||
for(int horizontalStep = 0; horizontalStep < BLOCK_WIDTH / 2; horizontalStep++)
|
||||
{
|
||||
for(int quadrant = 0; quadrant < 4; quadrant++)
|
||||
{
|
||||
int chromaIndex = chromaOffset + CROMA_QUADRANT_OFFSETS[quadrant] + horizontalStep;
|
||||
chromaBlueValue = macroBlock.DataBlocks[4][chromaIndex];
|
||||
chromaRedValue = macroBlock.DataBlocks[5][chromaIndex];
|
||||
|
||||
u = chromaBlueValue - 128;
|
||||
ug = 88 * u;
|
||||
ub = 454 * u;
|
||||
|
||||
v = chromaRedValue - 128;
|
||||
vg = 183 * v;
|
||||
vr = 359 * v;
|
||||
|
||||
for(int pixel = 0; pixel < 2; pixel++)
|
||||
{
|
||||
int deltaIndex = 2 * horizontalStep + pixel;
|
||||
lumaElementValue1 = macroBlock.DataBlocks[quadrant][lumaElementIndex1 + deltaIndex] << 8;
|
||||
lumaElementValue2 = macroBlock.DataBlocks[quadrant][lumaElementIndex2 + deltaIndex] << 8;
|
||||
|
||||
r = saturate5(lumaElementValue1 + vr);
|
||||
g = saturate6(lumaElementValue1 - ug - vg);
|
||||
b = saturate5(lumaElementValue1 + ub);
|
||||
|
||||
int index1 = dataIndex1 + pixelDataQuadrantOffsets[quadrant] + deltaIndex;
|
||||
pixelData[index1] = makeRGB(r, g, b);
|
||||
javaPixelData[index1] = pixelData[index1].intValue();
|
||||
|
||||
r = saturate5(lumaElementValue2 + vr);
|
||||
g = saturate6(lumaElementValue2 - ug - vg);
|
||||
b = saturate5(lumaElementValue2 + ub);
|
||||
|
||||
int index2 = dataIndex2 + pixelDataQuadrantOffsets[quadrant] + deltaIndex;
|
||||
pixelData[index2] = makeRGB(r, g, b);
|
||||
javaPixelData[index2] = pixelData[index2].intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imageDataOffset += 16;
|
||||
}
|
||||
}
|
||||
|
||||
private static int countLeadingZeros(uint value)
|
||||
{
|
||||
int accum = 0;
|
||||
|
||||
accum += CLZLUT[value.shiftRight(24).intValue()];
|
||||
if(accum == 8)
|
||||
accum += CLZLUT[(value.shiftRight(16).intValue()) & 0xFF];
|
||||
if(accum == 16)
|
||||
accum += CLZLUT[(value.shiftRight(8).intValue()) & 0xFF];
|
||||
if(accum == 24)
|
||||
accum += CLZLUT[value.intValue() & 0xFF];
|
||||
|
||||
return accum;
|
||||
}
|
||||
|
||||
private void decodeFieldBytes(int[] run, int[] level, boolean[] last)
|
||||
{
|
||||
uint streamCode = new uint(0);
|
||||
|
||||
int streamLength = 0;
|
||||
;
|
||||
int zeroCount = 0;
|
||||
int temp = 0;
|
||||
int sign = 0;
|
||||
|
||||
// Use the RLE and Huffman dictionaries to understand this code
|
||||
// fragment. You can find
|
||||
// them in the developers guide on page 34.
|
||||
// The bits in the data are actually composed of two kinds of fields:
|
||||
// - run fields - this field contains information on the number of
|
||||
// consecutive zeros.
|
||||
// - level fields - this field contains the actual non zero value which
|
||||
// can be negative or positive.
|
||||
// First we extract the run field info and then the level field info.
|
||||
|
||||
streamCode = peekStreamData(imageStream, 32);
|
||||
|
||||
// Determine number of consecutive zeros in zig zag. (a.k.a
|
||||
// 'run' field info)
|
||||
|
||||
// Suppose we have following bit sequence:
|
||||
// 00001111.....
|
||||
// 1 - Count the number of leading zeros -> 4
|
||||
// Coarse value lookup is thus 00001
|
||||
// 2 - Lookup the additional value, for coarse value 00001 this is 3
|
||||
// addtional bits
|
||||
// 3 - Calculate value of run, for coarse value 00001 this is (111) + 8
|
||||
|
||||
zeroCount = countLeadingZeros(streamCode); // - (1)
|
||||
streamCode.shiftLeftEquals(zeroCount + 1); // - (2) -> shift left to get
|
||||
// rid of the coarse value
|
||||
streamLength += zeroCount + 1; // - position bit pointer to keep track
|
||||
// off how many bits to consume later on
|
||||
// the stream.
|
||||
|
||||
if(zeroCount > 1)
|
||||
{
|
||||
temp = (streamCode.shiftRight(32 - (zeroCount - 1))).intValue(); // -
|
||||
// (2)
|
||||
// ->
|
||||
// shift
|
||||
// right
|
||||
// to
|
||||
// determine
|
||||
// the
|
||||
// addtional
|
||||
// bits
|
||||
// (number
|
||||
// of
|
||||
// additional
|
||||
// bits
|
||||
// is
|
||||
// zerocount
|
||||
// - 1)
|
||||
streamCode.shiftLeftEquals(zeroCount - 1); // - shift all of the run
|
||||
// bits out of the way
|
||||
// so the first bit is
|
||||
// points to the first
|
||||
// bit of the level
|
||||
// field.
|
||||
streamLength += zeroCount - 1;// - position bit pointer to keep
|
||||
// track off how many bits to
|
||||
// consume later on the stream.
|
||||
run[0] = temp + (1 << (zeroCount - 1)); // - (3) -> calculate run
|
||||
// value
|
||||
} else
|
||||
{
|
||||
run[0] = zeroCount;
|
||||
}
|
||||
|
||||
// Determine non zero value. (a.k.a 'level' field info)
|
||||
|
||||
// Suppose we have following bit sequence:
|
||||
// 000011111.....
|
||||
// 1 - Count the number of leading zeros -> 4
|
||||
// Coarse value lookup is thus 00001
|
||||
// 2 - Lookup the additional value, for coarse value 00001 this is 4
|
||||
// addtional bits (last bit is sign bit)
|
||||
// 3 - Calculate value of run, for coarse value 00001 this is (xxx) + 8,
|
||||
// multiply by sign
|
||||
|
||||
zeroCount = countLeadingZeros(streamCode);
|
||||
streamCode.shiftLeftEquals(zeroCount + 1); // - (1)
|
||||
streamLength += zeroCount + 1; // - position bit pointer to keep track
|
||||
// off how many bits to consume later on
|
||||
// the stream.
|
||||
|
||||
if(zeroCount == 1)
|
||||
{
|
||||
// If coarse value is 01 according to the Huffman dictionary this
|
||||
// means EOB, so there is
|
||||
// no run and level and we indicate this by setting last to true;
|
||||
run[0] = 0;
|
||||
last[0] = true;
|
||||
} else
|
||||
{
|
||||
if(zeroCount == 0)
|
||||
{
|
||||
zeroCount = 1;
|
||||
temp = 1;
|
||||
}
|
||||
|
||||
streamLength += zeroCount;// - position bit pointer to keep track
|
||||
// off how many bits to consume later on
|
||||
// the stream.
|
||||
streamCode.shiftRightEquals(32 - zeroCount);// - (2) -> shift right
|
||||
// to determine the
|
||||
// addtional bits
|
||||
// (number of additional
|
||||
// bits is zerocount)
|
||||
// sign = (sbyte)(streamCode & 1); // determine sign, last bit is
|
||||
// sign
|
||||
sign = (int) (streamCode.and(1).intValue()); // determine sign, last
|
||||
// bit is sign
|
||||
|
||||
if(zeroCount != 0)
|
||||
{
|
||||
// temp = (sbyte)(streamCode >> 1); // take into account that
|
||||
// last bit is sign, so shift it out of the way
|
||||
// temp += (sbyte)(1 << (zeroCount - 1)); // - (3) -> calculate
|
||||
// run value without sign
|
||||
temp = (streamCode.shiftRight(1)).intValue(); // take into
|
||||
// account
|
||||
// that last bit is
|
||||
// sign, so shift it
|
||||
// out of the way
|
||||
temp += (int) (1 << (zeroCount - 1)); // - (3) -> calculate run
|
||||
// value without sign
|
||||
}
|
||||
|
||||
level[0] = (sign == 1) ? -temp : temp; // - (3) -> calculate run
|
||||
// value with sign
|
||||
last[0] = false;
|
||||
}
|
||||
|
||||
readStreamData(streamLength);
|
||||
}
|
||||
|
||||
private void getBlockBytes(boolean acCoefficientsAvailable)
|
||||
{
|
||||
int[] run = new int[] { 0 };
|
||||
int[] level = new int[] { 0 };
|
||||
int zigZagPosition = 0;
|
||||
int matrixPosition = 0;
|
||||
boolean[] last = new boolean[] { false };
|
||||
|
||||
for(int i = 0; i < dataBlockBuffer.length; i++)
|
||||
dataBlockBuffer[i] = 0;
|
||||
|
||||
uint dcCoefficient = readStreamData(10);
|
||||
|
||||
if(quantizerMode == TABLE_QUANTIZATION_MODE)
|
||||
{
|
||||
dataBlockBuffer[0] = (short) (dcCoefficient.times(QUANTIZER_VALUES[0]));
|
||||
|
||||
if(acCoefficientsAvailable)
|
||||
{
|
||||
decodeFieldBytes(run, level, last);
|
||||
|
||||
while(!last[0])
|
||||
{
|
||||
zigZagPosition += run[0] + 1;
|
||||
matrixPosition = ZIGZAG_POSITIONS[zigZagPosition];
|
||||
level[0] *= QUANTIZER_VALUES[matrixPosition];
|
||||
dataBlockBuffer[matrixPosition] = (short) level[0];
|
||||
decodeFieldBytes(run, level, last);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
// Currently not implemented.
|
||||
throw new RuntimeException("ant quantizer mode is not yet implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
public int getFrameIndex()
|
||||
{
|
||||
return frameIndex;
|
||||
}
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
public int[] getJavaPixelData()
|
||||
{
|
||||
return javaPixelData;
|
||||
}
|
||||
|
||||
public int getPictureType()
|
||||
{
|
||||
return pictureType;
|
||||
}
|
||||
|
||||
public uint[] getPixelData()
|
||||
{
|
||||
return pixelData;
|
||||
}
|
||||
|
||||
public int getPixelRowSize()
|
||||
{
|
||||
return pixelRowSize;
|
||||
}
|
||||
|
||||
public int getSliceCount()
|
||||
{
|
||||
return sliceCount;
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
void inverseTransform(int macroBlockIndex, int dataBlockIndex)
|
||||
{
|
||||
int[] workSpace = new int[64];
|
||||
short[] data = new short[64];
|
||||
|
||||
int z1, z2, z3, z4, z5;
|
||||
int tmp0, tmp1, tmp2, tmp3;
|
||||
int tmp10, tmp11, tmp12, tmp13;
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
for(int index = 8; index > 0; index--)
|
||||
{
|
||||
if(dataBlockBuffer[pointer + 8] == 0 && dataBlockBuffer[pointer + 16] == 0
|
||||
&& dataBlockBuffer[pointer + 24] == 0 && dataBlockBuffer[pointer + 32] == 0
|
||||
&& dataBlockBuffer[pointer + 40] == 0 && dataBlockBuffer[pointer + 48] == 0
|
||||
&& dataBlockBuffer[pointer + 56] == 0)
|
||||
{
|
||||
int dcValue = dataBlockBuffer[pointer] << PASS1_BITS;
|
||||
|
||||
workSpace[pointer + 0] = dcValue;
|
||||
workSpace[pointer + 8] = dcValue;
|
||||
workSpace[pointer + 16] = dcValue;
|
||||
workSpace[pointer + 24] = dcValue;
|
||||
workSpace[pointer + 32] = dcValue;
|
||||
workSpace[pointer + 40] = dcValue;
|
||||
workSpace[pointer + 48] = dcValue;
|
||||
workSpace[pointer + 56] = dcValue;
|
||||
|
||||
pointer++;
|
||||
continue;
|
||||
}
|
||||
|
||||
z2 = dataBlockBuffer[pointer + 16];
|
||||
z3 = dataBlockBuffer[pointer + 48];
|
||||
|
||||
z1 = (z2 + z3) * FIX_0_541196100;
|
||||
tmp2 = z1 + z3 * -FIX_1_847759065;
|
||||
tmp3 = z1 + z2 * FIX_0_765366865;
|
||||
|
||||
z2 = dataBlockBuffer[pointer];
|
||||
z3 = dataBlockBuffer[pointer + 32];
|
||||
|
||||
tmp0 = (z2 + z3) << BITS;
|
||||
tmp1 = (z2 - z3) << BITS;
|
||||
|
||||
tmp10 = tmp0 + tmp3;
|
||||
tmp13 = tmp0 - tmp3;
|
||||
tmp11 = tmp1 + tmp2;
|
||||
tmp12 = tmp1 - tmp2;
|
||||
|
||||
tmp0 = dataBlockBuffer[pointer + 56];
|
||||
tmp1 = dataBlockBuffer[pointer + 40];
|
||||
tmp2 = dataBlockBuffer[pointer + 24];
|
||||
tmp3 = dataBlockBuffer[pointer + 8];
|
||||
|
||||
z1 = tmp0 + tmp3;
|
||||
z2 = tmp1 + tmp2;
|
||||
z3 = tmp0 + tmp2;
|
||||
z4 = tmp1 + tmp3;
|
||||
z5 = (z3 + z4) * FIX_1_175875602;
|
||||
|
||||
tmp0 = tmp0 * FIX_0_298631336;
|
||||
tmp1 = tmp1 * FIX_2_053119869;
|
||||
tmp2 = tmp2 * FIX_3_072711026;
|
||||
tmp3 = tmp3 * FIX_1_501321110;
|
||||
z1 = z1 * -FIX_0_899976223;
|
||||
z2 = z2 * -FIX_2_562915447;
|
||||
z3 = z3 * -FIX_1_961570560;
|
||||
z4 = z4 * -FIX_0_390180644;
|
||||
|
||||
z3 += z5;
|
||||
z4 += z5;
|
||||
|
||||
tmp0 += z1 + z3;
|
||||
tmp1 += z2 + z4;
|
||||
tmp2 += z2 + z3;
|
||||
tmp3 += z1 + z4;
|
||||
|
||||
workSpace[pointer + 0] = ((tmp10 + tmp3 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 56] = ((tmp10 - tmp3 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 8] = ((tmp11 + tmp2 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 48] = ((tmp11 - tmp2 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 16] = ((tmp12 + tmp1 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 40] = ((tmp12 - tmp1 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 24] = ((tmp13 + tmp0 + (1 << F1)) >> F2);
|
||||
workSpace[pointer + 32] = ((tmp13 - tmp0 + (1 << F1)) >> F2);
|
||||
|
||||
pointer++;
|
||||
}
|
||||
|
||||
pointer = 0;
|
||||
|
||||
for(int index = 0; index < 8; index++)
|
||||
{
|
||||
z2 = workSpace[pointer + 2];
|
||||
z3 = workSpace[pointer + 6];
|
||||
|
||||
z1 = (z2 + z3) * FIX_0_541196100;
|
||||
tmp2 = z1 + z3 * -FIX_1_847759065;
|
||||
tmp3 = z1 + z2 * FIX_0_765366865;
|
||||
|
||||
tmp0 = (workSpace[pointer + 0] + workSpace[pointer + 4]) << BITS;
|
||||
tmp1 = (workSpace[pointer + 0] - workSpace[pointer + 4]) << BITS;
|
||||
|
||||
tmp10 = tmp0 + tmp3;
|
||||
tmp13 = tmp0 - tmp3;
|
||||
tmp11 = tmp1 + tmp2;
|
||||
tmp12 = tmp1 - tmp2;
|
||||
|
||||
tmp0 = workSpace[pointer + 7];
|
||||
tmp1 = workSpace[pointer + 5];
|
||||
tmp2 = workSpace[pointer + 3];
|
||||
tmp3 = workSpace[pointer + 1];
|
||||
|
||||
z1 = tmp0 + tmp3;
|
||||
z2 = tmp1 + tmp2;
|
||||
z3 = tmp0 + tmp2;
|
||||
z4 = tmp1 + tmp3;
|
||||
|
||||
z5 = (z3 + z4) * FIX_1_175875602;
|
||||
|
||||
tmp0 = tmp0 * FIX_0_298631336;
|
||||
tmp1 = tmp1 * FIX_2_053119869;
|
||||
tmp2 = tmp2 * FIX_3_072711026;
|
||||
tmp3 = tmp3 * FIX_1_501321110;
|
||||
z1 = z1 * -FIX_0_899976223;
|
||||
z2 = z2 * -FIX_2_562915447;
|
||||
z3 = z3 * -FIX_1_961570560;
|
||||
z4 = z4 * -FIX_0_390180644;
|
||||
|
||||
z3 += z5;
|
||||
z4 += z5;
|
||||
|
||||
tmp0 += z1 + z3;
|
||||
tmp1 += z2 + z4;
|
||||
tmp2 += z2 + z3;
|
||||
tmp3 += z1 + z4;
|
||||
|
||||
data[pointer + 0] = (short) ((tmp10 + tmp3) >> F3);
|
||||
data[pointer + 7] = (short) ((tmp10 - tmp3) >> F3);
|
||||
data[pointer + 1] = (short) ((tmp11 + tmp2) >> F3);
|
||||
data[pointer + 6] = (short) ((tmp11 - tmp2) >> F3);
|
||||
data[pointer + 2] = (short) ((tmp12 + tmp1) >> F3);
|
||||
data[pointer + 5] = (short) ((tmp12 - tmp1) >> F3);
|
||||
data[pointer + 3] = (short) ((tmp13 + tmp0) >> F3);
|
||||
data[pointer + 4] = (short) ((tmp13 - tmp0) >> F3);
|
||||
|
||||
pointer += 8;
|
||||
}
|
||||
|
||||
for(int i = 0; i < data.length; i++)
|
||||
imageSlice.MacroBlocks[macroBlockIndex].DataBlocks[dataBlockIndex][i] = data[i];
|
||||
}
|
||||
|
||||
private uint makeRGB(int r, int g, int b)
|
||||
{
|
||||
r <<= 2;
|
||||
g <<= 1;
|
||||
b <<= 2;
|
||||
|
||||
uint ru = new uint(r);
|
||||
uint gu = new uint(g);
|
||||
uint bu = new uint(b);
|
||||
|
||||
uint retval = ru.shiftLeft(16);
|
||||
retval = retval.or(gu.shiftLeft(8));
|
||||
retval = retval.or(bu);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Blockline:
|
||||
// _______
|
||||
// | 1 | 2 |
|
||||
// |___|___| Y
|
||||
// | 3 | 4 |
|
||||
// |___|___|
|
||||
// ___
|
||||
// | 5 |
|
||||
// |___| Cb
|
||||
// ___
|
||||
// | 6 |
|
||||
// |___| Cr
|
||||
//
|
||||
// Layout in memory
|
||||
// _______________________
|
||||
// | 1 | 2 | 3 | 4 | 5 | 6 | ...
|
||||
// |___|___|___|___|___|___|
|
||||
//
|
||||
|
||||
// Example, suppose the six data sub blocks are as follows:
|
||||
|
||||
// ==============Y0============== ==============Y1==============
|
||||
// ==============Y2============== ==============Y3==============
|
||||
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7,
|
||||
// 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11,
|
||||
// 12, 13, 14, 15, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
// 16, 17, 18, 19, 20, 21, 22, 23, 16, 17, 18, 19, 20, 21, 22, 23, 16, 17,
|
||||
// 18, 19, 20, 21, 22, 23, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
// 24, 25, 26, 27, 28, 29, 30, 31, 24, 25, 26, 27, 28, 29, 30, 31, 24, 25,
|
||||
// 26, 27, 28, 29, 30, 31, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
// 32, 33, 34, 35, 36, 37, 38, 39, 32, 33, 34, 35, 36, 37, 38, 39, 32, 33,
|
||||
// 34, 35, 36, 37, 38, 39, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||
// 40, 41, 42, 43, 44, 45, 46, 47, 40, 41, 42, 43, 44, 45, 46, 47, 40, 41,
|
||||
// 42, 43, 44, 45, 46, 47, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
// 48, 49, 50, 51, 52, 53, 54, 55, 48, 49, 50, 51, 52, 53, 54, 55, 48, 49,
|
||||
// 50, 51, 52, 53, 54, 55, 48, 49, 50, 51, 52, 53, 54, 55,
|
||||
// 56, 57, 58, 59, 60, 61, 62, 63, 56, 57, 58, 59, 60, 61, 62, 63, 56, 57,
|
||||
// 58, 59, 60, 61, 62, 63, 56, 57, 58, 59, 60, 61, 62, 63
|
||||
|
||||
// ==============Cb============== ==============Cr==============
|
||||
|
||||
// 0, 1, 2, 3, | 4, 5, 6, 7, 0, 1, 2, 3, | 4, 5, 6, 7,
|
||||
// 8, 9, 10, 11, | 12, 13, 14, 15, 8, 9, 10, 11, | 12, 13, 14, 15,
|
||||
// 16, 17, 18, 19, | 20, 21, 22, 23, 16, 17, 18, 19, | 20, 21, 22, 23,
|
||||
// 24, 25, 26, 27, | 28, 29, 30, 31, 24, 25, 26, 27, | 28, 29, 30, 31,
|
||||
// ----------------| --------------- --------------- | ---------------
|
||||
// 32, 33, 34, 35, | 36, 37, 38, 39, 32, 33, 34, 35, | 36, 37, 38, 39,
|
||||
// 40, 41, 42, 43, | 44, 45, 46, 47, 40, 41, 42, 43, | 44, 45, 46, 47,
|
||||
// 48, 49, 50, 51, | 52, 53, 54, 55, 48, 49, 50, 51, | 52, 53, 54, 55,
|
||||
// 56, 57, 58, 59, | 60, 61, 62, 63, 56, 57, 58, 59, | 60, 61, 62, 63,
|
||||
|
||||
// Pixel Matrix
|
||||
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, | 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
// 16, 17, 18, 19, 20, 21, 22, 23, | 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
// 32, 33, 34, 35, 36, 37, 38, 39, | 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
// 48, 49, 50, 51, 52, 53, 54, 55, | 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
// 64, 65, 66, 67, 68, 69, 70, 71, | 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
// 80, 81, 82, 83, 84, 85, 86, 87, | 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
// 96, 97, 98, 99, 100, 101, 102, 103, | 104, 105, 106, 107, 108, 109, 110,
|
||||
// 111,
|
||||
// 112, 113, 114, 115, 116, 117, 118, 119, | 120, 121, 122, 123, 124, 125,
|
||||
// 126, 127,
|
||||
// ----------------------------------------|---------------------------------------
|
||||
// 128, 129, 130, 131, 132, 133, 134, 135, | 136, 137, 138, 139, 140, 141,
|
||||
// 142, 143,
|
||||
// 144, 145, 146, 147, 148, 149, 150, 151, | 152, 153, 154, 155, 156, 157,
|
||||
// 158, 159,
|
||||
// 160, 161, 162, 163, 164, 165, 166, 167, | 168, 169, 170, 171, 172, 173,
|
||||
// 174, 175,
|
||||
// 176, 177, 178, 179, 180, 181, 182, 183, | 184, 185, 186, 187, 188, 189,
|
||||
// 190, 191,
|
||||
// 192, 193, 194, 195, 196, 197, 198, 199, | 200, 201, 202, 203, 204, 205,
|
||||
// 206, 207,
|
||||
// 208, 209, 210, 211, 212, 213, 214, 215, | 216, 217, 218, 219, 220, 221,
|
||||
// 222, 223,
|
||||
// 224, 225, 226, 227, 228, 229, 230, 231, | 232, 233, 234, 235, 236, 237,
|
||||
// 238, 239,
|
||||
// 240, 241, 242, 243, 244, 245, 246, 247, | 248, 249, 250, 251, 252, 253,
|
||||
// 254, 255,
|
||||
|
||||
// The four Luma 8x8 matrices (quadrants Y0, Y1, Y2, Y3) form the basis of
|
||||
// the final 16x16 pixel matrix.
|
||||
// The two Croma 8x8 matrices are used to calculate the actual RGB value of
|
||||
// the pixel (RGB565, each pixel is represented by two bytes)
|
||||
|
||||
// Each processing loop processes from each Luma matrix two rows. In each
|
||||
// 'two row' loop the rows are processed
|
||||
// by two columns.
|
||||
|
||||
// First Loop will take (assume there is only one pixel matrix to fill):
|
||||
|
||||
// Quadrant 1
|
||||
// From Cb -> 0
|
||||
// From Cr -> 0
|
||||
// From Y0 -> 0, 8 and 1, 9 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 0, 16 and 1 and 17
|
||||
|
||||
// Quadrant 2
|
||||
// From Cb -> 4
|
||||
// From Cr -> 4
|
||||
// From Y1 -> 0, 8 and 1, 9 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 8, 24 and 9 and 25
|
||||
|
||||
// Quadrant 3
|
||||
// From Cb -> 32
|
||||
// From Cr -> 32
|
||||
// From Y2 -> 0, 8 and 1, 9 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 128, 144 and 129 and 145
|
||||
|
||||
// Quadrant 4
|
||||
// From Cb -> 36
|
||||
// From Cr -> 36
|
||||
// From Y3 -> 0, 8 and 1, 9 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 136, 152 and 137 and 153
|
||||
|
||||
// Second Loop will take (assume there is only one pixel matrix to fill):
|
||||
|
||||
// Quadrant 1
|
||||
// From Cb -> 1
|
||||
// From Cr -> 1
|
||||
// From Y0 -> 2, 10 and 3, 11 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 2, 18 and 3 and 19
|
||||
|
||||
// Quadrant 2
|
||||
// From Cb -> 5
|
||||
// From Cr -> 5
|
||||
// From Y1 -> 2, 10 and 3, 11 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 10, 26 and 11 and 27
|
||||
|
||||
// Quadrant 3
|
||||
// From Cb -> 33
|
||||
// From Cr -> 33
|
||||
// From Y2 -> 2, 10 and 3, 11 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 130, 146 and 131 and 147
|
||||
// Quadrant 4
|
||||
// From Cb -> 37
|
||||
// From Cr -> 37
|
||||
// From Y3 -> 2, 10 and 3, 11 - use Cb and Cr to calculate RGB and place in
|
||||
// pixel matrix in 138, 154 and 139 and 155
|
||||
|
||||
// We need third and fourth loop to complete first two lines of the luma
|
||||
// blocks. At this time we
|
||||
// have written 64 pixels to the pixel matrix.
|
||||
|
||||
// These four loops have to be repeated 4 more times (4 * 64 = 256) to fill
|
||||
// complete pixel matrix.
|
||||
|
||||
// Remark the offsets to use in the pixel matrix have to take into account
|
||||
// that an GroupOfBlocks contains multiple pixel matrices.
|
||||
// So to calculate the real index we have to take that also into account
|
||||
// (blockCount)
|
||||
|
||||
private uint peekStreamData(ByteBuffer stream, int count)
|
||||
{
|
||||
uint data = new uint(0);
|
||||
uint stream_field = streamField;
|
||||
int stream_field_bit_index = streamFieldBitIndex;
|
||||
|
||||
while(count > (32 - stream_field_bit_index) && streamIndex < (imageStream.capacity() >> 2))
|
||||
{
|
||||
data = (data.shiftLeft(32 - stream_field_bit_index)).or(stream_field.shiftRight(stream_field_bit_index));
|
||||
count -= 32 - stream_field_bit_index;
|
||||
stream_field = new uint(stream, streamIndex * 4);
|
||||
stream_field_bit_index = 0;
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
data = data.shiftLeft(count).or(stream_field.shiftRight((32 - count)));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void processStream()
|
||||
{
|
||||
boolean blockY0HasAcComponents = false;
|
||||
boolean blockY1HasAcComponents = false;
|
||||
boolean blockY2HasAcComponents = false;
|
||||
boolean blockY3HasAcComponents = false;
|
||||
boolean blockCbHasAcComponents = false;
|
||||
boolean blockCrHasAcComponents = false;
|
||||
|
||||
// Set streamFieldBitIndex to 32 to make sure that the first call to
|
||||
// ReadStreamData
|
||||
// actually consumes data from the stream
|
||||
streamFieldBitIndex = 32;
|
||||
streamField = new uint(0);
|
||||
streamIndex = 0;
|
||||
sliceIndex = 0;
|
||||
pictureComplete = false;
|
||||
|
||||
while(!pictureComplete && streamIndex < (imageStream.capacity() >> 2))
|
||||
{
|
||||
readHeader();
|
||||
|
||||
if(!pictureComplete)
|
||||
{
|
||||
for(int count = 0; count < blockCount; count++)
|
||||
{
|
||||
uint macroBlockEmpty = readStreamData(1);
|
||||
|
||||
if(macroBlockEmpty.intValue() == (0))
|
||||
{
|
||||
uint acCoefficients = readStreamData(8);
|
||||
|
||||
blockY0HasAcComponents = acCoefficients.shiftRight(0).and(1).intValue() == 1;
|
||||
blockY1HasAcComponents = acCoefficients.shiftRight(1).and(1).intValue() == 1;
|
||||
blockY2HasAcComponents = acCoefficients.shiftRight(2).and(1).intValue() == 1;
|
||||
blockY3HasAcComponents = acCoefficients.shiftRight(3).and(1).intValue() == 1;
|
||||
blockCbHasAcComponents = acCoefficients.shiftRight(4).and(1).intValue() == 1;
|
||||
blockCrHasAcComponents = acCoefficients.shiftRight(5).and(1).intValue() == 1;
|
||||
|
||||
if(acCoefficients.shiftRight(6).and(1).intValue() == 1)
|
||||
{
|
||||
uint quantizer_mode = readStreamData(2);
|
||||
quantizerMode = (int) ((quantizer_mode.intValue() < 2) ? quantizer_mode.flipBits()
|
||||
: quantizer_mode.intValue());
|
||||
}
|
||||
|
||||
getBlockBytes(blockY0HasAcComponents);
|
||||
inverseTransform(count, 0);
|
||||
|
||||
getBlockBytes(blockY1HasAcComponents);
|
||||
inverseTransform(count, 1);
|
||||
|
||||
getBlockBytes(blockY2HasAcComponents);
|
||||
inverseTransform(count, 2);
|
||||
|
||||
getBlockBytes(blockY3HasAcComponents);
|
||||
inverseTransform(count, 3);
|
||||
|
||||
getBlockBytes(blockCbHasAcComponents);
|
||||
inverseTransform(count, 4);
|
||||
|
||||
getBlockBytes(blockCrHasAcComponents);
|
||||
inverseTransform(count, 5);
|
||||
}
|
||||
}
|
||||
|
||||
composeImageSlice();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void readHeader()
|
||||
{
|
||||
alignStreamData();
|
||||
|
||||
uint code = readStreamData(22);
|
||||
uint startCode = new uint(code.and(~0x1F));
|
||||
|
||||
if(startCode.intValue() == 32)
|
||||
{
|
||||
if(((code.and(0x1F).intValue()) == 0x1F))
|
||||
{
|
||||
pictureComplete = true;
|
||||
} else
|
||||
{
|
||||
if(sliceIndex++ == 0)
|
||||
{
|
||||
pictureFormat = (int) readStreamData(2).intValue();
|
||||
resolution = (int) readStreamData(3).intValue();
|
||||
pictureType = (int) readStreamData(3).intValue();
|
||||
quantizerMode = (int) readStreamData(5).intValue();
|
||||
frameIndex = (int) readStreamData(32).intValue();
|
||||
|
||||
switch(pictureFormat)
|
||||
{
|
||||
case CIF:
|
||||
width = CIF_WIDTH << resolution - 1;
|
||||
height = CIG_HEIGHT << resolution - 1;
|
||||
break;
|
||||
case QVGA:
|
||||
width = VGA_WIDTH << resolution - 1;
|
||||
height = VGA_HEIGHT << resolution - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// We assume two bytes per pixel (RGB 565)
|
||||
pixelRowSize = width << 1;
|
||||
|
||||
sliceCount = height >> 4;
|
||||
blockCount = width >> 4;
|
||||
|
||||
if(imageSlice == null)
|
||||
{
|
||||
imageSlice = new ImageSlice(blockCount);
|
||||
pixelData = new uint[width * height];
|
||||
javaPixelData = new int[pixelData.length];
|
||||
} else
|
||||
{
|
||||
if(imageSlice.MacroBlocks.length != blockCount)
|
||||
{
|
||||
imageSlice = new ImageSlice(blockCount);
|
||||
pixelData = new uint[width * height];
|
||||
javaPixelData = new int[pixelData.length];
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
quantizerMode = (int) readStreamData(5).intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint readStreamData(int count)
|
||||
{
|
||||
uint data = new uint(0);
|
||||
|
||||
while(count > (32 - streamFieldBitIndex))
|
||||
{
|
||||
data = (data.shiftLeft((int) (32 - streamFieldBitIndex)).or(streamField.shiftRight(streamFieldBitIndex)));
|
||||
count -= 32 - streamFieldBitIndex;
|
||||
streamField = new uint(imageStream, streamIndex * 4);
|
||||
streamFieldBitIndex = 0;
|
||||
streamIndex++;
|
||||
}
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
data = data.shiftLeft(count).or(streamField.shiftRight(32 - count));
|
||||
streamField.shiftLeftEquals(count);
|
||||
streamFieldBitIndex += count;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int saturate5(int x)
|
||||
{
|
||||
if(x < 0)
|
||||
return 0;
|
||||
|
||||
x >>= 11;
|
||||
return (x > 0x1F) ? 0x1F : x;
|
||||
}
|
||||
|
||||
private static int saturate6(int x)
|
||||
{
|
||||
if(x < 0)
|
||||
return 0;
|
||||
|
||||
x >>= 10;
|
||||
return x > 0x3F ? 0x3F : x;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
package com.codeminders.ardrone.video;
|
||||
|
||||
// Copyright � 2007-2011, PARROT SA, all rights reserved.
|
||||
|
||||
// DISCLAIMER
|
||||
// The APIs is provided by PARROT and contributors "AS IS" and any express or
|
||||
// implied warranties, including, but not limited to, the implied warranties of
|
||||
// merchantability
|
||||
// and fitness for a particular purpose are disclaimed. In no event shall PARROT
|
||||
// and contributors be liable for any direct, indirect, incidental, special,
|
||||
// exemplary, or
|
||||
// consequential damages (including, but not limited to, procurement of
|
||||
// substitute goods or services; loss of use, data, or profits; or business
|
||||
// interruption) however
|
||||
// caused and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of the use
|
||||
// of this
|
||||
// software, even if advised of the possibility of such damage.
|
||||
|
||||
// Author : Daniel Schmidt
|
||||
// Publishing date : 2010-01-06
|
||||
// based on work by : Wilke Jansoone
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// - Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions, the disclaimer and the original author of the source
|
||||
// code.
|
||||
// - Neither the name of the PixVillage Team, nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
|
||||
class ImageSlice
|
||||
{
|
||||
MacroBlock[] MacroBlocks;
|
||||
|
||||
ImageSlice(int macroBlockCount)
|
||||
{
|
||||
MacroBlocks = new MacroBlock[macroBlockCount];
|
||||
|
||||
for(int index = 0; index < macroBlockCount; index++)
|
||||
MacroBlocks[index] = new MacroBlock();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
package com.codeminders.ardrone.video;
|
||||
|
||||
// Copyright � 2007-2011, PARROT SA, all rights reserved.
|
||||
|
||||
// DISCLAIMER
|
||||
// The APIs is provided by PARROT and contributors "AS IS" and any express or
|
||||
// implied warranties, including, but not limited to, the implied warranties of
|
||||
// merchantability
|
||||
// and fitness for a particular purpose are disclaimed. In no event shall PARROT
|
||||
// and contributors be liable for any direct, indirect, incidental, special,
|
||||
// exemplary, or
|
||||
// consequential damages (including, but not limited to, procurement of
|
||||
// substitute goods or services; loss of use, data, or profits; or business
|
||||
// interruption) however
|
||||
// caused and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of the use
|
||||
// of this
|
||||
// software, even if advised of the possibility of such damage.
|
||||
|
||||
// Author : Daniel Schmidt
|
||||
// Publishing date : 2010-01-06
|
||||
// based on work by : Wilke Jansoone
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// - Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions, the disclaimer and the original author of the source
|
||||
// code.
|
||||
// - Neither the name of the PixVillage Team, nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
|
||||
class MacroBlock
|
||||
{
|
||||
short[][] DataBlocks;
|
||||
|
||||
MacroBlock()
|
||||
{
|
||||
DataBlocks = new short[6][];
|
||||
|
||||
for(int index = 0; index < 6; index++)
|
||||
DataBlocks[index] = new short[64];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
|
||||
package com.codeminders.ardrone.video;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
// Copyright � 2007-2011, PARROT SA, all rights reserved.
|
||||
|
||||
// DISCLAIMER
|
||||
// The APIs is provided by PARROT and contributors "AS IS" and any express or
|
||||
// implied warranties, including, but not limited to, the implied warranties of
|
||||
// merchantability
|
||||
// and fitness for a particular purpose are disclaimed. In no event shall PARROT
|
||||
// and contributors be liable for any direct, indirect, incidental, special,
|
||||
// exemplary, or
|
||||
// consequential damages (including, but not limited to, procurement of
|
||||
// substitute goods or services; loss of use, data, or profits; or business
|
||||
// interruption) however
|
||||
// caused and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of the use
|
||||
// of this
|
||||
// software, even if advised of the possibility of such damage.
|
||||
|
||||
// Author : Daniel Schmidt
|
||||
// Publishing date : 2010-01-06
|
||||
// based on work by : Wilke Jansoone
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// - Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions, the disclaimer and the original author of the source
|
||||
// code.
|
||||
// - Neither the name of the PixVillage Team, nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
|
||||
public class uint
|
||||
{
|
||||
private int base2;
|
||||
|
||||
public uint(byte[] bp, int start)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] b = new byte[4];
|
||||
b[0] = bp[start + 3];
|
||||
b[1] = bp[start + 2];
|
||||
b[2] = bp[start + 1];
|
||||
b[3] = bp[start + 0];
|
||||
|
||||
ByteArrayInputStream bas = new ByteArrayInputStream(b);
|
||||
DataInputStream din = new DataInputStream(bas);
|
||||
|
||||
this.base2 = din.readInt();
|
||||
} catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException("error creating uint", e);
|
||||
}
|
||||
}
|
||||
|
||||
public uint(ByteBuffer bp, int start)
|
||||
{
|
||||
try
|
||||
{
|
||||
ByteBuffer bb = ByteBuffer.allocate(4);
|
||||
bb.put(bp.array()[start + 3]);
|
||||
bb.put(bp.array()[start + 2]);
|
||||
bb.put(bp.array()[start + 1]);
|
||||
bb.put(bp.array()[start + 0]);
|
||||
// bb.put(bp.get(start + 3));
|
||||
// bb.put(bp.get(start + 2));
|
||||
// bb.put(bp.get(start + 1));
|
||||
// bb.put(bp.get(start ));
|
||||
bb.flip();
|
||||
this.base2 = bb.getInt();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException("error creating uint", e);
|
||||
}
|
||||
}
|
||||
|
||||
public uint(int base)
|
||||
{
|
||||
this.base2 = base;
|
||||
}
|
||||
|
||||
public uint(uint that)
|
||||
{
|
||||
this.base2 = that.base2;
|
||||
}
|
||||
|
||||
public uint and(int andval)
|
||||
{
|
||||
int retval = base2 & andval;
|
||||
return new uint(retval);
|
||||
}
|
||||
|
||||
public int flipBits()
|
||||
{
|
||||
int base = ~base2;
|
||||
return base;
|
||||
}
|
||||
|
||||
public int intValue()
|
||||
{
|
||||
return base2;
|
||||
|
||||
}
|
||||
|
||||
public uint or(uint orval)
|
||||
{
|
||||
int retval = base2 | orval.base2;
|
||||
return new uint(retval);
|
||||
}
|
||||
|
||||
public uint shiftLeft(int i)
|
||||
{
|
||||
int base = base2;
|
||||
base <<= i;
|
||||
|
||||
return new uint(base);
|
||||
}
|
||||
|
||||
public void shiftLeftEquals(int i)
|
||||
{
|
||||
int base = base2;
|
||||
base <<= i;
|
||||
base2 = base;
|
||||
}
|
||||
|
||||
public uint shiftRight(int i)
|
||||
{
|
||||
int base = base2;
|
||||
base = base >>> i;
|
||||
return new uint(base);
|
||||
}
|
||||
|
||||
public void shiftRightEquals(int i)
|
||||
{
|
||||
int base = base2;
|
||||
base >>>= i;
|
||||
base2 = base;
|
||||
}
|
||||
|
||||
public short times(short i)
|
||||
{
|
||||
return (short) (intValue() * i);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return Integer.toString(base2, 2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
2c363d983267d2440d50ea6d3fd0af1ee581f95d
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>hidapi</artifactId>
|
||||
<version>1.0</version>
|
||||
<url>http://code.google.com/p/javahidapi/</url>
|
||||
<description>JNI wrapper around C/C++ HIDAPI library providing simple java API to work with devices such as USB gamepads, joysticks, keyboards</description>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>BSD License</name>
|
||||
<url>http://www.opensource.org/licenses/bsd-license.php</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<scm>
|
||||
<connection>scm:hg:http://code.google.com/p/javahidapi/</connection>
|
||||
</scm>
|
||||
</scm>
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
b13251b02ad7d824472950e02d05746250a55685
|
||||
@@ -0,0 +1,18 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.codeminders</groupId>
|
||||
<artifactId>javadrone</artifactId>
|
||||
<version>${currentVersion}</version>
|
||||
<name>javadrone</name>
|
||||
<packaging>pom</packaging>
|
||||
<description>Java API and demo programs to control Parrot's AR.Drone</description>
|
||||
<modules>
|
||||
<module>javadrone-api</module>
|
||||
<module>controltower</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<currentVersion>1.2</currentVersion>
|
||||
</properties>
|
||||
</project>
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys,os
|
||||
from subprocess import Popen,PIPE,call
|
||||
import re,string
|
||||
|
||||
INTF="wlan0"
|
||||
|
||||
def runCmd(cmd):
|
||||
retcode = call(string.join(cmd, " "), shell=True)
|
||||
if retcode < 0:
|
||||
print >>sys.stderr, "Child was terminated by signal", -retcode
|
||||
sys.exit(1)
|
||||
elif retcode!=0:
|
||||
print >>sys.stderr, "Child returned", retcode
|
||||
sys.exit(1)
|
||||
|
||||
if os.getuid()!=0:
|
||||
print >>sys.stderr, "Not enough privilidges"
|
||||
sys.exit(1)
|
||||
|
||||
print "Detecting AR Drone"
|
||||
|
||||
runCmd(["/sbin/ifconfig", INTF ,"up"])
|
||||
scanres = Popen(["/sbin/iwlist", INTF, "scan"], stdout=PIPE).communicate()[0]
|
||||
SSID=None
|
||||
cells = re.split(r'\s+Cell [0-9]+',scanres)
|
||||
for c in cells:
|
||||
m = re.search(r'\s+ESSID\:"(ardrone_[0-9]+)',c)
|
||||
if m:
|
||||
SSID=m.group(1)
|
||||
m = re.search(r'\s+Channel\:([0-9]+)',c)
|
||||
if not m:
|
||||
print >>sys.stderr, "Could not detect channel!"
|
||||
sys.exit(1)
|
||||
CHANNEL = m.group(1)
|
||||
break
|
||||
|
||||
if SSID:
|
||||
print "Found Ar.Drone with SSID %s on channel %s" % (SSID,CHANNEL)
|
||||
else:
|
||||
print >>sys.stderr, "Ar.Drone not found"
|
||||
sys.exit(1)
|
||||
|
||||
print "Configuring WiFi interface"
|
||||
|
||||
runCmd(["/sbin/ifconfig", INTF ,"up"])
|
||||
runCmd(["/sbin/iwconfig", INTF ,"mode","ad-hoc"])
|
||||
runCmd(["/sbin/iwconfig", INTF ,"channel",str(CHANNEL)])
|
||||
runCmd(["/sbin/iwconfig", INTF ,"Bit","54Mb/s"])
|
||||
runCmd(["/sbin/iwconfig", INTF ,"essid",SSID])
|
||||
|
||||
print "Connecting to Ar.Drone"
|
||||
|
||||
runCmd(["/sbin/dhclient", INTF])
|
||||
runCmd(["/sbin/route", "delete", "default", "gw", "192.168.1.1"])
|
||||
|
||||
print "Checking connectivity"
|
||||
runCmd(["/bin/ping", "-c3", "192.168.1.1"])
|
||||
|
||||
print "Ar.Drone is ready with IP 192.168.1.1"
|
||||