695e15df3db89bb66636691125136f55ca72593a
[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 #include "bluetooth5.h"
24 #include <listplusplus.h>
25 #include <superptr.hpp>
26
27 #include <iostream>
28 #include <boost/assert.hpp>
29 #include <boost/algorithm/string.hpp>
30 #include <time.h>
31
32
33 using namespace std;
34
35 #include "debugout.h"
36 #include "abstractpropertytype.h"
37
38 #define GPSTIME "GpsTime"
39 #define GPSSPEED "GpsSpeed"
40
41 template<typename T2>
42 inline T2 lexical_cast(const std::string &in) {
43         T2 out;
44         std::stringstream ss;
45         ss << std::hex << in;
46         ss >> out;
47         return out;
48 }
49
50 class Location
51 {
52 public:
53         Location(AbstractRoutingEngine* re, std::string uuid);
54
55         void parse(std::string gprmc);
56
57         VehicleProperty::LatitudeType* latitude()
58         {
59                 return mLatitude.get();
60         }
61
62         VehicleProperty::LongitudeType* longitude()
63         {
64                 return mLongitude.get();
65         }
66
67         VehicleProperty::AltitudeType* altitude()
68         {
69                 return mAltitude.get();
70         }
71
72         VehicleProperty::DirectionType* direction()
73         {
74                 return mDirection.get();
75         }
76
77         BasicPropertyType<uint16_t>* speed()
78         {
79                 return mSpeed.get();
80         }
81
82         BasicPropertyType<double>* gpsTime()
83         {
84                 return mGpsTime.get();
85         }
86
87         std::list<AbstractPropertyType*> fix()
88         {
89                 std::list<AbstractPropertyType*> l;
90
91                 l.push_back(mLatitude.get());
92                 l.push_back(mLongitude.get());
93                 l.push_back(mAltitude.get());
94                 l.push_back(mDirection.get());
95                 l.push_back(mSpeed.get());
96                 l.push_back(mGpsTime.get());
97
98                 return l;
99         }
100
101 private: ///methods:
102
103         void parseGprmc(string gprmc);
104         void parseGpgga(string gpgga);
105
106         void parseTime(std::string h, std::string m, std::string s, string dd, string mm, string yy);
107         void parseLatitude(std::string d, std::string m, std::string ns);
108         void parseLongitude(std::string d, string m, string ew);
109         void parseSpeed(std::string spd);
110         void parseDirection(std::string dir);
111         void parseAltitude(std::string alt);
112
113         double degsToDecimal(double degs);
114
115 private:
116
117         std::unique_ptr<VehicleProperty::LatitudeType> mLatitude;
118         std::unique_ptr<VehicleProperty::LongitudeType> mLongitude;
119         std::unique_ptr<VehicleProperty::AltitudeType> mAltitude;
120         std::unique_ptr<VehicleProperty::DirectionType>  mDirection;
121         std::unique_ptr<BasicPropertyType<uint16_t>> mSpeed;
122         std::unique_ptr<BasicPropertyType<double>> mGpsTime;
123
124         bool isActive;
125
126         std::string mUuid;
127
128         AbstractRoutingEngine* routingEngine;
129
130 };
131
132 Location::Location(AbstractRoutingEngine* re, std::string uuid)
133         :isActive(false), routingEngine(re), mUuid(uuid)
134 {
135         mLatitude = amb::make_unique(new VehicleProperty::LatitudeType(0));
136         mLongitude = amb::make_unique(new VehicleProperty::LongitudeType(0));
137         mAltitude = amb::make_unique(new VehicleProperty::AltitudeType(0));
138         mDirection = amb::make_unique(new VehicleProperty::DirectionType(0));
139         mSpeed = amb::make_unique(new BasicPropertyType<uint16_t>(GPSSPEED, 0));
140         mGpsTime = amb::make_unique(new BasicPropertyType<double>(GPSTIME, 0));
141 }
142
143 void Location::parse(string nmea)
144 {
145         if(boost::algorithm::starts_with(nmea,"GPRMC"))
146         {
147                 parseGprmc(nmea);
148         }
149         else if(boost::algorithm::starts_with(nmea,"GPGGA"))
150         {
151                 parseGpgga(nmea);
152         }
153         else
154         {
155                 DebugOut(7)<<"unknown/unhandled message: "<<nmea<<endl;
156         }
157 }
158
159 void Location::parseGprmc(string gprmc)
160 {
161         DebugOut(7)<<"parsing gprmc message"<<endl;
162
163         std::vector<std::string> tokens;
164         boost::split(tokens, gprmc, boost::is_any_of(","));
165
166         if(!tokens.size())
167         {
168                 return;
169         }
170
171         if(tokens[2] == "A")
172         {
173                 isActive = true;
174         }
175
176         if(tokens[1].empty() || tokens[9].empty() || tokens[3].empty() || tokens[4].empty() || tokens[5].empty() || tokens[6].empty() || tokens[7].empty() || tokens[8].empty())
177         {
178                 return;
179         }
180
181         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));
182
183         parseLatitude(tokens[3], "", tokens[4]);
184         parseLongitude(tokens[5], "", tokens[6]);
185         parseSpeed(tokens[7]);
186         parseDirection(tokens[8]);
187
188 }
189
190 void Location::parseGpgga(string gpgga)
191 {
192
193         std::vector<std::string> tokens;
194         boost::split(tokens, gpgga, boost::is_any_of(","));
195
196         if(tokens.size() != 15)
197         {
198                 DebugOut()<<"Invalid GPGGA message: "<<gpgga<<endl;
199                 return;
200         }
201
202         parseLatitude(tokens[2],"",tokens[3]);
203         parseLongitude(tokens[4],"",tokens[5]);
204         if(tokens[6] != "0")
205         {
206                 isActive = true;
207         }
208         else isActive = false;
209
210         parseAltitude(tokens[9]);
211 }
212
213 void Location::parseTime(string h, string m, string s, string dd, string mm, string yy)
214 {
215         try
216         {
217                 tm t;
218                 t.tm_hour = boost::lexical_cast<int>(h);
219                 t.tm_min = boost::lexical_cast<int>(m);
220                 t.tm_sec = boost::lexical_cast<int>(s);
221                 t.tm_mday = boost::lexical_cast<int>(dd);
222                 t.tm_mon = boost::lexical_cast<int>(mm);
223                 t.tm_year = boost::lexical_cast<int>(yy) + 100;
224
225                 time_t time = mktime(&t);
226
227                 if(mGpsTime->basicValue() != (double(time)))
228                 {
229                         mGpsTime->setValue(double(time));
230                         if(routingEngine)
231                                 routingEngine->updateProperty(mGpsTime.get(), mUuid);
232                 }
233         }
234         catch(...)
235         {
236                 DebugOut(5)<<"Failed to parse time "<<endl;
237         }
238 }
239
240 void Location::parseLatitude(string d, string m, string ns)
241 {
242         try
243         {
244                 if(d.empty() )
245                         return;
246
247                 double degs = boost::lexical_cast<double>(d + m);
248                 double dec = degsToDecimal(degs);
249
250                 if(ns == "S")
251                         dec *= -1;
252
253                 if(mLatitude->basicValue() != dec)
254                 {
255                         mLatitude->setValue(dec);
256
257                         if(routingEngine)
258                                 routingEngine->updateProperty(mLatitude.get(), mUuid);
259                 }
260         }
261         catch(...)
262         {
263                 DebugOut(5)<<"Failed to parse latitude"<<endl;
264         }
265 }
266
267 void Location::parseLongitude(string d, string m, string ew)
268 {
269         try
270         {
271                 if(d.empty()) return;
272
273                 double degs = boost::lexical_cast<double>(d + m);
274                 double dec = degsToDecimal(degs);
275
276                 if(ew == "W")
277                         dec *= -1;
278
279                 if(mLongitude->basicValue() != dec)
280                 {
281                         mLongitude->setValue(dec);
282
283                         if(routingEngine)
284                                 routingEngine->updateProperty(mLongitude.get(), mUuid);
285                 }
286         }
287         catch(...)
288         {
289                 DebugOut(5)<<"failed to parse longitude: "<<d<<" "<<m<<" "<<ew<<endl;
290         }
291 }
292
293 void Location::parseSpeed(string spd)
294 {
295         try
296         {
297                 double s = boost::lexical_cast<double>(spd);
298
299                 ///to kph:
300                 s *= 1.852;
301
302                 uint16_t speed = static_cast<uint16_t>(s);
303
304                 if(mSpeed->basicValue() != speed)
305                 {
306                         mSpeed->setValue(speed);
307
308                         if(routingEngine)
309                                 routingEngine->updateProperty(mSpeed.get(), mUuid);
310                 }
311         }
312         catch(...)
313         {
314                 DebugOut(5)<<"failed to parse speed"<<endl;
315         }
316 }
317
318 void Location::parseDirection(string dir)
319 {
320         try {
321                 uint16_t d = boost::lexical_cast<double>(dir);
322
323                 if(mDirection->basicValue() != d)
324                 {
325                         mDirection->setValue(d);
326
327                         if(routingEngine)
328                                 routingEngine->updateProperty(mDirection.get(), mUuid);
329                 }
330         }
331         catch(...)
332         {
333                 DebugOut(5)<<"Failed to parse direction: "<<dir<<endl;
334         }
335 }
336
337 void Location::parseAltitude(string alt)
338 {
339         try{
340
341                 if(alt.empty()) return;
342
343                 double a = boost::lexical_cast<double>(alt);
344
345                 if(mAltitude->basicValue() != a)
346                 {
347                         mAltitude->setValue(a);
348
349                         if(routingEngine)
350                                 routingEngine->updateProperty(mAltitude.get(), mUuid);
351                 }
352         }
353         catch(...)
354         {
355                 DebugOut(5)<<"failed to parse altitude"<<endl;
356         }
357 }
358
359 double Location::degsToDecimal(double degs)
360 {
361         double deg;
362         double min = 100.0 * modf(degs / 100.0, &deg);
363         return deg + (min / 60.0);
364 }
365
366 bool readCallback(GIOChannel *source, GIOCondition condition, gpointer data)
367 {
368 //      DebugOut(5) << "Polling..." << condition << endl;
369
370         if(condition & G_IO_ERR)
371         {
372                 DebugOut(DebugOut::Error)<<"GpsNmeaSource polling error."<<endl;
373         }
374
375         if (condition & G_IO_HUP)
376         {
377                 //Hang up. Returning false closes out the GIOChannel.
378                 //printf("Callback on G_IO_HUP\n");
379                 DebugOut(DebugOut::Warning)<<"socket hangup event..."<<endl;
380                 return false;
381         }
382
383         GpsNmeaSource* src = static_cast<GpsNmeaSource*>(data);
384
385         src->canHasData();
386
387         return true;
388 }
389
390 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
391 {
392         return new GpsNmeaSource(routingengine, config);
393
394 }
395
396 GpsNmeaSource::GpsNmeaSource(AbstractRoutingEngine *re, map<string, string> config)
397         :AbstractSource(re,config), mUuid("33d86462-1708-4f78-a001-99ea8d55422b"), device(nullptr), bt(nullptr)
398 {
399         int baudrate = 0;
400         location =new Location(re, mUuid);
401
402         VehicleProperty::registerProperty(GPSTIME,[](){ return new BasicPropertyType<double>(GPSTIME, 0); });
403         VehicleProperty::registerProperty(GPSSPEED,[](){ return new BasicPropertyType<uint16_t>(GPSSPEED, 0); });
404
405         addPropertySupport(VehicleProperty::Latitude, Zone::None);
406         addPropertySupport(VehicleProperty::Longitude, Zone::None);
407         addPropertySupport(VehicleProperty::Altitude, Zone::None);
408         addPropertySupport(GPSSPEED, Zone::None);
409         addPropertySupport(VehicleProperty::Direction, Zone::None);
410         addPropertySupport(GPSTIME, Zone::None);
411
412         ///test:
413
414         if(config.find("test") != config.end())
415         {
416                 test();
417         }
418
419         std::string btaddapter = config["bluetoothAdapter"];
420
421         if(config.find("baudrate")!= config.end())
422         {
423                 baudrate = boost::lexical_cast<int>( config["baudrate"] );
424         }
425
426         if(config.find("device")!= config.end())
427         {
428                 std::string dev = config["device"];
429
430                 if(dev.find(":") != string::npos)
431                 {
432 #ifdef USE_BLUEZ5
433                         bt = new Bluetooth5();
434                         bt->getDeviceForAddress(dev, [this](int fd) {
435                                 DebugOut() << "fd: " << fd << endl;
436                                 device = new SerialPort(fd);
437                                 int baudrate=0;
438
439                                 if(baudrate!=0)
440                                 {
441                                         if((static_cast<SerialPort*>(device))->setSpeed(baudrate))
442                                                 DebugOut(DebugOut::Error)<<"Unsupported baudrate " << configuration["baudrate"] << endl;
443                                 }
444
445                                 DebugOut()<<"read from device: "<<device->read()<<endl;
446
447                                 GIOChannel *chan = g_io_channel_unix_new(device->fileDescriptor());
448                                 g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR),(GIOFunc)readCallback, this);
449                                 g_io_channel_set_close_on_unref(chan, true);
450                                 g_io_channel_unref(chan);
451                         });
452 #else
453                         bt = new BluetoothDevice();
454                         dev = bt->getDeviceForAddress(dev, btaddapter);
455
456                         device = new SerialPort(dev);
457
458                         if(baudrate!=0)
459                         {
460                                 if((static_cast<SerialPort*>(device))->setSpeed(baudrate))
461                                         DebugOut(DebugOut::Error)<<"Unsupported baudrate " << config["baudrate"] << endl;
462                         }
463
464                         if(!device->open())
465                         {
466                                 DebugOut(DebugOut::Error)<<"Failed to open gps tty: "<<config["device"]<<endl;
467                                 perror("Error");
468                                 return;
469                         }
470
471                         DebugOut()<<"read from device: "<<device->read()<<endl;
472
473                         GIOChannel *chan = g_io_channel_unix_new(device->fileDescriptor());
474                         g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR),(GIOFunc)readCallback, this);
475                         g_io_channel_set_close_on_unref(chan, true);
476                         g_io_channel_unref(chan);
477 #endif
478                 }
479                 else
480                 {
481                         device = new SerialPort(dev);
482
483                         if(baudrate!=0)
484                         {
485                                 if((static_cast<SerialPort*>(device))->setSpeed(baudrate))
486                                         DebugOut(DebugOut::Error)<<"Unsupported baudrate " << config["baudrate"] << endl;
487                         }
488
489                         if(!device->open())
490                         {
491                                 DebugOut(DebugOut::Error)<<"Failed to open gps tty: "<<config["device"]<<endl;
492                                 perror("Error");
493                                 return;
494                         }
495
496                         DebugOut()<<"read from device: "<<device->read()<<endl;
497
498                         GIOChannel *chan = g_io_channel_unix_new(device->fileDescriptor());
499                         g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR), (GIOFunc)readCallback, this);
500                         g_io_channel_set_close_on_unref(chan, true);
501                         g_io_channel_unref(chan);
502                 }
503         }
504 }
505
506 GpsNmeaSource::~GpsNmeaSource()
507 {
508         if(device && device->isOpen())
509                 device->close();
510         if(bt)
511                 delete bt;
512 }
513
514 const string GpsNmeaSource::uuid()
515 {
516         return mUuid;
517 }
518
519
520 void GpsNmeaSource::getPropertyAsync(AsyncPropertyReply *reply)
521 {
522         DebugOut()<<"GpsNmeaSource: getPropertyAsync called for property: "<<reply->property<<endl;
523
524         std::list<AbstractPropertyType*> f = location->fix();
525
526         for(auto property : f)
527         {
528                 if(property->name == reply->property)
529                 {
530                         reply->success = true;
531                         reply->value = property;
532                         reply->completed(reply);
533                         return;
534                 }
535         }
536
537         reply->success = false;
538         reply->error = AsyncPropertyReply::InvalidOperation;
539         reply->completed(reply);
540 }
541
542 void GpsNmeaSource::getRangePropertyAsync(AsyncRangePropertyReply *reply)
543 {
544
545 }
546
547 AsyncPropertyReply *GpsNmeaSource::setProperty(AsyncSetPropertyRequest request )
548 {
549
550 }
551
552 void GpsNmeaSource::subscribeToPropertyChanges(VehicleProperty::Property property)
553 {
554         mRequests.push_back(property);
555 }
556
557 PropertyList GpsNmeaSource::supported()
558 {
559         return mSupported;
560 }
561
562 int GpsNmeaSource::supportedOperations()
563 {
564         return Get;
565 }
566
567 void GpsNmeaSource::canHasData()
568 {
569         std::string data = device->read();
570
571         tryParse(data);
572 }
573
574 void GpsNmeaSource::test()
575 {
576         Location location(nullptr, "");
577         location.parse("GPRMC,061211,A,2351.9605,S,15112.5239,E,000.0,053.4,170303,009.9,E*6E");
578
579         DebugOut(0)<<"lat: "<<location.latitude()->toString()<<endl;
580
581         g_assert(location.latitude()->toString() == "-23.86600833");
582         g_assert(location.gpsTime()->toString() == "1050585131");
583
584         location.parse("GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47");
585
586         DebugOut(0)<<"alt: "<<location.altitude()->toString()<<endl;
587         DebugOut(0)<<"lat: "<<location.latitude()->toString()<<endl;
588         g_assert(location.altitude()->toString() == "545.4");
589         g_assert(location.latitude()->toString() == "48.1173");
590
591         location.parse("GPRMC,060136.00,A,3101.40475,N,12126.87095,E,0.760,,160114,,,A*74");
592         DebugOut(0)<<"lon: "<<location.longitude()->toString()<<endl;
593         DebugOut(0)<<"lat: "<<location.latitude()->toString()<<endl;
594
595         //Test incomplete message:
596         location.parse("GPRMC,023633.00,V,,,,,,,180314,,,N*75");
597         DebugOut(0)<<"lon: "<<location.longitude()->toString()<<endl;
598         DebugOut(0)<<"lat: "<<location.latitude()->toString()<<endl;
599
600         std::string testChecksuming = "GPRMC,195617.00,V,,,,,,,310314,,,N*74";
601
602         g_assert(checksum(testChecksuming));
603
604         std::string multimessage1 = "GA,235320.00,4532.48633,N,12257.";
605         std::string multimessage2 = "57383,W,";
606         std::string multimessage3 = "1,03,7.53,51.6,M,-21.3,M,,*55";
607         std::string multimessage4 = "GPGSA,A,";
608         std::string multimessage5 = "2,27,23,19,,,,,,,,,,7.60";
609         std::string multimessage6 = ",7.53,1.00*";
610         std::string multimessage7 = "0E";
611
612         bool multimessageParse = false;
613
614         multimessageParse |= tryParse(multimessage1);
615         multimessageParse |= tryParse(multimessage2);
616         multimessageParse |= tryParse(multimessage3);
617         multimessageParse |= tryParse(multimessage4);
618         multimessageParse |= tryParse(multimessage5);
619         multimessageParse |= tryParse(multimessage6);
620         multimessageParse |= tryParse(multimessage7);
621
622         g_assert(multimessageParse);
623
624         //Test meaningingless message:
625         location.parse("GPRMC,,V,,,,,,,,,,N*53");
626
627         //test false message:
628
629         g_assert(!checksum("GPRMC,172758.296,V"));
630 }
631
632 bool GpsNmeaSource::tryParse(string data)
633 {
634         std::vector<std::string> lines;
635
636         boost::split(lines, data, boost::is_any_of("$"));
637
638         bool weFoundAMessage = false;
639
640         for(auto line : lines)
641         {
642                 if(checksum(line))
643                 {
644                         buffer = line;
645                 }
646                 else
647                 {
648                         buffer += line;
649                 }
650
651                 std::string::size_type pos = buffer.find('G');
652
653                 if(pos != std::string::npos && pos != 0)
654                 {
655                         ///Throw the incomplete stuff away.  if it doesn't begin with "G" it'll never be complete
656                         buffer = buffer.substr(pos);
657                 }
658
659                 if(checksum(buffer))
660                 {
661                         /// we have a complete message.  parse it!
662                         DebugOut(7)<<"Complete message: "<<buffer<<endl;
663                         location->parse(buffer);
664                         weFoundAMessage = true;
665                         buffer = "";
666                 }
667                 else
668                 {
669                         if(pos == 0 )
670                         {
671                                 uint cs = buffer.find('*');
672                                 if (cs != std::string::npos && cs != buffer.length()-1)
673                                 {
674                                         ///This means we have a false flag somewhere.
675                                         buffer = buffer.substr(cs+(buffer.length() - cs));
676                                 }
677                         }
678                 }
679
680                 DebugOut(7)<<"buffer: "<<buffer<<endl;
681         }
682
683         return weFoundAMessage;
684 }
685
686 void GpsNmeaSource::unsubscribeToPropertyChanges(VehicleProperty::Property property)
687 {
688         removeOne(&mRequests,property);
689 }
690
691 void GpsNmeaSource::addPropertySupport(VehicleProperty::Property property, Zone::Type zone)
692 {
693         mSupported.push_back(property);
694
695         Zone::ZoneList zones;
696
697         zones.push_back(zone);
698
699         PropertyInfo info(0, zones);
700
701         propertyInfoMap[property] = info;
702 }
703
704 bool GpsNmeaSource::checksum(std::string sentence)
705 {
706         if(sentence.empty() || sentence.length() < 4 || sentence.find("*") == string::npos || sentence.find("*") >= sentence.length()-2)
707         {
708                 return false;
709         }
710
711         int checksum = 0;
712
713         for(auto i : sentence)
714         {
715                 if(i == '*')
716                         break;
717                 if(i != '\n' || i != '\r')
718                         checksum ^= i;
719         }
720
721         std::string sentenceCheckStr = sentence.substr(sentence.find('*')+1,2);
722
723         try
724         {
725                 int sentenceCheck = lexical_cast<int>(sentenceCheckStr);
726
727                 return sentenceCheck == checksum;
728         }
729         catch(...)
730
731         {
732                 return false;
733         }
734
735         return false;
736 }
737
738
739 int main(int argc, char** argv)
740 {
741         DebugOut::setDebugThreshhold(7);
742         GpsNmeaSource plugin(nullptr, std::map<std::string, std::string>());
743         plugin.test();
744
745         return 1;
746 }