Airspeed consistency check with onboard wind estimation (#3478)
Issues due date / Add labels to issues (push) Has been cancelled
Doxygen / build (push) Has been cancelled

* initial commit airspeed consistency check module
* small edit
* address comments
* use INFO_MSG for airspeed consistency module
This commit is contained in:
NoahWe
2025-07-01 10:07:35 +02:00
committed by GitHub
parent b7aad07a04
commit 07558af7a2
14 changed files with 442 additions and 18 deletions
+1
View File
@@ -143,6 +143,7 @@
<module name="rotwing_state"/>
<module name="preflight_checks"/>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+1
View File
@@ -136,6 +136,7 @@
<define name="SDLOG_PREFLIGHT_ERROR" value="TRUE"/>
</module>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+1
View File
@@ -136,6 +136,7 @@
<define name="SDLOG_PREFLIGHT_ERROR" value="TRUE"/>
</module>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+1
View File
@@ -134,6 +134,7 @@
<define name="SDLOG_PREFLIGHT_ERROR" value="TRUE"/>
</module>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+1
View File
@@ -136,6 +136,7 @@
<define name="SDLOG_PREFLIGHT_ERROR" value="TRUE"/>
</module>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+1
View File
@@ -136,6 +136,7 @@
<define name="SDLOG_PREFLIGHT_ERROR" value="TRUE"/>
</module>
<module name="pfc_actuators"/>
<module name="airspeed_consistency"/>
<module name="agl_dist"/>
<module name="approach_moving_target"/>
</firmware>
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="airspeed_consistency" dir="checks">
<doc>
<description>
Check consistency of airspeed measurements while flying circles.
</description>
</doc>
<settings>
<dl_settings>
<dl_settings NAME="Airspeed Consistency">
<dl_setting var="asc_reset" min="0" max="1" step="1" values="FALSE|TRUE" type="bool" module="modules/checks/airspeed_consistency" handler="reset"/>
</dl_settings>
</dl_settings>
</settings>
<dep>
<depends></depends>
</dep>
<header>
<file name="airspeed_consistency.h"/>
</header>
<init fun="airspeed_consistency_init()"/>
<periodic fun="airspeed_consistency_periodic()" freq="1"/>
<makefile>
<file name="airspeed_consistency.c"/>
<file name="pprz_circfit_float.c" dir="math"/>
</makefile>
</module>
+1 -1
View File
@@ -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"
/>
<aircraft
+82
View File
@@ -0,0 +1,82 @@
/*
* 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
* <http://www.gnu.org/licenses/>.
*/
#include "math/pprz_circfit_float.h"
#include <math.h>
#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
}
+51
View File
@@ -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
* <http://www.gnu.org/licenses/>.
*/
#ifndef PPRZ_CIRCFIT_FLOAT_H
#define PPRZ_CIRCFIT_FLOAT_H
#include <std.h>
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
@@ -0,0 +1,140 @@
/*
* Copyright (C) 2025 Noah Wechtler <noahwechtler@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
/**
* @file "modules/checks/airspeed_consistency.c"
* @author Noah Wechtler <noahwechtler@tudelft.nl>
* 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();
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2025 Noah Wechtler <noahwechtler@tudelft.nl>
*
* 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
* <http://www.gnu.org/licenses/>.
*/
/**
* @file "modules/checks/airspeed_consistency.h"
* @author Noah Wechtler <noahwechtler@tudelft.nl>
* 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 <std.h>
#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
@@ -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()
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)
@@ -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()