2 Copyright (C) 2012 Intel Corporation
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "timestamp.h"
21 #include "serialport.hpp"
22 #include "bluetooth.hpp"
25 #include <boost/assert.hpp>
26 #include <boost/algorithm/string.hpp>
33 #include "abstractpropertytype.h"
35 #define GPSTIME "GpsTime"
38 inline T2 lexical_cast(const std::string &in) {
49 Location(AbstractRoutingEngine* re, std::string uuid);
51 void parse(std::string gprmc);
53 VehicleProperty::LatitudeType latitude()
58 VehicleProperty::LongitudeType longitude()
63 VehicleProperty::AltitudeType altitude()
68 VehicleProperty::DirectionType direction()
73 VehicleProperty::VehicleSpeedType speed()
78 BasicPropertyType<double> gpsTime()
83 std::list<AbstractPropertyType*> fix()
85 std::list<AbstractPropertyType*> l;
87 l.push_back(&mLatitude);
88 l.push_back(&mLongitude);
89 l.push_back(&mAltitude);
90 l.push_back(&mDirection);
92 l.push_back(&mGpsTime);
99 void parseGprmc(string gprmc);
100 void parseGpgga(string gpgga);
102 void parseTime(std::string h, std::string m, std::string s, string dd, string mm, string yy);
103 void parseLatitude(std::string d, std::string m, std::string ns);
104 void parseLongitude(std::string d, string m, string ew);
105 void parseSpeed(std::string spd);
106 void parseDirection(std::string dir);
107 void parseAltitude(std::string alt);
109 double degsToDecimal(double degs);
113 VehicleProperty::LatitudeType mLatitude;
114 VehicleProperty::LongitudeType mLongitude;
115 VehicleProperty::AltitudeType mAltitude;
116 VehicleProperty::DirectionType mDirection;
117 VehicleProperty::VehicleSpeedType mSpeed;
118 BasicPropertyType<double> mGpsTime;
124 AbstractRoutingEngine* routingEngine;
128 Location::Location(AbstractRoutingEngine* re, std::string uuid)
129 :mLatitude(0), mLongitude(0), mAltitude(0), mDirection(0), mSpeed(0), mGpsTime(GPSTIME,0), isActive(false), routingEngine(re), mUuid(uuid)
134 void Location::parse(string nmea)
136 if(boost::algorithm::starts_with(nmea,"GPRMC"))
140 else if(boost::algorithm::starts_with(nmea,"GPGGA"))
146 DebugOut(DebugOut::Warning)<<"unknown/unhandled message: "<<nmea<<endl;
150 void Location::parseGprmc(string gprmc)
152 DebugOut(7)<<"parsing gprmc message"<<endl;
154 std::vector<std::string> tokens;
155 boost::split(tokens, gprmc, boost::is_any_of(","));
162 parseTime(tokens[1].substr(0,2),tokens[1].substr(2,2),tokens[1].substr(4,2),tokens[9].substr(0,2),tokens[9].substr(2,2),tokens[9].substr(4,2));
164 parseLatitude(tokens[3], "", tokens[4]);
165 parseLongitude(tokens[5], "", tokens[6]);
166 parseSpeed(tokens[7]);
167 parseDirection(tokens[8]);
171 void Location::parseGpgga(string gpgga)
174 std::vector<std::string> tokens;
175 boost::split(tokens, gpgga, boost::is_any_of(","));
177 if(tokens.size() != 15)
179 DebugOut()<<"Invalid GPGGA message: "<<gpgga<<endl;
183 parseLatitude(tokens[2],"",tokens[3]);
184 parseLongitude(tokens[4],"",tokens[5]);
189 else isActive = false;
191 parseAltitude(tokens[9]);
194 void Location::parseTime(string h, string m, string s, string dd, string mm, string yy)
199 t.tm_hour = boost::lexical_cast<int>(h);
200 t.tm_min = boost::lexical_cast<int>(m);
201 t.tm_sec = boost::lexical_cast<int>(s);
202 t.tm_mday = boost::lexical_cast<int>(dd);
203 t.tm_mon = boost::lexical_cast<int>(mm);
204 t.tm_year = boost::lexical_cast<int>(yy) + 100;
206 time_t time = mktime(&t);
208 BasicPropertyType<double> temp(GPSTIME,(double)time);
213 routingEngine->updateProperty(&mGpsTime, mUuid);
218 DebugOut(DebugOut::Warning)<<"Failed to parse time "<<endl;
222 void Location::parseLatitude(string d, string m, string ns)
229 double degs = boost::lexical_cast<double>(d + m);
230 double dec = degsToDecimal(degs);
235 VehicleProperty::LatitudeType temp(dec);
237 if(mLatitude != temp)
240 routingEngine->updateProperty(&mLatitude, mUuid);
245 DebugOut(DebugOut::Warning)<<"Failed to parse latitude"<<endl;
249 void Location::parseLongitude(string d, string m, string ew)
253 if(d.empty()) return;
255 double degs = boost::lexical_cast<double>(d + m);
256 double dec = degsToDecimal(degs);
261 VehicleProperty::LongitudeType temp(dec);
263 if(mLongitude != temp)
266 routingEngine->updateProperty(&mLongitude, mUuid);
271 DebugOut(DebugOut::Warning)<<"failed to parse longitude"<<endl;
275 void Location::parseSpeed(string spd)
279 double s = boost::lexical_cast<double>(spd);
283 VehicleProperty::VehicleSpeedType temp(s);
287 routingEngine->updateProperty(&mSpeed, mUuid);
292 DebugOut(DebugOut::Warning)<<"failed to parse speed"<<endl;
296 void Location::parseDirection(string dir)
299 uint16_t d = boost::lexical_cast<double>(dir);
301 VehicleProperty::DirectionType temp(d);
302 if(mDirection != temp)
305 routingEngine->updateProperty(&mDirection, mUuid);
310 DebugOut(DebugOut::Warning)<<"Failed to parse direction: "<<dir<<endl;
314 void Location::parseAltitude(string alt)
318 if(alt.empty()) return;
320 double a = boost::lexical_cast<double>(alt);
322 VehicleProperty::AltitudeType temp(a);
323 if(mAltitude != temp)
326 routingEngine->updateProperty(&mAltitude, mUuid);
329 mAltitude = VehicleProperty::AltitudeType(a);
333 DebugOut(DebugOut::Warning)<<"failed to parse altitude"<<endl;
337 double Location::degsToDecimal(double degs)
340 double min = 100.0 * modf(degs / 100.0, °);
341 return deg + (min / 60.0);
344 bool readCallback(GIOChannel *source, GIOCondition condition, gpointer data)
346 // DebugOut(5) << "Polling..." << condition << endl;
348 if(condition & G_IO_ERR)
350 DebugOut(DebugOut::Error)<<"GpsNmeaSource polling error."<<endl;
353 if (condition & G_IO_HUP)
355 //Hang up. Returning false closes out the GIOChannel.
356 //printf("Callback on G_IO_HUP\n");
357 DebugOut(DebugOut::Warning)<<"socket hangup event..."<<endl;
361 GpsNmeaSource* src = static_cast<GpsNmeaSource*>(data);
368 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
370 return new GpsNmeaSource(routingengine, config);
374 GpsNmeaSource::GpsNmeaSource(AbstractRoutingEngine *re, map<string, string> config)
375 :AbstractSource(re,config), mUuid("33d86462-1708-4f78-a001-99ea8d55422b")
378 location =new Location(re, mUuid);
380 VehicleProperty::registerProperty(GPSTIME,[](){ return new BasicPropertyType<double>(GPSTIME,0); });
382 addPropertySupport(VehicleProperty::Latitude, Zone::None);
383 addPropertySupport(VehicleProperty::Longitude, Zone::None);
384 addPropertySupport(VehicleProperty::Altitude, Zone::None);
385 addPropertySupport(VehicleProperty::VehicleSpeed, Zone::None);
386 addPropertySupport(VehicleProperty::Direction, Zone::None);
387 addPropertySupport(GPSTIME, Zone::None);
392 if(config.find("test") != config.end())
394 Location location(routingEngine, mUuid);
395 location.parse("GPRMC,061211,A,2351.9605,S,15112.5239,E,000.0,053.4,170303,009.9,E*6E");
397 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
399 g_assert(location.latitude().toString() == "-23.86600833");
400 g_assert(location.gpsTime().toString() == "1050585131");
402 location.parse("GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47");
404 DebugOut(0)<<"alt: "<<location.altitude().toString()<<endl;
405 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
406 g_assert(location.altitude().toString() == "545.4");
407 g_assert(location.latitude().toString() == "48.1173");
409 location.parse("GPRMC,060136.00,A,3101.40475,N,12126.87095,E,0.760,,160114,,,A*74");
410 DebugOut(0)<<"lon: "<<location.longitude().toString()<<endl;
411 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
413 //Test incomplete message:
414 location.parse("GPRMC,023633.00,V,,,,,,,180314,,,N*75");
415 DebugOut(0)<<"lon: "<<location.longitude().toString()<<endl;
416 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
419 std::string btaddapter = config["bluetoothAdapter"];
421 if(config.find("baudrate")!= config.end())
423 baudrate = boost::lexical_cast<int>( config["baudrate"] );
426 if(config.find("device")!= config.end())
428 std::string dev = config["device"];
429 if(dev.find(":") != string::npos)
432 dev = bt.getDeviceForAddress(dev, btaddapter);
435 device = new SerialPort(dev);
439 if((static_cast<SerialPort*>(device))->setSpeed(baudrate))
440 DebugOut(DebugOut::Error)<<"Unsupported baudrate " << config["baudrate"] << endl;
445 DebugOut(DebugOut::Error)<<"Failed to open gps tty: "<<config["device"]<<endl;
450 DebugOut()<<"read from device: "<<device->read()<<endl;
452 GIOChannel *chan = g_io_channel_unix_new(device->fileDescriptor());
453 g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR),(GIOFunc)readCallback, this);
454 g_io_channel_set_close_on_unref(chan, true);
455 g_io_channel_unref(chan); //Pass ownership of the GIOChannel to the watch.
459 GpsNmeaSource::~GpsNmeaSource()
464 const string GpsNmeaSource::uuid()
470 void GpsNmeaSource::getPropertyAsync(AsyncPropertyReply *reply)
472 DebugOut()<<"GpsNmeaSource: getPropertyAsync called for property: "<<reply->property<<endl;
474 std::list<AbstractPropertyType*> f = location->fix();
476 for(auto property : f)
478 if(property->name == reply->property)
480 reply->success = true;
481 reply->value = property;
482 reply->completed(reply);
487 reply->success = false;
488 reply->error = AsyncPropertyReply::InvalidOperation;
489 reply->completed(reply);
492 void GpsNmeaSource::getRangePropertyAsync(AsyncRangePropertyReply *reply)
497 AsyncPropertyReply *GpsNmeaSource::setProperty(AsyncSetPropertyRequest request )
502 void GpsNmeaSource::subscribeToPropertyChanges(VehicleProperty::Property property)
504 mRequests.push_back(property);
507 PropertyList GpsNmeaSource::supported()
512 int GpsNmeaSource::supportedOperations()
517 void GpsNmeaSource::canHasData()
519 std::string data = device->read();
521 std::vector<std::string> lines;
523 boost::split(lines,data,boost::is_any_of("$"));
525 for(int i = 0; i < lines.size(); i++)
527 if(checksum(lines[i]))
538 /// we have a complete message. parse it!
539 DebugOut(7)<<"Complete message: "<<buffer<<endl;
540 location->parse(buffer);
543 DebugOut(7)<<"buffer: "<<buffer<<endl;
548 void GpsNmeaSource::unsubscribeToPropertyChanges(VehicleProperty::Property property)
550 mRequests.remove(property);
553 void GpsNmeaSource::addPropertySupport(VehicleProperty::Property property, Zone::Type zone)
555 mSupported.push_back(property);
557 std::list<Zone::Type> zones;
559 zones.push_back(zone);
561 PropertyInfo info(0, zones);
563 propertyInfoMap[property] = info;
566 bool GpsNmeaSource::checksum(std::string sentence)
568 if(sentence.empty() || sentence.length() < 4)
575 for(auto i : sentence)
579 if(i != '\n' || i != '\r')
583 std::string sentenceCheckStr = sentence.substr(sentence.length()-4,2);
587 int sentenceCheck = lexical_cast<int>(sentenceCheckStr);
589 return sentenceCheck == checksum;