DEV Community

zhonghua
zhonghua

Posted on • Edited on

HarmonyOS Sports Development: Calculating Outdoor Sports Step Frequency and Stride Length, and Drawing Map Routes

HarmonyOS Sports Development: Calculating Outdoor Sports Step Frequency and Stride Length, and Drawing Map Routes

Foreword

In outdoor sports, step frequency (the number of steps per minute) and stride length (the distance of each step) are key indicators for measuring the efficiency and intensity of exercise. Whether you are a running enthusiast or a fitness expert, understanding these data can not only help you optimize your exercise performance but also effectively prevent sports injuries. However, how can we accurately calculate step frequency and stride length in the HarmonyOS system and display the sports trajectory in real time on the map? This article will combine practical development experience to deeply analyze the entire process from sensor data collection to core algorithm implementation and then to map route drawing, helping you master the precise calculation and visual display of outdoor sports data step by step.

Image description

I. Step Frequency and Stride Length: Key Indicators of Sports Data

Step frequency and stride length are two core indicators in sports data, which can intuitively reflect the rhythm and efficiency of exercise.

  1. Step Frequency: The Rhythm of Exercise

Step frequency refers to the number of steps per minute, usually used to measure the speed and rhythm of running or walking. A higher step frequency generally means a faster movement speed, but it may also lead to a decrease in efficiency due to excessive fatigue. The ideal step frequency varies from person to person. Generally speaking, a step frequency of 160-180 steps per minute is more ideal when running. For beginners, maintaining a stable step frequency is more important than pursuing a high step frequency because an excessively high step frequency may lead to physical fatigue and injury.

  1. Stride Length: The Efficiency of Exercise

Stride length refers to the length of each step, that is, the distance between the two feet. A larger stride length can increase the movement speed, but an excessively large stride length may lead to instability of the body's center of gravity and increase the risk of injury. Therefore, it is very important to reasonably control the stride length to improve the efficiency and safety of exercise. Generally speaking, the size of the stride length should be adjusted according to personal physical conditions and exercise habits. For example, people with a taller stature may have a larger stride length, but an excessively large stride length may cause excessive pressure on the knees and ankles.

II. HarmonyOS Step Counter Sensor: The Core Tool for Data Collection

In the HarmonyOS system, we can use the built-in step counter sensor (Pedometer) to obtain the step data during the exercise process. The step counter sensor can monitor the user's steps in real time and provide accurate step counting statistics. Below are the usage methods and key code analysis of the step counter sensor.

  1. Initialization of the Step Counter Sensor and Permission Request

Before using the step counter sensor, we need to request the motion permission and initialize the sensor service. Below is the relevant code:

import { sensor } from '@kit.SensorServiceKit';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { common } from '@kit.AbilityKit';

export class StepCounterService {
  private static instance: StepCounterService;
  private stepCount: number = 0; // Current cumulative step count
  private initialStepCount: number = 0; // Initial step count
  private isMonitoring: boolean = false; // Whether it is currently listening
  private listeners: Array<(data: StepData) => void> = [];
  private context: common.UIAbilityContext;

  private constructor(context: common.UIAbilityContext) {
    this.context = context;
  }

  public static getInstance(context: common.UIAbilityContext): StepCounterService {
    if (!StepCounterService.instance) {
      StepCounterService.instance = new StepCounterService(context);
    }
    return StepCounterService.instance;
  }

