Arduino GPS-based Lap Timer Revisited

GPS-based lap timing code is located here. The source code provided is an MSVC program used for testing purposes. This is simply the basic algorithms needed to function. Very little error checking is performed by the program. It can easily be modified to work on a laptop using a GPS connected through the USB. With minimal modification, it could also be adapted for use on a microcontroller. A text-based file of GPS RMC sentences recorded from several laps of the Portland International Raceway circuit can be used with the program and is located in my github repository.

This is a work in progress [updated on 1/13/2020].

The main loop of the program looks like this:

	// Main gps string processing loop.
	while (1)
	{
		if (!GetRMCSentence(port, tokens))
		{
			std::cout << error.GetDescription() << std::endl;
			continue;
		}

		// Previous position gps time stamp.
		float prevTimeStamp = timeStamp;
		
		// Confirm sentence is sequential.
		timeStamp = atof(tokens[RMC_TIME]);
		if (!Equal(timeStamp, prevTimeStamp + GPS_UPDATE_PERIOD) && !Equal(timeStamp, prevTimeStamp + 40. + GPS_UPDATE_PERIOD))
		{
			error.SetError(err::ID::TIME_STAMP);
			std::cout << error.GetDescription() << std::endl;
			continue;
		}

		// Get current track position (lat, long).
		if (tokens[RMC_LATITUDE] != nullptr || tokens[RMC_LONGITUDE] != nullptr)
		{
			char temp[12];

			GeoCopy(tokens[RMC_LATITUDE], temp, LATITUDE);
			track.p1.x = atof(temp);
			GeoCopy(tokens[RMC_LONGITUDE], temp, LONGITUDE);
			track.p1.y = atof(temp);
		}
		else
			continue;

		// Ignore gps sentences for 1 second after crossing start/finish.
		if (hzCounter < GPS_UPDATE_FREQUENCY)
		{
			hzCounter++;
			// Prepare for next iteration.
			track.p0.x = track.p1.x;
			track.p0.y = track.p1.y;
			continue;
		}
		
		// Heading sanity check & check if crossed start/finish line?
		if (Within30(startHeading, (uint16_t)atol(tokens[RMC_TRACK])) && LineIntersection(track))
		{
			point_t intersectPoint;

			// Calculate track/start line intersection point.
			IntersectPoint(track.p0, track.p1, &intersectPoint);

			// Overall length of this track segment.
			float totDist = Distance(track.p0, track.p1);

			// Length from start line intersection point to track segment end point.
			float segDist = Distance(intersectPoint, track.p1);

			// Calculate startline crossing time for this and next lap.
			float xTime = timeStamp - (GPS_UPDATE_PERIOD * (segDist / totDist));
			lapData[numLaps].setStop(xTime);
			lapData[numLaps + 1].setStart(xTime);

			// Determine current lap stats.
			DisplayTime(numLaps + 1, lapData[numLaps].getTime());
			if (numLaps > 0)
				DisplayTime(bestTime.first + 1, bestTime.second);

			// Is this lap a new best?
			if (numLaps == 0 || lapData[numLaps].getTime() < bestTime.second)
			{
				// Announce new fast lap.
				std::cout << " << Fast Lap";
				bestTime = std::make_pair(numLaps, lapData[numLaps].getTime());
			}
			std::cout << "\n";

			// Increment counters.
			numLaps++;
			hzCounter = 1;
		}

		// Prepare for next iteration.
		track.p0.x = track.p1.x;
		track.p0.y = track.p1.y;
	}

About Jim Eli

µC experimenter
This entry was posted in arduino, c and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s