Remove performance from dbusconfig. fix crasher in varianttype.
[profile/ivi/automotive-message-broker.git] / plugins / gpsnmea / gpsnmea.cpp
1 /*
2 Copyright (C) 2012 Intel Corporation
3
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.
8
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.
13
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
17 */
18
19 #include "gpsnmea.h"
20 #include "timestamp.h"
21 #include "serialport.hpp"
22 #include "bluetooth.hpp"
23
24 #include <iostream>
25 #include <boost/assert.hpp>
26 #include <boost/algorithm/string.hpp>
27 #include <time.h>
28
29
30 using namespace std;
31
32 #include "debugout.h"
33 #include "abstractpropertytype.h"
34
35 #define GPSTIME "GpsTime"
36
37 template<typename T2>
38 inline T2 lexical_cast(const std::string &in) {
39         T2 out;
40         std::stringstream ss;
41         ss << std::hex << in;
42         ss >> out;
43         return out;
44 }
45
46 class Location
47 {
48 public:
49         Location(AbstractRoutingEngine* re, std::string uuid);
50
51         void parse(std::string gprmc);
52
53         VehicleProperty::LatitudeType latitude()
54         {
55                 return mLatitude;
56         }
57
58         VehicleProperty::LongitudeType longitude()
59         {
60                 return mLongitude;
61         }
62
63         VehicleProperty::AltitudeType altitude()
64         {
65                 return mAltitude;
66         }
67
68         VehicleProperty::DirectionType direction()
69         {
70                 return mDirection;
71         }
72
73         VehicleProperty::VehicleSpeedType speed()
74         {
75                 return mSpeed;
76         }
77
78         BasicPropertyType<double> gpsTime()
79         {
80                 return mGpsTime;
81         }
82
83         std::list<AbstractPropertyType*> fix()
84         {
85                 std::list<AbstractPropertyType*> l;
86
87                 l.push_back(&mLatitude);
88                 l.push_back(&mLongitude);
89                 l.push_back(&mAltitude);
90                 l.push_back(&mDirection);
91                 l.push_back(&mSpeed);
92                 l.push_back(&mGpsTime);
93
94                 return l;
95         }
96
97 private: ///methods:
98
99         void parseGprmc(string gprmc);
100         void parseGpgga(string gpgga);
101
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);
108
109         double degsToDecimal(double degs);
110
111 private:
112
113         VehicleProperty::LatitudeType mLatitude;
114         VehicleProperty::LongitudeType mLongitude;
115         VehicleProperty::AltitudeType mAltitude;
116         VehicleProperty::DirectionType  mDirection;
117         VehicleProperty::VehicleSpeedType mSpeed;
118         BasicPropertyType<double> mGpsTime;
119
120         bool isActive;
121
122         std::string mUuid;
123
124         AbstractRoutingEngine* routingEngine;
125
126 };
127
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)
130 {
131
132 }
133
134 void Location::parse(string nmea)
135 {
136         if(boost::algorithm::starts_with(nmea,"GPRMC"))
137         {
138                 parseGprmc(nmea);
139         }
140         else if(boost::algorithm::starts_with(nmea,"GPGGA"))
141         {
142                 parseGpgga(nmea);
143         }
144         else
145         {
146                 DebugOut(DebugOut::Warning)<<"unknown/unhandled message: "<<nmea<<endl;
147         }
148 }
149
150 void Location::parseGprmc(string gprmc)
151 {
152         DebugOut(7)<<"parsing gprmc message"<<endl;
153
154         std::vector<std::string> tokens;
155         boost::split(tokens, gprmc, boost::is_any_of(","));
156
157         if(tokens[2] == "A")
158         {
159                 isActive = true;
160         }
161
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));
163
164         parseLatitude(tokens[3], "", tokens[4]);
165         parseLongitude(tokens[5], "", tokens[6]);
166         parseSpeed(tokens[7]);
167         parseDirection(tokens[8]);
168
169 }
170
171 void Location::parseGpgga(string gpgga)
172 {
173
174         std::vector<std::string> tokens;
175         boost::split(tokens, gpgga, boost::is_any_of(","));
176
177         if(tokens.size() != 15)
178         {
179                 DebugOut()<<"Invalid GPGGA message: "<<gpgga<<endl;
180                 return;
181         }
182
183         parseLatitude(tokens[2],"",tokens[3]);
184         parseLongitude(tokens[4],"",tokens[5]);
185         if(tokens[6] != "0")
186         {
187                 isActive = true;
188         }
189         else isActive = false;
190
191         parseAltitude(tokens[9]);
192 }
193
194 void Location::parseTime(string h, string m, string s, string dd, string mm, string yy)
195 {
196         try
197         {
198                 tm t;
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;
205
206                 time_t time = mktime(&t);
207
208                 BasicPropertyType<double> temp(GPSTIME,(double)time);
209
210                 if(mGpsTime != temp)
211                 {
212                         mGpsTime = temp;
213                         routingEngine->updateProperty(&mGpsTime, mUuid);
214                 }
215         }
216         catch(...)
217         {
218                 DebugOut(DebugOut::Warning)<<"Failed to parse time "<<endl;
219         }
220 }
221
222 void Location::parseLatitude(string d, string m, string ns)
223 {
224         try
225         {
226                 if(d.empty() )
227                         return;
228
229                 double degs = boost::lexical_cast<double>(d + m);
230                 double dec = degsToDecimal(degs);
231
232                 if(ns == "S")
233                         dec *= -1;
234
235                 VehicleProperty::LatitudeType temp(dec);
236
237                 if(mLatitude != temp)
238                 {
239                         mLatitude = temp;\
240                         routingEngine->updateProperty(&mLatitude, mUuid);
241                 }
242         }
243         catch(...)
244         {
245                 DebugOut(DebugOut::Warning)<<"Failed to parse latitude"<<endl;
246         }
247 }
248
249 void Location::parseLongitude(string d, string m, string ew)
250 {
251         try
252         {
253                 if(d.empty()) return;
254
255                 double degs = boost::lexical_cast<double>(d + m);
256                 double dec = degsToDecimal(degs);
257
258                 if(ew == "W")
259                         dec *= -1;
260
261                 VehicleProperty::LongitudeType temp(dec);
262
263                 if(mLongitude != temp)
264                 {
265                         mLongitude = temp;\
266                         routingEngine->updateProperty(&mLongitude, mUuid);
267                 }
268         }
269         catch(...)
270         {
271                 DebugOut(DebugOut::Warning)<<"failed to parse longitude"<<endl;
272         }
273 }
274
275 void Location::parseSpeed(string spd)
276 {
277         double s = boost::lexical_cast<double>(spd);
278
279         ///to kph:
280         s *= 1.852;
281         VehicleProperty::VehicleSpeedType temp(s);
282         if(mSpeed != temp)
283         {
284                 mSpeed = temp;
285                 routingEngine->updateProperty(&mSpeed, mUuid);
286         }
287 }
288
289 void Location::parseDirection(string dir)
290 {
291         try {
292                 uint16_t d = boost::lexical_cast<double>(dir);
293
294                 VehicleProperty::DirectionType temp(d);
295                 if(mDirection != temp)
296                 {
297                         mDirection = temp;
298                         routingEngine->updateProperty(&mDirection, mUuid);
299                 }
300         }
301         catch(...)
302         {
303                 DebugOut(DebugOut::Warning)<<"Failed to parse direction: "<<dir<<endl;
304         }
305 }
306
307 void Location::parseAltitude(string alt)
308 {
309         try{
310
311                 if(alt.empty()) return;
312
313                 double a = boost::lexical_cast<double>(alt);
314
315                 VehicleProperty::AltitudeType temp(a);
316                 if(mAltitude != temp)
317                 {
318                         mAltitude = temp;
319                         routingEngine->updateProperty(&mAltitude, mUuid);
320                 }
321
322                 mAltitude = VehicleProperty::AltitudeType(a);
323         }
324         catch(...)
325         {
326                 DebugOut(DebugOut::Warning)<<"failed to parse altitude"<<endl;
327         }
328 }
329
330 double Location::degsToDecimal(double degs)
331 {
332         double deg;
333         double min = 100.0 * modf(degs / 100.0, &deg);
334         return deg + (min / 60.0);
335 }
336
337 bool readCallback(GIOChannel *source, GIOCondition condition, gpointer data)
338 {
339 //      DebugOut(5) << "Polling..." << condition << endl;
340
341         if(condition & G_IO_ERR)
342         {
343                 DebugOut(DebugOut::Error)<<"GpsNmeaSource polling error."<<endl;
344         }
345
346         if (condition & G_IO_HUP)
347         {
348                 //Hang up. Returning false closes out the GIOChannel.
349                 //printf("Callback on G_IO_HUP\n");
350                 DebugOut(DebugOut::Warning)<<"socket hangup event..."<<endl;
351                 return false;
352         }
353
354         GpsNmeaSource* src = static_cast<GpsNmeaSource*>(data);
355
356         src->canHasData();
357
358         return true;
359 }
360
361 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
362 {
363         return new GpsNmeaSource(routingengine, config);
364         
365 }
366
367 GpsNmeaSource::GpsNmeaSource(AbstractRoutingEngine *re, map<string, string> config)
368         :AbstractSource(re,config), mUuid("33d86462-1708-4f78-a001-99ea8d55422b")
369 {
370         location =new Location(re, mUuid);
371
372         VehicleProperty::registerProperty(GPSTIME,[](){ return new BasicPropertyType<double>(GPSTIME,0); });
373
374         addPropertySupport(VehicleProperty::Latitude, Zone::None);
375         addPropertySupport(VehicleProperty::Longitude, Zone::None);
376         addPropertySupport(VehicleProperty::Altitude, Zone::None);
377         addPropertySupport(VehicleProperty::VehicleSpeed, Zone::None);
378         addPropertySupport(VehicleProperty::Direction, Zone::None);
379         addPropertySupport(GPSTIME, Zone::None);
380
381
382         ///test:
383
384         if(config.find("test") != config.end())
385         {
386                 Location location(routingEngine, mUuid);
387                 location.parse("GPRMC,061211,A,2351.9605,S,15112.5239,E,000.0,053.4,170303,009.9,E*6E");
388
389                 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
390
391                 g_assert(location.latitude().toString() == "-23.86600833");
392                 g_assert(location.gpsTime().toString() == "1050585131");
393
394                 location.parse("GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47");
395
396                 DebugOut(0)<<"alt: "<<location.altitude().toString()<<endl;
397                 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
398                 g_assert(location.altitude().toString() == "545.4");
399                 g_assert(location.latitude().toString() == "48.1173");
400
401                 location.parse("GPRMC,060136.00,A,3101.40475,N,12126.87095,E,0.760,,160114,,,A*74");
402                 DebugOut(0)<<"lon: "<<location.longitude().toString()<<endl;
403                 DebugOut(0)<<"lat: "<<location.latitude().toString()<<endl;
404         }
405
406         std::string btaddapter = config["bluetoothAdapter"];
407
408         if(config.find("device")!= config.end())
409         {
410                 std::string dev = config["device"];
411                 if(dev.find(":") != string::npos)
412                 {
413                         BluetoothDevice bt;
414                         dev = bt.getDeviceForAddress(dev, btaddapter);
415                 }
416
417                 device = new SerialPort(dev);
418
419                 if(!device->open())
420                 {
421                         DebugOut(DebugOut::Error)<<"Failed to open gps tty: "<<config["device"]<<endl;
422                         perror("Error");
423                         return;
424                 }
425
426                 DebugOut()<<"read from device: "<<device->read()<<endl;
427
428                 GIOChannel *chan = g_io_channel_unix_new(device->fileDescriptor());
429                 g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR),(GIOFunc)readCallback, this);
430                 g_io_channel_set_close_on_unref(chan, true);
431                 g_io_channel_unref(chan); //Pass ownership of the GIOChannel to the watch.
432         }
433
434         re->setSupported(supported(), this);
435 }
436
437 GpsNmeaSource::~GpsNmeaSource()
438 {
439         device->close();
440 }
441
442 const string GpsNmeaSource::uuid()
443 {
444         return mUuid;
445 }
446
447
448 void GpsNmeaSource::getPropertyAsync(AsyncPropertyReply *reply)
449 {
450         DebugOut()<<"GpsNmeaSource: getPropertyAsync called for property: "<<reply->property<<endl;
451
452         std::list<AbstractPropertyType*> f = location->fix();
453
454         for(auto property : f)
455         {
456                 if(property->name == reply->property)
457                 {
458                         reply->success = true;
459                         reply->value = property;
460                         reply->completed(reply);
461                         return;
462                 }
463         }
464
465         reply->success = false;
466         reply->error = AsyncPropertyReply::InvalidOperation;
467         reply->completed(reply);
468 }
469
470 void GpsNmeaSource::getRangePropertyAsync(AsyncRangePropertyReply *reply)
471 {
472
473 }
474
475 AsyncPropertyReply *GpsNmeaSource::setProperty(AsyncSetPropertyRequest request )
476 {
477
478 }
479
480 void GpsNmeaSource::subscribeToPropertyChanges(VehicleProperty::Property property)
481 {
482         mRequests.push_back(property);
483 }
484
485 PropertyList GpsNmeaSource::supported()
486 {
487         return mSupported;
488 }
489
490 int GpsNmeaSource::supportedOperations()
491 {
492         return Get;
493 }
494
495 void GpsNmeaSource::canHasData()
496 {
497         std::string data = device->read();
498
499         std::vector<std::string> lines;
500
501         boost::split(lines,data,boost::is_any_of("$"));
502
503         for(int i = 0; i < lines.size(); i++)
504         {
505                 if(checksum(lines[i]))
506                 {
507                         buffer = lines[i];
508                 }
509                 else
510                 {
511                         buffer += lines[i];
512                 }
513
514                 if(checksum(buffer))
515                 {
516                         /// we have a complete message.  parse it!
517                         DebugOut(7)<<"Complete message: "<<buffer<<endl;
518                         location->parse(buffer);
519                 }
520
521                 DebugOut(7)<<"buffer: "<<buffer<<endl;
522
523         }
524 }
525
526 void GpsNmeaSource::unsubscribeToPropertyChanges(VehicleProperty::Property property)
527 {
528         mRequests.remove(property);
529 }
530
531 void GpsNmeaSource::addPropertySupport(VehicleProperty::Property property, Zone::Type zone)
532 {
533         mSupported.push_back(property);
534
535         std::list<Zone::Type> zones;
536
537         zones.push_back(zone);
538
539         PropertyInfo info(0, zones);
540
541         propertyInfoMap[property] = info;
542 }
543
544 bool GpsNmeaSource::checksum(std::string sentence)
545 {
546         if(sentence.empty() || sentence.length() < 4)
547         {
548                 return false;
549         }
550
551         int checksum = 0;
552
553         for(auto i : sentence)
554         {
555                 if(i == '*')
556                         break;
557                 if(i != '\n' || i != '\r')
558                         checksum ^= i;
559         }
560
561         std::string sentenceCheckStr = sentence.substr(sentence.length()-4,2);
562
563         try
564         {
565                 int sentenceCheck = lexical_cast<int>(sentenceCheckStr);
566
567                 return sentenceCheck == checksum;
568         }
569         catch(...)
570
571         {
572                 return false;
573         }
574 }