I'm developing an altimeter to measure small height variations (0 to 1.5 m) using the smartphone barometer. However, I'm facing accuracy issues due to the sensor's sensitivity to device temperature and pressure fluctuations.
Current Approach
Currently, I calculate height based on the difference in pressure between an initial reading and the ongoing readings. I use a constant pressure-to-height ratio, experimentally determined to be around 8.7 m/hPa, to estimate the height change.
To minimize noise, I apply a moving average filter to both the initial and ongoing pressure readings. I also set a dead zone threshold to ignore minor fluctuations in pressure that don't significantly affect the height measurement.
Problems
Question
You can try something like this, it's not as precise as it could be, but it's a start.
export default function App() {
const [pressureReadings, setPressureReadings] = useState([]);
const [initialPressureReadings, setInitialPressureReadings] = useState([]);
const [initialPressure, setInitialPressure] = useState(null);
const [relativeHeight, setRelativeHeight] = useState(0);
const [isCollecting, setIsCollecting] = useState(false);
const [subscription, setSubscription] = useState(null);
const PRESSURE_TO_HEIGHT_RATIO = 9;
const SAMPLE_SIZE = 6;
const INITIAL_SAMPLE_SIZE = 12;
const FILTER_SIZE = 6;
const HEIGHT_STEP = 0.3; // Precisão de 0,4 m
const HEIGHT_TARGET = 1.2; // Altura alvo
const HEIGHT_TOLERANCE = 0.05; // Margem de erro
const DEAD_ZONE = 0.1; // Ignorar pequenas variações
// Função de filtro de média móvel
const applyMovingAverageFilter = (values, size) => {
if (values.length < size) return values;
const filtered = [];
for (let i = 0; i <= values.length - size; i++) {
const slice = values.slice(i, i + size);
const average = slice.reduce((sum, v) => sum + v, 0) / size;
filtered.push(average);
}
return filtered;
};
useEffect(() => {
if (!cameraPermission?.granted) {
requestCameraPermission();
}
}, []);
useEffect(() => {
(async () => {
const { status } = await MediaLibrary.requestPermissionsAsync();
requestMediaLibraryPermission(status === "granted");
})();
}, []);
useEffect(() => {
if (isCollecting) {
const newSubscription = Barometer.addListener((data) => {
setPressureReadings((prev) => {
const updatedReadings = [...prev, data.pressure];
if (initialPressure === null) {
setInitialPressureReadings((prevInit) => {
const updatedInitReadings = [...prevInit, data.pressure];
if (updatedInitReadings.length === INITIAL_SAMPLE_SIZE) {
const avgInitialPressure =
updatedInitReadings.reduce((sum, p) => sum + p, 0) /
INITIAL_SAMPLE_SIZE;
setInitialPressure(avgInitialPressure);
setMessage("Levante o celular!");
}
return updatedInitReadings.length > INITIAL_SAMPLE_SIZE
? updatedInitReadings.slice(1)
: updatedInitReadings;
});
}
return updatedReadings.length > SAMPLE_SIZE
? updatedReadings.slice(1)
: updatedReadings;
});
});
setSubscription(newSubscription);
return () => {
if (newSubscription) newSubscription.remove();
};
}
}, [isCollecting]);
useEffect(() => {
if (initialPressure !== null && pressureReadings.length === SAMPLE_SIZE) {
// Aplicando filtro de média móvel
const filteredReadings = applyMovingAverageFilter(
pressureReadings,
FILTER_SIZE
);
const avgPressure =
filteredReadings.reduce((sum, p) => sum + p, 0) /
filteredReadings.length;
let pressureDifference = initialPressure - avgPressure;
// Calculando altura com precisão de 0,4m
let calculatedHeight =
Math.round((pressureDifference * PRESSURE_TO_HEIGHT_RATIO) / HEIGHT_STEP) *
HEIGHT_STEP;
// Aplicação da Dead Zone (ignorar pequenas variações)
if (Math.abs(calculatedHeight - relativeHeight) > DEAD_ZONE) {
setRelativeHeight(calculatedHeight);
}
}
}, [pressureReadings]);