1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-01-17 02:52:12 +01:00

Merge remote-tracking branch 'origin/Brian-PipXtreme-V2' into Brian-PipXtreme-V2

This commit is contained in:
Brian Webb 2012-04-06 17:34:46 -07:00
commit 16c9c145ff
24 changed files with 5130 additions and 3452 deletions

View File

@ -68,7 +68,7 @@ help:
@echo " Here is a summary of the available targets:"
@echo
@echo " [Tool Installers]"
@echo " qt_sdk_install - Install the QT v4.6.2 tools"
@echo " qt_sdk_install - Install the QT v4.7.3 tools"
@echo " arm_sdk_install - Install the Code Sourcery ARM gcc toolchain"
@echo " openocd_install - Install the OpenOCD JTAG daemon"
@echo " stm32flash_install - Install the stm32flash tool for unbricking boards"
@ -160,20 +160,30 @@ $(BUILD_DIR):
###############################################################
# Set up QT toolchain
QT_SDK_DIR := $(TOOLS_DIR)/qtsdk-2010.02
QT_SDK_DIR := $(TOOLS_DIR)/qtsdk-v1.2
.PHONY: qt_sdk_install
qt_sdk_install: QT_SDK_URL := http://get.qt.nokia.com/qtsdk/qt-sdk-linux-x86-opensource-2010.02.bin
qt_sdk_install: QT_SDK_FILE := $(notdir $(QT_SDK_URL))
qt_sdk_install: QT_SDK_URL := http://www.developer.nokia.com/dp?uri=http://sw.nokia.com/id/8ea74da4-fec1-4277-8b26-c58cc82e204b/Qt_SDK_Lin32_offline
qt_sdk_install: QT_SDK_FILE := Qt_SDK_Lin32_offline_v1_2_en.run
#qt_sdk_install: QT_SDK_URL := http://www.developer.nokia.com/dp?uri=http://sw.nokia.com/id/c365bbf5-c2b9-4dda-9c1f-34b2c8d07785/Qt_SDK_Lin32_offline_v1_1_2
#qt_sdk_install: QT_SDK_FILE := Qt_SDK_Lin32_offline_v1_1_2_en.run
# order-only prereq on directory existance:
qt_sdk_install : | $(DL_DIR) $(TOOLS_DIR)
qt_sdk_install: qt_sdk_clean
# download the source only if it's newer than what we already have
$(V1) wget -N -P "$(DL_DIR)" "$(QT_SDK_URL)"
$(V1) wget -N --content-disposition -P "$(DL_DIR)" "$(QT_SDK_URL)"
# tell the user exactly which path they should select in the GUI
$(V1) echo "*** NOTE NOTE NOTE ***"
$(V1) echo "*"
$(V1) echo "* In the GUI, please use exactly this path as the installation path:"
$(V1) echo "* $(QT_SDK_DIR)"
$(V1) echo "*"
$(V1) echo "*** NOTE NOTE NOTE ***"
#installer is an executable, make it executable and run it
$(V1) chmod u+x "$(DL_DIR)/$(QT_SDK_FILE)"
"$(DL_DIR)/$(QT_SDK_FILE)" --installdir "$(QT_SDK_DIR)"
$(V1) "$(DL_DIR)/$(QT_SDK_FILE)" -style cleanlooks
.PHONY: qt_sdk_clean
qt_sdk_clean:
@ -257,7 +267,7 @@ stm32flash_clean:
##############################
ifeq ($(shell [ -d "$(QT_SDK_DIR)" ] && echo "exists"), exists)
QMAKE=$(QT_SDK_DIR)/qt/bin/qmake
QMAKE=$(QT_SDK_DIR)/Desktop/Qt/4.8.0/gcc/bin/qmake
else
# not installed, hope it's in the path...
QMAKE=qmake

View File

