diff --git a/.gitignore b/.gitignore index 0deef206c..e2c9b04d7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ core # ground openpilotgcs-build-desktop +ground/openpilotgcs/.cproject +ground/openpilotgcs/.project +ground/openpilotgcs/.settings # Ignore some of the .pro.user files *.pro.user diff --git a/Makefile b/Makefile index 008d1176c..2f17cfc69 100644 --- a/Makefile +++ b/Makefile @@ -747,7 +747,7 @@ ifneq ($(strip $(filter package clean_package,$(MAKECMDGOALS))),) # Packaged GCS should depend on opfw_resource ifneq ($(strip $(filter package clean_package,$(MAKECMDGOALS))),) - $(eval openpilotgcs: $(OPFW_RESOURCE)) + $(eval openpilotgcs_qmake: $(OPFW_RESOURCE)) endif # Clean the build directory if clean_package is requested diff --git a/flight/libraries/alarms.c b/flight/libraries/alarms.c index fc96aa2ce..8805ee8a6 100644 --- a/flight/libraries/alarms.c +++ b/flight/libraries/alarms.c @@ -254,6 +254,33 @@ static int32_t hasSeverity(SystemAlarmsAlarmOptions severity) xSemaphoreGiveRecursive(lock); return 0; } +/** + * Get the highest alarm severity + * @return + */ +SystemAlarmsAlarmOptions AlarmsGetHighestSeverity() +{ + SystemAlarmsAlarmData alarmsData; + SystemAlarmsAlarmOptions highest = SYSTEMALARMS_ALARM_UNINITIALISED; + + // Lock + xSemaphoreTakeRecursive(lock, portMAX_DELAY); + + // Read alarms + SystemAlarmsAlarmGet(&alarmsData); + + // Go through alarms and find the highest severity + uint32_t n = 0; + while (n < SYSTEMALARMS_ALARM_NUMELEM && highest != SYSTEMALARMS_ALARM_CRITICAL) { + if (cast_struct_to_array(alarmsData, alarmsData.Actuator)[n] > highest) { + highest = cast_struct_to_array(alarmsData, alarmsData.Actuator)[n]; + } + n++; + } + + xSemaphoreGiveRecursive(lock); + return highest; +} /** * @} diff --git a/flight/libraries/inc/alarms.h b/flight/libraries/inc/alarms.h index 1d7a4ed3b..304916dbc 100644 --- a/flight/libraries/inc/alarms.h +++ b/flight/libraries/inc/alarms.h @@ -42,9 +42,11 @@ int32_t AlarmsDefault(SystemAlarmsAlarmElem alarm); void AlarmsDefaultAll(); int32_t AlarmsClear(SystemAlarmsAlarmElem alarm); void AlarmsClearAll(); + int32_t AlarmsHasWarnings(); int32_t AlarmsHasErrors(); int32_t AlarmsHasCritical(); +SystemAlarmsAlarmOptions AlarmsGetHighestSeverity(); #endif // ALARMS_H diff --git a/flight/libraries/inc/notification.h b/flight/libraries/inc/notification.h new file mode 100644 index 000000000..2053a9609 --- /dev/null +++ b/flight/libraries/inc/notification.h @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * + * @file notification.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief notification library + * -- + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef NOTIFICATION_H +#define NOTIFICATION_H + +// period of each blink phase +#define LED_BLINK_PERIOD_MS 200 + +// update the status snapshot used by notifcations +void NotificationUpdateStatus(); + +// run the led notifications +void NotificationOnboardLedsRun(); + +#endif /* NOTIFICATION_H */ diff --git a/flight/libraries/notification.c b/flight/libraries/notification.c new file mode 100644 index 000000000..9015e4dd3 --- /dev/null +++ b/flight/libraries/notification.c @@ -0,0 +1,296 @@ +/** + ****************************************************************************** + * + * @file notification.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief notification library. + * -- + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 "inc/notification.h" +#include +#include +#include +#include +#include + +#ifdef PIOS_LED_ALARM +#define ALARM_LED_ON() PIOS_LED_On(PIOS_LED_ALARM) +#define ALARM_LED_OFF() PIOS_LED_Off(PIOS_LED_ALARM) +#else +#define ALARM_LED_ON() +#define ALARM_LED_OFF() +#endif + +#ifdef PIOS_LED_HEARTBEAT +#define HEARTBEAT_LED_ON() PIOS_LED_On(PIOS_LED_HEARTBEAT) +#define HEARTBEAT_LED_OFF() PIOS_LED_Off(PIOS_LED_HEARTBEAT) +#else +#define HEARTBEAT_LED_ON() +#define HEARTBEAT_LED_OFF() +#endif + +#define BLINK_R_ALARM_PATTERN(x) \ + (x == SYSTEMALARMS_ALARM_OK ? 0 : \ + x == SYSTEMALARMS_ALARM_WARNING ? 0b0000000001000000 : \ + x == SYSTEMALARMS_ALARM_ERROR ? 0b0000001000100000 : \ + x == SYSTEMALARMS_ALARM_CRITICAL ? 0b0111111111111110 : 0) +#define BLINK_B_ALARM_PATTERN(x) \ + (x == SYSTEMALARMS_ALARM_OK ? 0 : \ + x == SYSTEMALARMS_ALARM_WARNING ? 0 : \ + x == SYSTEMALARMS_ALARM_ERROR ? 0 : \ + x == SYSTEMALARMS_ALARM_CRITICAL ? 0 : 0) + + +#define BLINK_B_FM_ARMED_PATTERN(x) \ + (x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED1 ? 0b0000000000000001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED2 ? 0b0000000000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED3 ? 0b0000010000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED4 ? 0b0000000000000001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED5 ? 0b0000000000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED6 ? 0b0000010000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD ? 0b0000010000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE ? 0b0001000100010001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_LAND ? 0b0001000100010001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER ? 0b0000010000100001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POI ? 0b0000010000100001 : 0b0000000000000001) + +#define BLINK_R_FM_ARMED_PATTERN(x) \ + (x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED1 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED2 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED3 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED4 ? 0b0000000000000001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED5 ? 0b0000000000000001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED6 ? 0b0000000000000001 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD ? 0b0000010000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE ? 0b0001000100000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_LAND ? 0b0001000100000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER ? 0b0000010000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POI ? 0b0000010000000000 : 0b0000010000000000) + +#define BLINK_B_FM_DISARMED_PATTERN(x) \ + (x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED1 ? 0b0000000000000011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED2 ? 0b0000000001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED3 ? 0b0000110001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED4 ? 0b0000000000000011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED5 ? 0b0000000001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED6 ? 0b0000110001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD ? 0b0000110001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE ? 0b0011001100110011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_LAND ? 0b0011001100110011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER ? 0b0000110001100011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POI ? 0b0000110001100011 : 0b0000000000000011) + +#define BLINK_R_FM_DISARMED_PATTERN(x) \ + (x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED1 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED2 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED3 ? 0b0000000000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED4 ? 0b0000000000000011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED5 ? 0b0000000000000011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_STABILIZED6 ? 0b0000000000000011 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD ? 0b0000110000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE ? 0b0011001100000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_LAND ? 0b0011001100000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER ? 0b0000110000000000 : \ + x == FLIGHTSTATUS_FLIGHTMODE_POI ? 0b0000110000000000 : 0b0000110000000000) + +#define BLINK_B_HEARTBEAT_PATTERN 0b0001111111111111 +#define BLINK_R_HEARTBEAT_PATTERN 0 + +#define BLINK_B_NOTIFY_PATTERN(x) \ + (x == NOTIFY_NONE ? 0 : \ + x == NOTIFY_OK ? 0b0000100100111111 : \ + x == NOTIFY_NOK ? 0b0000000000111111 : \ + x == NOTIFY_DRAW_ATTENTION ? 0b0101010101010101 : 0b0101010101010101) + +#define BLINK_R_NOTIFY_PATTERN(x) \ + (x == NOTIFY_NONE ? 0 : \ + x == NOTIFY_OK ? 0b0000000000001111 : \ + x == NOTIFY_NOK ? 0b0011000011001111 : \ + x == NOTIFY_DRAW_ATTENTION ? 0b1010101010101010 : 0b1010101010101010) + +// led notification handling +static volatile SystemAlarmsAlarmOptions currentAlarmLevel = SYSTEMALARMS_ALARM_OK; +static volatile FlightStatusData currentFlightStatus; +static volatile bool started = false; +static volatile pios_notify_notification nextNotification = NOTIFY_NONE; + +#ifdef PIOS_LED_ALARM +static bool handleAlarms(uint16_t *r_pattern, uint16_t *b_pattern); +#endif // PIOS_LED_ALARM +static bool handleNotifications(pios_notify_notification runningNotification, uint16_t *r_pattern, uint16_t *b_pattern); +static void handleFlightMode(uint16_t *r_pattern, uint16_t *b_pattern); +static void handleHeartbeat(uint16_t *r_pattern, uint16_t *b_pattern); +void NotificationUpdateStatus() +{ + started = true; + // get values to be used for led handling + FlightStatusGet((FlightStatusData *)¤tFlightStatus); + currentAlarmLevel = AlarmsGetHighestSeverity(); + if (nextNotification == NOTIFY_NONE) { + nextNotification = PIOS_NOTIFY_GetActiveNotification(true); + } +} + +void NotificationOnboardLedsRun() +{ + static portTickType lastRunTimestamp; + static uint16_t r_pattern; + static uint16_t b_pattern; + static uint8_t cycleCount; // count the number of cycles + static uint8_t lastFlightMode = -1; + static bool forceShowFlightMode = false; + static pios_notify_notification runningNotification = NOTIFY_NONE; + static enum { + STATUS_NOTIFY, + STATUS_ALARM, + STATUS_FLIGHTMODE, // flightMode/HeartBeat + STATUS_LENGHT + } status; + + if (!started || (xTaskGetTickCount() - lastRunTimestamp) < (LED_BLINK_PERIOD_MS * portTICK_RATE_MS / 4)) { + return; + } + + lastRunTimestamp = xTaskGetTickCount(); + // the led will show various status information, subdivided in three phases + // - Notification + // - Alarm + // - Flight status + // they are shown using the above priority + // a phase last exactly 8 cycles (so bit 1<<4 is used to determine if a phase end + + cycleCount++; + // Notifications are "modal" to other statuses so they takes precedence + if (status != STATUS_NOTIFY && nextNotification != NOTIFY_NONE) { + // read next notification to show + runningNotification = nextNotification; + nextNotification = NOTIFY_NONE; + // Force a notification status + status = STATUS_NOTIFY; + cycleCount = 0; // instantly start a notify cycle + } else { + if (lastFlightMode != currentFlightStatus.FlightMode) { + status = STATUS_FLIGHTMODE; + lastFlightMode = currentFlightStatus.FlightMode; + cycleCount = 0; // instantly start a flightMode cycle + forceShowFlightMode = true; + } + } + + // check if a phase has just finished + if (cycleCount & 0x10) { + HEARTBEAT_LED_OFF(); + ALARM_LED_OFF(); + cycleCount = 0x0; + forceShowFlightMode = false; + // Notification has been just shown, cleanup + if (status == STATUS_NOTIFY) { + runningNotification = NOTIFY_NONE; + } + status = (status + 1) % STATUS_LENGHT; + } + + if (status == STATUS_NOTIFY) { + if (!cycleCount && !handleNotifications(runningNotification, &r_pattern, &b_pattern)) { + // no notifications, advance + status++; + } + } + + // Handles Alarm display + if (status == STATUS_ALARM) { +#ifdef PIOS_LED_ALARM + if (!cycleCount && !handleAlarms(&r_pattern, &b_pattern)) { + // no alarms, advance + status++; + } +#else + // no alarms leds, advance + status++; +#endif // PIOS_LED_ALARM + } + + // **** Handles flightmode display + if (status == STATUS_FLIGHTMODE && !cycleCount) { + if (forceShowFlightMode || currentFlightStatus.Armed != FLIGHTSTATUS_ARMED_DISARMED) { + handleFlightMode(&r_pattern, &b_pattern); + } else { + handleHeartbeat(&r_pattern, &b_pattern); + } + } + + // led output + if (b_pattern & 0x1) { + HEARTBEAT_LED_ON(); + } else { + HEARTBEAT_LED_OFF(); + } + if (r_pattern & 0x1) { + ALARM_LED_ON(); + } else { + ALARM_LED_OFF(); + } + r_pattern >>= 1; + b_pattern >>= 1; +} + +#if defined(PIOS_LED_ALARM) +static bool handleAlarms(uint16_t *r_pattern, uint16_t *b_pattern) +{ + if (currentAlarmLevel == SYSTEMALARMS_ALARM_OK) { + return false; + } + *b_pattern = BLINK_B_ALARM_PATTERN(currentAlarmLevel); + *r_pattern = BLINK_R_ALARM_PATTERN(currentAlarmLevel); + return true; +} +#endif /* PIOS_LED_ALARM */ + + +static bool handleNotifications(pios_notify_notification runningNotification, uint16_t *r_pattern, uint16_t *b_pattern) +{ + if (runningNotification == NOTIFY_NONE) { + return false; + } + *b_pattern = BLINK_B_NOTIFY_PATTERN(runningNotification); + *r_pattern = BLINK_R_NOTIFY_PATTERN(runningNotification); + return true; +} + +static void handleFlightMode(uint16_t *r_pattern, uint16_t *b_pattern) +{ + // Flash the heartbeat LED + uint8_t flightmode = currentFlightStatus.FlightMode; + + if (currentFlightStatus.Armed == FLIGHTSTATUS_ARMED_DISARMED) { + *b_pattern = BLINK_B_FM_DISARMED_PATTERN(flightmode); + *r_pattern = BLINK_R_FM_DISARMED_PATTERN(flightmode); + } else { + *b_pattern = BLINK_B_FM_ARMED_PATTERN(flightmode); + *r_pattern = BLINK_R_FM_ARMED_PATTERN(flightmode); + } +} + +static void handleHeartbeat(uint16_t *r_pattern, uint16_t *b_pattern) +{ + // Flash the heartbeat LED + *b_pattern = BLINK_B_HEARTBEAT_PATTERN; + *r_pattern = BLINK_R_HEARTBEAT_PATTERN; +} diff --git a/flight/modules/Airspeed/airspeed.c b/flight/modules/Airspeed/airspeed.c index 456131a90..7111bc5bd 100644 --- a/flight/modules/Airspeed/airspeed.c +++ b/flight/modules/Airspeed/airspeed.c @@ -60,7 +60,7 @@ static xTaskHandle taskHandle; static bool airspeedEnabled = false; static AirspeedSettingsData airspeedSettings; -static AirspeedSettingsAirspeedSensorTypeOptions lastAirspeedSensorType = 0; +static AirspeedSettingsAirspeedSensorTypeOptions lastAirspeedSensorType = -1; static int8_t airspeedADCPin = -1; @@ -119,8 +119,6 @@ int32_t AirspeedInitialize() } } - lastAirspeedSensorType = airspeedSettings.AirspeedSensorType; - AirspeedSensorInitialize(); AirspeedSettingsInitialize(); @@ -137,17 +135,14 @@ MODULE_INITCALL(AirspeedInitialize, AirspeedStart); static void airspeedTask(__attribute__((unused)) void *parameters) { AirspeedSettingsUpdatedCb(AirspeedSettingsHandle()); - + bool imuAirspeedInitialized = false; AirspeedSensorData airspeedData; AirspeedSensorGet(&airspeedData); AirspeedSettingsUpdatedCb(NULL); - imu_airspeedInitialize(); - airspeedData.SensorConnected = AIRSPEEDSENSOR_SENSORCONNECTED_FALSE; - // Main task loop portTickType lastSysTime = xTaskGetTickCount(); while (1) { @@ -160,8 +155,21 @@ static void airspeedTask(__attribute__((unused)) void *parameters) if (airspeedSettings.AirspeedSensorType != lastAirspeedSensorType) { AlarmsSet(SYSTEMALARMS_ALARM_AIRSPEED, SYSTEMALARMS_ALARM_DEFAULT); lastAirspeedSensorType = airspeedSettings.AirspeedSensorType; + switch (airspeedSettings.AirspeedSensorType) { + case AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_NONE: + // AirspeedSensor will not be updated until a different sensor is selected + // set the disconencted satus now + airspeedData.SensorConnected = AIRSPEEDSENSOR_SENSORCONNECTED_FALSE; + AirspeedSensorSet(&airspeedData); + break; + case AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_GROUNDSPEEDBASEDWINDESTIMATION: + if (!imuAirspeedInitialized) { + imuAirspeedInitialized = true; + imu_airspeedInitialize(); + } + break; + } } - switch (airspeedSettings.AirspeedSensorType) { #if defined(PIOS_INCLUDE_MPXV) case AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_DIYDRONESMPXV7002: @@ -186,6 +194,10 @@ static void airspeedTask(__attribute__((unused)) void *parameters) imu_airspeedGet(&airspeedData, &airspeedSettings); break; case AIRSPEEDSETTINGS_AIRSPEEDSENSORTYPE_NONE: + // no need to check so often until a sensor is enabled + AlarmsSet(SYSTEMALARMS_ALARM_AIRSPEED, SYSTEMALARMS_ALARM_DEFAULT); + vTaskDelay(2000 / portTICK_RATE_MS); + continue; default: airspeedData.SensorConnected = AIRSPEEDSENSOR_SENSORCONNECTED_FALSE; break; diff --git a/flight/modules/Attitude/attitude.c b/flight/modules/Attitude/attitude.c index 85e18a8b0..83103750e 100644 --- a/flight/modules/Attitude/attitude.c +++ b/flight/modules/Attitude/attitude.c @@ -63,7 +63,7 @@ #include "taskinfo.h" #include "CoordinateConversions.h" - +#include // Private constants #define STACK_SIZE_BYTES 540 @@ -252,6 +252,7 @@ static void AttitudeTask(__attribute__((unused)) void *parameters) rollPitchBiasRate = 0.01f; accel_filter_enabled = false; init = 0; + PIOS_NOTIFY_StartNotification(NOTIFY_DRAW_ATTENTION, NOTIFY_PRIORITY_REGULAR); } else if (zero_during_arming && (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMING)) { accelKp = 1.0f; accelKi = 0.0f; @@ -259,6 +260,7 @@ static void AttitudeTask(__attribute__((unused)) void *parameters) rollPitchBiasRate = 0.01f; accel_filter_enabled = false; init = 0; + PIOS_NOTIFY_StartNotification(NOTIFY_DRAW_ATTENTION, NOTIFY_PRIORITY_REGULAR); } else if (init == 0) { // Reload settings (all the rates) AttitudeSettingsAccelKiGet(&accelKi); diff --git a/flight/modules/GPS/GPS.c b/flight/modules/GPS/GPS.c index 420f9599c..bdbad1f69 100644 --- a/flight/modules/GPS/GPS.c +++ b/flight/modules/GPS/GPS.c @@ -63,21 +63,24 @@ static float GravityAccel(float latitude, float longitude, float altitude); // **************** // Private constants -#define GPS_TIMEOUT_MS 500 - +#define GPS_TIMEOUT_MS 500 +// delay from detecting HomeLocation.Set == False before setting new homelocation +// this prevent that a save with homelocation.Set = false triggered by gps ends saving +// the new location with Set = true. +#define GPS_HOMELOCATION_SET_DELAY 5000 #ifdef PIOS_GPS_SETS_HOMELOCATION // Unfortunately need a good size stack for the WMM calculation - #define STACK_SIZE_BYTES 1024 + #define STACK_SIZE_BYTES 1024 #else #if defined(PIOS_GPS_MINIMAL) - #define STACK_SIZE_BYTES 500 + #define STACK_SIZE_BYTES 500 #else - #define STACK_SIZE_BYTES 650 + #define STACK_SIZE_BYTES 650 #endif // PIOS_GPS_MINIMAL #endif // PIOS_GPS_SETS_HOMELOCATION -#define TASK_PRIORITY (tskIDLE_PRIORITY + 1) +#define TASK_PRIORITY (tskIDLE_PRIORITY + 1) // **************** // Private variables @@ -200,6 +203,9 @@ static void gpsTask(__attribute__((unused)) void *parameters) portTickType xDelay = 100 / portTICK_RATE_MS; uint32_t timeNowMs = xTaskGetTickCount() * portTICK_RATE_MS; +#ifdef PIOS_GPS_SETS_HOMELOCATION + portTickType homelocationSetDelay = 0; +#endif GPSPositionSensorData gpspositionsensor; GPSSettingsData gpsSettings; @@ -260,7 +266,15 @@ static void gpsTask(__attribute__((unused)) void *parameters) HomeLocationGet(&home); if (home.Set == HOMELOCATION_SET_FALSE) { - setHomeLocation(&gpspositionsensor); + if (homelocationSetDelay == 0) { + homelocationSetDelay = xTaskGetTickCount(); + } + if (xTaskGetTickCount() - homelocationSetDelay > GPS_HOMELOCATION_SET_DELAY) { + setHomeLocation(&gpspositionsensor); + homelocationSetDelay = 0; + } + } else { + homelocationSetDelay = 0; } #endif } else if ((gpspositionsensor.Status == GPSPOSITIONSENSOR_STATUS_FIX3D) && diff --git a/flight/modules/ManualControl/armhandler.c b/flight/modules/ManualControl/armhandler.c index b16f1e39b..76833678e 100644 --- a/flight/modules/ManualControl/armhandler.c +++ b/flight/modules/ManualControl/armhandler.c @@ -274,6 +274,9 @@ static bool okToArm(void) case FLIGHTSTATUS_FLIGHTMODE_STABILIZED1: case FLIGHTSTATUS_FLIGHTMODE_STABILIZED2: case FLIGHTSTATUS_FLIGHTMODE_STABILIZED3: + case FLIGHTSTATUS_FLIGHTMODE_STABILIZED4: + case FLIGHTSTATUS_FLIGHTMODE_STABILIZED5: + case FLIGHTSTATUS_FLIGHTMODE_STABILIZED6: return true; break; diff --git a/flight/modules/StateEstimation/filtercf.c b/flight/modules/StateEstimation/filtercf.c index 992eef8ef..0daea0928 100644 --- a/flight/modules/StateEstimation/filtercf.c +++ b/flight/modules/StateEstimation/filtercf.c @@ -42,7 +42,7 @@ #include #include - +#include // Private constants #define STACK_REQUIRED 512 @@ -295,6 +295,7 @@ static int32_t complementaryFilter(struct data *this, float gyro[3], float accel this->accel_filter_enabled = false; this->rollPitchBiasRate = 0.01f; this->attitudeSettings.MagKp = this->magCalibrated ? 1.0f : 0.0f; + PIOS_NOTIFY_StartNotification(NOTIFY_DRAW_ATTENTION, NOTIFY_PRIORITY_REGULAR); } else if ((this->attitudeSettings.ZeroDuringArming == ATTITUDESETTINGS_ZERODURINGARMING_TRUE) && (flightStatus.Armed == FLIGHTSTATUS_ARMED_ARMING)) { this->attitudeSettings.AccelKp = 1.0f; this->attitudeSettings.AccelKi = 0.0f; @@ -303,6 +304,7 @@ static int32_t complementaryFilter(struct data *this, float gyro[3], float accel this->rollPitchBiasRate = 0.01f; this->attitudeSettings.MagKp = this->magCalibrated ? 1.0f : 0.0f; this->init = 0; + PIOS_NOTIFY_StartNotification(NOTIFY_DRAW_ATTENTION, NOTIFY_PRIORITY_REGULAR); } else if (this->init == 0) { // Reload settings (all the rates) AttitudeSettingsGet(&this->attitudeSettings); diff --git a/flight/modules/System/systemmod.c b/flight/modules/System/systemmod.c index 648e2d3a0..13bf7b264 100644 --- a/flight/modules/System/systemmod.c +++ b/flight/modules/System/systemmod.c @@ -42,7 +42,7 @@ #include // private includes #include "inc/systemmod.h" - +#include "notification.h" // UAVOs #include @@ -72,22 +72,15 @@ #endif // Private constants -#define SYSTEM_UPDATE_PERIOD_MS 1000 -#define LED_BLINK_RATE_HZ 5 - -#ifndef IDLE_COUNTS_PER_SEC_AT_NO_LOAD -#define IDLE_COUNTS_PER_SEC_AT_NO_LOAD 995998 // calibrated by running tests/test_cpuload.c -// must be updated if the FreeRTOS or compiler -// optimisation options are changed. -#endif +#define SYSTEM_UPDATE_PERIOD_MS 250 #if defined(PIOS_SYSTEM_STACK_SIZE) -#define STACK_SIZE_BYTES PIOS_SYSTEM_STACK_SIZE +#define STACK_SIZE_BYTES PIOS_SYSTEM_STACK_SIZE #else -#define STACK_SIZE_BYTES 1024 +#define STACK_SIZE_BYTES 1024 #endif -#define TASK_PRIORITY (tskIDLE_PRIORITY + 1) +#define TASK_PRIORITY (tskIDLE_PRIORITY + 1) // Private types @@ -98,6 +91,7 @@ static enum { STACKOVERFLOW_NONE = 0, STACKOVERFLOW_WARNING = 1, STACKOVERFLOW_C static bool mallocFailed; static HwSettingsData bootHwSettings; static struct PIOS_FLASHFS_Stats fsStats; + // Private functions static void objectUpdatedCb(UAVObjEvent *ev); static void hwSettingsUpdatedCb(UAVObjEvent *ev); @@ -170,8 +164,6 @@ MODULE_INITCALL(SystemModInitialize, 0); */ static void systemTask(__attribute__((unused)) void *parameters) { - uint8_t cycleCount = 0; - /* create all modules thread */ MODULE_TASKCREATE_ALL; @@ -189,9 +181,6 @@ static void systemTask(__attribute__((unused)) void *parameters) /* Record a successful boot */ PIOS_IAP_WriteBootCount(0); #endif - - // Initialize vars - // Listen for SettingPersistance object updates, connect a callback function ObjectPersistenceConnectQueue(objectPersistenceQueue); @@ -204,13 +193,10 @@ static void systemTask(__attribute__((unused)) void *parameters) TaskInfoData taskInfoData; CallbackInfoData callbackInfoData; #endif - // Main system loop while (1) { + NotificationUpdateStatus(); // Update the system statistics - - cycleCount = cycleCount > 0 ? cycleCount - 1 : 7; -// if(cycleCount == 1){ updateStats(); // Update the system alarms updateSystemAlarms(); @@ -230,35 +216,10 @@ static void systemTask(__attribute__((unused)) void *parameters) // } #endif // } - // Flash the heartbeat LED -#if defined(PIOS_LED_HEARTBEAT) - uint8_t armingStatus; - FlightStatusArmedGet(&armingStatus); - if ((armingStatus == FLIGHTSTATUS_ARMED_ARMED && (cycleCount & 0x1)) || - (armingStatus != FLIGHTSTATUS_ARMED_ARMED && (cycleCount & 0x4))) { - PIOS_LED_On(PIOS_LED_HEARTBEAT); - } else { - PIOS_LED_Off(PIOS_LED_HEARTBEAT); - } - - DEBUG_MSG("+ 0x%08x\r\n", 0xDEADBEEF); -#endif /* PIOS_LED_HEARTBEAT */ - - // Turn on the error LED if an alarm is set -#if defined(PIOS_LED_ALARM) - if (AlarmsHasCritical()) { - PIOS_LED_On(PIOS_LED_ALARM); - } else if ((AlarmsHasErrors() && (cycleCount & 0x1)) || - (!AlarmsHasErrors() && AlarmsHasWarnings() && (cycleCount & 0x4))) { - PIOS_LED_On(PIOS_LED_ALARM); - } else { - PIOS_LED_Off(PIOS_LED_ALARM); - } -#endif /* PIOS_LED_ALARM */ UAVObjEvent ev; - int delayTime = SYSTEM_UPDATE_PERIOD_MS / portTICK_RATE_MS / (LED_BLINK_RATE_HZ * 2); + int delayTime = SYSTEM_UPDATE_PERIOD_MS; #if defined(PIOS_INCLUDE_RFM22B) @@ -633,7 +594,7 @@ static void updateSystemAlarms() UAVObjClearStats(); EventClearStats(); if (objStats.eventCallbackErrors > 0 || objStats.eventQueueErrors > 0 || evStats.eventErrors > 0) { - AlarmsSet(SYSTEMALARMS_ALARM_EVENTSYSTEM, SYSTEMALARMS_ALARM_WARNING); + AlarmsSet(SYSTEMALARMS_ALARM_EVENTSYSTEM, SYSTEMALARMS_ALARM_ERROR); } else { AlarmsClear(SYSTEMALARMS_ALARM_EVENTSYSTEM); } @@ -649,11 +610,12 @@ static void updateSystemAlarms() } /** - * Called by the RTOS when the CPU is idle, used to measure the CPU idle time. + * Called by the RTOS when the CPU is idle, */ void vApplicationIdleHook(void) -{} - +{ + NotificationOnboardLedsRun(); +} /** * Called by the RTOS when a stack overflow is detected. */ diff --git a/flight/modules/Telemetry/telemetry.c b/flight/modules/Telemetry/telemetry.c index 153ca7d41..3a969635b 100644 --- a/flight/modules/Telemetry/telemetry.c +++ b/flight/modules/Telemetry/telemetry.c @@ -661,11 +661,10 @@ static void updateTelemetryStats() flightStats.Status = FLIGHTTELEMETRYSTATS_STATUS_DISCONNECTED; } - // Update the telemetry alarm + // TODO: check whether is there any error condition worth raising an alarm + // Disconnection is actually a normal (non)working status so it is not raising alarms anymore. if (flightStats.Status == FLIGHTTELEMETRYSTATS_STATUS_CONNECTED) { AlarmsClear(SYSTEMALARMS_ALARM_TELEMETRY); - } else { - AlarmsSet(SYSTEMALARMS_ALARM_TELEMETRY, SYSTEMALARMS_ALARM_ERROR); } // Update object diff --git a/flight/pios/common/pios_notify.c b/flight/pios/common/pios_notify.c new file mode 100644 index 000000000..bf83c22eb --- /dev/null +++ b/flight/pios/common/pios_notify.c @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * + * @file pios_notify.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief Handles user notifications. + * -- + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 "pios_notify.h" + +static volatile pios_notify_notification currentNotification = NOTIFY_NONE; +static volatile pios_notify_priority currentPriority; + + +void PIOS_NOTIFY_StartNotification(pios_notify_notification notification, pios_notify_priority priority) +{ + if (currentNotification == NOTIFY_NONE || currentPriority < priority) { + currentPriority = priority; + currentNotification = notification; + } +} + +pios_notify_notification PIOS_NOTIFY_GetActiveNotification(bool clear) +{ + pios_notify_notification ret = currentNotification; + + if (clear && ret != NOTIFY_NONE) { + currentNotification = NOTIFY_NONE; + } + return ret; +} diff --git a/flight/pios/inc/pios_notify.h b/flight/pios/inc/pios_notify.h new file mode 100644 index 000000000..1e800df5c --- /dev/null +++ b/flight/pios/inc/pios_notify.h @@ -0,0 +1,58 @@ +/** + ****************************************************************************** + * + * @file pios_notify.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief Handles user notifications. + * -- + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef PIOS_NOTIFY_H_ +#define PIOS_NOTIFY_H_ +#include + +typedef enum { + NOTIFY_NONE = 0, + NOTIFY_OK, + NOTIFY_NOK, + NOTIFY_DRAW_ATTENTION +} pios_notify_notification; + +typedef enum { + NOTIFY_PRIORITY_CRITICAL = 2, + NOTIFY_PRIORITY_REGULAR = 1, + NOTIFY_PRIORITY_LOW = 0, +} pios_notify_priority; + +/** + * start a new notification. If a notification is active it will be overwritten if its priority is lower than the new one. + * The new will be discarded otherwise + * @param notification kind of notification + * @param priority priority of the new notification + */ +void PIOS_NOTIFY_StartNotification(pios_notify_notification notification, pios_notify_priority priority); + +/** + * retrieve any active notification + * @param clear + * @return + */ +pios_notify_notification PIOS_NOTIFY_GetActiveNotification(bool clear); + +#endif /* PIOS_NOTIFY_H_ */ diff --git a/flight/pios/inc/pios_ws2811.h b/flight/pios/inc/pios_ws2811.h new file mode 100644 index 000000000..cb05070ee --- /dev/null +++ b/flight/pios/inc/pios_ws2811.h @@ -0,0 +1,147 @@ +/** + ****************************************************************************** + * + * @file pios_ws2811.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief A driver for ws2811 rgb led controller. + * this is a plain PiOS port of the very clever solution + * implemented by Omri Iluz in the chibios driver here: + * https://github.com/omriiluz/WS2812B-LED-Driver-ChibiOS + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 + */ +#ifndef PIOS_WS2811_H_ +#define PIOS_WS2811_H_ + +#include +#include +#include +#include +#include +#include +#include + + +#define sign(x) ((x > 0) - (x < 0)) +#define PIOS_WS2811_NUMLEDS 2 +#define PIOS_WS2811_BUFFER_SIZE (((PIOS_WS2811_NUMLEDS) * 24)) +#define PIOS_WS2811_MEMORYDATASIZE DMA_MemoryDataSize_HalfWord +#define PIOS_WS2811_PERIPHERALDATASIZE DMA_PeripheralDataSize_HalfWord +#define PIOS_WS2811_TIM_PERIOD 20 + +// Following times are keept on the lower side to accounts +// for bus contentions and irq response time +#define PIOS_WS2811_T0_HIGH_PERIOD 25 // .35us +/- 150nS +#define PIOS_WS2811_T1_HIGH_PERIOD 60 // .70us +/- 150nS + +#define PIOS_WS2811_DMA_CH1_CONFIG(channel) \ + { \ + .DMA_BufferSize = PIOS_WS2811_BUFFER_SIZE, \ + .DMA_Channel = channel, \ + .DMA_DIR = DMA_DIR_MemoryToPeripheral, \ + .DMA_FIFOMode = DMA_FIFOMode_Enable, \ + .DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull, \ + .DMA_Memory0BaseAddr = 0, \ + .DMA_MemoryBurst = DMA_MemoryBurst_INC4, \ + .DMA_MemoryDataSize = PIOS_WS2811_MEMORYDATASIZE, \ + .DMA_MemoryInc = DMA_MemoryInc_Enable, \ + .DMA_Mode = DMA_Mode_Circular, \ + .DMA_PeripheralBaseAddr = 0, \ + .DMA_PeripheralBurst = DMA_PeripheralBurst_Single, \ + .DMA_PeripheralDataSize = PIOS_WS2811_PERIPHERALDATASIZE, \ + .DMA_PeripheralInc = DMA_PeripheralInc_Disable, \ + .DMA_Priority = DMA_Priority_VeryHigh, } + +#define PIOS_WS2811_DMA_CH2_CONFIG(channel) \ + { \ + .DMA_BufferSize = 4, \ + .DMA_Channel = channel, \ + .DMA_DIR = DMA_DIR_MemoryToPeripheral, \ + .DMA_FIFOMode = DMA_FIFOMode_Enable, \ + .DMA_FIFOThreshold = DMA_FIFOThreshold_Full, \ + .DMA_Memory0BaseAddr = 0, \ + .DMA_MemoryBurst = DMA_MemoryBurst_INC4, \ + .DMA_MemoryDataSize = PIOS_WS2811_MEMORYDATASIZE, \ + .DMA_MemoryInc = DMA_MemoryInc_Enable, \ + .DMA_Mode = DMA_Mode_Circular, \ + .DMA_PeripheralBaseAddr = 0, \ + .DMA_PeripheralBurst = DMA_PeripheralBurst_Single, \ + .DMA_PeripheralDataSize = PIOS_WS2811_PERIPHERALDATASIZE, \ + .DMA_PeripheralInc = DMA_PeripheralInc_Disable, \ + .DMA_Priority = DMA_Priority_VeryHigh, } + +#define PIOS_WS2811_DMA_UPDATE_CONFIG(channel) \ + { \ + .DMA_BufferSize = 4, \ + .DMA_Channel = channel, \ + .DMA_DIR = DMA_DIR_MemoryToPeripheral, \ + .DMA_FIFOMode = DMA_FIFOMode_Enable, \ + .DMA_FIFOThreshold = DMA_FIFOThreshold_Full, \ + .DMA_Memory0BaseAddr = 0, \ + .DMA_MemoryBurst = DMA_MemoryBurst_INC4, \ + .DMA_MemoryDataSize = PIOS_WS2811_MEMORYDATASIZE, \ + .DMA_MemoryInc = DMA_MemoryInc_Enable, \ + .DMA_Mode = DMA_Mode_Circular, \ + .DMA_PeripheralBaseAddr = 0, \ + .DMA_PeripheralBurst = DMA_PeripheralBurst_Single, \ + .DMA_PeripheralDataSize = PIOS_WS2811_PERIPHERALDATASIZE, \ + .DMA_PeripheralInc = DMA_PeripheralInc_Disable, \ + .DMA_Priority = DMA_Priority_High } + + +typedef uint16_t ledbuf_t; + +typedef struct Color Color; +struct Color { + uint8_t R; + uint8_t G; + uint8_t B; +}; +struct pios_ws2811_pin_cfg { + GPIO_TypeDef *gpio; + GPIO_InitTypeDef gpioInit; +}; +struct pios_ws2811_cfg { + TIM_TimeBaseInitTypeDef timerInit; + TIM_TypeDef *timer; + uint8_t timerCh1; + uint8_t timerCh2; + + DMA_InitTypeDef dmaInitCh1; + DMA_Stream_TypeDef *streamCh1; + uint32_t dmaItCh1; + + DMA_InitTypeDef dmaInitCh2; + DMA_Stream_TypeDef *streamCh2; + uint32_t dmaItCh2; + + DMA_InitTypeDef dmaInitUpdate; + DMA_Stream_TypeDef *streamUpdate; + uint32_t dmaItUpdate; + uint16_t dmaSource; + struct stm32_irq irq; +}; + +void PIOS_WS2811_Init(const struct pios_ws2811_cfg *ws2811_cfg, const struct pios_ws2811_pin_cfg *ws2811_pin_cfg); + +void PIOS_WS2811_setColorRGB(Color c, uint8_t led, bool update); +void PIOS_WS2811_Update(); + +void PIOS_WS2811_DMA_irq_handler(); + +#endif /* PIOS_WS2811_H_ */ diff --git a/flight/pios/stm32f4xx/pios_ws2811.c b/flight/pios/stm32f4xx/pios_ws2811.c new file mode 100644 index 000000000..6faf1f7c6 --- /dev/null +++ b/flight/pios/stm32f4xx/pios_ws2811.c @@ -0,0 +1,344 @@ +/** + ****************************************************************************** + * + * @file pios_ws2811.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2014. + * @brief A driver for ws2811 rgb led controller. + * this is a plain PiOS port of the very clever solution + * implemented by Omri Iluz in the chibios driver here: + * https://github.com/omriiluz/WS2812B-LED-Driver-ChibiOS + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * 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 + +#ifdef PIOS_INCLUDE_WS2811 + +#include "pios_ws2811.h" +#include +#include +#include "FreeRTOS.h" +#include "task.h" + + +// framebuffer +static ledbuf_t *fb = 0; +// bitmask with pin to be set/reset using dma +static ledbuf_t dmaSource[4]; + +static const struct pios_ws2811_cfg *pios_ws2811_cfg; +static const struct pios_ws2811_pin_cfg *pios_ws2811_pin_cfg; + +static void setupTimer(); +static void setupDMA(); + +// generic wrapper around corresponding SPL functions +static void genericTIM_OCxInit(TIM_TypeDef *TIMx, const TIM_OCInitTypeDef *TIM_OCInitStruct, uint8_t ch); +static void genericTIM_OCxPreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload, uint8_t ch); + +// timer creates a 1.25 uS signal, with duty cycle controlled by frame buffer values + +/* Example configuration fragment for REVOLUTION + + #ifdef PIOS_INCLUDE_WS2811 + #include + #include + #define PIOS_WS2811_TIM_DIVIDER (PIOS_PERIPHERAL_APB2_CLOCK / (800000 * PIOS_WS2811_TIM_PERIOD)) + + // interrupt vector for DMA streamCh1 + void DMA2_Stream1_IRQHandler(void) __attribute__((alias("PIOS_WS2811_irq_handler"))); + + const struct pios_ws2811_pin_cfg pios_ws2811_pin_cfg[] = { + [HWSETTINGS_WS2811LED_OUT_SERVOOUT1] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_0, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + .... + [HWSETTINGS_WS2811LED_OUT_FLEXIPIN4] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_13, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + }; + + const struct pios_ws2811_cfg pios_ws2811_cfg = { + .timer = TIM1, + .timerInit = { + .TIM_Prescaler = PIOS_WS2811_TIM_DIVIDER - 1, + .TIM_ClockDivision = TIM_CKD_DIV1, + .TIM_CounterMode = TIM_CounterMode_Up, + // period (1.25 uS per period + .TIM_Period = PIOS_WS2811_TIM_PERIOD, + .TIM_RepetitionCounter = 0x0000, + }, + + .timerCh1 = 1, + .streamCh1 = DMA2_Stream1, + .timerCh2 = 3, + .streamCh2 = DMA2_Stream6, + .streamUpdate = DMA2_Stream5, + + // DMA streamCh1, triggered by timerCh1 pwm signal. + // if FrameBuffer indicates, reset output value early to indicate "0" bit to ws2812 + .dmaInitCh1 = PIOS_WS2811_DMA_UPDATE_CONFIG(DMA_Channel_6), + .dmaItCh1 = DMA_IT_TEIF1 | DMA_IT_TCIF1, + + // DMA streamCh2, triggered by timerCh2 pwm signal. + // Reset output value late to indicate "1" bit to ws2812. + .dmaInitCh2 = PIOS_WS2811_DMA_CH1_CONFIG(DMA_Channel_6), + .dmaItCh2 = DMA_IT_TEIF6 | DMA_IT_TCIF6, + + // DMA streamUpdate Triggered by timer update event + // Outputs a high logic level at beginning of a cycle + .dmaInitUpdate = PIOS_WS2811_DMA_CH2_CONFIG(DMA_Channel_6), + .dmaItUpdate = DMA_IT_TEIF5 | DMA_IT_TCIF5, + .dmaSource = TIM_DMA_CC1 | TIM_DMA_CC3 | TIM_DMA_Update, + + // DMA streamCh1 interrupt vector, used to block timer at end of framebuffer transfer + .irq = { + .flags = (DMA_IT_TCIF1), + .init = { + .NVIC_IRQChannel = DMA2_Stream1_IRQn, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGH, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, + }; + + void PIOS_WS2811_irq_handler(void) + { + PIOS_WS2811_DMA_irq_handler(); + } + #endif // PIOS_INCLUDE_WS2811 + + */ + +/* + * How it works: + * a timer and two channels will produce the timings events: + * timer period will be 1.25us + * Ch1 CC event will be raised at 0.40uS from the beginning of the cycle + * Ch2 CC event will be raised at 0.80uS from the beginning of the cycle + * At cycle init an Update event will be raised. + * + * Three dma streams will handle the output pin as following: + * - streamUpdate dma stream, triggered by update event will produce a logic 1 on the output pin + * - streamCh1 will bring the pin to 0 if framebuffer location is set to dmaSource value to send a "0" bit to WS281x + * - streamCh2 will bring pin to 0 once .8us are passed to send a "1" bit to ws281x + * Once StreamCh1 has finished to send the buffer the IRQ handler will stop the timer. + * + */ + +/** + * @brief Initialize WS2811 Led Driver + * @details Initialize the Led Driver based on passed configuration + * + * @param[in] ws2811_cfg ws2811 driver configuration + * @param[in] ws2811_pin_cfg pin to be used as output + * + */ + +void PIOS_WS2811_Init(const struct pios_ws2811_cfg *ws2811_cfg, const struct pios_ws2811_pin_cfg *ws2811_pin_cfg) +{ + assert_param(ws2811_cfg); + assert_param(ws2811_pin_cfg); + + pios_ws2811_pin_cfg = ws2811_pin_cfg; + pios_ws2811_cfg = ws2811_cfg; + GPIO_Init(pios_ws2811_pin_cfg->gpio, &pios_ws2811_pin_cfg->gpioInit); + for (uint8_t i = 0; i < 4; i++) { + dmaSource[i] = (ledbuf_t)pios_ws2811_pin_cfg->gpioInit.GPIO_Pin; + } + + fb = (ledbuf_t *)pvPortMalloc(PIOS_WS2811_BUFFER_SIZE * sizeof(ledbuf_t)); + memset(fb, 0, PIOS_WS2811_BUFFER_SIZE * sizeof(ledbuf_t)); + Color ledoff = { 0, 0, 0 }; + for (uint8_t i = 0; i < PIOS_WS2811_NUMLEDS; i++) { + PIOS_WS2811_setColorRGB(ledoff, i, false); + } + // Setup timers + setupTimer(); + setupDMA(); +} + +void setupTimer() +{ + // Stop timer + TIM_Cmd(pios_ws2811_cfg->timer, DISABLE); + // Configure timebase and internal clock + TIM_TimeBaseInit(pios_ws2811_cfg->timer, &pios_ws2811_cfg->timerInit); + TIM_InternalClockConfig(pios_ws2811_cfg->timer); + + genericTIM_OCxPreloadConfig(pios_ws2811_cfg->timer, TIM_OCPreload_Enable, pios_ws2811_cfg->timerCh1); + genericTIM_OCxPreloadConfig(pios_ws2811_cfg->timer, TIM_OCPreload_Enable, pios_ws2811_cfg->timerCh2); + TIM_ARRPreloadConfig(pios_ws2811_cfg->timer, ENABLE); + + // enable outputs + // TIM_CtrlPWMOutputs(pios_ws2811_cfg->timer, ENABLE); + + TIM_DMACmd(pios_ws2811_cfg->timer, pios_ws2811_cfg->dmaSource, ENABLE); + + TIM_OCInitTypeDef oc = { + .TIM_OCMode = TIM_OCMode_PWM1, + .TIM_OutputState = TIM_OutputState_Enable, + .TIM_OutputNState = TIM_OutputNState_Disable, + .TIM_Pulse = 0, + .TIM_OCPolarity = TIM_OCPolarity_High, + .TIM_OCNPolarity = TIM_OCNPolarity_High, + .TIM_OCIdleState = TIM_OCIdleState_Reset, + .TIM_OCNIdleState = TIM_OCNIdleState_Reset, + }; + + // (duty in ticks) / (period in ticks) * 1.25uS (period in S) = 0.40 uS + oc.TIM_Pulse = PIOS_WS2811_T0_HIGH_PERIOD * PIOS_WS2811_TIM_PERIOD / 125; + genericTIM_OCxInit(pios_ws2811_cfg->timer, &oc, pios_ws2811_cfg->timerCh1); + // (duty in ticks) / (period in ticks) * 1.25uS (period in S) = 0.80 uS + oc.TIM_Pulse = PIOS_WS2811_T1_HIGH_PERIOD * PIOS_WS2811_TIM_PERIOD / 125; + genericTIM_OCxInit(pios_ws2811_cfg->timer, &oc, pios_ws2811_cfg->timerCh2); +} + +void genericTIM_OCxInit(TIM_TypeDef *TIMx, const TIM_OCInitTypeDef *TIM_OCInitStruct, uint8_t ch) +{ + switch (ch) { + case 1: + TIM_OC1Init(TIMx, TIM_OCInitStruct); + break; + case 2: + TIM_OC2Init(TIMx, TIM_OCInitStruct); + break; + case 3: + TIM_OC3Init(TIMx, TIM_OCInitStruct); + break; + case 4: + TIM_OC4Init(TIMx, TIM_OCInitStruct); + break; + } +} + +void genericTIM_OCxPreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload, uint8_t ch) +{ + switch (ch) { + case 1: + TIM_OC1PreloadConfig(TIMx, TIM_OCPreload); + break; + case 2: + TIM_OC2PreloadConfig(TIMx, TIM_OCPreload); + break; + case 3: + TIM_OC3PreloadConfig(TIMx, TIM_OCPreload); + break; + case 4: + TIM_OC4PreloadConfig(TIMx, TIM_OCPreload); + break; + } +} + + +void setupDMA() +{ + // Configure Ch1 + DMA_Init(pios_ws2811_cfg->streamCh1, (DMA_InitTypeDef *)&pios_ws2811_cfg->dmaInitCh1); + pios_ws2811_cfg->streamCh1->PAR = (uint32_t)&pios_ws2811_pin_cfg->gpio->BSRRH; + pios_ws2811_cfg->streamCh1->M0AR = (uint32_t)fb; + + NVIC_Init((NVIC_InitTypeDef *)&(pios_ws2811_cfg->irq.init)); + DMA_ITConfig(pios_ws2811_cfg->streamCh1, DMA_IT_TC, ENABLE); + + + DMA_Init(pios_ws2811_cfg->streamCh2, (DMA_InitTypeDef *)&pios_ws2811_cfg->dmaInitCh2); + pios_ws2811_cfg->streamCh2->PAR = (uint32_t)&pios_ws2811_pin_cfg->gpio->BSRRH; + pios_ws2811_cfg->streamCh2->M0AR = (uint32_t)dmaSource; + + DMA_Init(pios_ws2811_cfg->streamUpdate, (DMA_InitTypeDef *)&pios_ws2811_cfg->dmaInitUpdate); + pios_ws2811_cfg->streamUpdate->PAR = (uint32_t)&pios_ws2811_pin_cfg->gpio->BSRRL; + pios_ws2811_cfg->streamUpdate->M0AR = (uint32_t)dmaSource; + + DMA_ClearITPendingBit(pios_ws2811_cfg->streamCh1, pios_ws2811_cfg->dmaItCh1); + DMA_ClearITPendingBit(pios_ws2811_cfg->streamCh2, pios_ws2811_cfg->dmaItCh2); + DMA_ClearITPendingBit(pios_ws2811_cfg->streamUpdate, pios_ws2811_cfg->dmaItUpdate); + + DMA_Cmd(pios_ws2811_cfg->streamCh2, ENABLE); + DMA_Cmd(pios_ws2811_cfg->streamCh1, ENABLE); + DMA_Cmd(pios_ws2811_cfg->streamUpdate, ENABLE); +} + +void setColor(uint8_t color, ledbuf_t *buf) +{ + uint8_t i; + + for (i = 0; i < 8; i++) { + buf[i] = ((color << i) & 0b10000000 ? 0x0 : dmaSource[0]); + } +} + +/** + * Set a led color + * @param c color + * @param led led number + * @param update Perform an update after changing led color + */ +void PIOS_WS2811_setColorRGB(Color c, uint8_t led, bool update) +{ + if (led >= PIOS_WS2811_NUMLEDS) { + return; + } + setColor(c.G, fb + (led * 24)); + setColor(c.R, fb + 8 + (led * 24)); + setColor(c.B, fb + 16 + (led * 24)); + if (update) { + PIOS_WS2811_Update(); + } +} + +/** + * trigger an update cycle if not already running + */ +void PIOS_WS2811_Update() +{ + // does not start if framebuffer is not allocated (init has not been called yet) or a transfer is still on going + if (!fb || (pios_ws2811_cfg->timer->CR1 & TIM_CR1_CEN)) { + return; + } + + // reset counters for synchronization + pios_ws2811_cfg->timer->CNT = PIOS_WS2811_TIM_PERIOD - 1; + // Start a new cycle + TIM_Cmd(pios_ws2811_cfg->timer, ENABLE); +} + +/** + * Stop timer once the complete framebuffer has been sent + */ + +void PIOS_WS2811_DMA_irq_handler() +{ + pios_ws2811_cfg->timer->CR1 &= (uint16_t) ~TIM_CR1_CEN; + DMA_ClearFlag(pios_ws2811_cfg->streamCh1, pios_ws2811_cfg->irq.flags); +} + +#endif // PIOS_INCLUDE_WS2811 diff --git a/flight/targets/boards/coptercontrol/firmware/Makefile b/flight/targets/boards/coptercontrol/firmware/Makefile index 62adbea68..69a714d66 100644 --- a/flight/targets/boards/coptercontrol/firmware/Makefile +++ b/flight/targets/boards/coptercontrol/firmware/Makefile @@ -48,9 +48,13 @@ OPTMODULES += Osd/osdoutput #OPTMODULES += Altitude #OPTMODULES += Fault +SRC += $(FLIGHTLIB)/notification.c + # Include all camera options CDEFS += -DUSE_INPUT_LPF -DUSE_GIMBAL_LPF -DUSE_GIMBAL_FF + + # Erase flash firmware should be buildable from command line ifeq ($(ERASE_FLASH), YES) CDEFS += -DERASE_FLASH diff --git a/flight/targets/boards/discoveryf4bare/board_hw_defs.c b/flight/targets/boards/discoveryf4bare/board_hw_defs.c index 69fc1b2f4..4583fe6b4 100644 --- a/flight/targets/boards/discoveryf4bare/board_hw_defs.c +++ b/flight/targets/boards/discoveryf4bare/board_hw_defs.c @@ -1810,3 +1810,69 @@ const struct pios_usb_hid_cfg pios_usb_hid_cfg = { .data_tx_ep = 1, }; #endif /* PIOS_INCLUDE_USB_HID && PIOS_INCLUDE_USB_CDC */ +#ifdef PIOS_INCLUDE_WS2811 +#include +#define PIOS_WS2811_TIM_DIVIDER (PIOS_PERIPHERAL_APB2_CLOCK / (800000 * PIOS_WS2811_TIM_PERIOD)) + +void DMA2_Stream1_IRQHandler(void) __attribute__((alias("PIOS_WS2811_irq_handler"))); + +const struct pios_ws2811_pin_cfg pios_ws2811_pin_cfg = { + .gpio = GPIOA, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_3, + .GPIO_Speed = GPIO_Speed_50MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, +}; + +const struct pios_ws2811_cfg pios_ws2811_cfg = { + .timer = TIM1, + .timerInit = { + .TIM_Prescaler = PIOS_WS2811_TIM_DIVIDER - 1, + .TIM_ClockDivision = TIM_CKD_DIV1, + .TIM_CounterMode = TIM_CounterMode_Up, + // period (1.25 uS per period + .TIM_Period = PIOS_WS2811_TIM_PERIOD, + .TIM_RepetitionCounter = 0x0000, + }, + + .timerCh1 = 1, + .streamCh1 = DMA2_Stream1, + .timerCh2 = 3, + .streamCh2 = DMA2_Stream6, + .streamUpdate = DMA2_Stream5, + + // DMA streamCh1, triggered by timerCh1 pwm signal. + // if FrameBuffer indicates, reset output value early to indicate "0" bit to ws2812 + .dmaInitCh1 = PIOS_WS2811_DMA_CH1_CONFIG(DMA_Channel_6), + .dmaItCh1 = DMA_IT_TEIF1 | DMA_IT_TCIF1, + + // DMA streamCh2, triggered by timerCh2 pwm signal. + // Reset output value late to indicate "1" bit to ws2812. + .dmaInitCh2 = PIOS_WS2811_DMA_CH2_CONFIG(DMA_Channel_6), + .dmaItCh2 = DMA_IT_TEIF6 | DMA_IT_TCIF6, + + // DMA streamUpdate Triggered by timer update event + // Outputs a high logic level at beginning of a cycle + .dmaInitUpdate = PIOS_WS2811_DMA_UPDATE_CONFIG(DMA_Channel_6), + .dmaItUpdate = DMA_IT_TEIF5 | DMA_IT_TCIF5, + .dmaSource = TIM_DMA_CC1 | TIM_DMA_CC3 | TIM_DMA_Update, + + // DMA streamCh1 interrupt vector, used to block timer at end of framebuffer transfer + .irq = { + .flags = (DMA_IT_TCIF1), + .init = { + .NVIC_IRQChannel = DMA2_Stream1_IRQn, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, +}; + +void PIOS_WS2811_irq_handler(void) +{ + PIOS_WS2811_DMA_irq_handler(); +} +#endif // PIOS_INCLUDE_WS2811 diff --git a/flight/targets/boards/discoveryf4bare/firmware/Makefile b/flight/targets/boards/discoveryf4bare/firmware/Makefile index 501703980..8b14a385e 100644 --- a/flight/targets/boards/discoveryf4bare/firmware/Makefile +++ b/flight/targets/boards/discoveryf4bare/firmware/Makefile @@ -52,6 +52,8 @@ MODULES += Telemetry OPTMODULES += ComUsbBridge +SRC += $(FLIGHTLIB)/notification.c + # Include all camera options CDEFS += -DUSE_INPUT_LPF -DUSE_GIMBAL_LPF -DUSE_GIMBAL_FF diff --git a/flight/targets/boards/discoveryf4bare/firmware/inc/pios_config.h b/flight/targets/boards/discoveryf4bare/firmware/inc/pios_config.h index 16dbd851a..897ef36d9 100644 --- a/flight/targets/boards/discoveryf4bare/firmware/inc/pios_config.h +++ b/flight/targets/boards/discoveryf4bare/firmware/inc/pios_config.h @@ -88,6 +88,7 @@ // #define PIOS_INCLUDE_MPXV // #define PIOS_INCLUDE_ETASV3 /* #define PIOS_INCLUDE_HCSR04 */ +#define PIOS_INCLUDE_WS2811 /* PIOS receiver drivers */ #define PIOS_INCLUDE_PWM diff --git a/flight/targets/boards/discoveryf4bare/firmware/pios_board.c b/flight/targets/boards/discoveryf4bare/firmware/pios_board.c index 63f13b442..ee7159705 100644 --- a/flight/targets/boards/discoveryf4bare/firmware/pios_board.c +++ b/flight/targets/boards/discoveryf4bare/firmware/pios_board.c @@ -36,7 +36,6 @@ #include #include #include - /* * Pull in the board-specific static HW definitions. * Including .c files is a bit ugly but this allows all of @@ -932,6 +931,11 @@ void PIOS_Board_Init(void) PIOS_MPU6000_Init(pios_spi_gyro_id, 0, &pios_mpu6000_cfg); PIOS_MPU6000_CONFIG_Configure(); #endif + +#ifdef PIOS_INCLUDE_WS2811 +#include + PIOS_WS2811_Init(&pios_ws2811_cfg, &pios_ws2811_pin_cfg); +#endif // PIOS_INCLUDE_WS2811 } /** diff --git a/flight/targets/boards/osd/firmware/Makefile b/flight/targets/boards/osd/firmware/Makefile index 14d863b1b..3744ee56a 100644 --- a/flight/targets/boards/osd/firmware/Makefile +++ b/flight/targets/boards/osd/firmware/Makefile @@ -37,6 +37,8 @@ MODULES += Telemetry OPTMODULES = +SRC += $(FLIGHTLIB)/notification.c + # Some diagnostics CDEFS += -DDIAG_TASKS diff --git a/flight/targets/boards/revolution/board_hw_defs.c b/flight/targets/boards/revolution/board_hw_defs.c index 78d106cbc..98fce19c7 100644 --- a/flight/targets/boards/revolution/board_hw_defs.c +++ b/flight/targets/boards/revolution/board_hw_defs.c @@ -1962,3 +1962,138 @@ const struct pios_usb_hid_cfg pios_usb_hid_cfg = { .data_tx_ep = 1, }; #endif /* PIOS_INCLUDE_USB_HID && PIOS_INCLUDE_USB_CDC */ + +#ifdef PIOS_INCLUDE_WS2811 +#include +#include +#define PIOS_WS2811_TIM_DIVIDER (PIOS_PERIPHERAL_APB2_CLOCK / (800000 * PIOS_WS2811_TIM_PERIOD)) + +void DMA2_Stream1_IRQHandler(void) __attribute__((alias("PIOS_WS2811_irq_handler"))); +// list of pin configurable as ws281x outputs. +// this will not clash with PWM in or servo output as +// pins will be reconfigured as _OUT so the alternate function is disabled. +const struct pios_ws2811_pin_cfg pios_ws2811_pin_cfg[] = { + [HWSETTINGS_WS2811LED_OUT_SERVOOUT1] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_0, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_SERVOOUT2] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_1, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_SERVOOUT3] = { + .gpio = GPIOA, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_3, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_SERVOOUT4] = { + .gpio = GPIOA, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_2, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_SERVOOUT5] = { + .gpio = GPIOA, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_1, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_SERVOOUT6] = { + .gpio = GPIOA, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_0, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_FLEXIPIN3] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_12, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, + [HWSETTINGS_WS2811LED_OUT_FLEXIPIN4] = { + .gpio = GPIOB, + .gpioInit = { + .GPIO_Pin = GPIO_Pin_13, + .GPIO_Speed = GPIO_Speed_25MHz, + .GPIO_Mode = GPIO_Mode_OUT, + .GPIO_OType = GPIO_OType_PP, + }, + }, +}; + +const struct pios_ws2811_cfg pios_ws2811_cfg = { + .timer = TIM1, + .timerInit = { + .TIM_Prescaler = PIOS_WS2811_TIM_DIVIDER - 1, + .TIM_ClockDivision = TIM_CKD_DIV1, + .TIM_CounterMode = TIM_CounterMode_Up, + // period (1.25 uS per period + .TIM_Period = PIOS_WS2811_TIM_PERIOD, + .TIM_RepetitionCounter = 0x0000, + }, + + .timerCh1 = 1, + .streamCh1 = DMA2_Stream1, + .timerCh2 = 3, + .streamCh2 = DMA2_Stream6, + .streamUpdate = DMA2_Stream5, + + // DMA streamCh1, triggered by timerCh1 pwm signal. + // if FrameBuffer indicates, reset output value early to indicate "0" bit to ws2812 + .dmaInitCh1 = PIOS_WS2811_DMA_CH1_CONFIG(DMA_Channel_6), + .dmaItCh1 = DMA_IT_TEIF1 | DMA_IT_TCIF1, + + // DMA streamCh2, triggered by timerCh2 pwm signal. + // Reset output value late to indicate "1" bit to ws2812. + .dmaInitCh2 = PIOS_WS2811_DMA_CH2_CONFIG(DMA_Channel_6), + .dmaItCh2 = DMA_IT_TEIF6 | DMA_IT_TCIF6, + + // DMA streamUpdate Triggered by timer update event + // Outputs a high logic level at beginning of a cycle + .dmaInitUpdate = PIOS_WS2811_DMA_UPDATE_CONFIG(DMA_Channel_6), + .dmaItUpdate = DMA_IT_TEIF5 | DMA_IT_TCIF5, + .dmaSource = TIM_DMA_CC1 | TIM_DMA_CC3 | TIM_DMA_Update, + + // DMAInitCh1 interrupt vector, used to block timer at end of framebuffer transfer + .irq = { + .flags = (DMA_IT_TCIF1), + .init = { + .NVIC_IRQChannel = DMA2_Stream1_IRQn, + .NVIC_IRQChannelPreemptionPriority = PIOS_IRQ_PRIO_HIGHEST, + .NVIC_IRQChannelSubPriority = 0, + .NVIC_IRQChannelCmd = ENABLE, + }, + }, +}; + +void PIOS_WS2811_irq_handler(void) +{ + PIOS_WS2811_DMA_irq_handler(); +} +#endif // PIOS_INCLUDE_WS2811 diff --git a/flight/targets/boards/revolution/firmware/Makefile b/flight/targets/boards/revolution/firmware/Makefile index bf9254e7c..88b8dd6b6 100644 --- a/flight/targets/boards/revolution/firmware/Makefile +++ b/flight/targets/boards/revolution/firmware/Makefile @@ -53,6 +53,8 @@ MODULES += Telemetry OPTMODULES += ComUsbBridge +SRC += $(FLIGHTLIB)/notification.c + # Include all camera options CDEFS += -DUSE_INPUT_LPF -DUSE_GIMBAL_LPF -DUSE_GIMBAL_FF @@ -96,7 +98,9 @@ endif # Optional component libraries include $(FLIGHTLIB)/rscode/library.mk + #include $(FLIGHTLIB)/PyMite/pymite.mk + include $(ROOT_DIR)/make/apps-defs.mk include $(ROOT_DIR)/make/common-defs.mk diff --git a/flight/targets/boards/revolution/firmware/inc/pios_config.h b/flight/targets/boards/revolution/firmware/inc/pios_config.h index 5b46f059a..0cc14adf2 100644 --- a/flight/targets/boards/revolution/firmware/inc/pios_config.h +++ b/flight/targets/boards/revolution/firmware/inc/pios_config.h @@ -89,6 +89,8 @@ #define PIOS_INCLUDE_ETASV3 #define PIOS_INCLUDE_MS4525DO +#define PIOS_INCLUDE_WS2811 + /* #define PIOS_INCLUDE_HCSR04 */ /* PIOS receiver drivers */ diff --git a/flight/targets/boards/revolution/firmware/pios_board.c b/flight/targets/boards/revolution/firmware/pios_board.c index 14d917b84..0c03f2a38 100644 --- a/flight/targets/boards/revolution/firmware/pios_board.c +++ b/flight/targets/boards/revolution/firmware/pios_board.c @@ -35,6 +35,7 @@ #include #include #include +#include /* * Pull in the board-specific static HW definitions. @@ -945,6 +946,17 @@ void PIOS_Board_Init(void) PIOS_MPU6000_Init(pios_spi_gyro_id, 0, &pios_mpu6000_cfg); PIOS_MPU6000_CONFIG_Configure(); #endif + +#ifdef PIOS_INCLUDE_WS2811 +#include + HwSettingsWS2811LED_OutOptions ws2811_pin_settings; + HwSettingsWS2811LED_OutGet(&ws2811_pin_settings); + + if (ws2811_pin_settings != HWSETTINGS_WS2811LED_OUT_DISABLED && ws2811_pin_settings < NELEMENTS(pios_ws2811_pin_cfg)) { + PIOS_WS2811_Init(&pios_ws2811_cfg, &pios_ws2811_pin_cfg[ws2811_pin_settings]); + } + +#endif // PIOS_INCLUDE_WS2811 } /** diff --git a/flight/targets/boards/revoproto/firmware/Makefile b/flight/targets/boards/revoproto/firmware/Makefile index 2b2a4ad99..affe5a570 100644 --- a/flight/targets/boards/revoproto/firmware/Makefile +++ b/flight/targets/boards/revoproto/firmware/Makefile @@ -50,6 +50,8 @@ MODULES += Osd/osdoutout MODULES += Logging MODULES += Telemetry +SRC += $(FLIGHTLIB)/notification.c + OPTMODULES += ComUsbBridge # Include all camera options diff --git a/flight/targets/boards/simposix/firmware/Makefile b/flight/targets/boards/simposix/firmware/Makefile index bc05d3b69..8d77fd4f1 100644 --- a/flight/targets/boards/simposix/firmware/Makefile +++ b/flight/targets/boards/simposix/firmware/Makefile @@ -45,6 +45,8 @@ MODULES += Airspeed #MODULES += AltitudeHold # now integrated in Stabilization #MODULES += OveroSync +SRC += $(FLIGHTLIB)/notification.c + # Paths OPSYSTEM = . BOARDINC = .. @@ -103,6 +105,8 @@ SRC += $(PIOSCORECOMMON)/pios_dosfs_logfs.c SRC += $(PIOSCORECOMMON)/pios_debuglog.c SRC += $(PIOSCORECOMMON)/pios_callbackscheduler.c SRC += $(PIOSCORECOMMON)/pios_deltatime.c +SRC += $(PIOSCORECOMMON)/pios_notify.c + ## PIOS Hardware include $(PIOS)/posix/library.mk diff --git a/ground/openpilotgcs/src/libs/opmapcontrol/src/core/urlfactory.cpp b/ground/openpilotgcs/src/libs/opmapcontrol/src/core/urlfactory.cpp index 0bc943ff4..6025eecc1 100644 --- a/ground/openpilotgcs/src/libs/opmapcontrol/src/core/urlfactory.cpp +++ b/ground/openpilotgcs/src/libs/opmapcontrol/src/core/urlfactory.cpp @@ -197,7 +197,6 @@ QString UrlFactory::MakeImageUrl(const MapType::Types &type, const Point &pos, c QString sec2 = ""; // after &zoom=... GetSecGoogleWords(pos, sec1, sec2); TryCorrectGoogleVersions(); - QString VersionGoogleSatellite = "132"; return QString("https://%1%2.google.com/%3/v=%4&hl=%5&x=%6%7&y=%8&z=%9&s=%10").arg(server).arg(GetServerNum(pos, 4)).arg(request).arg(VersionGoogleSatellite).arg(language).arg(pos.X()).arg(sec1).arg(pos.Y()).arg(zoom).arg(sec2); } break; diff --git a/ground/openpilotgcs/src/libs/utils/coordinateconversions.cpp b/ground/openpilotgcs/src/libs/utils/coordinateconversions.cpp index 4de1f311b..c6ceb0f01 100644 --- a/ground/openpilotgcs/src/libs/utils/coordinateconversions.cpp +++ b/ground/openpilotgcs/src/libs/utils/coordinateconversions.cpp @@ -44,7 +44,7 @@ CoordinateConversions::CoordinateConversions() * @param[in] LLA Longitude latitude altitude for this location * @param[out] Rne[3][3] Rotation matrix */ -void CoordinateConversions::RneFromLLA(double LLA[3], double Rne[3][3]) +void CoordinateConversions::RneFromLLA(double LLA[3], float Rne[3][3]) { float sinLat, sinLon, cosLat, cosLon; @@ -134,7 +134,7 @@ int CoordinateConversions::NED2LLA_HomeECEF(double BaseECEFm[3], double NED[3], // stored value is in cm, convert to m double BaseLLA[3]; double ECEF[3]; - double Rne[3][3]; + float Rne[3][3]; // Get LLA address to compute conversion matrix ECEF2LLA(BaseECEFm, BaseLLA); diff --git a/ground/openpilotgcs/src/libs/utils/coordinateconversions.h b/ground/openpilotgcs/src/libs/utils/coordinateconversions.h index de6ac7568..23b28fe47 100644 --- a/ground/openpilotgcs/src/libs/utils/coordinateconversions.h +++ b/ground/openpilotgcs/src/libs/utils/coordinateconversions.h @@ -41,7 +41,7 @@ public: CoordinateConversions(); int NED2LLA_HomeECEF(double BaseECEFcm[3], double NED[3], double position[3]); int NED2LLA_HomeLLA(double LLA[3], double NED[3], double position[3]); - void RneFromLLA(double LLA[3], double Rne[3][3]); + void RneFromLLA(double LLA[3], float Rne[3][3]); void LLA2ECEF(double LLA[3], double ECEF[3]); int ECEF2LLA(double ECEF[3], double LLA[3]); void LLA2Base(double LLA[3], double BaseECEF[3], float Rne[3][3], float NED[3]); diff --git a/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.cpp b/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.cpp index 2626ca47b..bc31f58c0 100644 --- a/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.cpp +++ b/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.cpp @@ -82,13 +82,34 @@ MyTabbedStackWidget::MyTabbedStackWidget(QWidget *parent, bool isVertical, bool void MyTabbedStackWidget::insertTab(const int index, QWidget *tab, const QIcon &icon, const QString &label) { - tab->setContentsMargins(0, 0, 0, 0); - m_stackWidget->insertWidget(index, tab); + // create and insert item QListWidgetItem *item = new QListWidgetItem(icon, label); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); item->setToolTip(label); m_listWidget->insertItem(index, item); + + // setup and insert widget + tab->setContentsMargins(0, 0, 0, 0); + m_stackWidget->insertWidget(index, tab); +} + +void MyTabbedStackWidget::replaceTab(int index, QWidget *tab) +{ + QWidget *wid = m_stackWidget->widget(index); + + // setup and insert new widget + tab->setContentsMargins(0, 0, 0, 0); + m_stackWidget->insertWidget(index, tab); + // check special case when replacing currenlty selected tab + if (index == currentIndex()) { + // currently selected tab is being replaced so select the new tab before removing the old one + m_stackWidget->setCurrentWidget(tab); + } + // remove and delete old widget + m_stackWidget->removeWidget(wid); + delete wid; } void MyTabbedStackWidget::removeTab(int index) @@ -97,6 +118,7 @@ void MyTabbedStackWidget::removeTab(int index) m_stackWidget->removeWidget(wid); delete wid; + QListWidgetItem *item = m_listWidget->item(index); m_listWidget->removeItemWidget(item); delete item; diff --git a/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.h b/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.h index 661200383..169eca9ba 100644 --- a/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.h +++ b/ground/openpilotgcs/src/libs/utils/mytabbedstackwidget.h @@ -40,6 +40,7 @@ public: MyTabbedStackWidget(QWidget *parent = 0, bool isVertical = false, bool iconAbove = true); void insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label); + void replaceTab(int index, QWidget *tab); void removeTab(int index); void setIconSize(int size) { diff --git a/ground/openpilotgcs/src/plugins/config/channelform.cpp b/ground/openpilotgcs/src/plugins/config/channelform.cpp new file mode 100644 index 000000000..a15a5f713 --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/channelform.cpp @@ -0,0 +1,73 @@ +#include "channelform.h" + +#include + + +ChannelForm::ChannelForm(const int index, QWidget *parent) : ConfigTaskWidget(parent), m_index(index) +{} + +ChannelForm::~ChannelForm() +{} + +int ChannelForm::index() const +{ + return m_index; +} + +void ChannelForm::moveTo(QGridLayout &dstLayout) +{ + QGridLayout *srcLayout = dynamic_cast(layout()); + + Q_ASSERT(srcLayout); + + // if we are the first row to be inserted then show the legend + bool showLegend = (dstLayout.rowCount() == 1); + + if (showLegend) { + // move legend row to target grid layout + moveRow(0, *srcLayout, dstLayout); + } else { + removeRow(0, *srcLayout); + } + + // move field row to target grid layout + moveRow(1, *srcLayout, dstLayout); + + // this form is now empty so hide it + setVisible(false); +} + +void ChannelForm::moveRow(int row, QGridLayout &srcLayout, QGridLayout &dstLayout) +{ + int dstRow = dstLayout.rowCount(); + + for (int col = 0; col < srcLayout.columnCount(); col++) { + QLayoutItem *item = srcLayout.itemAtPosition(row, col); + if (!item) { + continue; + } + QWidget *widget = item->widget(); + if (widget) { + dstLayout.addWidget(widget, dstRow, col); + continue; + } + // TODO handle item of type QLayout + } +} + +void ChannelForm::removeRow(int row, QGridLayout &layout) +{ + for (int col = 0; col < layout.columnCount(); col++) { + QLayoutItem *item = layout.itemAtPosition(row, col); + if (!item) { + continue; + } + QWidget *widget = item->widget(); + if (widget) { + layout.removeWidget(widget); + delete widget; + continue; + } + // TODO handle item of type QLayout + } +} diff --git a/ground/openpilotgcs/src/plugins/config/channelform.h b/ground/openpilotgcs/src/plugins/config/channelform.h new file mode 100644 index 000000000..6c62a2f4b --- /dev/null +++ b/ground/openpilotgcs/src/plugins/config/channelform.h @@ -0,0 +1,36 @@ +#ifndef CHANNELFORM_H +#define CHANNELFORM_H + +#include "configtaskwidget.h" + +#include + +namespace Ui { +class ChannelForm; +} + +class QGridLayout; + +class ChannelForm : public ConfigTaskWidget { + Q_OBJECT + +public: + explicit ChannelForm(const int index, QWidget *parent = 0); + ~ChannelForm(); + + int index() const; + + virtual QString name() = 0; + virtual void setName(const QString &name) = 0; + + void moveTo(QGridLayout &dstLayout); + +private: + // Channel index + int m_index; + + static void moveRow(int row, QGridLayout &srcLayout, QGridLayout &dstLayout); + static void removeRow(int row, QGridLayout &layout); +}; + +#endif // CHANNELFORM_H diff --git a/ground/openpilotgcs/src/plugins/config/config.pro b/ground/openpilotgcs/src/plugins/config/config.pro index 8c73c6d28..cea1b866d 100644 --- a/ground/openpilotgcs/src/plugins/config/config.pro +++ b/ground/openpilotgcs/src/plugins/config/config.pro @@ -1,18 +1,19 @@ TEMPLATE = lib TARGET = Config DEFINES += CONFIG_LIBRARY -QT += svg -QT += opengl -QT += qml quick + +QT += svg opengl qml quick include(config_dependencies.pri) INCLUDEPATH += ../../libs/eigen -OTHER_FILES += Config.pluginspec \ +OTHER_FILES += \ + Config.pluginspec \ calibration/WizardStepIndicator.qml -HEADERS += configplugin.h \ +HEADERS += \ + configplugin.h \ configgadgetwidget.h \ configgadgetfactory.h \ configgadget.h \ @@ -27,6 +28,7 @@ HEADERS += configplugin.h \ assertions.h \ defaultattitudewidget.h \ defaulthwsettingswidget.h \ + channelform.h \ inputchannelform.h \ configcamerastabilizationwidget.h \ configtxpidwidget.h \ @@ -57,7 +59,8 @@ HEADERS += configplugin.h \ calibration/gyrobiascalibrationmodel.h \ calibration/calibrationuiutils.h -SOURCES += configplugin.cpp \ +SOURCES += \ + configplugin.cpp \ configgadgetwidget.cpp \ configgadgetfactory.cpp \ configgadget.cpp \ @@ -71,6 +74,7 @@ SOURCES += configplugin.cpp \ configpipxtremewidget.cpp \ defaultattitudewidget.cpp \ defaulthwsettingswidget.cpp \ + channelform.cpp \ inputchannelform.cpp \ configcamerastabilizationwidget.cpp \ configrevowidget.cpp \ @@ -95,7 +99,8 @@ SOURCES += configplugin.cpp \ calibration/levelcalibrationmodel.cpp \ calibration/gyrobiascalibrationmodel.cpp -FORMS += airframe.ui \ +FORMS += \ + airframe.ui \ airframe_ccpm.ui \ airframe_fixedwing.ui \ airframe_ground.ui \ diff --git a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp index 4ae81eb08..cd359019b 100644 --- a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.cpp @@ -55,69 +55,67 @@ ConfigGadgetWidget::ConfigGadgetWidget(QWidget *parent) : QWidget(parent) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - ftw = new MyTabbedStackWidget(this, true, true); - ftw->setIconSize(64); + stackWidget = new MyTabbedStackWidget(this, true, true); + stackWidget->setIconSize(64); QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(ftw); + layout->addWidget(stackWidget); setLayout(layout); - // ********************* QWidget *qwd; QIcon *icon = new QIcon(); icon->addFile(":/configgadget/images/hardware_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/hardware_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new DefaultHwSettingsWidget(this); - ftw->insertTab(ConfigGadgetWidget::hardware, qwd, *icon, QString("Hardware")); + stackWidget->insertTab(ConfigGadgetWidget::hardware, qwd, *icon, QString("Hardware")); icon = new QIcon(); icon->addFile(":/configgadget/images/vehicle_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/vehicle_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigVehicleTypeWidget(this); - ftw->insertTab(ConfigGadgetWidget::aircraft, qwd, *icon, QString("Vehicle")); + stackWidget->insertTab(ConfigGadgetWidget::aircraft, qwd, *icon, QString("Vehicle")); icon = new QIcon(); icon->addFile(":/configgadget/images/input_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/input_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigInputWidget(this); - ftw->insertTab(ConfigGadgetWidget::input, qwd, *icon, QString("Input")); + stackWidget->insertTab(ConfigGadgetWidget::input, qwd, *icon, QString("Input")); icon = new QIcon(); icon->addFile(":/configgadget/images/output_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/output_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigOutputWidget(this); - ftw->insertTab(ConfigGadgetWidget::output, qwd, *icon, QString("Output")); + stackWidget->insertTab(ConfigGadgetWidget::output, qwd, *icon, QString("Output")); icon = new QIcon(); icon->addFile(":/configgadget/images/ins_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/ins_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new DefaultAttitudeWidget(this); - ftw->insertTab(ConfigGadgetWidget::sensors, qwd, *icon, QString("Attitude")); + stackWidget->insertTab(ConfigGadgetWidget::sensors, qwd, *icon, QString("Attitude")); icon = new QIcon(); icon->addFile(":/configgadget/images/stabilization_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/stabilization_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigStabilizationWidget(this); - ftw->insertTab(ConfigGadgetWidget::stabilization, qwd, *icon, QString("Stabilization")); + stackWidget->insertTab(ConfigGadgetWidget::stabilization, qwd, *icon, QString("Stabilization")); icon = new QIcon(); icon->addFile(":/configgadget/images/camstab_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/camstab_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigCameraStabilizationWidget(this); - ftw->insertTab(ConfigGadgetWidget::camerastabilization, qwd, *icon, QString("Gimbal")); + stackWidget->insertTab(ConfigGadgetWidget::camerastabilization, qwd, *icon, QString("Gimbal")); icon = new QIcon(); icon->addFile(":/configgadget/images/txpid_normal.png", QSize(), QIcon::Normal, QIcon::Off); icon->addFile(":/configgadget/images/txpid_selected.png", QSize(), QIcon::Selected, QIcon::Off); qwd = new ConfigTxPIDWidget(this); - ftw->insertTab(ConfigGadgetWidget::txpid, qwd, *icon, QString("TxPID")); + stackWidget->insertTab(ConfigGadgetWidget::txpid, qwd, *icon, QString("TxPID")); + + stackWidget->setCurrentIndex(ConfigGadgetWidget::hardware); - ftw->setCurrentIndex(ConfigGadgetWidget::hardware); - // ********************* // Listen to autopilot connection events - ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); TelemetryManager *telMngr = pm->getObject(); connect(telMngr, SIGNAL(connected()), this, SLOT(onAutopilotConnect())); @@ -129,9 +127,9 @@ ConfigGadgetWidget::ConfigGadgetWidget(QWidget *parent) : QWidget(parent) } help = 0; - connect(ftw, SIGNAL(currentAboutToShow(int, bool *)), this, SLOT(tabAboutToChange(int, bool *))); + connect(stackWidget, SIGNAL(currentAboutToShow(int, bool *)), this, SLOT(tabAboutToChange(int, bool *))); - // Connect to the PipXStatus object updates + // Connect to the OPLinkStatus object updates UAVObjectManager *objManager = pm->getObject(); oplinkStatusObj = dynamic_cast(objManager->getObject("OPLinkStatus")); if (oplinkStatusObj != NULL) { @@ -148,13 +146,13 @@ ConfigGadgetWidget::ConfigGadgetWidget(QWidget *parent) : QWidget(parent) ConfigGadgetWidget::~ConfigGadgetWidget() { - // TODO: properly delete all the tabs in ftw before exiting + delete stackWidget; } void ConfigGadgetWidget::startInputWizard() { - ftw->setCurrentIndex(ConfigGadgetWidget::input); - ConfigInputWidget *inputWidget = dynamic_cast(ftw->getWidget(ConfigGadgetWidget::input)); + stackWidget->setCurrentIndex(ConfigGadgetWidget::input); + ConfigInputWidget *inputWidget = dynamic_cast(stackWidget->getWidget(ConfigGadgetWidget::input)); Q_ASSERT(inputWidget); inputWidget->startInputWizard(); } @@ -166,24 +164,12 @@ void ConfigGadgetWidget::resizeEvent(QResizeEvent *event) void ConfigGadgetWidget::onAutopilotDisconnect() { - int selectedIndex = ftw->currentIndex(); - - QIcon *icon = new QIcon(); - - icon->addFile(":/configgadget/images/ins_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/ins_selected.png", QSize(), QIcon::Selected, QIcon::Off); QWidget *qwd = new DefaultAttitudeWidget(this); - ftw->removeTab(ConfigGadgetWidget::sensors); - ftw->insertTab(ConfigGadgetWidget::sensors, qwd, *icon, QString("Attitude")); - icon = new QIcon(); - icon->addFile(":/configgadget/images/hardware_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/hardware_selected.png", QSize(), QIcon::Selected, QIcon::Off); - qwd = new DefaultHwSettingsWidget(this); - ftw->removeTab(ConfigGadgetWidget::hardware); - ftw->insertTab(ConfigGadgetWidget::hardware, qwd, *icon, QString("Hardware")); + stackWidget->replaceTab(ConfigGadgetWidget::sensors, qwd); - ftw->setCurrentIndex(selectedIndex); + qwd = new DefaultHwSettingsWidget(this); + stackWidget->replaceTab(ConfigGadgetWidget::hardware, qwd); emit autopilotDisconnected(); } @@ -196,45 +182,25 @@ void ConfigGadgetWidget::onAutopilotConnect() ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); UAVObjectUtilManager *utilMngr = pm->getObject(); if (utilMngr) { - int selectedIndex = ftw->currentIndex(); int board = utilMngr->getBoardModel(); if ((board & 0xff00) == 1024) { // CopterControl family - - QIcon *icon = new QIcon(); - icon->addFile(":/configgadget/images/ins_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/ins_selected.png", QSize(), QIcon::Selected, QIcon::Off); QWidget *qwd = new ConfigCCAttitudeWidget(this); - ftw->removeTab(ConfigGadgetWidget::sensors); - ftw->insertTab(ConfigGadgetWidget::sensors, qwd, *icon, QString("Attitude")); + stackWidget->replaceTab(ConfigGadgetWidget::sensors, qwd); - icon = new QIcon(); - icon->addFile(":/configgadget/images/hardware_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/hardware_selected.png", QSize(), QIcon::Selected, QIcon::Off); - qwd = new ConfigCCHWWidget(this); - ftw->removeTab(ConfigGadgetWidget::hardware); - ftw->insertTab(ConfigGadgetWidget::hardware, qwd, *icon, QString("Hardware")); + qwd = new ConfigCCHWWidget(this); + stackWidget->replaceTab(ConfigGadgetWidget::hardware, qwd); } else if ((board & 0xff00) == 0x0900) { // Revolution family - - QIcon *icon = new QIcon(); - icon->addFile(":/configgadget/images/ins_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/ins_selected.png", QSize(), QIcon::Selected, QIcon::Off); QWidget *qwd = new ConfigRevoWidget(this); - ftw->removeTab(ConfigGadgetWidget::sensors); - ftw->insertTab(ConfigGadgetWidget::sensors, qwd, *icon, QString("Attitude")); + stackWidget->replaceTab(ConfigGadgetWidget::sensors, qwd); - icon = new QIcon(); - icon->addFile(":/configgadget/images/hardware_normal.png", QSize(), QIcon::Normal, QIcon::Off); - icon->addFile(":/configgadget/images/hardware_selected.png", QSize(), QIcon::Selected, QIcon::Off); - qwd = new ConfigRevoHWWidget(this); - ftw->removeTab(ConfigGadgetWidget::hardware); - ftw->insertTab(ConfigGadgetWidget::hardware, qwd, *icon, QString("Hardware")); + qwd = new ConfigRevoHWWidget(this); + stackWidget->replaceTab(ConfigGadgetWidget::hardware, qwd); } else { // Unknown board qDebug() << "Unknown board " << board; } - ftw->setCurrentIndex(selectedIndex); } emit autopilotConnected(); @@ -244,7 +210,7 @@ void ConfigGadgetWidget::tabAboutToChange(int i, bool *proceed) { Q_UNUSED(i); *proceed = true; - ConfigTaskWidget *wid = qobject_cast(ftw->currentWidget()); + ConfigTaskWidget *wid = qobject_cast(stackWidget->currentWidget()); if (!wid) { return; } @@ -275,7 +241,7 @@ void ConfigGadgetWidget::updateOPLinkStatus(UAVObject *) icon->addFile(":/configgadget/images/pipx-selected.png", QSize(), QIcon::Selected, QIcon::Off); QWidget *qwd = new ConfigPipXtremeWidget(this); - ftw->insertTab(ConfigGadgetWidget::oplink, qwd, *icon, QString("OPLink")); + stackWidget->insertTab(ConfigGadgetWidget::oplink, qwd, *icon, QString("OPLink")); oplinkConnected = true; } } @@ -284,6 +250,10 @@ void ConfigGadgetWidget::onOPLinkDisconnect() { qDebug() << "ConfigGadgetWidget onOPLinkDisconnect"; oplinkTimeout->stop(); - ftw->removeTab(ConfigGadgetWidget::oplink); oplinkConnected = false; + + if (stackWidget->currentIndex() == ConfigGadgetWidget::oplink) { + stackWidget->setCurrentIndex(0); + } + stackWidget->removeTab(ConfigGadgetWidget::oplink); } diff --git a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h index 45eadd8b5..0a443b9e0 100644 --- a/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h +++ b/ground/openpilotgcs/src/plugins/config/configgadgetwidget.h @@ -65,7 +65,6 @@ signals: protected: void resizeEvent(QResizeEvent *event); - MyTabbedStackWidget *ftw; private: UAVDataObject *oplinkStatusObj; @@ -73,6 +72,8 @@ private: // A timer that timesout the connction to the OPLink. QTimer *oplinkTimeout; bool oplinkConnected; + + MyTabbedStackWidget *stackWidget; }; #endif // CONFIGGADGETWIDGET_H diff --git a/ground/openpilotgcs/src/plugins/config/configinputwidget.cpp b/ground/openpilotgcs/src/plugins/config/configinputwidget.cpp index a4530978b..1f5402a06 100644 --- a/ground/openpilotgcs/src/plugins/config/configinputwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configinputwidget.cpp @@ -80,41 +80,46 @@ ConfigInputWidget::ConfigInputWidget(QWidget *parent) : unsigned int indexRT = 0; foreach(QString name, manualSettingsObj->getField("ChannelNumber")->getElementNames()) { Q_ASSERT(index < ManualControlSettings::CHANNELGROUPS_NUMELEM); - InputChannelForm *inpForm = new InputChannelForm(this, index == 0); - ui->channelSettings->layout()->addWidget(inpForm); // Add the row to the UI - inpForm->setName(name); + InputChannelForm *form = new InputChannelForm(index, this); + form->setName(name); + form->moveTo(*(ui->channelLayout)); // The order of the following binding calls is important. Since the values will be populated // in reverse order of the binding order otherwise the 'Reversed' logic will floor the neutral value // to the max value ( which is smaller than the neutral value when reversed ) and the channel number // will not be set correctly. - addWidgetBinding("ManualControlSettings", "ChannelNumber", inpForm->ui->channelNumber, index); - addWidgetBinding("ManualControlSettings", "ChannelGroups", inpForm->ui->channelGroup, index); - addWidgetBinding("ManualControlSettings", "ChannelNeutral", inpForm->ui->channelNeutral, index); - addWidgetBinding("ManualControlSettings", "ChannelNeutral", inpForm->ui->neutralValue, index); - addWidgetBinding("ManualControlSettings", "ChannelMax", inpForm->ui->channelMax, index); - addWidgetBinding("ManualControlSettings", "ChannelMin", inpForm->ui->channelMin, index); - addWidgetBinding("ManualControlSettings", "ChannelMax", inpForm->ui->channelMax, index); + addWidgetBinding("ManualControlSettings", "ChannelNumber", form->ui->channelNumber, index); + addWidgetBinding("ManualControlSettings", "ChannelGroups", form->ui->channelGroup, index); + addWidgetBinding("ManualControlSettings", "ChannelNeutral", form->ui->channelNeutral, index); + addWidgetBinding("ManualControlSettings", "ChannelNeutral", form->ui->neutralValue, index); + addWidgetBinding("ManualControlSettings", "ChannelMax", form->ui->channelMax, index); + addWidgetBinding("ManualControlSettings", "ChannelMin", form->ui->channelMin, index); + addWidgetBinding("ManualControlSettings", "ChannelMax", form->ui->channelMax, index); - addWidget(inpForm->ui->channelNumberDropdown); - addWidget(inpForm->ui->channelResponseTime); - addWidget(inpForm->ui->channelRev); + addWidget(form->ui->channelRev); + + // Reversing supported for some channels only + bool reversable = ((index == ManualControlSettings::CHANNELGROUPS_THROTTLE) || + (index == ManualControlSettings::CHANNELGROUPS_ROLL) || + (index == ManualControlSettings::CHANNELGROUPS_PITCH) || + (index == ManualControlSettings::CHANNELGROUPS_YAW)); + form->ui->channelRev->setVisible(reversable); // Input filter response time fields supported for some channels only switch (index) { case ManualControlSettings::CHANNELGROUPS_ROLL: case ManualControlSettings::CHANNELGROUPS_PITCH: case ManualControlSettings::CHANNELGROUPS_YAW: + case ManualControlSettings::CHANNELGROUPS_COLLECTIVE: case ManualControlSettings::CHANNELGROUPS_ACCESSORY0: case ManualControlSettings::CHANNELGROUPS_ACCESSORY1: case ManualControlSettings::CHANNELGROUPS_ACCESSORY2: - addWidgetBinding("ManualControlSettings", "ResponseTime", inpForm->ui->channelResponseTime, indexRT); + addWidgetBinding("ManualControlSettings", "ResponseTime", form->ui->channelResponseTime, indexRT); ++indexRT; break; case ManualControlSettings::CHANNELGROUPS_THROTTLE: case ManualControlSettings::CHANNELGROUPS_FLIGHTMODE: - case ManualControlSettings::CHANNELGROUPS_COLLECTIVE: - inpForm->ui->channelResponseTime->setEnabled(false); + form->ui->channelResponseTime->setVisible(false); break; default: Q_ASSERT(0); diff --git a/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp b/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp index 9ca2ed943..ba9618eea 100644 --- a/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp +++ b/ground/openpilotgcs/src/plugins/config/configoutputwidget.cpp @@ -76,10 +76,12 @@ ConfigOutputWidget::ConfigOutputWidget(QWidget *parent) : ConfigTaskWidget(paren // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10. // Register for ActuatorSettings changes: for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) { - OutputChannelForm *form = new OutputChannelForm(i, this, i == 0); + OutputChannelForm *form = new OutputChannelForm(i, this); + form->moveTo(*(ui->channelLayout)); + connect(ui->channelOutTest, SIGNAL(toggled(bool)), form, SLOT(enableChannelTest(bool))); connect(form, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int))); - ui->channelLayout->addWidget(form); + addWidget(form->ui.actuatorMin); addWidget(form->ui.actuatorNeutral); addWidget(form->ui.actuatorMax); @@ -194,7 +196,7 @@ OutputChannelForm *ConfigOutputWidget::getOutputChannelForm(const int index) con /** * Set the label for a channel output assignement */ -void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString str) +void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString &str) { // FIXME: use signal/ slot approach UAVObjectField *field = obj->getField(str); @@ -204,7 +206,7 @@ void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString str) OutputChannelForm *outputChannelForm = getOutputChannelForm(index); if (outputChannelForm) { - outputChannelForm->setAssignment(str); + outputChannelForm->setName(str); } } @@ -254,15 +256,15 @@ void ConfigOutputWidget::refreshWidgetsValues(UAVObject *obj) // Initialize output forms QList outputChannelForms = findChildren(); foreach(OutputChannelForm * outputChannelForm, outputChannelForms) { - outputChannelForm->setAssignment(ChannelDesc[outputChannelForm->index()]); + outputChannelForm->setName(ChannelDesc[outputChannelForm->index()]); // init min,max,neutral int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()]; int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()]; - outputChannelForm->minmax(minValue, maxValue); + outputChannelForm->setRange(minValue, maxValue); int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()]; - outputChannelForm->neutral(neutral); + outputChannelForm->setNeutral(neutral); } // Get the SpinWhileArmed setting @@ -349,10 +351,10 @@ void ConfigOutputWidget::refreshWidgetsValues(UAVObject *obj) int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()]; int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()]; - outputChannelForm->minmax(minValue, maxValue); + outputChannelForm->setRange(minValue, maxValue); int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()]; - outputChannelForm->neutral(neutral); + outputChannelForm->setNeutral(neutral); } setDirty(dirty); diff --git a/ground/openpilotgcs/src/plugins/config/configoutputwidget.h b/ground/openpilotgcs/src/plugins/config/configoutputwidget.h index 1cabfb5a2..fda2922c6 100644 --- a/ground/openpilotgcs/src/plugins/config/configoutputwidget.h +++ b/ground/openpilotgcs/src/plugins/config/configoutputwidget.h @@ -47,6 +47,8 @@ public: ConfigOutputWidget(QWidget *parent = 0); ~ConfigOutputWidget(); +protected: + void enableControls(bool enable); private: Ui_OutputWidget *ui; @@ -55,14 +57,14 @@ private: void updateChannelInSlider(QSlider *slider, QLabel *min, QLabel *max, QCheckBox *rev, int value); - void assignChannel(UAVDataObject *obj, QString str); - void assignOutputChannel(UAVDataObject *obj, QString str); + void assignOutputChannel(UAVDataObject *obj, QString &str); OutputChannelForm *getOutputChannelForm(const int index) const; int mccDataRate; UAVObject::Metadata accInitialData; bool wasItMe; + private slots: void stopTests(); void disableIfNotMe(UAVObject *obj); @@ -71,8 +73,6 @@ private slots: void runChannelTests(bool state); void sendChannelTest(int index, int value); void openHelp(); -protected: - void enableControls(bool enable); }; -#endif // ifndef CONFIGOUTPUTWIDGET_H +#endif // CONFIGOUTPUTWIDGET_H diff --git a/ground/openpilotgcs/src/plugins/config/input.ui b/ground/openpilotgcs/src/plugins/config/input.ui index 48fee6df8..b53d7ba6d 100644 --- a/ground/openpilotgcs/src/plugins/config/input.ui +++ b/ground/openpilotgcs/src/plugins/config/input.ui @@ -116,8 +116,8 @@ 0 0 - 774 - 748 + 772 + 751 @@ -143,16 +143,16 @@ - 12 + 9 - 0 + 9 - 12 + 9 - 0 + 9 @@ -162,21 +162,21 @@ - 12 + 0 - 12 + 0 - 12 + 0 - 12 + 0 - - - 0 + + + 12 @@ -198,7 +198,7 @@ - + Qt::Horizontal @@ -211,14 +211,14 @@ - + Roll/Pitch/Yaw stick deadband - + Stick deadband in percents of full range (0-10), zero to disable @@ -234,22 +234,6 @@ - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 12 - 20 - - - - @@ -260,7 +244,7 @@ 20 - 40 + 10 @@ -369,6 +353,12 @@ + + + 0 + 0 + + 210 @@ -413,6 +403,12 @@ true + + + 0 + 0 + + 210 @@ -546,8 +542,8 @@ 0 0 - 774 - 748 + 772 + 751 @@ -2048,8 +2044,8 @@ Setup the flight mode channel on the RC Input tab if you have not done so alread 0 0 - 504 - 156 + 772 + 751 @@ -2238,7 +2234,7 @@ Set to 0 to disable (recommended for soaring fixed wings). - + :/core/images/helpicon.svg:/core/images/helpicon.svg diff --git a/ground/openpilotgcs/src/plugins/config/inputchannelform.cpp b/ground/openpilotgcs/src/plugins/config/inputchannelform.cpp index f6df2a3c0..8d5ff8c7f 100644 --- a/ground/openpilotgcs/src/plugins/config/inputchannelform.cpp +++ b/ground/openpilotgcs/src/plugins/config/inputchannelform.cpp @@ -4,69 +4,36 @@ #include "manualcontrolsettings.h" #include "gcsreceiver.h" -InputChannelForm::InputChannelForm(QWidget *parent, bool showlegend) : - ConfigTaskWidget(parent), - ui(new Ui::InputChannelForm) +InputChannelForm::InputChannelForm(const int index, QWidget *parent) : + ChannelForm(index, parent), 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); - layout()->removeWidget(ui->legend1); - layout()->removeWidget(ui->legend2); - layout()->removeWidget(ui->legend3); - layout()->removeWidget(ui->legend4); - layout()->removeWidget(ui->legend5); - layout()->removeWidget(ui->legend6); - layout()->removeWidget(ui->legend7); - delete ui->legend0; - delete ui->legend1; - delete ui->legend2; - delete ui->legend3; - delete ui->legend4; - delete ui->legend5; - delete ui->legend6; - delete ui->legend7; - } - connect(ui->channelMin, SIGNAL(valueChanged(int)), this, SLOT(minMaxUpdated())); connect(ui->channelMax, SIGNAL(valueChanged(int)), this, SLOT(minMaxUpdated())); connect(ui->neutralValue, SIGNAL(valueChanged(int)), this, SLOT(neutralUpdated())); connect(ui->channelGroup, SIGNAL(currentIndexChanged(int)), this, SLOT(groupUpdated())); connect(ui->channelRev, SIGNAL(toggled(bool)), this, SLOT(reversedUpdated())); - // This is awkward but since we want the UI to be a dropdown but the field is not an enum - // it breaks the UAUVObject widget relation of the task gadget. Running the data through - // a spin box fixes this - connect(ui->channelNumberDropdown, SIGNAL(currentIndexChanged(int)), this, SLOT(channelDropdownUpdated(int))); - connect(ui->channelNumber, SIGNAL(valueChanged(int)), this, SLOT(channelNumberUpdated(int))); - disableMouseWheelEvents(); } - InputChannelForm::~InputChannelForm() { delete ui; } -void InputChannelForm::setName(QString &name) +QString InputChannelForm::name() +{ + return ui->channelName->text(); +} + +/** + * Set the channel assignment label. + */ +void InputChannelForm::setName(const QString &name) { ui->channelName->setText(name); - QFontMetrics metrics(ui->channelName->font()); - int width = metrics.width(name) + 5; - foreach(InputChannelForm * form, parent()->findChildren()) { - if (form == this) { - continue; - } - if (form->ui->channelName->minimumSize().width() < width) { - form->ui->channelName->setMinimumSize(width, 0); - } else { - width = form->ui->channelName->minimumSize().width(); - } - } - ui->channelName->setMinimumSize(width, 0); } /** @@ -136,8 +103,8 @@ void InputChannelForm::reversedUpdated() */ void InputChannelForm::groupUpdated() { - ui->channelNumberDropdown->clear(); - ui->channelNumberDropdown->addItem("Disabled"); + ui->channelNumber->clear(); + ui->channelNumber->addItem("Disabled"); quint8 count = 0; @@ -168,25 +135,6 @@ void InputChannelForm::groupUpdated() } for (int i = 0; i < count; i++) { - ui->channelNumberDropdown->addItem(QString(tr("Chan %1").arg(i + 1))); + ui->channelNumber->addItem(QString(tr("Chan %1").arg(i + 1))); } - - ui->channelNumber->setMaximum(count); - ui->channelNumber->setMinimum(0); -} - -/** - * Update the dropdown from the hidden control - */ -void InputChannelForm::channelDropdownUpdated(int newval) -{ - ui->channelNumber->setValue(newval); -} - -/** - * Update the hidden control from the dropdown - */ -void InputChannelForm::channelNumberUpdated(int newval) -{ - ui->channelNumberDropdown->setCurrentIndex(newval); } diff --git a/ground/openpilotgcs/src/plugins/config/inputchannelform.h b/ground/openpilotgcs/src/plugins/config/inputchannelform.h index 91d665b40..f48bcbd0a 100644 --- a/ground/openpilotgcs/src/plugins/config/inputchannelform.h +++ b/ground/openpilotgcs/src/plugins/config/inputchannelform.h @@ -1,27 +1,32 @@ #ifndef INPUTCHANNELFORM_H #define INPUTCHANNELFORM_H -#include +#include "channelform.h" #include "configinputwidget.h" + +#include + namespace Ui { class InputChannelForm; } -class InputChannelForm : public ConfigTaskWidget { +class InputChannelForm : public ChannelForm { Q_OBJECT public: - explicit InputChannelForm(QWidget *parent = 0, bool showlegend = false); + explicit InputChannelForm(const int index, QWidget *parent = NULL); ~InputChannelForm(); + friend class ConfigInputWidget; - void setName(QString &name); + + virtual QString name(); + virtual void setName(const QString &name); + private slots: void minMaxUpdated(); void neutralUpdated(); void reversedUpdated(); void groupUpdated(); - void channelDropdownUpdated(int); - void channelNumberUpdated(int); private: Ui::InputChannelForm *ui; diff --git a/ground/openpilotgcs/src/plugins/config/inputchannelform.ui b/ground/openpilotgcs/src/plugins/config/inputchannelform.ui index d07f2ae3b..2f4db720c 100644 --- a/ground/openpilotgcs/src/plugins/config/inputchannelform.ui +++ b/ground/openpilotgcs/src/plugins/config/inputchannelform.ui @@ -6,8 +6,8 @@ 0 0 - 828 - 93 + 923 + 51 @@ -24,268 +24,18 @@ 0 - 6 + 0 + + + 12 - - - - true - - - - 0 - 0 - - - - - 0 - 20 - - - - - 75 - false - true - - - - Channel neutral - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - Neutral - - - Qt::AlignCenter - - - - - - - - 0 - 25 - - - - Qt::StrongFocus - - - QAbstractSpinBox::UpDownArrows - - - 9999 - - - 1000 - - - - - - - true - - - - 0 - 0 - - - - - 30 - 20 - - - - - 75 - false - true - - - - Response time - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - 1 - - - RT - - - Qt::AlignCenter - - - - - - - true - - - - 0 - 0 - - - - - 75 - 20 - - - - - 75 - false - true - - - - Channel values are inverted - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - 1 - - - Reversed - - - Qt::AlignCenter - - - - - - - true - - - - 0 - 0 - - - - - 0 - 20 - - - - - 75 - false - true - - - - Channel function - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - Function - - - Qt::AlignCenter - - - - - - - true - - - - 0 - 0 - - - - - 0 - 20 - - - - - 75 - false - true - - - - Channel type - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - Type - - - Qt::AlignCenter - - - true - + 0 0 @@ -298,6 +48,7 @@ font:bold; + -1 75 false true @@ -310,8 +61,8 @@ font:bold; 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:1px; -font:bold; +font: bold 12px; +margin:1px; QFrame::StyledPanel @@ -324,133 +75,47 @@ font:bold; - - - - true + + + + Qt::Horizontal - - - 0 - 0 - + + QSizePolicy::Fixed - + - 0 + 10 20 - - - 75 - false - true - - - - Channel min - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - Min - - - Qt::AlignCenter - - + - - + + - + 0 0 - - - 80 - 25 - - - - TextLabel - - - - - - - - 4 - 0 - - 0 - 25 + 0 - 90 + 16777215 16777215 Qt::StrongFocus - - 7 - - - - - - - - 6 - 0 - - - - - 0 - 25 - - - - - 100 - 16777215 - - - - Qt::StrongFocus - - - - - - - - 0 - 25 - - - - Qt::StrongFocus + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter QAbstractSpinBox::UpDownArrows @@ -463,123 +128,111 @@ font:bold; - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - - - true - + + - + 0 0 - 0 - 20 + 110 + 0 - - - 75 - false - true - - - - Channel max - - - 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:1px; -font:bold; - - - QFrame::StyledPanel - - - 1 - - - Max - - - Qt::AlignCenter - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - + - 10 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 22 + 16777215 + 16777215 Qt::StrongFocus - - Qt::Horizontal + + QComboBox::AdjustToContents - - - - true - + + - + 0 0 - 30 - 25 + 100 + 0 + + + + + 16777215 + 16777215 + + + + Qt::StrongFocus + + + 7 + + + QComboBox::AdjustToContents + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 16777215 + 16777215 + + + + Text + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 @@ -598,6 +251,9 @@ even lead to crash. Use with caution. true + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + QAbstractSpinBox::UpDownArrows @@ -606,21 +262,544 @@ even lead to crash. Use with caution. - + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel function + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + Function + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel values are inverted + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + 1 + + + Reversed + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Response time + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + 1 + + + RT + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel max + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + 1 + + + Max + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel neutral + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + Neutral + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel min + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + Min + + + Qt::AlignCenter + + + + + + + true + + + + 0 + 0 + + + + + 0 + 20 + + + + + -1 + 75 + false + true + + + + Channel type + + + 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; +font: bold 12px; +margin:1px; + + + QFrame::StyledPanel + + + Type + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Qt::StrongFocus + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + QAbstractSpinBox::UpDownArrows + + + 9999 + + + 1000 + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 2 + + + 0 + + + 2 + + + 0 + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Qt::StrongFocus + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 + + + 1000 + + + + + + + + + + + 0 + 0 + + 75 0 + + + 16777215 + 16777215 + + QFrame::NoFrame QFrame::Raised - + 0 @@ -633,15 +812,21 @@ even lead to crash. Use with caution. 0 - + true + + + 0 + 0 + + 0 - 20 + 0 @@ -652,51 +837,8 @@ even lead to crash. Use with caution. - - - - - 0 - 25 - - - - 9999 - - - 1000 - - - - - - true - - - - 216 - 26 - 0 - 0 - - - - - 0 - 0 - - - - - channelNumber - channelGroup - channelNumberDropdown - channelMin - channelNeutral - channelMax - diff --git a/ground/openpilotgcs/src/plugins/config/output.ui b/ground/openpilotgcs/src/plugins/config/output.ui index aa2f892c7..575b03e58 100644 --- a/ground/openpilotgcs/src/plugins/config/output.ui +++ b/ground/openpilotgcs/src/plugins/config/output.ui @@ -122,8 +122,8 @@ 0 0 - 676 - 674 + 674 + 677 @@ -654,41 +654,46 @@ Leave at 50Hz for fixed wing. - - - QLayout::SetDefaultConstraint + + + 12 - - - - - - 519 - 20 - - - - Motors spin at neutral output when armed and throttle below zero (be careful) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + 0 + 0 + + + + + 519 + 20 + + + + Motors spin at neutral output when armed and throttle below zero (be careful) + + diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp b/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp index a69a88bf9..0ca1ecaf7 100644 --- a/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.cpp @@ -26,52 +26,24 @@ */ #include "outputchannelform.h" -#include "configoutputwidget.h" -OutputChannelForm::OutputChannelForm(const int index, QWidget *parent, const bool showLegend) : - ConfigTaskWidget(parent), - ui(), - m_index(index), - m_inChannelTest(false) +OutputChannelForm::OutputChannelForm(const int index, QWidget *parent) : + ChannelForm(index, parent), ui(), m_inChannelTest(false) { ui.setupUi(this); - if (!showLegend) { - // Remove legend - QGridLayout *grid_layout = dynamic_cast(layout()); - Q_ASSERT(grid_layout); - for (int col = 0; col < grid_layout->columnCount(); col++) { // remove every item in first row - QLayoutItem *item = grid_layout->itemAtPosition(0, col); - if (!item) { - continue; - } - // get widget from layout item - QWidget *legend_widget = item->widget(); - if (!legend_widget) { - continue; - } - // delete widget - grid_layout->removeWidget(legend_widget); - delete legend_widget; - } - } // The convention for OP is Channel 1 to Channel 10. - ui.actuatorNumber->setText(QString("%1:").arg(m_index + 1)); + ui.actuatorNumber->setText(QString("%1:").arg(index + 1)); // Register for ActuatorSettings changes: - connect(ui.actuatorMin, SIGNAL(editingFinished()), - this, SLOT(setChannelRange())); - connect(ui.actuatorMax, SIGNAL(editingFinished()), - this, SLOT(setChannelRange())); - connect(ui.actuatorRev, SIGNAL(toggled(bool)), - this, SLOT(reverseChannel(bool))); + connect(ui.actuatorMin, SIGNAL(editingFinished()), this, SLOT(setChannelRange())); + connect(ui.actuatorMax, SIGNAL(editingFinished()), this, SLOT(setChannelRange())); + connect(ui.actuatorRev, SIGNAL(toggled(bool)), this, SLOT(reverseChannel(bool))); // Now connect the channel out sliders to our signal to send updates in test mode - connect(ui.actuatorNeutral, SIGNAL(valueChanged(int)), - this, SLOT(sendChannelTest(int))); + connect(ui.actuatorNeutral, SIGNAL(valueChanged(int)), this, SLOT(sendChannelTest(int))); ui.actuatorLink->setChecked(false); - connect(ui.actuatorLink, SIGNAL(toggled(bool)), - this, SLOT(linkToggled(bool))); + connect(ui.actuatorLink, SIGNAL(toggled(bool)), this, SLOT(linkToggled(bool))); disableMouseWheelEvents(); } @@ -81,6 +53,19 @@ OutputChannelForm::~OutputChannelForm() // Do nothing } +QString OutputChannelForm::name() +{ + return ui.actuatorName->text(); +} + +/** + * Set the channel assignment label. + */ +void OutputChannelForm::setName(const QString &name) +{ + ui.actuatorName->setText(name); +} + /** * Restrict UI to protect users from accidental misuse. */ @@ -149,26 +134,49 @@ void OutputChannelForm::linkToggled(bool state) } } +int OutputChannelForm::max() const +{ + return ui.actuatorMax->value(); +} + /** * Set maximal channel value. */ -void OutputChannelForm::max(int maximum) +void OutputChannelForm::setMax(int maximum) { - minmax(ui.actuatorMin->value(), maximum); + setRange(ui.actuatorMin->value(), maximum); +} + +int OutputChannelForm::min() const +{ + return ui.actuatorMin->value(); } /** * Set minimal channel value. */ -void OutputChannelForm::min(int minimum) +void OutputChannelForm::setMin(int minimum) { - minmax(minimum, ui.actuatorMax->value()); + setRange(minimum, ui.actuatorMax->value()); +} + +int OutputChannelForm::neutral() const +{ + return ui.actuatorNeutral->value(); +} + +/** + * Set neutral of channel. + */ +void OutputChannelForm::setNeutral(int value) +{ + ui.actuatorNeutral->setValue(value); } /** * Set minimal and maximal channel value. */ -void OutputChannelForm::minmax(int minimum, int maximum) +void OutputChannelForm::setRange(int minimum, int maximum) { ui.actuatorMin->setValue(minimum); ui.actuatorMax->setValue(maximum); @@ -180,35 +188,6 @@ void OutputChannelForm::minmax(int minimum, int maximum) } } -/** - * Set neutral of channel. - */ -void OutputChannelForm::neutral(int value) -{ - ui.actuatorNeutral->setValue(value); -} - -/** - * Set the channel assignment label. - */ -void OutputChannelForm::setAssignment(const QString &assignment) -{ - ui.actuatorName->setText(assignment); - QFontMetrics metrics(ui.actuatorName->font()); - int width = metrics.width(assignment) + 1; - foreach(OutputChannelForm * form, parent()->findChildren()) { - if (form == this) { - continue; - } - if (form->ui.actuatorName->minimumSize().width() < width) { - form->ui.actuatorName->setMinimumSize(width, 0); - } else { - width = form->ui.actuatorName->minimumSize().width(); - } - } - ui.actuatorName->setMinimumSize(width, 0); -} - /** * Sets the minimum/maximum value of the channel output sliders. * Have to do it here because setMinimum is not a slot. @@ -220,7 +199,7 @@ void OutputChannelForm::setChannelRange() { int oldMini = ui.actuatorNeutral->minimum(); -// int oldMaxi = ui.actuatorNeutral->maximum(); + // int oldMaxi = ui.actuatorNeutral->maximum(); if (ui.actuatorMin->value() < ui.actuatorMax->value()) { ui.actuatorNeutral->setRange(ui.actuatorMin->value(), ui.actuatorMax->value()); @@ -234,8 +213,9 @@ void OutputChannelForm::setChannelRange() ui.actuatorNeutral->setValue(ui.actuatorNeutral->minimum()); } -// if (ui.actuatorNeutral->value() == oldMaxi) -// ui.actuatorNeutral->setValue(ui.actuatorNeutral->maximum()); // this can be dangerous if it happens to be controlling a motor at the time! + // if (ui.actuatorNeutral->value() == oldMaxi) + // this can be dangerous if it happens to be controlling a motor at the time! + // ui.actuatorNeutral->setValue(ui.actuatorNeutral->maximum()); } /** @@ -252,8 +232,7 @@ void OutputChannelForm::reverseChannel(bool state) return; } - // Now, swap the min & max values (only on the spinboxes, the slider - // does not change! + // Now, swap the min & max values (only on the spinboxes, the slider does not change!) int temp = ui.actuatorMax->value(); ui.actuatorMax->setValue(ui.actuatorMin->value()); ui.actuatorMin->setValue(temp); @@ -286,12 +265,14 @@ void OutputChannelForm::sendChannelTest(int value) } if (ui.actuatorRev->isChecked()) { - value = ui.actuatorMin->value() - value + ui.actuatorMax->value(); // the channel is reversed + // the channel is reversed + value = ui.actuatorMin->value() - value + ui.actuatorMax->value(); } // update the label - ui.actuatorValue->setText(QString::number(value)); + ui.actuatorValue->setValue(value); - if (ui.actuatorLink->checkState() && parent()) { // the channel is linked to other channels + if (ui.actuatorLink->checkState() && parent()) { + // the channel is linked to other channels QList outputChannelForms = parent()->findChildren(); // set the linked channels of the parent widget to the same value foreach(OutputChannelForm * outputChannelForm, outputChannelForms) { @@ -315,12 +296,13 @@ void OutputChannelForm::sendChannelTest(int value) } outputChannelForm->ui.actuatorNeutral->setValue(val); - outputChannelForm->ui.actuatorValue->setText(QString::number(val)); + outputChannelForm->ui.actuatorValue->setValue(val); } } if (!m_inChannelTest) { - return; // we are not in Test Output mode + // we are not in Test Output mode + return; } emit channelChanged(index(), value); } diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.h b/ground/openpilotgcs/src/plugins/config/outputchannelform.h index a537f55e7..78d008115 100644 --- a/ground/openpilotgcs/src/plugins/config/outputchannelform.h +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.h @@ -27,29 +27,32 @@ #ifndef OUTPUTCHANNELFORM_H #define OUTPUTCHANNELFORM_H -#include +#include "channelform.h" +#include "configoutputwidget.h" #include "ui_outputchannelform.h" -#include "configtaskwidget.h" -class OutputChannelForm : public ConfigTaskWidget { +#include + +class OutputChannelForm : public ChannelForm { Q_OBJECT public: - explicit OutputChannelForm(const int index, QWidget *parent = NULL, const bool showLegend = false); + explicit OutputChannelForm(const int index, QWidget *parent = NULL); ~OutputChannelForm(); + friend class ConfigOutputWidget; - void setAssignment(const QString &assignment); - int index() const; + virtual QString name(); + virtual void setName(const QString &name); public slots: - void max(int maximum); - int max() const; - void min(int minimum); int min() const; - void minmax(int minimum, int maximum); - void neutral(int value); + void setMin(int minimum); + int max() const; + void setMax(int maximum); int neutral() const; + void setNeutral(int value); + void setRange(int minimum, int maximum); void enableChannelTest(bool state); signals: @@ -57,8 +60,6 @@ signals: private: Ui::outputChannelForm ui; - /// Channel index - int m_index; bool m_inChannelTest; private slots: @@ -68,23 +69,4 @@ private slots: void setChannelRange(); }; -inline int OutputChannelForm::index() const -{ - return m_index; -} - -inline int OutputChannelForm::max() const -{ - return ui.actuatorMax->value(); -} - -inline int OutputChannelForm::min() const -{ - return ui.actuatorMin->value(); -} - -inline int OutputChannelForm::neutral() const -{ - return ui.actuatorNeutral->value(); -} #endif // OUTPUTCHANNELFORM_H diff --git a/ground/openpilotgcs/src/plugins/config/outputchannelform.ui b/ground/openpilotgcs/src/plugins/config/outputchannelform.ui index abe5d04dd..f6227939a 100644 --- a/ground/openpilotgcs/src/plugins/config/outputchannelform.ui +++ b/ground/openpilotgcs/src/plugins/config/outputchannelform.ui @@ -6,39 +6,52 @@ 0 0 - 825 - 58 + 768 + 51 Form + + 0 + - 1 + 0 + + + 0 - 1 + 0 12 - - + + - + 0 0 - 45 + 0 20 + + + 16777215 + 16777215 + + + -1 75 false true @@ -48,37 +61,61 @@ 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; -font:bold; +font: bold 12px; margin:1px; - Link + Assignment Qt::AlignCenter - - + + + + + 0 + 0 + + 0 - 25 + 20 - - Qt::StrongFocus + + + 16777215 + 16777215 + - - Maximum PWM value, beware of not overdriving your servo. + + + -1 + 75 + false + true + - - 9999 + + 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; +font: bold 12px; +margin:1px; + + + Min + + + Qt::AlignCenter - + @@ -94,6 +131,7 @@ margin:1px; + -1 75 false true @@ -103,7 +141,7 @@ margin:1px; 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; -font:bold; +font: bold 12px; margin:1px; @@ -114,10 +152,26 @@ margin:1px; + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + - + 0 0 @@ -128,8 +182,15 @@ margin:1px; 20 + + + 16777215 + 16777215 + + + -1 75 false true @@ -142,7 +203,7 @@ margin:1px; 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; -font:bold; +font: bold 12px; margin:1px; @@ -156,13 +217,13 @@ margin:1px; - - + + Qt::Horizontal - QSizePolicy::Minimum + QSizePolicy::Fixed @@ -172,10 +233,42 @@ margin:1px; - - + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + - + 0 0 @@ -183,37 +276,13 @@ margin:1px; 0 - 20 + 0 - - - 75 - false - 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; -font:bold; -margin:1px; - - - Assignment - - - Qt::AlignCenter - - - - - - + - 0 - 25 + 16777215 + 16777215 @@ -222,69 +291,14 @@ margin:1px; Minimum PWM value, beware of not overdriving your servo. - - 9999 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - Qt::StrongFocus - - - 9999 - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 75 - false - 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; -font:bold; -margin:1px; - - - Max - - Qt::AlignCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 + + + 0 @@ -299,35 +313,154 @@ margin:1px; 20 - 25 + 0 + + + + + 16777215 + 16777215 Channel Number - TextLabel + 0: Qt::AlignCenter - - - - Qt::Horizontal + + + + + 0 + 0 + - - QSizePolicy::Minimum - - + - 5 + 0 20 - + + + 16777215 + 16777215 + + + + + -1 + 75 + false + 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; +font: bold 12px; +margin:1px; + + + Max + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 16777215 + + + + + -1 + 75 + false + 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; +font: bold 12px; +margin:1px; + + + Reversed + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + 16777215 + 16777215 + + + + + -1 + 75 + false + 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; +font: bold 12px; +margin:1px; + + + Link + + + Qt::AlignCenter + + @@ -340,21 +473,27 @@ margin:1px; 110 - 25 + 0 + + + + + 16777215 + 16777215 - TextLabel + - Qt::AlignCenter - - + + - + 0 0 @@ -362,98 +501,133 @@ margin:1px; 0 - 20 + 0 - - - 75 - false - true - + + + 16777215 + 16777215 + - - 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; -font:bold; -margin:1px; + + Qt::StrongFocus - - Reversed + + Maximum PWM value, beware of not overdriving your servo. - Qt::AlignCenter + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 - - + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 2 + + + 0 + + + 2 + + + 0 + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Qt::StrongFocus + + + 9999 + + + Qt::Horizontal + + + + + + + true + + + + 0 + 0 + + + + true + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + QAbstractSpinBox::UpDownArrows + + + 9999 + + + + + + + + 0 0 - - - 0 - 25 - - - - Current value of slider. - - - 0000 - - - - - - - - 0 - 0 - - - - - 0 - 20 - - - - - 75 - false - 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; -font:bold; -margin:1px; - - - Min - - - Qt::AlignCenter - - - - - 75 0 - + + QFrame::NoFrame + + + + 0 + 0 @@ -466,13 +640,10 @@ margin:1px; 0 - - 0 - - + - + 0 0 @@ -494,8 +665,14 @@ margin:1px; - - + + + + + 0 + 0 + + 45 @@ -506,9 +683,12 @@ margin:1px; QFrame::NoFrame - QFrame::Raised + QFrame::Plain - + + + 0 + 0 @@ -521,13 +701,10 @@ margin:1px; 0 - - 0 - - + - + 0 0 @@ -551,11 +728,6 @@ margin:1px; - - actuatorMin - actuatorNeutral - actuatorMax - diff --git a/ground/openpilotgcs/src/plugins/coreplugin/mainwindow.cpp b/ground/openpilotgcs/src/plugins/coreplugin/mainwindow.cpp index 12b89cd46..81817c5d1 100644 --- a/ground/openpilotgcs/src/plugins/coreplugin/mainwindow.cpp +++ b/ground/openpilotgcs/src/plugins/coreplugin/mainwindow.cpp @@ -270,7 +270,7 @@ void MainWindow::extensionsInitialized() // We'll use qApp macro to get the QApplication pointer // and set the style sheet application wide. - qDebug() << "Setting application style sheet to:" << style; + // qDebug() << "Setting application style sheet to:" << style; qApp->setStyleSheet(style); qs->endGroup(); diff --git a/ground/openpilotgcs/src/plugins/hitl/fgsimulator.cpp b/ground/openpilotgcs/src/plugins/hitl/fgsimulator.cpp index 1b4b2a8da..eb53b75fb 100644 --- a/ground/openpilotgcs/src/plugins/hitl/fgsimulator.cpp +++ b/ground/openpilotgcs/src/plugins/hitl/fgsimulator.cpp @@ -268,12 +268,12 @@ void FGSimulator::processUpdate(const QByteArray & inp) float temperature = fields[19].toFloat(); // Get pressure (kpa) float pressure = fields[20].toFloat() * INHG2KPA; - // Get VelocityState Down (cm/s) - float velocityStateDown = -fields[21].toFloat() * FPS2CMPS; - // Get VelocityState East (cm/s) - float velocityStateEast = fields[22].toFloat() * FPS2CMPS; - // Get VelocityState Down (cm/s) - float velocityStateNorth = fields[23].toFloat() * FPS2CMPS; + // Get VelocityState Down (m/s) + float velocityStateDown = -fields[21].toFloat() * FPS2CMPS * 1e-2f; + // Get VelocityState East (m/s) + float velocityStateEast = fields[22].toFloat() * FPS2CMPS * 1e-2f; + // Get VelocityState Down (m/s) + float velocityStateNorth = fields[23].toFloat() * FPS2CMPS * 1e-2f; // Get UDP packets received by FG int n = fields[24].toInt(); @@ -286,16 +286,15 @@ void FGSimulator::processUpdate(const QByteArray & inp) Output2Hardware out; memset(&out, 0, sizeof(Output2Hardware)); + HomeLocation::DataFields homeData = posHome->getData(); + double HomeLLA[3] = { (double)homeData.Latitude * 1e-7, (double)homeData.Longitude * 1e-7, homeData.Altitude }; + double HomeECEF[3]; + float HomeRNE[3][3]; + double LLA[3] = { latitude, longitude, altitude_msl }; float NED[3]; - // convert from cm back to meters - - double LLA[3] = { latitude, longitude, altitude_msl }; - double ECEF[3]; - double RNE[9]; - Utils::CoordinateConversions().RneFromLLA(LLA, (double(*)[3])RNE); - Utils::CoordinateConversions().LLA2ECEF(LLA, ECEF); - Utils::CoordinateConversions().LLA2Base(LLA, ECEF, (float(*)[3])RNE, NED); - + Utils::CoordinateConversions().RneFromLLA(HomeLLA, HomeRNE); + Utils::CoordinateConversions().LLA2ECEF(HomeLLA, HomeECEF); + Utils::CoordinateConversions().LLA2Base(LLA, HomeECEF, HomeRNE, NED); // Update GPS Position objects out.latitude = latitude * 1e7; diff --git a/ground/openpilotgcs/src/plugins/plugins.pro b/ground/openpilotgcs/src/plugins/plugins.pro index c09d5567d..2019d848d 100644 --- a/ground/openpilotgcs/src/plugins/plugins.pro +++ b/ground/openpilotgcs/src/plugins/plugins.pro @@ -52,8 +52,9 @@ plugin_uavtalk.depends += plugin_coreplugin # Telemetry plugin SUBDIRS += plugin_telemetry plugin_telemetry.subdir = telemetry +plugin_telemetry.depends = plugin_coreplugin +plugin_telemetry.depends += plugin_uavobjectutil plugin_telemetry.depends += plugin_uavtalk -plugin_telemetry.depends += plugin_coreplugin # OPMap UAVGadget plugin_opmap.subdir = opmap @@ -98,9 +99,9 @@ macx:contains(QT_VERSION, ^4\\.8\\.0): CONFIG += disable_notify_plugin plugin_uploader.subdir = uploader plugin_uploader.depends = plugin_coreplugin plugin_uploader.depends += plugin_uavobjects +plugin_uploader.depends += plugin_uavobjectutil plugin_uploader.depends += plugin_uavtalk plugin_uploader.depends += plugin_opHID -plugin_uploader.depends += plugin_uavobjectutil SUBDIRS += plugin_uploader # Dial gadget diff --git a/ground/openpilotgcs/src/plugins/telemetry/telemetry.pro b/ground/openpilotgcs/src/plugins/telemetry/telemetry.pro index 42335df16..a1d8a8ad9 100644 --- a/ground/openpilotgcs/src/plugins/telemetry/telemetry.pro +++ b/ground/openpilotgcs/src/plugins/telemetry/telemetry.pro @@ -1,12 +1,11 @@ TEMPLATE = lib TARGET = Telemetry +DEFINES += TELEMETRY_LIBRARY QT += svg -include(../../openpilotgcsplugin.pri) -include(../../plugins/coreplugin/coreplugin.pri) -include(../../libs/version_info/version_info.pri) include(telemetry_dependencies.pri) +include(../../libs/version_info/version_info.pri) HEADERS += telemetry_global.h \ telemetryplugin.h \ @@ -23,8 +22,6 @@ SOURCES += telemetryplugin.cpp \ monitorgadgetfactory.cpp \ monitorgadgetoptionspage.cpp -DEFINES += TELEMETRY_LIBRARY +OTHER_FILES += Telemetry.pluginspec RESOURCES += telemetry.qrc - -OTHER_FILES += Telemetry.pluginspec diff --git a/ground/openpilotgcs/src/plugins/telemetry/telemetry_dependencies.pri b/ground/openpilotgcs/src/plugins/telemetry/telemetry_dependencies.pri index 3ee3a36e1..702d40aee 100644 --- a/ground/openpilotgcs/src/plugins/telemetry/telemetry_dependencies.pri +++ b/ground/openpilotgcs/src/plugins/telemetry/telemetry_dependencies.pri @@ -1,4 +1,4 @@ -include(../../plugins/uavobjects/uavobjects.pri) +include(../../openpilotgcsplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) include(../../plugins/uavobjectutil/uavobjectutil.pri) include(../../plugins/uavtalk/uavtalk.pri) - diff --git a/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.cpp b/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.cpp index 235448a57..1ab86536a 100644 --- a/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.cpp +++ b/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.cpp @@ -199,14 +199,14 @@ void UAVObjectField::limitsInitialize(const QString &limits) elementLimits.insert(index, limitList); ++index; } - foreach(QList limitList, elementLimits) { - foreach(LimitStruct limit, limitList) { - qDebug() << "Limit type" << limit.type << "for board" << limit.board << "for field" << getName(); - foreach(QVariant var, limit.values) { - qDebug() << "value" << var; - } - } - } + // foreach(QList limitList, elementLimits) { + // foreach(LimitStruct limit, limitList) { + // qDebug() << "Limit type" << limit.type << "for board" << limit.board << "for field" << getName(); + // foreach(QVariant var, limit.values) { + // qDebug() << "value" << var; + // } + // } + // } } bool UAVObjectField::isWithinLimits(QVariant var, quint32 index, int board) { @@ -811,43 +811,31 @@ bool UAVObjectField::isNumeric() { switch (type) { case INT8: - return true; - - break; case INT16: - return true; - - break; case INT32: - return true; - - break; case UINT8: - return true; - - break; case UINT16: - return true; - - break; case UINT32: - return true; - - break; case FLOAT32: - return true; - - break; - case ENUM: - return false; - - break; case BITFIELD: return true; break; - case STRING: + default: return false; + } +} + +bool UAVObjectField::isInteger() +{ + switch (type) { + case INT8: + case INT16: + case INT32: + case UINT8: + case UINT16: + case UINT32: + return true; break; default: @@ -858,42 +846,7 @@ bool UAVObjectField::isNumeric() bool UAVObjectField::isText() { switch (type) { - case INT8: - return false; - - break; - case INT16: - return false; - - break; - case INT32: - return false; - - break; - case UINT8: - return false; - - break; - case UINT16: - return false; - - break; - case UINT32: - return false; - - break; - case FLOAT32: - return false; - - break; case ENUM: - return true; - - break; - case BITFIELD: - return false; - - break; case STRING: return true; diff --git a/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.h b/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.h index 141fa6af7..778096729 100644 --- a/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.h +++ b/ground/openpilotgcs/src/plugins/uavobjects/uavobjectfield.h @@ -71,6 +71,7 @@ public: quint32 getDataOffset(); quint32 getNumBytes(); bool isNumeric(); + bool isInteger(); bool isText(); QString toString(); void toXML(QXmlStreamWriter *xmlWriter); diff --git a/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.cpp b/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.cpp index 0dd434c01..6df939d0a 100644 --- a/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.cpp +++ b/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.cpp @@ -58,7 +58,7 @@ void ConfigTaskWidget::addUAVObject(QString objectName, QList *reloadGroups void ConfigTaskWidget::addUAVObject(UAVObject *objectName, QList *reloadGroups) { - addUAVObject(objectName ? objectName->getName() : QString(""), reloadGroups); + addUAVObject(objectName ? objectName->getName() : QString(), reloadGroups); } int ConfigTaskWidget::fieldIndexFromElementName(QString objectName, QString fieldName, QString elementName) @@ -84,7 +84,7 @@ void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, Q void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, QString elementName) { - addWidgetBinding(object ? object->getName() : QString(""), field ? field->getName() : QString(""), widget, elementName); + addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, elementName); } void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, QWidget *widget, QString elementName, double scale, @@ -97,14 +97,14 @@ void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, Q void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, QString elementName, double scale, bool isLimited, QList *reloadGroupIDs, quint32 instID) { - addWidgetBinding(object ? object->getName() : QString(""), field ? field->getName() : QString(""), widget, elementName, scale, + addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, elementName, scale, isLimited, reloadGroupIDs, instID); } void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, int index, double scale, bool isLimited, QList *reloadGroupIDs, quint32 instID) { - addWidgetBinding(object ? object->getName() : QString(""), field ? field->getName() : QString(""), widget, index, scale, + addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, index, scale, isLimited, reloadGroupIDs, instID); } @@ -146,7 +146,6 @@ void ConfigTaskWidget::doAddWidgetBinding(QString objectName, QString fieldName, binding->setIsEnabled(m_widgetBindingsPerWidget.count(widget) == 0); m_widgetBindingsPerWidget.insert(widget, binding); - if (object) { m_widgetBindingsPerObject.insert(object, binding); if (m_saveButton) { @@ -183,9 +182,9 @@ void ConfigTaskWidget::setWidgetBindingObjectEnabled(QString objectName, bool en binding->setIsEnabled(enabled); if (enabled) { if (binding->value().isValid() && !binding->value().isNull()) { - setWidgetFromVariant(binding->widget(), binding->value(), binding->scale()); + setWidgetFromVariant(binding->widget(), binding->value(), binding); } else { - setWidgetFromField(binding->widget(), binding->field(), binding->index(), binding->scale(), binding->isLimited()); + setWidgetFromField(binding->widget(), binding->field(), binding); } } } @@ -236,7 +235,8 @@ void ConfigTaskWidget::onAutopilotDisconnect() invalidateObjects(); } -void ConfigTaskWidget::forceConnectedState() // dynamic widgets don't recieve the connected signal. This should be called instead. +// dynamic widgets don't recieve the connected signal. This should be called instead. +void ConfigTaskWidget::forceConnectedState() { m_isConnected = true; setDirty(false); @@ -261,7 +261,7 @@ void ConfigTaskWidget::populateWidgets() foreach(WidgetBinding * binding, m_widgetBindingsPerObject) { if (binding->isEnabled() && binding->object() != NULL && binding->field() != NULL && binding->widget() != NULL) { - setWidgetFromField(binding->widget(), binding->field(), binding->index(), binding->scale(), binding->isLimited()); + setWidgetFromField(binding->widget(), binding->field(), binding); } } setDirty(dirtyBack); @@ -277,7 +277,7 @@ void ConfigTaskWidget::refreshWidgetsValues(UAVObject *obj) emit refreshWidgetsValuesRequested(); foreach(WidgetBinding * binding, m_widgetBindingsPerObject.values(obj)) { if (binding->isEnabled() && binding->field() != NULL && binding->widget() != NULL) { - setWidgetFromField(binding->widget(), binding->field(), binding->index(), binding->scale(), binding->isLimited()); + setWidgetFromField(binding->widget(), binding->field(), binding); } } setDirty(dirtyBack); @@ -357,13 +357,15 @@ void ConfigTaskWidget::forceShadowUpdates() if (!binding->isEnabled()) { continue; } - QVariant widgetValue = getVariantFromWidget(binding->widget(), binding->scale(), binding->units()); + QVariant widgetValue = getVariantFromWidget(binding->widget(), binding); foreach(ShadowWidgetBinding * shadow, binding->shadows()) { disconnectWidgetUpdatesToSlot(shadow->widget(), SLOT(widgetsContentsChanged())); checkWidgetsLimits(shadow->widget(), binding->field(), binding->index(), shadow->isLimited(), widgetValue, shadow->scale()); - setWidgetFromVariant(shadow->widget(), widgetValue, shadow->scale()); + + WidgetBinding tmpBinding(shadow->widget(), binding->object(), binding->field(), binding->index(), shadow->scale(), shadow->isLimited()); + setWidgetFromVariant(shadow->widget(), widgetValue, &tmpBinding); emit widgetContentsChanged(shadow->widget()); connectWidgetUpdatesToSlot(shadow->widget(), SLOT(widgetsContentsChanged())); @@ -376,25 +378,23 @@ void ConfigTaskWidget::widgetsContentsChanged() { QWidget *emitter = ((QWidget *)sender()); emit widgetContentsChanged(emitter); - double scale = 1.0; QVariant value; foreach(WidgetBinding * binding, m_widgetBindingsPerWidget.values(emitter)) { if (binding && binding->isEnabled()) { if (binding->widget() == emitter) { - scale = binding->scale(); - checkWidgetsLimits(emitter, binding->field(), binding->index(), binding->isLimited(), - getVariantFromWidget(emitter, scale, binding->units()), scale); + value = getVariantFromWidget(emitter, binding); + checkWidgetsLimits(emitter, binding->field(), binding->index(), binding->isLimited(), value, binding->scale()); } else { foreach(ShadowWidgetBinding * shadow, binding->shadows()) { if (shadow->widget() == emitter) { - scale = shadow->scale(); - checkWidgetsLimits(emitter, binding->field(), binding->index(), shadow->isLimited(), - getVariantFromWidget(emitter, scale, binding->units()), scale); + WidgetBinding tmpBinding(shadow->widget(), binding->object(), binding->field(), + binding->index(), shadow->scale(), shadow->isLimited()); + value = getVariantFromWidget(emitter, &tmpBinding); + checkWidgetsLimits(emitter, binding->field(), binding->index(), shadow->isLimited(), value, shadow->scale()); } } } - value = getVariantFromWidget(emitter, scale, binding->units()); binding->setValue(value); if (binding->widget() != emitter) { @@ -402,7 +402,7 @@ void ConfigTaskWidget::widgetsContentsChanged() checkWidgetsLimits(binding->widget(), binding->field(), binding->index(), binding->isLimited(), value, binding->scale()); - setWidgetFromVariant(binding->widget(), value, binding->scale()); + setWidgetFromVariant(binding->widget(), value, binding); emit widgetContentsChanged(binding->widget()); connectWidgetUpdatesToSlot(binding->widget(), SLOT(widgetsContentsChanged())); @@ -413,7 +413,8 @@ void ConfigTaskWidget::widgetsContentsChanged() checkWidgetsLimits(shadow->widget(), binding->field(), binding->index(), shadow->isLimited(), value, shadow->scale()); - setWidgetFromVariant(shadow->widget(), value, shadow->scale()); + WidgetBinding tmp(shadow->widget(), binding->object(), binding->field(), binding->index(), shadow->scale(), shadow->isLimited()); + setWidgetFromVariant(shadow->widget(), value, &tmp); emit widgetContentsChanged(shadow->widget()); connectWidgetUpdatesToSlot(shadow->widget(), SLOT(widgetsContentsChanged())); @@ -708,7 +709,7 @@ void ConfigTaskWidget::defaultButtonClicked() continue; } UAVDataObject *temp = ((UAVDataObject *)binding->object())->dirtyClone(); - setWidgetFromField(binding->widget(), temp->getField(binding->field()->getName()), binding->index(), binding->scale(), binding->isLimited()); + setWidgetFromField(binding->widget(), temp->getField(binding->field()->getName()), binding); } } @@ -751,7 +752,7 @@ void ConfigTaskWidget::reloadButtonClicked() if (m_realtimeUpdateTimer->isActive()) { binding->object()->requestUpdate(); if (binding->widget()) { - setWidgetFromField(binding->widget(), binding->field(), binding->index(), binding->scale(), binding->isLimited()); + setWidgetFromField(binding->widget(), binding->field(), binding); } } m_realtimeUpdateTimer->stop(); @@ -823,9 +824,14 @@ void ConfigTaskWidget::disconnectWidgetUpdatesToSlot(QWidget *widget, const char } } -QVariant ConfigTaskWidget::getVariantFromWidget(QWidget *widget, double scale, QString units) +QVariant ConfigTaskWidget::getVariantFromWidget(QWidget *widget, WidgetBinding *binding) { + double scale = binding->scale(); + if (QComboBox * cb = qobject_cast(widget)) { + if (binding->isInteger()) { + return cb->currentIndex(); + } return (QString)cb->currentText(); } else if (QDoubleSpinBox * cb = qobject_cast(widget)) { return (double)(cb->value() * scale); @@ -837,7 +843,7 @@ QVariant ConfigTaskWidget::getVariantFromWidget(QWidget *widget, double scale, Q return (QString)(cb->isChecked() ? "TRUE" : "FALSE"); } else if (QLineEdit * cb = qobject_cast(widget)) { QString value = (QString)cb->displayText(); - if (units == "hex") { + if (binding->units() == "hex") { bool ok; return value.toUInt(&ok, 16); } else { @@ -848,11 +854,18 @@ QVariant ConfigTaskWidget::getVariantFromWidget(QWidget *widget, double scale, Q } } -bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, double scale, QString units) +bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, WidgetBinding *binding) { + double scale = binding->scale(); + if (QComboBox * cb = qobject_cast(widget)) { - cb->setCurrentIndex(cb->findText(value.toString())); - return true; + bool ok = true; + if (binding->isInteger()) { + cb->setCurrentIndex(value.toInt(&ok)); + } else { + cb->setCurrentIndex(cb->findText(value.toString())); + } + return ok; } else if (QLabel * cb = qobject_cast(widget)) { if (scale == 0) { cb->setText(value.toString()); @@ -875,7 +888,7 @@ bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, dou return true; } else if (QLineEdit * cb = qobject_cast(widget)) { if ((scale == 0) || (scale == 1)) { - if (units == "hex") { + if (binding->units() == "hex") { cb->setText(QString::number(value.toUInt(), 16).toUpper()); } else { cb->setText(value.toString()); @@ -889,24 +902,19 @@ bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, dou } } -bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, double scale) -{ - return setWidgetFromVariant(widget, value, scale, QString("")); -} - -bool ConfigTaskWidget::setWidgetFromField(QWidget *widget, UAVObjectField *field, int index, double scale, bool hasLimits) +bool ConfigTaskWidget::setWidgetFromField(QWidget *widget, UAVObjectField *field, WidgetBinding *binding) { if (!widget || !field) { return false; } if (QComboBox * cb = qobject_cast(widget)) { if (cb->count() == 0) { - loadWidgetLimits(cb, field, index, hasLimits, scale); + loadWidgetLimits(cb, field, binding->index(), binding->isLimited(), binding->scale()); } } - QVariant value = field->getValue(index); - checkWidgetsLimits(widget, field, index, hasLimits, value, scale); - bool result = setWidgetFromVariant(widget, value, scale, field->getUnits()); + QVariant value = field->getValue(binding->index()); + checkWidgetsLimits(widget, field, binding->index(), binding->isLimited(), value, binding->scale()); + bool result = setWidgetFromVariant(widget, value, binding); if (result) { return true; } else { @@ -1070,7 +1078,23 @@ QString WidgetBinding::units() const if (m_field) { return m_field->getUnits(); } - return QString(""); + return QString(); +} + +QString WidgetBinding::type() const +{ + if (m_field) { + return m_field->getTypeAsString(); + } + return QString(); +} + +bool WidgetBinding::isInteger() const +{ + if (m_field) { + return m_field->isInteger(); + } + return false; } UAVObject *WidgetBinding::object() const diff --git a/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.h b/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.h index dfc9a9c76..6ff337060 100644 --- a/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.h +++ b/ground/openpilotgcs/src/plugins/uavobjectwidgetutils/configtaskwidget.h @@ -69,6 +69,8 @@ public: ~WidgetBinding(); QString units() const; + QString type() const; + bool isInteger() const; UAVObject *object() const; UAVObjectField *field() const; int index() const; @@ -219,11 +221,10 @@ private: QString m_outOfLimitsStyle; QTimer *m_realtimeUpdateTimer; - bool setWidgetFromField(QWidget *widget, UAVObjectField *field, int index, double scale, bool hasLimits); + bool setWidgetFromField(QWidget *widget, UAVObjectField *field, WidgetBinding *binding); - QVariant getVariantFromWidget(QWidget *widget, double scale, const QString units); - bool setWidgetFromVariant(QWidget *widget, QVariant value, double scale, QString units); - bool setWidgetFromVariant(QWidget *widget, QVariant value, double scale); + QVariant getVariantFromWidget(QWidget *widget, WidgetBinding *binding); + bool setWidgetFromVariant(QWidget *widget, QVariant value, WidgetBinding *binding); void connectWidgetUpdatesToSlot(QWidget *widget, const char *function); void disconnectWidgetUpdatesToSlot(QWidget *widget, const char *function); diff --git a/ground/openpilotgcs/src/plugins/uploader/uploader.pro b/ground/openpilotgcs/src/plugins/uploader/uploader.pro index 20e358c8b..9dbdcc56b 100644 --- a/ground/openpilotgcs/src/plugins/uploader/uploader.pro +++ b/ground/openpilotgcs/src/plugins/uploader/uploader.pro @@ -46,15 +46,14 @@ SOURCES += uploadergadget.cpp \ SSP/qsspt.cpp \ runningdevicewidget.cpp -OTHER_FILES += Uploader.pluginspec \ +OTHER_FILES += Uploader.pluginspec FORMS += \ uploader.ui \ devicewidget.ui \ runningdevicewidget.ui -RESOURCES += \ - uploader.qrc +RESOURCES += uploader.qrc exists( ../../../../../build/openpilotgcs-synthetics/opfw_resource.qrc ) { RESOURCES += ../../../../../build/openpilotgcs-synthetics/opfw_resource.qrc diff --git a/make/apps-defs.mk b/make/apps-defs.mk index 27595fa7f..c65ade104 100644 --- a/make/apps-defs.mk +++ b/make/apps-defs.mk @@ -99,7 +99,7 @@ SRC += $(PIOSCOMMON)/pios_usb_util.c ## PIOS system code SRC += $(PIOSCOMMON)/pios_task_monitor.c SRC += $(PIOSCOMMON)/pios_callbackscheduler.c - +SRC += $(PIOSCOMMON)/pios_notify.c ## Misc library functions SRC += $(FLIGHTLIB)/fifo_buffer.c SRC += $(FLIGHTLIB)/sanitycheck.c diff --git a/shared/uavobjectdefinition/airspeedsettings.xml b/shared/uavobjectdefinition/airspeedsettings.xml index fbfce3e91..0c438afa5 100644 --- a/shared/uavobjectdefinition/airspeedsettings.xml +++ b/shared/uavobjectdefinition/airspeedsettings.xml @@ -4,7 +4,7 @@ - + diff --git a/shared/uavobjectdefinition/hwsettings.xml b/shared/uavobjectdefinition/hwsettings.xml index a1bc068e8..f005a31ab 100644 --- a/shared/uavobjectdefinition/hwsettings.xml +++ b/shared/uavobjectdefinition/hwsettings.xml @@ -25,6 +25,7 @@ +