780 linhas
26 KiB
Java
780 linhas
26 KiB
Java
|
|
package com.codeminders.ardrone;
|
|
|
|
import java.io.IOException;
|
|
import java.net.*;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import com.codeminders.ardrone.data.ARDroneDataReader;
|
|
import com.codeminders.ardrone.data.ChannelProcessor;
|
|
import com.codeminders.ardrone.data.navdata.FlyingState;
|
|
import com.codeminders.ardrone.data.navdata.Mode;
|
|
import com.codeminders.ardrone.data.reader.LigthUDPDataReader;
|
|
import com.codeminders.ardrone.data.reader.TCPDataRader;
|
|
import com.codeminders.ardrone.data.reader.UDPDataReader;
|
|
import com.codeminders.ardrone.commands.*;
|
|
import com.codeminders.ardrone.data.decoder.ardrone10.ARDrone10NavDataDecoder;
|
|
import com.codeminders.ardrone.data.decoder.ardrone10.ARDrone10VideoDataDecoder;
|
|
import com.codeminders.ardrone.data.logger.ARDroneDataReaderAndLogWrapper;
|
|
import com.codeminders.ardrone.data.logger.DataLogger;
|
|
import com.codeminders.ardrone.version.DroneVersionReader;
|
|
import com.codeminders.ardrone.version.ftp.DroneFTPversionReader;
|
|
|
|
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 NAVDATA_BUFFER_SIZE = 4096;
|
|
private static final int VIDEO_BUFFER_SIZE = 100 * 1024;
|
|
|
|
final static byte[] DEFAULT_DRONE_IP = { (byte) 192, (byte) 168, (byte) 1, (byte) 1 };
|
|
|
|
private static final int DEFAULT_DRONE_VERSION = 1;
|
|
|
|
private InetAddress drone_addr;
|
|
private DatagramSocket cmd_socket;
|
|
|
|
private CommandQueue cmd_queue = new CommandQueue(CMD_QUEUE_SIZE);
|
|
|
|
private ChannelProcessor drone_nav_channel_processor;
|
|
private ChannelProcessor drone_video_channel_processor;
|
|
|
|
private CommandSender cmd_sender;
|
|
|
|
private Thread cmd_sending_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>();
|
|
|
|
private int navDataReconnectTimeout = 1000; // 1 second
|
|
private int videoReconnectTimeout = 1000; // 1 second
|
|
|
|
private VideoDataDecoder ext_video_data_decoder;
|
|
private NavDataDecoder ext_nav_data_decoder;
|
|
|
|
private DroneVersionReader versionReader;
|
|
|
|
public ARDrone() throws UnknownHostException
|
|
{
|
|
this(InetAddress.getByAddress(DEFAULT_DRONE_IP), 1000, 1000);
|
|
}
|
|
|
|
public ARDrone(InetAddress drone_addr, int navDataReconnectTimeout, int videoReconnectTimeout)
|
|
{
|
|
this.drone_addr = drone_addr;
|
|
this.navDataReconnectTimeout = navDataReconnectTimeout;
|
|
this.videoReconnectTimeout = videoReconnectTimeout;
|
|
|
|
this.versionReader = new DroneFTPversionReader(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.fine("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.log(Level.FINE ,"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
|
|
{
|
|
connect(null, null);
|
|
}
|
|
|
|
public void connect(DataLogger videoLogger, DataLogger navdataLogger) throws IOException
|
|
{
|
|
try
|
|
{
|
|
int version = DEFAULT_DRONE_VERSION;
|
|
try {
|
|
String versionStr = versionReader.readDroneVersion();
|
|
log.log(Level.FINER, "Drone version string: " + versionStr);
|
|
version = Integer.parseInt(versionStr.substring(0, versionStr.indexOf('.')));
|
|
} catch (NumberFormatException e) {
|
|
log.log(Level.SEVERE, "Failed to discover drone version. Using configuration for drone version: " + version, e);
|
|
}
|
|
|
|
cmd_socket = new DatagramSocket();
|
|
|
|
cmd_sender = new CommandSender(cmd_queue, this, drone_addr, cmd_socket);
|
|
cmd_sending_thread = new Thread(cmd_sender);
|
|
cmd_sending_thread.setName("Command Sender");
|
|
cmd_sending_thread.start();
|
|
|
|
enableVideo();
|
|
enableAutomaticVideoBitrate();
|
|
|
|
NavDataDecoder nav_data_decoder = (null == ext_nav_data_decoder) ?
|
|
new ARDrone10NavDataDecoder(this, NAVDATA_BUFFER_SIZE)
|
|
:
|
|
ext_nav_data_decoder;
|
|
|
|
ARDroneDataReader nav_data_reader = (null == navdataLogger) ?
|
|
new LigthUDPDataReader(drone_addr, NAVDATA_PORT, navDataReconnectTimeout)
|
|
:
|
|
new ARDroneDataReaderAndLogWrapper(new LigthUDPDataReader(drone_addr, NAVDATA_PORT, navDataReconnectTimeout), navdataLogger);
|
|
|
|
drone_nav_channel_processor = new ChannelProcessor(nav_data_reader, nav_data_decoder);
|
|
|
|
VideoDataDecoder video_data_decoder = (null == ext_video_data_decoder) ?
|
|
getVideoDecoder(version)
|
|
:
|
|
ext_video_data_decoder;
|
|
ARDroneDataReader video_data_reader = (null == videoLogger) ?
|
|
getVideoReader(version)
|
|
:
|
|
new ARDroneDataReaderAndLogWrapper(new UDPDataReader(drone_addr, VIDEO_PORT, videoReconnectTimeout), videoLogger);
|
|
|
|
if (null != video_data_reader && null != video_data_decoder) {
|
|
drone_video_channel_processor = new ChannelProcessor(video_data_reader, video_data_decoder);
|
|
}
|
|
|
|
changeState(State.CONNECTING);
|
|
|
|
} catch(IOException ex)
|
|
{
|
|
changeToErrorState(ex);
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
private VideoDataDecoder getVideoDecoder(int version) throws IOException {
|
|
switch (version) {
|
|
case 1:
|
|
return new ARDrone10VideoDataDecoder(this, VIDEO_BUFFER_SIZE);
|
|
case 2:
|
|
return null; // no decoder implemented yet
|
|
default:
|
|
return new ARDrone10VideoDataDecoder(this, VIDEO_BUFFER_SIZE);
|
|
}
|
|
|
|
}
|
|
|
|
private ARDroneDataReader getVideoReader(int version) throws IOException {
|
|
switch (version) {
|
|
case 1:
|
|
return new LigthUDPDataReader(drone_addr, VIDEO_PORT, videoReconnectTimeout);
|
|
case 2:
|
|
return new TCPDataRader(drone_addr, VIDEO_PORT, videoReconnectTimeout);
|
|
default:
|
|
return new LigthUDPDataReader(drone_addr, VIDEO_PORT, videoReconnectTimeout);
|
|
}
|
|
}
|
|
|
|
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(drone_nav_channel_processor != null)
|
|
drone_nav_channel_processor.finish();
|
|
|
|
if(drone_video_channel_processor != null)
|
|
drone_video_channel_processor.finish();
|
|
|
|
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 enableVideo() throws IOException
|
|
{
|
|
setConfigOption("general:video_enable", "TRUE");
|
|
}
|
|
public void disableVideo() throws IOException
|
|
{
|
|
setConfigOption("general:video_enable", "FALSE");
|
|
}
|
|
|
|
|
|
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
|
|
protected void navDataReceived(NavData nd)
|
|
{
|
|
if(nd.isBatteryTooLow() || nd.isNotEnoughPower())
|
|
{
|
|
log.severe("Battery pb " + nd.toString());
|
|
}
|
|
|
|
synchronized(emergency_mutex)
|
|
{
|
|
emergencyMode = nd.isEmergency();
|
|
}
|
|
|
|
try
|
|
{
|
|
synchronized(state_mutex)
|
|
{
|
|
if(state != State.CONNECTING && nd.isControlReceived())
|
|
{
|
|
log.fine("Control received! ACK!");
|
|
cmd_queue.add(new ControlCommand(5, 0));
|
|
}
|
|
|
|
if(state == State.TAKING_OFF && nd.getFlyingState() == FlyingState.FLYING)
|
|
{
|
|
log.fine("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.fine("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() == Mode.BOOTSTRAP)
|
|
{
|
|
changeState(State.BOOTSTRAP);
|
|
} else if(state == State.BOOTSTRAP && nd.getMode() == 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.log(Level.SEVERE, "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
|
|
protected 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(state == State.DEMO)
|
|
{
|
|
return; // OK! We are now connected
|
|
} else if(state == State.ERROR || state == State.DISCONNECTED)
|
|
{
|
|
throw new IOException("Connection Error");
|
|
} else if((System.currentTimeMillis() - since) >= how_long)
|
|
{
|
|
try
|
|
{
|
|
disconnect();
|
|
} catch(IOException e)
|
|
{
|
|
}
|
|
// Timeout, too late
|
|
throw new IOException("Timeout connecting to ARDrone");
|
|
}
|
|
|
|
|
|
long p = Math.min(how_long - (System.currentTimeMillis() - since), how_long);
|
|
if(p > 0)
|
|
{
|
|
try
|
|
{
|
|
state_mutex.wait(p);
|
|
} catch(InterruptedException e)
|
|
{
|
|
// Ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void pauseNavData() {
|
|
if (null != drone_nav_channel_processor) {
|
|
drone_nav_channel_processor.pause();
|
|
}
|
|
}
|
|
|
|
public void resumeNavData() {
|
|
if (null != drone_nav_channel_processor) {
|
|
drone_nav_channel_processor.resume();
|
|
}
|
|
}
|
|
|
|
public void pauseVideo() {
|
|
if (null != drone_video_channel_processor) {
|
|
drone_video_channel_processor.pause();
|
|
}
|
|
}
|
|
|
|
public void resumeVideo() {
|
|
if (null != drone_video_channel_processor) {
|
|
drone_video_channel_processor.resume();
|
|
}
|
|
}
|
|
|
|
public State getState() {
|
|
return state;
|
|
}
|
|
|
|
public void setExternalVideoDataDecoder(VideoDataDecoder ext_video_data_decoder) {
|
|
this.ext_video_data_decoder = ext_video_data_decoder;
|
|
}
|
|
|
|
public void setExternalVideoDataDecoder(NavDataDecoder ext_nav_data_decoder) {
|
|
this.ext_nav_data_decoder = ext_nav_data_decoder;
|
|
}
|
|
/**
|
|
* Read Drone version.
|
|
* @return Drone version string e.g. "1.10.10". null - if version can't be obtained
|
|
*/
|
|
public String getDroneVersion() {
|
|
try {
|
|
return versionReader.readDroneVersion();
|
|
} catch (IOException e) {
|
|
log.log(Level.SEVERE, "Failed to read drone version.", e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
}
|