@ -60,7 +60,8 @@ typedef struct {
uint8_t data_size;
} PHPacketHeader;
#define PH_MAX_DATA (PH_MAX_PACKET - sizeof(PHPacketHeader) - RS_ECC_NPARITY)
#define PH_MAX_DATA (PIOS_PH_MAX_PACKET - sizeof(PHPacketHeader) - RS_ECC_NPARITY)
#define PH_PACKET_SIZE(p) (p->data + p->header.data_size - (uint8_t*)p + RS_ECC_NPARITY)
typedef struct {
PHPacketHeader header;
uint8_t data[PH_MAX_DATA + RS_ECC_NPARITY];
@ -70,24 +71,20 @@ typedef struct {
uint8_t txWinSize;
uint16_t maxConnections;
uint32_t id;
void *dev;
uint8_t (*output_stream)(void *dev, PHPacketHandle packet);
void (*set_baud)(uint32_t baud);
void (*data_handler)(void *dev, uint8_t *data, uint8_t len);
void (*receiver_handler)(void *dev, uint8_t *data, uint8_t len);
} PacketHandlerConfig;
typedef int32_t (*PHOutputStream)(PHPacketHandle packet);
typedef void (*PHDataHandler)(uint8_t *data, uint8_t len);
typedef void* PHInstHandle;
// Public functions
PHInstHandle PHInitialize(PacketHandlerConfig *cfg);
void PHRegisterOutputStream(PHInstHandle h, PHOutputStream f);
void PHRegisterDataHandler(PHInstHandle h, PHDataHandler f);
uint32_t PHConnect(PHInstHandle h, uint32_t dest_id);
PHPacketHandle PHGetRXPacket(PHInstHandle h);
PHPacketHandle PHGetTXPacket(PHInstHandle h);
PHPacketHandle PHReserveTXPacket(PHInstHandle h);
void PHReleaseLock(PHInstHandle h, bool keep_packet);
void PHReleaseTXPacket(PHInstHandle h, PHPacketHandle p);
uint8_t PHTransmitPacket(PHInstHandle h, PHPacketHandle p);
uint8_t PHReceivePacket(PHInstHandle h, PHPacketHandle p);

View File

@ -32,6 +32,8 @@
#include "aes.h"
#include "ecc.h"
extern char *debug_msg;
// Private types and constants
typedef struct {
PacketHandlerConfig cfg;
@ -42,6 +44,8 @@ typedef struct {
PHPacket rx_packet;
PHOutputStream stream;
xSemaphoreHandle lock;
PHOutputStream output_stream;
PHDataHandler data_handler;
} PHPacketData, *PHPacketDataHandle;
// Private functions
@ -49,14 +53,6 @@ static uint8_t PHLSendAck(PHPacketDataHandle data, PHPacketHandle p);
static uint8_t PHLSendNAck(PHPacketDataHandle data, PHPacketHandle p);
static uint8_t PHLTransmitPacket(PHPacketDataHandle data, PHPacketHandle p);
/*
static void
byte_err (int err, int loc, unsigned char *dst)
{
dst[loc-1] ^= err;
}
*/
/**
* Initialize the Packet Handler library
* \param[in] txWinSize The transmission window size (number of tx packet buffers).
@ -92,6 +88,30 @@ PHInstHandle PHInitialize(PacketHandlerConfig *cfg)
return (PHInstHandle)data;
}
/**
* Register an output stream handler
* \param[in] h The packet handler instance data pointer.
* \param[in] f The output stream handler function
*/
void PHRegisterOutputStream(PHInstHandle h, PHOutputStream f)
{
PHPacketDataHandle data = (PHPacketDataHandle)h;
data->output_stream = f;
}
/**
* Register a data handler
* \param[in] h The packet handler instance data pointer.
* \param[in] f The data handler function
*/
void PHRegisterDataHandler(PHInstHandle h, PHDataHandler f)
{
PHPacketDataHandle data = (PHPacketDataHandle)h;
data->data_handler = f;
}
/**
* Get a packet out of the transmit buffer.
* \param[in] h The packet handler instance data pointer.
@ -104,58 +124,6 @@ uint32_t PHConnect(PHInstHandle h, uint32_t dest_id)
return 1;
}
/**
* Temporarily reserve the next packet in the TX packet window.
* This function places a tempoary hold on the next TX packet and
* retains the packet handler lock.
*
* NOTE: PHReleaseLock must be called to release the lock and retain
* or release the reserved packet.
*
* \param[in] h The packet handler instance data pointer.
* \return PHPacketHandle A pointer to the packet buffer.
* \return 0 No packets buffers avaiable in the transmit window.
*/
PHPacketHandle PHReserveTXPacket(PHInstHandle h)
{
PHPacketDataHandle data = (PHPacketDataHandle)h;
// Lock
xSemaphoreTakeRecursive(data->lock, portMAX_DELAY);
// Is the window full?
uint8_t next_end = (data->tx_win_end + 1) % data->cfg.txWinSize;
if(next_end == data->tx_win_start) {
// Release the lock
xSemaphoreGiveRecursive(data->lock);
return NULL;
}
// Return a pointer to the packet at the end of the TX window.
return data->tx_packets + data->tx_win_end;
}
/**
* Get a packet out of the transmit buffer and keep the lock.
* NOTE: PHReleaseLock must be called to release the lock.
* \param[in] h The packet handler instance data pointer.
* \param[in] keep_packet Maintain a permanent (until released) lock on the packet.
*/
void PHReleaseLock(PHInstHandle h, bool keep_packet)
{
PHPacketDataHandle data = (PHPacketDataHandle)h;
uint8_t next_end = (data->tx_win_end + 1) % data->cfg.txWinSize;
// Increment the end index if packet is being kept.
if (keep_packet)
data->tx_win_end = next_end;
// Release lock
xSemaphoreGiveRecursive(data->lock);
}
/**
* Get a packet out of the transmit buffer.
* \param[in] h The packet handler instance data pointer.
@ -164,8 +132,22 @@ void PHReleaseLock(PHInstHandle h, bool keep_packet)
*/
PHPacketHandle PHGetTXPacket(PHInstHandle h)
{
PHPacketHandle p = PHReserveTXPacket(h);
PHReleaseLock(p, 1);
PHPacketDataHandle data = (PHPacketDataHandle)h;
// Lock
xSemaphoreTakeRecursive(data->lock, portMAX_DELAY);
PHPacketHandle p = data->tx_packets + data->tx_win_end;
// Is the window full?
uint8_t next_end = (data->tx_win_end + 1) % data->cfg.txWinSize;
if(next_end == data->tx_win_start)
return NULL;
data->tx_win_end = next_end;
// Release lock
xSemaphoreGiveRecursive(data->lock);
// Return a pointer to the packet at the end of the TX window.
return p;
}
@ -268,8 +250,8 @@ uint8_t PHReceivePacket(PHInstHandle h, PHPacketHandle p)
PHLSendAck(data, p);
// Pass on the data.
if(data->cfg.data_handler)
data->cfg.data_handler(data->cfg.dev, p->data, p->header.data_size);
if(data->data_handler)
data->data_handler(p->data, p->header.data_size);
}
break;
@ -307,15 +289,18 @@ uint8_t PHReceivePacket(PHInstHandle h, PHPacketHandle p)
break;
default:
case PACKET_TYPE_DATA:
if (!rx_error)
// Pass on the data to the receiver handler.
if(data->cfg.data_handler)
data->cfg.data_handler(data->cfg.dev, p->data, p->header.data_size);
if(data->data_handler)
data->data_handler(p->data, p->header.data_size);
break;
default:
break;
}
return 1;
@ -331,14 +316,17 @@ uint8_t PHReceivePacket(PHInstHandle h, PHPacketHandle p)
static uint8_t PHLTransmitPacket(PHPacketDataHandle data, PHPacketHandle p)
{
if(!data->output_stream)
return 0;
// Set the sequence ID to the current ID.
p->header.tx_seq = data->tx_seq_id++;
// Add the error correcting code.
encode_data((unsigned char*)p, PHPacketSize(p), (unsigned char*)p);
// Transmit the packet using the output stream.
if(!data->cfg.output_stream(data->cfg.dev, p))
if(data->output_stream(p) == -1)
return 0;
return 1;
@ -362,10 +350,8 @@ static uint8_t PHLSendAck(PHPacketDataHandle data, PHPacketHandle p)
ack.rx_seq = p->header.tx_seq;
ack.data_size = 0;
// Set the packet.
PHLTransmitPacket(data, (PHPacketHandle)&ack);
return 1;
// Send the packet.
return PHLTransmitPacket(data, (PHPacketHandle)&ack);
}
/**
@ -387,7 +373,5 @@ static uint8_t PHLSendNAck(PHPacketDataHandle data, PHPacketHandle p)
ack.data_size = 0;
// Set the packet.
PHLTransmitPacket(data, (PHPacketHandle)&ack);
return 1;
return PHLTransmitPacket(data, (PHPacketHandle)&ack);
}

View File

@ -30,28 +30,34 @@
// ****************
#include "openpilot.h"
#include "hwsettings.h"
#include "radiocombridge.h"
#include <openpilot.h>
#include <hwsettings.h>
#include <radiocombridge.h>
#include <packet_handler.h>
#include <stdbool.h>
#include "ecc.h"
extern char *debug_msg;
// ****************
// Private functions
static void radio2ComBridgeTask(void *parameters);
static void com2RadioBridgeTask(void *parameters);
static int32_t transmitData(uint8_t * data, int32_t length);
static int32_t transmitPacket(PHPacketHandle packet);
static void receiveData(uint8_t *buf, uint8_t len);
static void updateSettings();
// ****************
// Private constants
//#define STACK_SIZE_BYTES 280
#define STACK_SIZE_BYTES 350
#define STACK_SIZE_BYTES 300
#define TASK_PRIORITY (tskIDLE_PRIORITY + 1)
#define BRIDGE_BUF_LEN 10
#define BRIDGE_BUF_LEN 128
// ****************
// Private types
@ -76,6 +82,13 @@ typedef struct {
uint32_t com_tx_errors;
uint32_t radio_tx_errors;
// The packet handler.
PHInstHandle packet_handler;
portTickType send_timeout;
uint16_t min_packet_size;
PHPacket packet;
} RadioComBridgeData;
// ****************
@ -92,8 +105,8 @@ static int32_t RadioComBridgeStart(void)
{
if(data) {
// Start the tasks
xTaskCreate(radio2ComBridgeTask, (signed char *)"Radio2ComBridge", STACK_SIZE_BYTES/4, NULL, TASK_PRIORITY, &(data->radio2ComBridgeTaskHandle));
xTaskCreate(com2RadioBridgeTask, (signed char *)"Com2RadioBridge", STACK_SIZE_BYTES/4, NULL, TASK_PRIORITY, &(data->com2RadioBridgeTaskHandle));
xTaskCreate(radio2ComBridgeTask, (signed char *)"Radio2ComBridge", STACK_SIZE_BYTES/2, NULL, TASK_PRIORITY, &(data->radio2ComBridgeTaskHandle));
xTaskCreate(com2RadioBridgeTask, (signed char *)"Com2RadioBridge", STACK_SIZE_BYTES/2, NULL, TASK_PRIORITY, &(data->com2RadioBridgeTaskHandle));
return 0;
}
@ -122,7 +135,7 @@ static int32_t RadioComBridgeInitialize(void)
PIOS_Assert(data->radio2com_buf);
data->com2radio_buf = pvPortMalloc(BRIDGE_BUF_LEN);
PIOS_Assert(data->com2radio_buf);
// Initialise UAVTalk
data->uavTalkCon = UAVTalkInitialize(&transmitData);
@ -130,6 +143,22 @@ static int32_t RadioComBridgeInitialize(void)
data->com_tx_errors = 0;
data->radio_tx_errors = 0;
// Initialize the packet handler
PacketHandlerConfig phcfg = {
.txWinSize = PIOS_PH_TX_WIN_SIZE,
.maxConnections = PIOS_PH_MAX_CONNECTIONS,
.id = 0x36249acb,
};
data->packet_handler = PHInitialize(&phcfg);
// Register the callbacks with the packet handler
PHRegisterOutputStream(data->packet_handler, transmitPacket);
PHRegisterDataHandler(data->packet_handler, receiveData);
// Initialize the packet send timeout
data->send_timeout = 25; // ms
data->min_packet_size = 50;
updateSettings();
return 0;
@ -148,11 +177,7 @@ static void radio2ComBridgeTask(void *parameters)
// Receive data from the radio port
rx_bytes = PIOS_COM_ReceiveBuffer(data->radio_port, data->radio2com_buf, BRIDGE_BUF_LEN, 500);
if (rx_bytes > 0) {
/* Send the received data to the com port */
if (PIOS_COM_SendBuffer(data->com_port, data->radio2com_buf, rx_bytes) != rx_bytes) {
/* Error on transmit */
data->com_tx_errors++;
}
PHReceivePacket(data->packet_handler, (PHPacketHandle)data->radio2com_buf);
}
}
}
@ -162,22 +187,69 @@ static void radio2ComBridgeTask(void *parameters)
*/
static void com2RadioBridgeTask(void * parameters)
{
uint32_t rx_bytes = 0;
portTickType packet_start_time = 0;
uint32_t timeout = 500;
/* Handle usart/usb -> radio direction */
while (1) {
uint32_t rx_bytes;
// Receive data from the com port
rx_bytes = PIOS_COM_ReceiveBuffer(data->com_port, data->com2radio_buf, BRIDGE_BUF_LEN, 500);
//debug_msg = "COM receive";
uint32_t cur_rx_bytes = PIOS_COM_ReceiveBuffer(data->com_port, data->com2radio_buf +
rx_bytes, BRIDGE_BUF_LEN - rx_bytes, timeout);
//debug_msg = "COM receive done";
rx_bytes += cur_rx_bytes;
// Do we have an data to send?
if (rx_bytes > 0) {
/* Send the received data to the radio port */
if (PIOS_COM_SendBuffer(data->radio_port, data->com2radio_buf, rx_bytes) != rx_bytes) {
/* Error on transmit */
data->radio_tx_errors++;
// Check how long since last update
portTickType cur_sys_time = xTaskGetTickCount();
// Is this the start of a packet?
if(packet_start_time == 0)
packet_start_time = cur_sys_time;
// Just send the packet on wraparound
bool send_packet = (cur_sys_time < packet_start_time);
if (!send_packet)
{
portTickType dT = (cur_sys_time - packet_start_time) / portTICK_RATE_MS;
if (dT > data->send_timeout)
send_packet = true;
else
timeout = data->send_timeout - dT;
}
// Also send the packet if the size is over the minimum.
send_packet |= (rx_bytes > data->min_packet_size);
// Should we send this packet?
if (send_packet)
{
// Get a TX packet from the packet handler
PHPacketHandle p = PHGetTXPacket(data->packet_handler);
// Initialize the packet.
//p->header.type = PACKET_TYPE_ACKED_DATA;
p->header.type = PACKET_TYPE_DATA;
p->header.data_size = rx_bytes;
// Copy the data into the packet.
memcpy(p->data, data->com2radio_buf, rx_bytes);
// Transmit the packet
PHTransmitPacket(data->packet_handler, p);
// Reset the timeout
timeout = 500;
rx_bytes = 0;
packet_start_time = 0;
}
// Pass the data through UAVTalk
//for (uint8_t i = 0; i < rx_bytes; i++)
//for (uint8_t i = 0; i < cur_rx_bytes; i++)
//UAVTalkProcessInputStream(data->uavTalkCon, data->com2radio_buf[i]);
}
}
@ -196,6 +268,35 @@ static int32_t transmitData(uint8_t *buf, int32_t length)
return PIOS_COM_SendBuffer(data->com_port, buf, length);
}
/**
* Transmit a packet to the radio port.
* \param[in] buf Data buffer to send
* \param[in] length Length of buffer
* \return -1 on failure
* \return number of bytes transmitted on success
*/
static int32_t transmitPacket(PHPacketHandle p)
{
static uint32_t cntr = 0;
DEBUG_PRINTF(2, "Sending: %d %d\n\r", p->header.data_size, cntr++);
int32_t ret = PIOS_COM_SendBuffer(data->radio_port, (uint8_t*)p, PH_PACKET_SIZE(p));
return ret;
}
/**
* Receive a packet
* \param[in] buf The received data buffer
* \param[in] length Length of buffer
*/
static void receiveData(uint8_t *buf, uint8_t len)
{
DEBUG_PRINTF(2, "Received: %d\n\r", len);
/* Send the received data to the com port */
if (PIOS_COM_SendBuffer(data->com_port, buf, len) != len)
/* Error on transmit */
data->com_tx_errors++;
}
static void updateSettings()
{
if (data->com_port) {

View File

@ -166,9 +166,10 @@ extern uint32_t pios_com_rfm22b_id;
*/
#define PIOS_COM_DEBUG PIOS_COM_FLEXI
#define PIOS_COM_BRIDGE_COM PIOS_COM_TELEM_SERIAL
//#define PIOS_COM_BRIDGE_COM PIOS_COM_FLEXI
#define PIOS_COM_BRIDGE_RADIO PIOS_COM_RFM22B_RF
#define DEBUG_LEVEL 1
#define DEBUG_LEVEL 2
#if DEBUG_LEVEL > 0
#define DEBUG_PRINTF(level, ...) if(level <= DEBUG_LEVEL) { PIOS_COM_SendFormattedStringNonBlocking(PIOS_COM_DEBUG, __VA_ARGS__); }
#else
@ -229,26 +230,28 @@ extern uint32_t pios_com_rfm22b_id;
// RFM22
//-------------------------
//#define RFM22_EXT_INT_USE
#define RFM22_EXT_INT_USE
#define RFM22_PIOS_SPI PIOS_SPI_PORT // SPIx
#if defined(RFM22_EXT_INT_USE)
#define RFM22_EXT_INT_PORT_SOURCE GPIO_PortSourceGPIOA
#define RFM22_EXT_INT_PIN_SOURCE GPIO_PinSource2
#define RFM22_EXT_INT_LINE EXTI_Line2
#define RFM22_EXT_INT_IRQn EXTI2_IRQn
#define RFM22_EXT_INT_FUNC EXTI2_IRQHandler
#define RFM22_EXT_INT_PRIORITY 1
#define PIOS_RFM22_EXTI_GPIO_PORT GPIOA
#define PIOS_RFM22_EXTI_GPIO_PIN GPIO_Pin_2
#define PIOS_RFM22_EXTI_PORT_SOURCE GPIO_PortSourceGPIOA
#define PIOS_RFM22_EXTI_PIN_SOURCE GPIO_PinSource2
#define PIOS_RFM22_EXTI_CLK RCC_APB2Periph_GPIOA
#define PIOS_RFM22_EXTI_LINE EXTI_Line2
#define PIOS_RFM22_EXTI_IRQn EXTI2_IRQn
#define PIOS_RFM22_EXTI_PRIO PIOS_IRQ_PRIO_LOW
#endif
//-------------------------
// Packet Handler
//-------------------------
#define PH_MAX_PACKET 255
#define PIOS_PH_MAX_PACKET 255
#define PIOS_PH_TX_WIN_SIZE 3
#define PIOS_PH_MAX_CONNECTIONS 1
//-------------------------
// Reed-Solomon ECC

View File

@ -79,7 +79,10 @@
#define TX_PREAMBLE_NIBBLES 12 // 7 to 511 (number of nibbles)
#define RX_PREAMBLE_NIBBLES 6 // 5 to 31 (number of nibbles)
#define FIFO_SIZE 64 // the size of the rf modules internal FIFO buffers
// the size of the rf modules internal transmit buffers.
#define TX_BUFFER_SIZE 256
// the size of the rf modules internal FIFO buffers
#define FIFO_SIZE 64
#define TX_FIFO_HI_WATERMARK 62 // 0-63
#define TX_FIFO_LO_WATERMARK 32 // 0-63
@ -137,8 +140,6 @@
// 10-nibble tx preamble length
// AFC enabled
#define LOOKUP_SIZE 14
/* Local type definitions */
enum pios_rfm22b_dev_magic {
PIOS_RFM22B_DEV_MAGIC = 0x68e971b6,
@ -148,8 +149,6 @@ struct pios_rfm22b_dev {
enum pios_rfm22b_dev_magic magic;
const struct pios_rfm22b_cfg *cfg;
xTaskHandle taskHandle;
uint32_t countdownTimer;
pios_com_callback rx_in_cb;
@ -157,18 +156,32 @@ struct pios_rfm22b_dev {
pios_com_callback tx_out_cb;
uint32_t tx_out_context;
PHInstHandle packet_handler;
PHPacketHandle cur_tx_packet;
};
uint32_t random32 = 0x459ab8d8;
/* Local function forwared declarations */
static uint8_t PIOS_RFM22B_send_packet(void *rfm22b, PHPacketHandle packet);
static void PIOS_RFM22B_receive_data(void *rfm22b, uint8_t *data, uint8_t len);
static void PIOS_RFM22B_Task(void *parameters);
void rfm22_processInt(void);
void PIOS_RFM22_EXT_Int(void);
void rfm22_setTxMode(uint8_t mode);
// SPI read/write functions
void rfm22_startBurstWrite(uint8_t addr);
inline void rfm22_burstWrite(uint8_t data)
{
PIOS_SPI_TransferByte(RFM22_PIOS_SPI, data);
}
void rfm22_endBurstWrite(void);
void rfm22_write(uint8_t addr, uint8_t data);
void rfm22_startBurstRead(uint8_t addr);
inline uint8_t rfm22_burstRead(void)
{
return PIOS_SPI_TransferByte(RFM22_PIOS_SPI, 0xff);
}
void rfm22_endBurstRead(void);
uint8_t rfm22_read(uint8_t addr);
uint8_t rfm22_txStart();
/* Provide a COM driver */
static void PIOS_RFM22B_ChangeBaud(uint32_t rfm22b_id, uint32_t baud);
@ -186,35 +199,65 @@ const struct pios_com_driver pios_rfm22b_com_driver = {
.bind_rx_cb = PIOS_RFM22B_RegisterRxCallback,
};
// External interrupt configuration
static const struct pios_exti_cfg pios_exti_rfm22b_cfg __exti_config = {
.vector = PIOS_RFM22_EXT_Int,
.line = PIOS_RFM22_EXTI_LINE,
.pin = {
.gpio = PIOS_RFM22_EXTI_GPIO_PORT,
.init = {
.GPIO_Pin = PIOS_RFM22_EXTI_GPIO_PIN,
.GPIO_Mode = GPIO_Mode_IN_FLOATING,
},
},
.irq = {
.init = {
.NVIC_IRQChannel = PIOS_RFM22_EXTI_IRQn,
.NVIC_IRQChannelPreemptionPriority = PIOS_RFM22_EXTI_PRIO,
.NVIC_IRQChannelSubPriority = 0,
.NVIC_IRQChannelCmd = ENABLE,
},
},
.exti = {
.init = {
.EXTI_Line = PIOS_RFM22_EXTI_LINE,
.EXTI_Mode = EXTI_Mode_Interrupt,
.EXTI_Trigger = EXTI_Trigger_Falling,
.EXTI_LineCmd = ENABLE,
},
},
};
// xtal 10 ppm, 434MHz
const uint32_t data_rate[LOOKUP_SIZE] = { 500, 1000, 2000, 4000, 8000, 9600, 16000, 19200, 24000, 32000, 64000, 128000, 192000, 256000};
const uint8_t modulation_index[LOOKUP_SIZE] = { 16, 8, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const uint32_t freq_deviation[LOOKUP_SIZE] = { 4000, 4000, 4000, 4000, 4000, 4800, 8000, 9600, 12000, 16000, 32000, 64000, 96000, 128000};
const uint32_t rx_bandwidth[LOOKUP_SIZE] = { 17500, 17500, 17500, 17500, 17500, 19400, 32200, 38600, 51200, 64100, 137900, 269300, 420200, 518800};
const int8_t est_rx_sens_dBm[LOOKUP_SIZE] = { -118, -118, -117, -116, -115, -115, -112, -112, -110, -109, -106, -103, -101, -100}; // estimated receiver sensitivity for BER = 1E-3
#define LOOKUP_SIZE 14
const uint32_t data_rate[] = { 500, 1000, 2000, 4000, 8000, 9600, 16000, 19200, 24000, 32000, 64000, 128000, 192000, 256000};
const uint8_t modulation_index[] = { 16, 8, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const uint32_t freq_deviation[] = { 4000, 4000, 4000, 4000, 4000, 4800, 8000, 9600, 12000, 16000, 32000, 64000, 96000, 128000};
const uint32_t rx_bandwidth[] = { 17500, 17500, 17500, 17500, 17500, 19400, 32200, 38600, 51200, 64100, 137900, 269300, 420200, 518800};
const int8_t est_rx_sens_dBm[] = { -118, -118, -117, -116, -115, -115, -112, -112, -110, -109, -106, -103, -101, -100}; // estimated receiver sensitivity for BER = 1E-3
const uint8_t reg_1C[LOOKUP_SIZE] = { 0x37, 0x37, 0x37, 0x37, 0x3A, 0x3B, 0x26, 0x28, 0x2E, 0x16, 0x07, 0x83, 0x8A, 0x8C}; // rfm22_if_filter_bandwidth
const uint8_t reg_1C[] = { 0x37, 0x37, 0x37, 0x37, 0x3A, 0x3B, 0x26, 0x28, 0x2E, 0x16, 0x07, 0x83, 0x8A, 0x8C}; // rfm22_if_filter_bandwidth
const uint8_t reg_1D[LOOKUP_SIZE] = { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}; // rfm22_afc_loop_gearshift_override
const uint8_t reg_1E[LOOKUP_SIZE] = { 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x02}; // rfm22_afc_timing_control
const uint8_t reg_1D[] = { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}; // rfm22_afc_loop_gearshift_override
const uint8_t reg_1E[] = { 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x02}; // rfm22_afc_timing_control
const uint8_t reg_1F[LOOKUP_SIZE] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; // rfm22_clk_recovery_gearshift_override
const uint8_t reg_20[LOOKUP_SIZE] = { 0xE8, 0xF4, 0xFA, 0x70, 0x3F, 0x34, 0x3F, 0x34, 0x2A, 0x3F, 0x3F, 0x5E, 0x3F, 0x2F}; // rfm22_clk_recovery_oversampling_ratio
const uint8_t reg_21[LOOKUP_SIZE] = { 0x60, 0x20, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x02, 0x02}; // rfm22_clk_recovery_offset2
const uint8_t reg_22[LOOKUP_SIZE] = { 0x20, 0x41, 0x83, 0x06, 0x0C, 0x75, 0x0C, 0x75, 0x12, 0x0C, 0x0C, 0x5D, 0x0C, 0xBB}; // rfm22_clk_recovery_offset1
const uint8_t reg_23[LOOKUP_SIZE] = { 0xC5, 0x89, 0x12, 0x25, 0x4A, 0x25, 0x4A, 0x25, 0x6F, 0x4A, 0x4A, 0x86, 0x4A, 0x0D}; // rfm22_clk_recovery_offset0
const uint8_t reg_24[LOOKUP_SIZE] = { 0x00, 0x00, 0x00, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07}; // rfm22_clk_recovery_timing_loop_gain1
const uint8_t reg_25[LOOKUP_SIZE] = { 0x0A, 0x23, 0x85, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x74, 0xFF, 0xFF}; // rfm22_clk_recovery_timing_loop_gain0
const uint8_t reg_1F[] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; // rfm22_clk_recovery_gearshift_override
const uint8_t reg_20[] = { 0xE8, 0xF4, 0xFA, 0x70, 0x3F, 0x34, 0x3F, 0x34, 0x2A, 0x3F, 0x3F, 0x5E, 0x3F, 0x2F}; // rfm22_clk_recovery_oversampling_ratio
const uint8_t reg_21[] = { 0x60, 0x20, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x02, 0x02}; // rfm22_clk_recovery_offset2
const uint8_t reg_22[] = { 0x20, 0x41, 0x83, 0x06, 0x0C, 0x75, 0x0C, 0x75, 0x12, 0x0C, 0x0C, 0x5D, 0x0C, 0xBB}; // rfm22_clk_recovery_offset1
const uint8_t reg_23[] = { 0xC5, 0x89, 0x12, 0x25, 0x4A, 0x25, 0x4A, 0x25, 0x6F, 0x4A, 0x4A, 0x86, 0x4A, 0x0D}; // rfm22_clk_recovery_offset0
const uint8_t reg_24[] = { 0x00, 0x00, 0x00, 0x02, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x07, 0x07}; // rfm22_clk_recovery_timing_loop_gain1
const uint8_t reg_25[] = { 0x0A, 0x23, 0x85, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x74, 0xFF, 0xFF}; // rfm22_clk_recovery_timing_loop_gain0
const uint8_t reg_2A[LOOKUP_SIZE] = { 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0E, 0x12, 0x17, 0x31, 0x50, 0x50, 0x50}; // rfm22_afc_limiter .. AFC_pull_in_range = ±AFCLimiter[7:0] x (hbsel+1) x 625 Hz
const uint8_t reg_2A[] = { 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0E, 0x12, 0x17, 0x31, 0x50, 0x50, 0x50}; // rfm22_afc_limiter .. AFC_pull_in_range = ±AFCLimiter[7:0] x (hbsel+1) x 625 Hz
const uint8_t reg_6E[LOOKUP_SIZE] = { 0x04, 0x08, 0x10, 0x20, 0x41, 0x4E, 0x83, 0x9D, 0xC4, 0x08, 0x10, 0x20, 0x31, 0x41}; // rfm22_tx_data_rate1
const uint8_t reg_6F[LOOKUP_SIZE] = { 0x19, 0x31, 0x62, 0xC5, 0x89, 0xA5, 0x12, 0x49, 0x9C, 0x31, 0x62, 0xC5, 0x27, 0x89}; // rfm22_tx_data_rate0
const uint8_t reg_6E[] = { 0x04, 0x08, 0x10, 0x20, 0x41, 0x4E, 0x83, 0x9D, 0xC4, 0x08, 0x10, 0x20, 0x31, 0x41}; // rfm22_tx_data_rate1
const uint8_t reg_6F[] = { 0x19, 0x31, 0x62, 0xC5, 0x89, 0xA5, 0x12, 0x49, 0x9C, 0x31, 0x62, 0xC5, 0x27, 0x89}; // rfm22_tx_data_rate0
const uint8_t reg_70[LOOKUP_SIZE] = { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}; // rfm22_modulation_mode_control1
const uint8_t reg_71[LOOKUP_SIZE] = { 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23}; // rfm22_modulation_mode_control2
const uint8_t reg_70[] = { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D}; // rfm22_modulation_mode_control1
const uint8_t reg_71[] = { 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23}; // rfm22_modulation_mode_control2
const uint8_t reg_72[LOOKUP_SIZE] = { 0x06, 0x06, 0x06, 0x06, 0x06, 0x08, 0x0D, 0x0F, 0x13, 0x1A, 0x33, 0x66, 0x9A, 0xCD}; // rfm22_frequency_deviation
const uint8_t reg_72[] = { 0x06, 0x06, 0x06, 0x06, 0x06, 0x08, 0x0D, 0x0F, 0x13, 0x1A, 0x33, 0x66, 0x9A, 0xCD}; // rfm22_frequency_deviation
// ************************************
// Scan Spectrum settings
@ -224,27 +267,25 @@ const uint8_t reg_72[LOOKUP_SIZE] = { 0x06, 0x06, 0x06, 0x06,
// FIFO mode
// 5-nibble rx preamble length detection
// 10-nibble tx preamble length
// AFC disabled
#define SS_LOOKUP_SIZE 2
// xtal 1 ppm, 434MHz
const uint32_t ss_rx_bandwidth[SS_LOOKUP_SIZE] = { 2600, 10600};
const uint32_t ss_rx_bandwidth[] = { 2600, 10600};
const uint8_t ss_reg_1C[SS_LOOKUP_SIZE] = { 0x51, 0x32}; // rfm22_if_filter_bandwidth
const uint8_t ss_reg_1D[SS_LOOKUP_SIZE] = { 0x00, 0x00}; // rfm22_afc_loop_gearshift_override
const uint8_t ss_reg_1C[] = { 0x51, 0x32}; // rfm22_if_filter_bandwidth
const uint8_t ss_reg_1D[] = { 0x00, 0x00}; // rfm22_afc_loop_gearshift_override
const uint8_t ss_reg_20[SS_LOOKUP_SIZE] = { 0xE8, 0x38}; // rfm22_clk_recovery_oversampling_ratio
const uint8_t ss_reg_21[SS_LOOKUP_SIZE] = { 0x60, 0x02}; // rfm22_clk_recovery_offset2
const uint8_t ss_reg_22[SS_LOOKUP_SIZE] = { 0x20, 0x4D}; // rfm22_clk_recovery_offset1
const uint8_t ss_reg_23[SS_LOOKUP_SIZE] = { 0xC5, 0xD3}; // rfm22_clk_recovery_offset0
const uint8_t ss_reg_24[SS_LOOKUP_SIZE] = { 0x00, 0x07}; // rfm22_clk_recovery_timing_loop_gain1
const uint8_t ss_reg_25[SS_LOOKUP_SIZE] = { 0x0F, 0xFF}; // rfm22_clk_recovery_timing_loop_gain0
const uint8_t ss_reg_20[] = { 0xE8, 0x38}; // rfm22_clk_recovery_oversampling_ratio
const uint8_t ss_reg_21[] = { 0x60, 0x02}; // rfm22_clk_recovery_offset2
const uint8_t ss_reg_22[] = { 0x20, 0x4D}; // rfm22_clk_recovery_offset1
const uint8_t ss_reg_23[] = { 0xC5, 0xD3}; // rfm22_clk_recovery_offset0
const uint8_t ss_reg_24[] = { 0x00, 0x07}; // rfm22_clk_recovery_timing_loop_gain1
const uint8_t ss_reg_25[] = { 0x0F, 0xFF}; // rfm22_clk_recovery_timing_loop_gain0
const uint8_t ss_reg_2A[SS_LOOKUP_SIZE] = { 0xFF, 0xFF}; // rfm22_afc_limiter .. AFC_pull_in_range = ±AFCLimiter[7:0] x (hbsel+1) x 625 Hz
const uint8_t ss_reg_2A[] = { 0xFF, 0xFF}; // rfm22_afc_limiter .. AFC_pull_in_range = ±AFCLimiter[7:0] x (hbsel+1) x 625 Hz
const uint8_t ss_reg_70[SS_LOOKUP_SIZE] = { 0x24, 0x2D}; // rfm22_modulation_mode_control1
const uint8_t ss_reg_71[SS_LOOKUP_SIZE] = { 0x2B, 0x23}; // rfm22_modulation_mode_control2
const uint8_t ss_reg_70[] = { 0x24, 0x2D}; // rfm22_modulation_mode_control1
const uint8_t ss_reg_71[] = { 0x2B, 0x23}; // rfm22_modulation_mode_control2
// ************************************
@ -252,7 +293,6 @@ volatile bool initialized = false;
#if defined(RFM22_EXT_INT_USE)
volatile bool exec_using_spi; // set this if you want to access the SPI bus outside of the interrupt
volatile bool inside_ext_int; // this is set whenever we are inside the interrupt
#endif
uint8_t device_type; // the RF chips device ID number
@ -294,7 +334,9 @@ volatile uint8_t prev_int_status1; // " "
volatile uint8_t prev_int_status2; // " "
volatile uint8_t prev_ezmac_status; // " "
bool debug_outputted;
const char *debug_msg = NULL;
const char *error_msg = NULL;
static uint32_t debug_val = 0;
#endif
volatile uint8_t osc_load_cap; // xtal frequency calibration value
@ -302,29 +344,36 @@ volatile uint8_t osc_load_cap; // xtal frequency calibration value
volatile uint8_t rssi; // the current RSSI (register value)
volatile int16_t rssi_dBm; // dBm value
uint8_t tx_power; // the transmit power to use for data transmissions
volatile uint8_t tx_pwr; // the tx power register read back
// the transmit power to use for data transmissions
uint8_t tx_power;
// the tx power register read back
volatile uint8_t tx_pwr;
volatile uint8_t rx_buffer_current; // the current receive buffer in use (double buffer)
volatile uint8_t rx_buffer[256] __attribute__ ((aligned(4))); // the receive buffer .. received packet data is saved here
volatile uint16_t rx_buffer_wr; // the receive buffer write index
// The transmit buffer. Holds data that is being transmitted.
uint8_t tx_buffer[TX_BUFFER_SIZE] __attribute__ ((aligned(4)));
// The transmit buffer. Hosts data that is wating to be transmitted.
uint8_t tx_pre_buffer[TX_BUFFER_SIZE] __attribute__ ((aligned(4)));
// The tx pre-buffer write index.
uint16_t tx_pre_buffer_size;
// the tx data read index
volatile uint16_t tx_data_rd;
// the tx data write index
volatile uint16_t tx_data_wr;
volatile uint8_t rx_packet_buf[256] __attribute__ ((aligned(4))); // the received packet
// the current receive buffer in use (double buffer)
volatile uint8_t rx_buffer_current;
// the receive buffer .. received packet data is saved here
volatile uint8_t rx_buffer[256] __attribute__ ((aligned(4)));
// the receive buffer write index
volatile uint16_t rx_buffer_wr;
// the received packet
volatile uint16_t rx_packet_wr; // the receive packet write index
volatile int16_t rx_packet_start_rssi_dBm; //
volatile int32_t rx_packet_start_afc_Hz; //
volatile int16_t rx_packet_rssi_dBm; // the received packet signal strength
volatile int32_t rx_packet_afc_Hz; // the receive packet frequency offset
volatile uint8_t *tx_data_addr; // the address of the data we send in the transmitted packets
volatile uint16_t tx_data_rd; // the tx data read index
volatile uint16_t tx_data_wr; // the tx data write index
//volatile uint8_t tx_fifo[FIFO_SIZE]; //
volatile uint8_t rx_fifo[FIFO_SIZE]; //
volatile uint8_t rx_fifo_wr; //
int lookup_index;
int ss_lookup_index;
@ -334,14 +383,11 @@ volatile uint16_t rfm22_int_timer; // used to detect if the RF module stops
volatile uint16_t rfm22_int_time_outs; // counter
volatile uint16_t prev_rfm22_int_time_outs; //
uint32_t clear_channel_count = (TX_PREAMBLE_NIBBLES + 4) * 2; // minimum clear channel time before allowing transmit
uint16_t timeout_ms = 20000; //
uint16_t timeout_sync_ms = 3; //
uint16_t timeout_data_ms = 20; //
t_rfm22_TxDataByteCallback tx_data_byte_callback_function = NULL;
t_rfm22_RxDataCallback rx_data_callback_function = NULL;
struct pios_rfm22b_dev * rfm22b_dev_g;
static bool PIOS_RFM22B_validate(struct pios_rfm22b_dev * rfm22b_dev)
@ -356,6 +402,7 @@ static struct pios_rfm22b_dev * PIOS_RFM22B_alloc(void)
rfm22b_dev = (struct pios_rfm22b_dev *)pvPortMalloc(sizeof(*rfm22b_dev));
if (!rfm22b_dev) return(NULL);
rfm22b_dev_g = rfm22b_dev;
rfm22b_dev->magic = PIOS_RFM22B_DEV_MAGIC;
return(rfm22b_dev);
@ -394,25 +441,14 @@ int32_t PIOS_RFM22B_Init(uint32_t *rfm22b_id, const struct pios_rfm22b_cfg *cfg)
// Bind the configuration to the device instance
rfm22b_dev->cfg = cfg;
// Configure the packet handler.
PacketHandlerConfig phcfg = {
.txWinSize = cfg->txWinSize,
.maxConnections = cfg->maxConnections,
.id = cfg->id,
.dev = (void*) rfm22b_dev,
.output_stream = PIOS_RFM22B_send_packet,
.set_baud = 0,
.data_handler = PIOS_RFM22B_receive_data,
.receiver_handler = 0,
};
rfm22b_dev->packet_handler = PHInitialize(&phcfg);
rfm22b_dev->cur_tx_packet = 0;
*rfm22b_id = (uint32_t)rfm22b_dev;
tx_pre_buffer_size = 0;
// Initialize the radio device.
PIOS_COM_SendString(PIOS_COM_DEBUG, "Init Radio\n\r");
DEBUG_PRINTF(2, "Init Radio\n\r");
int initval = rfm22_init_normal(cfg->minFrequencyHz, cfg->maxFrequencyHz, 50000);
if (initval < 0)
@ -459,9 +495,6 @@ int32_t PIOS_RFM22B_Init(uint32_t *rfm22b_id, const struct pios_rfm22b_cfg *cfg)
DEBUG_PRINTF(2, "RF frequency: %dHz\n\r", rfm22_getNominalCarrierFrequency());
DEBUG_PRINTF(2, "RF TX power: %d\n\r", rfm22_getTxPower());
// Start the data handeling task.
xTaskCreate(PIOS_RFM22B_Task, (signed char *)"RFM22BTask", STACK_SIZE_BYTES, (void*)rfm22b_dev, tskIDLE_PRIORITY + 2, &(rfm22b_dev->taskHandle));
return(0);
}
@ -480,6 +513,31 @@ static void PIOS_RFM22B_TxStart(uint32_t rfm22b_id, uint16_t tx_bytes_avail)
bool valid = PIOS_RFM22B_validate(rfm22b_dev);
PIOS_Assert(valid);
// Get some data to send
bool need_yield = false;
if(tx_pre_buffer_size== 0)
tx_pre_buffer_size = (rfm22b_dev->tx_out_cb)(rfm22b_dev->tx_out_context, tx_pre_buffer,
TX_BUFFER_SIZE, NULL, &need_yield);
if(tx_pre_buffer_size > 0)
{
// already have data to be sent
if (tx_data_wr > 0)
return;
// we are currently transmitting or scanning the spectrum
if (rf_mode == TX_DATA_MODE || rf_mode == TX_STREAM_MODE || rf_mode == TX_CARRIER_MODE ||
rf_mode == TX_PN_MODE || rf_mode == RX_SCAN_SPECTRUM)
return;
// is the channel clear to transmit on?
if (!rfm22_channelIsClear())
return;
// Start the transmit
rfm22_txStart();
}
}
/**
@ -526,132 +584,14 @@ static void PIOS_RFM22B_RegisterTxCallback(uint32_t rfm22b_id, pios_com_callback
rfm22b_dev->tx_out_cb = tx_out_cb;
}
static uint8_t PIOS_RFM22B_send_packet(void *rfm22b, PHPacketHandle packet)
void rfm22_setDebug(const char* msg)
{
//struct pios_rfm22b_dev * rfm22b_dev = (struct pios_rfm22b_dev *)rfm22b;
uint16_t len = PHPacketSizeECC(packet);
DEBUG_PRINTF(2, "Sending: %d %d %x\n\r", len, packet->header.data_size, (int)(packet->data[0]));
return rfm22_sendData(packet, len, 1);
debug_msg = msg;
}
static void PIOS_RFM22B_receive_data(void *rfm22b, uint8_t *data, uint8_t len)
void rfm22_setError(const char* msg)
{
struct pios_rfm22b_dev * rfm22b_dev = (struct pios_rfm22b_dev *)rfm22b;
DEBUG_PRINTF(2, "Data: %d %x\n\r", len, (int)(data[0]));
// Pass the received data the the receive callback.
bool need_yield = false;
if (rfm22b_dev->rx_in_cb)
(rfm22b_dev->rx_in_cb)(rfm22b_dev->rx_in_context, data, len, NULL, &need_yield);
#if defined(PIOS_INCLUDE_FREERTOS)
if (need_yield)
vPortYieldFromISR();
#endif /* PIOS_INCLUDE_FREERTOS */
}
static void PIOS_RFM22B_Task(void *parameters)
{
struct pios_rfm22b_dev * rfm22b_dev = (struct pios_rfm22b_dev *)parameters;
bool valid = PIOS_RFM22B_validate(rfm22b_dev);
PIOS_Assert(valid);
// Loop forever
while (1)
{
// Recieve data to transmit if it's available.
// Reserve a TX packet if necessary
PHPacketHandle p = rfm22b_dev->cur_tx_packet;
bool reserve = (p == NULL);
if (reserve)
{
if((p = PHReserveTXPacket(rfm22b_dev->packet_handler)))
p->header.data_size = 0;
}
// Do we have a packet to receive into?
if (p != NULL)
{
// Try to get some data.
bool need_yield = false;
uint16_t bytes_to_send = (rfm22b_dev->tx_out_cb)(rfm22b_dev->tx_out_context, p->data + p->header.data_size, PH_MAX_DATA + RS_ECC_NPARITY - p->header.data_size, NULL, &need_yield);
p->header.data_size += bytes_to_send;
// Did we just reserve this packet?
if (reserve)
{
if(bytes_to_send == 0)
{
// If there's no data available, just unlock our reserve on the packet.
PHReleaseLock(p, 0);
p = NULL;
}
else
{
// Keep the packet and release the lock.
PHReleaseLock(p, 1);
// Set the time so that we will send this data at least by the send timout.
rfm22b_dev->countdownTimer = rfm22b_dev->cfg->sendTimeout;
// Initialize the packet.
p->header.type = PACKET_TYPE_ACKED_DATA;
rfm22b_dev->cur_tx_packet = p;
}
}
}
// Do we have data to send?
if (p != NULL)
{
// Send the packet if the data size is over the minimum threshold
// or if this packet is older than the send timeout.
if((p->header.data_size >= rfm22b_dev->cfg->minPacketSize) || (rfm22b_dev->countdownTimer == 0))
{
// Transmit the packet
PHTransmitPacket(rfm22b_dev->packet_handler, p);
// Clear our link to the old packet.
rfm22b_dev->cur_tx_packet = NULL;
}
else
// Decrement the packet timer if we didn't send the packet.
rfm22b_dev->countdownTimer--;
}
rfm22_process();
// See if a packet was received.
uint16_t packet_size = rfm22_receivedLength();
if (packet_size != 0)
{
// copy the received packet into our own buffer
// Get the receive packet buffer.
PHPacketHandle p = PHGetRXPacket(rfm22b_dev->packet_handler);
memmove(p, rfm22_receivedPointer(), packet_size);
DEBUG_PRINTF(2, "Received: %d %d\n\r", packet_size, p->header.data_size);
// Process the received packet with the packet handler.
PHReceivePacket(rfm22b_dev->packet_handler, p);
// the received packet has been saved
rfm22_receivedDone();
}
vTaskDelay(1);
}
error_msg = msg;
}
// ************************************
@ -668,11 +608,6 @@ void rfm22_startBurstWrite(uint8_t addr)
PIOS_SPI_TransferByte(RFM22_PIOS_SPI, 0x80 | addr);
}
inline void rfm22_burstWrite(uint8_t data)
{
PIOS_SPI_TransferByte(RFM22_PIOS_SPI, data);
}
void rfm22_endBurstWrite(void)
{
// chip select line HIGH
@ -705,11 +640,6 @@ void rfm22_startBurstRead(uint8_t addr)
PIOS_SPI_TransferByte(RFM22_PIOS_SPI, addr & 0x7f);
}
inline uint8_t rfm22_burstRead(void)
{
return PIOS_SPI_TransferByte(RFM22_PIOS_SPI, 0xff);
}
void rfm22_endBurstRead(void)
{
// chip select line HIGH
@ -738,69 +668,40 @@ uint8_t rfm22_read(uint8_t addr)
// ************************************
// external interrupt
#if defined(RFM22_EXT_INT_USE)
void RFM22_EXT_INT_FUNC(void)
void PIOS_RFM22_EXT_Int(void)
{
inside_ext_int = TRUE;
if (EXTI_GetITStatus(RFM22_EXT_INT_LINE) != RESET)
{
// Clear the EXTI line pending bit
EXTI_ClearITPendingBit(RFM22_EXT_INT_LINE);
// USB_LED_TOGGLE; // TEST ONLY
if (!exec_using_spi)
{
// while (!GPIO_IN(RF_INT_PIN) && !exec_using_spi)
{
// stay here until the interrupt line returns HIGH
rfm22_processInt();
}
}
}
inside_ext_int = FALSE;
rfm22_setDebug("Ext Int");
if (!exec_using_spi)
rfm22_processInt();
rfm22_setDebug("Ext Done");
}
void rfm22_disableExtInt(void)
{
#if defined(RFM22_EXT_INT_USE)
rfm22_setDebug("Disable Int");
// Configure the external interrupt
GPIO_EXTILineConfig(RFM22_EXT_INT_PORT_SOURCE, RFM22_EXT_INT_PIN_SOURCE);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = RFM22_EXT_INT_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
GPIO_EXTILineConfig(PIOS_RFM22_EXTI_PORT_SOURCE, PIOS_RFM22_EXTI_PIN_SOURCE);
EXTI_InitTypeDef EXTI_InitStructure = pios_exti_rfm22b_cfg.exti.init;
EXTI_InitStructure.EXTI_LineCmd = DISABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_ClearFlag(RFM22_EXT_INT_LINE);
EXTI_ClearFlag(PIOS_RFM22_EXTI_LINE);
rfm22_setDebug("Disable Int done");
#endif
}
void rfm22_enableExtInt(void)
{
// Configure the external interrupt
GPIO_EXTILineConfig(RFM22_EXT_INT_PORT_SOURCE, RFM22_EXT_INT_PIN_SOURCE);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = RFM22_EXT_INT_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_ClearFlag(RFM22_EXT_INT_LINE);
// Enable and set the external interrupt
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = RFM22_EXT_INT_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = RFM22_EXT_INT_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#if defined(RFM22_EXT_INT_USE)
rfm22_setDebug("Ensable Int");
if (PIOS_EXTI_Init(&pios_exti_rfm22b_cfg))
PIOS_Assert(0);
rfm22_setDebug("Ensable Int done");
#endif
}
#endif
// ************************************
// set/get the current tx power setting
@ -840,9 +741,7 @@ uint32_t rfm22_maxFrequency(void)
void rfm22_setNominalCarrierFrequency(uint32_t frequency_hz)
{
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// *******
@ -887,9 +786,7 @@ void rfm22_setNominalCarrierFrequency(uint32_t frequency_hz)
// DEBUG_PRINTF(2, "rf setFreq frequency_step_size: %0.2f\n\r", frequency_step_size);
#endif
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
}
uint32_t rfm22_getNominalCarrierFrequency(void)
@ -934,10 +831,7 @@ uint32_t rfm22_freqHopSize(void)
void rfm22_setDatarate(uint32_t datarate_bps, bool data_whitening)
{
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// *******
lookup_index = 0;
while (lookup_index < (LOOKUP_SIZE - 1) && data_rate[lookup_index] < datarate_bps)
@ -1049,9 +943,7 @@ void rfm22_setDatarate(uint32_t datarate_bps, bool data_whitening)
// *******
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
}
uint32_t rfm22_getDatarate(void)
@ -1064,10 +956,7 @@ uint32_t rfm22_getDatarate(void)
void rfm22_setSSBandwidth(uint32_t bandwidth_index)
{
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// *******
ss_lookup_index = bandwidth_index;
@ -1103,57 +992,41 @@ void rfm22_setSSBandwidth(uint32_t bandwidth_index)
// *******
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
}
// ************************************
void rfm22_setRxMode(uint8_t mode, bool multi_packet_mode)
{
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// disable interrupts
rfm22_write(RFM22_interrupt_enable1, 0x00);
rfm22_write(RFM22_interrupt_enable2, 0x00);
// disable the receiver and transmitter
// rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_xton); // READY mode
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon); // TUNE mode
// Switch to TUNE mode
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon);
RX_LED_OFF;
TX_LED_OFF;
// rfm22_write(RFM22_rx_fifo_control, RX_FIFO_HI_WATERMARK); // RX FIFO Almost Full Threshold (0 - 63)
if (rf_mode == TX_CARRIER_MODE || rf_mode == TX_PN_MODE)
{
// FIFO mode, GFSK modulation
uint8_t fd_bit = rfm22_read(RFM22_modulation_mode_control2) & RFM22_mmc2_fd;
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_fifo | RFM22_mmc2_modtyp_gfsk);
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_fifo |
RFM22_mmc2_modtyp_gfsk);
}
rx_buffer_wr = 0; // empty the rx buffer
rfm22_int_timer = 0; // reset the timer
// empty the rx buffer
rx_buffer_wr = 0;
// reset the timer
rfm22_int_timer = 0;
rf_mode = mode;
if (mode != RX_SCAN_SPECTRUM)
{
//STOPWATCH_reset(); // reset clear channel detect timer
// enable RX interrupts
rfm22_write(RFM22_interrupt_enable1, RFM22_ie1_encrcerror | RFM22_ie1_enpkvalid | RFM22_ie1_enrxffafull | RFM22_ie1_enfferr);
rfm22_write(RFM22_interrupt_enable2, RFM22_ie2_enpreainval | RFM22_ie2_enpreaval | RFM22_ie2_enswdet);
}
// read interrupt status - clear interrupts
rfm22_read(RFM22_interrupt_status1);
rfm22_read(RFM22_interrupt_status2);
// Clear the TX buffer.
tx_data_rd = tx_data_wr = 0;
// clear FIFOs
if (!multi_packet_mode)
@ -1161,21 +1034,22 @@ void rfm22_setRxMode(uint8_t mode, bool multi_packet_mode)
rfm22_write(RFM22_op_and_func_ctrl2, RFM22_opfc2_ffclrrx | RFM22_opfc2_ffclrtx);
rfm22_write(RFM22_op_and_func_ctrl2, 0x00);
} else {
rfm22_write(RFM22_op_and_func_ctrl2, RFM22_opfc2_rxmpk | RFM22_opfc2_ffclrrx | RFM22_opfc2_ffclrtx);
rfm22_write(RFM22_op_and_func_ctrl2, RFM22_opfc2_rxmpk | RFM22_opfc2_ffclrrx |
RFM22_opfc2_ffclrtx);
rfm22_write(RFM22_op_and_func_ctrl2, RFM22_opfc2_rxmpk);
}
// enable RX interrupts
rfm22_write(RFM22_interrupt_enable1, RFM22_ie1_encrcerror | RFM22_ie1_enpkvalid |
RFM22_ie1_enrxffafull | RFM22_ie1_enfferr);
rfm22_write(RFM22_interrupt_enable2, RFM22_ie2_enpreainval | RFM22_ie2_enpreaval |
RFM22_ie2_enswdet);
// enable the receiver
// rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_xton | RFM22_opfc1_rxon);
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon | RFM22_opfc1_rxon);
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
#if defined(RFM22_DEBUG)
DEBUG_PRINTF(2, " RX Mode\n\r");
#endif
}
// ************************************
@ -1197,14 +1071,96 @@ uint16_t rfm22_addHeader()
// ************************************
uint8_t rfm22_txStart()
{
if((tx_pre_buffer_size == 0) || (exec_using_spi == TRUE))
{
// Clear the TX buffer.
tx_data_rd = tx_data_wr = 0;
return 0;
}
exec_using_spi = TRUE;
// Disable interrrupts.
PIOS_IRQ_Disable();
// disable interrupts
rfm22_write(RFM22_interrupt_enable1, 0x00);
rfm22_write(RFM22_interrupt_enable2, 0x00);
// TUNE mode
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon);
// Queue the data up for sending
memcpy(tx_buffer, tx_pre_buffer, tx_pre_buffer_size);
tx_data_rd = 0;
tx_data_wr = tx_pre_buffer_size;
tx_pre_buffer_size = 0;
RX_LED_OFF;
// FIFO mode, GFSK modulation
uint8_t fd_bit = rfm22_read(RFM22_modulation_mode_control2) & RFM22_mmc2_fd;
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_fifo |
RFM22_mmc2_modtyp_gfsk);
// set the tx power
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_1 |
RFM22_tx_pwr_papeaklvl_0 | RFM22_tx_pwr_lna_sw | tx_power);
// clear FIFOs
rfm22_write(RFM22_op_and_func_ctrl2, RFM22_opfc2_ffclrrx | RFM22_opfc2_ffclrtx);
rfm22_write(RFM22_op_and_func_ctrl2, 0x00);
// *******************
// add some data to the chips TX FIFO before enabling the transmitter
// set the total number of data bytes we are going to transmit
rfm22_write(RFM22_transmit_packet_length, tx_data_wr);
// add some data
rfm22_startBurstWrite(RFM22_fifo_access);
for (uint16_t i = 0; (tx_data_rd < tx_data_wr) && (i < FIFO_SIZE); ++tx_data_rd, ++i)
rfm22_burstWrite(tx_buffer[tx_data_rd]);
rfm22_endBurstWrite();
// *******************
// reset the timer
rfm22_int_timer = 0;
rf_mode = TX_DATA_MODE;
// enable TX interrupts
// rfm22_write(RFM22_interrupt_enable1, RFM22_ie1_enpksent | RFM22_ie1_entxffaem | RFM22_ie1_enfferr);
rfm22_write(RFM22_interrupt_enable1, RFM22_ie1_enpksent | RFM22_ie1_entxffaem);
// read interrupt status - clear interrupts
//rfm22_read(RFM22_interrupt_status1);
//rfm22_read(RFM22_interrupt_status2);
// enable the transmitter
// rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_xton | RFM22_opfc1_txon);
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon | RFM22_opfc1_txon);
// Re-ensable interrrupts.
PIOS_IRQ_Enable();
TX_LED_ON;
exec_using_spi = FALSE;
return 1;
}
void rfm22_setTxMode(uint8_t mode)
{
rfm22_setDebug("setTxMode");
if (mode != TX_DATA_MODE && mode != TX_STREAM_MODE && mode != TX_CARRIER_MODE && mode != TX_PN_MODE)
return; // invalid mode
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// *******************
@ -1218,20 +1174,23 @@ void rfm22_setTxMode(uint8_t mode)
RX_LED_OFF;
// set the tx power
// rfm22_write(RFM22_tx_power, RFM22_tx_pwr_lna_sw | tx_power);
// rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_0 | RFM22_tx_pwr_lna_sw | tx_power);
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_1 | RFM22_tx_pwr_papeaklvl_0 | RFM22_tx_pwr_lna_sw | tx_power);
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_1 |
RFM22_tx_pwr_papeaklvl_0 | RFM22_tx_pwr_lna_sw | tx_power);
uint8_t fd_bit = rfm22_read(RFM22_modulation_mode_control2) & RFM22_mmc2_fd;
if (mode == TX_CARRIER_MODE)
// blank carrier mode - for testing
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_pn9 | RFM22_mmc2_modtyp_none); // FIFO mode, Blank carrier
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_pn9 |
RFM22_mmc2_modtyp_none); // FIFO mode, Blank carrier
else if (mode == TX_PN_MODE)
// psuedo random data carrier mode - for testing
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_pn9 | RFM22_mmc2_modtyp_gfsk); // FIFO mode, PN9 carrier
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_pn9 |
RFM22_mmc2_modtyp_gfsk); // FIFO mode, PN9 carrier
else
// data transmission
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_fifo | RFM22_mmc2_modtyp_gfsk); // FIFO mode, GFSK modulation
// FIFO mode, GFSK modulation
rfm22_write(RFM22_modulation_mode_control2, fd_bit | RFM22_mmc2_dtmod_fifo |
RFM22_mmc2_modtyp_gfsk);
// rfm22_write(0x72, reg_72[lookup_index]); // rfm22_frequency_deviation
@ -1244,17 +1203,14 @@ void rfm22_setTxMode(uint8_t mode)
{
uint16_t rd = 0;
uint16_t wr = tx_data_wr;
if (!tx_data_addr) wr = 0;
if (mode == TX_DATA_MODE)
rfm22_write(RFM22_transmit_packet_length, wr); // set the total number of data bytes we are going to transmit
// set the total number of data bytes we are going to transmit
rfm22_write(RFM22_transmit_packet_length, wr);
uint16_t max_bytes = FIFO_SIZE - 1;
uint16_t i = 0;
rfm22_startBurstWrite(RFM22_fifo_access);
if (mode == TX_STREAM_MODE) {
if (rd >= wr) {
// no data to send - yet .. just send preamble pattern
@ -1268,7 +1224,7 @@ void rfm22_setTxMode(uint8_t mode)
// add some data
for (uint16_t j = wr - rd; j > 0; j--) {
rfm22_burstWrite(tx_data_addr[rd++]);
rfm22_burstWrite(tx_buffer[rd++]);
if (++i >= max_bytes)
break;
}
@ -1280,7 +1236,8 @@ void rfm22_setTxMode(uint8_t mode)
// *******************
rfm22_int_timer = 0; // reset the timer
// reset the timer
rfm22_int_timer = 0;
rf_mode = mode;
@ -1299,34 +1256,10 @@ void rfm22_setTxMode(uint8_t mode)
TX_LED_ON;
// *******************
// create new slightly random clear channel detector count value
uint32_t ccc = (TX_PREAMBLE_NIBBLES + 8) + 4; // minimum clear channel time before allowing transmit
clear_channel_count = ccc + (random32 % (ccc * 2)); // plus a some randomness
// *******************
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
#if defined(RFM22_DEBUG)
switch (rf_mode)
{
case TX_DATA_MODE:
DEBUG_PRINTF(2, " TX_Data_Mode\n\r");
break;
case TX_STREAM_MODE:
DEBUG_PRINTF(2, " TX_Stream_Mode\n\r");
break;
case TX_CARRIER_MODE:
DEBUG_PRINTF(2, " TX_Carrier_Mode\n\r");
break;
case TX_PN_MODE:
DEBUG_PRINTF(2, " TX_PN_Mode\n\r");
break;
}
#endif
rfm22_setDebug("setTxMode end");
}
// ************************************
@ -1336,129 +1269,119 @@ void rfm22_processRxInt(void)
{
register uint8_t int_stat1 = int_status1;
register uint8_t int_stat2 = int_status2;
rfm22_setDebug("processRxInt");
// FIFO under/over flow error. Restart RX mode.
if (device_status & (RFM22_ds_ffunfl | RFM22_ds_ffovfl))
{
rfm22_setError("R_UNDER/OVERRUN");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
// Sync timeout. Restart RX mode.
if (rf_mode == RX_WAIT_SYNC_MODE && rfm22_int_timer >= timeout_sync_ms)
{
rfm22_int_time_outs++;
rfm22_setError("R_SYNC_TIMEOUT");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
// RX timeout. Restart RX mode.
if (rf_mode == RX_DATA_MODE && rfm22_int_timer >= timeout_data_ms)
{
rfm22_setError("MISSING_INTERRUPTS");
rfm22_int_time_outs++;
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
// The module is not in RX mode. Restart RX mode.
if ((device_status & RFM22_ds_cps_mask) != RFM22_ds_cps_rx)
{
// the rf module is not in rx mode
if (rfm22_int_timer >= 100)
{
rfm22_int_time_outs++;
// reset the receiver
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
}
// Valid preamble detected
if (int_stat2 & RFM22_is2_ipreaval)
{ // Valid preamble detected
{
if (rf_mode == RX_WAIT_PREAMBLE_MODE)
{
rfm22_int_timer = 0; // reset the timer
rf_mode = RX_WAIT_SYNC_MODE;
RX_LED_ON;
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " pream_det");
debug_outputted = true;
#endif
rfm22_setDebug("pream_det");
}
}
/* else
if (int_stat2 & RFM22_is2_ipreainval)
{ // Invalid preamble detected
if (rf_mode == RX_WAIT_SYNC_MODE)
{
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " invalid_preamble");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
else
{
}
}
*/
// Sync word detected
if (int_stat2 & RFM22_is2_iswdet)
{ // Sync word detected
//STOPWATCH_reset(); // reset timer
{
if (rf_mode == RX_WAIT_PREAMBLE_MODE || rf_mode == RX_WAIT_SYNC_MODE)
{
rfm22_int_timer = 0; // reset the timer
rf_mode = RX_DATA_MODE;
RX_LED_ON;
rfm22_setDebug("sync_det");
#ifdef NEVER
// read the 10-bit signed afc correction value
afc_correction = (uint16_t)rfm22_read(RFM22_afc_correction_read) << 8; // bits 9 to 2
afc_correction |= (uint16_t)rfm22_read(RFM22_ook_counter_value1) & 0x00c0; // bits 1 & 0
// bits 9 to 2
afc_correction = (uint16_t)rfm22_read(RFM22_afc_correction_read) << 8;
// bits 1 & 0
afc_correction |= (uint16_t)rfm22_read(RFM22_ook_counter_value1) & 0x00c0;
afc_correction >>= 6;
afc_correction_Hz = (int32_t)(frequency_step_size * afc_correction + 0.5f); // convert the afc value to Hz
// convert the afc value to Hz
afc_correction_Hz = (int32_t)(frequency_step_size * afc_correction + 0.5f);
rx_packet_start_rssi_dBm = rssi_dBm; // remember the rssi for this packet
rx_packet_start_afc_Hz = afc_correction_Hz; // remember the afc value for this packet
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " sync_det");
DEBUG_PRINTF(2, " AFC_%d_%dHz", afc_correction, afc_correction_Hz);
debug_outputted = true;
// remember the rssi for this packet
rx_packet_start_rssi_dBm = rssi_dBm;
// remember the afc value for this packet
rx_packet_start_afc_Hz = afc_correction_Hz;
#endif
}
}
// RX FIFO almost full, it needs emptying
if (int_stat1 & RFM22_is1_irxffafull)
{ // RX FIFO almost full, it needs emptying
{
if (rf_mode == RX_DATA_MODE)
{ // read data from the rf chips FIFO buffer
{
// read data from the rf chips FIFO buffer
rfm22_int_timer = 0; // reset the timer
register uint16_t len = rfm22_read(RFM22_received_packet_length); // read the total length of the packet data
register uint16_t wr = rx_buffer_wr;
if ((wr + RX_FIFO_HI_WATERMARK) > len)
{ // some kind of error in the RF module
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " r_size_error1");
debug_outputted = true;
#endif
// read the total length of the packet data
uint16_t len = rfm22_read(RFM22_received_packet_length);
// The received packet is going to be larger than the specified length
if ((rx_buffer_wr + RX_FIFO_HI_WATERMARK) > len)
{
rfm22_setError("r_size_error1");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
if (((wr + RX_FIFO_HI_WATERMARK) >= len) && !(int_stat1 & RFM22_is1_ipkvalid))
{ // some kind of error in the RF module
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " r_size_error2");
debug_outputted = true;
#endif
// Another packet length error.
if (((rx_buffer_wr + RX_FIFO_HI_WATERMARK) >= len) && !(int_stat1 & RFM22_is1_ipkvalid))
{
rfm22_setError("r_size_error2");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
// fetch the rx'ed data from the rf chips RX FIFO
// Fetch the data from the RX FIFO
rfm22_startBurstRead(RFM22_fifo_access);
rx_fifo_wr = 0;
for (register uint8_t i = RX_FIFO_HI_WATERMARK; i > 0; i--)
rx_fifo[rx_fifo_wr++] = rfm22_burstRead(); // read a byte from the rf modules RX FIFO buffer
for (uint8_t i = 0; i < RX_FIFO_HI_WATERMARK; ++i)
rx_buffer[rx_buffer_wr++] = rfm22_burstRead();
rfm22_endBurstRead();
uint16_t i = rx_fifo_wr;
if (wr + i > sizeof(rx_buffer)) i = sizeof(rx_buffer) - wr;
memcpy((void *)(rx_buffer + wr), (void *)rx_fifo, i); // save the new bytes into our rx buffer
wr += i;
rx_buffer_wr = wr;
if (rx_data_callback_function)
{ // pass the new data onto whoever wanted it
if (!rx_data_callback_function((void *)rx_fifo, rx_fifo_wr))
{
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
}
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
// DEBUG_PRINTF(2, " r_data_%u/%u", rx_buffer_wr, len);
// debug_outputted = true;
#endif
}
else
{ // just clear the RX FIFO
@ -1469,21 +1392,13 @@ void rfm22_processRxInt(void)
}
}
// CRC error .. discard the received data
if (int_stat1 & RFM22_is1_icrerror)
{ // CRC error .. discard the received data
if (rf_mode == RX_DATA_MODE)
{
rfm22_int_timer = 0; // reset the timer
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " CRC_ERR");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
return;
}
{
rfm22_int_timer = 0; // reset the timer
rfm22_setError("CRC_ERR");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
// if (int_stat2 & RFM22_is2_irssi)
@ -1498,235 +1413,159 @@ void rfm22_processRxInt(void)
// { // Header check error
// }
// Valid packet received
if (int_stat1 & RFM22_is1_ipkvalid)
{ // Valid packet received
if (rf_mode == RX_DATA_MODE)
{
rfm22_int_timer = 0; // reset the timer
// disable the receiver
// rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_xton); // READY mode
rfm22_write(RFM22_op_and_func_ctrl1, RFM22_opfc1_pllon); // TUNE mode
register uint16_t len = rfm22_read(RFM22_received_packet_length); // read the total length of the packet data
register uint16_t wr = rx_buffer_wr;
if (wr < len)
{ // their must still be data in the RX FIFO we need to get
// fetch the rx'ed data from the rf chips RX FIFO
rfm22_startBurstRead(RFM22_fifo_access);
rx_fifo_wr = 0;
for (register uint8_t i = len - wr; i > 0; i--)
rx_fifo[rx_fifo_wr++] = rfm22_burstRead(); // read a byte from the rf modules RX FIFO buffer
rfm22_endBurstRead();
uint16_t i = rx_fifo_wr;
if (wr + i > sizeof(rx_buffer)) i = sizeof(rx_buffer) - wr;
memcpy((void *)(rx_buffer + wr), (void *)rx_fifo, i); // save the new bytes into our rx buffer
wr += i;
rx_buffer_wr = wr;
if (rx_data_callback_function)
{ // pass the new data onto whoever wanted it
if (!rx_data_callback_function((void *)rx_fifo, rx_fifo_wr))
{
// rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
// return;
}
}
}
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
if (wr != len)
{ // we have a packet length error .. discard the packet
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " r_pack_len_error_%u_%u", len, wr);
debug_outputted = true;
#endif
return;
}
// we have a valid received packet
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " VALID_R_PACKET_%u", wr);
debug_outputted = true;
#endif
if (rx_packet_wr == 0)
{ // save the received packet for further processing
rx_packet_rssi_dBm = rx_packet_start_rssi_dBm; // remember the rssi for this packet
rx_packet_afc_Hz = rx_packet_start_afc_Hz; // remember the afc offset for this packet
memmove((void *)rx_packet_buf, (void *)rx_buffer, wr); // copy the packet data
rx_packet_wr = wr; // save the length of the data
}
else
{ // the save buffer is still in use .. nothing we can do but to drop the packet
}
// return;
}
else
{
// rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
// return;
}
}
}
uint8_t rfm22_topUpRFTxFIFO(void)
{
rfm22_int_timer = 0; // reset the timer
uint16_t rd = tx_data_rd;
uint16_t wr = tx_data_wr;
if (rf_mode == TX_DATA_MODE && (!tx_data_addr || rd >= wr))
return 0; // no more data to send
uint16_t max_bytes = FIFO_SIZE - TX_FIFO_LO_WATERMARK - 1;
uint16_t i = 0;
// top-up the rf chips TX FIFO buffer
rfm22_startBurstWrite(RFM22_fifo_access);
// add some data
for (uint16_t j = wr - rd; j > 0; j--)
{
// int16_t b = -1;
// if (tx_data_byte_callback_function)
// b = tx_data_byte_callback_function();
// reset the timer
rfm22_int_timer = 0;
rfm22_burstWrite(tx_data_addr[rd++]);
if (++i >= max_bytes) break;
}
tx_data_rd = rd;
// read the total length of the packet data
register uint16_t len = rfm22_read(RFM22_received_packet_length);
if (rf_mode == TX_STREAM_MODE && rd >= wr)
{ // all data sent .. need to start sending RF header again
tx_data_addr = NULL;
tx_data_rd = tx_data_wr = 0;
while (i < max_bytes)
// their must still be data in the RX FIFO we need to get
if (rx_buffer_wr < len)
{
rfm22_burstWrite(PREAMBLE_BYTE); // preamble byte
i++;
// Fetch the data from the RX FIFO
rfm22_startBurstRead(RFM22_fifo_access);
while (rx_buffer_wr < len)
rx_buffer[rx_buffer_wr++] = rfm22_burstRead();
rfm22_endBurstRead();
}
// todo:
if (rx_buffer_wr != len)
{
// we have a packet length error .. discard the packet
rfm22_setError("r_pack_len_error");
debug_val = len;
return;
}
// add the RF heaader
// i += rfm22_addHeader();
// we have a valid received packet
rfm22_setDebug("VALID_R_PACKET");
if (rx_packet_wr == 0)
{
// remember the rssi for this packet
rx_packet_rssi_dBm = rx_packet_start_rssi_dBm;
// remember the afc offset for this packet
rx_packet_afc_Hz = rx_packet_start_afc_Hz;
// Pass this packet on
bool need_yield = false;
if (rfm22b_dev_g->rx_in_cb)
(rfm22b_dev_g->rx_in_cb)(rfm22b_dev_g->rx_in_context, (uint8_t*)rx_buffer,
rx_buffer_wr, NULL, &need_yield);
rx_buffer_wr = 0;
}
// Send a packet if it's available.
if(!rfm22_txStart())
{
// Switch to RX mode
rfm22_setDebug(" Set RX");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
}
}
rfm22_endBurstWrite();
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
// DEBUG_PRINTF(2, " added_%d_bytes", i);
// debug_outputted = true;
#endif
return i;
rfm22_setDebug("processRxInt end");
}
void rfm22_processTxInt(void)
{
register uint8_t int_stat1 = int_status1;
// register uint8_t int_stat2 = int_status2;
/*
if (int_stat1 & RFM22_is1_ifferr)
{ // FIFO underflow/overflow error
// reset the timer
rfm22_int_timer = 0;
// FIFO under/over flow error. Back to RX mode.
if (device_status & (RFM22_ds_ffunfl | RFM22_ds_ffovfl))
{
rfm22_setError("T_UNDER/OVERRUN");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
tx_data_addr = NULL;
tx_data_rd = tx_data_wr = 0;
return;
}
*/
if (int_stat1 & RFM22_is1_ixtffaem)
{ // TX FIFO almost empty, it needs filling up
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
// DEBUG_PRINTF(2, " T_FIFO_AE");
// debug_outputted = true;
#endif
// uint8_t bytes_added = rfm22_topUpRFTxFIFO();
rfm22_topUpRFTxFIFO();
}
if (int_stat1 & RFM22_is1_ipksent)
{ // Packet has been sent
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " T_Sent");
debug_outputted = true;
#endif
// Transmit timeout. Abort the transmit.
if (rfm22_int_timer >= timeout_data_ms)
{
rfm22_int_time_outs++;
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
if (rf_mode == TX_DATA_MODE)
// the rf module is not in tx mode
if ((device_status & RFM22_ds_cps_mask) != RFM22_ds_cps_tx)
{
if (rfm22_int_timer >= 100)
{
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to receive mode
tx_data_addr = NULL;
tx_data_rd = tx_data_wr = 0;
rfm22_int_time_outs++;
rfm22_setError("T_TIMEOUT");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to rx mode
return;
}
else
if (rf_mode == TX_STREAM_MODE)
{
tx_data_addr = NULL;
tx_data_rd = tx_data_wr = 0;
rfm22_setTxMode(TX_STREAM_MODE);
return;
}
}
// if (int_stat1 & RFM22_is1_itxffafull)
// { // TX FIFO almost full, it needs to be transmitted
// }
// TX FIFO almost empty, it needs filling up
if (int_stat1 & RFM22_is1_ixtffaem)
{
// top-up the rf chips TX FIFO buffer
uint16_t max_bytes = FIFO_SIZE - TX_FIFO_LO_WATERMARK - 1;
rfm22_startBurstWrite(RFM22_fifo_access);
for (uint16_t i = 0; (tx_data_rd < tx_data_wr) && (i < max_bytes); ++i, ++tx_data_rd)
rfm22_burstWrite(tx_buffer[tx_data_rd]);
rfm22_endBurstWrite();
}
// Packet has been sent
if (int_stat1 & RFM22_is1_ipksent)
{
rfm22_setDebug(" T_Sent");
// Send another packet if it's available.
if(!rfm22_txStart())
{
// Switch to RX mode
rfm22_setDebug(" Set RX");
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
return;
}
}
rfm22_setDebug("ProcessTxInt done");
}
void rfm22_processInt(void)
{
rfm22_setDebug("ProcessInt");
// this is called from the external interrupt handler
#if !defined(RFM22_EXT_INT_USE)
// if (GPIO_IN(RF_INT_PIN))
//return; // the external int line is high (no signalled interrupt)
#endif
if (!initialized || power_on_reset)
return; // we haven't yet been initialized
// we haven't yet been initialized
return;
#if defined(RFM22_DEBUG)
debug_outputted = false;
#endif
exec_using_spi = TRUE;
// ********************************
// read the RF modules current status registers
// read device status register
device_status = rfm22_read(RFM22_device_status);
// read ezmac status register
ezmac_status = rfm22_read(RFM22_ezmac_status);
// read interrupt status registers - clears the interrupt line
int_status1 = rfm22_read(RFM22_interrupt_status1);
int_status2 = rfm22_read(RFM22_interrupt_status2);
if (rf_mode != TX_DATA_MODE && rf_mode != TX_STREAM_MODE && rf_mode != TX_CARRIER_MODE && rf_mode != TX_PN_MODE)
// read device status register
device_status = rfm22_read(RFM22_device_status);
#ifdef NEVER
// read ezmac status register
ezmac_status = rfm22_read(RFM22_ezmac_status);
// Read the RSSI if we're in RX mode
if (rf_mode != TX_DATA_MODE && rf_mode != TX_STREAM_MODE &&
rf_mode != TX_CARRIER_MODE && rf_mode != TX_PN_MODE)
{
rssi = rfm22_read(RFM22_rssi); // read rx signal strength .. 45 = -100dBm, 205 = -20dBm
rssi_dBm = ((int16_t)rssi / 2) - 122; // convert to dBm
// read rx signal strength .. 45 = -100dBm, 205 = -20dBm
rssi = rfm22_read(RFM22_rssi);
// convert to dBm
rssi_dBm = ((int16_t)rssi / 2) - 122;
// calibrate the RSSI value (rf bandwidth appears to affect it)
// if (rf_bandwidth_used > 0)
@ -1734,64 +1573,20 @@ void rfm22_processInt(void)
}
else
{
tx_pwr = rfm22_read(RFM22_tx_power); // read the tx power register
}
if (int_status2 & RFM22_is2_ipor)
{ // the RF module has gone and done a reset - we need to re-initialize the rf module
initialized = FALSE;
power_on_reset = TRUE;
return;
}
// ********************************
// debug stuff
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
if (prev_device_status != device_status || prev_int_status1 != int_status1 || prev_int_status2 != int_status2 || prev_ezmac_status != ezmac_status)
{
DEBUG_PRINTF(2, "%02x %02x %02x %02x %dC", device_status, int_status1, int_status2, ezmac_status, temperature_reg);
if ((device_status & RFM22_ds_cps_mask) == RFM22_ds_cps_rx)
{
DEBUG_PRINTF(2, " %ddBm", rssi_dBm); // rx mode
}
else
if ((device_status & RFM22_ds_cps_mask) == RFM22_ds_cps_tx)
{
DEBUG_PRINTF(2, " %s", (tx_pwr & RFM22_tx_pwr_papeakval) ? "ANT_MISMATCH" : "ant_ok"); // tx mode
}
debug_outputted = true;
prev_device_status = device_status;
prev_int_status1 = int_status1;
prev_int_status2 = int_status2;
prev_ezmac_status = ezmac_status;
// read the tx power register
tx_pwr = rfm22_read(RFM22_tx_power);
}
#endif
// ********************************
// read the ADC - temperature sensor .. this can only be used in IDLE mode
/*
if (!(rfm22_read(RFM22_adc_config) & RFM22_ac_adcstartbusy))
{ // the ADC has completed it's conversion
// read the ADC sample
temperature_reg = (int16_t)rfm22_read(RFM22_adc_value) * 0.5f - 64;
// start a new ADC conversion
rfm22_write(RFM22_adc_config, adc_config | RFM22_ac_adcstartbusy);
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, ", %dC", temperature_reg);
debug_outputted = true;
#endif
}
*/
// ********************************
register uint16_t timer_ms = rfm22_int_timer;
// the RF module has gone and done a reset - we need to re-initialize the rf module
if (int_status2 & RFM22_is2_ipor)
{
initialized = FALSE;
power_on_reset = TRUE;
rfm22_setError("Reset");
// Need to do something here!
return;
}
switch (rf_mode)
{
@ -1802,115 +1597,19 @@ void rfm22_processInt(void)
case RX_WAIT_SYNC_MODE:
case RX_DATA_MODE:
if (device_status & (RFM22_ds_ffunfl | RFM22_ds_ffovfl))
{ // FIFO under/over flow error
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " R_UNDER/OVERRUN");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
if (rf_mode == RX_WAIT_SYNC_MODE && timer_ms >= timeout_sync_ms)
{
rfm22_int_time_outs++;
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " R_SYNC_TIMEOUT");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
if (rf_mode == RX_DATA_MODE && timer_ms >= timeout_data_ms)
{ // missing interrupts
rfm22_int_time_outs++;
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
if ((device_status & RFM22_ds_cps_mask) != RFM22_ds_cps_rx)
{ // the rf module is not in rx mode
if (timer_ms >= 100)
{
rfm22_int_time_outs++;
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " R_TIMEOUT");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // reset the receiver
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
}
rfm22_processRxInt(); // process the interrupt
rfm22_processRxInt();
break;
case TX_DATA_MODE:
if (device_status & (RFM22_ds_ffunfl | RFM22_ds_ffovfl))
{ // FIFO under/over flow error
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " T_UNDER/OVERRUN");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to rx mode
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
if (timer_ms >= timeout_data_ms)
{
rfm22_int_time_outs++;
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to rx mode
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
if ((device_status & RFM22_ds_cps_mask) != RFM22_ds_cps_tx)
{ // the rf module is not in tx mode
if (timer_ms >= 100)
{
rfm22_int_time_outs++;
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
DEBUG_PRINTF(2, " T_TIMEOUT");
debug_outputted = true;
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to rx mode
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
}
rfm22_processTxInt(); // process the interrupt
break;
case TX_STREAM_MODE:
// todo:
rfm22_processTxInt(); // process the interrupt
rfm22_processTxInt();
break;
case TX_CARRIER_MODE:
case TX_PN_MODE:
// if (timer_ms >= TX_TEST_MODE_TIMELIMIT_MS) // 'nn'ms limit
// if (rfm22_int_timer >= TX_TEST_MODE_TIMELIMIT_MS) // 'nn'ms limit
// {
// rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // back to rx mode
// tx_data_rd = tx_data_wr = 0; // wipe TX buffer
@ -1921,66 +1620,24 @@ void rfm22_processInt(void)
default: // unknown mode - this should NEVER happen, maybe we should do a complete CPU reset here
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false); // to rx mode
tx_data_rd = tx_data_wr = 0; // wipe TX buffer
break;
}
// ********************************
exec_using_spi = FALSE;
#if defined(RFM22_DEBUG) && !defined(RFM22_EXT_INT_USE)
if (debug_outputted)
{
switch (rf_mode)
{
case RX_SCAN_SPECTRUM:
DEBUG_PRINTF(2, " R_SCAN_SPECTRUM\n\r");
break;
case RX_WAIT_PREAMBLE_MODE:
DEBUG_PRINTF(2, " R_WAIT_PREAMBLE\n\r");
break;
case RX_WAIT_SYNC_MODE:
DEBUG_PRINTF(2, " R_WAIT_SYNC\n\r");
break;
case RX_DATA_MODE:
DEBUG_PRINTF(2, " R_DATA\n\r");
break;
case TX_DATA_MODE:
DEBUG_PRINTF(2, " T_DATA\n\r");
break;
case TX_STREAM_MODE:
DEBUG_PRINTF(2, " T_STREAM\n\r");
break;
case TX_CARRIER_MODE:
DEBUG_PRINTF(2, " T_CARRIER\n\r");
break;
case TX_PN_MODE:
DEBUG_PRINTF(2, " T_PN\n\r");
break;
default:
DEBUG_PRINTF(2, " UNKNOWN_MODE\n\r");
break;
}
}
#endif
// ********************************
rfm22_setDebug("ProcessInt done");
}
// ************************************
int16_t rfm22_getRSSI(void)
{
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
rssi = rfm22_read(RFM22_rssi); // read rx signal strength .. 45 = -100dBm, 205 = -20dBm
rssi_dBm = ((int16_t)rssi / 2) - 122; // convert to dBm
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
return rssi_dBm;
}
@ -2000,57 +1657,8 @@ int32_t rfm22_receivedAFCHz(void)
return rx_packet_afc_Hz;
}
uint16_t rfm22_receivedLength(void)
{ // return the size of the data received
if (!initialized)
return 0;
else
return rx_packet_wr;
}
uint8_t * rfm22_receivedPointer(void)
{ // return the address of the data
return (uint8_t *)&rx_packet_buf;
}
void rfm22_receivedDone(void)
{ // empty the rx packet buffer
rx_packet_wr = 0;
}
// ************************************
int32_t rfm22_sendData(void *data, uint16_t length, bool send_immediately)
{
if (!initialized)
return -1; // we are not yet initialized
if (length == 0)
return -2; // no data to send
if (!data || length > 255)
return -3; // no data or too much data to send
if (tx_data_wr > 0)
return -4; // already have data to be sent
if (rf_mode == TX_DATA_MODE || rf_mode == TX_STREAM_MODE || rf_mode == TX_CARRIER_MODE || rf_mode == TX_PN_MODE || rf_mode == RX_SCAN_SPECTRUM)
return -5; // we are currently transmitting or scanning the spectrum
tx_data_addr = data;
tx_data_rd = 0;
tx_data_wr = length;
#if defined(RFM22_DEBUG)
DEBUG_PRINTF(2, "rf sendData(0x%08x %u)\n\r", (uint32_t)tx_data_addr, tx_data_wr);
#endif
if (send_immediately || rfm22_channelIsClear()) // is the channel clear to transmit on?
rfm22_setTxMode(TX_DATA_MODE); // transmit NOW
return tx_data_wr;
}
// ************************************
void rfm22_setTxStream(void) // TEST ONLY
@ -2122,10 +1730,12 @@ bool rfm22_transmitting(void)
bool rfm22_channelIsClear(void)
{
if (!initialized)
return FALSE; // we haven't yet been initialized
// we haven't yet been initialized
return FALSE;
if (rf_mode != RX_WAIT_PREAMBLE_MODE && rf_mode != RX_WAIT_SYNC_MODE)
return FALSE; // we are receiving something or we are transmitting or we are scanning the spectrum
// we are receiving something or we are transmitting or we are scanning the spectrum
return FALSE;
return TRUE;
}
@ -2134,9 +1744,12 @@ bool rfm22_channelIsClear(void)
bool rfm22_txReady(void)
{
if (!initialized)
return FALSE; // we haven't yet been initialized
// we haven't yet been initialized
return FALSE;
return (tx_data_rd == 0 && tx_data_wr == 0 && rf_mode != TX_DATA_MODE && rf_mode != TX_STREAM_MODE && rf_mode != TX_CARRIER_MODE && rf_mode != TX_PN_MODE && rf_mode != RX_SCAN_SPECTRUM);
return (tx_data_rd == 0 && tx_data_wr == 0 && rf_mode != TX_DATA_MODE &&
rf_mode != TX_STREAM_MODE && rf_mode != TX_CARRIER_MODE && rf_mode != TX_PN_MODE &&
rf_mode != RX_SCAN_SPECTRUM);
}
// ************************************
@ -2157,15 +1770,11 @@ void rfm22_setFreqCalibration(uint8_t value)
tx_data_rd = tx_data_wr = 0;
}
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
rfm22_write(RFM22_xtal_osc_load_cap, osc_load_cap);
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
if (prev_rf_mode == TX_CARRIER_MODE || prev_rf_mode == TX_PN_MODE)
rfm22_setTxMode(prev_rf_mode);
@ -2202,7 +1811,7 @@ void rfm22_process(void)
{
static int cntr = 0;
if (cntr >= 500) {
if (cntr >= 5000) {
DEBUG_PRINTF(2, "Process\n\r");
cntr = 0;
} else
@ -2334,18 +1943,6 @@ void rfm22_process(void)
#endif
}
// ************************************
void rfm22_TxDataByte_SetCallback(t_rfm22_TxDataByteCallback new_function)
{
tx_data_byte_callback_function = new_function;
}
void rfm22_RxData_SetCallback(t_rfm22_RxDataCallback new_function)
{
rx_data_callback_function = new_function;
}
// ************************************
// reset the RF module
@ -2361,9 +1958,7 @@ int rfm22_resetModule(uint8_t mode, uint32_t min_frequency_hz, uint32_t max_freq
// ****************
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = TRUE;
#endif
// ****************
// setup the SPI port
@ -2410,16 +2005,10 @@ int rfm22_resetModule(uint8_t mode, uint32_t min_frequency_hz, uint32_t max_freq
// ****************
#if defined(RFM22_EXT_INT_USE)
exec_using_spi = FALSE;
#endif
// ****************
#if defined(RFM22_EXT_INT_USE)
inside_ext_int = FALSE;
#endif
rf_mode = mode;
device_status = int_status1 = int_status2 = ezmac_status = 0;
@ -2427,16 +2016,12 @@ int rfm22_resetModule(uint8_t mode, uint32_t min_frequency_hz, uint32_t max_freq
rssi = 0;
rssi_dBm = -200;
tx_data_byte_callback_function = NULL;
rx_data_callback_function = NULL;
rx_buffer_current = 0;
rx_buffer_wr = 0;
rx_packet_wr = 0;
rx_packet_rssi_dBm = -200;
rx_packet_afc_Hz = 0;
tx_data_addr = NULL;
tx_data_rd = tx_data_wr = 0;
lookup_index = 0;
@ -2584,11 +2169,14 @@ int rfm22_init_scan_spectrum(uint32_t min_frequency_hz, uint32_t max_frequency_h
rfm22_write(RFM22_header_enable1, 0xff);
rfm22_write(RFM22_header_enable0, 0xff);
// rfm22_write(RFM22_frequency_hopping_step_size, 0); // set frequency hopping channel step size (multiples of 10kHz)
// set frequency hopping channel step size (multiples of 10kHz)
// rfm22_write(RFM22_frequency_hopping_step_size, 0);
rfm22_setNominalCarrierFrequency(min_frequency_hz); // set our nominal carrier frequency
// set our nominal carrier frequency
rfm22_setNominalCarrierFrequency(min_frequency_hz);
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_lna_sw | 0); // set minimum tx power
// set minimum tx power
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_lna_sw | 0);
rfm22_write(RFM22_agc_override1, RFM22_agc_ovr1_sgi | RFM22_agc_ovr1_agcen);
@ -2596,10 +2184,8 @@ int rfm22_init_scan_spectrum(uint32_t min_frequency_hz, uint32_t max_frequency_h
// rfm22_write(RFM22_vco_calibration_override, 0x40);
// rfm22_write(RFM22_chargepump_current_trimming_override, 0x80);
#if defined(RFM22_EXT_INT_USE)
// Enable RF module external interrupt
rfm22_enableExtInt();
#endif
rfm22_setRxMode(RX_SCAN_SPECTRUM, true);
@ -2660,10 +2246,8 @@ int rfm22_init_tx_stream(uint32_t min_frequency_hz, uint32_t max_frequency_hz)
rfm22_write(RFM22_tx_fifo_control1, TX_FIFO_HI_WATERMARK); // TX FIFO Almost Full Threshold (0 - 63)
rfm22_write(RFM22_tx_fifo_control2, TX_FIFO_LO_WATERMARK); // TX FIFO Almost Empty Threshold (0 - 63)
#if defined(RFM22_EXT_INT_USE)
// Enable RF module external interrupt
rfm22_enableExtInt();
#endif
initialized = true;
@ -2722,12 +2306,11 @@ int rfm22_init_rx_stream(uint32_t min_frequency_hz, uint32_t max_frequency_hz)
// rfm22_write(RFM22_vco_calibration_override, 0x40);
// rfm22_write(RFM22_chargepump_current_trimming_override, 0x80);
rfm22_write(RFM22_rx_fifo_control, RX_FIFO_HI_WATERMARK); // RX FIFO Almost Full Threshold (0 - 63)
// RX FIFO Almost Full Threshold (0 - 63)
rfm22_write(RFM22_rx_fifo_control, RX_FIFO_HI_WATERMARK);
#if defined(RFM22_EXT_INT_USE)
// Enable RF module external interrupt
rfm22_enableExtInt();
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);
@ -2745,15 +2328,11 @@ int rfm22_init_normal(uint32_t min_frequency_hz, uint32_t max_frequency_hz, uint
if (res < 0)
return res;
// ****************
// initialize the frequency hopping step size
freq_hop_step_size /= 10000; // in 10kHz increments
if (freq_hop_step_size > 255) freq_hop_step_size = 255;
frequency_hop_step_size_reg = freq_hop_step_size;
// ****************
// set the RF datarate
rfm22_setDatarate(RFM22_DEFAULT_RF_DATARATE, TRUE);
@ -2762,77 +2341,76 @@ int rfm22_init_normal(uint32_t min_frequency_hz, uint32_t max_frequency_hz, uint
rfm22_write(RFM22_modulation_mode_control2, RFM22_mmc2_trclk_clk_none | RFM22_mmc2_dtmod_fifo | fd_bit | RFM22_mmc2_modtyp_gfsk);
// setup to read the internal temperature sensor
adc_config = RFM22_ac_adcsel_temp_sensor | RFM22_ac_adcref_bg; // ADC used to sample the temperature sensor
rfm22_write(RFM22_adc_config, adc_config); //
rfm22_write(RFM22_adc_sensor_amp_offset, 0); // adc offset
rfm22_write(RFM22_temp_sensor_calib, RFM22_tsc_tsrange0 | RFM22_tsc_entsoffs); // temp sensor calibration .. 40C to +64C 0.5C resolution
rfm22_write(RFM22_temp_value_offset, 0); // temp sensor offset
rfm22_write(RFM22_adc_config, adc_config | RFM22_ac_adcstartbusy); // start an ADC conversion
rfm22_write(RFM22_rssi_threshold_clear_chan_indicator, (-90 + 122) * 2); // set the RSSI threshold interrupt to about -90dBm
// ADC used to sample the temperature sensor
adc_config = RFM22_ac_adcsel_temp_sensor | RFM22_ac_adcref_bg;
rfm22_write(RFM22_adc_config, adc_config);
// adc offset
rfm22_write(RFM22_adc_sensor_amp_offset, 0);
// temp sensor calibration .. 40C to +64C 0.5C resolution
rfm22_write(RFM22_temp_sensor_calib, RFM22_tsc_tsrange0 | RFM22_tsc_entsoffs);
// temp sensor offset
rfm22_write(RFM22_temp_value_offset, 0);
// start an ADC conversion
rfm22_write(RFM22_adc_config, adc_config | RFM22_ac_adcstartbusy);
// set the RSSI threshold interrupt to about -90dBm
rfm22_write(RFM22_rssi_threshold_clear_chan_indicator, (-90 + 122) * 2);
// enable the internal Tx & Rx packet handlers (with CRC)
// rfm22_write(RFM22_data_access_control, RFM22_dac_enpacrx | RFM22_dac_enpactx | RFM22_dac_encrc | RFM22_dac_crc_crc16);
// enable the internal Tx & Rx packet handlers (without CRC)
rfm22_write(RFM22_data_access_control, RFM22_dac_enpacrx | RFM22_dac_enpactx);
rfm22_write(RFM22_preamble_length, TX_PREAMBLE_NIBBLES); // x-nibbles tx preamble
rfm22_write(RFM22_preamble_detection_ctrl1, RX_PREAMBLE_NIBBLES << 3); // x-nibbles rx preamble detection
// x-nibbles tx preamble
rfm22_write(RFM22_preamble_length, TX_PREAMBLE_NIBBLES);
// x-nibbles rx preamble detection
rfm22_write(RFM22_preamble_detection_ctrl1, RX_PREAMBLE_NIBBLES << 3);
rfm22_write(RFM22_header_control1, RFM22_header_cntl1_bcen_none | RFM22_header_cntl1_hdch_none); // header control - we are not using the header
rfm22_write(RFM22_header_control2, RFM22_header_cntl2_hdlen_none | RFM22_header_cntl2_synclen_3210 | ((TX_PREAMBLE_NIBBLES >> 8) & 0x01)); // no header bytes, synchronization word length 3, 2, 1 & 0 used, packet length included in header.
// header control - we are not using the header
rfm22_write(RFM22_header_control1, RFM22_header_cntl1_bcen_none | RFM22_header_cntl1_hdch_none);
rfm22_write(RFM22_sync_word3, SYNC_BYTE_1); // sync word
rfm22_write(RFM22_sync_word2, SYNC_BYTE_2); //
rfm22_write(RFM22_sync_word1, SYNC_BYTE_3); //
rfm22_write(RFM22_sync_word0, SYNC_BYTE_4); //
/*
rfm22_write(RFM22_transmit_header3, 'p'); // set tx header
rfm22_write(RFM22_transmit_header2, 'i'); //
rfm22_write(RFM22_transmit_header1, 'p'); //
rfm22_write(RFM22_transmit_header0, ' '); //
// no header bytes, synchronization word length 3, 2, 1 & 0 used, packet length included in header.
rfm22_write(RFM22_header_control2, RFM22_header_cntl2_hdlen_none |
RFM22_header_cntl2_synclen_3210 | ((TX_PREAMBLE_NIBBLES >> 8) & 0x01));
rfm22_write(RFM22_check_header3, 'p'); // set expected rx header
rfm22_write(RFM22_check_header2, 'i'); //
rfm22_write(RFM22_check_header1, 'p'); //
rfm22_write(RFM22_check_header0, ' '); //
// sync word
rfm22_write(RFM22_sync_word3, SYNC_BYTE_1);
rfm22_write(RFM22_sync_word2, SYNC_BYTE_2);
rfm22_write(RFM22_sync_word1, SYNC_BYTE_3);
rfm22_write(RFM22_sync_word0, SYNC_BYTE_4);
// all the bits to be checked
rfm22_write(RFM22_header_enable3, 0xff);
rfm22_write(RFM22_header_enable2, 0xff);
rfm22_write(RFM22_header_enable1, 0xff);
rfm22_write(RFM22_header_enable0, 0xff);
*/ // no bits to be checked
// no bits to be checked
rfm22_write(RFM22_header_enable3, 0x00);
rfm22_write(RFM22_header_enable2, 0x00);
rfm22_write(RFM22_header_enable1, 0x00);
rfm22_write(RFM22_header_enable0, 0x00);
// rfm22_write(RFM22_modem_test, 0x01);
rfm22_write(RFM22_agc_override1, RFM22_agc_ovr1_agcen);
// rfm22_write(RFM22_agc_override1, RFM22_agc_ovr1_sgi | RFM22_agc_ovr1_agcen);
rfm22_write(RFM22_frequency_hopping_step_size, frequency_hop_step_size_reg); // set frequency hopping channel step size (multiples of 10kHz)
// set frequency hopping channel step size (multiples of 10kHz)
rfm22_write(RFM22_frequency_hopping_step_size, frequency_hop_step_size_reg);
rfm22_setNominalCarrierFrequency((min_frequency_hz + max_frequency_hz) / 2); // set our nominal carrier frequency
// set our nominal carrier frequency
rfm22_setNominalCarrierFrequency((min_frequency_hz + max_frequency_hz) / 2);
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_0 | RFM22_tx_pwr_lna_sw | tx_power); // set the tx power
// rfm22_write(RFM22_tx_power, RFM22_tx_pwr_lna_sw | tx_power); // set the tx power
// set the tx power
rfm22_write(RFM22_tx_power, RFM22_tx_pwr_papeaken | RFM22_tx_pwr_papeaklvl_0 |
RFM22_tx_pwr_lna_sw | tx_power);
// rfm22_write(RFM22_vco_current_trimming, 0x7f);
// rfm22_write(RFM22_vco_calibration_override, 0x40);
// rfm22_write(RFM22_chargepump_current_trimming_override, 0x80);
// TX FIFO Almost Full Threshold (0 - 63)
rfm22_write(RFM22_tx_fifo_control1, TX_FIFO_HI_WATERMARK);
rfm22_write(RFM22_tx_fifo_control1, TX_FIFO_HI_WATERMARK); // TX FIFO Almost Full Threshold (0 - 63)
rfm22_write(RFM22_tx_fifo_control2, TX_FIFO_LO_WATERMARK); // TX FIFO Almost Empty Threshold (0 - 63)
// TX FIFO Almost Empty Threshold (0 - 63)
rfm22_write(RFM22_tx_fifo_control2, TX_FIFO_LO_WATERMARK);
rfm22_write(RFM22_rx_fifo_control, RX_FIFO_HI_WATERMARK); // RX FIFO Almost Full Threshold (0 - 63)
// RX FIFO Almost Full Threshold (0 - 63)
rfm22_write(RFM22_rx_fifo_control, RX_FIFO_HI_WATERMARK);
#if defined(RFM22_EXT_INT_USE)
// Enable RF module external interrupt
rfm22_enableExtInt();
#endif
rfm22_setRxMode(RX_WAIT_PREAMBLE_MODE, false);

View File

@ -490,4 +490,5 @@ const struct pios_rfm22b_cfg pios_rfm22b_cfg = {
.maxConnections = 1,
.id = 0x36249acb
};
#endif /* PIOS_INCLUDE_RFM22B */

View File

@ -38,7 +38,7 @@
</font>
</property>
<property name="text">
<string>Aircraft type:</string>
<string>Vehicle type:</string>
</property>
</widget>
</item>
@ -173,7 +173,7 @@ QGroupBox::title {
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="fwEngineChannel">
<widget class="QComboBox" name="fwEngineChannelBox">
<property name="toolTip">
<string>Select output channel for the engine</string>
</property>
@ -193,7 +193,7 @@ QGroupBox::title {
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="fwAileron1Channel">
<widget class="QComboBox" name="fwAileron1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first aileron (or elevon)</string>
</property>
@ -216,7 +216,7 @@ QGroupBox::title {
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="fwAileron2Channel">
<widget class="QComboBox" name="fwAileron2ChannelBox">
<property name="enabled">
<bool>false</bool>
</property>
@ -239,7 +239,7 @@ QGroupBox::title {
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="fwElevator1Channel">
<widget class="QComboBox" name="fwElevator1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first elevator</string>
</property>
@ -262,12 +262,12 @@ QGroupBox::title {
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="fwElevator2Channel">
<widget class="QComboBox" name="fwElevator2ChannelBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Select output channel for a secondry elevator</string>
<string>Select output channel for a secondary elevator</string>
</property>
</widget>
</item>
@ -279,7 +279,7 @@ QGroupBox::title {
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="fwRudder1Channel">
<widget class="QComboBox" name="fwRudder1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first rudder</string>
</property>
@ -293,7 +293,7 @@ QGroupBox::title {
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="fwRudder2Channel">
<widget class="QComboBox" name="fwRudder2ChannelBox">
<property name="toolTip">
<string>Select output channel for a secondary rudder</string>
</property>
@ -390,7 +390,7 @@ margin:1px;</string>
</widget>
</item>
<item>
<widget class="QLabel" name="label_18">
<widget class="QLabel" name="elevonSliderLabel1">
<property name="text">
<string>50</string>
</property>
@ -446,7 +446,7 @@ margin:1px;</string>
</widget>
</item>
<item>
<widget class="QLabel" name="label_25">
<widget class="QLabel" name="elevonSliderLabel2">
<property name="text">
<string>50</string>
</property>
@ -533,6 +533,12 @@ margin:1px;</string>
</item>
<item>
<widget class="QPushButton" name="fwThrottleReset">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Reset</string>
</property>
@ -681,7 +687,13 @@ margin:1px;</string>
<item>
<layout class="QVBoxLayout" name="verticalLayout_22">
<item>
<widget class="QLabel" name="label_43">
<widget class="QLabel" name="mrRollMixValue">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>100</string>
</property>
@ -692,6 +704,12 @@ margin:1px;</string>
</item>
<item>
<widget class="QSlider" name="mrRollMixLevel">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Weight of Roll mixing in percent.
Typical values are 100% for + configuration and 50% for X configuration on quads.</string>
@ -729,7 +747,13 @@ margin:1px;</string>
<item>
<layout class="QVBoxLayout" name="verticalLayout_23">
<item>
<widget class="QLabel" name="label_44">
<widget class="QLabel" name="mrYawPitchValue">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>100</string>
</property>
@ -740,6 +764,12 @@ margin:1px;</string>
</item>
<item>
<widget class="QSlider" name="mrPitchMixLevel">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Weight of Pitch mixing in percent.
Typical values are 100% for + configuration and 50% for X configuration on quads.</string>
@ -777,7 +807,13 @@ margin:1px;</string>
<item>
<layout class="QVBoxLayout" name="verticalLayout_21">
<item>
<widget class="QLabel" name="label_45">
<widget class="QLabel" name="mrYawMixValue">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>50</string>
</property>
@ -788,6 +824,12 @@ margin:1px;</string>
</item>
<item>
<widget class="QSlider" name="mrYawMixLevel">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Weight of Yaw mixing in percent.
Typical value is 50% for + or X configuration on quads.</string>
@ -923,6 +965,12 @@ margin:1px;</string>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Reset</string>
</property>
@ -992,7 +1040,7 @@ margin:1px;</string>
</widget>
</item>
<item>
<widget class="QComboBox" name="triYawChannel">
<widget class="QComboBox" name="triYawChannelBox">
<property name="enabled">
<bool>false</bool>
</property>
@ -1046,7 +1094,7 @@ margin:1px;</string>
<number>3</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<widget class="QLabel" name="MotorOutputLabel1">
<property name="styleSheet">
<string notr="true">background-color: qlineargradient(spread:reflect, x1:0.507, y1:0, x2:0.507, y2:0.772, stop:0.208955 rgba(74, 74, 74, 255), stop:0.78607 rgba(36, 36, 36, 255));
color: rgb(255, 255, 255);
@ -1060,7 +1108,7 @@ margin:1px;</string>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="multiMotor1">
<widget class="QComboBox" name="multiMotorChannelBox1">
<property name="toolTip">
<string>Assign your motor output channels using the drawing above as a reference. Respect propeller rotation.</string>
</property>
@ -1081,7 +1129,7 @@ margin:1px;</string>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="multiMotor2">
<widget class="QComboBox" name="multiMotorChannelBox2">
<property name="toolTip">
<string>Assign your motor output channels using the drawing above as a reference. Respect propeller rotation.</string>
</property>
@ -1102,7 +1150,7 @@ margin:1px;</string>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="multiMotor3">
<widget class="QComboBox" name="multiMotorChannelBox3">
<property name="toolTip">
<string>Assign your motor output channels using the drawing above as a reference. Respect propeller rotation.</string>
</property>
@ -1123,7 +1171,7 @@ margin:1px;</string>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="multiMotor4">
<widget class="QComboBox" name="multiMotorChannelBox4">
<property name="toolTip">
<string>Assign your motor output channels using the drawing above as a reference. Respect propeller rotation.</string>
</property>
@ -1170,7 +1218,7 @@ margin:1px;</string>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="multiMotor5">
<widget class="QComboBox" name="multiMotorChannelBox5">
<property name="enabled">
<bool>false</bool>
</property>
@ -1200,7 +1248,7 @@ margin:1px;</string>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="multiMotor6">
<widget class="QComboBox" name="multiMotorChannelBox6">
<property name="enabled">
<bool>false</bool>
</property>
@ -1224,7 +1272,7 @@ margin:1px;</string>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="multiMotor7">
<widget class="QComboBox" name="multiMotorChannelBox7">
<property name="enabled">
<bool>false</bool>
</property>
@ -1248,7 +1296,7 @@ margin:1px;</string>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="multiMotor8">
<widget class="QComboBox" name="multiMotorChannelBox8">
<property name="enabled">
<bool>false</bool>
</property>
@ -1334,6 +1382,520 @@ margin:1px;</string>
</item>
</layout>
</widget>
<widget class="QWidget" name="groundVehicle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Vehicle type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="groundVehicleType"/>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_23">
<item>
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Channel Assignment</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="title">
<string>Output channel asignmets</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="gvEngineLabel">
<property name="minimumSize">
<size>
<width>77</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Engine</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="gvEngineChannelBox">
<property name="toolTip">
<string>Select output channel for the engine</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="gvAileron1Label">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Aileron 1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="gvAileron1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first aileron (or elevon)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="gvAileron2Label">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Aileron 2</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="gvAileron2ChannelBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Select output channel for the second aileron (or elevon)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="gvMotor1Label">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Motor</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="gvMotor1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first motor</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="gvMotor2Label">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>47</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Motor 2</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="gvMotor2ChannelBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Select output channel for a second motor</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="gvSteering1Label">
<property name="text">
<string>Front Steering</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="gvSteering1ChannelBox">
<property name="toolTip">
<string>Select output channel for the first steering actuator</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="gvSteering2Label">
<property name="text">
<string>Rear Steering</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="gvSteering2ChannelBox">
<property name="toolTip">
<string>Select output channel for a second steering actuator</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="differentialSteeringMixBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Differential Steering Mix</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QLabel" name="differentialSteeringLabel1">
<property name="minimumSize">
<size>
<width>65</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Left %</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="differentialSteeringSlider1">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="gvDiffSteering1Label">
<property name="text">
<string>50</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<widget class="QLabel" name="differentialSteeringLabel2">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Right %</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="differentialSteeringSlider2">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="gvDiffSteering2Label">
<property name="text">
<string>50</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="gvThrottleCurve1GroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Front throttle curve</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="MixerCurveWidget" name="groundVehicleThrottle1" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="gvThrottleCurve1Reset">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="gvThrottleCurve1ItemValue">
<property name="text">
<string>Val: 0.00</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gvThrottleCurve2GroupBox">
<property name="title">
<string>Rear throttle curve</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<widget class="MixerCurveWidget" name="groundVehicleThrottle2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="gvThrottleCurve2Reset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="gvThrottleCurve2ItemValue">
<property name="text">
<string>Val: 0.00</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="gvStatusLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Mixer OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="custom">
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
@ -1990,7 +2552,7 @@ margin:1px;</string>
</widget>
</item>
<item>
<widget class="QLabel" name="feedForwardValue">
<widget class="QLabel" name="feedForwardSliderValue">
<property name="minimumSize">
<size>
<width>30</width>
@ -2095,7 +2657,7 @@ Do it after accel time is setup.</string>
</widget>
</item>
<item>
<widget class="QLabel" name="label_33">
<widget class="QLabel" name="maxAccelSliderValue">
<property name="text">
<string>1000</string>
</property>
@ -2329,7 +2891,7 @@ p, li { white-space: pre-wrap; }
<customwidget>
<class>ConfigccpmWidget</class>
<extends>QWidget</extends>
<header>configccpmwidget.h</header>
<header>cfg_vehicletypes/configccpmwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
@ -2340,7 +2902,7 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>feedForwardSlider</sender>
<signal>valueChanged(int)</signal>
<receiver>feedForwardValue</receiver>
<receiver>feedForwardSliderValue</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
@ -2356,7 +2918,7 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>maxAccelSlider</sender>
<signal>valueChanged(int)</signal>
<receiver>label_33</receiver>
<receiver>maxAccelSliderValue</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
@ -2372,7 +2934,7 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>elevonSlider1</sender>
<signal>valueChanged(int)</signal>
<receiver>label_18</receiver>
<receiver>elevonSliderLabel1</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
@ -2388,12 +2950,12 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>elevonSlider2</sender>
<signal>valueChanged(int)</signal>
<receiver>label_25</receiver>
<receiver>elevonSliderLabel2</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>126</y>
<x>362</x>
<y>299</y>
</hint>
<hint type="destinationlabel">
<x>124</x>
@ -2401,10 +2963,42 @@ p, li { white-space: pre-wrap; }
</hint>
</hints>
</connection>
<connection>
<sender>differentialSteeringSlider1</sender>
<signal>valueChanged(int)</signal>
<receiver>gvDiffSteering1Label</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>126</y>
</hint>
<hint type="destinationlabel">
<x>315</x>
<y>391</y>
</hint>
</hints>
</connection>
<connection>
<sender>differentialSteeringSlider2</sender>
<signal>valueChanged(int)</signal>
<receiver>gvDiffSteering2Label</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
<x>124</x>
<y>126</y>
</hint>
<hint type="destinationlabel">
<x>390</x>
<y>391</y>
</hint>
</hints>
</connection>
<connection>
<sender>mrPitchMixLevel</sender>
<signal>valueChanged(int)</signal>
<receiver>label_44</receiver>
<receiver>mrPitchMixValue</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
@ -2420,7 +3014,7 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>mrRollMixLevel</sender>
<signal>valueChanged(int)</signal>
<receiver>label_43</receiver>
<receiver>mrRollMixValue</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">
@ -2436,7 +3030,7 @@ p, li { white-space: pre-wrap; }
<connection>
<sender>mrYawMixLevel</sender>
<signal>valueChanged(int)</signal>
<receiver>label_45</receiver>
<receiver>mrYawMixValue</receiver>
<slot>setNum(int)</slot>
<hints>
<hint type="sourcelabel">

View File

@ -81,7 +81,7 @@ public:
ConfigccpmWidget(QWidget *parent = 0);
~ConfigccpmWidget();
friend class ConfigAirframeWidget;
friend class ConfigVehicleTypeWidget;
private:
Ui_ccpmWidget *m_ccpm;

View File

@ -0,0 +1,699 @@
/**
******************************************************************************
*
* @file configfixedwidget.cpp
* @author E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
* @{
* @brief ccpm configuration panel
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//#include "configfixedwingwidget.h"
#include "configvehicletypewidget.h"
#include "mixersettings.h"
#include <QDebug>
#include <QStringList>
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <QBrush>
#include <math.h>
#include <QMessageBox>
#include "mixersettings.h"
#include "systemsettings.h"
#include "actuatorcommand.h"
/**
Helper function to setup the UI
*/
void ConfigVehicleTypeWidget::setupFixedWingUI(QString frameType)
{
if (frameType == "FixedWing" || frameType == "Elevator aileron rudder") {
// Setup the UI
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Elevator aileron rudder"));
m_aircraft->fwRudder1ChannelBox->setEnabled(true);
m_aircraft->fwRudder1Label->setEnabled(true);
m_aircraft->fwRudder2ChannelBox->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
m_aircraft->fwElevator1ChannelBox->setEnabled(true);
m_aircraft->fwElevator1Label->setEnabled(true);
m_aircraft->fwElevator2ChannelBox->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
m_aircraft->fwAileron1ChannelBox->setEnabled(true);
m_aircraft->fwAileron1Label->setEnabled(true);
m_aircraft->fwAileron2ChannelBox->setEnabled(true);
m_aircraft->fwAileron2Label->setEnabled(true);
m_aircraft->fwAileron1Label->setText("Aileron 1");
m_aircraft->fwAileron2Label->setText("Aileron 2");
m_aircraft->fwElevator1Label->setText("Elevator 1");
m_aircraft->fwElevator2Label->setText("Elevator 2");
m_aircraft->elevonMixBox->setHidden(true);
} else if (frameType == "FixedWingElevon" || frameType == "Elevon") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Elevon"));
m_aircraft->fwAileron1Label->setText("Elevon 1");
m_aircraft->fwAileron2Label->setText("Elevon 2");
m_aircraft->fwElevator1ChannelBox->setEnabled(false);
m_aircraft->fwElevator1Label->setEnabled(false);
m_aircraft->fwElevator2ChannelBox->setEnabled(false);
m_aircraft->fwElevator2Label->setEnabled(false);
m_aircraft->fwRudder1ChannelBox->setEnabled(true);
m_aircraft->fwRudder1Label->setEnabled(true);
m_aircraft->fwRudder2ChannelBox->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
m_aircraft->fwElevator1Label->setText("Elevator 1");
m_aircraft->fwElevator2Label->setText("Elevator 2");
m_aircraft->elevonMixBox->setHidden(false);
m_aircraft->elevonLabel1->setText("Roll");
m_aircraft->elevonLabel2->setText("Pitch");
} else if (frameType == "FixedWingVtail" || frameType == "Vtail") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Vtail"));
m_aircraft->fwRudder1ChannelBox->setEnabled(false);
m_aircraft->fwRudder1Label->setEnabled(false);
m_aircraft->fwRudder2ChannelBox->setEnabled(false);
m_aircraft->fwRudder2Label->setEnabled(false);
m_aircraft->fwElevator1ChannelBox->setEnabled(true);
m_aircraft->fwElevator1Label->setEnabled(true);
m_aircraft->fwElevator1Label->setText("Vtail 1");
m_aircraft->fwElevator2Label->setText("Vtail 2");
m_aircraft->elevonMixBox->setHidden(false);
m_aircraft->fwElevator2ChannelBox->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
m_aircraft->fwAileron1Label->setText("Aileron 1");
m_aircraft->fwAileron2Label->setText("Aileron 2");
m_aircraft->elevonLabel1->setText("Rudder");
m_aircraft->elevonLabel2->setText("Pitch");
}
}
/**
Helper function to update the UI widget objects
*/
QString ConfigVehicleTypeWidget::updateFixedWingObjectsFromWidgets()
{
QString airframeType = "FixedWing";
// Save the curve (common to all Fixed wing frames)
UAVDataObject *obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// Remove Feed Forward, it is pointless on a plane:
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble(0);
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->fixedWingThrottle->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
//All airframe types must start with "FixedWing"
if (m_aircraft->fixedWingType->currentText() == "Elevator aileron rudder" ) {
airframeType = "FixedWing";
setupFrameFixedWing( airframeType );
} else if (m_aircraft->fixedWingType->currentText() == "Elevon") {
airframeType = "FixedWingElevon";
setupFrameElevon( airframeType );
} else { // "Vtail"
airframeType = "FixedWingVtail";
setupFrameVtail( airframeType );
}
// Now reflect those settings in the "Custom" panel as well
updateCustomAirframeUI();
return airframeType;
}
/**
Helper function to refresh the UI widget values
*/
void ConfigVehicleTypeWidget::refreshFixedWingWidgetsValues(QString frameType)
{
UAVDataObject* obj;
UAVObjectField *field;
// Then retrieve how channels are setup
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
field = obj->getField(QString("FixedWingThrottle"));
Q_ASSERT(field);
m_aircraft->fwEngineChannelBox->setCurrentIndex(m_aircraft->fwEngineChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll1"));
Q_ASSERT(field);
m_aircraft->fwAileron1ChannelBox->setCurrentIndex(m_aircraft->fwAileron1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll2"));
Q_ASSERT(field);
m_aircraft->fwAileron2ChannelBox->setCurrentIndex(m_aircraft->fwAileron2ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingPitch1"));
Q_ASSERT(field);
m_aircraft->fwElevator1ChannelBox->setCurrentIndex(m_aircraft->fwElevator1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingPitch2"));
Q_ASSERT(field);
m_aircraft->fwElevator2ChannelBox->setCurrentIndex(m_aircraft->fwElevator2ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw1"));
Q_ASSERT(field);
m_aircraft->fwRudder1ChannelBox->setCurrentIndex(m_aircraft->fwRudder1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw2"));
Q_ASSERT(field);
m_aircraft->fwRudder2ChannelBox->setCurrentIndex(m_aircraft->fwRudder2ChannelBox->findText(field->getValue().toString()));
if (frameType == "FixedWingElevon") {
// If the airframe is elevon, restore the slider setting
// Find the channel number for Elevon1 (FixedWingRoll1)
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int chMixerNumber = m_aircraft->fwAileron1ChannelBox->currentIndex()-1;
if (chMixerNumber >= 0) { // If for some reason the actuators were incoherent, we might fail here, hence the check.
field = obj->getField(mixerVectors.at(chMixerNumber));
int ti = field->getElementNames().indexOf("Roll");
m_aircraft->elevonSlider1->setValue(field->getDouble(ti)*100);
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->elevonSlider2->setValue(field->getDouble(ti)*100);
}
}
if (frameType == "FixedWingVtail") {
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int chMixerNumber = m_aircraft->fwElevator1ChannelBox->currentIndex()-1;
if (chMixerNumber >=0) {
field = obj->getField(mixerVectors.at(chMixerNumber));
int ti = field->getElementNames().indexOf("Yaw");
m_aircraft->elevonSlider1->setValue(field->getDouble(ti)*100);
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->elevonSlider2->setValue(field->getDouble(ti)*100);
}
}
}
/**
Setup Elevator/Aileron/Rudder airframe.
If both Aileron channels are set to 'None' (EasyStar), do Pitch/Rudder mixing
Returns False if impossible to create the mixer.
*/
bool ConfigVehicleTypeWidget::setupFrameFixedWing(QString airframeType)
{
// Check coherence:
//Show any config errors in GUI
throwFixedWingChannelConfigError(airframeType);
// - At least Pitch and either Roll or Yaw
if (m_aircraft->fwEngineChannelBox->currentText() == "None" ||
m_aircraft->fwElevator1ChannelBox->currentText() == "None" ||
((m_aircraft->fwAileron1ChannelBox->currentText() == "None") &&
(m_aircraft->fwRudder1ChannelBox->currentText() == "None"))) {
// TODO: explain the problem in the UI
// m_aircraft->fwStatusLabel->setText("ERROR: check channel assignment");
return false;
}
// Now setup the channels:
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevator
UAVObjectField *field = obj->getField("FixedWingPitch1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator1ChannelBox->currentText());
field = obj->getField("FixedWingPitch2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator2ChannelBox->currentText());
// Aileron
field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1ChannelBox->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2ChannelBox->currentText());
// Rudder
field = obj->getField("FixedWingYaw1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder1ChannelBox->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannelBox->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int tmpVal = m_aircraft->fwEngineChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(tmpVal));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
// Rudder
tmpVal = m_aircraft->fwRudder1ChannelBox->currentIndex()-1;
// tmpVal will be -1 if rudder is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(127, ti);
} // Else: we have no rudder, only ailerons, we're fine with it.
// Ailerons
tmpVal = m_aircraft->fwAileron1ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127, ti);
// Only set Aileron 2 if Aileron 1 is defined
tmpVal = m_aircraft->fwAileron2ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127, ti);
}
} // Else we have no ailerons. Our consistency check guarantees we have
// rudder in this case, so we're fine with it too.
// Elevator
tmpVal = m_aircraft->fwElevator1ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(127, ti);
// Only set Elevator 2 if it is defined
tmpVal = m_aircraft->fwElevator2ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(127, ti);
}
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup Elevon
*/
bool ConfigVehicleTypeWidget::setupFrameElevon(QString airframeType)
{
// Check coherence:
//Show any config errors in GUI
throwFixedWingChannelConfigError(airframeType);
// - At least Aileron1 and Aileron 2, and engine
if (m_aircraft->fwEngineChannelBox->currentText() == "None" ||
m_aircraft->fwAileron1ChannelBox->currentText() == "None" ||
m_aircraft->fwAileron2ChannelBox->currentText() == "None") {
// TODO: explain the problem in the UI
// m_aircraft->fwStatusLabel->setText("ERROR: check channel assignment");
return false;
}
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevons
UAVObjectField *field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1ChannelBox->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2ChannelBox->currentText());
// Rudder 1 (can be None)
field = obj->getField("FixedWingYaw1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder1ChannelBox->currentText());
// Rudder 2 (can be None)
field = obj->getField("FixedWingYaw2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder2ChannelBox->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannelBox->currentText());
obj->updated();
// Save the curve:
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int tmpVal = m_aircraft->fwEngineChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(tmpVal));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
// Rudder 1
tmpVal = m_aircraft->fwRudder1ChannelBox->currentIndex()-1;
// tmpVal will be -1 if rudder 1 is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(127, ti);
} // Else: we have no rudder, only elevons, we're fine with it.
// Rudder 2
tmpVal = m_aircraft->fwRudder2ChannelBox->currentIndex()-1;
// tmpVal will be -1 if rudder 2 is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(-127, ti);
} // Else: we have no rudder, only elevons, we're fine with it.
tmpVal = m_aircraft->fwAileron1ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue((double)m_aircraft->elevonSlider1->value()*1.27,ti);
}
tmpVal = m_aircraft->fwAileron2ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(-(double)m_aircraft->elevonSlider1->value()*1.27,ti);
}
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup VTail
*/
bool ConfigVehicleTypeWidget::setupFrameVtail(QString airframeType)
{
// Check coherence:
//Show any config errors in GUI
throwFixedWingChannelConfigError(airframeType);
// - At least Pitch1 and Pitch2, and engine
if (m_aircraft->fwEngineChannelBox->currentText() == "None" ||
m_aircraft->fwElevator1ChannelBox->currentText() == "None" ||
m_aircraft->fwElevator2ChannelBox->currentText() == "None") {
// TODO: explain the problem in the UI
// m_aircraft->fwStatusLabel->setText("WARNING: check channel assignment");
return false;
}
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevons
UAVObjectField *field = obj->getField("FixedWingPitch1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator1ChannelBox->currentText());
field = obj->getField("FixedWingPitch2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator2ChannelBox->currentText());
field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1ChannelBox->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2ChannelBox->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannelBox->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int tmpVal = m_aircraft->fwEngineChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(tmpVal));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
tmpVal = m_aircraft->fwAileron1ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127,ti);
}
tmpVal = m_aircraft->fwAileron2ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(-127,ti);
}
// Now compute the VTail
tmpVal = m_aircraft->fwElevator1ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue((double)m_aircraft->elevonSlider1->value()*1.27,ti);
tmpVal = m_aircraft->fwElevator2ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(-(double)m_aircraft->elevonSlider1->value()*1.27,ti);
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
This function displays text and color formatting in order to help the user understand what channels have not yet been configured.
*/
void ConfigVehicleTypeWidget::throwFixedWingChannelConfigError(QString airframeType)
{
//Initialize configuration error flag
bool error=false;
//Create a red block. All combo boxes are the same size, so any one should do as a model
int size = m_aircraft->fwEngineChannelBox->style()->pixelMetric(QStyle::PM_SmallIconSize);
QPixmap pixmap(size,size);
pixmap.fill(QColor("red"));
if (airframeType == "FixedWing" ) {
if (m_aircraft->fwEngineChannelBox->currentText() == "None"){
m_aircraft->fwEngineChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwEngineChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if (m_aircraft->fwElevator1ChannelBox->currentText() == "None"){
m_aircraft->fwElevator1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwElevator1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if ((m_aircraft->fwAileron1ChannelBox->currentText() == "None") && (m_aircraft->fwRudder1ChannelBox->currentText() == "None")) {
pixmap.fill(QColor("green"));
m_aircraft->fwAileron1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
m_aircraft->fwRudder1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwAileron1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->fwRudder1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
} else if (airframeType == "FixedWingElevon"){
if (m_aircraft->fwEngineChannelBox->currentText() == "None"){
m_aircraft->fwEngineChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwEngineChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if(m_aircraft->fwAileron1ChannelBox->currentText() == "None"){
m_aircraft->fwAileron1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwAileron1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if (m_aircraft->fwAileron2ChannelBox->currentText() == "None"){
m_aircraft->fwAileron2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwAileron2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
} else if ( airframeType == "FixedWingVtail"){
if (m_aircraft->fwEngineChannelBox->currentText() == "None"){
m_aircraft->fwEngineChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwEngineChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if(m_aircraft->fwElevator1ChannelBox->currentText() == "None"){
m_aircraft->fwElevator1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwElevator1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if(m_aircraft->fwElevator2ChannelBox->currentText() == "None"){
m_aircraft->fwElevator2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->fwElevator2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
}
if (error){
m_aircraft->fwStatusLabel->setText(QString("<font color='red'>ERROR: Assign all necessary channels</font>"));
}
}

View File

@ -0,0 +1,751 @@
/**
******************************************************************************
*
* @file configgroundvehiclemwidget.cpp
* @author K. Sebesta & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
* @{
* @brief ccpm configuration panel
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//#include "configgroundvehiclewidget.h"
#include "configvehicletypewidget.h"
#include "mixersettings.h"
#include <QDebug>
#include <QStringList>
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <QBrush>
#include <math.h>
#include <QMessageBox>
#include "mixersettings.h"
#include "systemsettings.h"
#include "actuatorcommand.h"
/**
Helper function to setup the UI
*/
void ConfigVehicleTypeWidget::setupGroundVehicleUI(QString frameType)
{
m_aircraft->differentialSteeringMixBox->setHidden(true);
//STILL NEEDS WORK
// Setup the UI
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Ground"));
m_aircraft->gvEngineChannelBox->setEnabled(false);
m_aircraft->gvEngineLabel->setEnabled(false);
m_aircraft->gvAileron1ChannelBox->setEnabled(false);
m_aircraft->gvAileron1Label->setEnabled(false);
m_aircraft->gvAileron2ChannelBox->setEnabled(false);
m_aircraft->gvAileron2Label->setEnabled(false);
if (frameType == "GroundVehicleDifferential" || frameType == "Differential (tank)"){ //Tank
m_aircraft->groundVehicleType->setCurrentIndex(m_aircraft->groundVehicleType->findText("Differential (tank)"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor1Label->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvMotor2Label->setEnabled(true);
m_aircraft->gvMotor1Label->setText("Left motor");
m_aircraft->gvMotor2Label->setText("Right motor");
m_aircraft->gvSteering1ChannelBox->setEnabled(false);
m_aircraft->gvSteering1Label->setEnabled(false);
m_aircraft->gvSteering2ChannelBox->setEnabled(false);
m_aircraft->gvSteering2Label->setEnabled(false);
m_aircraft->gvSteering2Label->setText("Rear steering");
m_aircraft->differentialSteeringMixBox->setHidden(false);
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Left throttle curve");
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Right throttle curve");
}
else if (frameType == "GroundVehicleMotorcycle" || frameType == "Motorcycle"){ //Motorcycle
m_aircraft->groundVehicleType->setCurrentIndex(m_aircraft->groundVehicleType->findText("Motorcycle"));
m_aircraft->gvMotor1ChannelBox->setEnabled(false);
m_aircraft->gvMotor1Label->setEnabled(false);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvMotor2Label->setEnabled(true);
m_aircraft->gvMotor1Label->setText("Front motor");
m_aircraft->gvMotor2Label->setText("Rear motor");
m_aircraft->gvSteering1ChannelBox->setEnabled(true);
m_aircraft->gvSteering1Label->setEnabled(true);
m_aircraft->gvSteering2ChannelBox->setEnabled(true);
m_aircraft->gvSteering2Label->setEnabled(true);
m_aircraft->gvSteering2Label->setText("Balancing");
m_aircraft->differentialSteeringMixBox->setHidden(true);
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Front throttle curve");
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Rear throttle curve");
}
else {//Car
m_aircraft->groundVehicleType->setCurrentIndex(m_aircraft->groundVehicleType->findText("Turnable (car)"));
m_aircraft->gvMotor1ChannelBox->setEnabled(true);
m_aircraft->gvMotor1Label->setEnabled(true);
m_aircraft->gvMotor2ChannelBox->setEnabled(true);
m_aircraft->gvMotor2Label->setEnabled(true);
m_aircraft->gvMotor1Label->setText("Front motor");
m_aircraft->gvMotor2Label->setText("Rear motor");
m_aircraft->gvSteering1ChannelBox->setEnabled(true);
m_aircraft->gvSteering1Label->setEnabled(true);
m_aircraft->gvSteering2ChannelBox->setEnabled(true);
m_aircraft->gvSteering2Label->setEnabled(true);
m_aircraft->differentialSteeringMixBox->setHidden(true);
m_aircraft->gvThrottleCurve1GroupBox->setTitle("Front throttle curve");
m_aircraft->gvThrottleCurve2GroupBox->setTitle("Rear throttle curve");
}
}
/**
Helper function to update the UI widget objects
*/
QString ConfigVehicleTypeWidget::updateGroundVehicleObjectsFromWidgets()
{
QString airframeType = "GroundVehicleCar";
// Save the curve (common to all ground vehicle frames)
UAVDataObject *obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// Remove Feed Forward, it is pointless on a ground vehicle:
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble(0);
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->groundVehicleThrottle1->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
field = obj->getField("ThrottleCurve2");
curve = m_aircraft->groundVehicleThrottle2->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
//All airframe types must start with "GroundVehicle"
if (m_aircraft->groundVehicleType->currentText() == "Turnable (car)" ) {
airframeType = "GroundVehicleCar";
setupGroundVehicleCar(airframeType);
} else if (m_aircraft->groundVehicleType->currentText() == "Differential (tank)") {
airframeType = "GroundVehicleDifferential";
setupGroundVehicleDifferential(airframeType);
} else { // "Motorcycle"
airframeType = "GroundVehicleMotorcycle";
setupGroundVehicleMotorcycle(airframeType);
}
// Now reflect those settings in the "Custom" panel as well
updateCustomAirframeUI();
return airframeType;
}
/**
Helper function to refresh the UI widget values
*/
void ConfigVehicleTypeWidget::refreshGroundVehicleWidgetsValues(QString frameType)
{
UAVDataObject* obj;
UAVObjectField *field;
//THIS SECTION STILL NEEDS WORK. FOR THE MOMENT, USE THE FIXED-WING ONBOARD SETTING IN ORDER TO MINIMIZE CHANCES OF BOLLOXING REAL CODE
// Retrieve channel setup values
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
field = obj->getField(QString("FixedWingThrottle"));
Q_ASSERT(field);
m_aircraft->gvEngineChannelBox->setCurrentIndex(m_aircraft->gvEngineChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll1"));
Q_ASSERT(field);
m_aircraft->gvAileron1ChannelBox->setCurrentIndex(m_aircraft->gvAileron1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll2"));
Q_ASSERT(field);
m_aircraft->gvAileron2ChannelBox->setCurrentIndex(m_aircraft->gvAileron2ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("GroundVehicleThrottle1"));
Q_ASSERT(field);
m_aircraft->gvMotor1ChannelBox->setCurrentIndex(m_aircraft->gvMotor1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("GroundVehicleThrottle2"));
Q_ASSERT(field);
m_aircraft->gvMotor2ChannelBox->setCurrentIndex(m_aircraft->gvMotor2ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("GroundVehicleSteering1"));
Q_ASSERT(field);
m_aircraft->gvSteering1ChannelBox->setCurrentIndex(m_aircraft->gvSteering1ChannelBox->findText(field->getValue().toString()));
field = obj->getField(QString("GroundVehicleSteering2"));
Q_ASSERT(field);
m_aircraft->gvSteering2ChannelBox->setCurrentIndex(m_aircraft->gvSteering2ChannelBox->findText(field->getValue().toString()));
if (frameType == "GroundVehicleDifferential") {
//CURRENTLY BROKEN UNTIL WE DECIDE HOW DIFFERENTIAL SHOULD BEHAVE
// If the vehicle type is "differential", restore the slider setting
// Find the channel number for Motor1
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int chMixerNumber = m_aircraft->gvMotor1ChannelBox->currentIndex()-1;
if (chMixerNumber >= 0) { // If for some reason the actuators were incoherent, we might fail here, hence the check.
field = obj->getField(mixerVectors.at(chMixerNumber));
int ti = field->getElementNames().indexOf("Roll");
m_aircraft->differentialSteeringSlider1->setValue(field->getDouble(ti)*100);
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->differentialSteeringSlider2->setValue(field->getDouble(ti)*100);
}
}
if (frameType == "GroundVehicleMotorcycle") {
//CURRENTLY BROKEN UNTIL WE DECIDE HOW MOTORCYCLE SHOULD BEHAVE
// obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// Q_ASSERT(obj);
// int chMixerNumber = m_aircraft->gvMotor1ChannelBox->currentIndex()-1;
// if (chMixerNumber >=0) {
// field = obj->getField(mixerVectors.at(chMixerNumber));
// int ti = field->getElementNames().indexOf("Yaw");
// m_aircraft->differentialSteeringSlider1->setValue(field->getDouble(ti)*100);
//
// ti = field->getElementNames().indexOf("Pitch");
// m_aircraft->differentialSteeringSlider2->setValue(field->getDouble(ti)*100);
// }
}
}
/**
Setup balancing ground vehicle.
Returns False if impossible to create the mixer.
*/
bool ConfigVehicleTypeWidget::setupGroundVehicleMotorcycle(QString airframeType){
// Check coherence:
//Show any config errors in GUI
throwGroundVehicleChannelConfigError(airframeType);
// - Motor, steering, and balance
if (m_aircraft->gvMotor1ChannelBox->currentText() == "None" ||
(m_aircraft->gvSteering1ChannelBox->currentText() == "None" ||
m_aircraft->gvSteering2ChannelBox->currentText() == "None") )
{
return false;
}
// Now setup the channels:
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Left motor
UAVObjectField *field = obj->getField("GroundVehicleThrottle1");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor1ChannelBox->currentText());
// Right motor
field = obj->getField("GroundVehicleThrottle2");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor2ChannelBox->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
int tmpVal, ti;
// Disable all output channels
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
//Disable output channel
field->setValue("Disabled");
}
// Set all mixer values to zero
foreach(QString mixer, mixerVectors) {
field = obj->getField(mixer);
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(0, ti);
}
// Motor
// Setup motor
tmpVal = m_aircraft->gvMotor2ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo"); //Set motor mixer type to Servo
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1"); //Set motor to full forward
field->setValue(127, ti);
//Steering
// Setup steering
tmpVal = m_aircraft->gvSteering1ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo"); //Set motor mixer type to Servo
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw"); //Set steering response to roll
field->setValue(-127, ti);
ti = field->getElementNames().indexOf("Roll"); //Set steering response to roll
field->setValue(-127, ti);
//Balancing
// Setup balancing servo
tmpVal = m_aircraft->gvSteering2ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo"); //Set motor mixer type to Servo
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw"); //Set balance response to yaw
field->setValue(127, ti);
ti = field->getElementNames().indexOf("Roll"); //Set balance response to roll
field->setValue(127, ti);
obj->updated();
//Output success message
m_aircraft->gvStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup differentially steered ground vehicle.
Returns False if impossible to create the mixer.
*/
bool ConfigVehicleTypeWidget::setupGroundVehicleDifferential(QString airframeType){
// Check coherence:
//Show any config errors in GUI
throwGroundVehicleChannelConfigError(airframeType);
// - Left and right steering
if ( m_aircraft->gvMotor2ChannelBox->currentText() == "None" ||
m_aircraft->gvSteering1ChannelBox->currentText() == "None")
{
return false;
}
// Now setup the channels:
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Left motor
UAVObjectField *field = obj->getField("GroundVehicleThrottle1");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor1ChannelBox->currentText());
// Right motor
field = obj->getField("GroundVehicleThrottle2");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor2ChannelBox->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
int tmpVal, ti;
// Disable all output channels
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
//Disable output channel
field->setValue("Disabled");
}
// Set all mixer values to zero
foreach(QString mixer, mixerVectors) {
field = obj->getField(mixer);
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(0, ti);
}
// Motor
// Setup left motor
tmpVal = m_aircraft->gvMotor1ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo"); //Set motor mixer type to Servo
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1"); //Set motor to full forward
field->setValue(127, ti);
ti = field->getElementNames().indexOf("Yaw"); //Set motor to turn right with increasing throttle
field->setValue(127, ti);
// Setup right motor
tmpVal = m_aircraft->gvMotor2ChannelBox->currentIndex()-1;
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo"); //Set motor mixer type to Servo
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve2"); //Set motor to full forward
field->setValue(127, ti);
ti = field->getElementNames().indexOf("Yaw"); //Set motor to turn left with increasing throttle
field->setValue(-127, ti);
obj->updated();
//Output success message
m_aircraft->gvStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup steerable ground vehicle.
Returns False if impossible to create the mixer.
*/
bool ConfigVehicleTypeWidget::setupGroundVehicleCar(QString airframeType)
{
// Check coherence:
//Show any config errors in GUI
throwGroundVehicleChannelConfigError(airframeType);
// - At least one motor and one steering servo
if ((m_aircraft->gvMotor1ChannelBox->currentText() == "None" &&
m_aircraft->gvMotor2ChannelBox->currentText() == "None") ||
(m_aircraft->gvSteering1ChannelBox->currentText() == "None" &&
m_aircraft->gvSteering2ChannelBox->currentText() == "None"))
{
return false;
}
// else{
// // m_aircraft->gvStatusLabel->setText("Mixer generated");
// QTextEdit* htmlText=new QTextEdit(m_aircraft->gvSteering1Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvSteering1Label->setText(htmlText->toPlainText());
// delete htmlText;
//
// htmlText=new QTextEdit(m_aircraft->gvSteering2Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvSteering2Label->setText(htmlText->toPlainText());
// delete htmlText;
//
// htmlText=new QTextEdit(m_aircraft->gvMotor1Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvMotor1Label->setText(htmlText->toPlainText());
// delete htmlText;
//
// htmlText=new QTextEdit(m_aircraft->gvMotor2Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvMotor2Label->setText(htmlText->toPlainText());
// }
// Now setup the channels:
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Front motor
UAVObjectField *field = obj->getField("GroundVehicleThrottle1");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor1ChannelBox->currentText());
// Rear motor
field = obj->getField("GroundVehicleThrottle2");
Q_ASSERT(field);
field->setValue(m_aircraft->gvMotor2ChannelBox->currentText());
// // Aileron
// field = obj->getField("FixedWingRoll1");
// Q_ASSERT(field);
// field->setValue(m_aircraft->fwAileron1ChannelBox->currentText());
//
// field = obj->getField("FixedWingRoll2");
// Q_ASSERT(field);
// field->setValue(m_aircraft->fwAileron2ChannelBox->currentText());
// Front steering
field = obj->getField("GroundVehicleSteering1");
Q_ASSERT(field);
field->setValue(m_aircraft->gvSteering1ChannelBox->currentText());
// Rear steering
field = obj->getField("GroundVehicleSteering2");
Q_ASSERT(field);
field->setValue(m_aircraft->gvSteering2ChannelBox->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
int tmpVal, ti;
// Disable all output channels
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
//Disable output channel
field->setValue("Disabled");
}
// Set all mixer values to zero
foreach(QString mixer, mixerVectors) {
field = obj->getField(mixer);
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(0, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(0, ti);
}
// Steering
// Only set front steering if it is defined
tmpVal = m_aircraft->gvSteering1ChannelBox->currentIndex()-1;
// tmpVal will be -1 if steering is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(127, ti);
} // Else: we have no front steering. We're fine with it as long as we have rear steering
// Only set rear steering if it is defined
tmpVal = m_aircraft->gvSteering2ChannelBox->currentIndex()-1;
// tmpVal will be -1 if steering is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(-127, ti);
} // Else: we have no rear steering. We're fine with it as long as we have front steering
// Motor
// Only set front motor if it is defined
tmpVal = m_aircraft->gvMotor1ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
}
// Only set rear motor if it is defined
tmpVal = m_aircraft->gvMotor2ChannelBox->currentIndex()-1;
if (tmpVal > -1) {
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(127, ti);
}
obj->updated();
//Output success message
m_aircraft->gvStatusLabel->setText("Mixer generated");
return true;
}
/**
This function displays text and color formatting in order to help the user understand what channels have not yet been configured.
*/
void ConfigVehicleTypeWidget::throwGroundVehicleChannelConfigError(QString airframeType)
{
//Initialize configuration error flag
bool error=false;
//Create a red block. All combo boxes are the same size, so any one should do as a model
int size = m_aircraft->gvEngineChannelBox->style()->pixelMetric(QStyle::PM_SmallIconSize);
QPixmap pixmap(size,size);
pixmap.fill(QColor("red"));
if (airframeType == "GroundVehicleCar" ) { //Car
if(m_aircraft->gvMotor1ChannelBox->currentText() == "None" && m_aircraft->gvMotor2ChannelBox->currentText() == "None"){
pixmap.fill(QColor("green"));
m_aircraft->gvMotor1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
m_aircraft->gvMotor2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
// m_aircraft->gvMotor1Label->setText("<font color='red'>" + m_aircraft->gvMotor1Label->text() + "</font>");
// m_aircraft->gvMotor2Label->setText("<font color='red'>" + m_aircraft->gvMotor2Label->text() + "</font>");
error=true;
}
else{
m_aircraft->gvMotor1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->gvMotor2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
// QTextEdit* htmlText=new QTextEdit(m_aircraft->gvMotor1Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvMotor1Label->setText(htmlText->toPlainText());
// delete htmlText;
//
// htmlText=new QTextEdit(m_aircraft->gvMotor2Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvMotor2Label->setText(htmlText->toPlainText());
}
if (m_aircraft->gvSteering1ChannelBox->currentText() == "None" && m_aircraft->gvSteering2ChannelBox->currentText() == "None") {
m_aircraft->gvSteering1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
m_aircraft->gvSteering2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
// m_aircraft->gvStatusLabel->setText("<font color='red'>ERROR: check steering channel assignment</font>");
// m_aircraft->gvSteering1Label->setText("<font color='red'>" + m_aircraft->gvSteering1Label->text() + "</font>");
// m_aircraft->gvSteering2Label->setText("<font color='red'>" + m_aircraft->gvSteering2Label->text() + "</font>");
error=true;
}
else{
m_aircraft->gvSteering1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->gvSteering2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
// QTextEdit* htmlText=new QTextEdit(m_aircraft->gvSteering1Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvSteering1Label->setText(htmlText->toPlainText());
// delete htmlText;
//
// htmlText=new QTextEdit(m_aircraft->gvSteering2Label->text()); // HtmlText is any QString with html tags.
// m_aircraft->gvSteering2Label->setText(htmlText->toPlainText());
}
} else if (airframeType == "GroundVehicleDifferential"){ //Tank
if(m_aircraft->gvMotor1ChannelBox->currentText() == "None" || m_aircraft->gvMotor2ChannelBox->currentText() == "None"){
m_aircraft->gvMotor1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
m_aircraft->gvMotor2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->gvMotor1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->gvMotor2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
//Always reset
m_aircraft->gvSteering1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->gvSteering2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
} else if ( airframeType == "GroundVehicleMotorcycle"){ //Motorcycle
if(m_aircraft->gvMotor1ChannelBox->currentText() == "None" && m_aircraft->gvMotor2ChannelBox->currentText() == "None"){
m_aircraft->gvMotor2ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->gvMotor2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if (m_aircraft->gvSteering1ChannelBox->currentText() == "None" && m_aircraft->gvSteering2ChannelBox->currentText() == "None") {
m_aircraft->gvSteering1ChannelBox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
error=true;
}
else{
m_aircraft->gvSteering1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
//Always reset
m_aircraft->gvMotor1ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
m_aircraft->gvSteering2ChannelBox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
}
if (error){
m_aircraft->gvStatusLabel->setText(QString("<font color='red'>ERROR: Assign all necessary channels</font>"));
}
}

View File

@ -0,0 +1,1146 @@
/**
******************************************************************************
*
* @file configmultirotorwidget.cpp
* @author E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
* @{
* @brief ccpm configuration panel
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//#include "configmultirotorwidget.h"
#include "configvehicletypewidget.h"
#include "mixersettings.h"
#include <QDebug>
#include <QStringList>
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <QBrush>
#include <math.h>
#include <QMessageBox>
#include "mixersettings.h"
#include "systemsettings.h"
#include "actuatorcommand.h"
//#define Pi 3.14159265358979323846
/**
Helper function to setup the UI
*/
void ConfigVehicleTypeWidget::setupMultiRotorUI(QString frameType)
{
if (frameType == "Tri" || frameType == "Tricopter Y") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Tricopter Y"));
quad->setElementId("tri");
//Enable all necessary motor channel boxes...
for (int i=1; i <=3; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=4; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(true);
} else if (frameType == "QuadX" || frameType == "Quad X") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Quad X"));
quad->setElementId("quad-X");
//Enable all necessary motor channel boxes...
for (int i=1; i <=4; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=5; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "QuadP" || frameType == "Quad +") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Quad +"));
quad->setElementId("quad-plus");
//Enable all necessary motor channel boxes...
for (int i=1; i <=4; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=5; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(100);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "Hexa" || frameType == "Hexacopter") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter"));
quad->setElementId("quad-hexa");
//Enable all necessary motor channel boxes...
for (int i=1; i <=6; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=7; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(33);
m_aircraft->mrYawMixLevel->setValue(33);
} else if (frameType == "HexaX" || frameType == "Hexacopter X" ) {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter X"));
quad->setElementId("quad-hexa-H");
//Enable all necessary motor channel boxes...
for (int i=1; i <=6; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=7; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(33);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(33);
} else if (frameType == "HexaCoax" || frameType == "Hexacopter Y6") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter Y6"));
quad->setElementId("hexa-coax");
//Enable all necessary motor channel boxes...
for (int i=1; i <=6; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
//and grey out all unused motor channel boxes
for (int i=7; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(false);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(66);
} else if (frameType == "Octo" || frameType == "Octocopter") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octocopter"));
quad->setElementId("quad-octo");
//Enable all necessary motor channel boxes
for (int i=1; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(33);
m_aircraft->mrPitchMixLevel->setValue(33);
m_aircraft->mrYawMixLevel->setValue(25);
} else if (frameType == "OctoV" || frameType == "Octocopter V") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octocopter V"));
quad->setElementId("quad-octo-v");
//Enable all necessary motor channel boxes
for (int i=1; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(25);
m_aircraft->mrPitchMixLevel->setValue(25);
m_aircraft->mrYawMixLevel->setValue(25);
} else if (frameType == "OctoCoaxP" || frameType == "Octo Coax +") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octo Coax +"));
quad->setElementId("octo-coax-P");
//Enable all necessary motor channel boxes
for (int i=1; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(100);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "OctoCoaxX" || frameType == "Octo Coax X") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octo Coax X"));
quad->setElementId("octo-coax-X");
//Enable all necessary motor channel boxes
for (int i=1; i <=8; i++) {
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i));
combobox->setEnabled(true);
}
m_aircraft->triYawChannelBox->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(50);
}
}
/**
Helper function to update the UI widget objects
*/
QString ConfigVehicleTypeWidget::updateMultiRotorObjectsFromWidgets()
{
QString airframeType;
QList<QString> motorList;
// We can already setup the feedforward here, as it is common to all platforms
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble((double)m_aircraft->feedForwardSlider->value()/100);
field = obj->getField(QString("AccelTime"));
field->setDouble(m_aircraft->accelTime->value());
field = obj->getField(QString("DecelTime"));
field->setDouble(m_aircraft->decelTime->value());
field = obj->getField(QString("MaxAccel"));
field->setDouble(m_aircraft->maxAccelSlider->value());
// Curve is also common to all quads:
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->multiThrottleCurve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
if (m_aircraft->multirotorFrameType->currentText() == "Quad +") {
airframeType = "QuadP";
setupQuad(true);
} else if (m_aircraft->multirotorFrameType->currentText() == "Quad X") {
airframeType = "QuadX";
setupQuad(false);
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter") {
airframeType = "Hexa";
setupHexa(true);
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter X") {
airframeType = "HexaX";
setupHexa(false);
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter Y6") {
airframeType = "HexaCoax";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(6);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None" ) {
return airframeType;
}
motorList << "VTOLMotorNW" << "VTOLMotorW" << "VTOLMotorNE" << "VTOLMotorE"
<< "VTOLMotorS" << "VTOLMotorSE";
setupMotors(motorList);
// Motor 1 to 6, Y6 Layout:
// pitch roll yaw
double mixer [8][3] = {
{ 0.5, 1, -1},
{ 0.5, 1, 1},
{ 0.5, -1, -1},
{ 0.5, -1, 1},
{ -1, 0, -1},
{ -1, 0, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
setupMultiRotorMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octocopter") {
airframeType = "Octo";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(8);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None" ||
m_aircraft->multiMotorChannelBox7->currentText() == "None" ||
m_aircraft->multiMotorChannelBox8->currentText() == "None") {
return airframeType;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 0, -1},
{ 1, -1, 1},
{ 0, -1, -1},
{ -1, -1, 1},
{ -1, 0, -1},
{ -1, 1, 1},
{ 0, 1, -1},
{ 1, 1, 1}
};
setupMultiRotorMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octocopter V") {
airframeType = "OctoV";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(8);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None" ||
m_aircraft->multiMotorChannelBox7->currentText() == "None" ||
m_aircraft->multiMotorChannelBox8->currentText() == "None") {
return airframeType;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// IMPORTANT: Assumes evenly spaced engines
// pitch roll yaw
double mixer [8][3] = {
{ 0.33, -1, -1},
{ 1 , -1, 1},
{ -1 , -1, -1},
{ -0.33, -1, 1},
{ -0.33, 1, -1},
{ -1 , 1, 1},
{ 1 , 1, -1},
{ 0.33, 1, 1}
};
setupMultiRotorMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octo Coax +") {
airframeType = "OctoCoaxP";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(8);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None" ||
m_aircraft->multiMotorChannelBox7->currentText() == "None" ||
m_aircraft->multiMotorChannelBox8->currentText() == "None") {
return airframeType;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 0, -1},
{ 1, 0, 1},
{ 0, -1, -1},
{ 0, -1, 1},
{ -1, 0, -1},
{ -1, 0, 1},
{ 0, 1, -1},
{ 0, 1, 1}
};
setupMultiRotorMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octo Coax X") {
airframeType = "OctoCoaxX";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(8);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None" ||
m_aircraft->multiMotorChannelBox7->currentText() == "None" ||
m_aircraft->multiMotorChannelBox8->currentText() == "None") {
return airframeType;
}
motorList << "VTOLMotorNW" << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE"
<< "VTOLMotorSE" << "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 1, -1},
{ 1, 1, 1},
{ 1, -1, -1},
{ 1, -1, 1},
{ -1, -1, -1},
{ -1, -1, 1},
{ -1, 1, -1},
{ -1, 1, 1}
};
setupMultiRotorMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Tricopter Y") {
airframeType = "Tri";
//Show any config errors in GUI
throwMultiRotorChannelConfigError(3);
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ) {
return airframeType;
}
if (m_aircraft->triYawChannelBox->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("<font color='red'>Error: Assign a Yaw channel</font>");
return airframeType;
}
motorList << "VTOLMotorNW" << "VTOLMotorNE" << "VTOLMotorS";
setupMotors(motorList);
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
field = obj->getField("FixedWingYaw1");
field->setValue(m_aircraft->triYawChannelBox->currentText());
// Motor 1 to 6, Y6 Layout:
// pitch roll yaw
double mixer [8][3] = {
{ 0.5, 1, 0},
{ 0.5, -1, 0},
{ -1, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
setupMultiRotorMixer(mixer);
int tmpVal = m_aircraft->triYawChannelBox->currentIndex()-1;
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
field = obj->getField(mixerTypes.at(tmpVal));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(tmpVal));
resetField(field);
int ti = field->getElementNames().indexOf("Yaw");
field->setValue(127,ti);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
}
// Now reflect those settings in the "Custom" panel as well
updateCustomAirframeUI();
return airframeType;
}
/**
Helper function to refresh the UI widget values
*/
void ConfigVehicleTypeWidget::refreshMultiRotorWidgetsValues(QString frameType)
{
//////////////////////////////////////////////////////////////////
// Retrieve settings
//////////////////////////////////////////////////////////////////
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
UAVObjectField *field;
if (frameType == "QuadP") {
// Motors 1/2/3/4 are: N / E / S / W
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = field->getDouble(i)/1.27;
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = (1-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox2->currentIndex()-1;
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Roll");
val = -field->getDouble(i)/1.27;
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "QuadX") {
// Motors 1/2/3/4 are: NW / NE / SE / SW
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = field->getDouble(i)/1.27;
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = 1-field->getDouble(i)/1.27;
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = field->getDouble(i)/1.27;
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Hexa") {
// Motors 1/2/3 4/5/6 are: N / NE / SE / S / SW / NW
field = obj->getField(QString("VTOLMotorN"));
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
m_aircraft->multiMotorChannelBox5->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotorChannelBox6->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox2->currentIndex()-1;
if(tmpVal>-1)
{
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Roll");
val = floor(1-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
} else if (frameType == "HexaX") {
// Motors 1/2/3 4/5/6 are: NE / E / SE / SW / W / NW
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
m_aircraft->multiMotorChannelBox5->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotorChannelBox6->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox2->currentIndex()-1;
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Roll");
val = floor(1-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "HexaCoax") {
// Motors 1/2/3 4/5/6 are: NW/W NE/E S/SE
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
m_aircraft->multiMotorChannelBox5->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotorChannelBox6->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(2*field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Octo" || frameType == "OctoV" ||
frameType == "OctoCoaxP") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox5->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox6->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox7->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox8->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
if (frameType == "Octo") {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox2->currentIndex()-1;
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
} else if (frameType == "OctoV") {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Yaw");
double val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox2->currentIndex()-1;
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Pitch");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
} else if (frameType == "OctoCoaxP") {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
tmpVal = m_aircraft->multiMotorChannelBox3->currentIndex()-1;
field = obj->getField(mixerVectors.at(tmpVal));
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
} else if (frameType == "OctoCoaxX") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox4->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox5->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox6->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox7->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox8->setCurrentIndex(m_aircraft->multiMotorChannelBox4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Tri") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox1->setCurrentIndex(m_aircraft->multiMotorChannelBox1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox2->setCurrentIndex(m_aircraft->multiMotorChannelBox2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotorChannelBox3->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw1"));
Q_ASSERT(field);
m_aircraft->triYawChannelBox->setCurrentIndex(m_aircraft->multiMotorChannelBox3->findText(field->getValue().toString()));
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int tmpVal= m_aircraft->multiMotorChannelBox1->currentIndex()-1;
// tmpVal will be -1 if value is set to "None"
if (tmpVal > -1) {
field = obj->getField(mixerVectors.at(tmpVal));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(2*field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
}
/**
Helper function: setupQuadMotor
*/
void ConfigVehicleTypeWidget::setupQuadMotor(int channel, double pitch, double roll, double yaw)
{
qDebug()<<QString("Setup quad motor channel=%0 pitch=%1 roll=%2 yaw=%3").arg(channel).arg(pitch).arg(roll).arg(yaw);
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
UAVObjectField *field = obj->getField(mixerTypes.at(channel));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(channel));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(roll*127,ti);
qDebug()<<"Set roll="<<roll*127;
ti = field->getElementNames().indexOf("Pitch");
field->setValue(pitch*127,ti);
qDebug()<<"Set pitch="<<pitch*127;
ti = field->getElementNames().indexOf("Yaw");
field->setValue(yaw*127,ti);
qDebug()<<"Set yaw="<<yaw*127;
}
/**
Helper function: setup motors. Takes a list of channel names in input.
*/
void ConfigVehicleTypeWidget::setupMotors(QList<QString> motorList)
{
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
UAVObjectField *field;
QList<QComboBox*> mmList;
mmList << m_aircraft->multiMotorChannelBox1 << m_aircraft->multiMotorChannelBox2 << m_aircraft->multiMotorChannelBox3
<< m_aircraft->multiMotorChannelBox4 << m_aircraft->multiMotorChannelBox5 << m_aircraft->multiMotorChannelBox6
<< m_aircraft->multiMotorChannelBox7 << m_aircraft->multiMotorChannelBox8;
foreach (QString motor, motorList) {
field = obj->getField(motor);
field->setValue(mmList.takeFirst()->currentText());
}
//obj->updated(); // Save...
}
/**
Set up a Quad-X or Quad-P mixer
*/
bool ConfigVehicleTypeWidget::setupQuad(bool pLayout)
{
// Check coherence:
//Show any config errors in GUI
throwMultiRotorChannelConfigError(4);
// - Four engines have to be defined
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None") {
return false;
}
QList<QString> motorList;
if (pLayout) {
motorList << "VTOLMotorN" << "VTOLMotorE" << "VTOLMotorS"
<< "VTOLMotorW";
} else {
motorList << "VTOLMotorNW" << "VTOLMotorNE" << "VTOLMotorSE"
<< "VTOLMotorSW";
}
setupMotors(motorList);
// Now, setup the mixer:
// Motor 1 to 4, X Layout:
// pitch roll yaw
// {0.5 ,0.5 ,-0.5 //Front left motor (CW)
// {0.5 ,-0.5 ,0.5 //Front right motor(CCW)
// {-0.5 ,-0.5 ,-0.5 //rear right motor (CW)
// {-0.5 ,0.5 ,0.5 //Rear left motor (CCW)
double xMixer [8][3] = {
{ 1, 1, -1},
{ 1, -1, 1},
{-1, -1, -1},
{-1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
//
// Motor 1 to 4, P Layout:
// pitch roll yaw
// {1 ,0 ,-0.5 //Front motor (CW)
// {0 ,-1 ,0.5 //Right motor(CCW)
// {-1 ,0 ,-0.5 //Rear motor (CW)
// {0 ,1 ,0.5 //Left motor (CCW)
double pMixer [8][3] = {
{ 1, 0, -1},
{ 0, -1, 1},
{-1, 0, -1},
{ 0, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
if (pLayout) {
setupMultiRotorMixer(pMixer);
} else {
setupMultiRotorMixer(xMixer);
}
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
return true;
}
/**
Set up a Hexa-X or Hexa-P mixer
*/
bool ConfigVehicleTypeWidget::setupHexa(bool pLayout)
{
// Check coherence:
//Show any config errors in GUI
throwMultiRotorChannelConfigError(6);
// - Four engines have to be defined
if (m_aircraft->multiMotorChannelBox1->currentText() == "None" ||
m_aircraft->multiMotorChannelBox2->currentText() == "None" ||
m_aircraft->multiMotorChannelBox3->currentText() == "None" ||
m_aircraft->multiMotorChannelBox4->currentText() == "None" ||
m_aircraft->multiMotorChannelBox5->currentText() == "None" ||
m_aircraft->multiMotorChannelBox6->currentText() == "None") {
return false;
}
QList<QString> motorList;
if (pLayout) {
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorNW";
} else {
motorList << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
}
setupMotors(motorList);
// and set only the relevant channels:
// Motor 1 to 6, P Layout:
// pitch roll yaw
// 1 { 0.3 , 0 ,-0.3 // N CW
// 2 { 0.3 ,-0.5 , 0.3 // NE CCW
// 3 {-0.3 ,-0.5 ,-0.3 // SE CW
// 4 {-0.3 , 0 , 0.3 // S CCW
// 5 {-0.3 , 0.5 ,-0.3 // SW CW
// 6 { 0.3 , 0.5 , 0.3 // NW CCW
double pMixer [8][3] = {
{ 1, 0, -1},
{ 1, -1, 1},
{-1, -1, -1},
{-1, 0, 1},
{-1, 1, -1},
{ 1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
//
// Motor 1 to 6, X Layout:
// 1 [ 0.5, -0.3, -0.3 ] NE
// 2 [ 0 , -0.3, 0.3 ] E
// 3 [ -0.5, -0.3, -0.3 ] SE
// 4 [ -0.5, 0.3, 0.3 ] SW
// 5 [ 0 , 0.3, -0.3 ] W
// 6 [ 0.5, 0.3, 0.3 ] NW
double xMixer [8][3] = {
{ 1, -1, -1},
{ 0, -1, 1},
{ -1, -1, -1},
{ -1, 1, 1},
{ 0, 1, -1},
{ 1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
if (pLayout) {
setupMultiRotorMixer(pMixer);
} else {
setupMultiRotorMixer(xMixer);
}
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
return true;
}
/**
This function sets up the multirotor mixer values.
*/
bool ConfigVehicleTypeWidget::setupMultiRotorMixer(double mixerFactors[8][3])
{
qDebug()<<"Mixer factors";
qDebug()<<mixerFactors[0][0]<<" "<<mixerFactors[0][1]<<" "<<mixerFactors[0][2];
qDebug()<<mixerFactors[1][0]<<" "<<mixerFactors[1][1]<<" "<<mixerFactors[1][2];
qDebug()<<mixerFactors[2][0]<<" "<<mixerFactors[2][1]<<" "<<mixerFactors[2][2];
qDebug()<<mixerFactors[3][0]<<" "<<mixerFactors[3][1]<<" "<<mixerFactors[3][2];
qDebug()<<mixerFactors[4][0]<<" "<<mixerFactors[4][1]<<" "<<mixerFactors[4][2];
qDebug()<<mixerFactors[5][0]<<" "<<mixerFactors[5][1]<<" "<<mixerFactors[5][2];
qDebug()<<mixerFactors[6][0]<<" "<<mixerFactors[6][1]<<" "<<mixerFactors[6][2];
qDebug()<<mixerFactors[7][0]<<" "<<mixerFactors[7][1]<<" "<<mixerFactors[7][2];
UAVObjectField *field;
QList<QComboBox*> mmList;
mmList << m_aircraft->multiMotorChannelBox1 << m_aircraft->multiMotorChannelBox2 << m_aircraft->multiMotorChannelBox3
<< m_aircraft->multiMotorChannelBox4 << m_aircraft->multiMotorChannelBox5 << m_aircraft->multiMotorChannelBox6
<< m_aircraft->multiMotorChannelBox7 << m_aircraft->multiMotorChannelBox8;
UAVDataObject *obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and enable only the relevant channels:
double pFactor = (double)m_aircraft->mrPitchMixLevel->value()/100;
double rFactor = (double)m_aircraft->mrRollMixLevel->value()/100;
double yFactor = (double)m_aircraft->mrYawMixLevel->value()/100;
qDebug()<<QString("pFactor=%0 rFactor=%1 yFactor=%2").arg(pFactor).arg(rFactor).arg(yFactor);
for (int i=0 ; i<8; i++) {
if(mmList.at(i)->isEnabled())
{
int channel = mmList.at(i)->currentIndex()-1;
if (channel > -1)
setupQuadMotor(channel, mixerFactors[i][0]*pFactor,
rFactor*mixerFactors[i][1], yFactor*mixerFactors[i][2]);
}
}
// obj->updated();
return true;
}
/**
This function displays text and color formatting in order to help the user understand what channels have not yet been configured.
*/
void ConfigVehicleTypeWidget::throwMultiRotorChannelConfigError(int numMotors)
{
//Initialize configuration error flag
bool error=false;
//Iterate through all instances of multiMotorChannelBox
for (int i=0; i<numMotors; i++) {
//Fine widgets with text "multiMotorChannelBox.x", where x is an integer
QComboBox *combobox = qFindChild<QComboBox*>(this, "multiMotorChannelBox" + QString::number(i+1));
if (combobox){ //if QLabel exists
QLabel *label = qFindChild<QLabel*>(this, "MotorOutputLabel" + QString::number(i+1));
if (combobox->currentText() == "None") {
// label->setText("<font color='red'>" + label->text() + "</font>");
int size = combobox->style()->pixelMetric(QStyle::PM_SmallIconSize);
QPixmap pixmap(size,size);
pixmap.fill(QColor("red"));
combobox->setItemData(0, pixmap, Qt::DecorationRole);//Set color palettes
// combobox->setStyleSheet("QComboBox { color: red}");
error=true;
}
else {
combobox->setItemData(0, 0, Qt::DecorationRole);//Reset color palettes
// combobox->setStyleSheet("color: black;");
// QTextEdit* htmlText=new QTextEdit(label->text()); // htmlText is any QString with html tags.
// label->setText(htmlText->toPlainText());
}
}
}
if (error){
m_aircraft->mrStatusLabel->setText(QString("<font color='red'>ERROR: Assign all %1 motor channels</font>").arg(numMotors));
}
}

View File

@ -4,7 +4,9 @@ DEFINES += CONFIG_LIBRARY
QT += svg
include(config_dependencies.pri)
INCLUDEPATH += ../../libs/eigen
OTHER_FILES += Config.pluginspec
HEADERS += configplugin.h \
configgadgetconfiguration.h \
configgadgetwidget.h \
@ -14,12 +16,12 @@ HEADERS += configplugin.h \
fancytabwidget.h \
configinputwidget.h \
configoutputwidget.h \
configairframewidget.h \
configvehicletypewidget.h \
config_pro_hw_widget.h \
config_cc_hw_widget.h \
configahrswidget.h \
configccattitudewidget.h \
configccpmwidget.h \
cfg_vehicletypes/configccpmwidget.h \
configstabilizationwidget.h \
assertions.h \
calibration.h \
@ -39,12 +41,11 @@ SOURCES += configplugin.cpp \
fancytabwidget.cpp \
configinputwidget.cpp \
configoutputwidget.cpp \
configairframewidget.cpp \
configvehicletypewidget.cpp \
config_pro_hw_widget.cpp \
config_cc_hw_widget.cpp \
configahrswidget.cpp \
configccattitudewidget.cpp \
configccpmwidget.cpp \
configstabilizationwidget.cpp \
twostep.cpp \
legacy-calibration.cpp \
@ -55,6 +56,10 @@ SOURCES += configplugin.cpp \
inputchannelform.cpp \
configcamerastabilizationwidget.cpp \
configtxpidwidget.cpp \
cfg_vehicletypes/configmultirotorwidget.cpp \
cfg_vehicletypes/configgroundvehiclewidget.cpp \
cfg_vehicletypes/configfixedwingwidget.cpp \
cfg_vehicletypes/configccpmwidget.cpp \
outputchannelform.cpp
FORMS += airframe.ui \
cc_hw_settings.ui \

View File

@ -1,2221 +0,0 @@
/**
******************************************************************************
*
* @file configairframewidget.cpp
* @author E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
* @{
* @brief Airframe configuration panel
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "configairframewidget.h"
#include <QDebug>
#include <QStringList>
#include <QTimer>
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <math.h>
#include <QDesktopServices>
#include <QUrl>
#include "systemsettings.h"
#include "mixersettings.h"
#include "actuatorsettings.h"
#include <QEventLoop>
/**
Helper delegate for the custom mixer editor table.
Taken straight from Qt examples, thanks!
*/
SpinBoxDelegate::SpinBoxDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(-127);
editor->setMaximum(127);
return editor;
}
void SpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
/**********************************************************************************/
ConfigAirframeWidget::ConfigAirframeWidget(QWidget *parent) : ConfigTaskWidget(parent)
{
m_aircraft = new Ui_AircraftWidget();
m_aircraft->setupUi(this);
addApplySaveButtons(m_aircraft->saveAircraftToRAM,m_aircraft->saveAircraftToSD);
addUAVObject("SystemSettings");
addUAVObject("MixerSettings");
addUAVObject("ActuatorSettings");
ffTuningInProgress = false;
ffTuningPhase = false;
QStringList channels;
channels << "None";
for (int i = 0; i < ActuatorSettings::CHANNELADDR_NUMELEM; i++) {
mixerTypes << QString("Mixer%1Type").arg(i+1);
mixerVectors << QString("Mixer%1Vector").arg(i+1);
channels << QString("Channel%1").arg(i+1);
}
QStringList airframeTypes;
airframeTypes << "Fixed Wing" << "Multirotor" << "Helicopter" << "Custom";
m_aircraft->aircraftType->addItems(airframeTypes);
m_aircraft->aircraftType->setCurrentIndex(1);
QStringList fixedWingTypes;
fixedWingTypes << "Elevator aileron rudder" << "Elevon" << "Vtail";
m_aircraft->fixedWingType->addItems(fixedWingTypes);
QStringList multiRotorTypes;
multiRotorTypes << "Quad +" << "Quad X" << "Hexacopter" << "Octocopter" << "Hexacopter X" << "Octocopter V" << "Octo Coax +"
<< "Octo Coax X" << "Hexacopter Y6" << "Tricopter Y";
m_aircraft->multirotorFrameType->addItems(multiRotorTypes);
// Now load all the channel assignements for fixed wing
m_aircraft->fwElevator1Channel->addItems(channels);
m_aircraft->fwElevator2Channel->addItems(channels);
m_aircraft->fwEngineChannel->addItems(channels);
m_aircraft->fwRudder1Channel->addItems(channels);
m_aircraft->fwRudder2Channel->addItems(channels);
m_aircraft->fwAileron1Channel->addItems(channels);
m_aircraft->fwAileron2Channel->addItems(channels);
m_aircraft->multiMotor1->addItems(channels);
m_aircraft->multiMotor2->addItems(channels);
m_aircraft->multiMotor3->addItems(channels);
m_aircraft->multiMotor4->addItems(channels);
m_aircraft->multiMotor5->addItems(channels);
m_aircraft->multiMotor6->addItems(channels);
m_aircraft->multiMotor7->addItems(channels);
m_aircraft->multiMotor8->addItems(channels);
m_aircraft->triYawChannel->addItems(channels);
// Setup the Multirotor picture in the Quad settings interface
m_aircraft->quadShape->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_aircraft->quadShape->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QSvgRenderer *renderer = new QSvgRenderer();
renderer->load(QString(":/configgadget/images/quad-shapes.svg"));
quad = new QGraphicsSvgItem();
quad->setSharedRenderer(renderer);
quad->setElementId("quad-plus");
QGraphicsScene *scene = new QGraphicsScene(this);
scene->addItem(quad);
scene->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->setScene(scene);
// Put combo boxes in line one of the custom mixer table:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("Mixer1Type"));
QStringList list = field->getOptions();
for (int i=0;i<8;i++) {
QComboBox* qb = new QComboBox(m_aircraft->customMixerTable);
qb->addItems(list);
m_aircraft->customMixerTable->setCellWidget(0,i,qb);
}
SpinBoxDelegate *sbd = new SpinBoxDelegate();
for (int i=1;i<8; i++) {
m_aircraft->customMixerTable->setItemDelegateForRow(i, sbd);
}
connect(m_aircraft->fixedWingType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupAirframeUI(QString)));
connect(m_aircraft->multirotorFrameType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupAirframeUI(QString)));
connect(m_aircraft->aircraftType, SIGNAL(currentIndexChanged(int)), this, SLOT(switchAirframeType(int)));
connect(m_aircraft->fwThrottleReset, SIGNAL(clicked()), this, SLOT(resetFwMixer()));
connect(m_aircraft->mrThrottleCurveReset, SIGNAL(clicked()), this, SLOT(resetMrMixer()));
connect(m_aircraft->customReset1, SIGNAL(clicked()), this, SLOT(resetCt1Mixer()));
connect(m_aircraft->customReset2, SIGNAL(clicked()), this, SLOT(resetCt2Mixer()));
connect(m_aircraft->fixedWingThrottle, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateFwThrottleCurveValue(QList<double>,double)));
connect(m_aircraft->multiThrottleCurve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateMrThrottleCurveValue(QList<double>,double)));
connect(m_aircraft->customThrottle1Curve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateCustomThrottle1CurveValue(QList<double>,double)));
connect(m_aircraft->customThrottle2Curve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateCustomThrottle2CurveValue(QList<double>,double)));
// connect(m_aircraft->fwAileron1Channel, SIGNAL(currentIndexChanged(int)), this, SLOT(toggleAileron2(int)));
// connect(m_aircraft->fwElevator1Channel, SIGNAL(currentIndexChanged(int)), this, SLOT(toggleElevator2(int)));
// Now connect the three feed forward test checkboxes
connect(m_aircraft->ffTestBox1, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
connect(m_aircraft->ffTestBox2, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
connect(m_aircraft->ffTestBox3, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
enableControls(false);
refreshWidgetsValues();
// Connect the help button
connect(m_aircraft->airframeHelp, SIGNAL(clicked()), this, SLOT(openHelp()));
addToDirtyMonitor();
}
ConfigAirframeWidget::~ConfigAirframeWidget()
{
// Do nothing
}
/**
Slot for switching the airframe type. We do it explicitely
rather than a signal in the UI, because we want to force a fitInView of the quad shapes.
This is because this method (fitinview) only works when the widget is shown.
*/
void ConfigAirframeWidget::switchAirframeType(int index){
m_aircraft->airframesWidget->setCurrentIndex(index);
m_aircraft->quadShape->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
if (m_aircraft->aircraftType->findText("Custom")) {
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
}
void ConfigAirframeWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
// Thit fitInView method should only be called now, once the
// widget is shown, otherwise it cannot compute its values and
// the result is usually a ahrsbargraph that is way too small.
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
void ConfigAirframeWidget::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
// Make the custom table columns autostretch:
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
void ConfigAirframeWidget::toggleAileron2(int index)
{
if (index) {
m_aircraft->fwAileron2Channel->setEnabled(true);
m_aircraft->fwAileron2Label->setEnabled(true);
} else {
m_aircraft->fwAileron2Channel->setEnabled(false);
m_aircraft->fwAileron2Label->setEnabled(false);
}
}
void ConfigAirframeWidget::toggleElevator2(int index)
{
if (index) {
m_aircraft->fwElevator2Channel->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
} else {
m_aircraft->fwElevator2Channel->setEnabled(false);
m_aircraft->fwElevator2Label->setEnabled(false);
}
}
void ConfigAirframeWidget::toggleRudder2(int index)
{
if (index) {
m_aircraft->fwRudder2Channel->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
} else {
m_aircraft->fwRudder2Channel->setEnabled(false);
m_aircraft->fwRudder2Label->setEnabled(false);
}
}
/////////////////////////////////////////////////////////
/// Feed Forward Testing
/////////////////////////////////////////////////////////
/**
Enables and runs feed forward testing
*/
void ConfigAirframeWidget::enableFFTest()
{
// Role:
// - Check if all three checkboxes are checked
// - Every other timer event: toggle engine from 45% to 55%
// - Every other time event: send FF settings to flight FW
if (m_aircraft->ffTestBox1->isChecked() &&
m_aircraft->ffTestBox2->isChecked() &&
m_aircraft->ffTestBox3->isChecked()) {
if (!ffTuningInProgress)
{
// Initiate tuning:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
UAVObject::Metadata mdata = obj->getMetadata();
accInitialData = mdata;
mdata.flightAccess = UAVObject::ACCESS_READONLY;
obj->setMetadata(mdata);
}
// Depending on phase, either move actuator or send FF settings:
if (ffTuningPhase) {
// Send FF settings to the board
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble((double)m_aircraft->feedForwardSlider->value()/100);
field = obj->getField(QString("AccelTime"));
field->setDouble(m_aircraft->accelTime->value());
field = obj->getField(QString("DecelTime"));
field->setDouble(m_aircraft->decelTime->value());
field = obj->getField(QString("MaxAccel"));
field->setDouble(m_aircraft->maxAccelSlider->value());
obj->updated();
} else {
// Toggle motor state
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
double value = obj->getField("Throttle")->getDouble();
double target = (value < 0.5) ? 0.55 : 0.45;
obj->getField("Throttle")->setValue(target);
obj->updated();
}
ffTuningPhase = !ffTuningPhase;
ffTuningInProgress = true;
QTimer::singleShot(1000, this, SLOT(enableFFTest()));
} else {
// - If no: disarm timer, restore actuatorcommand metadata
// Disarm!
if (ffTuningInProgress) {
ffTuningInProgress = false;
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
UAVObject::Metadata mdata = obj->getMetadata();
mdata = accInitialData; // Restore metadata
obj->setMetadata(mdata);
}
}
}
/**
Resets Fixed wing throttle mixer
*/
void ConfigAirframeWidget::resetFwMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->fixedWingThrottle, field->getNumElements(),1);
}
/**
Resets Multirotor throttle mixer
*/
void ConfigAirframeWidget::resetMrMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->multiThrottleCurve, field->getNumElements(),0.9);
}
/**
Resets Custom throttle 1 mixer
*/
void ConfigAirframeWidget::resetCt1Mixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->customThrottle1Curve, field->getNumElements(),1);
}
/**
Resets Custom throttle 2 mixer
*/
void ConfigAirframeWidget::resetCt2Mixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve2"));
resetMixer(m_aircraft->customThrottle2Curve, field->getNumElements(),1);
}
/**
Resets a mixer curve
*/
void ConfigAirframeWidget::resetMixer(MixerCurveWidget *mixer, int numElements, double maxvalue)
{
// Setup all Throttle1 curves for all types of airframes
mixer->initLinearCurve((quint32)numElements,maxvalue);
}
/**
Updates the currently moved throttle curve item value
*/
void ConfigAirframeWidget::updateFwThrottleCurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->fwThrottleCurveItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved throttle curve item value
*/
void ConfigAirframeWidget::updateMrThrottleCurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->mrThrottleCurveItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved throttle curve item value (Custom throttle 1)
*/
void ConfigAirframeWidget::updateCustomThrottle1CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->customThrottleCurve1Value->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved throttle curve item value (Custom throttle 2)
*/
void ConfigAirframeWidget::updateCustomThrottle2CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->customThrottleCurve2Value->setText(QString().sprintf("Val: %.2f",value));
}
/**************************
* Aircraft settings
**************************/
/**
Refreshes the current value of the SystemSettings which holds the aircraft type
*/
void ConfigAirframeWidget::refreshWidgetsValues()
{
if(!allObjectsUpdated())
return;
bool dirty=isDirty();
// Get the Airframe type from the system settings:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("SystemSettings")));
Q_ASSERT(obj);
UAVObjectField *field = obj->getField(QString("AirframeType"));
Q_ASSERT(field);
// At this stage, we will need to have some hardcoded settings in this code, this
// is not ideal, but here you go.
QString frameType = field->getValue().toString();
setupAirframeUI(frameType);
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
field = obj->getField(QString("ThrottleCurve1"));
Q_ASSERT(field);
QList<double> curveValues;
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->multiThrottleCurve->initLinearCurve(field->getNumElements(),(double)1);
m_aircraft->fixedWingThrottle->initLinearCurve(field->getNumElements(),(double)1);
}
else {
double temp=0;
double value;
for (unsigned int i=0; i < field->getNumElements(); i++) {
value=field->getValue(i).toDouble();
temp+=value;
curveValues.append(value);
}
if(temp==0)
{
m_aircraft->multiThrottleCurve->initLinearCurve(field->getNumElements(),0.9);;
m_aircraft->fixedWingThrottle->initLinearCurve(field->getNumElements(),(double)1);
}
else
{
m_aircraft->multiThrottleCurve->initCurve(curveValues);
m_aircraft->fixedWingThrottle->initCurve(curveValues);
}
}
// Setup all Throttle1 curves for all types of airframes
// Load the Settings for fixed wing frames:
if (frameType.startsWith("FixedWing")) {
// Then retrieve how channels are setup
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
field = obj->getField(QString("FixedWingThrottle"));
Q_ASSERT(field);
m_aircraft->fwEngineChannel->setCurrentIndex(m_aircraft->fwEngineChannel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll1"));
Q_ASSERT(field);
m_aircraft->fwAileron1Channel->setCurrentIndex(m_aircraft->fwAileron1Channel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingRoll2"));
Q_ASSERT(field);
m_aircraft->fwAileron2Channel->setCurrentIndex(m_aircraft->fwAileron2Channel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingPitch1"));
Q_ASSERT(field);
m_aircraft->fwElevator1Channel->setCurrentIndex(m_aircraft->fwElevator1Channel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingPitch2"));
Q_ASSERT(field);
m_aircraft->fwElevator2Channel->setCurrentIndex(m_aircraft->fwElevator2Channel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw1"));
Q_ASSERT(field);
m_aircraft->fwRudder1Channel->setCurrentIndex(m_aircraft->fwRudder1Channel->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw2"));
Q_ASSERT(field);
m_aircraft->fwRudder2Channel->setCurrentIndex(m_aircraft->fwRudder2Channel->findText(field->getValue().toString()));
if (frameType == "FixedWingElevon") {
// If the airframe is elevon, restore the slider setting
// Find the channel number for Elevon1 (FixedWingRoll1)
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int chMixerNumber = m_aircraft->fwAileron1Channel->currentIndex()-1;
if (chMixerNumber >= 0) { // If for some reason the actuators were incoherent, we might fail here, hence the check.
field = obj->getField(mixerVectors.at(chMixerNumber));
int ti = field->getElementNames().indexOf("Roll");
m_aircraft->elevonSlider1->setValue(field->getDouble(ti)*100);
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->elevonSlider2->setValue(field->getDouble(ti)*100);
}
}
if (frameType == "FixedWingVtail") {
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int chMixerNumber = m_aircraft->fwElevator1Channel->currentIndex()-1;
if (chMixerNumber >=0) {
field = obj->getField(mixerVectors.at(chMixerNumber));
int ti = field->getElementNames().indexOf("Yaw");
m_aircraft->elevonSlider1->setValue(field->getDouble(ti)*100);
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->elevonSlider2->setValue(field->getDouble(ti)*100);
}
}
} else if (frameType == "QuadX" || frameType == "QuadP" ||
frameType == "Hexa" || frameType == "Octo" ||
frameType == "HexaCoax" || frameType == "OctoV" ||
frameType == "HexaX" || frameType == "OctoCoaxP" ||
frameType == "OctoCoaxX" || frameType == "Tri") {
//////////////////////////////////////////////////////////////////
// Retrieve Multirotor settings
//////////////////////////////////////////////////////////////////
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
if (frameType == "QuadP") {
// Motors 1/2/3/4 are: N / E / S / W
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = field->getDouble(i)/1.27;
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = (1-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
eng = m_aircraft->multiMotor2->currentIndex()-1;
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Roll");
val = -field->getDouble(i)/1.27;
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "QuadX") {
// Motors 1/2/3/4 are: NW / NE / SE / SW
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = field->getDouble(i)/1.27;
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = 1-field->getDouble(i)/1.27;
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = field->getDouble(i)/1.27;
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Hexa") {
// Motors 1/2/3 4/5/6 are: N / NE / SE / S / SW / NW
field = obj->getField(QString("VTOLMotorN"));
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
m_aircraft->multiMotor5->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotor6->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
eng = m_aircraft->multiMotor2->currentIndex()-1;
if(eng>-1)
{
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Roll");
val = floor(1-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
} else if (frameType == "HexaX") {
// Motors 1/2/3 4/5/6 are: NE / E / SE / SW / W / NW
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
m_aircraft->multiMotor5->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotor6->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
eng = m_aircraft->multiMotor2->currentIndex()-1;
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Roll");
val = floor(1-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "HexaCoax") {
// Motors 1/2/3 4/5/6 are: NW/W NE/E S/SE
field = obj->getField(QString("VTOLMotorNW"));
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
m_aircraft->multiMotor5->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
m_aircraft->multiMotor6->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(2*field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Octo" || frameType == "OctoV" ||
frameType == "OctoCoaxP") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotor5->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotor6->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotor7->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotor8->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
if (frameType == "Octo") {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
eng = m_aircraft->multiMotor2->currentIndex()-1;
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
} else if (frameType == "OctoV") {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Yaw");
double val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
eng = m_aircraft->multiMotor2->currentIndex()-1;
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Pitch");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
} else if (frameType == "OctoCoaxP") {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
eng = m_aircraft->multiMotor3->currentIndex()-1;
field = obj->getField(mixerVectors.at(eng));
i = field->getElementNames().indexOf("Roll");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
} else if (frameType == "OctoCoaxX") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorN"));
Q_ASSERT(field);
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorE"));
Q_ASSERT(field);
m_aircraft->multiMotor4->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSE"));
Q_ASSERT(field);
m_aircraft->multiMotor5->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotor6->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorSW"));
Q_ASSERT(field);
m_aircraft->multiMotor7->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorW"));
Q_ASSERT(field);
m_aircraft->multiMotor8->setCurrentIndex(m_aircraft->multiMotor4->findText(field->getValue().toString()));
// Now, read the 1st mixer R/P/Y levels and initialize the mix sliders.
// This assumes that all vectors are identical - if not, the user should use the
// "custom" setting.
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Yaw");
val = floor(-field->getDouble(i)/1.27);
m_aircraft->mrYawMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
} else if (frameType == "Tri") {
// Motors 1 to 8 are N / NE / E / etc
field = obj->getField(QString("VTOLMotorNW"));
Q_ASSERT(field);
m_aircraft->multiMotor1->setCurrentIndex(m_aircraft->multiMotor1->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorNE"));
Q_ASSERT(field);
m_aircraft->multiMotor2->setCurrentIndex(m_aircraft->multiMotor2->findText(field->getValue().toString()));
field = obj->getField(QString("VTOLMotorS"));
Q_ASSERT(field);
m_aircraft->multiMotor3->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
field = obj->getField(QString("FixedWingYaw1"));
Q_ASSERT(field);
m_aircraft->triYawChannel->setCurrentIndex(m_aircraft->multiMotor3->findText(field->getValue().toString()));
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
int eng= m_aircraft->multiMotor1->currentIndex()-1;
// eng will be -1 if value is set to "None"
if (eng > -1) {
field = obj->getField(mixerVectors.at(eng));
int i = field->getElementNames().indexOf("Pitch");
double val = floor(2*field->getDouble(i)/1.27);
m_aircraft->mrPitchMixLevel->setValue(val);
i = field->getElementNames().indexOf("Roll");
val = floor(field->getDouble(i)/1.27);
m_aircraft->mrRollMixLevel->setValue(val);
}
}
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// Now, retrieve the Feedforward values:
field = obj->getField(QString("FeedForward"));
Q_ASSERT(field);
m_aircraft->feedForwardSlider->setValue(field->getDouble()*100);
field = obj->getField(QString("AccelTime"));
Q_ASSERT(field);
m_aircraft->accelTime->setValue(field->getDouble());
field = obj->getField(QString("DecelTime"));
Q_ASSERT(field);
m_aircraft->decelTime->setValue(field->getDouble());
field = obj->getField(QString("MaxAccel"));
Q_ASSERT(field);
m_aircraft->maxAccelSlider->setValue(field->getDouble());
} else if (frameType == "HeliCP") {
m_aircraft->widget_3->requestccpmUpdate();
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Helicopter"));//"Helicopter"
} else if (frameType == "Custom") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Custom"));
}
updateCustomAirframeUI();
setDirty(dirty);
}
/**
\brief Sets up the mixer depending on Airframe type. Accepts either system settings or
combo box entry from airframe type, as those do not overlap.
*/
void ConfigAirframeWidget::setupAirframeUI(QString frameType)
{
bool dirty=isDirty();
if (frameType == "FixedWing" || frameType == "Elevator aileron rudder") {
// Setup the UI
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Elevator aileron rudder"));
m_aircraft->fwRudder1Channel->setEnabled(true);
m_aircraft->fwRudder1Label->setEnabled(true);
m_aircraft->fwRudder2Channel->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
m_aircraft->fwElevator1Channel->setEnabled(true);
m_aircraft->fwElevator1Label->setEnabled(true);
m_aircraft->fwElevator2Channel->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
m_aircraft->fwAileron1Channel->setEnabled(true);
m_aircraft->fwAileron1Label->setEnabled(true);
m_aircraft->fwAileron2Channel->setEnabled(true);
m_aircraft->fwAileron2Label->setEnabled(true);
m_aircraft->fwAileron1Label->setText("Aileron 1");
m_aircraft->fwAileron2Label->setText("Aileron 2");
m_aircraft->fwElevator1Label->setText("Elevator 1");
m_aircraft->fwElevator2Label->setText("Elevator 2");
m_aircraft->elevonMixBox->setHidden(true);
} else if (frameType == "FixedWingElevon" || frameType == "Elevon") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Elevon"));
m_aircraft->fwAileron1Label->setText("Elevon 1");
m_aircraft->fwAileron2Label->setText("Elevon 2");
m_aircraft->fwElevator1Channel->setEnabled(false);
m_aircraft->fwElevator1Label->setEnabled(false);
m_aircraft->fwElevator2Channel->setEnabled(false);
m_aircraft->fwElevator2Label->setEnabled(false);
m_aircraft->fwRudder1Channel->setEnabled(true);
m_aircraft->fwRudder1Label->setEnabled(true);
m_aircraft->fwRudder2Channel->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
m_aircraft->fwElevator1Label->setText("Elevator 1");
m_aircraft->fwElevator2Label->setText("Elevator 2");
m_aircraft->elevonMixBox->setHidden(false);
m_aircraft->elevonLabel1->setText("Roll");
m_aircraft->elevonLabel2->setText("Pitch");
} else if (frameType == "FixedWingVtail" || frameType == "Vtail") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Fixed Wing"));
m_aircraft->fixedWingType->setCurrentIndex(m_aircraft->fixedWingType->findText("Vtail"));
m_aircraft->fwRudder1Channel->setEnabled(false);
m_aircraft->fwRudder1Label->setEnabled(false);
m_aircraft->fwRudder2Channel->setEnabled(false);
m_aircraft->fwRudder2Label->setEnabled(false);
m_aircraft->fwElevator1Channel->setEnabled(true);
m_aircraft->fwElevator1Label->setEnabled(true);
m_aircraft->fwElevator1Label->setText("Vtail 1");
m_aircraft->fwElevator2Label->setText("Vtail 2");
m_aircraft->elevonMixBox->setHidden(false);
m_aircraft->fwElevator2Channel->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
m_aircraft->fwAileron1Label->setText("Aileron 1");
m_aircraft->fwAileron2Label->setText("Aileron 2");
m_aircraft->elevonLabel1->setText("Rudder");
m_aircraft->elevonLabel2->setText("Pitch");
} else if (frameType == "QuadX" || frameType == "Quad X") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Quad X"));
quad->setElementId("quad-X");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(false);
m_aircraft->multiMotor6->setEnabled(false);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "QuadP" || frameType == "Quad +") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Quad +"));
quad->setElementId("quad-plus");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(false);
m_aircraft->multiMotor6->setEnabled(false);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(100);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "Hexa" || frameType == "Hexacopter") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter"));
quad->setElementId("quad-hexa");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(33);
m_aircraft->mrYawMixLevel->setValue(33);
} else if (frameType == "Octo" || frameType == "Octocopter") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octocopter"));
quad->setElementId("quad-octo");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(true);
m_aircraft->multiMotor8->setEnabled(true);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(33);
m_aircraft->mrPitchMixLevel->setValue(33);
m_aircraft->mrYawMixLevel->setValue(25);
} else if (frameType == "HexaX" || frameType == "Hexacopter X" ) {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter X"));
quad->setElementId("quad-hexa-H");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(33);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(33);
} else if (frameType == "OctoV" || frameType == "Octocopter V") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octocopter V"));
quad->setElementId("quad-octo-v");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(true);
m_aircraft->multiMotor8->setEnabled(true);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(25);
m_aircraft->mrPitchMixLevel->setValue(25);
m_aircraft->mrYawMixLevel->setValue(25);
} else if (frameType == "OctoCoaxP" || frameType == "Octo Coax +") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octo Coax +"));
quad->setElementId("octo-coax-P");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(true);
m_aircraft->multiMotor8->setEnabled(true);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(100);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "OctoCoaxX" || frameType == "Octo Coax X") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Octo Coax X"));
quad->setElementId("octo-coax-X");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(true);
m_aircraft->multiMotor8->setEnabled(true);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(50);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(50);
} else if (frameType == "HexaCoax" || frameType == "Hexacopter Y6") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Hexacopter Y6"));
quad->setElementId("hexa-coax");
m_aircraft->multiMotor4->setEnabled(true);
m_aircraft->multiMotor5->setEnabled(true);
m_aircraft->multiMotor6->setEnabled(true);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(false);
m_aircraft->mrRollMixLevel->setValue(100);
m_aircraft->mrPitchMixLevel->setValue(50);
m_aircraft->mrYawMixLevel->setValue(66);
} else if (frameType == "Tri" || frameType == "Tricopter Y") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Multirotor"));
m_aircraft->multirotorFrameType->setCurrentIndex(m_aircraft->multirotorFrameType->findText("Tricopter Y"));
quad->setElementId("tri");
m_aircraft->multiMotor4->setEnabled(false);
m_aircraft->multiMotor5->setEnabled(false);
m_aircraft->multiMotor6->setEnabled(false);
m_aircraft->multiMotor7->setEnabled(false);
m_aircraft->multiMotor8->setEnabled(false);
m_aircraft->triYawChannel->setEnabled(true);
}
m_aircraft->quadShape->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
setDirty(dirty);
}
/**
Reset the contents of a field
*/
void ConfigAirframeWidget::resetField(UAVObjectField * field)
{
for (unsigned int i=0;i<field->getNumElements();i++) {
field->setValue(0,i);
}
}
/**
Reset actuator values
*/
void ConfigAirframeWidget::resetActuators()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
QList<UAVObjectField*> fieldList = obj->getFields();
// Reset all assignements first:
foreach (UAVObjectField* field, fieldList) {
// NOTE: we assume that all options in ActuatorSettings are a channel assignement
// except for the options called "ChannelXXX"
if (field->getUnits().contains("channel")) {
field->setValue(field->getOptions().last());
}
}
}
/**
Setup Elevator/Aileron/Rudder airframe.
If both Aileron channels are set to 'None' (EasyStar), do Pitch/Rudder mixing
Returns False if impossible to create the mixer.
*/
bool ConfigAirframeWidget::setupFrameFixedWing()
{
// Check coherence:
// - At least Pitch and either Roll or Yaw
if (m_aircraft->fwEngineChannel->currentText() == "None" ||
m_aircraft->fwElevator1Channel->currentText() == "None" ||
((m_aircraft->fwAileron1Channel->currentText() == "None") &&
(m_aircraft->fwRudder1Channel->currentText() == "None"))) {
// TODO: explain the problem in the UI
m_aircraft->fwStatusLabel->setText("ERROR: check channel assignment");
return false;
}
// Now setup the channels:
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevator
UAVObjectField *field = obj->getField("FixedWingPitch1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator1Channel->currentText());
field = obj->getField("FixedWingPitch2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator2Channel->currentText());
// Aileron
field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1Channel->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2Channel->currentText());
// Rudder
field = obj->getField("FixedWingYaw1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder1Channel->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannel->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int eng = m_aircraft->fwEngineChannel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(eng));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
// Rudder
eng = m_aircraft->fwRudder1Channel->currentIndex()-1;
// eng will be -1 if rudder is set to "None"
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(127, ti);
} // Else: we have no rudder, only ailerons, we're fine with it.
// Ailerons
eng = m_aircraft->fwAileron1Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127, ti);
// Only set Aileron 2 if Aileron 1 is defined
eng = m_aircraft->fwAileron2Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127, ti);
}
} // Else we have no ailerons. Our consistency check guarantees we have
// rudder in this case, so we're fine with it too.
// Elevator
eng = m_aircraft->fwElevator1Channel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(127, ti);
// Only set Elevator 2 if it is defined
eng = m_aircraft->fwElevator2Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(127, ti);
}
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup Elevon
*/
bool ConfigAirframeWidget::setupFrameElevon()
{
// Check coherence:
// - At least Aileron1 and Aileron 2, and engine
if (m_aircraft->fwEngineChannel->currentText() == "None" ||
m_aircraft->fwAileron1Channel->currentText() == "None" ||
m_aircraft->fwAileron2Channel->currentText() == "None") {
// TODO: explain the problem in the UI
m_aircraft->fwStatusLabel->setText("ERROR: check channel assignment");
return false;
}
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevons
UAVObjectField *field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1Channel->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2Channel->currentText());
// Rudder 1 (can be None)
field = obj->getField("FixedWingYaw1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder1Channel->currentText());
// Rudder 2 (can be None)
field = obj->getField("FixedWingYaw2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwRudder2Channel->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannel->currentText());
obj->updated();
// Save the curve:
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int eng = m_aircraft->fwEngineChannel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(eng));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
// Rudder 1
eng = m_aircraft->fwRudder1Channel->currentIndex()-1;
// eng will be -1 if rudder 1 is set to "None"
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(127, ti);
} // Else: we have no rudder, only elevons, we're fine with it.
// Rudder 2
eng = m_aircraft->fwRudder2Channel->currentIndex()-1;
// eng will be -1 if rudder 2 is set to "None"
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(-127, ti);
} // Else: we have no rudder, only elevons, we're fine with it.
eng = m_aircraft->fwAileron1Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue((double)m_aircraft->elevonSlider1->value()*1.27,ti);
}
eng = m_aircraft->fwAileron2Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(-(double)m_aircraft->elevonSlider1->value()*1.27,ti);
}
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
Setup VTail
*/
bool ConfigAirframeWidget::setupFrameVtail()
{
// Check coherence:
// - At least Pitch1 and Pitch2, and engine
if (m_aircraft->fwEngineChannel->currentText() == "None" ||
m_aircraft->fwElevator1Channel->currentText() == "None" ||
m_aircraft->fwElevator2Channel->currentText() == "None") {
// TODO: explain the problem in the UI
m_aircraft->fwStatusLabel->setText("WARNING: check channel assignment");
return false;
}
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
// Elevons
UAVObjectField *field = obj->getField("FixedWingPitch1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator1Channel->currentText());
field = obj->getField("FixedWingPitch2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwElevator2Channel->currentText());
field = obj->getField("FixedWingRoll1");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron1Channel->currentText());
field = obj->getField("FixedWingRoll2");
Q_ASSERT(field);
field->setValue(m_aircraft->fwAileron2Channel->currentText());
// Throttle
field = obj->getField("FixedWingThrottle");
Q_ASSERT(field);
field->setValue(m_aircraft->fwEngineChannel->currentText());
obj->updated();
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
// ... and compute the matrix:
// In order to make code a bit nicer, we assume:
// - Channel dropdowns start with 'None', then 0 to 7
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and set only the relevant channels:
// Engine
int eng = m_aircraft->fwEngineChannel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(eng));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
eng = m_aircraft->fwAileron1Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(127,ti);
}
eng = m_aircraft->fwAileron2Channel->currentIndex()-1;
if (eng > -1) {
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Roll");
field->setValue(-127,ti);
}
// Now compute the VTail
eng = m_aircraft->fwElevator1Channel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue((double)m_aircraft->elevonSlider1->value()*1.27,ti);
eng = m_aircraft->fwElevator2Channel->currentIndex()-1;
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
ti = field->getElementNames().indexOf("Pitch");
field->setValue((double)m_aircraft->elevonSlider2->value()*1.27, ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(-(double)m_aircraft->elevonSlider1->value()*1.27,ti);
obj->updated();
m_aircraft->fwStatusLabel->setText("Mixer generated");
return true;
}
/**
Set up a complete mixer, taking a table of factors. The factors
shoudl mainly be +/- 1 factors, since they will be weighted by the
value of the Pitch/Roll/Yaw sliders.
Example:
double xMixer [8][3] = {
P R Y
{ 1, 1, -1}, Motor 1
{ 1, -1, 1}, Motor 2
{-1, -1, -1}, Motor 3
{-1, 1, 1}, ...
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
*/
bool ConfigAirframeWidget::setupMixer(double mixerFactors[8][3])
{
qDebug()<<"Mixer factors";
qDebug()<<mixerFactors[0][0]<<" "<<mixerFactors[0][1]<<" "<<mixerFactors[0][2];
qDebug()<<mixerFactors[1][0]<<" "<<mixerFactors[1][1]<<" "<<mixerFactors[1][2];
qDebug()<<mixerFactors[2][0]<<" "<<mixerFactors[2][1]<<" "<<mixerFactors[2][2];
qDebug()<<mixerFactors[3][0]<<" "<<mixerFactors[3][1]<<" "<<mixerFactors[3][2];
qDebug()<<mixerFactors[4][0]<<" "<<mixerFactors[4][1]<<" "<<mixerFactors[4][2];
qDebug()<<mixerFactors[5][0]<<" "<<mixerFactors[5][1]<<" "<<mixerFactors[5][2];
qDebug()<<mixerFactors[6][0]<<" "<<mixerFactors[6][1]<<" "<<mixerFactors[6][2];
qDebug()<<mixerFactors[7][0]<<" "<<mixerFactors[7][1]<<" "<<mixerFactors[7][2];
UAVObjectField *field;
QList<QComboBox*> mmList;
mmList << m_aircraft->multiMotor1 << m_aircraft->multiMotor2 << m_aircraft->multiMotor3
<< m_aircraft->multiMotor4 << m_aircraft->multiMotor5 << m_aircraft->multiMotor6
<< m_aircraft->multiMotor7 << m_aircraft->multiMotor8;
UAVDataObject *obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// 1. Assign the servo/motor/none for each channel
// Disable all
foreach(QString mixer, mixerTypes) {
field = obj->getField(mixer);
Q_ASSERT(field);
field->setValue("Disabled");
}
// and enable only the relevant channels:
double pFactor = (double)m_aircraft->mrPitchMixLevel->value()/100;
double rFactor = (double)m_aircraft->mrRollMixLevel->value()/100;
double yFactor = (double)m_aircraft->mrYawMixLevel->value()/100;
qDebug()<<QString("pFactor=%0 rFactor=%1 yFactor=%2").arg(pFactor).arg(rFactor).arg(yFactor);
for (int i=0 ; i<8; i++) {
if(mmList.at(i)->isEnabled())
{
int channel = mmList.at(i)->currentIndex()-1;
if (channel > -1)
setupQuadMotor(channel, mixerFactors[i][0]*pFactor,
rFactor*mixerFactors[i][1], yFactor*mixerFactors[i][2]);
}
}
// obj->updated();
return true;
}
/**
Help function: setupQuadMotor
*/
void ConfigAirframeWidget::setupQuadMotor(int channel, double pitch, double roll, double yaw)
{
qDebug()<<QString("Setup quad motor channel=%0 pitch=%1 roll=%2 yaw=%3").arg(channel).arg(pitch).arg(roll).arg(yaw);
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
UAVObjectField *field = obj->getField(mixerTypes.at(channel));
field->setValue("Motor");
field = obj->getField(mixerVectors.at(channel));
// First of all reset the vector
resetField(field);
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(127, ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(roll*127,ti);
qDebug()<<"Set roll="<<roll*127;
ti = field->getElementNames().indexOf("Pitch");
field->setValue(pitch*127,ti);
qDebug()<<"Set pitch="<<pitch*127;
ti = field->getElementNames().indexOf("Yaw");
field->setValue(yaw*127,ti);
qDebug()<<"Set yaw="<<yaw*127;
}
/**
Helper function: setup motors. Takes a list of channel names in input.
*/
void ConfigAirframeWidget::setupMotors(QList<QString> motorList)
{
resetActuators();
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
UAVObjectField *field;
QList<QComboBox*> mmList;
mmList << m_aircraft->multiMotor1 << m_aircraft->multiMotor2 << m_aircraft->multiMotor3
<< m_aircraft->multiMotor4 << m_aircraft->multiMotor5 << m_aircraft->multiMotor6
<< m_aircraft->multiMotor7 << m_aircraft->multiMotor8;
foreach (QString motor, motorList) {
field = obj->getField(motor);
field->setValue(mmList.takeFirst()->currentText());
}
//obj->updated(); // Save...
}
/**
Set up a Quad-X or Quad-P
*/
bool ConfigAirframeWidget::setupQuad(bool pLayout)
{
// Check coherence:
// - Four engines have to be defined
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 4 motor channels");
return false;
}
QList<QString> motorList;
if (pLayout) {
motorList << "VTOLMotorN" << "VTOLMotorE" << "VTOLMotorS"
<< "VTOLMotorW";
} else {
motorList << "VTOLMotorNW" << "VTOLMotorNE" << "VTOLMotorSE"
<< "VTOLMotorSW";
}
setupMotors(motorList);
// Now, setup the mixer:
// Motor 1 to 4, X Layout:
// pitch roll yaw
// {0.5 ,0.5 ,-0.5 //Front left motor (CW)
// {0.5 ,-0.5 ,0.5 //Front right motor(CCW)
// {-0.5 ,-0.5 ,-0.5 //rear right motor (CW)
// {-0.5 ,0.5 ,0.5 //Rear left motor (CCW)
double xMixer [8][3] = {
{ 1, 1, -1},
{ 1, -1, 1},
{-1, -1, -1},
{-1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
//
// Motor 1 to 4, P Layout:
// pitch roll yaw
// {1 ,0 ,-0.5 //Front motor (CW)
// {0 ,-1 ,0.5 //Right motor(CCW)
// {-1 ,0 ,-0.5 //Rear motor (CW)
// {0 ,1 ,0.5 //Left motor (CCW)
double pMixer [8][3] = {
{ 1, 0, -1},
{ 0, -1, 1},
{-1, 0, -1},
{ 0, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
if (pLayout) {
setupMixer(pMixer);
} else {
setupMixer(xMixer);
}
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
return true;
}
/**
Set up a Hexa-X or Hexa-P
*/
bool ConfigAirframeWidget::setupHexa(bool pLayout)
{
// Check coherence:
// - Four engines have to be defined
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 6 motor channels");
return false;
}
QList<QString> motorList;
if (pLayout) {
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorNW";
} else {
motorList << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
}
setupMotors(motorList);
// and set only the relevant channels:
// Motor 1 to 6, P Layout:
// pitch roll yaw
// 1 { 0.3 , 0 ,-0.3 // N CW
// 2 { 0.3 ,-0.5 , 0.3 // NE CCW
// 3 {-0.3 ,-0.5 ,-0.3 // SE CW
// 4 {-0.3 , 0 , 0.3 // S CCW
// 5 {-0.3 , 0.5 ,-0.3 // SW CW
// 6 { 0.3 , 0.5 , 0.3 // NW CCW
double pMixer [8][3] = {
{ 1, 0, -1},
{ 1, -1, 1},
{-1, -1, -1},
{-1, 0, 1},
{-1, 1, -1},
{ 1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
//
// Motor 1 to 6, X Layout:
// 1 [ 0.5, -0.3, -0.3 ] NE
// 2 [ 0 , -0.3, 0.3 ] E
// 3 [ -0.5, -0.3, -0.3 ] SE
// 4 [ -0.5, 0.3, 0.3 ] SW
// 5 [ 0 , 0.3, -0.3 ] W
// 6 [ 0.5, 0.3, 0.3 ] NW
double xMixer [8][3] = {
{ 1, -1, -1},
{ 0, -1, 1},
{ -1, -1, -1},
{ -1, 1, 1},
{ 0, 1, -1},
{ 1, 1, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
if (pLayout) {
setupMixer(pMixer);
} else {
setupMixer(xMixer);
}
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
return true;
}
/**
Updates the custom airframe settings based on the current airframe.
Note: does NOT ask for an object refresh itself!
*/
void ConfigAirframeWidget::updateCustomAirframeUI()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
QList<double> curveValues;
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->customThrottle1Curve->initLinearCurve(field->getNumElements(),(double)1);
} else {
double temp=0;
double value;
for (unsigned int i=0; i < field->getNumElements(); i++) {
value=field->getValue(i).toDouble();
temp+=value;
curveValues.append(value);
}
if(temp==0)
m_aircraft->customThrottle1Curve->initLinearCurve(field->getNumElements(),(double)1);
else
m_aircraft->customThrottle1Curve->initCurve(curveValues);
}
field = obj->getField(QString("ThrottleCurve2"));
curveValues.clear();;
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->customThrottle2Curve->initLinearCurve(field->getNumElements(),(double)1);
} else {
for (unsigned int i=0; i < field->getNumElements(); i++) {
curveValues.append(field->getValue(i).toDouble());
}
m_aircraft->customThrottle2Curve->initCurve(curveValues);
}
// Update the table:
for (int i=0; i<8; i++) {
field = obj->getField(mixerTypes.at(i));
QComboBox* q = (QComboBox*)m_aircraft->customMixerTable->cellWidget(0,i);
QString s = field->getValue().toString();
q->setCurrentIndex(q->findText(s));
//bool en = (s != "Disabled");
field = obj->getField(mixerVectors.at(i));
int ti = field->getElementNames().indexOf("ThrottleCurve1");
m_aircraft->customMixerTable->item(1,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("ThrottleCurve2");
m_aircraft->customMixerTable->item(2,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Roll");
m_aircraft->customMixerTable->item(3,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->customMixerTable->item(4,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Yaw");
m_aircraft->customMixerTable->item(5,i)->setText(field->getValue(ti).toString());
}
}
/**
Sends the config to the board (airframe type)
We do all the tasks common to all airframes, or family of airframes, and
we call additional methods for specific frames, so that we do not have a code
that is too heavy.
*/
void ConfigAirframeWidget::updateObjectsFromWidgets()
{
qDebug()<<"updateObjectsFromWidgets";
QString airframeType = "Custom";
if (m_aircraft->aircraftType->currentText() == "Fixed Wing") {
// Save the curve (common to all Fixed wing frames)
UAVDataObject *obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
// Remove Feed Forward, it is pointless on a plane:
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble(0);
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->fixedWingThrottle->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
if (m_aircraft->fixedWingType->currentText() == "Elevator aileron rudder" ) {
airframeType = "FixedWing";
setupFrameFixedWing();
} else if (m_aircraft->fixedWingType->currentText() == "Elevon") {
airframeType = "FixedWingElevon";
setupFrameElevon();
} else { // "Vtail"
airframeType = "FixedWingVtail";
setupFrameVtail();
}
// Now reflect those settings in the "Custom" panel as well
updateCustomAirframeUI();
} else if (m_aircraft->aircraftType->currentText() == "Multirotor") {
QList<QString> motorList;
// We can already setup the feedforward here, as it is common to all platforms
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble((double)m_aircraft->feedForwardSlider->value()/100);
field = obj->getField(QString("AccelTime"));
field->setDouble(m_aircraft->accelTime->value());
field = obj->getField(QString("DecelTime"));
field->setDouble(m_aircraft->decelTime->value());
field = obj->getField(QString("MaxAccel"));
field->setDouble(m_aircraft->maxAccelSlider->value());
// Curve is also common to all quads:
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->multiThrottleCurve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
if (m_aircraft->multirotorFrameType->currentText() == "Quad +") {
airframeType = "QuadP";
setupQuad(true);
} else if (m_aircraft->multirotorFrameType->currentText() == "Quad X") {
airframeType = "QuadX";
setupQuad(false);
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter") {
airframeType = "Hexa";
setupHexa(true);
} else if (m_aircraft->multirotorFrameType->currentText() == "Octocopter") {
airframeType = "Octo";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None" ||
m_aircraft->multiMotor7->currentText() == "None" ||
m_aircraft->multiMotor8->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 8 motor channels");
return;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 0, -1},
{ 1, -1, 1},
{ 0, -1, -1},
{ -1, -1, 1},
{ -1, 0, -1},
{ -1, 1, 1},
{ 0, 1, -1},
{ 1, 1, 1}
};
setupMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter X") {
airframeType = "HexaX";
setupHexa(false);
} else if (m_aircraft->multirotorFrameType->currentText() == "Octocopter V") {
airframeType = "OctoV";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None" ||
m_aircraft->multiMotor7->currentText() == "None" ||
m_aircraft->multiMotor8->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 8 motor channels");
return;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// IMPORTANT: Assumes evenly spaced engines
// pitch roll yaw
double mixer [8][3] = {
{ 0.33, -1, -1},
{ 1 , -1, 1},
{ -1 , -1, -1},
{ -0.33, -1, 1},
{ -0.33, 1, -1},
{ -1 , 1, 1},
{ 1 , 1, -1},
{ 0.33, 1, 1}
};
setupMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octo Coax +") {
airframeType = "OctoCoaxP";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None" ||
m_aircraft->multiMotor7->currentText() == "None" ||
m_aircraft->multiMotor8->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 8 motor channels");
return;
}
motorList << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE" << "VTOLMotorSE"
<< "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW" << "VTOLMotorNW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 0, -1},
{ 1, 0, 1},
{ 0, -1, -1},
{ 0, -1, 1},
{ -1, 0, -1},
{ -1, 0, 1},
{ 0, 1, -1},
{ 0, 1, 1}
};
setupMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Octo Coax X") {
airframeType = "OctoCoaxX";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None" ||
m_aircraft->multiMotor7->currentText() == "None" ||
m_aircraft->multiMotor8->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 8 motor channels");
return;
}
motorList << "VTOLMotorNW" << "VTOLMotorN" << "VTOLMotorNE" << "VTOLMotorE"
<< "VTOLMotorSE" << "VTOLMotorS" << "VTOLMotorSW" << "VTOLMotorW";
setupMotors(motorList);
// Motor 1 to 8:
// pitch roll yaw
double mixer [8][3] = {
{ 1, 1, -1},
{ 1, 1, 1},
{ 1, -1, -1},
{ 1, -1, 1},
{ -1, -1, -1},
{ -1, -1, 1},
{ -1, 1, -1},
{ -1, 1, 1}
};
setupMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Hexacopter Y6") {
airframeType = "HexaCoax";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ||
m_aircraft->multiMotor4->currentText() == "None" ||
m_aircraft->multiMotor5->currentText() == "None" ||
m_aircraft->multiMotor6->currentText() == "None" ) {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 6 motor channels");
return;
}
motorList << "VTOLMotorNW" << "VTOLMotorW" << "VTOLMotorNE" << "VTOLMotorE"
<< "VTOLMotorS" << "VTOLMotorSE";
setupMotors(motorList);
// Motor 1 to 6, Y6 Layout:
// pitch roll yaw
double mixer [8][3] = {
{ 0.5, 1, -1},
{ 0.5, 1, 1},
{ 0.5, -1, -1},
{ 0.5, -1, 1},
{ -1, 0, -1},
{ -1, 0, 1},
{ 0, 0, 0},
{ 0, 0, 0}
};
setupMixer(mixer);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
} else if (m_aircraft->multirotorFrameType->currentText() == "Tricopter Y") {
airframeType = "Tri";
if (m_aircraft->multiMotor1->currentText() == "None" ||
m_aircraft->multiMotor2->currentText() == "None" ||
m_aircraft->multiMotor3->currentText() == "None" ) {
m_aircraft->mrStatusLabel->setText("ERROR: Assign 3 motor channels");
return;
}
if (m_aircraft->triYawChannel->currentText() == "None") {
m_aircraft->mrStatusLabel->setText("Error: Assign a Yaw channel");
return;
}
motorList << "VTOLMotorNW" << "VTOLMotorNE" << "VTOLMotorS";
setupMotors(motorList);
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
field = obj->getField("FixedWingYaw1");
field->setValue(m_aircraft->triYawChannel->currentText());
// Motor 1 to 6, Y6 Layout:
// pitch roll yaw
double mixer [8][3] = {
{ 0.5, 1, 0},
{ 0.5, -1, 0},
{ -1, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
};
setupMixer(mixer);
int eng = m_aircraft->triYawChannel->currentIndex()-1;
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
field = obj->getField(mixerTypes.at(eng));
field->setValue("Servo");
field = obj->getField(mixerVectors.at(eng));
resetField(field);
int ti = field->getElementNames().indexOf("Yaw");
field->setValue(127,ti);
m_aircraft->mrStatusLabel->setText("SUCCESS: Mixer Saved OK");
}
// Now reflect those settings in the "Custom" panel as well
updateCustomAirframeUI();
} else if (m_aircraft->aircraftType->currentText() == "Helicopter") {
airframeType = "HeliCP";
m_aircraft->widget_3->sendccpmUpdate();
} else {
airframeType = "Custom";
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
// Curve is also common to all quads:
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->customThrottle1Curve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
field = obj->getField("ThrottleCurve2");
curve.clear();
curve = m_aircraft->customThrottle2Curve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
// Update the table:
for (int i=0; i<8; i++) {
field = obj->getField(mixerTypes.at(i));
QComboBox* q = (QComboBox*)m_aircraft->customMixerTable->cellWidget(0,i);
field->setValue(q->currentText());
field = obj->getField(mixerVectors.at(i));
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(m_aircraft->customMixerTable->item(1,i)->text(),ti);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(m_aircraft->customMixerTable->item(2,i)->text(),ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(m_aircraft->customMixerTable->item(3,i)->text(),ti);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(m_aircraft->customMixerTable->item(4,i)->text(),ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(m_aircraft->customMixerTable->item(5,i)->text(),ti);
}
}
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("SystemSettings")));
UAVObjectField* field = obj->getField(QString("AirframeType"));
field->setValue(airframeType);
}
void ConfigAirframeWidget::openHelp()
{
QDesktopServices::openUrl( QUrl("http://wiki.openpilot.org/display/Doc/Airframe+configuration", QUrl::StrictMode) );
}
void ConfigAirframeWidget::addToDirtyMonitor()
{
addWidget(m_aircraft->customMixerTable);
addWidget(m_aircraft->customThrottle2Curve);
addWidget(m_aircraft->customThrottle1Curve);
addWidget(m_aircraft->multiThrottleCurve);
addWidget(m_aircraft->fixedWingThrottle);
addWidget(m_aircraft->fixedWingType);
addWidget(m_aircraft->feedForwardSlider);
addWidget(m_aircraft->accelTime);
addWidget(m_aircraft->decelTime);
addWidget(m_aircraft->maxAccelSlider);
addWidget(m_aircraft->multirotorFrameType);
addWidget(m_aircraft->multiMotor1);
addWidget(m_aircraft->multiMotor2);
addWidget(m_aircraft->multiMotor3);
addWidget(m_aircraft->multiMotor4);
addWidget(m_aircraft->multiMotor5);
addWidget(m_aircraft->multiMotor6);
addWidget(m_aircraft->multiMotor7);
addWidget(m_aircraft->multiMotor8);
addWidget(m_aircraft->triYawChannel);
addWidget(m_aircraft->aircraftType);
addWidget(m_aircraft->fwEngineChannel);
addWidget(m_aircraft->fwAileron1Channel);
addWidget(m_aircraft->fwAileron2Channel);
addWidget(m_aircraft->fwElevator1Channel);
addWidget(m_aircraft->fwElevator2Channel);
addWidget(m_aircraft->fwRudder1Channel);
addWidget(m_aircraft->fwRudder2Channel);
addWidget(m_aircraft->elevonSlider1);
addWidget(m_aircraft->elevonSlider2);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmType);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmTailChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmEngineChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoWChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoXChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoYChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmSingleServo);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoZChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleW);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleX);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCorrectionAngle);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleZ);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleY);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectivePassthrough);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmLinkRoll);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmLinkCyclic);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRevoSlider);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmREVOspinBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveSlider);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectivespinBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCyclicScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmPitchScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmPitchScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRollScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRollScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->SwashLvlPositionSlider);
addWidget(m_aircraft->widget_3->m_ccpm->SwashLvlPositionSpinBox);
addWidget(m_aircraft->widget_3->m_ccpm->CurveType);
addWidget(m_aircraft->widget_3->m_ccpm->NumCurvePoints);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue1);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue2);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue3);
addWidget(m_aircraft->widget_3->m_ccpm->CurveToGenerate);
addWidget(m_aircraft->widget_3->m_ccpm->CurveSettings);
addWidget(m_aircraft->widget_3->m_ccpm->ThrottleCurve);
addWidget(m_aircraft->widget_3->m_ccpm->PitchCurve);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAdvancedSettingsTable);
}

View File

@ -28,7 +28,7 @@
#include "configahrswidget.h"
#include "configgadgetwidget.h"
#include "configairframewidget.h"
#include "configvehicletypewidget.h"
#include "configccattitudewidget.h"
#include "configinputwidget.h"
#include "configoutputwidget.h"
@ -68,7 +68,7 @@ ConfigGadgetWidget::ConfigGadgetWidget(QWidget *parent) : QWidget(parent)
qwd = new DefaultHwSettingsWidget(this);
ftw->insertTab(ConfigGadgetWidget::hardware, qwd, QIcon(":/configgadget/images/hw_config.png"), QString("HW Settings"));
qwd = new ConfigAirframeWidget(this);
qwd = new ConfigVehicleTypeWidget(this);
ftw->insertTab(ConfigGadgetWidget::aircraft, qwd, QIcon(":/configgadget/images/Airframe.png"), QString("Aircraft"));
qwd = new ConfigInputWidget(this);

View File

@ -56,18 +56,19 @@ ConfigInputWidget::ConfigInputWidget(QWidget *parent) : ConfigTaskWidget(parent)
addApplySaveButtons(m_config->saveRCInputToRAM,m_config->saveRCInputToSD);
//Generate the rows of buttons in the input channel form GUI
unsigned int index=0;
foreach (QString name, manualSettingsObj->getField("ChannelNumber")->getElementNames())
{
Q_ASSERT(index < ManualControlSettings::CHANNELGROUPS_NUMELEM);
inputChannelForm * inp=new inputChannelForm(this,index==0);
m_config->channelSettings->layout()->addWidget(inp);
inp->setName(name);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelGroups",inp->ui->channelGroup,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelNumber",inp->ui->channelNumber,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelMin",inp->ui->channelMin,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelNeutral",inp->ui->channelNeutral,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelMax",inp->ui->channelMax,index);
inputChannelForm * inpForm=new inputChannelForm(this,index==0);
m_config->channelSettings->layout()->addWidget(inpForm); //Add the row to the UI
inpForm->setName(name);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelGroups",inpForm->ui->channelGroup,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelNumber",inpForm->ui->channelNumber,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelMin",inpForm->ui->channelMin,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelNeutral",inpForm->ui->channelNeutral,index);
addUAVObjectToWidgetRelation("ManualControlSettings","ChannelMax",inpForm->ui->channelMax,index);
++index;
}

View File

@ -0,0 +1,926 @@
/**
******************************************************************************
*
* @file configvehicletypewidget.cpp
* @author E. Lafargue, K. Sebesta & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
* @{
* @brief Airframe configuration panel
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "configvehicletypewidget.h"
#include <QDebug>
#include <QStringList>
#include <QTimer>
#include <QtGui/QWidget>
#include <QtGui/QTextEdit>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <math.h>
#include <QDesktopServices>
#include <QUrl>
#include "systemsettings.h"
#include "mixersettings.h"
#include "actuatorsettings.h"
#include <QEventLoop>
/**
Helper delegate for the custom mixer editor table.
Taken straight from Qt examples, thanks!
*/
SpinBoxDelegate::SpinBoxDelegate(QObject *parent)
: QItemDelegate(parent)
{
}
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QSpinBox *editor = new QSpinBox(parent);
editor->setMinimum(-127);
editor->setMaximum(127);
return editor;
}
void SpinBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
/**********************************************************************************/
/**
Constructor
*/
ConfigVehicleTypeWidget::ConfigVehicleTypeWidget(QWidget *parent) : ConfigTaskWidget(parent)
{
m_aircraft = new Ui_AircraftWidget();
m_aircraft->setupUi(this);
addApplySaveButtons(m_aircraft->saveAircraftToRAM,m_aircraft->saveAircraftToSD);
addUAVObject("SystemSettings");
addUAVObject("MixerSettings");
addUAVObject("ActuatorSettings");
ffTuningInProgress = false;
ffTuningPhase = false;
//Generate list of channels
QStringList channels;
channels << "None";
for (int i = 0; i < ActuatorSettings::CHANNELADDR_NUMELEM; i++) {
mixerTypes << QString("Mixer%1Type").arg(i+1);
mixerVectors << QString("Mixer%1Vector").arg(i+1);
channels << QString("Channel%1").arg(i+1);
}
QStringList airframeTypes;
airframeTypes << "Fixed Wing" << "Multirotor" << "Helicopter" << "Ground" << "Custom";
m_aircraft->aircraftType->addItems(airframeTypes);
m_aircraft->aircraftType->setCurrentIndex(1);
QStringList fixedWingTypes;
fixedWingTypes << "Elevator aileron rudder" << "Elevon" << "Vtail";
m_aircraft->fixedWingType->addItems(fixedWingTypes);
m_aircraft->fixedWingType->setCurrentIndex(0); //Set default model to "Elevator aileron rudder"
QStringList groundVehicleTypes;
groundVehicleTypes << "Turnable (car)" << "Differential (tank)" << "Motorcycle";
m_aircraft->groundVehicleType->addItems(groundVehicleTypes);
m_aircraft->groundVehicleType->setCurrentIndex(0); //Set default model to "Turnable (car)"
QStringList multiRotorTypes;
multiRotorTypes << "Tricopter Y"<< "Quad +" << "Quad X" <<
"Hexacopter" << "Hexacopter X" << "Hexacopter Y6" <<
"Octocopter" << "Octocopter V" << "Octo Coax +" << "Octo Coax X" ;
m_aircraft->multirotorFrameType->addItems(multiRotorTypes);
m_aircraft->multirotorFrameType->setCurrentIndex(1); //Set default model to "Quad +"
// Now load all the channel assignements
//OLD STYLE: DO IT MANUALLY
// m_aircraft->triYawChannelBox->addItems(channels);
// m_aircraft->gvMotor1ChannelBox->addItems(channels);
// m_aircraft->gvMotor2ChannelBox->addItems(channels);
// m_aircraft->gvSteering1ChannelBox->addItems(channels);
// m_aircraft->gvSteering2ChannelBox->addItems(channels);
// m_aircraft->fwElevator1ChannelBox->addItems(channels);
// m_aircraft->fwElevator2ChannelBox->addItems(channels);
// m_aircraft->fwEngineChannelBox->addItems(channels);
// m_aircraft->fwRudder1ChannelBox->addItems(channels);
// m_aircraft->fwRudder2ChannelBox->addItems(channels);
// m_aircraft->fwAileron1ChannelBox->addItems(channels);
// m_aircraft->fwAileron2ChannelBox->addItems(channels);
//NEW STYLE: Loop through the widgets looking for all widgets that have "ChannelBox" in their name
// The upshot of this is that ALL new ComboBox widgets for selecting the output channel must have "ChannelBox" in their name
foreach(QComboBox *combobox, this->findChildren<QComboBox*>(QRegExp("\\S+ChannelBo\\S+")))//FOR WHATEVER REASON, THIS DOES NOT WORK WITH ChannelBox. ChannelBo is sufficiently accurate
{
combobox->addItems(channels);
}
// Setup the Multirotor picture in the Quad settings interface
m_aircraft->quadShape->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_aircraft->quadShape->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QSvgRenderer *renderer = new QSvgRenderer();
renderer->load(QString(":/configgadget/images/quad-shapes.svg"));
quad = new QGraphicsSvgItem();
quad->setSharedRenderer(renderer);
quad->setElementId("quad-plus");
QGraphicsScene *scene = new QGraphicsScene(this);
scene->addItem(quad);
scene->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->setScene(scene);
// Put combo boxes in line one of the custom mixer table:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("Mixer1Type"));
QStringList list = field->getOptions();
for (int i=0;i<8;i++) {
QComboBox* qb = new QComboBox(m_aircraft->customMixerTable);
qb->addItems(list);
m_aircraft->customMixerTable->setCellWidget(0,i,qb);
}
SpinBoxDelegate *sbd = new SpinBoxDelegate();
for (int i=1;i<8; i++) {
m_aircraft->customMixerTable->setItemDelegateForRow(i, sbd);
}
//Connect aircraft type selection dropbox to callback function
connect(m_aircraft->aircraftType, SIGNAL(currentIndexChanged(int)), this, SLOT(switchAirframeType(int)));
//Connect airframe selection dropbox to callback functions
connect(m_aircraft->fixedWingType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupAirframeUI(QString)));
connect(m_aircraft->multirotorFrameType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupAirframeUI(QString)));
connect(m_aircraft->groundVehicleType, SIGNAL(currentIndexChanged(QString)), this, SLOT(setupAirframeUI(QString)));
//Connect throttle curve reset pushbuttons to reset functions
connect(m_aircraft->fwThrottleReset, SIGNAL(clicked()), this, SLOT(resetFwMixer()));
connect(m_aircraft->mrThrottleCurveReset, SIGNAL(clicked()), this, SLOT(resetMrMixer()));
connect(m_aircraft->gvThrottleCurve1Reset, SIGNAL(clicked()), this, SLOT(resetGvFrontMixer()));
connect(m_aircraft->gvThrottleCurve2Reset, SIGNAL(clicked()), this, SLOT(resetGvRearMixer()));
connect(m_aircraft->customReset1, SIGNAL(clicked()), this, SLOT(resetCt1Mixer()));
connect(m_aircraft->customReset2, SIGNAL(clicked()), this, SLOT(resetCt2Mixer()));
//Connect throttle curve manipulation points to output text
connect(m_aircraft->fixedWingThrottle, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateFwThrottleCurveValue(QList<double>,double)));
connect(m_aircraft->multiThrottleCurve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateMrThrottleCurveValue(QList<double>,double)));
connect(m_aircraft->groundVehicleThrottle1, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateGvThrottle1CurveValue(QList<double>,double)));
connect(m_aircraft->groundVehicleThrottle2, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateGvThrottle2CurveValue(QList<double>,double)));
connect(m_aircraft->customThrottle1Curve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateCustomThrottle1CurveValue(QList<double>,double)));
connect(m_aircraft->customThrottle2Curve, SIGNAL(curveUpdated(QList<double>,double)), this, SLOT(updateCustomThrottle2CurveValue(QList<double>,double)));
// connect(m_aircraft->fwAileron1Channel, SIGNAL(currentIndexChanged(int)), this, SLOT(toggleAileron2(int)));
// connect(m_aircraft->fwElevator1Channel, SIGNAL(currentIndexChanged(int)), this, SLOT(toggleElevator2(int)));
//Connect the three feed forward test checkboxes
connect(m_aircraft->ffTestBox1, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
connect(m_aircraft->ffTestBox2, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
connect(m_aircraft->ffTestBox3, SIGNAL(clicked(bool)), this, SLOT(enableFFTest()));
//WHAT DOES THIS DO?
enableControls(false);
refreshWidgetsValues();
// Connect the help pushbutton
connect(m_aircraft->airframeHelp, SIGNAL(clicked()), this, SLOT(openHelp()));
addToDirtyMonitor();
//Initialize GUI tabs //MOVING THIS FROM THE END OF THIS FUNCTION CAN CAUSE RUNTIME ERRORS DUE TO setupMultiRotorUI. WHY?
setupMultiRotorUI( m_aircraft->multirotorFrameType->currentText() );
setupGroundVehicleUI( m_aircraft->groundVehicleType->currentText() );
setupFixedWingUI( m_aircraft->fixedWingType->currentText() );
}
/**
Destructor
*/
ConfigVehicleTypeWidget::~ConfigVehicleTypeWidget()
{
// Do nothing
}
/**
Slot for switching the airframe type. We do it explicitely
rather than a signal in the UI, because we want to force a fitInView of the quad shapes.
This is because this method (fitinview) only works when the widget is shown.
*/
void ConfigVehicleTypeWidget::switchAirframeType(int index){
m_aircraft->airframesWidget->setCurrentIndex(index);
m_aircraft->quadShape->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
if (m_aircraft->aircraftType->findText("Custom")) {
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
}
/**
WHAT DOES THIS DO???
*/
void ConfigVehicleTypeWidget::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
// Thit fitInView method should only be called now, once the
// widget is shown, otherwise it cannot compute its values and
// the result is usually a ahrsbargraph that is way too small.
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
/**
Resize the GUI contents when the user changes the window size
*/
void ConfigVehicleTypeWidget::resizeEvent(QResizeEvent* event)
{
Q_UNUSED(event);
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
// Make the custom table columns autostretch:
m_aircraft->customMixerTable->resizeColumnsToContents();
for (int i=0;i<8;i++) {
m_aircraft->customMixerTable->setColumnWidth(i,(m_aircraft->customMixerTable->width()-
m_aircraft->customMixerTable->verticalHeader()->width())/8);
}
}
void ConfigVehicleTypeWidget::toggleAileron2(int index)
{
if (index) {
m_aircraft->fwAileron2ChannelBox->setEnabled(true);
m_aircraft->fwAileron2Label->setEnabled(true);
} else {
m_aircraft->fwAileron2ChannelBox->setEnabled(false);
m_aircraft->fwAileron2Label->setEnabled(false);
}
}
void ConfigVehicleTypeWidget::toggleElevator2(int index)
{
if (index) {
m_aircraft->fwElevator2ChannelBox->setEnabled(true);
m_aircraft->fwElevator2Label->setEnabled(true);
} else {
m_aircraft->fwElevator2ChannelBox->setEnabled(false);
m_aircraft->fwElevator2Label->setEnabled(false);
}
}
void ConfigVehicleTypeWidget::toggleRudder2(int index)
{
if (index) {
m_aircraft->fwRudder2ChannelBox->setEnabled(true);
m_aircraft->fwRudder2Label->setEnabled(true);
} else {
m_aircraft->fwRudder2ChannelBox->setEnabled(false);
m_aircraft->fwRudder2Label->setEnabled(false);
}
}
/////////////////////////////////////////////////////////
/// Feed Forward Testing
/////////////////////////////////////////////////////////
/**
Enables and runs feed forward testing
*/
void ConfigVehicleTypeWidget::enableFFTest()
{
// Role:
// - Check if all three checkboxes are checked
// - Every other timer event: toggle engine from 45% to 55%
// - Every other time event: send FF settings to flight FW
if (m_aircraft->ffTestBox1->isChecked() &&
m_aircraft->ffTestBox2->isChecked() &&
m_aircraft->ffTestBox3->isChecked()) {
if (!ffTuningInProgress)
{
// Initiate tuning:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
UAVObject::Metadata mdata = obj->getMetadata();
accInitialData = mdata;
mdata.flightAccess = UAVObject::ACCESS_READONLY;
obj->setMetadata(mdata);
}
// Depending on phase, either move actuator or send FF settings:
if (ffTuningPhase) {
// Send FF settings to the board
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
field->setDouble((double)m_aircraft->feedForwardSlider->value()/100);
field = obj->getField(QString("AccelTime"));
field->setDouble(m_aircraft->accelTime->value());
field = obj->getField(QString("DecelTime"));
field->setDouble(m_aircraft->decelTime->value());
field = obj->getField(QString("MaxAccel"));
field->setDouble(m_aircraft->maxAccelSlider->value());
obj->updated();
} else {
// Toggle motor state
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
double value = obj->getField("Throttle")->getDouble();
double target = (value < 0.5) ? 0.55 : 0.45;
obj->getField("Throttle")->setValue(target);
obj->updated();
}
ffTuningPhase = !ffTuningPhase;
ffTuningInProgress = true;
QTimer::singleShot(1000, this, SLOT(enableFFTest()));
} else {
// - If no: disarm timer, restore actuatorcommand metadata
// Disarm!
if (ffTuningInProgress) {
ffTuningInProgress = false;
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ManualControlCommand")));
UAVObject::Metadata mdata = obj->getMetadata();
mdata = accInitialData; // Restore metadata
obj->setMetadata(mdata);
}
}
}
/**
Resets Fixed wing throttle mixer
*/
void ConfigVehicleTypeWidget::resetFwMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->fixedWingThrottle, field->getNumElements(),1);
}
/**
Resets Multirotor throttle mixer
*/
void ConfigVehicleTypeWidget::resetMrMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->multiThrottleCurve, field->getNumElements(),0.9);
}
/**
Resets Ground vehicle front throttle mixer
*/
void ConfigVehicleTypeWidget::resetGvFrontMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->groundVehicleThrottle1, field->getNumElements(),1);
}
/**
Resets Ground vehicle rear throttle mixer
*/
void ConfigVehicleTypeWidget::resetGvRearMixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve2"));
resetMixer(m_aircraft->groundVehicleThrottle2, field->getNumElements(),1);
}
/**
Resets Custom throttle 1 mixer
*/
void ConfigVehicleTypeWidget::resetCt1Mixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
resetMixer(m_aircraft->customThrottle1Curve, field->getNumElements(),1);
}
/**
Resets Custom throttle 2 mixer
*/
void ConfigVehicleTypeWidget::resetCt2Mixer()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve2"));
resetMixer(m_aircraft->customThrottle2Curve, field->getNumElements(),1);
}
/**
Resets a mixer curve
*/
void ConfigVehicleTypeWidget::resetMixer(MixerCurveWidget *mixer, int numElements, double maxvalue)
{
// Setup all Throttle1 curves for all types of airframes
mixer->initLinearCurve((quint32)numElements,maxvalue);
}
/**
Updates the currently moved fixed wing throttle curve item value
*/
void ConfigVehicleTypeWidget::updateFwThrottleCurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->fwThrottleCurveItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved multi-rotor throttle curve item value
*/
void ConfigVehicleTypeWidget::updateMrThrottleCurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->mrThrottleCurveItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the moved ground vehicle front throttle curve item value
*/
void ConfigVehicleTypeWidget::updateGvThrottle1CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->gvThrottleCurve1ItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the moved ground vehicle rear throttle curve item value
*/
void ConfigVehicleTypeWidget::updateGvThrottle2CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->gvThrottleCurve2ItemValue->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved custom throttle curve item value (Custom throttle 1)
*/
void ConfigVehicleTypeWidget::updateCustomThrottle1CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->customThrottleCurve1Value->setText(QString().sprintf("Val: %.2f",value));
}
/**
Updates the currently moved custom throttle curve item value (Custom throttle 2)
*/
void ConfigVehicleTypeWidget::updateCustomThrottle2CurveValue(QList<double> list, double value)
{
Q_UNUSED(list);
m_aircraft->customThrottleCurve2Value->setText(QString().sprintf("Val: %.2f",value));
}
/**************************
* Aircraft settings
**************************/
/**
Refreshes the current value of the SystemSettings which holds the aircraft type
*/
void ConfigVehicleTypeWidget::refreshWidgetsValues()
{
if(!allObjectsUpdated())
return;
//WHAT DOES THIS DO?
bool dirty=isDirty(); //WHY IS THIS CALLED HERE AND THEN AGAIN SEVERAL LINES LATER IN setupAirframeUI()
// Get the Airframe type from the system settings:
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("SystemSettings")));
Q_ASSERT(obj);
UAVObjectField *field = obj->getField(QString("AirframeType"));
Q_ASSERT(field);
// At this stage, we will need to have some hardcoded settings in this code, this
// is not ideal, but here you go.
QString frameType = field->getValue().toString();
setupAirframeUI(frameType);
obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
Q_ASSERT(obj);
field = obj->getField(QString("ThrottleCurve1"));
Q_ASSERT(field);
QList<double> curveValues;
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->multiThrottleCurve->initLinearCurve(field->getNumElements(),(double)1);
m_aircraft->fixedWingThrottle->initLinearCurve(field->getNumElements(),(double)1);
m_aircraft->groundVehicleThrottle1->initLinearCurve(field->getNumElements(),(double)1);
}
else {
double temp=0;
double value;
for (unsigned int i=0; i < field->getNumElements(); i++) {
value=field->getValue(i).toDouble();
temp+=value;
curveValues.append(value);
}
if(temp==0)
{
m_aircraft->multiThrottleCurve->initLinearCurve(field->getNumElements(),0.9);
m_aircraft->fixedWingThrottle->initLinearCurve(field->getNumElements(),(double)1);
m_aircraft->groundVehicleThrottle1->initLinearCurve(field->getNumElements(),(double)1);
}
else
{
m_aircraft->multiThrottleCurve->initCurve(curveValues);
m_aircraft->fixedWingThrottle->initCurve(curveValues);
m_aircraft->groundVehicleThrottle1->initCurve(curveValues);
}
}
// Setup all Throttle2 curves for all types of airframes //AT THIS MOMENT, THAT MEANS ONLY GROUND VEHICLES
Q_ASSERT(obj);
field = obj->getField(QString("ThrottleCurve2"));
Q_ASSERT(field);
curveValues.clear();
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->groundVehicleThrottle2->initLinearCurve(field->getNumElements(),(double)1);
}
else {
double temp=0;
double value;
for (unsigned int i=0; i < field->getNumElements(); i++) {
value=field->getValue(i).toDouble();
temp+=value;
curveValues.append(value);
}
if(temp==0)
{
m_aircraft->groundVehicleThrottle2->initLinearCurve(field->getNumElements(),(double)1);
}
else
{
m_aircraft->groundVehicleThrottle2->initCurve(curveValues);
}
}
// Load the Settings for fixed wing frames:
if (frameType.startsWith("FixedWing")) {
// Retrieve fixed wing settings
refreshFixedWingWidgetsValues(frameType);
} else if (frameType == "Tri" ||
frameType == "QuadX" || frameType == "QuadP" ||
frameType == "Hexa" || frameType == "HexaCoax" || frameType == "HexaX" ||
frameType == "Octo" || frameType == "OctoV" || frameType == "OctoCoaxP" || frameType == "OctoCoaxX" ) {
// Retrieve multirotor settings
refreshMultiRotorWidgetsValues(frameType);
} else if (frameType == "HeliCP") {
m_aircraft->widget_3->requestccpmUpdate();
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Helicopter"));//"Helicopter"
} else if (frameType.startsWith("GroundVehicle")) {
// Retrieve ground vehicle settings
refreshGroundVehicleWidgetsValues(frameType);
} else if (frameType == "Custom") {
m_aircraft->aircraftType->setCurrentIndex(m_aircraft->aircraftType->findText("Custom"));
}
updateCustomAirframeUI();
setDirty(dirty);
}
/**
\brief Sets up the mixer depending on Airframe type. Accepts either system settings or
combo box entry from airframe type, as those do not overlap.
*/
void ConfigVehicleTypeWidget::setupAirframeUI(QString frameType)
{
bool dirty=isDirty();
if(frameType == "FixedWing" || frameType == "Elevator aileron rudder" ||
frameType == "FixedWingElevon" || frameType == "Elevon" ||
frameType == "FixedWingVtail" || frameType == "Vtail"){
setupFixedWingUI(frameType);
} else if (frameType == "Tri" || frameType == "Tricopter Y" ||
frameType == "QuadX" || frameType == "Quad X" ||
frameType == "QuadP" || frameType == "Quad +" ||
frameType == "Hexa" || frameType == "Hexacopter" ||
frameType == "HexaX" || frameType == "Hexacopter X" ||
frameType == "HexaCoax" || frameType == "Hexacopter Y6" ||
frameType == "Octo" || frameType == "Octocopter" ||
frameType == "OctoV" || frameType == "Octocopter V" ||
frameType == "OctoCoaxP" || frameType == "Octo Coax +" ) {
//Call multi-rotor setup UI
setupMultiRotorUI(frameType);
}
else if (frameType == "GroundVehicleCar" || frameType == "Turnable (car)" ||
frameType == "GroundVehicleDifferential" || frameType == "Differential (tank)" ||
frameType == "GroundVehicleMotorcyle" || frameType == "Motorcycle") {
setupGroundVehicleUI(frameType);
}
//SHOULDN'T THIS BE DONE ONLY IN QUAD SETUP, AND NOT ALL THE REST???
m_aircraft->quadShape->setSceneRect(quad->boundingRect());
m_aircraft->quadShape->fitInView(quad, Qt::KeepAspectRatio);
setDirty(dirty);
}
/**
Reset the contents of a field
*/
void ConfigVehicleTypeWidget::resetField(UAVObjectField * field)
{
for (unsigned int i=0;i<field->getNumElements();i++) {
field->setValue(0,i);
}
}
/**
Reset actuator values
*/
void ConfigVehicleTypeWidget::resetActuators()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("ActuatorSettings")));
Q_ASSERT(obj);
QList<UAVObjectField*> fieldList = obj->getFields();
// Reset all assignements first:
foreach (UAVObjectField* field, fieldList) {
// NOTE: we assume that all options in ActuatorSettings are a channel assignement
// except for the options called "ChannelBoxXXX"
if (field->getUnits().contains("channel")) {
field->setValue(field->getOptions().last());
}
}
}
/**
Updates the custom airframe settings based on the current airframe.
Note: does NOT ask for an object refresh itself!
*/
void ConfigVehicleTypeWidget::updateCustomAirframeUI()
{
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("ThrottleCurve1"));
QList<double> curveValues;
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->customThrottle1Curve->initLinearCurve(field->getNumElements(),(double)1);
} else {
double temp=0;
double value;
for (unsigned int i=0; i < field->getNumElements(); i++) {
value=field->getValue(i).toDouble();
temp+=value;
curveValues.append(value);
}
if(temp==0)
m_aircraft->customThrottle1Curve->initLinearCurve(field->getNumElements(),(double)1);
else
m_aircraft->customThrottle1Curve->initCurve(curveValues);
}
field = obj->getField(QString("ThrottleCurve2"));
curveValues.clear();
// If the 1st element of the curve is <= -10, then the curve
// is a straight line (that's how the mixer works on the mainboard):
if (field->getValue(0).toInt() <= -10) {
m_aircraft->customThrottle2Curve->initLinearCurve(field->getNumElements(),(double)1);
} else {
for (unsigned int i=0; i < field->getNumElements(); i++) {
curveValues.append(field->getValue(i).toDouble());
}
m_aircraft->customThrottle2Curve->initCurve(curveValues);
}
// Update the table:
for (int i=0; i<8; i++) {
field = obj->getField(mixerTypes.at(i));
QComboBox* q = (QComboBox*)m_aircraft->customMixerTable->cellWidget(0,i);
QString s = field->getValue().toString();
q->setCurrentIndex(q->findText(s));
//bool en = (s != "Disabled");
field = obj->getField(mixerVectors.at(i));
int ti = field->getElementNames().indexOf("ThrottleCurve1");
m_aircraft->customMixerTable->item(1,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("ThrottleCurve2");
m_aircraft->customMixerTable->item(2,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Roll");
m_aircraft->customMixerTable->item(3,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Pitch");
m_aircraft->customMixerTable->item(4,i)->setText(field->getValue(ti).toString());
ti = field->getElementNames().indexOf("Yaw");
m_aircraft->customMixerTable->item(5,i)->setText(field->getValue(ti).toString());
}
}
/**
Sends the config to the board (airframe type)
We do all the tasks common to all airframes, or family of airframes, and
we call additional methods for specific frames, so that we do not have a code
that is too heavy.
*/
void ConfigVehicleTypeWidget::updateObjectsFromWidgets()
{
qDebug()<<"updateObjectsFromWidgets";
QString airframeType = "Custom"; //Sets airframe type default to "Custom"
if (m_aircraft->aircraftType->currentText() == "Fixed Wing") {
airframeType = updateFixedWingObjectsFromWidgets();
} else if (m_aircraft->aircraftType->currentText() == "Multirotor") {
//update the mixer
airframeType = updateMultiRotorObjectsFromWidgets();
} else if (m_aircraft->aircraftType->currentText() == "Helicopter") {
airframeType = "HeliCP";
m_aircraft->widget_3->sendccpmUpdate();
} else if (m_aircraft->aircraftType->currentText() == "Ground") {
airframeType = updateGroundVehicleObjectsFromWidgets();
} else {
airframeType = "Custom";
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("MixerSettings")));
UAVObjectField* field = obj->getField(QString("FeedForward"));
// Curve is also common to all quads:
field = obj->getField("ThrottleCurve1");
QList<double> curve = m_aircraft->customThrottle1Curve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
field = obj->getField("ThrottleCurve2");
curve.clear();
curve = m_aircraft->customThrottle2Curve->getCurve();
for (int i=0;i<curve.length();i++) {
field->setValue(curve.at(i),i);
}
// Update the table:
for (int i=0; i<8; i++) {
field = obj->getField(mixerTypes.at(i));
QComboBox* q = (QComboBox*)m_aircraft->customMixerTable->cellWidget(0,i);
field->setValue(q->currentText());
field = obj->getField(mixerVectors.at(i));
int ti = field->getElementNames().indexOf("ThrottleCurve1");
field->setValue(m_aircraft->customMixerTable->item(1,i)->text(),ti);
ti = field->getElementNames().indexOf("ThrottleCurve2");
field->setValue(m_aircraft->customMixerTable->item(2,i)->text(),ti);
ti = field->getElementNames().indexOf("Roll");
field->setValue(m_aircraft->customMixerTable->item(3,i)->text(),ti);
ti = field->getElementNames().indexOf("Pitch");
field->setValue(m_aircraft->customMixerTable->item(4,i)->text(),ti);
ti = field->getElementNames().indexOf("Yaw");
field->setValue(m_aircraft->customMixerTable->item(5,i)->text(),ti);
}
}
//WHAT DOES THIS DO?
UAVDataObject* obj = dynamic_cast<UAVDataObject*>(getObjectManager()->getObject(QString("SystemSettings")));
UAVObjectField* field = obj->getField(QString("AirframeType"));
field->setValue(airframeType);
}
/**
Opens the wiki from the user's default browser
*/
void ConfigVehicleTypeWidget::openHelp()
{
QDesktopServices::openUrl( QUrl("http://wiki.openpilot.org/display/Doc/Airframe+configuration", QUrl::StrictMode) );
}
/**
WHAT DOES THIS DO???
*/
void ConfigVehicleTypeWidget::addToDirtyMonitor()
{
addWidget(m_aircraft->customMixerTable);
addWidget(m_aircraft->customThrottle1Curve);
addWidget(m_aircraft->customThrottle2Curve);
addWidget(m_aircraft->multiThrottleCurve);
addWidget(m_aircraft->fixedWingThrottle);
addWidget(m_aircraft->fixedWingType);
addWidget(m_aircraft->groundVehicleThrottle1);
addWidget(m_aircraft->groundVehicleThrottle2);
addWidget(m_aircraft->groundVehicleType);
addWidget(m_aircraft->feedForwardSlider);
addWidget(m_aircraft->accelTime);
addWidget(m_aircraft->decelTime);
addWidget(m_aircraft->maxAccelSlider);
addWidget(m_aircraft->multirotorFrameType);
addWidget(m_aircraft->multiMotorChannelBox1);
addWidget(m_aircraft->multiMotorChannelBox2);
addWidget(m_aircraft->multiMotorChannelBox3);
addWidget(m_aircraft->multiMotorChannelBox4);
addWidget(m_aircraft->multiMotorChannelBox5);
addWidget(m_aircraft->multiMotorChannelBox6);
addWidget(m_aircraft->multiMotorChannelBox7);
addWidget(m_aircraft->multiMotorChannelBox8);
addWidget(m_aircraft->triYawChannelBox);
addWidget(m_aircraft->aircraftType);
addWidget(m_aircraft->fwEngineChannelBox);
addWidget(m_aircraft->fwAileron1ChannelBox);
addWidget(m_aircraft->fwAileron2ChannelBox);
addWidget(m_aircraft->fwElevator1ChannelBox);
addWidget(m_aircraft->fwElevator2ChannelBox);
addWidget(m_aircraft->fwRudder1ChannelBox);
addWidget(m_aircraft->fwRudder2ChannelBox);
addWidget(m_aircraft->elevonSlider1);
addWidget(m_aircraft->elevonSlider2);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmType);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmTailChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmEngineChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoWChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoXChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoYChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmSingleServo);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmServoZChannel);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleW);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleX);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCorrectionAngle);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleZ);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAngleY);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectivePassthrough);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmLinkRoll);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmLinkCyclic);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRevoSlider);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmREVOspinBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveSlider);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectivespinBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCollectiveScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmCyclicScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmPitchScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmPitchScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRollScale);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmRollScaleBox);
addWidget(m_aircraft->widget_3->m_ccpm->SwashLvlPositionSlider);
addWidget(m_aircraft->widget_3->m_ccpm->SwashLvlPositionSpinBox);
addWidget(m_aircraft->widget_3->m_ccpm->CurveType);
addWidget(m_aircraft->widget_3->m_ccpm->NumCurvePoints);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue1);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue2);
addWidget(m_aircraft->widget_3->m_ccpm->CurveValue3);
addWidget(m_aircraft->widget_3->m_ccpm->CurveToGenerate);
addWidget(m_aircraft->widget_3->m_ccpm->CurveSettings);
addWidget(m_aircraft->widget_3->m_ccpm->ThrottleCurve);
addWidget(m_aircraft->widget_3->m_ccpm->PitchCurve);
addWidget(m_aircraft->widget_3->m_ccpm->ccpmAdvancedSettingsTable);
}

