diff --git a/conf/airframes/tudelft/rotwing_v3b.xml b/conf/airframes/tudelft/rotwing_v3b.xml
index 02f0b1557a..5ad15a7649 100644
--- a/conf/airframes/tudelft/rotwing_v3b.xml
+++ b/conf/airframes/tudelft/rotwing_v3b.xml
@@ -143,6 +143,7 @@
+
diff --git a/conf/airframes/tudelft/rotwing_v3d.xml b/conf/airframes/tudelft/rotwing_v3d.xml
index 196c668502..adeb1f174d 100644
--- a/conf/airframes/tudelft/rotwing_v3d.xml
+++ b/conf/airframes/tudelft/rotwing_v3d.xml
@@ -136,6 +136,7 @@
+
diff --git a/conf/airframes/tudelft/rotwing_v3e.xml b/conf/airframes/tudelft/rotwing_v3e.xml
index 32baea627d..9cc143e698 100644
--- a/conf/airframes/tudelft/rotwing_v3e.xml
+++ b/conf/airframes/tudelft/rotwing_v3e.xml
@@ -136,6 +136,7 @@
+
diff --git a/conf/airframes/tudelft/rotwing_v3f.xml b/conf/airframes/tudelft/rotwing_v3f.xml
index f6338e89d4..0cedfd1c43 100644
--- a/conf/airframes/tudelft/rotwing_v3f.xml
+++ b/conf/airframes/tudelft/rotwing_v3f.xml
@@ -134,6 +134,7 @@
+
diff --git a/conf/airframes/tudelft/rotwing_v3g.xml b/conf/airframes/tudelft/rotwing_v3g.xml
index 7fc5d6a553..cd66a133c7 100644
--- a/conf/airframes/tudelft/rotwing_v3g.xml
+++ b/conf/airframes/tudelft/rotwing_v3g.xml
@@ -136,6 +136,7 @@
+
diff --git a/conf/airframes/tudelft/rotwing_v3h.xml b/conf/airframes/tudelft/rotwing_v3h.xml
index 6ce71f2861..e0408c0671 100644
--- a/conf/airframes/tudelft/rotwing_v3h.xml
+++ b/conf/airframes/tudelft/rotwing_v3h.xml
@@ -136,6 +136,7 @@
+
diff --git a/conf/modules/airspeed_consistency.xml b/conf/modules/airspeed_consistency.xml
new file mode 100644
index 0000000000..cefc39a8e4
--- /dev/null
+++ b/conf/modules/airspeed_consistency.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ Check consistency of airspeed measurements while flying circles.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/userconf/tudelft/conf.xml b/conf/userconf/tudelft/conf.xml
index c97237c8bd..be57bcf6d3 100644
--- a/conf/userconf/tudelft/conf.xml
+++ b/conf/userconf/tudelft/conf.xml
@@ -601,7 +601,7 @@
telemetry="telemetry/highspeed_rotorcraft.xml"
flight_plan="flight_plans/tudelft/rotwing_EHVB.xml"
settings="settings/rotorcraft_basic.xml"
- settings_modules="modules/air_data.xml modules/airspeed_ms45xx_i2c.xml modules/airspeed_uavcan.xml modules/approach_moving_target.xml modules/eff_scheduling_rotwing.xml modules/ekf_aw.xml modules/electrical.xml modules/follow_me.xml modules/gps.xml modules/gps_ublox.xml modules/guidance_indi_hybrid.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/imu_heater.xml modules/ins_ekf2.xml modules/logger_sd_chibios.xml modules/nav_hybrid.xml modules/nav_rotorcraft.xml modules/parachute.xml modules/pfc_actuators.xml modules/preflight_checks.xml modules/rotwing_state.xml modules/stabilization_indi.xml modules/sys_id_auto_doublets.xml modules/sys_id_doublet.xml modules/target_pos.xml"
+ settings_modules="modules/air_data.xml modules/airspeed_consistency.xml modules/airspeed_ms45xx_i2c.xml modules/airspeed_uavcan.xml modules/approach_moving_target.xml modules/eff_scheduling_rotwing.xml modules/ekf_aw.xml modules/electrical.xml modules/follow_me.xml modules/gps.xml modules/gps_ublox.xml modules/guidance_indi_hybrid.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/imu_heater.xml modules/ins_ekf2.xml modules/logger_sd_chibios.xml modules/nav_hybrid.xml modules/nav_rotorcraft.xml modules/parachute.xml modules/pfc_actuators.xml modules/preflight_checks.xml modules/rotwing_state.xml modules/stabilization_indi.xml modules/sys_id_auto_doublets.xml modules/sys_id_doublet.xml modules/target_pos.xml"
gui_color="red"
/>
.
+ */
+
+#include "math/pprz_circfit_float.h"
+#include
+
+#ifndef PPRZ_CIRCFIT_EPSILON
+#define PPRZ_CIRCFIT_EPSILON 1e-3f
+#endif
+
+#ifndef PPRZ_CIRCFIT_ITER_MAX
+#define PPRZ_CIRCFIT_ITER_MAX 250
+#endif
+
+enum CircFitStatus_t pprz_circfit_wei_float(struct circle_t *c, const float *x, const float *y, uint16_t n, struct circle_t *g) {
+
+ // Check if initial guess is provided
+ if (g != NULL) {
+ c->r = g->r;
+ c->x = g->x;
+ c->y = g->y;
+ } else {
+ c->x = 0.0f;
+ c->y = 0.0f;
+ c->r = 1.0f;
+ }
+
+ float norm[n];
+ float x_prev = 0;
+ float y_prev = 0;
+ float r_prev = -1;
+ uint16_t i = 0;
+
+ while (fabsf(c->r - r_prev) > PPRZ_CIRCFIT_EPSILON || fabsf(c->x - x_prev) > PPRZ_CIRCFIT_EPSILON || fabsf(c->y - y_prev) > PPRZ_CIRCFIT_EPSILON) {
+
+ float sum_norm = 0.0f;
+ x_prev = c->x;
+ y_prev = c->y;
+ r_prev = c->r;
+
+ // Prepare ||x - c||
+ for (int i = 0; i < n; i++) {
+ norm[i] = sqrtf((x[i] - x_prev) * (x[i] - x_prev) + (y[i] - y_prev) * (y[i] - y_prev));
+ sum_norm += norm[i];
+ }
+
+ c->r = sum_norm / n;
+
+ for (int i = 0; i < n; i++) {
+ c->x += x[i] + c->r * (x_prev - x[i]) / norm[i];
+ c->y += y[i] + c->r * (y_prev - y[i]) / norm[i];
+ }
+
+ c->x /= n;
+ c->y /= n;
+
+ if (i >= PPRZ_CIRCFIT_ITER_MAX) {
+ return CIRC_FIT_ITERATION_LIMIT; // Reached iteration limit
+ }
+
+ i++;
+
+ }
+
+ return CIRC_FIT_OK; // Circle fit successful
+
+}
\ No newline at end of file
diff --git a/sw/airborne/math/pprz_circfit_float.h b/sw/airborne/math/pprz_circfit_float.h
new file mode 100644
index 0000000000..f0ddfaff6b
--- /dev/null
+++ b/sw/airborne/math/pprz_circfit_float.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of paparazzi
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, see
+ * .
+ */
+
+#ifndef PPRZ_CIRCFIT_FLOAT_H
+#define PPRZ_CIRCFIT_FLOAT_H
+
+#include
+
+enum CircFitStatus_t {
+ CIRC_FIT_OK = 0, // Circle fit successful
+ CIRC_FIT_ERROR = -1, // Circle fit failed
+ CIRC_FIT_ITERATION_LIMIT = -2 // Circle fit reached iteration limit
+};
+
+struct circle_t {
+ float x; // x coordinate of the circle center
+ float y; // y coordinate of the circle center
+ float r; // radius of the circle
+};
+
+/*
+ * Fitting Noisy Data to a Circle: A Simple Iterative Maximum Likelihood Approach
+ * Wei Li, et al.
+ * DOI: 10.1109/icc.2011.5963101
+ *
+ * inputs:
+ * c: struct to store the fitted circle
+ * x: x coordinates used during fitting
+ * y: y coordinates used during fitting
+ * n: number of points
+ * g: initial guess for the circle (NULL for no initial guess)
+ */
+enum CircFitStatus_t pprz_circfit_wei_float(struct circle_t *c, const float *x, const float *y, uint16_t n, struct circle_t *g);
+
+
+#endif // PPRZ_CIRCFIT_FLOAT_H
\ No newline at end of file
diff --git a/sw/airborne/modules/checks/airspeed_consistency.c b/sw/airborne/modules/checks/airspeed_consistency.c
new file mode 100644
index 0000000000..6b70756314
--- /dev/null
+++ b/sw/airborne/modules/checks/airspeed_consistency.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2025 Noah Wechtler
+ *
+ * This file is part of paparazzi
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, see
+ * .
+ */
+
+/**
+ * @file "modules/checks/airspeed_consistency.c"
+ * @author Noah Wechtler
+ * Simple consistency check for airspeed measurements while flying circles
+ */
+
+#include "modules/checks/airspeed_consistency.h"
+#include "navigation.h"
+#include "state.h"
+#include "filters/low_pass_filter.h"
+#include "generated/modules.h"
+#include "modules/core/abi.h"
+#include "modules/datalink/telemetry.h"
+#include "modules/nav/nav_rotorcraft_base.h"
+
+#ifndef AIRSPEED_CONSISTENCY_MIN_SAMPLES
+#define AIRSPEED_CONSISTENCY_MIN_SAMPLES 20 // Minimum number of samples before circle fitting starts
+#endif
+
+#ifndef AIRSPEED_CONSISTENCY_INTERVAL
+#define AIRSPEED_CONSISTENCY_INTERVAL 10
+#endif
+
+#if AIRSPEED_CONSISTENCY_BUFFER_SIZE < AIRSPEED_CONSISTENCY_MIN_SAMPLES
+#error "AIRSPEED_CONSISTENCY_BUFFER_SIZE must be larger than or equal to AIRSPEED_CONSISTENCY_MIN_SAMPLES"
+#endif
+
+#if AIRSPEED_CONSISTENCY_BUFFER_SIZE > UINT16_MAX
+#error "AIRSPEED_CONSISTENCY_BUFFER_SIZE must be less than or equal to UINT16_MAX"
+#endif
+
+bool asc_reset = false; // Flag to reset the airspeed consistency check
+
+static void save_speeds(void);
+static void reset_speeds(void);
+static enum CircFitStatus_t get_circle_from_speeds(struct circle_t *c, float *N, float *E);
+
+static struct asc_t asc = {0};
+static Butterworth2LowPass tas_filt;
+
+void airspeed_consistency_init(void) {
+ reset_speeds();
+ float tau = 1.0 / (2.0 * M_PI);
+ init_butterworth_2_low_pass(&tas_filt, tau, 1.0 / NAVIGATION_FREQUENCY, 0.0);
+}
+
+void airspeed_consistency_periodic(void) {
+ struct FloatVect2 diff;
+ VECT2_DIFF(diff, nav_rotorcraft_base.circle.center, *stateGetPositionEnu_f());
+ float dist_to_point = float_vect2_norm(&diff);
+
+ if (nav.horizontal_mode != NAV_HORIZONTAL_MODE_CIRCLE || dist_to_point > fabsf(nav_rotorcraft_base.circle.radius) + ARRIVED_AT_WAYPOINT
+ || dist_to_point < fabsf(nav_rotorcraft_base.circle.radius) - ARRIVED_AT_WAYPOINT) {
+ // Wipe old data if not flying circles
+ if (asc.i != 0) {
+ reset_speeds();
+ }
+ return;
+ }
+
+ save_speeds();
+
+ RunOnceEvery(AIRSPEED_CONSISTENCY_PERIODIC_FREQ * AIRSPEED_CONSISTENCY_INTERVAL, {
+ if (asc.filled || asc.i >= AIRSPEED_CONSISTENCY_MIN_SAMPLES) {
+ asc.as_circ_status = get_circle_from_speeds(&asc.as, asc.as_N, asc.as_E);
+ asc.gs_circ_status = get_circle_from_speeds(&asc.gs, asc.gs_N, asc.gs_E);
+ }
+
+ if (asc.as_circ_status == CIRC_FIT_OK && asc.gs_circ_status == CIRC_FIT_OK && asc.as.r > 1e-3) {
+ asc.ratio = asc.gs.r / asc.as.r; // Calculate the ratio of the radii of the circles
+ asc.ratio *= asc.ratio; // Square the ratio so it can be multiplied with the current airspeed sensor scale
+ char error_msg[100];
+ int rc = snprintf(error_msg, sizeof(error_msg), "ASC: ratio squared = %.4f", asc.ratio);
+ DOWNLINK_SEND_INFO_MSG(DefaultChannel, DefaultDevice, rc, error_msg);
+#if FLIGHTRECORDER_SDLOG
+ pprz_msg_send_INFO_MSG(&pprzlog_tp.trans_tx, &flightrecorder_sdlog.device, AC_ID, rc, error_msg);
+#endif
+ }
+ });
+}
+
+static void save_speeds(void) {
+ asc.gs_N[asc.i] = stateGetSpeedNed_f()->x;
+ asc.gs_E[asc.i] = stateGetSpeedNed_f()->y;
+
+ update_butterworth_2_low_pass(&tas_filt, air_data.tas);
+ float psi = stateGetNedToBodyEulers_f()->psi;
+ asc.as_N[asc.i] = tas_filt.o[0] * cosf(psi);
+ asc.as_E[asc.i] = tas_filt.o[0] * sinf(psi);
+
+ asc.i++;
+ if (asc.i >= AIRSPEED_CONSISTENCY_BUFFER_SIZE) {
+ asc.filled = true; // Set to true if the buffer has been filled once
+ asc.i = 0;
+ }
+}
+
+static void reset_speeds(void) {
+ asc.filled = false;
+ asc.i = 0;
+ asc.as_circ_status = CIRC_FIT_ERROR;
+ asc.gs_circ_status = CIRC_FIT_ERROR;
+
+ for (int j = 0; j < AIRSPEED_CONSISTENCY_BUFFER_SIZE; j++) {
+ asc.gs_N[j] = NAN;
+ asc.gs_E[j] = NAN;
+ asc.as_N[j] = NAN;
+ asc.as_E[j] = NAN;
+ }
+}
+
+static enum CircFitStatus_t get_circle_from_speeds(struct circle_t *c, float *X, float *Y) {
+ uint16_t N = asc.filled? AIRSPEED_CONSISTENCY_BUFFER_SIZE : asc.i;
+ enum CircFitStatus_t status = pprz_circfit_wei_float(c, X, Y, N, NULL); // Feeding the last estimated circle as initial guess leads to NaNs here
+ return status;
+}
+
+void airspeed_consistency_reset(bool __attribute__((unused)) reset) {
+ reset_speeds();
+}
\ No newline at end of file
diff --git a/sw/airborne/modules/checks/airspeed_consistency.h b/sw/airborne/modules/checks/airspeed_consistency.h
new file mode 100644
index 0000000000..96a21ae8b7
--- /dev/null
+++ b/sw/airborne/modules/checks/airspeed_consistency.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2025 Noah Wechtler
+ *
+ * This file is part of paparazzi
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, see
+ * .
+ */
+
+/**
+ * @file "modules/checks/airspeed_consistency.h"
+ * @author Noah Wechtler
+ * Check the consistency of airspeed measurements while flying circles
+ */
+
+#ifndef AIRSPEED_CONSISTENCY_H
+#define AIRSPEED_CONSISTENCY_H
+
+#ifndef AIRSPEED_CONSISTENCY_BUFFER_SIZE
+#define AIRSPEED_CONSISTENCY_BUFFER_SIZE 255
+#endif
+
+#include
+#include "math/pprz_circfit_float.h"
+#include "filters/low_pass_filter.h"
+
+extern bool asc_reset;
+
+struct asc_t {
+ bool filled;
+ uint16_t i;
+ float gs_N[AIRSPEED_CONSISTENCY_BUFFER_SIZE];
+ float gs_E[AIRSPEED_CONSISTENCY_BUFFER_SIZE];
+ float as_N[AIRSPEED_CONSISTENCY_BUFFER_SIZE];
+ float as_E[AIRSPEED_CONSISTENCY_BUFFER_SIZE];
+ struct circle_t as;
+ struct circle_t gs;
+ enum CircFitStatus_t as_circ_status;
+ enum CircFitStatus_t gs_circ_status;
+ float ratio;
+};
+
+extern void airspeed_consistency_init(void);
+extern void airspeed_consistency_periodic(void);
+extern void airspeed_consistency_reset(bool reset); // Reset the airspeed consistency check
+
+#endif // AIRSPEED_CONSISTENCY_H
\ No newline at end of file
diff --git a/sw/ground_segment/python/rotwing_mon/rotwing_mon.py b/sw/ground_segment/python/rotwing_mon/rotwing_mon.py
index 64fcc1c84d..324dbd2b9a 100755
--- a/sw/ground_segment/python/rotwing_mon/rotwing_mon.py
+++ b/sw/ground_segment/python/rotwing_mon/rotwing_mon.py
@@ -22,15 +22,26 @@ import wx
import rotwing_viewer
class RotWingApp(wx.App):
+
+ def __init__(self, args):
+ self.args = args
+ wx.App.__init__(self)
+
def OnInit(self):
- self.main = rotwing_viewer.RotWingFrame()
+ self.main = rotwing_viewer.RotWingFrame(self.args)
self.main.Show()
self.SetTopWindow(self.main)
return True
-def main():
- application = RotWingApp(0)
+def main(args):
+ application = RotWingApp(args)
application.MainLoop()
if __name__ == '__main__':
- main()
\ No newline at end of file
+ import argparse
+
+ parser = argparse.ArgumentParser(description="Rotwing Status Monitor")
+ parser.add_argument('-s', '--speech', dest='speech', default=False, type=bool, help="Rotwing status viewer speech")
+ args = parser.parse_args()
+
+ main(args)
\ No newline at end of file
diff --git a/sw/ground_segment/python/rotwing_mon/rotwing_viewer.py b/sw/ground_segment/python/rotwing_mon/rotwing_viewer.py
index 4a03d5ba13..7dc98527cc 100644
--- a/sw/ground_segment/python/rotwing_mon/rotwing_viewer.py
+++ b/sw/ground_segment/python/rotwing_mon/rotwing_viewer.py
@@ -24,7 +24,9 @@ import os
import math
import datetime
import numpy as np
-
+import pyttsx3
+import re
+engine = pyttsx3.init()
PPRZ_HOME = os.getenv("PAPARAZZI_HOME", os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'../../../..')))
@@ -39,6 +41,18 @@ from pprzlink.ivy import IvyMessagesInterface
WIDTH = 600
BARH = 140
+class Speech(object):
+
+ def __init__(self):
+ self.last_speech_time = datetime.datetime.now()
+
+ def speak_every_interval(self, text, interval):
+ if (datetime.datetime.now() - self.last_speech_time).total_seconds() > interval:
+ self.last_speech_time = datetime.datetime.now()
+ engine.say(text)
+ engine.runAndWait()
+
+
class EscMessage(object):
def __init__(self, msg):
self.id = int(msg['motor_id'])
@@ -173,6 +187,14 @@ class AIRDATAMessage(object):
self.airspeed = float(msg['airspeed'])
self.tas = float(msg['tas'])
+class INFOMSGMessage(object):
+ def __init__(self, msg):
+ str_of_char_arr = ''.join(msg['msg'])
+ str_of_char_arr = str_of_char_arr.replace('\"', '')
+ str_of_char_arr = str_of_char_arr.split(" ")
+ if str_of_char_arr[0] == 'ASC:':
+ self.ratio = float(str_of_char_arr[-1])
+
class IMUHEATERMessage(object):
def __init__(self, msg):
self.meas_temp = msg['meas_temp']
@@ -362,6 +384,10 @@ class RotWingFrame(wx.Frame):
self.air_data = AIRDATAMessage(msg)
wx.CallAfter(self.update)
+ if msg.name == "INFO_MSG":
+ self.infomsg = INFOMSGMessage(msg)
+ wx.CallAfter(self.update)
+
if msg.name == "IMU_HEATER":
self.imu_heater = IMUHEATERMessage(msg)
wx.CallAfter(self.update)
@@ -503,35 +529,52 @@ class RotWingFrame(wx.Frame):
dc.DrawText("Nav airspeed: " + str(round(self.rw_status.nav_airspeed,1 )) + " [min: " + str(round(self.rw_status.min_airspeed,1 )) + ", max:" + str(round(self.rw_status.max_airspeed,1 )) + "]", 10, int(4.5*line))
if hasattr(self, 'air_data'):
dc.DrawText("Meas airspeed: " + str(round(self.air_data.airspeed,1 )) + " (TAS: " + str(round(self.air_data.tas,1 )) + ")", 10, int(5.5*line))
+
+ if hasattr(self, 'infomsg') and hasattr(self.infomsg, 'ratio'):
+ # MS45XX inflight calibration monitoring
+ if abs(1-self.infomsg.ratio) < 0.08:
+ dc.SetTextForeground(wx.Colour(0, 0, 0))
+ dc.DrawText("Airspeed ratio: " + str(round(self.infomsg.ratio, 2)), 10, int(6.5*line))
+ elif abs(1-self.infomsg.ratio) < 0.15:
+ dc.SetTextForeground(wx.Colour(139, 64, 0))
+ dc.DrawText("Airspeed ratio: " + str(round(self.infomsg.ratio, 2)), 10, int(6.5*line))
+ if self.speech:
+ self.airspeed_speaker.speak_every_interval("Airspeed is off by more than 8%, Please Calibrate", 30)
+ else:
+ dc.SetTextForeground(wx.Colour(255, 0, 0))
+ dc.DrawText("Airspeed ratio: " + str(round(self.infomsg.ratio, 2)), 10, int(6.5*line))
+ if self.speech:
+ self.airspeed_speaker.speak_every_interval("Airspeed is off by more than 15%, Please Calibrate", 5)
+
#self.StatusBox(dc, 5, 5, 0, 0, self.rw_status.get_state(), 1, 1)
dc.SetTextForeground(wx.Colour(0, 0, 0))
- dc.DrawText("Force Skew: ", 10, int(6.5*line))
+ dc.DrawText("Force Skew: ", 10, int(7.5*line))
lbw = dc.GetTextExtent("Force Skew: ").width
if self.rw_status.skew_forced:
dc.SetTextForeground(wx.Colour(255, 0, 0))
- dc.DrawText("(Enabled)", 10 + lbw, int(6.5*line))
+ dc.DrawText("(Enabled)", 10 + lbw, int(7.5*line))
else:
dc.SetTextForeground(wx.Colour(0, 0, 0))
- dc.DrawText("(Disabled)", 10 + lbw, int(6.5*line))
+ dc.DrawText("(Disabled)", 10 + lbw, int(7.5*line))
if hasattr(self, 'imu_heater'):
imu_temp = float(self.imu_heater.meas_temp)
if imu_temp < 65.0:
dc.SetTextForeground(wx.Colour(0, 0, 0))
- dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(7.5*line))
+ dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(8.5*line))
elif imu_temp < 85.0:
dc.SetTextForeground(wx.Colour(139, 64, 0))
- dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(7.5*line))
+ dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(8.5*line))
else:
dc.SetTextForeground(wx.Colour(255, 0, 0))
- dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(7.5*line))
+ dc.DrawText("Meas IMU temp: " + str(round(imu_temp, 0)), 10, int(8.5*line))
if self.powers.get_backbat() != None:
- dc.DrawText("Back Bat: " + str(round(self.powers.get_backbat().current*self.powers.get_backbat().voltage, 1)) + "W (" + str(round(self.powers.get_backbat().voltage, 1)) + "V, " + str(round(self.powers.get_backbat().current, 1)) + "A)", 10, int(8.5*line))
+ dc.DrawText("Back Bat: " + str(round(self.powers.get_backbat().current*self.powers.get_backbat().voltage, 1)) + "W (" + str(round(self.powers.get_backbat().voltage, 1)) + "V, " + str(round(self.powers.get_backbat().current, 1)) + "A)", 10, int(9.5*line))
if self.powers.get_frontbat() != None:
- dc.DrawText("Front Bat: " + str(round(self.powers.get_frontbat().current*self.powers.get_frontbat().voltage, 1)) + "W (" + str(round(self.powers.get_frontbat().voltage, 1)) + "V, " + str(round(self.powers.get_frontbat().current, 1)) + "A)", 10, int(9.5*line))
+ dc.DrawText("Front Bat: " + str(round(self.powers.get_frontbat().current*self.powers.get_frontbat().voltage, 1)) + "W (" + str(round(self.powers.get_frontbat().voltage, 1)) + "V, " + str(round(self.powers.get_frontbat().current, 1)) + "A)", 10, int(10.5*line))
if self.powers.get_backmot() != None:
- dc.DrawText("Back Mot: " + str(round(self.powers.get_backmot().current*self.powers.get_backmot().voltage, 1)) + "W (" + str(round(self.powers.get_backmot().voltage, 1)) + "V, " + str(round(self.powers.get_backmot().current, 1)) + "A)", 10, int(10.5*line))
+ dc.DrawText("Back Mot: " + str(round(self.powers.get_backmot().current*self.powers.get_backmot().voltage, 1)) + "W (" + str(round(self.powers.get_backmot().voltage, 1)) + "V, " + str(round(self.powers.get_backmot().current, 1)) + "A)", 10, int(11.5*line))
if hasattr(self, 'fuelcell'):
dc.SetBrush(wx.Brush(wx.Colour(200,200,200)))
@@ -610,7 +653,7 @@ class RotWingFrame(wx.Frame):
except:
pass
- def __init__(self):
+ def __init__(self, args):
self.w = WIDTH
self.h = WIDTH + BARH
@@ -642,7 +685,12 @@ class RotWingFrame(wx.Frame):
self.interface = IvyMessagesInterface("rotwingframe")
self.interface.subscribe(self.message_recv)
+ if args.speech == True:
+ self.speech = True
+ self.airspeed_speaker = Speech()
+ else:
+ self.speech = False
+
def OnClose(self, event):
self.interface.shutdown()
- self.Destroy()
-
+ self.Destroy()
\ No newline at end of file