package org.droidplanner.services.android.impl.core.drone.autopilot.apm; import android.content.Context; import android.os.Bundle; import android.os.Handler; import com.MAVLink.Messages.MAVLinkMessage; import com.github.zafarkhaja.semver.Version; import com.o3dr.android.client.apis.CapabilityApi; import com.o3dr.services.android.lib.drone.action.ControlActions; import com.o3dr.services.android.lib.drone.attribute.error.CommandExecutionError; import com.o3dr.services.android.lib.drone.property.Parameter; import com.o3dr.services.android.lib.model.ICommandListener; import org.droidplanner.services.android.impl.communication.model.DataLink; import org.droidplanner.services.android.impl.core.MAVLink.MavLinkCommands; import org.droidplanner.services.android.impl.core.drone.DroneInterfaces; import org.droidplanner.services.android.impl.core.drone.DroneManager; import org.droidplanner.services.android.impl.core.drone.LogMessageListener; import org.droidplanner.services.android.impl.core.drone.profiles.ParameterManager; import org.droidplanner.services.android.impl.core.drone.variables.ApmModes; import org.droidplanner.services.android.impl.core.drone.variables.State; import org.droidplanner.services.android.impl.core.firmware.FirmwareType; import org.droidplanner.services.android.impl.core.model.AutopilotWarningParser; import org.droidplanner.services.android.impl.utils.CommonApiUtils; import java.util.concurrent.ConcurrentHashMap; /** * Created by Fredia Huya-Kouadio on 7/27/15. */ public class ArduCopter extends ArduPilot { private static final Version ARDU_COPTER_V3_3 = Version.forIntegers(3,3,0); private static final Version ARDU_COPTER_V3_4 = Version.forIntegers(3,4,0); private static final Version BRAKE_FEATURE_FIRMWARE_VERSION = ARDU_COPTER_V3_3; private static final Version COMPASS_CALIBRATION_MIN_VERSION = ARDU_COPTER_V3_4; private final ConcurrentHashMap<String, ICommandListener> manualControlStateListeners = new ConcurrentHashMap<>(); public ArduCopter(String droneId, Context context, DataLink.DataLinkProvider<MAVLinkMessage> mavClient, Handler handler, AutopilotWarningParser warningParser, LogMessageListener logListener) { super(droneId, context, mavClient, handler, warningParser, logListener); } @Override public FirmwareType getFirmwareType() { return FirmwareType.ARDU_COPTER; } @Override protected boolean setVelocity(Bundle data, ICommandListener listener){ //Retrieve the normalized values float normalizedXVel = data.getFloat(ControlActions.EXTRA_VELOCITY_X); float normalizedYVel = data.getFloat(ControlActions.EXTRA_VELOCITY_Y); float normalizedZVel = data.getFloat(ControlActions.EXTRA_VELOCITY_Z); double attitudeInRad = Math.toRadians(attitude.getYaw()); final double cosAttitude = Math.cos(attitudeInRad); final double sinAttitude = Math.sin(attitudeInRad); float projectedX = (float) (normalizedXVel * cosAttitude) - (float) (normalizedYVel * sinAttitude); float projectedY = (float) (normalizedXVel * sinAttitude) + (float) (normalizedYVel * cosAttitude); //Retrieve the speed parameters. float defaultSpeed = 5; //m/s ParameterManager parameterManager = getParameterManager(); //Retrieve the horizontal speed value Parameter horizSpeedParam = parameterManager.getParameter("WPNAV_SPEED"); double horizontalSpeed = horizSpeedParam == null ? defaultSpeed : horizSpeedParam.getValue() / 100; //Retrieve the vertical speed value. String vertSpeedParamName = normalizedZVel >= 0 ? "WPNAV_SPEED_UP" : "WPNAV_SPEED_DN"; Parameter vertSpeedParam = parameterManager.getParameter(vertSpeedParamName); double verticalSpeed = vertSpeedParam == null ? defaultSpeed : vertSpeedParam.getValue() / 100; MavLinkCommands.setVelocityInLocalFrame(this, (float) (projectedX * horizontalSpeed), (float) (projectedY * horizontalSpeed), (float) (normalizedZVel * verticalSpeed), listener); return true; } @Override public void destroy(){ super.destroy(); manualControlStateListeners.clear(); } @Override protected boolean enableManualControl(Bundle data, ICommandListener listener){ boolean enable = data.getBoolean(ControlActions.EXTRA_DO_ENABLE); String appId = data.getString(DroneManager.EXTRA_CLIENT_APP_ID); State state = getState(); ApmModes vehicleMode = state.getMode(); if(enable){ if(vehicleMode == ApmModes.ROTOR_GUIDED){ CommonApiUtils.postSuccessEvent(listener); } else{ state.changeFlightMode(ApmModes.ROTOR_GUIDED, listener); } if(listener != null) { manualControlStateListeners.put(appId, listener); } } else{ manualControlStateListeners.remove(appId); if(vehicleMode != ApmModes.ROTOR_GUIDED){ CommonApiUtils.postSuccessEvent(listener); } else{ state.changeFlightMode(ApmModes.ROTOR_LOITER, listener); } } return true; } @Override public void notifyDroneEvent(DroneInterfaces.DroneEventsType event){ switch(event){ case MODE: //Listen for vehicle mode updates, and update the manual control state listeners appropriately ApmModes currentMode = getState().getMode(); for(ICommandListener listener: manualControlStateListeners.values()) { if (currentMode == ApmModes.ROTOR_GUIDED) { CommonApiUtils.postSuccessEvent(listener); } else { CommonApiUtils.postErrorEvent(CommandExecutionError.COMMAND_FAILED, listener); } } break; } super.notifyDroneEvent(event); } @Override protected boolean isFeatureSupported(String featureId){ switch(featureId){ case CapabilityApi.FeatureIds.KILL_SWITCH: return CommonApiUtils.isKillSwitchSupported(this); case CapabilityApi.FeatureIds.COMPASS_CALIBRATION: return getFirmwareVersionNumber().greaterThanOrEqualTo(COMPASS_CALIBRATION_MIN_VERSION); default: return super.isFeatureSupported(featureId); } } @Override protected boolean brakeVehicle(ICommandListener listener) { if (getFirmwareVersionNumber().greaterThanOrEqualTo(BRAKE_FEATURE_FIRMWARE_VERSION)) { getState().changeFlightMode(ApmModes.ROTOR_BRAKE, listener); } else { super.brakeVehicle(listener); } return true; } }