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