@@ -16,6 +16,41 @@ function axisPeak(values) {
1616 return Math . max ( ...numbers . map ( ( value ) => Math . abs ( value ) ) ) ;
1717}
1818
19+ function percentile ( values , ratio ) {
20+ if ( ! values . length ) {
21+ return null ;
22+ }
23+ const sorted = [ ...values ] . sort ( ( left , right ) => left - right ) ;
24+ const index = Math . min (
25+ sorted . length - 1 ,
26+ Math . max ( 0 , Math . floor ( ( sorted . length - 1 ) * ratio ) )
27+ ) ;
28+ return sorted [ index ] ;
29+ }
30+
31+ function buildMotorChatterThresholds ( samples ) {
32+ const activeWindows = samples . filter (
33+ ( sample ) =>
34+ sample . motorChatter . activePairCount >= 4 &&
35+ ( sample . motorChatter . avgThrottle ?? 0 ) >= 18 &&
36+ ( sample . motorChatter . avgRpm ?? 0 ) >= 900
37+ ) ;
38+ const scores = activeWindows
39+ . map ( ( sample ) => sample . motorChatter . oscillationScore )
40+ . filter ( ( value ) => Number . isFinite ( value ) ) ;
41+ const normalizedDeltas = activeWindows
42+ . map ( ( sample ) => sample . motorChatter . avgNormalizedDelta )
43+ . filter ( ( value ) => Number . isFinite ( value ) ) ;
44+
45+ return {
46+ scoreThreshold : Math . max ( 0.055 , ( percentile ( scores , 0.97 ) ?? 0 ) * 0.92 ) ,
47+ normalizedDeltaThreshold : Math . max (
48+ 0.05 ,
49+ ( percentile ( normalizedDeltas , 0.95 ) ?? 0 ) * 0.9
50+ ) ,
51+ } ;
52+ }
53+
1954function summarizeSegment (
2055 type ,
2156 samples ,
@@ -317,6 +352,13 @@ export function detectAnalysisEvents(windowSlice, locale = "en", options = {}) {
317352 sample . rc . throttle !== undefined
318353 ? previous . rc . throttle - sample . rc . throttle
319354 : null ,
355+ throttleRise :
356+ previous ?. rc . throttle !== null &&
357+ previous ?. rc . throttle !== undefined &&
358+ sample . rc . throttle !== null &&
359+ sample . rc . throttle !== undefined
360+ ? sample . rc . throttle - previous . rc . throttle
361+ : null ,
320362 status,
321363 errorMagnitude : getErrorMagnitude ( sample . error ) ,
322364 turnInput : rcTurnInput ,
@@ -335,15 +377,16 @@ export function detectAnalysisEvents(windowSlice, locale = "en", options = {}) {
335377 } ;
336378 } ) ;
337379 const derivedWithMotorChatter = derived . map ( ( sample , index ) => {
338- const localStart = Math . max ( 0 , index - 2 ) ;
339- const localEnd = Math . min ( derived . length , index + 3 ) ;
380+ const localStart = Math . max ( 0 , index - 3 ) ;
381+ const localEnd = Math . min ( derived . length , index + 4 ) ;
340382 const motorChatter = getMotorChatterReviewSummary ( derived . slice ( localStart , localEnd ) ) ;
341383
342384 return {
343385 ...sample ,
344386 motorChatter,
345387 } ;
346388 } ) ;
389+ const motorChatterThresholds = buildMotorChatterThresholds ( derivedWithMotorChatter ) ;
347390
348391 const events = [
349392 ...segmentByPredicate ( derivedWithMotorChatter , EVENT_TYPES . HIGH_THROTTLE_STRAIGHT , ( sample ) => {
@@ -419,27 +462,45 @@ export function detectAnalysisEvents(windowSlice, locale = "en", options = {}) {
419462 derivedWithMotorChatter ,
420463 EVENT_TYPES . MOTOR_CHATTER ,
421464 ( sample ) => {
465+ const turnDemand = Math . max (
466+ sample . turnInput ?? 0 ,
467+ ( sample . setpointTurnInput ?? 0 ) * 0.75
468+ ) ;
469+ const throttlePunch =
470+ ( sample . throttleRise ?? 0 ) >= 10 ||
471+ ( ( sample . previousThrottle ?? 0 ) < 55 && ( sample . rc . throttle ?? 0 ) >= 62 ) ;
472+ const loadedDemand =
473+ turnDemand >= 135 ||
474+ throttlePunch ||
475+ ( ( sample . rc . throttle ?? 0 ) >= 72 && ( sample . motorChatter . flipRate ?? 0 ) >= 0.35 ) ;
476+
422477 if (
423478 sample . mode ?. armed === false ||
424479 sample . motorChatter . activePairCount < 4 ||
425- ( sample . motorChatter . avgThrottle ?? 0 ) < 18 ||
480+ ( sample . motorChatter . avgThrottle ?? 0 ) < 22 ||
426481 ( sample . motorChatter . avgRpm ?? 0 ) < 900 ||
427- sample . motorChatter . affectedMotorCount < 2
482+ ! loadedDemand
428483 ) {
429484 return false ;
430485 }
431486
432487 if (
433- ( sample . motorChatter . oscillationScore ?? 0 ) < 0.04 ||
434- ( sample . motorChatter . avgNormalizedDelta ?? 0 ) < 0.045
488+ ( sample . motorChatter . oscillationScore ?? 0 ) <
489+ motorChatterThresholds . scoreThreshold ||
490+ ( sample . motorChatter . avgNormalizedDelta ?? 0 ) <
491+ motorChatterThresholds . normalizedDeltaThreshold ||
492+ ( sample . motorChatter . affectedMotorCount < 2 &&
493+ ( sample . motorChatter . peakMotorSpreadRatio ?? 0 ) < 0.24 )
435494 ) {
436495 return false ;
437496 }
438497
439498 return {
440499 score :
441500 ( sample . motorChatter . oscillationScore ?? 0 ) * 1000 +
442- ( sample . motorChatter . flipRate ?? 0 ) * 100 ,
501+ ( sample . motorChatter . flipRate ?? 0 ) * 100 +
502+ turnDemand * 0.2 +
503+ ( ( sample . throttleRise ?? 0 ) * 4 ) ,
443504 } ;
444505 } ,
445506 locale
0 commit comments