  // Request motion permission
  private async requestMotionPermission(): Promise<boolean> {
    const atManager = abilityAccessCtrl.createAtManager();
    try {
      const result = await atManager.requestPermissionsFromUser(
        this.context,
        ['ohos.permission.ACTIVITY_MOTION']
      );
      return result.permissions[0] === 'ohos.permission.ACTIVITY_MOTION' &&
        result.authResults[0] === 0;
    } catch (err) {
      console.error('Permission request failed:', err);
      return false;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we request the motion permission through abilityAccessCtrl and implement the initialization logic of the step counter sensor in the StepCounterService class. Permission request is a key step to ensure the normal operation of the sensor. If the user does not grant the relevant permission, the sensor will not be able to work properly.

  1. Real-time Monitoring of Step Data

After initializing the sensor, we need to monitor the changes in step data and update the step data in real time. Below is the key code for monitoring step data changes:

public async startStepCounter(): Promise<void> {
  if (this.isMonitoring) return;

  const hasPermission = await this.requestMotionPermission();
  if (!hasPermission) {
    throw new Error('Motion sensor permission not granted');
  }

  try {
    sensor.on(sensor.SensorId.PEDOMETER, (data: sensor.PedometerResponse) => {
      this.deviceTotalSteps = data.steps;

      if (this.initialStepCount === 0) {
        this.initialStepCount = data.steps;
      }

      const deltaSteps = this.deviceTotalSteps - this.initialStepCount;

      if (this.isPaused) {
        // When paused, only update the device total steps, do not change the business steps
      } else {
        // Calculate the total steps of all paused intervals
        const totalPausedSteps = this.pausedIntervals.reduce((sum, interval) => {
          return sum + (interval.end - interval.start);
        }, 0);

        this.stepCount = deltaSteps - totalPausedSteps;

        // Save the current step data
        this.lastStepData = {
          steps: this.stepCount
        };

        this.notifyListeners();
      }
    });

  } catch (error) {
    const e = error as BusinessError;
    console.error(`Step counter subscription failed: Code=${e.code}, Message=${e.message}`);
    throw error as Error;
  }
}

public pauseStepCounter(): void {
  if (!this.isMonitoring || this.isPaused) return;
  this.pausedIntervals.push({
    start: this.deviceTotalSteps,
    end: 0
  });
  this.isPaused = true;
}

public resumeStepCounter(): void {
  if (!this.isMonitoring || !this.isPaused) return;

  const lastInterval = this.pausedIntervals[this.pausedIntervals.length - 1];
  lastInterval.end = this.deviceTotalSteps;

  this.isPaused = false;
}

// Stop listening
public stopStepCounter(): void {
  if (!this.isMonitoring) return;

  this.stepCount = 0;
  this.initialStepCount = 0;
  this.isPaused = false;
  this.pausedIntervals = []; // Clear all paused records
  try {
    sensor.off(sensor.SensorId.PEDOMETER);
    this.isMonitoring = false;
  } catch (error) {
    const e = error as BusinessError;
    console.error(`Step counter unsubscribe failed: Code=${e.code}, Message=${e.message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we monitor the data changes of the step counter sensor through the sensor.on method and calculate the current steps in real time. At the same time, we also handle the logic of pausing and resuming listening to ensure the accuracy of step counting. This is very useful for pauses during the exercise process (such as waiting for traffic lights), which can avoid step counting errors caused by pauses.

III. Calculation Logic of Step Frequency and Stride Length

With the step data, we can calculate step frequency and stride length. Below is the core logic for calculating step frequency and stride length.

  1. Calculation of Step Frequency

The formula for calculating step frequency is:

[
\text{Step Frequency}=\frac{\text{Step Difference}}{\text{Time Difference}}\times 60
]

In the actual code, we calculate the step frequency through two consecutive trajectory points (containing time and step information). Below is the relevant code:

if (this.previousPoint && this.previousPoint.steps > 0) {
  const timeDiff = (point.timestamp - this.previousPoint.timestamp) / 1000; // Convert to seconds
  const stepDiff = point.steps - this.previousPoint.steps;

  if (timeDiff > 0 && stepDiff > 0) {
    // Calculate step frequency (steps per minute)
    const instantCadence = (stepDiff / timeDiff) * 60;
    // Use moving average to smooth step frequency data
    point.cadence = this.currentPoint.cadence * 0.7 + instantCadence * 0.3;
  } else {
    // If the time difference or step difference is 0, keep the data of the previous point
    point.cadence = this.currentPoint.cadence;
  }
} else {
  // The first point, initialize to 0
  point.cadence = 0;
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we calculate the instantaneous step frequency through the time difference and step difference of two trajectory points, and use the moving average to smooth the step frequency data to avoid excessive data fluctuations. The use of moving average can effectively reduce the step frequency data fluctuations caused by sensor errors or irregular user movements.

  1. Calculation of Stride Length

The formula for calculating stride length is:

[
\text{Stride Length}=\frac{\text{Distance Difference}}{\text{Step Difference}}
]

In the actual code, we calculate the stride length through two consecutive trajectory points (containing distance and step information). Below is the relevant code:

if (this.previousPoint && this.previousPoint.steps > 0) {
  const distance = this.calculateDistance(this.previousPoint, point) * 1000; // Convert to meters
  const stepDiff = point.steps - this.previousPoint.steps;

  if (stepDiff > 0) {
    // Calculate stride length (meters per step)
    const instantStride = distance / stepDiff;
    // Use moving average to smooth stride length data
    point.stride = this.currentPoint.stride * 0.7 + instantStride * 0.3;
  } else {
    // If the step difference is 0, keep the data of the previous point
    point.stride = this.currentPoint.stride;
  }
} else {
  // The first point, initialize to 0
  point.stride = 0;
}
Enter fullscreen mode Exit fullscreen mode

In the above code, we calculate the instantaneous stride length through the distance difference and step difference of two trajectory points, and use the moving average to smooth the stride length data. The calculation of stride length depends on the distance between trajectory points, so it is necessary to ensure the accuracy and density of trajectory points.

IV. Real-time Data Update and Display

During the outdoor sports process, we need to update the step frequency and stride length data in real time and display them to the user. This can not only help the user understand their current sports status in real time but also provide them with a basis for adjusting their sports rhythm and intensity. Below is the key code for data update and display:

  1. Data Update Logic

During the sports process, we calculate the step frequency and stride length in real time by monitoring the step counter sensor and trajectory point data, and store these data in the current trajectory point object. Below is the core code for data update:

// Update the trajectory point
this.currentPoint = point;

// Notify listeners
this.notifyListeners();

// Return the current total distance
return this.totalDistance;
Enter fullscreen mode Exit fullscreen mode

In the above code, the notifyListeners method is used to notify all registered listeners and pass the latest step frequency and stride length data to them. These listeners can be UI components or other services that require real-time data.

  1. Data Display

To display the step frequency and stride length data to the user, we need to update these data in real time on the application's interface. Below is a simple example code showing how to display step frequency and stride length in the UI:

// Assume there is a UI component to display step frequency and stride length
updateUI(point: RunPoint) {
  this.cadenceDisplayElement.textContent = `Step Frequency: ${point.cadence.toFixed(2)} steps/minute`;
  this.strideDisplayElement.textContent = `Stride Length: ${point.stride.toFixed(2)} meters/step`;
}
Enter fullscreen mode Exit fullscreen mode

In the above code, cadenceDisplayElement and strideDisplayElement are HTML elements used to display step frequency and stride length. By calling the toFixed method, we can format the values of step frequency and stride length to two decimal places, making the data more intuitive and readable.

V. Map Route Drawing: Real-time Trajectory Display

In addition to the calculation of step frequency and stride length, real-time drawing of sports trajectories is also an important feature of outdoor sports applications. Displaying the user's sports path on the map can help the user better understand their sports trajectory and also increase the fun of sports. Below is the core code for map route drawing:

  1. Initializing the Map

Before drawing the trajectory, we need to initialize the map. Here we take Baidu Map as an example:

import { MapController, Polyline, SysEnum } from '@ohos.maps';

// Initialize the map controller
this.mapController = new MapController(this.mapView);

// Set the map center point
this.mapController.setMapCenter({
  latitude: this.startLatitude,
  longitude: this.startLongitude,
});
Enter fullscreen mode Exit fullscreen mode

In the above code, MapController is the class used to control the map, and the setMapCenter method is used to set the map center point. We usually use the user's starting position as the map center point.

  1. Drawing the Trajectory Line

During the sports process, we need to draw the trajectory line according to the user's real-time location. Below is the core code for drawing the trajectory line:

// Create a trajectory point array
this.trackPoints = [];

// Each time a new trajectory point is obtained, add it to the trajectory point array
this.trackPoints.push({
  latitude: point.latitude,
  longitude: point.longitude,
});

// Create a trajectory line
this.polyline = new Polyline({
  points: this.trackPoints,
  strokeColor: '#f0f', // Trajectory line color
  strokeWidth: 20, // Trajectory line width
  lineJoin: SysEnum.LineJoinType.ROUND, // Trajectory line connection method
  lineCap: SysEnum.LineCapType.ROUND, // Trajectory line end point style
  isThined: true, // Whether to simplify the trajectory line
});

// Add the trajectory line to the map
this.mapController.addOverlay(this.polyline);
Enter fullscreen mode Exit fullscreen mode

In the above code, Polyline is the class used to draw the trajectory line, and the points property is an array containing the coordinates of the trajectory points. Each time a new trajectory point is obtained, we add this point to the trackPoints array and recreate the trajectory line object. By calling the addOverlay method, we can add the trajectory line to the map.

  1. Dynamically Updating the Trajectory

To achieve dynamic updating of the trajectory, we need to redraw the trajectory line each time a new trajectory point is obtained. Below is the core code for dynamically updating the trajectory:

// Update the trajectory point array
this.trackPoints.push({
  latitude: point.latitude,
  longitude: point.longitude,
});

this.mapController!.removeOverlay(this.polyline);

this.polyline.setPoints(this.trackPoints);

this.mapController!.addOverlay(this.polyline);
Enter fullscreen mode Exit fullscreen mode

In the above code, the removeOverlay method is used to clear the trajectory line's point array, the setPoints method is used to update the trajectory line's point array, and the addOverlay method is used to redraw the trajectory line. In this way, we can achieve real-time updating of the trajectory, allowing users to see their sports trajectory during the sports process.

VI. Summary

Through the HarmonyOS step counter sensor and map service, we have achieved accurate calculation of step frequency and stride length in outdoor sports, as well as real-time drawing of sports trajectories.

Top comments (0)