View File

@ -2,7 +2,7 @@
******************************************************************************
*
* @file configairframetwidget.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @addtogroup ConfigPlugin Config Plugin
@ -24,8 +24,8 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CONFIGAIRFRAMEWIDGET_H
#define CONFIGAIRFRAMEWIDGET_H
#ifndef CONFIGVEHICLETYPEWIDGET_H
#define CONFIGVEHICLETYPEWIDGET_H
#include "ui_airframe.h"
#include "../uavobjectwidgetutils/configtaskwidget.h"
@ -39,24 +39,27 @@
class Ui_Widget;
class ConfigAirframeWidget: public ConfigTaskWidget
class ConfigVehicleTypeWidget: public ConfigTaskWidget
{
Q_OBJECT
public:
ConfigAirframeWidget(QWidget *parent = 0);
~ConfigAirframeWidget();
ConfigVehicleTypeWidget(QWidget *parent = 0);
~ConfigVehicleTypeWidget();
private:
Ui_AircraftWidget *m_aircraft;
bool setupFrameFixedWing();
bool setupFrameElevon();
bool setupFrameVtail();
bool setupFrameFixedWing(QString airframeType);
bool setupFrameElevon(QString airframeType);
bool setupFrameVtail(QString airframeType);
bool setupQuad(bool pLayout);
bool setupHexa(bool pLayout);
bool setupOcto();
bool setupGroundVehicleCar(QString airframeType);
bool setupGroundVehicleDifferential(QString airframeType);
bool setupGroundVehicleMotorcycle(QString airframeType);
void updateCustomAirframeUI();
bool setupMixer(double mixerFactors[8][3]);
bool setupMultiRotorMixer(double mixerFactors[8][3]);
void setupMotors(QList<QString> motorList);
void addToDirtyMonitor();
void resetField(UAVObjectField * field);
@ -74,19 +77,39 @@ private:
private slots:
virtual void refreshWidgetsValues();
void refreshFixedWingWidgetsValues(QString frameType);
void refreshMultiRotorWidgetsValues(QString frameType);
void refreshGroundVehicleWidgetsValues(QString frameType);
void updateObjectsFromWidgets();
QString updateFixedWingObjectsFromWidgets();
QString updateMultiRotorObjectsFromWidgets();
QString updateGroundVehicleObjectsFromWidgets();
// void saveAircraftUpdate();
void setupAirframeUI(QString type);
void setupFixedWingUI(QString frameType);
void setupMultiRotorUI(QString frameType);
void setupGroundVehicleUI(QString frameType);
void throwMultiRotorChannelConfigError(int numMotors);
void throwFixedWingChannelConfigError(QString airframeType);
void throwGroundVehicleChannelConfigError(QString airframeType);
void toggleAileron2(int index);
void toggleElevator2(int index);
void toggleRudder2(int index);
void switchAirframeType(int index);
void resetFwMixer();
void resetMrMixer();
void resetGvFrontMixer();
void resetGvRearMixer();
void resetCt1Mixer();
void resetCt2Mixer();
void updateFwThrottleCurveValue(QList<double> list, double value);
void updateMrThrottleCurveValue(QList<double> list, double value);
void updateGvThrottle1CurveValue(QList<double> list, double value);
void updateGvThrottle2CurveValue(QList<double> list, double value);
void updateCustomThrottle1CurveValue(QList<double> list, double value);
void updateCustomThrottle2CurveValue(QList<double> list, double value);
void enableFFTest();
@ -117,4 +140,4 @@ public:
const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
#endif // CONFIGAIRFRAMEWIDGET_H
#endif // CONFIGVEHICLETYPEWIDGET_H

View File

@ -8,6 +8,8 @@ inputChannelForm::inputChannelForm(QWidget *parent,bool showlegend) :
ui(new Ui::inputChannelForm)
{
ui->setupUi(this);
//The first time through the loop, keep the legend. All other times, delete it.
if(!showlegend)
{
layout()->removeWidget(ui->legend0);

View File

@ -37,7 +37,7 @@
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
<height>25</height>
</size>
</property>
<property name="text">
@ -53,6 +53,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
@ -63,6 +69,12 @@
</item>
<item row="2" column="4">
<widget class="QSpinBox" name="channelMin">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
@ -76,6 +88,12 @@
</item>
<item row="2" column="8">
<widget class="QSpinBox" name="channelMax">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
@ -93,11 +111,17 @@
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -126,11 +150,17 @@ font:bold;</string>
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -159,11 +189,17 @@ font:bold;</string>
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -192,11 +228,17 @@ font:bold;</string>
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -227,6 +269,12 @@ font:bold;</string>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -240,6 +288,12 @@ font:bold;</string>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
@ -269,6 +323,12 @@ font:bold;</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>Rev</string>
</property>
@ -277,14 +337,14 @@ font:bold;</string>
<item row="2" column="9">
<widget class="QLabel" name="neutral">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>35</width>
<height>0</height>
</size>
</property>
@ -331,50 +391,23 @@ font:bold;</string>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QLabel" name="legend2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: qlineargradient(spread:reflect, x1:0.507, y1:0, x2:0.507, y2:0.772, stop:0.208955 rgba(74, 74, 74, 255), stop:0.78607 rgba(36, 36, 36, 255));
color: rgb(255, 255, 255);
border-radius: 5;
margin:5px;
font:bold;</string>
</property>
<property name="text">
<string>Number</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="legend1">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -397,6 +430,45 @@ font:bold;</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="legend2">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: qlineargradient(spread:reflect, x1:0.507, y1:0, x2:0.507, y2:0.772, stop:0.208955 rgba(74, 74, 74, 255), stop:0.78607 rgba(36, 36, 36, 255));
color: rgb(255, 255, 255);
border-radius: 5;
margin:5px;
font:bold;</string>
</property>
<property name="text">
<string>Number</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -1,31 +1,35 @@
<xml>
<object name="ActuatorSettings" singleinstance="true" settings="true">
<description>Settings for the @ref ActuatorModule that controls the channel assignments for the mixer based on AircraftType</description>
<field name="FixedWingRoll1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingRoll2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingPitch1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingPitch2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingYaw1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingYaw2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingThrottle" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorN" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorNE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorSE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorS" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorSW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorNW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="ChannelUpdateFreq" units="Hz" type="uint16" elements="4" defaultvalue="50"/>
<field name="ChannelMax" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelNeutral" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelMin" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelType" units="" type="enum" elements="10" options="PWM,MK,ASTEC4,PWM Alarm Buzzer" defaultvalue="PWM"/>
<field name="ChannelAddr" units="" type="uint8" elements="10" defaultvalue="0,1,2,3,4,5,6,7,8,9"/>
<field name="MotorsSpinWhileArmed" units="" type="enum" elements="1" options="FALSE,TRUE" defaultvalue="FALSE"/>
<access gcs="readwrite" flight="readwrite"/>
<telemetrygcs acked="true" updatemode="onchange" period="0"/>
<telemetryflight acked="true" updatemode="onchange" period="0"/>
<logging updatemode="never" period="0"/>
</object>
</xml>
<xml>
<object name="ActuatorSettings" singleinstance="true" settings="true">
<description>Settings for the @ref ActuatorModule that controls the channel assignments for the mixer based on AircraftType</description>
<field name="FixedWingRoll1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingRoll2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingPitch1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingPitch2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingYaw1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingYaw2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="FixedWingThrottle" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorN" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorNE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorSE" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorS" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorSW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="VTOLMotorNW" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="GroundVehicleThrottle1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="GroundVehicleThrottle2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="GroundVehicleSteering1" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="GroundVehicleSteering2" units="channel" type="enum" elements="1" options="Channel1,Channel2,Channel3,Channel4,Channel5,Channel6,Channel7,Channel8,Channel9,Channel10,None" defaultvalue="None"/>
<field name="ChannelUpdateFreq" units="Hz" type="uint16" elements="4" defaultvalue="50"/>
<field name="ChannelMax" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelNeutral" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelMin" units="us" type="int16" elements="10" defaultvalue="1000"/>
<field name="ChannelType" units="" type="enum" elements="10" options="PWM,MK,ASTEC4,PWM Alarm Buzzer" defaultvalue="PWM"/>
<field name="ChannelAddr" units="" type="uint8" elements="10" defaultvalue="0,1,2,3,4,5,6,7,8,9"/>
<field name="MotorsSpinWhileArmed" units="" type="enum" elements="1" options="FALSE,TRUE" defaultvalue="FALSE"/>
<access gcs="readwrite" flight="readwrite"/>
<telemetrygcs acked="true" updatemode="onchange" period="0"/>
<telemetryflight acked="true" updatemode="onchange" period="0"/>
<logging updatemode="never" period="0"/>
</object>
</xml>

View File

@ -9,6 +9,8 @@
<field name="Mixer6" units="" type="float" elements="1"/>
<field name="Mixer7" units="" type="float" elements="1"/>
<field name="Mixer8" units="" type="float" elements="1"/>
<field name="Mixer9" units="" type="float" elements="1"/>
<field name="Mixer10" units="" type="float" elements="1"/>
<access gcs="readwrite" flight="readwrite"/>
<telemetrygcs acked="false" updatemode="manual" period="0"/>
<telemetryflight acked="false" updatemode="periodic" period="1000"/>

View File

@ -1,7 +1,7 @@
<xml>
<object name="SystemSettings" singleinstance="true" settings="true">
<description>Select airframe type. Currently used by @ref ActuatorModule to choose mixing from @ref ActuatorDesired to @ref ActuatorCommand</description>
<field name="AirframeType" units="" type="enum" elements="1" options="FixedWing,FixedWingElevon,FixedWingVtail,VTOL,HeliCP,QuadX,QuadP,Hexa,Octo,Custom,HexaX,OctoV,OctoCoaxP,OctoCoaxX,HexaCoax,Tri" defaultvalue="FixedWing"/>
<field name="AirframeType" units="" type="enum" elements="1" options="FixedWing,FixedWingElevon,FixedWingVtail,VTOL,HeliCP,QuadX,QuadP,Hexa,Octo,Custom,HexaX,OctoV,OctoCoaxP,OctoCoaxX,HexaCoax,Tri,GroundVehicleCar,GroundVehicleDifferential,GroundVehicleMotorcycle" defaultvalue="FixedWing"/>
<field name="GUIConfigData" units="bits" type="uint32" elements="2" defaultvalue="0"/>
<access gcs="readwrite" flight="readwrite"/>
<telemetrygcs acked="true" updatemode="onchange" period="0"/>