Tools: autotest: Web: remove tools (now here : https://github.com/ArduPilot/WebTools)

This commit is contained in:
Iampete1 2023-05-22 02:09:54 +01:00 committed by Andrew Tridgell
parent f77635a5a6
commit 4d72a86032
9 changed files with 0 additions and 4353 deletions

View File

@ -1,154 +0,0 @@
/*
Translated from Tools/scripts/decode_devid.py
*/
const DEVICE_TYPE_COMPASS = 0
const DEVICE_TYPE_IMU = 1
const DEVICE_TYPE_BARO = 2
const DEVICE_TYPE_AIRSPEED = 3
function decode_devid(ID, type) {
const bus_type = ID & 0x07
const bus = (ID>>3) & 0x1F
const address = (ID>>8) & 0xFF
const devtype = (ID>>16)
bustypes = {
1: "I2C",
2: "SPI",
3: "DRONECAN",
4: "SITL",
5: "MSP",
6: "SERIAL",
}
compass_types = {
0x01 : "HMC5883_OLD",
0x07 : "HMC5883",
0x02 : "LSM303D",
0x04 : "AK8963 ",
0x05 : "BMM150 ",
0x06 : "LSM9DS1",
0x08 : "LIS3MDL",
0x09 : "AK09916",
0x0A : "IST8310",
0x0B : "ICM20948",
0x0C : "MMC3416",
0x0D : "QMC5883L",
0x0E : "MAG3110",
0x0F : "SITL",
0x10 : "IST8308",
0x11 : "RM3100_OLD",
0x12 : "RM3100",
0x13 : "MMC5883",
0x14 : "AK09918",
}
imu_types = {
0x09 : "BMI160",
0x10 : "L3G4200D",
0x11 : "ACC_LSM303D",
0x12 : "ACC_BMA180",
0x13 : "ACC_MPU6000",
0x16 : "ACC_MPU9250",
0x17 : "ACC_IIS328DQ",
0x21 : "GYR_MPU6000",
0x22 : "GYR_L3GD20",
0x24 : "GYR_MPU9250",
0x25 : "GYR_I3G4250D",
0x26 : "GYR_LSM9DS1",
0x27 : "ICM20789",
0x28 : "ICM20689",
0x29 : "BMI055",
0x2A : "SITL",
0x2B : "BMI088",
0x2C : "ICM20948",
0x2D : "ICM20648",
0x2E : "ICM20649",
0x2F : "ICM20602",
0x30 : "ICM20601",
0x31 : "ADIS1647x",
0x32 : "SERIAL",
0x33 : "ICM40609",
0x34 : "ICM42688",
0x35 : "ICM42605",
0x36 : "ICM40605",
0x37 : "IIM42652",
0x38 : "BMI270",
0x39 : "BMI085",
0x3A : "ICM42670",
}
baro_types = {
0x01 : "SITL",
0x02 : "BMP085",
0x03 : "BMP280",
0x04 : "BMP388",
0x05 : "DPS280",
0x06 : "DPS310",
0x07 : "FBM320",
0x08 : "ICM20789",
0x09 : "KELLERLD",
0x0A : "LPS2XH",
0x0B : "MS5611",
0x0C : "SPL06",
0x0D : "DRONECAN",
0x0E : "MSP",
0x0F : "ICP101XX",
0x10 : "ICP201XX",
0x11 : "MS5607",
0x12 : "MS5837",
0x13 : "MS5637",
0x14 : "BMP390",
}
airspeed_types = {
0x01 : "SITL",
0x02 : "MS4525",
0x03 : "MS5525",
0x04 : "DLVR",
0x05 : "MSP",
0x06 : "SDP3X",
0x07 : "DRONECAN",
0x08 : "ANALOG",
0x09 : "NMEA",
0x0A : "ASP5033",
}
function get(lookup, index) {
if (lookup[index] != null) {
return lookup[index]
}
return "Unknown"
}
var name
switch (type) {
case DEVICE_TYPE_COMPASS:
name = "Compass: " + get(compass_types, devtype)
break
case DEVICE_TYPE_IMU:
name = "IMU: " + get(imu_types, devtype)
break
case DEVICE_TYPE_BARO:
name = "Baro: " + get(baro_types, devtype)
break
case DEVICE_TYPE_AIRSPEED:
name = "Airspeed: " + get(airspeed_types, devtype)
break
default:
console.error("Unknown type");
return
}
const bus_type_string = get(bustypes, bus_type)
if (bus_type == 3) {
// dronecan devtype represents sensor_id
return { bus_type: bus_type_string, bus: bus, address: address, sensor_id: devtype-1, name: name }
}
return { bus_type: bus_type_string, bus: bus, address: address, devtype: devtype, name: name }
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
Test with `python -m http.server --bind 127.0.0.1`
Uses log parser from https://github.com/Williangalvani/JsDataflashParser
WIP!

View File

@ -1,354 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>ArduPilot Filter Review Tool</title>
<script src='https://cdn.plot.ly/plotly-2.20.0.min.js'></script>
<script src="https://unpkg.com/mathjs/lib/browser/math.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"> </script>
<script type="text/javascript" src="parser.js"></script>
<script type="text/javascript" src="DecodeDevID.js"></script>
</head>
<a href="https://ardupilot.org"><img src="logo.png"></a>
<h1>ArduPilot Filter Review Tool</h1>
<body>
<style>
div.plotly-notifier {
visibility: hidden;
}
label.parameter_input_label {
display: inline-block;
width: 160px;
text-align: right;
margin:5px 0px;
}
</style>
<table>
<tr>
<td style="width: 30px;"></td>
<td>
<fieldset style="width:1100px">
<legend>Setup</legend>
<table>
<tr>
<td>
<fieldset style="width:150px;height:80px">
<legend>Amplitude scale</legend>
<input type="radio" id="ScaleLog" name="Scale" checked>
<label for="LogScale">dB</label><br>
<input type="radio" id="ScaleLinear" name="Scale">
<label for="LinearScale">Linear</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:200px;height:80px">
<legend>Spectrum scale</legend>
<input type="radio" id="SpectrumLinear" name="SpectrumScale" checked>
<label for="SpectrumLinear">Linear</label><br>
<input type="radio" id="SpectrumPSD" name="SpectrumScale">
<label for="SpectrumPSD">Power Spectral Density</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:150px;height:80px">
<legend>Frequency scale</legend>
<table>
<tr>
<td>
<input type="radio" id="freq_ScaleLinear" name="feq_scale" checked>
<label for="LinearScale">Linear</label><br>
<input type="radio" id="freq_ScaleLog" name="feq_scale">
<label for="LogScale">Log</label><br>
</td>
<td>
<input type="radio" id="freq_Scale_Hz" name="feq_unit" checked>
<label for="Scale_unit_Hz">Hz</label><br>
<input type="radio" id="freq_Scale_RPM" name="feq_unit">
<label for="Scale_unit_RPM">RPM</label><br>
</td>
</tr>
</table>
</fieldset>
</td>
<td>
<fieldset style="width:200px;height:80px">
<legend>FFT Settings</legend>
<label for="FFTWindow">Windows per batch</label>
<input id="FFTWindow" name="FFTWindow" type="number" min="1" step="1" value="1" onchange="clear_calculation()" style="width:50px"/>
<br><br>
<label id="FFTWindowInfo"></label>
</fieldset>
</td>
<td>
<fieldset style="width:200px;height:80px">
<legend>Analysis time</legend>
<label for="TimeStart">Start</label>
<input id="TimeStart" name="TimeStart" type="number" min="0" step="1" value="0" onchange="" style="width:50px" disabled/>
s<br><br>
<label for="TimeEnd">End</label>
<input id="TimeEnd" name="TimeEnd" type="number" min="0" step="1" value="0" onchange="" style="width:50px" disabled/>
s
</fieldset>
</td>
</tr>
</table>
<p>
Bin log:
<input id="fileItem" type="file" accept=".bin" onchange="readFile(this)">
<input type="button" id="calculate" value="Calculate" onclick="re_calc()" disabled>
</p>
</fieldset>
</td>
</tr>
</table>
<p>
<div id="FFTPlot" style="width:1200px;height:450px"></div>
</p>
<table>
<tr>
<td style="width: 60px;"></td>
<td>
<fieldset style="width:300px">
<legend>Gyro 1</legend>
<label id="Gyro0_info"><br></label>
<p>
<fieldset style="width:300px">
<legend>Pre-filter</legend>
<input type="checkbox" id="Gyro0PreX" name="Gyro0PreX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PreX">X</label>
<input type="checkbox" id="Gyro0PreY" name="Gyro0PreY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PreY">Y</label>
<input type="checkbox" id="Gyro0PreZ" name="Gyro0PreZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PreZ">Z</label>
</fieldset>
</p>
<p>
<fieldset style="width:300px">
<legend>Post-filter</legend>
<input type="checkbox" id="Gyro0PostX" name="Gyro0PostX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PostX">X</label>
<input type="checkbox" id="Gyro0PostY" name="Gyro0PostY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PostY">Y</label>
<input type="checkbox" id="Gyro0PostZ" name="Gyro0PostZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro0PostZ">Z</label>
</fieldset>
</p>
<label id="Gyro0_FFT_info"><br><br><br></label>
</fieldset>
</td>
<td>
<fieldset style="width:300px">
<legend>Gyro 2</legend>
<label id="Gyro1_info"><br></label>
<p>
<fieldset style="width:300px">
<legend>Pre-filter</legend>
<input type="checkbox" id="Gyro1PreX" name="Gyro1PreX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PreX">X</label>
<input type="checkbox" id="Gyro1PreY" name="Gyro1PreY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PreY">Y</label>
<input type="checkbox" id="Gyro1PreZ" name="Gyro1PreZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PreZ">Z</label>
</fieldset>
</p>
<p>
<fieldset style="width:300px">
<legend>Post-filter</legend>
<input type="checkbox" id="Gyro1PostX" name="Gyro1PostX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PostX">X</label>
<input type="checkbox" id="Gyro1PostY" name="Gyro1PostY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PostY">Y</label>
<input type="checkbox" id="Gyro1PostZ" name="Gyro1PostZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro1PostZ">Z</label>
</fieldset>
</p>
<label id="Gyro1_FFT_info"><br><br><br></label>
</fieldset>
</td>
<td>
<fieldset style="width:300px">
<legend>Gyro 3</legend>
<label id="Gyro2_info"><br></label>
<p>
<fieldset style="width:300px">
<legend>Pre-filter</legend>
<input type="checkbox" id="Gyro2PreX" name="Gyro2PreX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PreX">X</label>
<input type="checkbox" id="Gyro2PreY" name="Gyro2PreY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PreY">Y</label>
<input type="checkbox" id="Gyro2PreZ" name="Gyro2PreZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PreZ">Z</label>
</fieldset>
</p>
<p>
<fieldset style="width:300px">
<legend>Post-filter</legend>
<input type="checkbox" id="Gyro2PostX" name="Gyro2PostX" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PostX">X</label>
<input type="checkbox" id="Gyro2PostY" name="Gyro2PostY" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PostY">Y</label>
<input type="checkbox" id="Gyro2PostZ" name="Gyro2PostZ" onchange="update_hidden(this)" checked disabled>
<label for="Gyro2PostZ">Z</label>
</fieldset>
</p>
<label id="Gyro2_FFT_info"><br><br><br></label>
</fieldset>
</td>
</tr>
</table>
<p>
<div id="Spectrogram" style="width:1200px;height:450px"></div>
</p>
<table>
<tr>
<td style="width: 60px;"></td>
<td>
<fieldset style="width:300px;height:350px">
<legend>Spectrogram Options</legend>
<p>
<fieldset style="width:300px">
<legend>Gyro instance</legend>
<input type="radio" id="SpecGyroInst0" name="SpecGyroInst" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroInst0">1</label>
<input type="radio" id="SpecGyroInst1" name="SpecGyroInst" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroInst1">2</label>
<input type="radio" id="SpecGyroInst2" name="SpecGyroInst" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroInst2">3</label>
</fieldset>
</p>
<p>
<fieldset style="width:300px">
<legend>Filtering</legend>
<input type="radio" id="SpecGyroPre" name="SpecGyroPrePost" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroPre">Pre-filter</label>
<input type="radio" id="SpecGyroPost" name="SpecGyroPrePost" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroPost">Post-filter</label>
</fieldset>
</p>
<p>
<fieldset style="width:300px">
<legend>Axis</legend>
<input type="radio" id="SpecGyroAxisX" name="SpecGyroAxis" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroAxisX">X</label>
<input type="radio" id="SpecGyroAxisY" name="SpecGyroAxis" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroAxisY">Y</label>
<input type="radio" id="SpecGyroAxisZ" name="SpecGyroAxis" onchange="redraw_Spectrogram()" disabled>
<label for="SpecGyroAxisZ">Z</label>
</fieldset>
</p>
</fieldset>
</td>
<td>
<fieldset style="width:300px;height:350px">
<legend>First Notch Filter</legend>
<p>
<label class="parameter_input_label" for="INS_HNTCH_ENABLE">INS_HNTCH_ENABLE</label>
<input id="INS_HNTCH_ENABLE" name="INS_HNTCH_ENABLE" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_MODE">INS_HNTCH_MODE</label>
<input id="INS_HNTCH_MODE" name="INS_HNTCH_MODE" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_FREQ">INS_HNTCH_FREQ</label>
<input id="INS_HNTCH_FREQ" name="INS_HNTCH_FREQ" type="number" step="0.1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_REF">INS_HNTCH_REF</label>
<input id="INS_HNTCH_REF" name="INS_HNTCH_REF" type="number" step="0.01" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_FM_RAT">INS_HNTCH_FM_RAT</label>
<input id="INS_HNTCH_FM_RAT" name="INS_HNTCH_FM_RAT" type="number" step="0.01" value="1.0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_HMNCS">INS_HNTCH_HMNCS</label>
<input id="INS_HNTCH_HMNCS" name="INS_HNTCH_HMNCS" type="number" step="1" value="1" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTCH_OPTS">INS_HNTCH_OPTS</label>
<input id="INS_HNTCH_OPTS" name="INS_HNTCH_OPTS" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
</fieldset>
</td>
<td>
<fieldset style="width:300px;height:350px">
<legend>Second Notch Filter</legend>
<p>
<label class="parameter_input_label" for="INS_HNTC2_ENABLE">INS_HNTC2_ENABLE</label>
<input id="INS_HNTC2_ENABLE" name="INS_HNTC2_ENABLE" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_MODE">INS_HNTC2_MODE</label>
<input id="INS_HNTC2_MODE" name="INS_HNTC2_MODE" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_FREQ">INS_HNTC2_FREQ</label>
<input id="INS_HNTC2_FREQ" name="INS_HNTC2_FREQ" type="number" step="0.1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_REF">INS_HNTC2_REF</label>
<input id="INS_HNTC2_REF" name="INS_HNTC2_REF" type="number" step="0.01" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_FM_RAT">INS_HNTC2_FM_RAT</label>
<input id="INS_HNTC2_FM_RAT" name="INS_HNTC2_FM_RAT" type="number" step="0.01" value="1.0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_HMNCS">INS_HNTC2_HMNCS</label>
<input id="INS_HNTC2_HMNCS" name="INS_HNTC2_HMNCS" type="number" step="1" value="1" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
<p>
<label class="parameter_input_label" for="INS_HNTC2_OPTS">INS_HNTC2_OPTS</label>
<input id="INS_HNTC2_OPTS" name="INS_HNTC2_OPTS" type="number" step="1" value="0" onchange="HNotch_param_read(); redraw_Spectrogram();" style="width: 100px" disabled/>
</p>
</fieldset>
</td>
</tr>
</table>
</body>
<script type="text/javascript" src="FilterReview.js"></script>
<script>
function readFile(e) {
const file = e.files[0]
if (file == null) {
return
}
let reader = new FileReader()
reader.onload = function (e) {
load(reader.result)
}
reader.readAsArrayBuffer(file)
}
</script>

View File

@ -1,882 +0,0 @@
const MAV_TYPE_GENERIC = 0 // Generic micro air vehicle.
const MAV_TYPE_FIXED_WING = 1 // Fixed wing aircraft.
const MAV_TYPE_QUADROTOR = 2 // Quadrotor
const MAV_TYPE_COAXIAL = 3 // Coaxial helicopter
const MAV_TYPE_HELICOPTER = 4 // Normal helicopter with tail rotor.
const MAV_TYPE_ANTENNA_TRACKER = 5 // Ground installation
const MAV_TYPE_GCS = 6 // Operator control unit / ground control station
const MAV_TYPE_AIRSHIP = 7 // Airship, controlled
const MAV_TYPE_FREE_BALLOON = 8 // Free balloon, uncontrolled
const MAV_TYPE_ROCKET = 9 // Rocket
const MAV_TYPE_GROUND_ROVER = 10 // Ground rover
const MAV_TYPE_SURFACE_BOAT = 11 // Surface vessel, boat, ship
const MAV_TYPE_SUBMARINE = 12 // Submarine
const MAV_TYPE_HEXAROTOR = 13 // Hexarotor
const MAV_TYPE_OCTOROTOR = 14 // Octorotor
const MAV_TYPE_TRICOPTER = 15 // Tricopter
const MAV_TYPE_FLAPPING_WING = 16 // Flapping wing
const MAV_TYPE_KITE = 17 // Kite
const MAV_TYPE_ONBOARD_CONTROLLER = 18 // Onboard companion controller
const MAV_TYPE_VTOL_DUOROTOR = 19 // Two-rotor VTOL using control surfaces in vertical operation in addition. Tailsitter.
const MAV_TYPE_VTOL_QUADROTOR = 20 // Quad-rotor VTOL using a V-shaped quad config in vertical operation.
// Tailsitter.
const MAV_TYPE_VTOL_TILTROTOR = 21 // Tiltrotor VTOL
const MAV_TYPE_VTOL_RESERVED2 = 22 // VTOL reserved 2
const MAV_TYPE_VTOL_RESERVED3 = 23 // VTOL reserved 3
const MAV_TYPE_VTOL_RESERVED4 = 24 // VTOL reserved 4
const MAV_TYPE_VTOL_RESERVED5 = 25 // VTOL reserved 5
const MAV_TYPE_GIMBAL = 26 // Onboard gimbal
const MAV_TYPE_ADSB = 27 // Onboard ADSB peripheral
const MAV_TYPE_PARAFOIL = 28 // Steerable, nonrigid airfoil
const MAV_TYPE_DODECAROTOR = 29 // Dodecarotor
const MAV_TYPE_CAMERA = 30 // Camera
const MAV_TYPE_CHARGING_STATION = 31 // Charging station
const MAV_TYPE_FLARM = 32 // Onboard FLARM collision avoidance system
const MAV_TYPE_ENUM_END = 33 //
const modeMappingApm = {
0: 'MANUAL',
1: 'CIRCLE',
2: 'STABILIZE',
3: 'TRAINING',
4: 'ACRO',
5: 'FBWA',
6: 'FBWB',
7: 'CRUISE',
8: 'AUTOTUNE',
10: 'AUTO',
11: 'RTL',
12: 'LOITER',
13: 'TAKEOFF',
14: 'AVOID_ADSB',
15: 'GUIDED',
16: 'INITIALISING',
17: 'QSTABILIZE',
18: 'QHOVER',
19: 'QLOITER',
20: 'QLAND',
21: 'QRTL',
22: 'QAUTOTUNE',
23: 'QACRO',
24: 'THERMAL'
}
const modeMappingAcm = {
0: 'STABILIZE',
1: 'ACRO',
2: 'ALT_HOLD',
3: 'AUTO',
4: 'GUIDED',
5: 'LOITER',
6: 'RTL',
7: 'CIRCLE',
9: 'LAND',
11: 'DRIFT',
13: 'SPORT',
14: 'FLIP',
15: 'AUTOTUNE',
16: 'POSHOLD',
17: 'BRAKE',
18: 'THROW',
19: 'AVOID_ADSB',
20: 'GUIDED_NOGPS',
21: 'SMART_RTL',
22: 'FLOWHOLD',
23: 'FOLLOW',
24: 'ZIGZAG',
25: 'SYSTEMID',
26: 'AUTOROTATE'
}
const modeMappingRover = {
0: 'MANUAL',
1: 'ACRO',
3: 'STEERING',
4: 'HOLD',
5: 'LOITER',
6: 'FOLLOW',
7: 'SIMPLE',
10: 'AUTO',
11: 'RTL',
12: 'SMART_RTL',
15: 'GUIDED',
16: 'INITIALISING'
}
const modeMappingTracker = {
0: 'MANUAL',
1: 'STOP',
2: 'SCAN',
3: 'SERVO_TEST',
10: 'AUTO',
16: 'INITIALISING'
}
const modeMappingSub = {
0: 'STABILIZE',
1: 'ACRO',
2: 'ALT_HOLD',
3: 'AUTO',
4: 'GUIDED',
7: 'CIRCLE',
9: 'SURFACE',
16: 'POSHOLD',
19: 'MANUAL',
20: 'MOTOR_DETECT'
}
const multipliers = {
'-': 0, // no multiplier e.g. a string
'?': 1, // multipliers which haven't been worked out yet....
// <leave a gap here, just in case....>
'2': 1e2,
'1': 1e1,
'0': 1e0,
'A': 1e-1,
'B': 1e-2,
'C': 1e-3,
'D': 1e-4,
'E': 1e-5,
'F': 1e-6,
'G': 1e-7,
// <leave a gap here, just in case....>
'!': 3.6, // (ampere*second => milliampere*hour) and (km/h => m/s)
'/': 3600 // (ampere*second => ampere*hour)
}
const multipliersTable = {
0.000001: 'n',
1000: 'M',
0.001: 'm'
}
const HEAD1 = 163
const HEAD2 = 149
const units = {
'-': '', // no units e.g. Pi, or a string
'?': 'UNKNOWN', // Units which haven't been worked out yet....
'A': 'A', // Ampere
'd': '°', // of the angular variety, -180 to 180
'b': 'B', // bytes
'k': '°/s', // degrees per second. Degrees are NOT SI, but is some situations more user-friendly than radians
'D': '°', // degrees of latitude
'e': '°/s/s', // degrees per second per second. Degrees are NOT SI, but is some situations more user-friendly
'E': 'rad/s', // radians per second
'G': 'Gauss', // Gauss is not an SI unit, but 1 tesla = 10000 gauss so a simple replacement is not possible here
'h': '°', // 0.? to 359.?
'i': 'A.s', // Ampere second
'J': 'W.s', // Joule (Watt second)
// { 'l', "l" }, // litres
'L': 'rad/s/s', // radians per second per second
'm': 'm', // metres
'n': 'm/s', // metres per second
// { 'N', "N" }, // Newton
'o': 'm/s/s', // metres per second per second
'O': '°C', // degrees Celsius. Not SI, but Kelvin is too cumbersome for most users
'%': '%', // percent
'S': 'satellites', // number of satellites
's': 's', // seconds
'q': 'rpm', // rounds per minute. Not SI, but sometimes more intuitive than Hertz
'r': 'rad', // radians
'U': '°', // degrees of longitude
'u': 'ppm', // pulses per minute
'v': 'V', // Volt
'P': 'Pa', // Pascal
'w': 'Ohm', // Ohm
'Y': 'us', // pulse width modulation in microseconds
'z': 'Hz', // Hertz
'#': 'instance' // instance number for message
}
function getModeMap (mavType) {
let map
if ([MAV_TYPE_QUADROTOR,
MAV_TYPE_HELICOPTER,
MAV_TYPE_HEXAROTOR,
MAV_TYPE_OCTOROTOR,
MAV_TYPE_COAXIAL,
MAV_TYPE_TRICOPTER].includes(mavType)) {
map = modeMappingAcm
}
if (mavType === MAV_TYPE_FIXED_WING) {
map = modeMappingApm
}
if (mavType === MAV_TYPE_GROUND_ROVER) {
map = modeMappingRover
}
if (mavType === MAV_TYPE_ANTENNA_TRACKER) {
map = modeMappingTracker
}
if (mavType === MAV_TYPE_SUBMARINE) {
map = modeMappingSub
}
if (map == null) {
return null
}
return map
}
function assignColumn (obj) {
var ArrayOfString = obj.split(',')
return ArrayOfString
}
// Converts from degrees to radians.
Math.radians = function (degrees) {
return degrees * Math.PI / 180
}
// Converts from radians to degrees.
Math.degrees = function (radians) {
return radians * 180 / Math.PI
}
class DataflashParser {
constructor () {
this.time = null
this.timebase = null
this.buffer = null
this.data = null
this.FMT = []
this.FMT[128] = {
'Type': '128',
'length': '89',
'Name': 'FMT',
'Format': 'BBnNZ',
'Columns': 'Type,Length,Name,Format,Columns'
}
this.offset = 0
this.msgType = []
this.offsetArray = []
this.totalSize = null
this.messages = {}
this.lastPercentage = 0
this.sent = false
this.maxPercentageInterval = 0.05
this.messageTypes = {}
this.alreadyParsed = []
}
FORMAT_TO_STRUCT (obj) {
var temp
var dict = {
name: obj.Name,
fieldnames: obj.Columns.split(',')
}
let column = assignColumn(obj.Columns)
let low
let n
for (let i = 0; i < obj.Format.length; i++) {
temp = obj.Format.charAt(i)
switch (temp) {
case 'a': // int16_t[32]
dict[column[i]] = []
for (let j = 0; j < 32; j++) {
dict[column[i]][j] = this.data.getInt16(this.offset, true)
this.offset += 2
}
break
case 'b':
dict[column[i]] = this.data.getInt8(this.offset)
this.offset += 1
break
case 'B':
dict[column[i]] = this.data.getUint8(this.offset)
this.offset += 1
break
case 'h':
dict[column[i]] = this.data.getInt16(this.offset, true)
this.offset += 2
break
case 'H':
dict[column[i]] = this.data.getUint16(this.offset, true)
this.offset += 2
break
case 'i':
dict[column[i]] = this.data.getInt32(this.offset, true)
this.offset += 4
break
case 'I':
dict[column[i]] = this.data.getUint32(this.offset, true)
this.offset += 4
break
case 'f':
dict[column[i]] = this.data.getFloat32(this.offset, true)
this.offset += 4
break
case 'd':
dict[column[i]] = this.data.getFloat64(this.offset, true)
this.offset += 8
break
case 'Q':
low = this.data.getUint32(this.offset, true)
this.offset += 4
n = this.data.getUint32(this.offset, true) * 4294967296.0 + low
if (low < 0) n += 4294967296
dict[column[i]] = n
this.offset += 4
break
case 'q':
low = this.data.getInt32(this.offset, true)
this.offset += 4
n = this.data.getInt32(this.offset, true) * 4294967296.0 + low
if (low < 0) n += 4294967296
dict[column[i]] = n
this.offset += 4
break
case 'n':
// TODO: fix these regex and unsilent linter
// eslint-disable-next-line
dict[column[i]] = String.fromCharCode.apply(null, new Uint8Array(this.buffer, this.offset, 4)).replace(/\x00+$/g, '')
this.offset += 4
break
case 'N':
// eslint-disable-next-line
dict[column[i]] = String.fromCharCode.apply(null, new Uint8Array(this.buffer, this.offset, 16)).replace(/\x00+$/g, '')
this.offset += 16
break
case 'Z':
// eslint-disable-next-line
dict[column[i]] = String.fromCharCode.apply(null, new Uint8Array(this.buffer, this.offset, 64)).replace(/\x00+$/g, '')
this.offset += 64
break
case 'c':
// this.this.data.setInt16(offset,true);
dict[column[i]] = this.data.getInt16(this.offset, true) / 100
this.offset += 2
break
case 'C':
// this.data.setUint16(offset,true);
dict[column[i]] = this.data.getUint16(this.offset, true) / 100
this.offset += 2
break
case 'E':
// this.data.setUint32(offset,true);
dict[column[i]] = this.data.getUint32(this.offset, true) / 100
this.offset += 4
break
case 'e':
// this.data.setInt32(offset,true);
dict[column[i]] = this.data.getInt32(this.offset, true) / 100
this.offset += 4
break
case 'L':
// this.data.setInt32(offset,true);
dict[column[i]] = this.data.getInt32(this.offset, true)
this.offset += 4
break
case 'M':
// this.data.setInt32(offset,true);
dict[column[i]] = this.data.getUint8(this.offset)
this.offset += 1
break
}
}
return dict
}
gpstimetoTime (week, msec) {
let epoch = 86400 * (10 * 365 + (1980 - 1969) / 4 + 1 + 6 - 2)
return epoch + 86400 * 7 * week + msec * 0.001 - 15
}
setTimeBase (base) {
this.timebase = base
}
findTimeBase (gps) {
const temp = this.gpstimetoTime(parseInt(gps['GWk']), parseInt(gps['GMS']))
this.setTimeBase(parseInt(temp - gps['TimeUS'] * 0.000001))
}
getMsgType (element) {
for (let i = 0; i < this.FMT.length; i++) {
if (this.FMT[i] != null) {
// eslint-disable-next-line
if (this.FMT[i].Name == element) {
return i
}
}
}
}
onMessage (message) {
if (this.totalSize == null) { // for percentage calculation
this.totalSize = this.buffer.byteLength
}
if (message.name in this.messages) {
this.messages[message.name].push(this.fixData(message))
} else {
this.messages[message.name] = [this.fixData(message)]
}
let percentage = 100 * this.offset / this.totalSize
if ((percentage - this.lastPercentage) > this.maxPercentageInterval) {
self.postMessage({percentage: percentage})
this.lastPercentage = percentage
}
}
messageHasInstances (name) {
let type = this.FMT.find(msg => msg !== undefined && msg.Name === name)
return type !== undefined && type.units !== undefined && type.units.includes('instance')
}
getInstancesFieldName (name) {
let type = this.FMT.find(msg => msg !== undefined && msg.Name === name)
if (type.units === undefined) {
return null
}
return type.Columns.split(',')[type.units.indexOf('instance')]
}
// Next three functions are used for transfering data on postmessage, instead of cloning
isTypedArray (arr) {
return ArrayBuffer.isView(arr) && !(arr instanceof DataView)
}
getType (arr) {
return this.isTypedArray(arr) && arr.constructor.name
}
postData (data) {
data['dataType'] = {}
const transferables = []
for (let field of Object.keys(data.messageList)) {
const arrayType = this.getType(data.messageList[field])
if (arrayType) {
transferables.push(data.messageList[field].buffer)
}
// Apparently it is magically decoded on the other end, no need for metadata
// data['dataType'][field] = arrayType
}
self.postMessage(data, transferables)
}
parseAtOffset (name) {
let type = this.getMsgType(name)
var parsed = []
for (var i = 0; i < this.msgType.length; i++) {
if (type === this.msgType[i]) {
this.offset = this.offsetArray[i]
try {
let temp = this.FORMAT_TO_STRUCT(this.FMT[this.msgType[i]])
if (temp['name'] != null) {
parsed.push(this.fixData(temp))
}
} catch (e) {
console.log('reached log end?')
console.log(e)
}
}
if (i % 100000 === 0) {
let perc = 100 * i / this.msgType.length
self.postMessage({percentage: perc})
}
}
delete this.messages[name]
this.messages[name] = parsed
self.postMessage({percentage: 100})
console.log(name, this.messageHasInstances(name) ? 'has instances' : 'has no instances')
if (parsed.length && this.messageHasInstances(name)) {
let instanceField = this.getInstancesFieldName(name)
let instances = {}
for (let msg of parsed) {
try {
instances[msg[instanceField]].push(msg)
} catch (e) {
instances[msg[instanceField]] = [ msg ]
}
}
if (Object.keys(instances).length === 1) {
this.fixDataOnce(name)
this.simplifyData(name)
this.postData({messageType: name, messageList: this.messages[name]})
return parsed
}
for (let [index, messages] of Object.entries(instances)) {
let newName = name + '[' + index + ']'
this.messages[newName] = messages
this.fixDataOnce(newName)
this.simplifyData(newName)
this.postData({messageType: newName,
messageList: this.messages[newName]})
}
} else if (parsed.length) {
this.fixDataOnce(name)
this.simplifyData(name)
this.postData({messageType: name, messageList: this.messages[name]})
}
this.alreadyParsed.push(name)
return parsed
}
checkNumberOfInstances (name) {
// Similar to parseOffset, but finishes earlier and updates messageTypes
let type = this.getMsgType(name)
let availableInstances = []
let instanceField = this.getInstancesFieldName(name)
if (instanceField === null) {
return [1]
}
let repeats = 0
for (var i = 0; i < this.msgType.length; i++) {
if (type === this.msgType[i]) {
this.offset = this.offsetArray[i]
try {
let temp = this.FORMAT_TO_STRUCT(this.FMT[this.msgType[i]])
if (temp['name'] != null) {
let msg = temp
if (!msg.hasOwnProperty(instanceField)) {
break
}
// we do an early return after we get 20 repeated instance numbers. should we?
const instance = msg[instanceField]
if (availableInstances.includes(instance)) {
repeats += 1
if (repeats > 20) {
return availableInstances
}
} else {
availableInstances.push(instance)
}
}
} catch (e) {
console.log(e)
}
}
}
return availableInstances
}
timestamp (TimeUs) {
let temp = this.timebase + TimeUs * 0.000001
if (temp > 0) {
TimeUs = temp
}
let date = new Date(TimeUs * 1000)
let hours = date.getHours()
let minutes = '0' + date.getMinutes()
let seconds = '0' + date.getSeconds()
let formattedTime = hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2)
date = date.toString()
let time = date.split(' ')
if (time[0] !== 'Invalid') {
this.time = time[0] + ' ' + time[1] + ' ' + time[2] + ' ' + time[3]
}
return formattedTime
}
DfReader () {
let lastOffset = 0
while (this.offset < (this.buffer.byteLength - 3)) {
if (this.data.getUint8(this.offset) !== HEAD1 || this.data.getUint8(this.offset + 1) !== HEAD2) {
this.offset += 1
continue
}
this.offset += 2
let attribute = this.data.getUint8(this.offset)
if (this.FMT[attribute] != null) {
this.offset += 1
this.offsetArray.push(this.offset)
this.msgType.push(attribute)
try {
var value = this.FORMAT_TO_STRUCT(this.FMT[attribute])
if (this.FMT[attribute].Name === 'GPS') {
this.findTimeBase(value)
}
} catch (e) {
// console.log('reached log end?')
// console.log(e)
this.offset += 1
}
if (attribute === 128) {
this.FMT[value['Type']] = {
'Type': value['Type'],
'length': value['Length'],
'Name': value['Name'],
'Format': value['Format'],
'Columns': value['Columns']
}
}
// this.onMessage(value)
} else {
this.offset += 1
}
if (this.offset - lastOffset > 50000) {
let perc = 100 * this.offset / this.buffer.byteLength
self.postMessage({percentage: perc})
lastOffset = this.offset
}
}
self.postMessage({percentage: 100})
self.postMessage({messages: this.messages})
this.sent = true
}
getModeString (cmode) {
let mavtype
let msgs = this.messages['MSG']
for (let i in msgs.Message) {
if (msgs.Message[i].toLowerCase().includes('arduplane')) {
mavtype = MAV_TYPE_FIXED_WING
return getModeMap(mavtype)[cmode]
} else if (msgs.Message[i].toLowerCase().includes('arducopter')) {
mavtype = MAV_TYPE_QUADROTOR
return getModeMap(mavtype)[cmode]
} else if (msgs.Message[i].toLowerCase().includes('ardusub')) {
mavtype = MAV_TYPE_SUBMARINE
return getModeMap(mavtype)[cmode]
} else if (msgs.Message[i].toLowerCase().includes('rover')) {
mavtype = MAV_TYPE_GROUND_ROVER
return getModeMap(mavtype)[cmode]
} else if (msgs.Message[i].toLowerCase().includes('tracker')) {
mavtype = MAV_TYPE_ANTENNA_TRACKER
return getModeMap(mavtype)[cmode]
}
}
console.log('defaulting to quadcopter')
return getModeMap(MAV_TYPE_QUADROTOR)[cmode]
}
fixData (message) {
if (message.name === 'MODE') {
message.asText = this.getModeString(message['Mode'])
}
// eslint-disable-next-line
message.time_boot_ms = message.TimeUS / 1000
delete message.TimeUS
delete message.fieldnames
return message
}
fixDataOnce (name) {
if (!['GPS', 'ATT', 'AHR2', 'MODE'].includes(name)) {
if (this.messageTypes.hasOwnProperty(name)) {
let fields = this.messages[name][0].fieldnames
if (this.messageTypes[name].hasOwnProperty('multipliers')) {
for (let message in this.messages[name]) {
for (let i = 1; i < fields.length; i++) {
let fieldname = fields[i]
if (!isNaN(this.messageTypes[name].multipliers[i])) {
this.messages[name][message][fieldname] *= this.messageTypes[name].multipliers[i]
}
}
}
}
}
}
}
concatTypedArrays (a, b) { // a, b TypedArray of same type
var c = new (a.constructor)(a.length + b.length)
c.set(a, 0)
c.set(b, a.length)
return c
}
createUint8ArrayFromString (str) {
const array = new Uint8Array(str.length)
for (let i = 0, strLen = str.length; i < strLen; i++) {
array[i] = str.charCodeAt(i)
}
return array
}
processFiles () {
this.files = {}
for (let msg of this.messages['FILE']) {
if (!this.files.hasOwnProperty(msg.FileName)) {
this.files[msg.FileName] = this.createUint8ArrayFromString(msg.Data)
} else {
this.files[msg.FileName] = this.concatTypedArrays(
this.files[msg.FileName], this.createUint8ArrayFromString(msg.Data)
)
}
}
self.postMessage({files: this.files})
}
simplifyData (name) {
if (name === 'MODE') {
this.messageTypes[name].expressions.push('asText')
}
if (name === 'FILE') {
return
}
if (!['FMTU'].includes(name)) {
if (this.messageTypes.hasOwnProperty(name)) {
let fields = this.messageTypes[name].expressions
if (!fields.includes('time_boot_ms')) {
fields.push('time_boot_ms')
}
let mergedData = {}
for (let field of fields) {
mergedData[field] = []
}
for (let message of this.messages[name]) {
for (let i = 1; i < fields.length; i++) {
let fieldname = fields[i]
mergedData[fieldname].push(message[fieldname])
}
}
delete this.messages[name]
this.messages[name] = mergedData
for (const field of this.messageTypes[name].expressions) {
if (this.messages[name][field] && !isNaN(this.messages[name][field][0])) {
const newData = new Float64Array(this.messages[name][field])
delete this.messages[name][field]
this.messages[name][field] = newData
}
}
}
}
}
populateUnits () {
// console.log(this.messages['FMTU'])
for (let msg of this.messages['FMTU']) {
this.FMT[msg.FmtType]['units'] = []
for (let unit of msg.UnitIds) {
this.FMT[msg.FmtType]['units'].push(units[unit])
}
this.FMT[msg.FmtType]['multipliers'] = []
for (let mult of msg.MultIds) {
this.FMT[msg.FmtType]['multipliers'].push(multipliers[mult])
}
}
}
extractStartTime () {
let msgs = this.messages['GPS']
for (let i in msgs.time_boot_ms) {
if (msgs.GWk[i] > 1000) { // lousy validation
let weeks = msgs.GWk[i]
let ms = msgs.GMS[i]
let d = new Date((315964800.0 + ((60 * 60 * 24 * 7) * weeks) + ms / 1000.0) * 1000.0)
// adjusting for leap seconds
d = new Date(d.getTime() - this.leapSecondsGPS(d.getUTCFullYear(), d.getUTCMonth() + 1) * 1000)
return d
}
}
}
leapSecondsGPS (year, month) {
return this.leapSecondsTAI(year, month) - 19
}
leapSecondsTAI (year, month) {
const yyyymm = year * 100 + month
if (yyyymm >= 201701) return 37
if (yyyymm >= 201507) return 36
if (yyyymm >= 201207) return 35
if (yyyymm >= 200901) return 34
if (yyyymm >= 200601) return 33
if (yyyymm >= 199901) return 32
if (yyyymm >= 199707) return 31
if (yyyymm >= 199601) return 30
return 0
}
processData (data) {
this.buffer = data
this.data = new DataView(this.buffer)
this.DfReader()
let messageTypes = {}
this.parseAtOffset('FMTU')
this.populateUnits()
let typeSet = new Set(this.msgType)
for (let msg of this.FMT) {
if (msg) {
if (typeSet.has(msg.Type)) {
let fields = msg.Columns.split(',')
// expressions = expressions.filter(e => e !== 'TimeUS')
let complexFields = {}
if (msg.hasOwnProperty('units')) {
for (let field in fields) {
complexFields[fields[field]] = {
name: fields[field],
units: (multipliersTable[msg.multipliers[field]] || '') + msg.units[field],
multiplier: msg.multipliers[field]
}
}
} else {
for (let field in fields) {
complexFields[fields[field]] = {
name: fields[field],
units: '?',
multiplier: 1
}
}
}
let availableInstances = this.checkNumberOfInstances(msg.Name)
if (availableInstances.length > 1) {
for (let instance of availableInstances) {
messageTypes[msg.Name + '[' + instance + ']'] = {
expressions: fields,
units: msg.units,
multipiers: msg.multipliers,
complexFields: complexFields
}
}
} else {
messageTypes[msg.Name] = {
expressions: fields,
units: msg.units,
multipiers: msg.multipliers,
complexFields: complexFields
}
}
}
}
}
self.postMessage({availableMessages: messageTypes})
this.messageTypes = messageTypes
this.parseAtOffset('CMD')
this.parseAtOffset('MSG')
this.parseAtOffset('FILE')
this.processFiles()
this.parseAtOffset('MODE')
this.parseAtOffset('AHR2')
this.parseAtOffset('ATT')
this.parseAtOffset('GPS')
this.parseAtOffset('POS')
this.parseAtOffset('XKQ1')
this.parseAtOffset('XKQ')
this.parseAtOffset('NKQ1')
this.parseAtOffset('NKQ2')
this.parseAtOffset('XKQ2')
this.parseAtOffset('PARM')
this.parseAtOffset('MSG')
this.parseAtOffset('STAT')
this.parseAtOffset('EV')
let metadata = {
startTime: this.extractStartTime()
}
self.postMessage({metadata: metadata})
self.postMessage({messagesDoneLoading: true})
return {types: this.messageTypes, messages: this.messages}
}
loadType (type) {
this.parseAtOffset(type)
console.log('done')
}
}
self.addEventListener('message', function (event) {
if (event.data === null) {
console.log('got bad file message!')
} else if (event.data.action === 'parse') {
parser = new DataflashParser()
let data = event.data.file
parser.processData(data)
} else if (event.data.action === 'loadType') {
parser.loadType(event.data.type.split('[')[0])
} else if (event.data.action === 'trimFile') {
parser.trimFile(event.data.time)
}
})

View File

@ -1,171 +0,0 @@
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window
? window : typeof self === 'object' && self.self === self
? self : typeof global === 'object' && global.global === global
? global
: this
function bom (blob, opts) {
if (typeof opts === 'undefined') opts = { autoBom: false }
else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object')
opts = { autoBom: !opts }
}
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
}
return blob
}
function download (url, name, opts) {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'blob'
xhr.onload = function () {
saveAs(xhr.response, name, opts)
}
xhr.onerror = function () {
console.error('could not download file')
}
xhr.send()
}
function corsEnabled (url) {
var xhr = new XMLHttpRequest()
// use sync to avoid popup blocker
xhr.open('HEAD', url, false)
try {
xhr.send()
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299
}
// `a.click()` doesn't work for all browsers (#465)
function click (node) {
try {
node.dispatchEvent(new MouseEvent('click'))
} catch (e) {
var evt = document.createEvent('MouseEvents')
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
20, false, false, false, false, 0, null)
node.dispatchEvent(evt)
}
}
// Detect WebView inside a native macOS app by ruling out all browsers
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)
var saveAs = _global.saveAs || (
// probably in some web worker
(typeof window !== 'object' || window !== _global)
? function saveAs () { /* noop */ }
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
: ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
? function saveAs (blob, name, opts) {
var URL = _global.URL || _global.webkitURL
var a = document.createElement('a')
name = name || blob.name || 'download'
a.download = name
a.rel = 'noopener' // tabnabbing
// TODO: detect chrome extensions & packaged apps
// a.target = '_blank'
if (typeof blob === 'string') {
// Support regular links
a.href = blob
if (a.origin !== location.origin) {
corsEnabled(a.href)
? download(blob, name, opts)
: click(a, a.target = '_blank')
} else {
click(a)
}
} else {
// Support blobs
a.href = URL.createObjectURL(blob)
setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
setTimeout(function () { click(a) }, 0)
}
}
// Use msSaveOrOpenBlob as a second approach
: 'msSaveOrOpenBlob' in navigator
? function saveAs (blob, name, opts) {
name = name || blob.name || 'download'
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts)
} else {
var a = document.createElement('a')
a.href = blob
a.target = '_blank'
setTimeout(function () { click(a) })
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name)
}
}
// Fallback to using FileReader and a popup
: function saveAs (blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank')
if (popup) {
popup.document.title =
popup.document.body.innerText = 'downloading...'
}
if (typeof blob === 'string') return download(blob, name, opts)
var force = blob.type === 'application/octet-stream'
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader()
reader.onloadend = function () {
var url = reader.result
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
if (popup) popup.location.href = url
else location = url
popup = null // reverse-tabnabbing #460
}
reader.readAsDataURL(blob)
} else {
var URL = _global.URL || _global.webkitURL
var url = URL.createObjectURL(blob)
if (popup) popup.location = url
else location.href = url
popup = null // reverse-tabnabbing #460
setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
}
}
)
_global.saveAs = saveAs.saveAs = saveAs
if (typeof module !== 'undefined') {
module.exports = saveAs;
}

