/** * Pedestrian Dead Reckoning (PDR). * * PDR estimates position changes from walking by: * 1. Detecting steps from accelerometer data (peak detection) * 2. Estimating step length (fixed or cadence-adaptive) * 3. Converting step + heading to displacement (dx, dy) * * All functions are pure (no side effects) and accept explicit timestamps * for deterministic testing. */ /** * Displacement vector in meters. */ export interface StepDisplacement { /** Displacement in meters along east-west axis (positive = east) */ dx: number; /** Displacement in meters along north-south axis (positive = north) */ dy: number; } /** * State for step peak detection. * Tracks acceleration magnitude trend to detect step peaks. */ export interface PeakDetectionState { /** Whether acceleration magnitude was rising in the previous sample */ wasRising: boolean; /** Previous acceleration magnitude (m/s²) */ lastMagnitude: number; /** Previous net acceleration (magnitude - gravity) (m/s²) */ lastNetAcceleration: number; } /** * Result of step peak detection. */ export interface PeakDetectionResult { /** Whether a step peak was detected */ isPeak: boolean; /** Updated state for next detection cycle */ nextState: PeakDetectionState; } /** * Apply exponential smoothing to heading with proper angle wrapping. * * Prevents zigzag paths caused by heading jitter by smoothing small fluctuations. * Handles the discontinuity at 0°/360° correctly (e.g., 350° to 10° blends smoothly). * * @param currentSmoothed - Current smoothed heading (undefined if first sample) * @param newHeading - New raw heading measurement in degrees (0-360) * @param smoothingFactor - Factor from 0-1 (lower = smoother but slower to respond) * @returns Smoothed heading in degrees (0-360) * * @example * ```ts * // First sample - returns as-is * smoothHeading(undefined, 45, 0.3); // 45 * * // Small change - heavily smoothed * smoothHeading(45, 47, 0.3); // 45.6 * * // Crossing 0°/360° boundary * smoothHeading(350, 10, 0.3); // 356 (moves toward 10° the short way) * ``` */ export declare function smoothHeading(currentSmoothed: number | undefined, newHeading: number, smoothingFactor: number): number; /** * Normalize an angle to the range [0, 360). * * @param angle - Angle in degrees (any value) * @returns Normalized angle in degrees [0, 360) */ export declare function normalizeAngle(angle: number): number; /** * Detect step peaks from acceleration data. * * A step is detected when acceleration magnitude transitions from rising to falling, * indicating the heel strike of a walking step. Uses peak detection rather than * threshold crossing for better accuracy. * * @param state - Current peak detection state * @param currentMagnitude - Current acceleration magnitude (m/s²) * @param stepThreshold - Minimum net acceleration for step detection (m/s²) * @returns Detection result with isPeak flag and updated state * * @example * ```ts * let state = { wasRising: false, lastMagnitude: 9.8, lastNetAcceleration: 0 }; * * // Rising acceleration (not a peak) * state = detectStepPeak(state, 11.5, 1.0).nextState; // isPeak: false * * // Peak detected (rising → falling with sufficient acceleration) * const result = detectStepPeak(state, 10.0, 1.0); // isPeak: true * ``` */ export declare function detectStepPeak(state: PeakDetectionState, currentMagnitude: number, stepThreshold: number): PeakDetectionResult; /** * Calculate adaptive step length based on walking cadence. * * Faster walking cadence typically correlates with longer stride length. * This function adjusts step length proportionally to deviation from * normal walking cadence (~110 steps/min). * * @param baseStepLength - Base step length in meters * @param stepTimestamps - Array of recent step timestamps (ms) * @param nowMs - Current timestamp in milliseconds * @param windowMs - Time window for cadence calculation (ms) * @param adaptationFactor - How much to adapt (0 = none, 1 = full) * @returns Adapted step length in meters * * @example * ```ts * const timestamps = [0, 500, 1000, 1500, 2000]; // 120 steps/min * * // With adaptation enabled, faster cadence = longer steps * calculateAdaptiveStepLength(0.7, timestamps, 2000, 5000, 0.5); * // Returns ~0.73m (slightly longer due to fast cadence) * * // With adaptation disabled * calculateAdaptiveStepLength(0.7, timestamps, 2000, 5000, 0); * // Returns 0.7m (base length) * ``` */ export declare function calculateAdaptiveStepLength(baseStepLength: number, stepTimestamps: number[], nowMs: number, windowMs: number, adaptationFactor: number): number; /** * Calculate displacement from a step given heading and step length. * * Converts polar coordinates (heading + distance) to cartesian displacement (dx, dy). * Uses standard compass heading where 0° = North, 90° = East. * * @param headingDegrees - Heading in degrees (0 = North, 90 = East) * @param stepLength - Step length in meters * @returns Displacement vector { dx: east-west, dy: north-south } * * @example * ```ts * // Walking north * calculateStepDisplacement(0, 0.7); // { dx: 0, dy: 0.7 } * * // Walking east * calculateStepDisplacement(90, 0.7); // { dx: 0.7, dy: 0 } * * // Walking northeast * calculateStepDisplacement(45, 0.7); // { dx: ~0.495, dy: ~0.495 } * ``` */ export declare function calculateStepDisplacement(headingDegrees: number, stepLength: number): StepDisplacement; /** * Configuration for step suppression logic. */ export interface StepSuppressionConfig { /** Time window after motion stops where steps are still detected (ms) */ zuptDelayMs: number; /** Rotation rate threshold above which indicates tilting, not walking (deg/s) */ tiltThreshold: number; /** Minimum time between steps to prevent double-counting (ms) */ minStepInterval: number; } /** * Default step suppression configuration. */ export declare const DEFAULT_STEP_SUPPRESSION_CONFIG: StepSuppressionConfig; /** * Determine if step detection should be suppressed. * * Steps are suppressed when: * 1. Device has been stationary too long (ZUPT - Zero Velocity Update) * 2. Device is being tilted/rotated (not walking) * 3. Not enough time has passed since last step * 4. No heading is available (can't calculate displacement direction) * * @param lastMovingTimeMs - Timestamp when motion was last detected (ms) * @param lastStepTimeMs - Timestamp of last detected step (ms) * @param nowMs - Current timestamp (ms) * @param rotationMagnitude - Current rotation rate magnitude (deg/s) * @param hasHeading - Whether heading is available * @param config - Suppression configuration * @returns True if step detection should be suppressed * * @example * ```ts * const config = { zuptDelayMs: 500, tiltThreshold: 50, minStepInterval: 300 }; * * // Stationary for too long - suppress * shouldSuppressStepDetection(0, 0, 1000, 0, true, config); // true * * // Just moved, has heading - allow * shouldSuppressStepDetection(900, 0, 1000, 0, true, config); // false * * // Tilting device - suppress * shouldSuppressStepDetection(999, 0, 1000, 100, true, config); // true * * // Step too recent - suppress * shouldSuppressStepDetection(999, 900, 1000, 0, true, config); // true * ``` */ export declare function shouldSuppressStepDetection(lastMovingTimeMs: number, lastStepTimeMs: number, nowMs: number, rotationMagnitude: number, hasHeading: boolean, config: StepSuppressionConfig): boolean; /** * Calculate net acceleration (deviation from gravity). * * @param magnitude - Total acceleration magnitude (m/s²) * @returns Net acceleration excluding gravity (m/s²) */ export declare function calculateNetAcceleration(magnitude: number): number; /** * Calculate acceleration magnitude from 3D components. * * @param x - X-axis acceleration (m/s²) * @param y - Y-axis acceleration (m/s²) * @param z - Z-axis acceleration (m/s²) * @returns Magnitude of acceleration vector (m/s²) */ export declare function calculateAccelerationMagnitude(x: number, y: number, z: number): number; /** * Calculate rotation rate magnitude from 3D components. * * @param alpha - Rotation around Z-axis (deg/s) * @param beta - Rotation around X-axis (deg/s) * @param gamma - Rotation around Y-axis (deg/s) * @returns Magnitude of rotation rate vector (deg/s) */ export declare function calculateRotationMagnitude(alpha: number, beta: number, gamma: number): number; /** * Prune old timestamps from a step timestamp array. * * @param timestamps - Array of step timestamps (ms) * @param nowMs - Current timestamp (ms) * @param windowMs - Time window to keep (ms) * @returns Filtered array with only recent timestamps */ export declare function pruneOldTimestamps(timestamps: number[], nowMs: number, windowMs: number): number[];