def testManualPrefix(self): # The type name of the test enum is completely artifical. Here, we claim it # has a type name that disagrees with the prefix in order to test the prefix # argument. helper = c_helpers.EnumHelper('FakeTypeName', FakeModule(FULL_ENUM), prefix='kTestEnum') for name, value in ENUM_VALUES.iteritems(): self.assertEqual(value, helper.Value(name))
def testNameCollision(self): bad_enum = {'kCollisionAValue1': 1, 'kCollisionBValue1': 1} module = FakeModule(bad_enum) with self.assertRaises(AssertionError) as e: c_helpers.EnumHelper('Collision', module) self.assertIn('kCollisionAValue1', e.exception.message) self.assertIn('kCollisionBValue1', e.exception.message)
class JoystickAioUpdateIndicator(indicator.BaseIndicator): _MASK = c_helpers.EnumHelper('JoystickWarning', pack_avionics_messages).Value('NotPresent') def __init__(self): super(JoystickAioUpdateIndicator, self).__init__('Joystick') def Filter(self, messages): if not messages: return '--', stoplights.STOPLIGHT_UNAVAILABLE status = messages['JoystickStatus.JoystickA.status'] if status is None: return 'No Update', stoplights.STOPLIGHT_ERROR elif avionics_util.CheckWarning(status, self._MASK): return 'Not Present', stoplights.STOPLIGHT_ERROR else: return 'Up', stoplights.STOPLIGHT_NORMAL
This module looks for an 'ltc6804_config' tuple in the configuration file (specified on command line). The ltc6804_config tuple should contain the hardware revision EnumHelper and a dictionary that maps the board's hardware revision to a list of dictionaries. Each dictionary in this list specifies the configuration for each LTC6804 chip. """ import sys import textwrap from makani.avionics.firmware.drivers import ltc6804_types from makani.avionics.firmware.monitors import generate_monitor_base from makani.lib.python import c_helpers rate_helper = c_helpers.EnumHelper('Ltc6804Rate', ltc6804_types) cell_ch_helper = c_helpers.EnumHelper('Ltc6804CellCh', ltc6804_types) aux_ch_helper = c_helpers.EnumHelper('Ltc6804AuxCh', ltc6804_types) stat_ch_helper = c_helpers.EnumHelper('Ltc6804StatCh', ltc6804_types) dcto_helper = c_helpers.EnumHelper('Ltc6804Dcto', ltc6804_types) self_test_helper = c_helpers.EnumHelper('Ltc6804SelfTest', ltc6804_types) class Ltc6804DeviceConfig(generate_monitor_base.DeviceConfigBase): """Generate LTC6804 voltage/current monitor configuration.""" # TODO: Add unit tests. def __init__(self, config): expected_parameters = { 'name', 'address', 'input_mask', 'balance_min_cutoff',
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Classes to perform checks on flight computers.""" from makani.analysis.checks import base_check from makani.analysis.checks.collection import avionics_checks from makani.avionics.firmware.monitors import fc_types from makani.avionics.network import aio_node from makani.lib.python import c_helpers _FC_LABELS_HELPER = c_helpers.EnumHelper('FcLabel', aio_node, prefix='kAioNodeFc') _FC_ANALOG_VOLTAGE_HELPER = c_helpers.EnumHelper('FcAnalogVoltage', fc_types) _FC_INA219_MONITOR_HELPER = c_helpers.EnumHelper('FcIna219Monitor', fc_types) class FcMonAnalogChecker(avionics_checks.BaseVoltageChecker): """The monitor to check flight computer voltage.""" @base_check.RegisterSpecs def __init__(self, for_log, fc_short_name, **base_kwargs): """Initialize the voltage checker for a given flight computer. Args: for_log: True if this check is performed over a log. False if it is for realtime AIO messages. fc_short_name: Short name of a Flight Computer.
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Dump routes from a TMS570 AIO node.""" import socket import sys import textwrap from makani.avionics.common import aio from makani.avionics.common import pack_avionics_messages from makani.avionics.network import aio_node from makani.avionics.network import message_type from makani.lib.python import c_helpers aio_node_helper = c_helpers.EnumHelper('AioNode', aio_node) message_type_helper = c_helpers.EnumHelper('MessageType', message_type) def _MacToString(mac): return '%02X:%02X:%02X:%02X:%02X:%02X' % (mac.a, mac.b, mac.c, mac.d, mac.e, mac.f) def _FormatResponse(source, msg): source_name = aio_node_helper.ShortName(source) mac = _MacToString(msg.entry.ethernet_address) mcast = (msg.entry.ethernet_address.a & 1) == 1 mcast_str = 'Multicast' if mcast else 'Unicast' port_str = ('mask 0x%02X' if mcast else 'port %d') % msg.entry.port_map print(
def __init__(self, for_log, message_type, node, **base_kwargs): super(AioMonAnalogChecker, self).__init__( for_log, message_type, node, 'aio_mon.analog_data', 'aio_mon.analog_populated', c_helpers.EnumHelper('AioAnalogVoltage', aio_types), **base_kwargs)
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Scoring functions relating to the hover controller.""" from makani.analysis.control import geometry from makani.avionics.common import plc_messages from makani.lib.python import c_helpers from makani.lib.python.batch_sim import scoring_functions from makani.lib.python.h5_utils import numpy_utils import numpy as np import pandas as pd import scoring_functions_util as scoring_util _GROUND_STATION_MODE_HELPER = c_helpers.EnumHelper( 'GroundStationMode', plc_messages) class TetherElevationScoringFunction( scoring_functions.DoubleSidedLimitScoringFunction): """Tests the tether elevation.""" def __init__(self, bad_lower_limit, good_lower_limit, good_upper_limit, bad_upper_limit, severity, transform_stages=None, sustained_duration=0.0, extra_system_labels=None): super(TetherElevationScoringFunction, self).__init__( ('Tether Elevation %s' % transform_stages if transform_stages else 'Tether Elevation'), 'deg', bad_lower_limit, good_lower_limit, good_upper_limit, bad_upper_limit, severity) self._sustained_duration = sustained_duration
import os import sys import textwrap import gflags import makani from makani.avionics.network import message_type from makani.lib.python import c_helpers gflags.DEFINE_string('h2py_dir', '.', 'Full path to H2PY_DIR containing pack modules.') gflags.DEFINE_string('autogen_root', makani.HOME, 'Root of the source tree for the output files.') FLAGS = gflags.FLAGS message_type_helper = c_helpers.EnumHelper('MessageType', message_type) def _GenStructName(message_type_name): short_name = message_type_helper.ShortName(message_type_name) if short_name in [ 'ControlTelemetry', 'ControlSlowTelemetry', 'SimTelemetry', 'GroundTelemetry' ]: return short_name else: return short_name + 'Message' def _GenPackFunctionName(message_type_name): return 'Pack' + _GenStructName(message_type_name)
def __init__(self, for_log, message_type, node, **base_kwargs): super(AioMonHumidityChecker, self).__init__( for_log, message_type, node, 'aio_mon.si7021_data[:].rel_humidity', 'aio_mon.si7021_populated', c_helpers.EnumHelper('AioSi7021Monitor', aio_types), **base_kwargs)
""""Monitor indicators from the ground station.""" from makani.avionics.firmware.monitors import mvlv_types from makani.control import control_types from makani.control import system_params from makani.control import system_types from makani.gs.monitor2.apps.layout import indicator from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.plugins import common from makani.gs.monitor2.apps.plugins.indicators import avionics from makani.gs.monitor2.apps.plugins.indicators import batt from makani.lib.python import c_helpers from makani.lib.python import struct_tree FLIGHT_MODE_HELPER = c_helpers.EnumHelper('FlightMode', control_types) MVLV_ANALOG_VOLTAGE_HELPER = c_helpers.EnumHelper('MvlvAnalogVoltage', mvlv_types) MVLV_MCP342X_MONITOR_HELPER = c_helpers.EnumHelper('MvlvMcp342xMonitor', mvlv_types) MVLV_MON_WARNING_HELPER = c_helpers.EnumHelper('MvlvMonitorWarning', mvlv_types) MVLV_MON_ERROR_HELPER = c_helpers.EnumHelper('MvlvMonitorError', mvlv_types) MVLV_MON_STATUS_HELPER = c_helpers.EnumHelper('MvlvMonitorStatus', mvlv_types) _SYSTEM_PARAMS = system_params.GetSystemParams().contents
1.0 if flap_idx in [system_types.kFlapA4, system_types.kFlapA5] else 0.0 for flap_idx in range(system_types.kNumFlaps) ] _ELE_FLAP_BASIS = [ 1.0 if flap_idx == system_types.kFlapEle else 0.0 for flap_idx in range(system_types.kNumFlaps) ] _RUD_FLAP_BASIS = [ 1.0 if flap_idx == system_types.kFlapRud else 0.0 for flap_idx in range(system_types.kNumFlaps) ] _FLAP_LABEL_LIST = c_helpers.EnumHelper('FlapLabel', system_labels, prefix='kFlap').ShortNames() _FLAP_LABELS = {i: _FLAP_LABEL_LIST[i] for i in range(len(_FLAP_LABEL_LIST))} def GetFlapOffsetParameterRanges(): return [ # Assuming maximum 1 deg (0.02 rad) error in control surface angles for # Monte Carlo sweeps, ~3 deg (0.05 rad) variation in crosswind sweeps. parameter_tables.AeroSimFlapOffsetParameterRange( 'Center Flap Offset [rad]', _CENTER_FLAP_BASIS, [-0.05, 0.05], distribution={ 'mean': 0.0, 'sigma': 0.01, 'bound': 2.0,
This module looks for an 'mcp9800_config' tuple in the configuration file (specified on command line). The mcp9800_config tuple should contain the hardware revision EnumHelper and a dictionary that maps the board's hardware revision to a list of dictionaries. Each dictionary in this list specifies the configuration for each MCP9800 chip. """ import sys import textwrap from makani.avionics.firmware.drivers import mcp9800_types from makani.avionics.firmware.monitors import generate_monitor_base from makani.lib.python import c_helpers resolution_helper = c_helpers.EnumHelper('Mcp9800Resolution', mcp9800_types) class Mcp9800DeviceConfig(generate_monitor_base.DeviceConfigBase): """Generate MCP9800 monitor configuration.""" # TODO: Add unit tests. def __init__(self, config): expected_parameters = {'name', 'address', 'resolution'} super(Mcp9800DeviceConfig, self).__init__(config, expected_parameters) def CheckParameterValues(self, config): """Verify the MCP9800 configuration.""" name = config['name']
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import unittest from makani.avionics.bootloader import bootloader_client from makani.avionics.common import aio from makani.avionics.firmware.identity import identity_types from makani.avionics.network import aio_labels from makani.avionics.network import aio_node from makani.lib.python import c_helpers import mock hardware_type_helper = c_helpers.EnumHelper('HardwareType', identity_types) class ParseArgumentsTest(unittest.TestCase): def RunParse(self, arg_string): argv = ['bootloader_client.py'] + arg_string.split() with mock.patch.object(sys, 'argv', argv): return bootloader_client.ParseArguments() def testBatt(self): parsed_args = self.RunParse('--target batt_a batt_application.elf') args = parsed_args['args'] self.assertEqual(parsed_args['file'], 'batt_application.elf') self.assertEqual(args.force_hardware, None)
from makani.analysis.aero import aero_ssam from makani.analysis.aero import apparent_wind_util from makani.analysis.log_analysis import loop_averager from makani.control import system_types from makani.lib.python import c_helpers from makani.lib.python.batch_sim import scoring_functions from makani.lib.python.h5_utils import numpy_utils from makani.system import labels as system_labels import numpy as np from scipy import interpolate import scoring_functions_util as scoring_util _FLAP_LABEL_HELPER = c_helpers.EnumHelper('FlapLabel', system_labels, prefix='kFlap') _WING_MODEL_HELPER = c_helpers.EnumHelper('WingModel', system_types) _WING_SERIAL_HELPER = c_helpers.EnumHelper('WingSerial', system_types) class AirspeedMaxScoringFunction( scoring_functions.SingleSidedLimitScoringFunction): """Tests if the airspeed falls outside of acceptable limits.""" def __init__(self, good_limit, bad_limit, severity): super(AirspeedMaxScoringFunction, self).__init__('Max Airspeed', 'm/s', good_limit, bad_limit, severity) def GetSystemLabels(self): return ['controls']
def _ReduceWorkerOutput(self, outputs): matplotlib.use('Agg') pylab = importlib.import_module('pylab') # Create directory to place output files. if not os.path.exists(FLAGS.output_dir): os.makedirs(FLAGS.output_dir) # Write a text report of extreme values experienced during flight. self._WriteReport(outputs) # pylint: disable=g-long-lambda outputs.sort(cmp=lambda x, y: cmp(x['parameters']['wind_speed'], y[ 'parameters']['wind_speed'])) data_all = batch_sim_util.CollateOutputs(outputs) data = batch_sim_util.CollateOutputs([ o for o in outputs if (o['parameters']['joystick_throttle'] == FLAGS.joystick_throttles[0]) ]) stats_all = data_all['statistics'] stats = data['statistics'] wind_speeds_all = data_all['parameters']['wind_speed'] wind_speeds = data['parameters']['wind_speed'] # Save power curve to HDF5 file. # # TODO: Make a Python dictionary to HDF5 conversion # function. data_file = h5py.File(os.path.join(FLAGS.output_dir, 'data.h5'), 'w') data_file.create_dataset('wind_speed', data=wind_speeds) data_file.create_dataset('crosswind_power', data=stats['crosswind_power']['mean'][:]) data_file.create_dataset('sim_success', data=data['sim_success']) flap_labels = [ 'Port aileron (outer)', 'Port aileron (inner)', 'Center flap (port)', 'Center flap (starboard)', 'Starboard aileron (inner)', 'Starboard aileron (outer)', 'Elevator', 'Rudder' ] subsystem_helper = c_helpers.EnumHelper('SubsystemLabel', control_types, prefix='kSubsys') # Plot mean flap deflections. pylab.figure().patch.set_alpha(0.0) for flap_num in [0, 2, 4, 6, 7]: pylab.plot(wind_speeds, stats['flaps'][flap_num]['mean'], label=flap_labels[flap_num]) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.6, 0.1]) pylab.title('Mean flap deflections') pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Deflection [rad]') pylab.legend(framealpha=0.5) pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.savefig(FLAGS.output_dir + '/mean_flap_deflections.svg') pylab.close() # Plot standard deviation flap deflections. pylab.figure().patch.set_alpha(0.0) for flap_num in [0, 2, 4, 6, 7]: pylab.plot(wind_speeds, stats['flaps'][flap_num]['std'], label=flap_labels[flap_num]) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.0, 0.2]) pylab.title('Standard deviation flap deflections') pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Deflection [rad]') pylab.legend(framealpha=0.5) pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.savefig(FLAGS.output_dir + '/standard_deviation_flap_deflections.svg') pylab.close() # Plot controller faults. pylab.figure().patch.set_alpha(0.0) num_subsystems = numpy.shape(data['faults'])[0] # The values in data['faults'] are bitfields (see the FaultType # enum). Here we want just a faults / no faults indication. pylab.imshow(numpy.array(data['faults']) != 0, extent=[ numpy.min(wind_speeds), numpy.max(wind_speeds), 0, num_subsystems ], origin='lower', interpolation='none', cmap=pylab.get_cmap('RdYlGn_r'), aspect=0.3) pylab.title('Controller faults') pylab.xlabel('Wind speed [m/s]') pylab.gca().set_yticks( numpy.linspace(0.5, num_subsystems - 0.5, num_subsystems)) pylab.gca().set_yticklabels( [subsystem_helper.ShortName(i) for i in range(num_subsystems)], fontsize=8) pylab.savefig(FLAGS.output_dir + '/faults_vs_wind_speed.png') pylab.close() # Plot power curves by throttle position. pylab.figure().patch.set_alpha(0.0) throttles_all = data_all['parameters']['joystick_throttle'] throttle_list = sorted(set(throttles_all)) for throttle in throttle_list: pylab.plot( wind_speeds_all[throttles_all == throttle], stats_all['crosswind_power']['mean'][throttles_all == throttle] / 1e3, label='%0.2f' % throttle) # Reset the color cycle so we get the same colors for tension. pylab.gca().set_color_cycle(None) for throttle in throttle_list: pylab.plot(wind_speeds_all[throttles_all == throttle], stats_all['tension']['max'][throttles_all == throttle] / 1e3, linestyle='--') self._ShadeFailureRegions(pylab, wind_speeds_all, data_all['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds_all), max(wind_speeds_all)]) pylab.title('Power curve by throttle position') pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Aerodynamic power [kW] / Tension [kN]') pylab.legend(title='Throttle position', loc=2) pylab.grid() pylab.savefig(FLAGS.output_dir + '/power_curve_by_throttle.svg') pylab.close() # Plot power and tension curves. pylab.figure().patch.set_alpha(0.0) pylab.subplot(2, 1, 1) pylab.plot(annotations['power_curve']['v_wind'], annotations['power_curve']['P'] / 1e3, color='gray', linestyle='-', linewidth=4, alpha=0.6) pylab.plot(wind_speeds, stats['crosswind_power']['mean'] / 1e3, label='Aerodynamic power') pylab.fill_between(wind_speeds, stats['crosswind_power']['min'] / 1e3, stats['crosswind_power']['max'] / 1e3, color='blue', alpha=0.3) pylab.fill_between(wind_speeds, (stats['crosswind_power']['mean'] - stats['crosswind_power']['std']) / 1e3, (stats['crosswind_power']['mean'] + stats['crosswind_power']['std']) / 1e3, color='blue', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.title('Power and tension curves') pylab.ylabel('Aerodynamic power [kW]') pylab.grid() # Annotate plot with the date and time of creation, and git commit hash. datestr = datetime.datetime.now().strftime('%Y-%m-%d %H:%M %Z') git_commit = outputs[0]['git_commit'] if 'git_commit' in outputs[ 0] else '' pylab.text(0.05, 0.8, '%s %s' % (datestr, git_commit[0:7]), transform=pylab.gca().transAxes, fontdict={ 'size': 16, 'color': 'grey' }) pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([ -1.0 * annotations['P_max'] / 1e3, 1.5 * annotations['P_max'] / 1e3 ]) pylab.subplot(2, 1, 2) pylab.plot(wind_speeds, stats['tension']['max'] / 1e3, color='green', label='Tension') pylab.plot(wind_speeds, stats['tension']['mean'] / 1e3, color='green', linestyle=':', label='Tension') pylab.fill_between(wind_speeds, stats['tension']['min'] / 1e3, stats['tension']['max'] / 1e3, color='green', alpha=0.3) pylab.fill_between( wind_speeds, (stats['tension']['mean'] - stats['tension']['std']) / 1e3, (stats['tension']['mean'] + stats['tension']['std']) / 1e3, color='green', alpha=0.3) pylab.axhline(y=annotations['T_max'] / 1e3, color='gray', linestyle='--', label='Max Tension', linewidth=2) pylab.axhline(y=annotations['T_NE'] / 1e3, color='gray', linestyle='-', label='Tension NE', linewidth=2) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([0.0, 1.2 * annotations['T_max'] / 1e3]) pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Tension [kN]') pylab.grid() pylab.savefig(FLAGS.output_dir + '/power_and_tension_curve.svg') pylab.close() # Plot angles. pylab.figure().patch.set_alpha(0.0) pylab.subplot(3, 1, 1) pylab.plot(wind_speeds, stats['tether_roll']['mean'], color='blue') pylab.fill_between(wind_speeds, stats['tether_roll']['min'], stats['tether_roll']['max'], color='blue', alpha=0.3) pylab.fill_between( wind_speeds, stats['tether_roll']['mean'] - stats['tether_roll']['std'], stats['tether_roll']['mean'] + stats['tether_roll']['std'], color='blue', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.2, 0.4]) pylab.title('Angles') pylab.ylabel('Tether roll [rad]') pylab.grid() pylab.subplot(3, 1, 2) pylab.plot(wind_speeds, stats['alpha']['mean'], color='green') pylab.fill_between(wind_speeds, stats['alpha']['min'], stats['alpha']['max'], color='green', alpha=0.3) pylab.fill_between(wind_speeds, stats['alpha']['mean'] - stats['alpha']['std'], stats['alpha']['mean'] + stats['alpha']['std'], color='green', alpha=0.3) pylab.axhline(0, color='black') pylab.ylabel('Angle-of-attack [rad]') pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.15, 0.15]) pylab.subplot(3, 1, 3) pylab.plot(wind_speeds, stats['beta']['mean'], color='red') pylab.fill_between(wind_speeds, stats['beta']['min'], stats['beta']['max'], color='red', alpha=0.3) pylab.fill_between(wind_speeds, stats['beta']['mean'] - stats['beta']['std'], stats['beta']['mean'] + stats['beta']['std'], color='red', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.15, 0.15]) pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Sideslip [rad]') pylab.grid() pylab.savefig(FLAGS.output_dir + '/angles_vs_wind_speed.svg') pylab.close() # Plot angle errors. pylab.figure().patch.set_alpha(0.0) pylab.subplot(3, 1, 1) pylab.plot(wind_speeds, stats['tether_roll_error']['mean'], color='blue') pylab.fill_between(wind_speeds, stats['tether_roll_error']['min'], stats['tether_roll_error']['max'], color='blue', alpha=0.3) pylab.fill_between(wind_speeds, stats['tether_roll_error']['mean'] - stats['tether_roll_error']['std'], stats['tether_roll_error']['mean'] + stats['tether_roll_error']['std'], color='blue', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.3, 0.3]) pylab.title('Angle errors') pylab.ylabel('Tether roll\nerror [rad]') pylab.grid() pylab.subplot(3, 1, 2) pylab.plot(wind_speeds, stats['alpha_error']['mean'], color='green') pylab.fill_between(wind_speeds, stats['alpha_error']['min'], stats['alpha_error']['max'], color='green', alpha=0.3) pylab.fill_between( wind_speeds, stats['alpha_error']['mean'] - stats['alpha_error']['std'], stats['alpha_error']['mean'] + stats['alpha_error']['std'], color='green', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.ylabel('Angle-of-attack\nerror [rad]') pylab.grid() pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.15, 0.15]) pylab.subplot(3, 1, 3) pylab.plot(wind_speeds, stats['beta_error']['mean'], color='red') pylab.fill_between(wind_speeds, stats['beta_error']['min'], stats['beta_error']['max'], color='red', alpha=0.3) pylab.fill_between( wind_speeds, stats['beta_error']['mean'] - stats['beta_error']['std'], stats['beta_error']['mean'] + stats['beta_error']['std'], color='red', alpha=0.3) self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.15, 0.15]) pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Sideslip\nerror [rad]') pylab.grid() pylab.savefig(FLAGS.output_dir + '/angle_errors_vs_wind_speed.svg') pylab.close() # Plot radius versus wind speed. pylab.figure().patch.set_alpha(0.0) pylab.plot(wind_speeds, stats['radius']['mean'], color='blue') pylab.fill_between(wind_speeds, stats['radius']['min'], stats['radius']['max'], color='blue', alpha=0.3) pylab.fill_between(wind_speeds, stats['radius']['mean'] - stats['radius']['std'], stats['radius']['mean'] + stats['radius']['std'], color='blue', alpha=0.3) pylab.xlim([min(wind_speeds), max(wind_speeds)]) radius_round_50 = 50.0 * round( numpy.median(stats['radius']['mean']) / 50.0) pylab.ylim([radius_round_50 - 25.0, radius_round_50 + 25.0]) pylab.title('Radius') pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Radius [m]') pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.savefig(FLAGS.output_dir + '/radius_vs_wind_speed.svg') pylab.close() # Plot curvature errors. pylab.figure().patch.set_alpha(0.0) pylab.subplot(2, 1, 1) pylab.plot(wind_speeds, stats['geom_curvature_error']['mean'], color='blue') pylab.fill_between(wind_speeds, stats['geom_curvature_error']['min'], stats['geom_curvature_error']['max'], color='blue', alpha=0.3) pylab.fill_between(wind_speeds, stats['geom_curvature_error']['mean'] - stats['geom_curvature_error']['std'], stats['geom_curvature_error']['mean'] + stats['geom_curvature_error']['std'], color='blue', alpha=0.3) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.01, 0.01]) pylab.title('Curvature errors') pylab.ylabel('Geometric curvature\nerror [1/m]') pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.subplot(2, 1, 2) pylab.plot(wind_speeds, stats['aero_curvature_error']['mean'], color='green') pylab.fill_between(wind_speeds, stats['aero_curvature_error']['min'], stats['aero_curvature_error']['max'], color='green', alpha=0.3) pylab.fill_between(wind_speeds, stats['aero_curvature_error']['mean'] - stats['aero_curvature_error']['std'], stats['aero_curvature_error']['mean'] + stats['aero_curvature_error']['std'], color='green', alpha=0.3) pylab.axhline(0, color='black') pylab.xlim([min(wind_speeds), max(wind_speeds)]) pylab.ylim([-0.01, 0.01]) pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Aerodynamic curvature\nerror [1/m]') pylab.grid() self._ShadeFailureRegions(pylab, wind_speeds, data['sim_success']) pylab.savefig(FLAGS.output_dir + '/curvature_errors_vs_wind_speed.svg') pylab.close() pylab.figure().patch.set_alpha(0.0) for success_val, plot_style in [(True, 'g+'), (False, 'rs')]: pylab.plot([ output['parameters']['wind_speed'] for output in outputs if output['sim_success'] == success_val ], [ output['parameters']['joystick_throttle'] for output in outputs if output['sim_success'] == success_val ], plot_style) pylab.xlabel('Wind speed [m/s]') pylab.ylabel('Joystick throttle') pylab.ylim([0.0, 1.1]) pylab.legend(['successful simulations', 'crashed simulations'], loc='lower right') pylab.savefig(FLAGS.output_dir + '/sim_success_param_space.svg') # Copy HTML file that displays the charts to the output directory. dashboard_file = os.path.join( makani.HOME, 'analysis/power_curve/power_curve_dashboard.html') final_output_file = os.path.join(FLAGS.output_dir, 'index.html') shutil.copyfile(dashboard_file, final_output_file) logging.info('Output may be viewed at file://%s.', os.path.abspath(final_output_file))
# limitations under the License. """Command line client for controlling anti-collision lights.""" import os import re import socket import makani from makani.avionics.common import aio from makani.avionics.common import cmd_client from makani.avionics.common import pack_avionics_messages from makani.avionics.network import aio_node from makani.avionics.network import message_type from makani.lib.python import c_helpers aio_node_helper = c_helpers.EnumHelper('AioNode', aio_node) def BuildLightParamDict(): """Builds a dict mapping light param names to their indices.""" # Build up parameter list. filename = os.path.join(makani.HOME, 'avionics/firmware/drivers/faa_light.c') with open(filename) as f: f_text = f.read() # Get parameter array string. re_string = r'static float \*g_mutable_param_addrs\[\] = {\s*^([\s\S]*)^};' array_string = re.search(re_string, f_text, re.MULTILINE) re_string = r'^ *&[\w.]+\[kLightType([\w\[\]\.]+)'
def testBadLength(self): bad_enum = FULL_ENUM.copy() bad_enum['kNumTestEnums'] = len(ENUM_VALUES) + 1 module = FakeModule(bad_enum) with self.assertRaises(AssertionError): c_helpers.EnumHelper('TestEnum', module)
from makani.control import sensor_util from makani.control import system_params from makani.gs.monitor import monitor_params from makani.gs.monitor2.apps.layout import checks from makani.gs.monitor2.apps.layout import indicator from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.layout import widgets from makani.gs.monitor2.apps.plugins import common from makani.gs.monitor2.project import settings from makani.lib.python import c_helpers from makani.lib.python import ctype_util from makani.lib.python import struct_tree import numpy as np _AIO_SI7021_HELPER = c_helpers.EnumHelper('AioSi7021Monitor', aio_types) _SERVO_LABEL_HELPER = c_helpers.EnumHelper('ServoLabel', aio_labels, prefix='kServo') _CS_LABEL_HELPER = c_helpers.EnumHelper('CoreSwitchLabel', aio_labels, prefix='kCoreSwitch') _CS_SI7021_HELPER = c_helpers.EnumHelper('CsSi7021Monitor', cs_types) _FLIGHT_COMPUTER_HELPER = c_helpers.EnumHelper('FlightComputer', aio_labels) _BRIDLE_JUNC_WARNING_HELPER = c_helpers.EnumHelper('BridleJuncWarning', loadcell_types) _MOTOR_LABELS_HELPER = c_helpers.EnumHelper('MotorLabel', aio_labels, prefix='kMotor') _PITOT_COVER_STATUS_HELPER = c_helpers.EnumHelper('PitotCoverStatus', pitot_cover_types) _MONITOR_PARAMS = monitor_params.GetMonitorParams().contents _SYSTEM_PARAMS = system_params.GetSystemParams().contents
from makani.avionics.network import aio_labels from makani.avionics.network import aio_node from makani.avionics.network import message_type as aio_message_type from makani.control import control_types from makani.control import system_params from makani.gs.monitor import monitor_params from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.plugins import common from makani.gs.monitor2.apps.plugins.indicators import control from makani.gs.monitor2.apps.receiver import test_util from makani.gs.monitor2.high_frequency_filters import filter_handlers from makani.lib.python import c_helpers from makani.lib.python import struct_tree import mock AIO_NODE_HELPER = c_helpers.EnumHelper('AioNode', aio_node) MESSAGE_TYPE_HELPER = c_helpers.EnumHelper('MessageType', aio_message_type) MONITOR_PARAMS = monitor_params.GetMonitorParams().contents class TestIndicators(unittest.TestCase): @classmethod def setUp(cls): aio_util.InitFilters() def _SynthesizeControlTelemetry(self, sequence=0): return test_util.SynthesizeMessages( ['ControlTelemetry', 'ControlSlowTelemetry'], sequence) def _MockMessage(self, message,
# See the License for the specific language governing permissions and # limitations under the License. """Indicators for GPS.""" from makani.analysis.checks.collection import gps_checks from makani.avionics.common import novatel_types from makani.avionics.common import pack_avionics_messages from makani.avionics.common import septentrio_types from makani.gs.monitor2.apps.layout import indicator from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.plugins import common from makani.lib.python import c_helpers from makani.system import labels import numpy NOVATEL_SOLUTION_STATUS_HELPER = c_helpers.EnumHelper('NovAtelSolutionStatus', novatel_types) NOVATEL_SOLUTION_TYPE_HELPER = c_helpers.EnumHelper('NovAtelSolutionType', novatel_types) SEPTENTRIO_ERROR_HELPER = c_helpers.EnumHelper('SeptentrioPvtError', septentrio_types) SEPTENTRIO_MODE_HELPER = c_helpers.EnumHelper('SeptentrioPvtMode', septentrio_types) SEPTENTRIO_MODE_BITMASK = c_helpers.EnumHelper( 'SeptentrioPvtModeBit', septentrio_types).Value('SolutionMask') TETHER_GPS_SOLUTION_STATUS_HELPER = c_helpers.EnumHelper( 'TetherGpsSolutionStatus', pack_avionics_messages) WING_GPS_RECEIVER_HELPER = c_helpers.EnumHelper('WingGpsReceiver', labels)
def GetParams(wing_model, wing_serial, use_wake_model=True): """Returns the set of parameters used for the model. Args: wing_model: Wing model (e.g. 'm600'). wing_serial: String giving the desired wing serial number (e.g. '01'). use_wake_model: Boolean flag that determines whether the advected rotor wake is included in the calculation of the local apparent wind. Returns: Parameter structure for the hover model. """ wing_config_name = wing_flag.WingModelToConfigName(wing_model) if wing_config_name == 'oktoberkite': wing_serial_helper = c_helpers.EnumHelper('WingSerialOktoberKite', system_types) db_name = 'avl/oktoberkite.avl' elif wing_config_name == 'm600': wing_serial_helper = c_helpers.EnumHelper('WingSerial', system_types) db_name = 'avl/m600_low_tail_no_winglets.avl' else: assert False, 'Wing model, %s, is not recognized.' % wing_config_name mconfig.WING_MODEL = wing_config_name system_params = mconfig.MakeParams( wing_config_name + '.system_params', overrides={'wing_serial': wing_serial_helper.Value(wing_serial)}, override_method='derived') # Thrust level is necessary for the propeller wake model. It is # determined by assuming the kite is supporting the full weight of # the tether and has to balance the pitching moment about the # C.G. with differential thrust between the top and bottom motor # rows: # # 4.0 * thrust_top + 4.0 * thrust_bot = weight # z_top * thrust_top + z_bot * thrust_bot = 0.0 total_mass = (system_params['wing']['m'] + (system_params['tether']['linear_density'] * system_params['tether']['length'])) weight = system_params['phys']['g'] * total_mass main_wing_incidence_deg = system_params['wing']['wing_i'] rotor_pos_z = [ rotor['pos'][2] - system_params['wing']['center_of_mass_pos'][2] for rotor in system_params['rotors'] ] z_bot = np.mean([z for z in rotor_pos_z if z > 0.0]) z_top = np.mean([z for z in rotor_pos_z if z < 0.0]) thrust_bot = weight / (4.0 * (1.0 + (-z_bot / z_top))) thrust_top = thrust_bot * (-z_bot / z_top) rotors = [{ 'pos': rotor['pos'], 'radius': rotor['D'] / 2.0, 'thrust': thrust_bot if rotor['pos'][2] > 0.0 else thrust_top } for rotor in system_params['rotors']] # It is assumed that all rotors have the same pitch angle. rotor_pitches = [ np.arctan2(-rotor['axis'][2], rotor['axis'][0]) for rotor in system_params['rotors'] ] assert np.all(rotor_pitches == rotor_pitches[0]) aero_home = os.path.join(makani.HOME, 'analysis/aero/') wing = avl_reader.AvlReader(os.path.join(aero_home, db_name)) # TODO: Extract airfoil information from avl_reader.py. if wing_config_name == 'm600': # The maximum value of 16.0 degrees for the M600 is because of the airfoil # hysteresis going from attached flow to detached flow, which occures during # HoverAccel. stall_angles_deg = [1.0, 16.0] elif wing_config_name == 'oktoberkite': # (b/146071300) Currently, there is no understanding of the hysteresis # curve. This needs to be understood and modified once available. # (b/146061441) Additional ramifications for this choice. stall_angles_deg = [1.0, 20.0] else: assert False, 'Unknown wing model.' # (b/146081917) Oktoberkite will utilize the same airfoils as the M600. outer_wing_airfoil = airfoil.Airfoil(os.path.join( aero_home, 'hover_model/airfoils/oref.json'), stall_angles_deg=stall_angles_deg) inner_wing_airfoil = airfoil.Airfoil(os.path.join( aero_home, 'hover_model/airfoils/ref.json'), stall_angles_deg=stall_angles_deg) pylon_airfoil = airfoil.Airfoil(os.path.join( aero_home, 'hover_model/airfoils/mp6d.json'), stall_angles_deg=[-10.0, 10.0]) airfoils = { 'Wing (panel 0)': outer_wing_airfoil, 'Wing (panel 1)': outer_wing_airfoil, 'Wing (panel 2)': inner_wing_airfoil, 'Wing (panel 3)': inner_wing_airfoil, 'Wing (panel 4)': inner_wing_airfoil, 'Wing (panel 5)': inner_wing_airfoil, 'Wing (panel 6)': inner_wing_airfoil, 'Wing (panel 7)': inner_wing_airfoil, 'Wing (panel 8)': outer_wing_airfoil, 'Wing (panel 9)': outer_wing_airfoil, 'Pylon 1': pylon_airfoil, 'Pylon 2': pylon_airfoil, 'Pylon 3': pylon_airfoil, 'Pylon 4': pylon_airfoil, 'Horizontal tail': airfoil.Airfoil(os.path.join(aero_home, 'hover_model/airfoils/f8.json'), stall_angles_deg=[-10.0, 10.0]), 'Vertical tail': airfoil.Airfoil(os.path.join( aero_home, 'hover_model/airfoils/approx_blade_rj8.json'), stall_angles_deg=[-10.0, 10.0]) } # TODO: Extract flap index information from # avl_reader.py. # The indices of the wing pannel indicate which flap number they are going to # be in the controller, i.e. "0" is A1 for the M600 and "7" is the rudder for # the M600. An index of -1 means that the panel has no flap section. if wing_config_name == 'm600': delta_indices = { 'Wing (panel 0)': 0, 'Wing (panel 1)': 1, 'Wing (panel 2)': -1, 'Wing (panel 3)': 2, 'Wing (panel 4)': -1, 'Wing (panel 5)': -1, 'Wing (panel 6)': 3, 'Wing (panel 7)': -1, 'Wing (panel 8)': 4, 'Wing (panel 9)': 5, 'Pylon 1': -1, 'Pylon 2': -1, 'Pylon 3': -1, 'Pylon 4': -1, 'Horizontal tail': 6, 'Vertical tail': 7 } elif wing_config_name == 'oktoberkite': delta_indices = { 'Wing (panel 0)': 0, 'Wing (panel 1)': 1, 'Wing (panel 2)': 2, 'Wing (panel 3)': -1, 'Wing (panel 4)': -1, 'Wing (panel 5)': -1, 'Wing (panel 6)': -1, 'Wing (panel 7)': 3, 'Wing (panel 8)': 4, 'Wing (panel 9)': 5, 'Pylon 1': -1, 'Pylon 2': -1, 'Pylon 3': -1, 'Pylon 4': -1, 'Horizontal tail': 6, 'Vertical tail': 7 } # TODO: Modify the hover model to accept the # avl_reader.py surface dictionary directly. hover_model_panels = [] for surface in wing.properties['surfaces']: if surface['name'] in ('Horizontal tail', 'Vertical tail', 'Pylon 1', 'Pylon 2', 'Pylon 3', 'Pylon 4'): hover_model_panels.append({ 'name': surface['name'], 'area': surface['area'], 'span': surface['span'], 'surface_span': surface['span'], 'chord': surface['standard_mean_chord'], 'aspect_ratio': surface['span']**2.0 / surface['area'], # The 2.0 is a fudge factor to match the C_Y slope from AVL. 'cl_weight': 2.0 if surface['name'][0:5] == 'Pylon' else 1.0, 'pos_b': surface['aerodynamic_center_b'], 'airfoil': airfoils[surface['name']], 'incidence': surface['mean_incidence'], # These surfaces are single panel. So there is no relative incidence. 'relative_incidence': 0.0, 'dcm_b2s': surface['dcm_b2surface'], 'delta_index': delta_indices[surface['name']] }) elif surface['name'] == 'Wing': for i, panel in enumerate(surface['panels']): panel_name = surface['name'] + ' (panel %d)' % i hover_model_panels.append({ 'name': panel_name, 'area': panel['area'], 'span': panel['span'], 'surface_span': surface['span'], 'chord': panel['standard_mean_chord'], 'aspect_ratio': surface['span']**2.0 / surface['area'], 'cl_weight': (surface['mean_incidence'] * surface['standard_mean_chord'] / (panel['mean_incidence'] * panel['standard_mean_chord'])), 'pos_b': panel['aerodynamic_center_b'], 'airfoil': airfoils[panel_name], 'incidence': panel['mean_incidence'], 'relative_incidence': (panel['mean_incidence'] - np.deg2rad(main_wing_incidence_deg)), 'dcm_b2s': surface['dcm_b2surface'], 'delta_index': delta_indices[panel_name] }) else: assert False # Check that we aren't missing any panels. assert (len(hover_model_panels) == len(delta_indices) and len(hover_model_panels) == len(airfoils)) return { 'wing_serial': wing_serial, 'git_commit': build_info.GetGitSha(), 'use_wake_model': use_wake_model, 'rotors': rotors, 'rotor_pitch': rotor_pitches[0], 'phys': { 'rho': 1.1, 'dynamic_viscosity': 1.789e-5 }, 'wing': { 'area': system_params['wing']['A'], 'b': system_params['wing']['b'], 'c': system_params['wing']['c'], 'wing_i': system_params['wing']['wing_i'] }, 'center_of_mass_pos': system_params['wing']['center_of_mass_pos'], 'panels': hover_model_panels }
# limitations under the License. """Indicators for network status.""" import collections from makani.analysis.checks import check_range from makani.avionics.common import cvt from makani.avionics.network import aio_node from makani.control import system_params from makani.control import system_types from makani.gs.monitor2.apps.layout import indicator from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.plugins import common from makani.lib.python import c_helpers _TETHER_NODE_FLAGS_HELPER = c_helpers.EnumHelper('TetherNodeFlag', cvt) _AIO_NODE_HELPER = c_helpers.EnumHelper('AioNode', aio_node) _SYSTEM_PARAMS = system_params.GetSystemParams().contents _WING_TMS570_NODES = common.WingTms570Nodes() class BaseTetherNodeStatusIndicator(indicator.BaseAttributeIndicator): """Base indicator class for individual node status.""" def __init__(self, name, node_name): node_id = _AIO_NODE_HELPER.Value(node_name) super(BaseTetherNodeStatusIndicator, self).__init__([ ('filtered', 'merge_tether_down', 'node_status[%d]' % node_id), ('filtered', 'merge_tether_down', 'node_status_valid[%d]' % node_id), ], name)
def __init__(self, for_log, message_type, node, **base_kwargs): super(AioMonBusCurrentChecker, self).__init__( for_log, message_type, node, 'aio_mon.ina219_data[:].current', 'aio_mon.ina219_populated', c_helpers.EnumHelper('AioIna219Monitor', aio_types), **base_kwargs)
# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Classes to perform checks on servos.""" from makani.analysis.checks import base_check from makani.analysis.checks.collection import avionics_checks from makani.avionics.firmware.monitors import servo_types from makani.avionics.network import aio_node from makani.lib.python import c_helpers _SERVO_LABELS_HELPER = c_helpers.EnumHelper('ServoLabel', aio_node, prefix='kAioNodeServo') _SERVO_ANALOG_VOLTAGE_HELPER = c_helpers.EnumHelper('ServoAnalogVoltage', servo_types) _SERVO_MCP9800_MONITOR_HELPER = c_helpers.EnumHelper('ServoMcp9800', servo_types) _SERVO_MONITOR_ERROR_HELPER = c_helpers.EnumHelper('ServoMonitorError', servo_types) _SERVO_MONITOR_WARNING_HELPER = c_helpers.EnumHelper('ServoMonitorWarning', servo_types) class ServoMonAnalogChecker(avionics_checks.BaseVoltageChecker): """The monitor to check servo voltage.""" @base_check.RegisterSpecs def __init__(self,
def setUp(self): module = FakeModule(FULL_ENUM) self.helper = c_helpers.EnumHelper('TestEnum', module)
This module looks for an 'ads7828_config' tuple in the configuration file (specified on command line). The ads7828_config tuple should contain the hardware revision EnumHelper and a dictionary that maps the board's hardware revision to a list of dictionaries. Each dictionary in this list specifies the configuration for each ADS7828 chip. """ import sys import textwrap from makani.avionics.firmware.drivers import ads7828_types from makani.avionics.firmware.monitors import generate_monitor_base from makani.lib.python import c_helpers select_helper = c_helpers.EnumHelper('Ads7828Select', ads7828_types) convert_helper = c_helpers.EnumHelper('Ads7828PowerConverter', ads7828_types) ref_helper = c_helpers.EnumHelper('Ads7828PowerReference', ads7828_types) class Ads7828DeviceConfig(generate_monitor_base.DeviceConfigBase): """Generate ADS7828 voltage/current monitor configuration.""" # TODO: Add unit tests. def __init__(self, config): expected_parameters = { 'name', 'address', 'channel', 'converter', 'reference', 'input_divider', 'input_offset', 'nominal', 'min', 'max' }
# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Functions to write controller gains into output files.""" import json import pprint from makani.control import system_types from makani.lib.python import c_helpers _WING_SERIAL_HELPER = c_helpers.EnumHelper('WingSerial', system_types) def WriteControllers(generating_file, filename, controllers): """Writes gain matrices to configuration file. Args: generating_file: File calling this function. filename: Full path to the gain matrix file to write. controllers: OrderedDict of airspeed values and gain matrices. """ docstring = (' """Returns control gain matrices' 'for any kite serial number."""') with open(filename, 'w') as f:
import collections import copy import datetime import logging import threading from makani.avionics.network import message_type as aio_message_type from makani.gs.monitor2.apps.receiver import base_receiver from makani.lib.python import c_helpers from makani.lib.python import struct_tree from makani.lib.python.trackers import json_obj import numpy import pytz _MESSAGE_TYPE_HELPER = c_helpers.EnumHelper('MessageType', aio_message_type) class LogMessage(json_obj.JsonObj): """A message reconstructed from the log.""" def __init__(self, payload, timestamp, seq_num, version, msg_enum, source): self._payload = payload self._timestamp = timestamp self._sequence = seq_num self._version = version self._msg_enum = msg_enum self._source = source self._message = None def Get(self, readonly): """Obtain a JSON representation of the message.
from makani.analysis.control import flap_limits from makani.avionics.common import pack_avionics_messages from makani.avionics.common import servo_types as servo_common from makani.avionics.firmware.monitors import servo_types from makani.avionics.network import aio_labels from makani.control import control_types from makani.gs.monitor2.apps.layout import indicator from makani.gs.monitor2.apps.layout import stoplights from makani.gs.monitor2.apps.plugins import common from makani.gs.monitor2.apps.plugins.indicators import avionics from makani.lib.python import c_helpers from makani.lib.python import struct_tree import numpy _SERVO_WARNING_HELPER = c_helpers.EnumHelper('ServoWarning', servo_common) _SERVO_ERROR_HELPER = c_helpers.EnumHelper('ServoError', servo_common) _SERVO_STATUS_HELPER = c_helpers.EnumHelper('ServoStatus', servo_common) _SERVO_LABELS_HELPER = c_helpers.EnumHelper('ServoLabel', aio_labels, prefix='kServo') _SERVO_ANALOG_VOLTAGE_HELPER = c_helpers.EnumHelper('ServoAnalogVoltage', servo_types) _SERVO_MON_WARNING_HELPER = c_helpers.EnumHelper('ServoMonitorWarning', servo_types) _SERVO_MON_ERROR_HELPER = c_helpers.EnumHelper('ServoMonitorError', servo_types)