An OOP version of a GPS NMEA Sentence. See the output of the example codes from under the below class definition.
<?php
/**
* A class for generating and validating objects which represent GPS NMEA Sentences
*
* @author Jim Auldridge
* @link http://www.codepedia.com/1/The+GPRMC+Sentence
* @link http://www.codepedia.com/1/Calculating+and+Validating+NMEA+Checksums
*/
class GPS_NMEA_Sentence
{
protected $Sentence;
protected $Data;
protected $Internal_Checksum;
protected $Calculated_Checksum;
protected $Valid_Command_Words = array('GPRMC');
public $CommandWord = 'GPRMC';
public $SatelliteDerivedTime = '';
public $SatelliteFixStatus = '';
public $LatitudeDecimalDegrees = '';
public $LatitudeHemisphere = '';
public $LongitudeDecimalDegrees = '';
public $LongitudeHemisphere = '';
public $Speed = '';
public $Bearing = '';
public $UTCDate = '';
protected $Valid = false;
protected $in_error = false;
protected $error_str = '';
/**
* The constructor; If parapeter passed, implicitly calls ::setSentence()
*
* @access public
* @param OPTIONAL STRING $GPS_NMEA_Sentence
* @return void
*/
public function __construct($GPS_NMEA_Sentence = null)
{
if(!is_null($GPS_NMEA_Sentence))
{
$this->setSentence($GPS_NMEA_Sentence);
}
}
/**
* Set the ::Sentence property, implicitly calls ::parseSentence()
*
* @access public
* @param STRING $GPS_NMEA_Sentence --The value to which ::Sentence should be set
* @return OBJECT $this
*
*/
public function setSentence($GPS_NMEA_Sentence)
{
if(is_string($GPS_NMEA_Sentence) || empty($GPS_NMEA_Sentence))
$this->Sentence = $GPS_NMEA_Sentence;
else
$this->setError('Non string or empty value passed to '.__METHOD__);
$this->resetStatuses();
$this->parseSentence();
return $this;
}
/**
* Set the Command Word for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setCommandWord($value)
{
$this->CommandWord = $value;
return $this;
}
/**
* Set the Satellite Derived Time for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setSatelliteDerivedTime($value)
{
$this->SatelliteDerivedTime = $value;
return $this;
}
/**
* Set the Satellite Fix Status for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setSatelliteFixStatus($value)
{
$this->SatelliteFixStatus = $value;
return $this;
}
/**
* Set the Latitude Decimal Degrees for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setLatitudeDecimalDegrees($value)
{
$this->LatitudeDecimalDegrees = $value;
return $this;
}
/**
* Set the Latitude Hemisphere for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setLatitudeHemisphere($value)
{
$this->LatitudeHemisphere = $value;
return $this;
}
/**
* Set the Longitude Decimal Degrees for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setLongitudeDecimalDegrees($value)
{
$this->LongitudeDecimalDegrees = $value;
return $this;
}
/**
* Set the Longitude Hemisphere for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setLongitudeHemisphere($value)
{
$this->LongitudeHemisphere = $value;
return $this;
}
/**
* Set the Speed for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setSpeed($value)
{
$this->Speed = $value;
return $this;
}
/**
* Set the Bearing for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setBearing($value)
{
$this->Bearing = $value;
return $this;
}
/**
* Set the UTC Date for the Sentence
*
* @access public
* @param STRING $value
* return OBJECT $this
*/
public function setUTCDate($value)
{
$this->UTCDate = $value;
return $this;
}
/**
* Return the ::Sentence property or null if in error or not valid
*
* @access public
* @return MIXED
*/
public function getSentence()
{
return (!$this->inError() && $this->isValid()) ? $this->Sentence : null;
}
/**
* Return the Command Word property
*
* @access public
* @return STRING
*/
public function getCommandWord()
{
return $this->CommandWord;
}
/**
* Return the Satellite Derived Time property
*
* @access public
* @return STRING
*/
public function getSatelliteDerivedTime()
{
return $this->SatelliteDerivedTime;
}
/**
* Return the Satellite Fix Status property
*
* @access public
* @return STRING
*/
public function getSatelliteFixStatus()
{
return $this->SatelliteFixStatus;
}
/**
* Return the Latitude Decimal Degrees property
*
* @access public
* @return STRING
*/
public function getLatitudeDecimalDegrees()
{
return $this->LatitudeDecimalDegrees;
}
/**
* Return the Latitude Hemisphere property
*
* @access public
* @return STRING
*/
public function getLatitudeHemisphere()
{
return $this->LatitudeHemisphere;
}
/**
* Return the Longitude Decimal Degrees property
*
* @access public
* @return STRING
*/
public function getLongitudeDecimalDegrees()
{
return $this->LongitudeDecimalDegrees;
}
/**
* Return the Longitude Hemisphere property
*
* @access public
* @return STRING
*/
public function getLongitudeHemisphere()
{
return $this->LongitudeHemisphere;
}
/**
* Return the Speed property
*
* @access public
* @return STRING
*/
public function getSpeed()
{
return $this->Speed;
}
/**
* Return the Bearing property
*
* @access public
* @return STRING
*/
public function getBearing()
{
return $this->Bearing;
}
/**
* Return the UTC Date property
*
* @access public
* @return STRING
*/
public function getUTCDate()
{
return $this->UTCDate;
}
/**
* Extract and validate ::Sentence string
*
* @access public
* @return OBJECT $this
*/
public function parseSentence()
{
if(!$this->inError())
{
$this->extractDataFromSentence();
$this->calculateChecksum();
$this->extractWordsFromData();
$this->extractInternalChecksum();
$this->Valid = !!($this->compareChecksums() && $this->validCommandWord());
}
return $this;
}
/**
* Compile properties into a GPS NMEA Sentence string, validate them and write to ::Sentence property. If you have been modifying the object and are having trouble even though you believe everything should be correct, try calling ::resetStatuses();
*
* @access public
* @return OBJECT $this
*/
public function writeSentence()
{
if(!$this->inError())
{
$this->Data = "$this->CommandWord,$this->SatelliteDerivedTime,$this->SatelliteFixStatus,$this->LatitudeDecimalDegrees,$this->LatitudeHemisphere,$this->LongitudeDecimalDegrees,$this->LongitudeHemisphere,$this->Speed,$this->Bearing,$this->UTCDate";
$this->calculateChecksum();
$this->Internal_Checksum = $this->Calculated_Checksum;
$this->Valid = !!($this->compareChecksums() && $this->validCommandWord());
$this->Sentence = '$'.$this->Data.'*'.$this->Internal_Checksum;
}
return $this;
}
/**
* Return ::Valid property (which is set when data checks are run--used to know if your data is currently good or bad)
*
* @access public
* @return BOOLEAN
*/
public function isValid()
{
return $this->Valid;
}
/**
* Return ::in_error property (which is true when an error has occured)
*
* @access public
* @return BOOLEAN
*/
public function inError()
{
return $this->in_error;
}
/**
* Return ::error_str property when inError, or null if not
*
* @access public
* @return MIXED
*/
public function getErrorStr()
{
return $this->in_error ? $this->error_str : null;
}
/**
* Write ::error_str property and set ::in_error to true
*
* @access protected
* @return void
*/
protected function setError($error_str)
{
$this->in_error = true;
$this->error_str = $error_str;
}
/**
* Reset the status properties
*
* @access public
* @return OBJECT $this
*/
public function resetStatuses()
{
$this->Valid = false;
$this->in_error = false;
$this->error_str = '';
}
/**
* Extract data portion of sentence and set it to ::Data property or setError on failure
*
* @access protected
* @return void
*/
protected function extractDataFromSentence()
{
if(!$this->inError())
{
preg_match('/^\$([^*]+)\*/',$this->Sentence,$GPS_NMEA_Data);
if(is_array($GPS_NMEA_Data) && !empty($GPS_NMEA_Data[1]))
$this->Data = $GPS_NMEA_Data[1];
else
$this->setError(__METHOD__.' could not extract data from '.__CLASS__.'::Sentence');
}
}
/**
* Extract GPS NMEA 'words' from ::Data
*
* @access protected
* @return void
*/
protected function extractWordsFromData()
{
if(!$this->inError())
{
$words = explode(',',$this->Data);
$wordnames = array('CommandWord','SatelliteDerivedTime','SatelliteFixStatus','LatitudeDecimalDegrees','LatitudeHemisphere','LongitudeDecimalDegrees','LongitudeHemisphere','Speed','Bearing','UTCDate');
for($i=0;$i<count($wordnames);$i++)
$this->$wordnames[$i] = empty($words[$i]) ? '' : $words[$i];
}
}
/**
* Extract checksum portion of sentence and set it to ::Internal_Checksum property or setError on failure
*
* @access protected
* @return void
*/
protected function extractInternalChecksum()
{
if(!$this->inError())
{
preg_match('/\*([^,]+)(,|$)/',$this->Sentence,$GPS_NMEA_Internal_Checksum);
if(is_array($GPS_NMEA_Internal_Checksum) && !empty($GPS_NMEA_Internal_Checksum[1]))
$this->Internal_Checksum = $GPS_NMEA_Internal_Checksum[1];
else
$this->setError(__METHOD__.' could not extract internal checksum from '.__CLASS__.'::Sentence');
}
}
/**
* Calculate checksum of ::Data and set ::Calculated_Checksum property
*
* @access protected
* @return void
*/
protected function calculateChecksum()
{
if(!$this->inError())
{
$Calculated_Checksum = 0;
for($i=0;$i<strlen($this->Data);$i++)
$Calculated_Checksum = ($Calculated_Checksum ^ ord($this->Data{$i}));
$this->Calculated_Checksum = dechex($Calculated_Checksum);
}
}
/**
* Compare ::Internal_Checksum and ::Calculated_Checksum properties. On good compare return true, on bad return false and setError
*
* @access protected
* @return void
*/
protected function compareChecksums()
{
$return_value = false;
if(!$this->inError())
{
if($this->Internal_Checksum === $this->Calculated_Checksum)
$return_value = true;
else
$this->setError(__METHOD__.' found checksum mismatch.');
}
return $return_value;
}
/**
* Ensure ::CommandWord property matches what a GPS NMEA Sentence Command Word should be. On good compare return true, on bad return false and setError
*
* @access protected
* @return void
*/
protected function validCommandWord()
{
$return_value = false;
if(!$this->inError())
{
if(isset($this->CommandWord) && in_array($this->CommandWord,$this->Valid_Command_Words))
$return_value = true;
else
$this->setError(__METHOD__.' detected empty or incorrect CommandWord in '.__CLASS__.'::Sentence');
}
return $return_value;
}
}
/* ***** EXAMPLES ********* */
//Two valid GPS NMEA Sentence Strings
$GPS_Serial_Output_1 = '$GPRMC,030748.000,A,4553.0146,N,07352.1516,W,0.25,0.00,171107,,,A*76,AUTO';
$GPS_Serial_Output_2 = '$GPRMC,155123.000,A,4043.8432,N,07359.7653,W,0.15,83.25,200407,,*28';
//And a third that is bad
$GPS_Serial_Output_3 = '$GPRMC,155213.000,A,4053.8432,N,07359.7653,W,0.15,83.25,200407,,*49';
//
$oGPS_NMEA_Sentence = new GPS_NMEA_Sentence($GPS_Serial_Output_1);
echo "\n\n<pre>\n";var_dump($oGPS_NMEA_Sentence);echo "\n</pre>\n\n";
$oGPS_NMEA_Sentence->setSentence($GPS_Serial_Output_2);
echo "\n\n<pre>\n";var_dump($oGPS_NMEA_Sentence);echo "\n</pre>\n\n";
$oGPS_NMEA_Sentence->setSentence($GPS_Serial_Output_3);
echo "\n\n<pre>\n";var_dump($oGPS_NMEA_Sentence);echo "\n</pre>\n\n";
//Make a new empty GPS NMEA Sentence Object
$brandNew = new GPS_NMEA_Sentence();
//Set some properties on it and write the sentence property
$brandNew->setLatitudeHemisphere('E')->setLongitudeHemisphere('S')->writeSentence();
//dump out the object to see what it looks like
echo "\n\n<pre>\n";var_dump($brandNew);echo "\n</pre>\n\n";
//echo the sentence property
echo $brandNew->getSentence().'<br />';
//Make a new sentence with the otput string of the original to see if it validates
$testingBrandNew = new GPS_NMEA_Sentence($brandNew->getSentence());
//Dump it and see what we got
echo "\n\n<pre>\n";var_dump($testingBrandNew);echo "\n</pre>\n\n";
?>