Fix ansible-lint violations: FQCN, formatting, bugs, role renames
- Auto-fix FQCN, YAML formatting, jinja spacing, and free-form module syntax via ansible-lint --fix - Fix comments misplaced inside module args by the auto-fixer (bluetooth-monitor, pi_standard_setup, pi_musicmouse) - Fix notify: references left stale (lowercase) after handler names were re-cased, which would have silently broken reboot/restart handlers (pi_disable_onboard_bluetooth, pi_hifiberry_amp, pi_squeezelite, pi_standard_setup) - Fix a task in pis/debmatic-install.yml missing its module name (apt_repository), which caused a real syntax-check failure - Add missing play names, fix comment spacing, literal-compare idiom, and no-changed-when annotations - Delete unused/broken roles/better-shell-env (unreferenced, invalid YAML) - Rename all hyphenated role directories to underscore form to satisfy ansible-lint's role-name rule, updating every playbook/meta reference Remaining lint findings (var-naming, package-latest, risky-file-permissions, no-handler) intentionally left for follow-up per user decision.
This commit is contained in:
131
roles/bluetooth_monitor/files/filter.cpp
Normal file
131
roles/bluetooth_monitor/files/filter.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using real_t = double;
|
||||
|
||||
static constexpr real_t SPIKE_THRESHOLD = 1.0f; // Threshold for spike detection
|
||||
static constexpr int NUM_READINGS = 12; // Number of readings to keep track of
|
||||
|
||||
|
||||
class FilteredDistance {
|
||||
public:
|
||||
FilteredDistance(real_t minCutoff = 1e-1f, real_t beta = 1e-3, real_t dcutoff = 5e-3);
|
||||
void addMeasurement(real_t dist, real_t time_now_in_seconds);
|
||||
const real_t getMedianDistance() const;
|
||||
const real_t getDistance() const;
|
||||
const real_t getVariance() const;
|
||||
|
||||
bool hasValue() const { return lastTime != 0; }
|
||||
|
||||
private:
|
||||
real_t minCutoff;
|
||||
real_t beta;
|
||||
real_t dcutoff;
|
||||
real_t x, dx;
|
||||
real_t lastDist;
|
||||
real_t lastTime;
|
||||
|
||||
real_t getAlpha(real_t cutoff, real_t dT);
|
||||
|
||||
real_t readings[NUM_READINGS]; // Array to store readings
|
||||
int readIndex; // Current position in the array
|
||||
real_t total; // Total of the readings
|
||||
real_t totalSquared; // Total of the squared readings
|
||||
|
||||
void initSpike(real_t dist);
|
||||
real_t removeSpike(real_t dist);
|
||||
};
|
||||
|
||||
FilteredDistance::FilteredDistance(real_t minCutoff, real_t beta, real_t dcutoff)
|
||||
: minCutoff(minCutoff), beta(beta), dcutoff(dcutoff), x(0), dx(0), lastDist(0), lastTime(-1), total(0), totalSquared(0), readIndex(0) {
|
||||
}
|
||||
|
||||
void FilteredDistance::initSpike(real_t dist) {
|
||||
for (size_t i = 0; i < NUM_READINGS; i++) {
|
||||
readings[i] = dist;
|
||||
}
|
||||
total = dist * NUM_READINGS;
|
||||
totalSquared = dist * dist * NUM_READINGS; // Initialize sum of squared distances
|
||||
}
|
||||
|
||||
real_t FilteredDistance::removeSpike(real_t dist) {
|
||||
total -= readings[readIndex]; // Subtract the last reading
|
||||
totalSquared -= readings[readIndex] * readings[readIndex]; // Subtract the square of the last reading
|
||||
|
||||
readings[readIndex] = dist; // Read the sensor
|
||||
total += readings[readIndex]; // Add the reading to the total
|
||||
totalSquared += readings[readIndex] * readings[readIndex]; // Add the square of the reading
|
||||
|
||||
readIndex = (readIndex + 1) % NUM_READINGS; // Advance to the next position in the array
|
||||
|
||||
auto average = total / static_cast<real_t>(NUM_READINGS); // Calculate the average
|
||||
|
||||
if (std::fabs(dist - average) > SPIKE_THRESHOLD)
|
||||
return average; // Spike detected, use the average as the filtered value
|
||||
|
||||
return dist; // No spike, return the new value
|
||||
}
|
||||
|
||||
void FilteredDistance::addMeasurement(real_t dist, real_t time_now_in_seconds) {
|
||||
const bool initialized = lastTime >= 0;
|
||||
const real_t elapsed = time_now_in_seconds - lastTime;
|
||||
lastTime = time_now_in_seconds;
|
||||
|
||||
if (!initialized) {
|
||||
x = dist; // Set initial filter state to the first reading
|
||||
dx = 0; // Initial derivative is unknown, so we set it to zero
|
||||
lastDist = dist;
|
||||
initSpike(dist);
|
||||
} else {
|
||||
real_t dT = std::max(elapsed, real_t(0.05)); // Convert microseconds to seconds, enforce a minimum dT
|
||||
const real_t alpha = getAlpha(minCutoff, dT);
|
||||
const real_t dAlpha = getAlpha(dcutoff, dT);
|
||||
dist = removeSpike(dist);
|
||||
x += alpha * (dist - x);
|
||||
dx = dAlpha * ((dist - lastDist) / dT);
|
||||
lastDist = x + beta * dx;
|
||||
std::cout << "alpha=" << alpha <<
|
||||
" dAlpha=" << dAlpha <<
|
||||
" dist=" << dist <<
|
||||
" x=" << x <<
|
||||
" dx=" << dx <<
|
||||
" lastDist=" << lastDist <<
|
||||
std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
const real_t FilteredDistance::getDistance() const {
|
||||
return lastDist;
|
||||
}
|
||||
|
||||
real_t FilteredDistance::getAlpha(real_t cutoff, real_t dT) {
|
||||
real_t tau = 1.0f / (2 * M_PI * cutoff);
|
||||
return 1.0f / (1.0f + tau / dT);
|
||||
}
|
||||
|
||||
const real_t FilteredDistance::getVariance() const {
|
||||
auto mean = total / static_cast<real_t>(NUM_READINGS);
|
||||
auto meanOfSquares = totalSquared / static_cast<real_t>(NUM_READINGS);
|
||||
auto variance = meanOfSquares - (mean * mean); // Variance formula: E(X^2) - (E(X))^2
|
||||
if (variance < 0.0f) return 0.0f;
|
||||
return variance;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
FilteredDistance f;
|
||||
std::vector<real_t> values = {1.5, 2.9, 5.3, 15.1, 1.5, 2.5, 1.5, 2.9, 5.3, 15.1};
|
||||
|
||||
real_t time = 0.0;
|
||||
//std::cout << " result_cpp = [";
|
||||
for(int i=0; i < 1; ++i)
|
||||
for(auto value : values) {
|
||||
f.addMeasurement(value, time);
|
||||
time += 1.0;
|
||||
//std::cout << f.getDistance() << ", ";
|
||||
}
|
||||
//std::cout << "]" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
91
roles/bluetooth_monitor/files/filter.py
Normal file
91
roles/bluetooth_monitor/files/filter.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#from time import time
|
||||
import math
|
||||
from scipy import signal
|
||||
|
||||
# Taken from ESPresense C++ code
|
||||
class FilteredDistance:
|
||||
NUM_READINGS = 100
|
||||
SPIKE_THRESHOLD = 1.0
|
||||
|
||||
def __init__(self, min_cutoff : float = 1e-1, beta : float = 1e-3, dcutoff : float = 5e-3):
|
||||
self.min_cutoff = min_cutoff
|
||||
self.beta = beta
|
||||
self.dcutoff = dcutoff
|
||||
self.x = 0
|
||||
self.dx = 0
|
||||
self.last_dist = 0
|
||||
self.last_time = -1.0
|
||||
self.total = 0
|
||||
self.read_index = 0
|
||||
self.readings = []
|
||||
|
||||
def _init_spike(self, dist : float):
|
||||
self.readings = [dist] * self.NUM_READINGS
|
||||
self.total = sum(self.readings)
|
||||
|
||||
def _remove_spike(self, dist: float):
|
||||
self.total -= self.readings[self.read_index]
|
||||
|
||||
self.readings[self.read_index] = dist
|
||||
|
||||
self.total += dist
|
||||
|
||||
self.read_index = (self.read_index + 1) % self.NUM_READINGS
|
||||
average = self.total / self.NUM_READINGS
|
||||
if abs(dist - average) > self.SPIKE_THRESHOLD:
|
||||
return average # spike detected
|
||||
else:
|
||||
return dist
|
||||
|
||||
def _get_alpha(self, cutoff : float, dT : float):
|
||||
tau = 1 / (2 * math.pi * cutoff)
|
||||
return 1 / (1 + tau / dT)
|
||||
|
||||
def add_measurement(self, dist : float, time_now_in_seconds: float):
|
||||
initialized = (self.last_time >= 0.0)
|
||||
elapsed = time_now_in_seconds - self.last_time
|
||||
self.last_time = time_now_in_seconds
|
||||
if not initialized:
|
||||
self.x = dist
|
||||
self.dx = 0
|
||||
self.last_dist = dist
|
||||
self._init_spike(dist)
|
||||
else:
|
||||
dT = max(elapsed, 0.05)
|
||||
alpha = self._get_alpha(self.min_cutoff, dT)
|
||||
d_alpha = self._get_alpha(self.dcutoff, dT)
|
||||
dist = self._remove_spike(dist)
|
||||
self.x += alpha * (dist - self.x)
|
||||
self.dx = d_alpha * ((dist - self.last_dist) / dT)
|
||||
self.last_dist = self.x + self.beta * self.dx
|
||||
#print(f"{alpha=} {d_alpha=} {dist=} {self.x=} {self.dx=} {self.last_dist=}")
|
||||
|
||||
def get_distance(self):
|
||||
return self.last_dist
|
||||
|
||||
def run_test(times, values, **kwargs):
|
||||
f = FilteredDistance(**kwargs)
|
||||
result = []
|
||||
for t, value in zip(times, values):
|
||||
f.add_measurement(value, t)
|
||||
result.append(f.get_distance())
|
||||
return result
|
||||
|
||||
def smooth(y, box_pts):
|
||||
box = np.ones(box_pts)/box_pts
|
||||
y_smooth = np.convolve(y, box, mode='same')
|
||||
return y_smooth
|
||||
|
||||
if __name__ == "__main__":
|
||||
import numpy as np
|
||||
values = np.array([1] * 20 + [2, 4, 6, 7, 10, 16, 10, 13, 16, 24, 13] + [1] * 20 )
|
||||
|
||||
times = np.arange(0, len(values)) * 10
|
||||
result_default = run_test(times, values)
|
||||
result_beta1 = smooth(values, 6)
|
||||
import matplotlib.pyplot as plt
|
||||
plt.plot(times, values, label="raw")
|
||||
#plt.plot(times, result_default, marker="o", label="filtered")
|
||||
plt.plot(times, result_beta1, marker='x', label="altered")
|
||||
plt.legend()
|
||||
plt.show()
|
||||
BIN
roles/bluetooth_monitor/files/filtered
Executable file
BIN
roles/bluetooth_monitor/files/filtered
Executable file
Binary file not shown.
11
roles/bluetooth_monitor/files/my_btmonitor.service
Normal file
11
roles/bluetooth_monitor/files/my_btmonitor.service
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=My Bluetooth monitor
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
ExecStart=/usr/bin/my_btmonitor
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user