View File

@ -1,16 +0,0 @@
import os
from flask import Flask
from flask import render_template
# A flask app to allow hosting filter tool locally
this_path = os.path.dirname(os.path.realpath(__file__))
app = Flask(__name__, template_folder=this_path, static_folder=this_path)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == "__main__":
app.run()

File diff suppressed because it is too large Load Diff

View File

@ -1,378 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>ArduPilot Filter Analysis</title>
<script type="text/javascript" src="filters.js"></script>
<script type="text/javascript" src="FileSaver.js"></script>
<script type="text/javascript" src={{url_for('static', filename='filters.js')}}></script>
<script type="text/javascript" src={{url_for('static', filename='FileSaver.js')}}></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script>
<script src="https://www.lactame.com/lib/ml/6.0.0/ml.min.js"></script>
</head>
<a href="https://ardupilot.org"><img src="logo.png"></a>
<h1>ArduPilot Filter Analysis</h1>
The following form will display the attenuation and phase lag for an
ArduPilot 4.2 filter setup.
<body onload="load(); fill_docs(); update_all_hidden(); check_nyquist(); calculate_filter(); calculate_pid();">
<canvas id="Attenuation" style="width:100%;max-width:1200px"></canvas>
<canvas id="Phase" style="width:100%;max-width:1200px;"></canvas>
<p>
<input type="button" id="calculate" value="Calculate">
<input type="button" id="SaveParams" value="Save Parameters" onclick="save_parameters();">
<button class="styleClass" onclick="document.getElementById('param_file').click()">Load Parameters</button>
<input type='file' id="param_file" style="display:none" onchange="load_parameters(this.files[0]);">
<input type="button" id="GetLink" value="Get Link" onclick="get_link();">
<form id="params" action="">
<fieldset style="max-width:1200px">
<legend>Graph Settings</legend>
<table>
<tr>
<td>
<fieldset style="width:150px">
<legend>Magnitude scale</legend>
<input type="radio" id="ScaleLog" name="Scale" value="Log" checked>
<label for="LogScale">dB</label><br>
<input type="radio" id="ScaleLinear" name="Scale" value="Linear">
<label for="LinearScale">Linear</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:150px">
<legend>Phase scale</legend>
<input type="radio" id="ScaleUnWrap" name="PhaseScale" value="unwrap" checked>
<label for="ScaleUnWrap">un-wrapped</label><br>
<input type="radio" id="ScaleWrap" name="PhaseScale" value="wrap">
<label for="ScaleWrap">±180</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:150px">
<legend>Frequency scale</legend>
<table>
<tr>
<td>
<input type="radio" id="freq_ScaleLog" name="feq_scale" value="Log" checked>
<label for="LogScale">Log</label><br>
<input type="radio" id="freq_ScaleLinear" name="feq_scale" value="Linear">
<label for="LinearScale">Linear</label><br>
</td>
<td>
<input type="radio" id="freq_Scale_Hz" name="feq_unit" value="Hz" checked>
<label for="Scale_unit_Hz">Hz</label><br>
<input type="radio" id="freq_Scale_RPM" name="feq_unit" value="RPM">
<label for="Scale_unit_RPM">RPM</label><br>
</td>
</tr>
</table>
</fieldset>
</td>
</tr>
</table>
<p>
<label for="MaxFreq">Maximum Displayed Frequency</label>
<input id="MaxFreq" name="MaxFreq" type="number" step="1" value="150" onchange="check_nyquist();"/>
<label id="MaxFreq_warning"></label>
</p>
<p>
<label for="MaxPhaseLag">Maximum Displayed Phase Lag</label>
<input id="MaxPhaseLag" name="MaxPhaseLag" type="number" step="1" value="360"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>INS Settings</legend>
<p>
<label for="GyroSampleRate">Gyro Sample Rate</label>
<input id="GyroSampleRate" name="GYRO_SAMPLE_RATE" type="number" step="1" value="2000" onchange="check_nyquist();"/>
</p>
<p>
<label for="INS_GYRO_FILTER">INS_GYRO_FILTER</label>
<input id="INS_GYRO_FILTER" name="INS_GYRO_FILTER" type="number" step="0.1" value="20.0"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>First Notch Filter</legend>
<p>
<label for="INS_HNTCH_ENABLE">INS_HNTCH_ENABLE</label>
<input id="INS_HNTCH_ENABLE" name="INS_HNTCH_ENABLE" type="number" step="1" value="0" onchange="update_hidden(this.id); fill_docs();"/>
<label id="INS_HNTCH_ENABLE.doc"></label>
</p>
<p>
<label for="INS_HNTCH_MODE">INS_HNTCH_MODE</label>
<input id="INS_HNTCH_MODE" name="INS_HNTCH_MODE" type="number" step="1" value="0" onchange="update_hidden_mode(); fill_docs();"/>
<label id="INS_HNTCH_MODE.doc"></label>
</p>
<p>
<label for="INS_HNTCH_FREQ">INS_HNTCH_FREQ</label>
<input id="INS_HNTCH_FREQ" name="INS_HNTCH_FREQ" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTCH_BW">INS_HNTCH_BW</label>
<input id="INS_HNTCH_BW" name="INS_HNTCH_BW" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTCH_ATT">INS_HNTCH_ATT</label>
<input id="INS_HNTCH_ATT" name="INS_HNTCH_ATT" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTCH_REF">INS_HNTCH_REF</label>
<input id="INS_HNTCH_REF" name="INS_HNTCH_REF" type="number" step="0.01" value="0"/>
</p>
<p>
<label for="INS_HNTCH_FM_RAT">INS_HNTCH_FM_RAT</label>
<input id="INS_HNTCH_FM_RAT" name="INS_HNTCH_FM_RAT" type="number" step="0.01" value="1.0"/>
</p>
<p>
<label for="INS_HNTCH_HMNCS">INS_HNTCH_HMNCS</label>
<input id="INS_HNTCH_HMNCS" name="INS_HNTCH_HMNCS" type="number" step="1" value="1"/>
<label id="INS_HNTCH_HMNCS.doc"></label>
</p>
<p>
<label for="INS_HNTCH_OPTS">INS_HNTCH_OPTS</label>
<input id="INS_HNTCH_OPTS" name="INS_HNTCH_OPTS" type="number" step="1" value="0"/>
<label id="INS_HNTCH_OPTS.doc"></label>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>Second Notch Filter</legend>
<p>
<label for="INS_HNTC2_ENABLE">INS_HNTC2_ENABLE</label>
<input id="INS_HNTC2_ENABLE" name="INS_HNTC2_ENABLE" type="number" step="1" value="0" onchange="update_hidden(this.id); fill_docs();"/>
<label id="INS_HNTC2_ENABLE.doc"></label>
</p>
<p>
<label for="INS_HNTC2_MODE">INS_HNTC2_MODE</label>
<input id="INS_HNTC2_MODE" name="INS_HNTC2_MODE" type="number" step="1" value="0" onchange="update_hidden_mode(); fill_docs();"/>
<label id="INS_HNTC2_MODE.doc"></label>
</p>
<p>
<label for="INS_HNTC2_FREQ">INS_HNTC2_FREQ</label>
<input id="INS_HNTC2_FREQ" name="INS_HNTC2_FREQ" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTC2_BW">INS_HNTC2_BW</label>
<input id="INS_HNTC2_BW" name="INS_HNTC2_BW" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTC2_ATT">INS_HNTC2_ATT</label>
<input id="INS_HNTC2_ATT" name="INS_HNTC2_ATT" type="number" step="0.1" value="0"/>
</p>
<p>
<label for="INS_HNTC2_REF">INS_HNTC2_REF</label>
<input id="INS_HNTC2_REF" name="INS_HNTC2_REF" type="number" step="0.01" value="0"/>
</p>
<p>
<label for="INS_HNTC2_FM_RAT">INS_HNTC2_FM_RAT</label>
<input id="INS_HNTC2_FM_RAT" name="INS_HNTC2_FM_RAT" type="number" step="0.01" value="1.0"/>
</p>
<p>
<label for="INS_HNTC2_HMNCS">INS_HNTC2_HMNCS</label>
<input id="INS_HNTC2_HMNCS" name="INS_HNTC2_HMNCS" type="number" step="1" value="1"/>
<label id="INS_HNTC2_HMNCS.doc"></label>
</p>
<p>
<label for="INS_HNTC2_OPTS">INS_HNTC2_OPTS</label>
<input id="INS_HNTC2_OPTS" name="INS_HNTC2_OPTS" type="number" step="1" value="0"/>
<label id="INS_HNTC2_OPTS.doc"></label>
</p>
</fieldset>
<fieldset style="max-width:1200px" id="Throttle_input">
<legend>Throttle Based</legend>
<p>
<label for="Throttle">Throttle</label>
<input id="Throttle" name="Throttle" type="number" step="0.01" value="0.3"/>
</p>
</fieldset>
<fieldset style="max-width:1200px" id="ESC_input">
<legend>ESC Telemetry</legend>
<p>
<label for="NUM_MOTORS">Number of Motors</label>
<input id="NUM_MOTORS" name="NUM_MOTORS" type="number" step="1" value="1"/>
</p>
<p>
<label for="ESC_RPM">ESC RPM</label>
<input id="ESC_RPM" name="ESC_RPM" type="number" step="1" value="2500"/>
</p>
</fieldset>
<fieldset style="max-width:1200px" id="RPM_input">
<legend>RPM/EFI Based</legend>
<p>
<label for="RPM1">RPM1</label>
<input id="RPM1" name="RPM1" type="number" step="1" value="2500"/>
</p>
<p>
<label for="RPM2">RPM2</label>
<input id="RPM2" name="RPM2" type="number" step="1" value="2500"/>
</p>
</fieldset>
</form>
<h2>PIDs</h2>
<h3><label id="PID_title">Title</label></h3>
<canvas id="PID_Attenuation" style="width:100%;max-width:1200px"></canvas>
<canvas id="PID_Phase" style="width:100%;max-width:1200px"></canvas>
<p>
<input type="button" id="CalculateRoll" value="Caculate Roll" onclick="calculate_pid(this.id);">
<input type="button" id="CalculatePitch" value="Caculate Pitch" onclick="calculate_pid(this.id);">
<input type="button" id="CalculateYaw" value="Caculate Yaw" onclick="calculate_pid(this.id);">
</p>
<form id="PID_params" action="">
<fieldset style="max-width:1200px">
<legend>Graph Settings</legend>
<p>
<table>
<tr>
<td>
<fieldset style="width:150px">
<legend>Gain scale</legend>
<input type="radio" id="PID_ScaleLog" name="PID_Scale" value="Log" checked>
<label for="LogScale">dB</label><br>
<input type="radio" id="PID_ScaleLinear" name="PID_Scale" value="Linear">
<label for="LinearScale">Linear</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:150px">
<legend>Phase scale</legend>
<input type="radio" id="PID_ScaleUnWrap" name="PID_PhaseScale" value="unwrap" checked>
<label for="ScaleUnWrap">un-wrapped</label><br>
<input type="radio" id="PID_ScaleWrap" name="PID_PhaseScale" value="wrap">
<label for="ScaleWrap">±180</label><br>
</fieldset>
</td>
<td>
<fieldset style="width:150px">
<legend>Frequency scale</legend>
<table>
<tr>
<td>
<input type="radio" id="PID_freq_ScaleLog" name="PID_feq_scale" value="Log" checked>
<label for="LogScale">Log</label><br>
<input type="radio" id="PID_freq_ScaleLinear" name="PID_feq_scale" value="Linear">
<label for="LinearScale">Linear</label><br>
</td>
<td>
<input type="radio" id="PID_freq_Scale_Hz" name="PID_feq_unit" value="Hz" checked>
<label for="Scale_unit_Hz">Hz</label><br>
<input type="radio" id="PID_freq_Scale_RPM" name="PID_feq_unit" value="RPM">
<label for="Scale_unit_RPM">RPM</label><br>
</td>
</tr>
</table>
</fieldset>
</td>
<td>
<fieldset style="width:150px">
<legend>Filtering</legend>
<input type="radio" id="PID_filtering_Pre" name="filtering" value="Pre" checked>
<label for="LogScale">Pre</label><br>
<input type="radio" id="PID_filtering_Post" name="filtering" value="Post">
<label for="LinearScale">Post</label><br>
</fieldset>
</td>
</tr>
</table>
</p>
<p>
<label for="PID_MaxFreq">Maximum Displayed Frequency</label>
<input id="PID_MaxFreq" name="PID_MaxFreq" type="number" step="1" value="150" onchange="check_nyquist();"/>
<label id="PID_MaxFreq_warning"></label>
</p>
<p>
<label for="PID_MaxPhaseLag">Maximum Displayed Phase Lag</label>
<input id="PID_MaxPhaseLag" name="PID_MaxPhaseLag" type="number" step="1" value="360"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>Loop Rate</legend>
<p>
<label for="SCHED_LOOP_RATE">SCHED_LOOP_RATE</label>
<input id="SCHED_LOOP_RATE" name="SCHED_LOOP_RATE" type="number" step="1" value="400" onchange="check_nyquist();"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>Roll</legend>
<p>
<label for="ATC_RAT_RLL_P">ATC_RAT_RLL_P</label>
<input id="ATC_RAT_RLL_P" name="ATC_RAT_RLL_P" type="number" step="0.01" value="0.135"/>
</p>
<p>
<label for="ATC_RAT_RLL_I">ATC_RAT_RLL_I</label>
<input id="ATC_RAT_RLL_I" name="ATC_RAT_RLL_I" type="number" step="0.01" value="0.135"/>
</p>
<p>
<label for="ATC_RAT_RLL_D">ATC_RAT_RLL_D</label>
<input id="ATC_RAT_RLL_D" name="ATC_RAT_RLL_D" type="number" step="0.0001" value="0.0036"/>
</p>
<p>
<label for="ATC_RAT_RLL_FLTE">ATC_RAT_RLL_FLTE</label>
<input id="ATC_RAT_RLL_FLTE" name="ATC_RAT_RLL_FLTE" type="number" step="0.01" value="0"/>
</p>
<p>
<label for="ATC_RAT_RLL_FLTD">ATC_RAT_RLL_FLTD</label>
<input id="ATC_RAT_RLL_FLTD" name="ATC_RAT_RLL_FLTD" type="number" step="0.01" value="20"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>Pitch</legend>
<p>
<label for="ATC_RAT_PIT_P">ATC_RAT_PIT_P</label>
<input id="ATC_RAT_PIT_P" name="ATC_RAT_PIT_P" type="number" step="0.01" value="0.135"/>
</p>
<p>
<label for="ATC_RAT_PIT_I">ATC_RAT_PIT_I</label>
<input id="ATC_RAT_PIT_I" name="ATC_RAT_PIT_I" type="number" step="0.01" value="0.135"/>
</p>
<p>
<label for="ATC_RAT_PIT_D">ATC_RAT_PIT_D</label>
<input id="ATC_RAT_PIT_D" name="ATC_RAT_PIT_D" type="number" step="0.0001" value="0.0036"/>
</p>
<p>
<label for="ATC_RAT_PIT_FLTE">ATC_RAT_PIT_FLTE</label>
<input id="ATC_RAT_PIT_FLTE" name="ATC_RAT_PIT_FLTE" type="number" step="0.01" value="0"/>
</p>
<p>
<label for="ATC_RAT_PIT_FLTD">ATC_RAT_PIT_FLTD</label>
<input id="ATC_RAT_PIT_FLTD" name="ATC_RAT_PIT_FLTD" type="number" step="0.01" value="20"/>
</p>
</fieldset>
<fieldset style="max-width:1200px">
<legend>Yaw</legend>
<p>
<label for="ATC_RAT_YAW_P">ATC_RAT_YAW_P</label>
<input id="ATC_RAT_YAW_P" name="ATC_RAT_YAW_P" type="number" step="0.01" value="0.09"/>
</p>
<p>
<label for="ATC_RAT_YAW_I">ATC_RAT_YAW_I</label>
<input id="ATC_RAT_YAW_I" name="ATC_RAT_YAW_I" type="number" step="0.01" value="0.009"/>
</p>
<p>
<label for="ATC_RAT_YAW_D">ATC_RAT_YAW_D</label>
<input id="ATC_RAT_YAW_D" name="ATC_RAT_YAW_D" type="number" step="0.0001" value="0"/>
</p>
<p>
<label for="ATC_RAT_YAW_FLTE">ATC_RAT_YAW_FLTE</label>
<input id="ATC_RAT_YAW_FLTE" name="ATC_RAT_YAW_FLTE" type="number" step="0.01" value="2.5"/>
</p>
<p>
<label for="ATC_RAT_YAW_FLTD">ATC_RAT_YAW_FLTD</label>
<input id="ATC_RAT_YAW_FLTD" name="ATC_RAT_YAW_FLTD" type="number" step="0.01" value="0"/>
</p>
</fieldset>
</form>
<script>
var calc_btn = document.getElementById('calculate');
calc_btn.onclick = function() {
calculate_filter();
}
//var clear_btn = document.getElementById('clear_cookies');
//clear_btn.onclick = function() {
// clear_cookies();
//}
</script>
</body>
</html>