From 0afce3be786e531be339cb7ed54a0687cb381551 Mon Sep 17 00:00:00 2001 From: bnsgeyer Date: Sat, 1 Nov 2025 22:01:02 -0400 Subject: [PATCH 01/24] AnalyticTune: Make all discrete filter functions a separate js file --- AnalyticTune/AnalyticTune.js | 591 --------------------------------- AnalyticTune/DiscreteFilter.js | 589 ++++++++++++++++++++++++++++++++ AnalyticTune/index.html | 1 + 3 files changed, 590 insertions(+), 591 deletions(-) create mode 100644 AnalyticTune/DiscreteFilter.js diff --git a/AnalyticTune/AnalyticTune.js b/AnalyticTune/AnalyticTune.js index 306e53fd..221e97cc 100644 --- a/AnalyticTune/AnalyticTune.js +++ b/AnalyticTune/AnalyticTune.js @@ -2,389 +2,6 @@ var DataflashParser import('../modules/JsDataflashParser/parser.js').then((mod) => { DataflashParser = mod.default }) - -function PID(sample_rate,kP,kI,kD,filtE,filtD) { - this.sample_rate = sample_rate - - this._kP = kP; - this._kI = kI; - this._kD = kD; - - this.E_filter = new LPF_1P(sample_rate, filtE) - this.D_filter = new LPF_1P(sample_rate, filtD) - - this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { - const E_trans = this.E_filter.transfer(Z, Z1, Z2, false, false) - const D_trans = complex_mul(E_trans, this.D_filter.transfer(Z, Z1, Z2, false, false)) - - // I term is k*z / (z - 1) - const Z_less_one = [array_offset(Z[0], -1), Z[1].slice()] - const I_comp = complex_mul(complex_div(Z,Z_less_one), E_trans) - const kI = this._kI/this.sample_rate - - // D term is k * (1 - Z^-1) - const one_less_Z1 = [array_offset(array_scale(Z1[0],-1), 1), array_scale(Z1[1],-1)] - const D_comp = complex_mul(one_less_Z1, D_trans) - const kD = this._kD*this.sample_rate - - - const len = Z1[0].length - let ret = [new Array(len), new Array(len)] - let P = [new Array(len), new Array(len)] - let I = [new Array(len), new Array(len)] - let D = [new Array(len), new Array(len)] - for (let i = 0; i 0.0) && (this.center_freq_hz < 0.5 * this.sample_rate) && (this.Q > 0.0)) { - this.A = Math.pow(10.0, -this.attenuation_dB / 40.0); - var omega = 2.0 * Math.PI * this.center_freq_hz / this.sample_rate; - var alpha = Math.sin(omega) / (2 * this.Q); - this.b0 = 1.0 + alpha*(this.A**2); - this.b1 = -2.0 * Math.cos(omega); - this.b2 = 1.0 - alpha*(this.A**2); - this.a0_inv = 1.0/(1.0 + alpha); - this.a1 = this.b1; - this.a2 = 1.0 - alpha; - this.initialised = true; - } else { - this.initialised = false; - } - - this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { - if (!this.initialised) { - const len = Z1[0].length - return [new Array(len).fill(1), new Array(len).fill(0)] - } - - const a0 = 1 / this.a0_inv - - const len = Z1[0].length - let numerator = [new Array(len), new Array(len)] - let denominator = [new Array(len), new Array(len)] - for (let i = 0; i 0.5 * this.bandwidth_hz) { - var octaves = Math.log2(this.center_freq_hz / (this.center_freq_hz - this.bandwidth_hz / 2.0)) * 2.0; - this.Q = Math.sqrt(Math.pow(2.0, octaves)) / (Math.pow(2.0, octaves) - 1.0); - } else { - this.Q = 0.0; - } - } - - this.init_with_A_and_Q = function() { - if ((this.center_freq_hz > 0.0) && (this.center_freq_hz < 0.5 * this.sample_freq) && (this.Q > 0.0)) { - var omega = 2.0 * Math.PI * this.center_freq_hz / this.sample_freq; - var alpha = Math.sin(omega) / (2 * this.Q); - this.b0 = 1.0 + alpha*(this.A**2); - this.b1 = -2.0 * Math.cos(omega); - this.b2 = 1.0 - alpha*(this.A**2); - this.a0_inv = 1.0/(1.0 + alpha); - this.a1 = this.b1; - this.a2 = 1.0 - alpha; - this.initialised = true; - } else { - this.initialised = false; - } - } - - // check center frequency is in the allowable range - if ((center_freq_hz > 0.5 * bandwidth_hz) && (center_freq_hz < 0.5 * sample_freq)) { - this.calculate_A_and_Q(); - this.init_with_A_and_Q(); - } else { - this.initialised = false; - } - - this.transfer = function(Z, Z1, Z2) { - if (!this.initialised) { - const len = Z1[0].length - return [new Array(len).fill(1), new Array(len).fill(0)] - } - - const a0 = 1 / this.a0_inv - - const len = Z1[0].length - let numerator = [new Array(len), new Array(len)] - let denominator = [new Array(len), new Array(len)] - for (let i = 0; i 1) { - var notch_center_double; - // only enable the filter if its center frequency is below the nyquist frequency - notch_center_double = notch_center * (1.0 - notch_spread); - if (notch_center_double < nyquist_limit) { - this.notches.push(new NotchFilter(sample_freq,notch_center_double,bandwidth_hz/composite_notches,att)); - } - // only enable the filter if its center frequency is below the nyquist frequency - notch_center_double = notch_center * (1.0 + notch_spread); - if (notch_center_double < nyquist_limit) { - this.notches.push(new NotchFilter(sample_freq,notch_center_double,bandwidth_hz/composite_notches,att)); - } - } - } - } - } - - this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { - const len = Z1[0].length - var H_total = [new Array(len).fill(1), new Array(len).fill(0)] - for (n in this.notches) { - const H = this.notches[n].transfer(Z, Z1, Z2); - H_total = complex_mul(H_total, H) - } - - this.attenuation = complex_abs(H_total) - this.phase = array_scale(complex_phase(H_total), 180/Math.PI) - if (use_dB) { - this.attenuation = array_scale(array_log10(this.attenuation), 20.0) - } - if (unwrap_phase) { - this.phase = unwrap(this.phase) - } - - return H_total; - } -} - function get_form(vname) { var v = parseFloat(document.getElementById(vname).value); return v; } -function get_filters(sample_rate) { - var filters = [] - filters.push(new HarmonicNotchFilter(sample_rate, - get_form("INS_HNTCH_ENABLE"), - get_form("INS_HNTCH_MODE"), - get_form("INS_HNTCH_FREQ"), - get_form("INS_HNTCH_BW"), - get_form("INS_HNTCH_ATT"), - get_form("INS_HNTCH_REF"), - get_form("INS_HNTCH_FM_RAT"), - get_form("INS_HNTCH_HMNCS"), - get_form("INS_HNTCH_OPTS"))); - filters.push(new HarmonicNotchFilter(sample_rate, - get_form("INS_HNTC2_ENABLE"), - get_form("INS_HNTC2_MODE"), - get_form("INS_HNTC2_FREQ"), - get_form("INS_HNTC2_BW"), - get_form("INS_HNTC2_ATT"), - get_form("INS_HNTC2_REF"), - get_form("INS_HNTC2_FM_RAT"), - get_form("INS_HNTC2_HMNCS"), - get_form("INS_HNTC2_OPTS"))); - filters.push(new DigitalBiquadFilter(sample_rate,get_form("INS_GYRO_FILTER"))); - - return filters; -} - -// Unwrap phase by looking for jumps of larger than 180 deg -function unwrap(phase) { - const len = phase.length - - // Notches result in large positive phase changes, bias the unwrap to do a better job - const neg_threshold = 45 - const pos_threshold = 360 - neg_threshold - - let unwrapped = new Array(len) - - unwrapped[0] = phase[0] - for (let i = 1; i < len; i++) { - let phase_diff = phase[i] - phase[i-1]; - if (phase_diff > pos_threshold) { - phase_diff -= 360.0; - } else if (phase_diff < -neg_threshold) { - phase_diff += 360.0; - } - unwrapped[i] = unwrapped[i-1] + phase_diff - } - - return unwrapped -} - -function evaluate_transfer_functions(filter_groups, freq_max, freq_step, use_dB, unwrap_phase) { - - // Not sure why range does not return expected array, _data gets us the array - const freq = array_from_range(freq_step, freq_max, freq_step) - - // Start with unity transfer function, input = output - const len = freq.length - var H_total = [new Array(len).fill(1), new Array(len).fill(0)] - - for (let i = 0; i < filter_groups.length; i++) { - // Allow for batches at different sample rates - const filters = filter_groups[i] - - const sample_rate = filters[0].sample_rate - for (let j = 1; j < filters.length; j++) { - if (filters[0].sample_rate != sample_rate) { - error("Sample rate miss match") - } - } - - // Calculate Z for transfer function - // Z = e^jw - const Z = exp_jw(freq, sample_rate) - - // Z^-1 - const Z1 = complex_inverse(Z) - - // Z^-2 - const Z2 = complex_inverse(complex_square(Z)) - - // Apply all transfer functions - for (let filter of filters) { - const H = filter.transfer(Z, Z1, Z2, use_dB, unwrap_phase) - H_total = complex_mul(H_total, H) - } - } - - // Calculate total filter transfer function - let attenuation = complex_abs(H_total) - let phase = array_scale(complex_phase(H_total), 180/Math.PI) - if (use_dB) { - attenuation = array_scale(array_log10(attenuation), 20.0) - } - if (unwrap_phase) { - phase = unwrap(phase) - } - - // Return attenuation and phase - return { attenuation: attenuation, phase: phase, freq: freq, H_total: H_total} -} - var flight_data = {} var fft_plot = {} var fft_plot_Phase = {} diff --git a/AnalyticTune/DiscreteFilter.js b/AnalyticTune/DiscreteFilter.js new file mode 100644 index 00000000..99803428 --- /dev/null +++ b/AnalyticTune/DiscreteFilter.js @@ -0,0 +1,589 @@ +function PID(sample_rate,kP,kI,kD,filtE,filtD) { + this.sample_rate = sample_rate + + this._kP = kP; + this._kI = kI; + this._kD = kD; + + this.E_filter = new LPF_1P(sample_rate, filtE) + this.D_filter = new LPF_1P(sample_rate, filtD) + + this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { + const E_trans = this.E_filter.transfer(Z, Z1, Z2, false, false) + const D_trans = complex_mul(E_trans, this.D_filter.transfer(Z, Z1, Z2, false, false)) + + // I term is k*z / (z - 1) + const Z_less_one = [array_offset(Z[0], -1), Z[1].slice()] + const I_comp = complex_mul(complex_div(Z,Z_less_one), E_trans) + const kI = this._kI/this.sample_rate + + // D term is k * (1 - Z^-1) + const one_less_Z1 = [array_offset(array_scale(Z1[0],-1), 1), array_scale(Z1[1],-1)] + const D_comp = complex_mul(one_less_Z1, D_trans) + const kD = this._kD*this.sample_rate + + + const len = Z1[0].length + let ret = [new Array(len), new Array(len)] + let P = [new Array(len), new Array(len)] + let I = [new Array(len), new Array(len)] + let D = [new Array(len), new Array(len)] + for (let i = 0; i 0.0) && (this.center_freq_hz < 0.5 * this.sample_rate) && (this.Q > 0.0)) { + this.A = Math.pow(10.0, -this.attenuation_dB / 40.0); + var omega = 2.0 * Math.PI * this.center_freq_hz / this.sample_rate; + var alpha = Math.sin(omega) / (2 * this.Q); + this.b0 = 1.0 + alpha*(this.A**2); + this.b1 = -2.0 * Math.cos(omega); + this.b2 = 1.0 - alpha*(this.A**2); + this.a0_inv = 1.0/(1.0 + alpha); + this.a1 = this.b1; + this.a2 = 1.0 - alpha; + this.initialised = true; + } else { + this.initialised = false; + } + + this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { + if (!this.initialised) { + const len = Z1[0].length + return [new Array(len).fill(1), new Array(len).fill(0)] + } + + const a0 = 1 / this.a0_inv + + const len = Z1[0].length + let numerator = [new Array(len), new Array(len)] + let denominator = [new Array(len), new Array(len)] + for (let i = 0; i 0.5 * this.bandwidth_hz) { + var octaves = Math.log2(this.center_freq_hz / (this.center_freq_hz - this.bandwidth_hz / 2.0)) * 2.0; + this.Q = Math.sqrt(Math.pow(2.0, octaves)) / (Math.pow(2.0, octaves) - 1.0); + } else { + this.Q = 0.0; + } + } + + this.init_with_A_and_Q = function() { + if ((this.center_freq_hz > 0.0) && (this.center_freq_hz < 0.5 * this.sample_freq) && (this.Q > 0.0)) { + var omega = 2.0 * Math.PI * this.center_freq_hz / this.sample_freq; + var alpha = Math.sin(omega) / (2 * this.Q); + this.b0 = 1.0 + alpha*(this.A**2); + this.b1 = -2.0 * Math.cos(omega); + this.b2 = 1.0 - alpha*(this.A**2); + this.a0_inv = 1.0/(1.0 + alpha); + this.a1 = this.b1; + this.a2 = 1.0 - alpha; + this.initialised = true; + } else { + this.initialised = false; + } + } + + // check center frequency is in the allowable range + if ((center_freq_hz > 0.5 * bandwidth_hz) && (center_freq_hz < 0.5 * sample_freq)) { + this.calculate_A_and_Q(); + this.init_with_A_and_Q(); + } else { + this.initialised = false; + } + + this.transfer = function(Z, Z1, Z2) { + if (!this.initialised) { + const len = Z1[0].length + return [new Array(len).fill(1), new Array(len).fill(0)] + } + + const a0 = 1 / this.a0_inv + + const len = Z1[0].length + let numerator = [new Array(len), new Array(len)] + let denominator = [new Array(len), new Array(len)] + for (let i = 0; i 1) { + var notch_center_double; + // only enable the filter if its center frequency is below the nyquist frequency + notch_center_double = notch_center * (1.0 - notch_spread); + if (notch_center_double < nyquist_limit) { + this.notches.push(new NotchFilter(sample_freq,notch_center_double,bandwidth_hz/composite_notches,att)); + } + // only enable the filter if its center frequency is below the nyquist frequency + notch_center_double = notch_center * (1.0 + notch_spread); + if (notch_center_double < nyquist_limit) { + this.notches.push(new NotchFilter(sample_freq,notch_center_double,bandwidth_hz/composite_notches,att)); + } + } + } + } + } + + this.transfer = function(Z, Z1, Z2, use_dB, unwrap_phase) { + const len = Z1[0].length + var H_total = [new Array(len).fill(1), new Array(len).fill(0)] + for (n in this.notches) { + const H = this.notches[n].transfer(Z, Z1, Z2); + H_total = complex_mul(H_total, H) + } + + this.attenuation = complex_abs(H_total) + this.phase = array_scale(complex_phase(H_total), 180/Math.PI) + if (use_dB) { + this.attenuation = array_scale(array_log10(this.attenuation), 20.0) + } + if (unwrap_phase) { + this.phase = unwrap(this.phase) + } + + return H_total; + } +} + +function get_filters(sample_rate) { + var filters = [] + filters.push(new HarmonicNotchFilter(sample_rate, + get_form("INS_HNTCH_ENABLE"), + get_form("INS_HNTCH_MODE"), + get_form("INS_HNTCH_FREQ"), + get_form("INS_HNTCH_BW"), + get_form("INS_HNTCH_ATT"), + get_form("INS_HNTCH_REF"), + get_form("INS_HNTCH_FM_RAT"), + get_form("INS_HNTCH_HMNCS"), + get_form("INS_HNTCH_OPTS"))); + filters.push(new HarmonicNotchFilter(sample_rate, + get_form("INS_HNTC2_ENABLE"), + get_form("INS_HNTC2_MODE"), + get_form("INS_HNTC2_FREQ"), + get_form("INS_HNTC2_BW"), + get_form("INS_HNTC2_ATT"), + get_form("INS_HNTC2_REF"), + get_form("INS_HNTC2_FM_RAT"), + get_form("INS_HNTC2_HMNCS"), + get_form("INS_HNTC2_OPTS"))); + filters.push(new DigitalBiquadFilter(sample_rate,get_form("INS_GYRO_FILTER"))); + + return filters; +} + +// Unwrap phase by looking for jumps of larger than 180 deg +function unwrap(phase) { + const len = phase.length + + // Notches result in large positive phase changes, bias the unwrap to do a better job + const neg_threshold = 45 + const pos_threshold = 360 - neg_threshold + + let unwrapped = new Array(len) + + unwrapped[0] = phase[0] + for (let i = 1; i < len; i++) { + let phase_diff = phase[i] - phase[i-1]; + if (phase_diff > pos_threshold) { + phase_diff -= 360.0; + } else if (phase_diff < -neg_threshold) { + phase_diff += 360.0; + } + unwrapped[i] = unwrapped[i-1] + phase_diff + } + + return unwrapped +} + +function evaluate_transfer_functions(filter_groups, freq_max, freq_step, use_dB, unwrap_phase) { + + // Not sure why range does not return expected array, _data gets us the array + const freq = array_from_range(freq_step, freq_max, freq_step) + + // Start with unity transfer function, input = output + const len = freq.length + var H_total = [new Array(len).fill(1), new Array(len).fill(0)] + + for (let i = 0; i < filter_groups.length; i++) { + // Allow for batches at different sample rates + const filters = filter_groups[i] + + const sample_rate = filters[0].sample_rate + for (let j = 1; j < filters.length; j++) { + if (filters[0].sample_rate != sample_rate) { + error("Sample rate miss match") + } + } + + // Calculate Z for transfer function + // Z = e^jw + const Z = exp_jw(freq, sample_rate) + + // Z^-1 + const Z1 = complex_inverse(Z) + + // Z^-2 + const Z2 = complex_inverse(complex_square(Z)) + + // Apply all transfer functions + for (let filter of filters) { + const H = filter.transfer(Z, Z1, Z2, use_dB, unwrap_phase) + H_total = complex_mul(H_total, H) + } + } + + // Calculate total filter transfer function + let attenuation = complex_abs(H_total) + let phase = array_scale(complex_phase(H_total), 180/Math.PI) + if (use_dB) { + attenuation = array_scale(array_log10(attenuation), 20.0) + } + if (unwrap_phase) { + phase = unwrap(phase) + } + + // Return attenuation and phase + return { attenuation: attenuation, phase: phase, freq: freq, H_total: H_total} +} diff --git a/AnalyticTune/index.html b/AnalyticTune/index.html index cde68ce3..2ef52cab 100644 --- a/AnalyticTune/index.html +++ b/AnalyticTune/index.html @@ -5,6 +5,7 @@ ArduCopter Analytic Tune Tool + From 62fbd6ac723b8c5c4af778af3aa643e4c928b066 Mon Sep 17 00:00:00 2001 From: bnsgeyer Date: Tue, 4 Nov 2025 22:30:46 -0500 Subject: [PATCH 02/24] have attitude control params load dynamically --- AnalyticTune/AnalyticTune.js | 3 +- AnalyticTune/FormBuilder.js | 223 +++++++++++++++++++++++++++++ AnalyticTune/index.html | 263 ++--------------------------------- 3 files changed, 233 insertions(+), 256 deletions(-) create mode 100644 AnalyticTune/FormBuilder.js diff --git a/AnalyticTune/AnalyticTune.js b/AnalyticTune/AnalyticTune.js index 221e97cc..f94b7e3f 100644 --- a/AnalyticTune/AnalyticTune.js +++ b/AnalyticTune/AnalyticTune.js @@ -782,7 +782,7 @@ function update_PID_filters() { } document.getElementById(ele_prefix + 'PitchPIDS').style.display = 'block'; document.getElementById(ele_prefix + 'PitchNOTCH').style.display = 'block'; - console.log(ele_prefix + 'PitchPIDS') + const NTF_num = document.getElementById(get_rate_param_prefix() + 'NTF').value; if (NTF_num > 0) { document.getElementById('FILT' + NTF_num).style.display = 'block'; @@ -814,6 +814,7 @@ var calc_freq_resp var pred_freq_resp function calculate_freq_resp() { const start = performance.now() + console.log(params) // Window size from user const window_size = parseInt(document.getElementById("FFTWindow_size").value) diff --git a/AnalyticTune/FormBuilder.js b/AnalyticTune/FormBuilder.js new file mode 100644 index 00000000..95f46fde --- /dev/null +++ b/AnalyticTune/FormBuilder.js @@ -0,0 +1,223 @@ +// Data structure for table definitions +const tableData = { + // RollPitchTC Prefix Tables + "RollPitchTC": [ + { + id: "RollPitchTC", + inputs: [ + { id: "ATC_INPUT_TC", name: "ATC_INPUT_TC", type: "number", step: "0.01", value: "0.15" } + ] + } + ], + // RollPIDS Prefix Tables + "RollPIDS": [ + { + id: "RollPIDS", + inputs: [ + { id: "ATC_ANG_RLL_P", name: "ATC_ANG_RLL_P", type: "number", step: "0.1", value: "4.5" }, + { id: "ATC_RAT_RLL_FF", name: "ATC_RAT_RLL_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "ATC_RAT_RLL_P", name: "ATC_RAT_RLL_P", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_RLL_I", name: "ATC_RAT_RLL_I", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_RLL_D", name: "ATC_RAT_RLL_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "ATC_RAT_RLL_D_FF", name: "ATC_RAT_RLL_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "ATC_RAT_RLL_FLTT", name: "ATC_RAT_RLL_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "ATC_RAT_RLL_FLTE", name: "ATC_RAT_RLL_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "ATC_RAT_RLL_FLTD", name: "ATC_RAT_RLL_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // PitchPIDS Prefix Tables + "PitchPIDS": [ + { + id: "PitchPIDS", + inputs: [ + { id: "ATC_ANG_PIT_P", name: "ATC_ANG_PIT_P", type: "number", step: "0.1", value: "4.5" }, + { id: "ATC_RAT_PIT_FF", name: "ATC_RAT_PIT_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "ATC_RAT_PIT_P", name: "ATC_RAT_PIT_P", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_PIT_I", name: "ATC_RAT_PIT_I", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_PIT_D", name: "ATC_RAT_PIT_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "ATC_RAT_PIT_D_FF", name: "ATC_RAT_PIT_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "ATC_RAT_PIT_FLTT", name: "ATC_RAT_PIT_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "ATC_RAT_PIT_FLTE", name: "ATC_RAT_PIT_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "ATC_RAT_PIT_FLTD", name: "ATC_RAT_PIT_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // YawTC Prefix Tables + "YawTC": [ + { + id: "YawTC", + inputs: [ + { id: "PILOT_Y_RATE_TC", name: "PILOT_Y_RATE_TC", type: "number", step: "0.01", value: "0.0" } + ] + } + ], + // YawPIDS Prefix Tables + "YawPIDS": [ + { + id: "YawPIDS", + inputs: [ + { id: "ATC_ANG_YAW_P", name: "ATC_ANG_YAW_P", type: "number", step: "0.1", value: "4.5" }, + { id: "ATC_RAT_YAW_FF", name: "ATC_RAT_YAW_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "ATC_RAT_YAW_P", name: "ATC_RAT_YAW_P", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_YAW_I", name: "ATC_RAT_YAW_I", type: "number", step: "0.01", value: "0.288" }, + { id: "ATC_RAT_YAW_D", name: "ATC_RAT_YAW_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "ATC_RAT_YAW_D_FF", name: "ATC_RAT_YAW_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "ATC_RAT_YAW_FLTT", name: "ATC_RAT_YAW_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "ATC_RAT_YAW_FLTE", name: "ATC_RAT_YAW_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "ATC_RAT_YAW_FLTD", name: "ATC_RAT_YAW_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // QRollPitchTC Prefix Tables + "QRollPitchTC": [ + { + id: "QRollPitchTC", + inputs: [ + { id: "Q_A_INPUT_TC", name: "Q_A_INPUT_TC", type: "number", step: "0.01", value: "0.15" } + ] + } + ], + // QRollPIDS Prefix Tables + "QRollPIDS": [ + { + id: "QRollPIDS", + inputs: [ + { id: "Q_A_ANG_RLL_P", name: "Q_A_ANG_RLL_P", type: "number", step: "0.1", value: "4.5" }, + { id: "Q_A_RAT_RLL_FF", name: "Q_A_RAT_RLL_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "Q_A_RAT_RLL_P", name: "Q_A_RAT_RLL_P", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_RLL_I", name: "Q_A_RAT_RLL_I", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_RLL_D", name: "Q_A_RAT_RLL_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "Q_A_RAT_RLL_D_FF", name: "Q_A_RAT_RLL_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "Q_A_RAT_RLL_FLTT", name: "Q_A_RAT_RLL_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "Q_A_RAT_RLL_FLTE", name: "Q_A_RAT_RLL_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "Q_A_RAT_RLL_FLTD", name: "Q_A_RAT_RLL_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // QPitchPIDS Prefix Tables + "QPitchPIDS": [ + { + id: "QPitchPIDS", + inputs: [ + { id: "Q_A_ANG_PIT_P", name: "Q_A_ANG_PIT_P", type: "number", step: "0.1", value: "4.5" }, + { id: "Q_A_RAT_PIT_FF", name: "Q_A_RAT_PIT_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "Q_A_RAT_PIT_P", name: "Q_A_RAT_PIT_P", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_PIT_I", name: "Q_A_RAT_PIT_I", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_PIT_D", name: "Q_A_RAT_PIT_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "Q_A_RAT_PIT_D_FF", name: "Q_A_RAT_PIT_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "Q_A_RAT_PIT_FLTT", name: "Q_A_RAT_PIT_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "Q_A_RAT_PIT_FLTE", name: "Q_A_RAT_PIT_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "Q_A_RAT_PIT_FLTD", name: "Q_A_RAT_PIT_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // QYawTC Prefix Tables + "QYawTC": [ + { + id: "QYawTC", + inputs: [ + { id: "Q_PLT_Y_RATE_TC", name: "Q_PLT_Y_RATE_TC", type: "number", step: "0.01", value: "0.0" } + ] + } + ], + // QYawPIDS Prefix Tables + "QYawPIDS": [ + { + id: "QYawPIDS", + inputs: [ + { id: "Q_A_ANG_YAW_P", name: "Q_A_ANG_YAW_P", type: "number", step: "0.1", value: "4.5" }, + { id: "Q_A_RAT_YAW_FF", name: "Q_A_RAT_YAW_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "Q_A_RAT_YAW_P", name: "Q_A_RAT_YAW_P", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_YAW_I", name: "Q_A_RAT_YAW_I", type: "number", step: "0.01", value: "0.288" }, + { id: "Q_A_RAT_YAW_D", name: "Q_A_RAT_YAW_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "Q_A_RAT_YAW_D_FF", name: "Q_A_RAT_YAW_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "Q_A_RAT_YAW_FLTT", name: "Q_A_RAT_YAW_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "Q_A_RAT_YAW_FLTE", name: "Q_A_RAT_YAW_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "Q_A_RAT_YAW_FLTD", name: "Q_A_RAT_YAW_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // FWRoll Prefix Tables + "FWRoll": [ + { + id: "FWRollPIDS", + inputs: [ + { id: "RLL2SRV_TCONST", name: "RLL2SRV_TCONST", type: "number", step: "0.01", value: "0.25" }, + { id: "RLL_RATE_FF", name: "RLL_RATE_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "RLL_RATE_P", name: "RLL_RATE_P", type: "number", step: "0.01", value: "0.288" }, + { id: "RLL_RATE_I", name: "RLL_RATE_I", type: "number", step: "0.01", value: "0.288" }, + { id: "RLL_RATE_D", name: "RLL_RATE_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "RLL_RATE_D_FF", name: "RLL_RATE_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "RLL_RATE_FLTT", name: "RLL_RATE_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "RLL_RATE_FLTE", name: "RLL_RATE_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "RLL_RATE_FLTD", name: "RLL_RATE_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ], + // FWPitch Prefix Tables + "FWPitch": [ + { + id: "FWPitchPIDS", + inputs: [ + { id: "PTCH2SRV_TCONST", name: "PTCH2SRV_TCONST", type: "number", step: "0.01", value: "0.5" }, + { id: "PTCH_RATE_FF", name: "PTCH_RATE_FF", type: "number", step: "0.01", value: "0.0" }, + { id: "PTCH_RATE_P", name: "PTCH_RATE_P", type: "number", step: "0.01", value: "0.288" }, + { id: "PTCH_RATE_I", name: "PTCH_RATE_I", type: "number", step: "0.01", value: "0.288" }, + { id: "PTCH_RATE_D", name: "PTCH_RATE_D", type: "number", step: "0.0001", value: "0.0117" }, + { id: "PTCH_RATE_D_FF", name: "PTCH_RATE_D_FF", type: "number", step: "0.0001", value: "0.0" }, + { id: "PTCH_RATE_FLTT", name: "PTCH_RATE_FLTT", type: "number", step: "0.01", value: "1.77" }, + { id: "PTCH_RATE_FLTE", name: "PTCH_RATE_FLTE", type: "number", step: "0.01", value: "0" }, + { id: "PTCH_RATE_FLTD", name: "PTCH_RATE_FLTD", type: "number", step: "0.01", value: "20" } + ] + } + ] +}; + +// --- Configuration --- +// Set this variable to "", "Q", or "FW" to control which tables are loaded +//var activePrefix = "Roll"; +function buildAllTables() { + buildTables("RollPitchTC", "none"); + buildTables("YawTC", "none"); + buildTables("RollPIDS", "none"); + buildTables("PitchPIDS", "none"); + buildTables("YawPIDS", "none"); + buildTables("QRollPitchTC", "none"); + buildTables("QYawTC", "none"); + buildTables("QRollPIDS", "none"); + buildTables("QPitchPIDS", "none"); + buildTables("QYawPIDS", "none"); + buildTables("FWRoll", "none"); + buildTables("FWPitch", "none"); +} + +function buildTables(activePrefix, setdisplay) { + const container = document.getElementById('tables-container'); +// container.innerHTML = ''; // Clear previous content + + // Create and append tables for the active prefix + if (tableData[activePrefix]) { + tableData[activePrefix].forEach(tableDef => { + const div = document.createElement('div'); + div.id = tableDef.id; + div.style.display = setdisplay; // Or manage visibility as needed + + tableDef.inputs.forEach(inputDef => { + const p = document.createElement('p'); + const input = document.createElement('input'); + input.setAttribute('id', inputDef.id); + input.setAttribute('name', inputDef.name); + input.setAttribute('type', inputDef.type); + input.setAttribute('step', inputDef.step); + input.setAttribute('value', inputDef.value); + input.setAttribute('onchange', "calculate_freq_resp(this)") + + p.appendChild(input); + div.appendChild(p); + }); + + container.appendChild(div); + }); + } +} diff --git a/AnalyticTune/index.html b/AnalyticTune/index.html index 2ef52cab..0bfbb6f6 100644 --- a/AnalyticTune/index.html +++ b/AnalyticTune/index.html @@ -6,6 +6,7 @@ + @@ -198,260 +199,9 @@