knock sensing peak detect (#2910)

* knock peak detect

* comments

* comment

* initialize

* test

* fix warnings while we're here

* those are functions, not values

* ram
This commit is contained in:
Matthew Kennedy 2021-07-06 18:44:59 -07:00 committed by GitHub
parent df98194106
commit 5e08907590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 10 deletions

View File

@ -11,7 +11,7 @@
#include "closed_loop_controller.h"
#include "pid.h"
class IPwm;
struct IPwm;
class BoostController : public ClosedLoopController<float, percent_t> {
public:

View File

@ -6,7 +6,7 @@
struct gppwm_channel;
class OutputPin;
class IPwm;
struct IPwm;
class ValueProvider3D;
class GppwmChannel {

View File

@ -698,7 +698,7 @@ void initEngineContoller(DECLARE_ENGINE_PARAMETER_SUFFIX) {
* UNUSED_SIZE constants.
*/
#ifndef RAM_UNUSED_SIZE
#define RAM_UNUSED_SIZE 1500
#define RAM_UNUSED_SIZE 1400
#endif
#ifndef CCM_UNUSED_SIZE
#define CCM_UNUSED_SIZE 300

View File

@ -1,4 +1,3 @@
#include "global.h"
#include "engine.h"
#include "biquad.h"
@ -7,6 +6,7 @@
#include "knock_logic.h"
#include "software_knock.h"
#include "thread_priority.h"
#include "peak_detect.h"
#if EFI_SOFTWARE_KNOCK
@ -15,9 +15,10 @@ EXTERN_ENGINE;
#include "knock_config.h"
#include "ch.hpp"
NO_CACHE adcsample_t sampleBuffer[2000];
int8_t currentCylinderIndex = 0;
Biquad knockFilter;
static NO_CACHE adcsample_t sampleBuffer[2000];
static int8_t currentCylinderIndex = 0;
static efitick_t lastKnockSampleTime = 0;
static Biquad knockFilter;
static volatile bool knockIsSampling = false;
static volatile bool knockNeedsProcess = false;
@ -141,6 +142,7 @@ void startKnockSampling(uint8_t cylinderIndex) {
currentCylinderIndex = cylinderIndex;
adcStartConversionI(&KNOCK_ADC, conversionGroup, sampleBuffer, sampleCount);
lastKnockSampleTime = getTimeNowNt();
}
class KnockThread : public ThreadController<256> {
@ -164,6 +166,10 @@ void initSoftwareKnock() {
}
}
using PD = PeakDetect<float, MS2NT(100)>;
static PD peakDetectors[12];
static PD allCylinderPeakDetector;
void processLastKnockEvent() {
if (!knockNeedsProcess) {
return;
@ -190,16 +196,27 @@ void processLastKnockEvent() {
sumSq += filtered * filtered;
}
// take a local copy
auto lastKnockTime = lastKnockSampleTime;
// We're done with inspecting the buffer, another sample can be taken
knockNeedsProcess = false;
// mean of squares (not yet root)
float meanSquares = sumSq / localCount;
// RMS
float db = 10 * log10(meanSquares);
tsOutputChannels.knockLevels[currentCylinderIndex] = roundf(clampF(-100, db, 100));
tsOutputChannels.knockLevel = db;
// clamp to reasonable range
db = clampF(-100, db, 100);
// Pass through peak detector
float cylPeak = peakDetectors[currentCylinderIndex].detect(db, lastKnockTime);
tsOutputChannels.knockLevels[currentCylinderIndex] = roundf(cylPeak);
tsOutputChannels.knockLevel = allCylinderPeakDetector.detect(db, lastKnockTime);
knockNeedsProcess = false;
}
void KnockThread::ThreadTask() {

View File

@ -0,0 +1,25 @@
#pragma once
#include "rusefi_types.h"
/**
* Stores the recent peak value, preventing loss of intermittent peaks in a signal.
*/
template <typename TValue, efitick_t TTimeoutPeriod>
class PeakDetect {
public:
TValue detect(TValue currentValue, efitick_t nowNt) {
if ((nowNt > m_lastPeakTime + TTimeoutPeriod) || // if timed out
(currentValue > m_peak)) { // or current is higher than the previous peak
// store new peak and time
m_peak = currentValue;
m_lastPeakTime = nowNt;
}
return m_peak;
}
private:
TValue m_peak = std::numeric_limits<TValue>::min();
efitick_t m_lastPeakTime = std::numeric_limits<efitick_t>::min();
};

View File

@ -24,6 +24,7 @@
#include "io_pins.h"
#include "efi_gpio.h"
#include "efilib.h"
#include "peak_detect.h"
#include "gtest/gtest.h"
@ -490,3 +491,24 @@ TEST(util, datalogging) {
// printf("Got [%s]\r\n", LOGGING_BUFFER);
// ASSERT_STREQ("rusEfiVersion,776655@321ID DEFAULT_FRANKENSO 239,", LOGGING_BUFFER);
}
TEST(util, PeakDetect) {
constexpr int startTime = 50;
constexpr int timeout = 100;
PeakDetect<int, timeout> dut;
// Set a peak
EXPECT_EQ(dut.detect(1000, startTime), 1000);
// Smaller value at the same time is ignored
EXPECT_EQ(dut.detect(500, startTime), 1000);
// Larger value at the same time raises the peak
EXPECT_EQ(dut.detect(1500, startTime), 1500);
// Small value at almost the timeout is ignored
EXPECT_EQ(dut.detect(500, startTime + timeout - 1), 1500);
// Small value past the timeout is used
EXPECT_EQ(dut.detect(500, startTime + timeout + 1), 500);
}