Merge lp:~thomas-voss/location-service/enable-assist-now-online into lp:location-service

Proposed by Thomas Voß
Status: Merged
Approved by: Thomas Voß
Approved revision: 299
Merged at revision: 297
Proposed branch: lp:~thomas-voss/location-service/enable-assist-now-online
Merge into: lp:location-service
Prerequisite: lp:~thomas-voss/location-service/enable-ublox-protocol
Diff against target: 1717 lines (+1011/-196)
24 files modified
prebuilt/meta/hooks/configure (+15/-0)
snapcraft.yaml (+5/-2)
src/location/cmds/run.cpp (+4/-0)
src/location/cmds/test.cpp (+9/-1)
src/location/cmds/test.h (+3/-0)
src/location/providers/ubx/CMakeLists.txt (+4/-1)
src/location/providers/ubx/_8/assist_now_online_client.cpp (+160/-0)
src/location/providers/ubx/_8/assist_now_online_client.h (+105/-0)
src/location/providers/ubx/_8/cfg/rst.cpp (+43/-0)
src/location/providers/ubx/_8/cfg/rst.h (+84/-0)
src/location/providers/ubx/_8/gnss_id.cpp (+10/-3)
src/location/providers/ubx/_8/gnss_id.h (+1/-0)
src/location/providers/ubx/_8/message.h (+2/-0)
src/location/providers/ubx/_8/nmea/scanner.cpp (+2/-2)
src/location/providers/ubx/_8/receiver.cpp (+32/-3)
src/location/providers/ubx/_8/receiver.h (+4/-4)
src/location/providers/ubx/_8/scanner.cpp (+17/-11)
src/location/providers/ubx/_8/scanner.h (+3/-0)
src/location/providers/ubx/_8/serial_port_receiver.cpp (+32/-20)
src/location/providers/ubx/_8/serial_port_receiver.h (+8/-2)
src/location/providers/ubx/provider.cpp (+290/-117)
src/location/providers/ubx/provider.h (+53/-23)
src/location/runtime_tests.cpp (+123/-6)
src/location/runtime_tests.h (+2/-1)
To merge this branch: bzr merge lp:~thomas-voss/location-service/enable-assist-now-online
Reviewer Review Type Date Requested Status
Simon Fels (community) Approve
Thomas Voß Pending
Review via email: mp+320795@code.launchpad.net

Commit message

Enable AssistNow for ublox 8 receivers.

Support assisted GNSS scenarios and add infrastructure to
query the ublox AssistNow online service.

Description of the change

Enable AssistNow for ublox 8 receivers.

Support assisted GNSS scenarios and add infrastructure to
query the ublox AssistNow online service.

To post a comment you must log in.
Revision history for this message
Simon Fels (morphis) :
review: Needs Fixing
297. By Thomas Voß

Document dropping of exceptions in Receiver::process_chunk.

Revision history for this message
Simon Fels (morphis) wrote :

LGTM

review: Approve
298. By Thomas Voß

Introduce locationd.test to the snap.

299. By Thomas Voß

Fix a couple of issues found during testing.

300. By Thomas Voß

Make the number of trials in testing configurable via an env variable.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'prebuilt/meta/hooks/configure'
2--- prebuilt/meta/hooks/configure 2017-01-09 09:16:58 +0000
3+++ prebuilt/meta/hooks/configure 2017-03-29 20:24:25 +0000
4@@ -4,3 +4,18 @@
5 mkdir -p $SNAP_DATA/ubx/provider
6 echo "$path" > $SNAP_DATA/ubx/provider/path
7 fi
8+
9+if enable=$(snapctl get ubx.provider.assistnow.enable); then
10+ mkdir -p $SNAP_DATA/ubx/provider/assist_now
11+ echo "$enable" > $SNAP_DATA/ubx/provider/assist_now/enable
12+fi
13+
14+if token=$(snapctl get ubx.provider.assistnow.token); then
15+ mkdir -p $SNAP_DATA/ubx/provider/assist_now
16+ echo "$token" > $SNAP_DATA/ubx/provider/assist_now/token
17+fi
18+
19+if timeout=$(snapctl get ubx.provider.assistnow.acquisition_timeout); then
20+ mkdir -p $SNAP_DATA/ubx/provider/assist_now
21+ echo "$timeout" > $SNAP_DATA/ubx/provider/assist_now/acquisition_timeout
22+fi
23
24=== modified file 'snapcraft.yaml'
25--- snapcraft.yaml 2017-03-07 10:01:34 +0000
26+++ snapcraft.yaml 2017-03-29 20:24:25 +0000
27@@ -12,7 +12,7 @@
28 daemon: simple
29 command: bin/locationd run --bus=system
30 slots: [service-control, service-observe]
31- plugs: [ubx, network-manager]
32+ plugs: [ubx, network, network-manager]
33 list-providers:
34 command: bin/locationd list
35 status:
36@@ -23,7 +23,10 @@
37 plugs: [client-control, client-observe]
38 provide:
39 command: bin/locationd provider --bus=system
40- plugs: [client-control]
41+ plugs: [client-control, network]
42+ test:
43+ command: bin/locationd test
44+ plugs: [ubx, network]
45
46 slots:
47 service-observe:
48
49=== modified file 'src/location/cmds/run.cpp'
50--- src/location/cmds/run.cpp 2017-03-09 10:07:56 +0000
51+++ src/location/cmds/run.cpp 2017-03-29 20:24:25 +0000
52@@ -74,6 +74,10 @@
53 {
54 engine->add_provider(location::providers::ubx::Provider::create_instance(ProviderFactory::Configuration{}));
55 }
56+ catch (const std::exception& e)
57+ {
58+ ctxt.cout << "Error adding UBX provider: " << e.what() << std::endl;
59+ }
60 catch (...)
61 {
62 ctxt.cout << "Error adding UBX provider." << std::endl;
63
64=== modified file 'src/location/cmds/test.cpp'
65--- src/location/cmds/test.cpp 2016-06-30 09:15:49 +0000
66+++ src/location/cmds/test.cpp 2017-03-29 20:24:25 +0000
67@@ -25,8 +25,16 @@
68 location::cmds::Test::Test()
69 : CommandWithFlagsAndAction{cli::Name{"test"}, cli::Usage{"test"}, cli::Description{"executes runtime tests against the gps provider."}}
70 {
71+ flag(cli::make_flag(cli::Name{"test-suite"}, cli::Description{"test-suite that should be executed"}, test_suite));
72+
73 action([this](const Context& ctxt)
74 {
75- return location::execute_runtime_tests(ctxt.cout, ctxt.cout);
76+ if (not test_suite)
77+ {
78+ ctxt.cout << "Missing parameter test-suite." << std::endl;
79+ return EXIT_FAILURE;
80+ }
81+
82+ return execute_runtime_tests(*test_suite, ctxt.cout, ctxt.cout);
83 });
84 }
85
86=== modified file 'src/location/cmds/test.h'
87--- src/location/cmds/test.h 2016-08-24 14:12:05 +0000
88+++ src/location/cmds/test.h 2017-03-29 20:24:25 +0000
89@@ -35,6 +35,9 @@
90 public:
91 // List initializes a new instance.
92 Test();
93+
94+private:
95+ Optional<std::string> test_suite; // The test suite that should be executed.
96 };
97 }
98 }
99
100=== modified file 'src/location/providers/ubx/CMakeLists.txt'
101--- src/location/providers/ubx/CMakeLists.txt 2017-03-29 20:24:25 +0000
102+++ src/location/providers/ubx/CMakeLists.txt 2017-03-29 20:24:25 +0000
103@@ -7,6 +7,8 @@
104
105 bits.h
106
107+ _8/assist_now_online_client.h
108+ _8/assist_now_online_client.cpp
109 _8/checksum.h
110 _8/checksum.cpp
111 _8/gnss_id.h
112@@ -25,11 +27,12 @@
113 _8/ack/ack.cpp
114 _8/ack/nak.h
115 _8/ack/nak.cpp
116- _8/cfg/cfg.h
117 _8/cfg/gnss.h
118 _8/cfg/gnss.cpp
119 _8/cfg/msg.h
120 _8/cfg/msg.cpp
121+ _8/cfg/rst.h
122+ _8/cfg/rst.cpp
123 _8/nav/pvt.h
124 _8/nav/pvt.cpp
125 _8/nav/sat.h
126
127=== added file 'src/location/providers/ubx/_8/assist_now_online_client.cpp'
128--- src/location/providers/ubx/_8/assist_now_online_client.cpp 1970-01-01 00:00:00 +0000
129+++ src/location/providers/ubx/_8/assist_now_online_client.cpp 2017-03-29 20:24:25 +0000
130@@ -0,0 +1,160 @@
131+// Copyright (C) 2017 Thomas Voss <thomas.voss.bochum@gmail.com>
132+//
133+// This library is free software: you can redistribute it and/or modify
134+// it under the terms of the GNU Lesser General Public License as published
135+// by the Free Software Foundation, either version 3 of the License, or
136+// (at your option) any later version.
137+//
138+// This program is distributed in the hope that it will be useful,
139+// but WITHOUT ANY WARRANTY; without even the implied warranty of
140+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141+// GNU General Public License for more details.
142+//
143+// You should have received a copy of the GNU Lesser General Public License
144+// along with this program. If not, see <http://www.gnu.org/licenses/>.
145+
146+#include <location/providers/ubx/_8/assist_now_online_client.h>
147+
148+#include <core/net/uri.h>
149+#include <core/net/http/response.h>
150+
151+#include <iostream>
152+
153+namespace _8 = location::providers::ubx::_8;
154+namespace http = core::net::http;
155+
156+namespace
157+{
158+
159+template<typename T>
160+std::string join(const std::set<T>& set)
161+{
162+ std::stringstream ss; bool first = true;
163+
164+ for (auto element : set)
165+ {
166+ if (!first)
167+ ss << ",";
168+
169+ ss << element;
170+ first = false;
171+ }
172+
173+ return ss.str();
174+}
175+
176+template<typename T, typename U>
177+std::string join(const std::set<std::pair<T, U>>& set)
178+{
179+ std::stringstream ss; bool first = true;
180+
181+ for (auto element : set)
182+ {
183+ if (!first)
184+ ss << ",";
185+
186+ ss << element.first << ":" << element.second;
187+ first = false;
188+ }
189+
190+ return ss.str();
191+}
192+
193+} // namespace
194+
195+_8::AssistNowOnlineClient::AssistNowOnlineClient(const std::shared_ptr<http::Client>& http_client)
196+ : http_client{http_client}, worker{[this]() { _8::AssistNowOnlineClient::http_client->run(); }}
197+{
198+}
199+
200+_8::AssistNowOnlineClient::~AssistNowOnlineClient()
201+{
202+ http_client->stop();
203+ if (worker.joinable())
204+ worker.join();
205+}
206+
207+void _8::AssistNowOnlineClient::request_assistance_data(const Parameters& parameters, const std::function<void(const Result<std::string>&)>& cb)
208+{
209+ core::net::Uri::QueryParameters query
210+ {
211+ {"gnss", join(parameters.gnss)},
212+ {"datatype", join(parameters.data_types)}
213+ };
214+
215+ if (parameters.filter_on_pos)
216+ query.push_back({"filteronpos", "notstrictlyneeded"});
217+
218+ if (!parameters.filter_on_svs.empty())
219+ query.push_back({"filteronsvs", join(parameters.filter_on_svs)});
220+
221+ if (parameters.position)
222+ {
223+ const auto& position = parameters.position.get();
224+ query.push_back({"lat", std::to_string(position.latitude().value())});
225+ query.push_back({"lon", std::to_string(position.longitude().value())});
226+
227+ if (position.altitude())
228+ {
229+ const auto& altitude = position.altitude().get();
230+ query.push_back({"alt", std::to_string(altitude.value())});
231+ }
232+
233+ if (position.accuracy().horizontal())
234+ {
235+ const auto& accuracy = position.accuracy().horizontal().get();
236+ query.push_back({"pacc", std::to_string(accuracy.value())});
237+ }
238+ }
239+
240+ auto uri = service_url + parameters.token;
241+ for (const auto& param : query)
242+ uri += (boost::format("&%1%=%2%") % param.first % param.second).str();
243+
244+ auto request = http_client->get(http::Request::Configuration::from_uri_as_string(uri));
245+ http::Request::Handler handler;
246+
247+ handler.on_response([cb](const http::Response& response)
248+ {
249+ if (response.status == http::Status::ok)
250+ cb(make_result(response.body));
251+ else
252+ cb(make_error_result<std::string>(
253+ std::make_exception_ptr(
254+ std::runtime_error{response.body})));
255+ });
256+
257+ handler.on_error([cb](const core::net::Error& error)
258+ {
259+ cb(make_error_result<std::string>(std::make_exception_ptr(error)));
260+ });
261+
262+ request->async_execute(handler);
263+}
264+
265+bool _8::operator<(AssistNowOnlineClient::DataType lhs, AssistNowOnlineClient::DataType rhs)
266+{
267+ using UT = typename std::underlying_type<AssistNowOnlineClient::DataType>::type;
268+ return static_cast<UT>(lhs) < static_cast<UT>(rhs);
269+}
270+
271+std::ostream& _8::operator<<(std::ostream& out, AssistNowOnlineClient::DataType rhs)
272+{
273+ switch (rhs)
274+ {
275+ case AssistNowOnlineClient::DataType::almanac:
276+ out << "alm";
277+ break;
278+ case AssistNowOnlineClient::DataType::ephemeris:
279+ out << "eph";
280+ break;
281+ case AssistNowOnlineClient::DataType::position:
282+ out << "pos";
283+ break;
284+ case AssistNowOnlineClient::DataType::aux:
285+ out << "aux";
286+ break;
287+ }
288+
289+ return out;
290+}
291
292=== added file 'src/location/providers/ubx/_8/assist_now_online_client.h'
293--- src/location/providers/ubx/_8/assist_now_online_client.h 1970-01-01 00:00:00 +0000
294+++ src/location/providers/ubx/_8/assist_now_online_client.h 2017-03-29 20:24:25 +0000
295@@ -0,0 +1,105 @@
296+// Copyright (C) 2017 Thomas Voss <thomas.voss.bochum@gmail.com>
297+//
298+// This library is free software: you can redistribute it and/or modify
299+// it under the terms of the GNU Lesser General Public License as published
300+// by the Free Software Foundation, either version 3 of the License, or
301+// (at your option) any later version.
302+//
303+// This program is distributed in the hope that it will be useful,
304+// but WITHOUT ANY WARRANTY; without even the implied warranty of
305+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
306+// GNU General Public License for more details.
307+//
308+// You should have received a copy of the GNU Lesser General Public License
309+// along with this program. If not, see <http://www.gnu.org/licenses/>.
310+
311+#ifndef UBX_8_ASSIST_NOW_ONLINE_CLIENT_H_
312+#define UBX_8_ASSIST_NOW_ONLINE_CLIENT_H_
313+
314+#include <location/providers/ubx/_8/gnss_id.h>
315+
316+#include <location/optional.h>
317+#include <location/position.h>
318+#include <location/result.h>
319+
320+#include <core/net/http/client.h>
321+
322+#include <chrono>
323+#include <cstdint>
324+#include <iosfwd>
325+#include <set>
326+#include <string>
327+#include <thread>
328+
329+namespace location
330+{
331+namespace providers
332+{
333+namespace ubx
334+{
335+namespace _8
336+{
337+
338+class AssistNowOnlineClient
339+{
340+public:
341+ static constexpr const char* service_url{"http://online-live1.services.u-blox.com/GetOnlineData.ashx?token="};
342+
343+ enum class DataType
344+ {
345+ ephemeris = 0,
346+ almanac = 1,
347+ aux = 2,
348+ position = 3
349+ };
350+
351+ struct Parameters
352+ {
353+ // The authorization token supplied by u-blox when a client registers to
354+ // use the service.
355+ std::string token;
356+ // List of the GNSS for which data should be
357+ // returned. Valid GNSS are: GnssId::gps, GnssId::qzss and GnssId::glonass.
358+ std::set<GnssId> gnss;
359+ // List of data types required by the client.
360+ std::set<DataType> data_types;
361+ // Approximate user latitude, longitude and altitude in WGS 84 expressed in degrees and
362+ // fractional degrees.
363+ Optional<Position> position;
364+ // The timing accuracy (see time parameters note below).
365+ Optional<std::chrono::seconds> time_accuracy;
366+ // Typical latency between the time the server receives the request, and
367+ // the time when the assistance data arrives at the u-blox receiver. The
368+ // server can use this value to correct the time being transmitted to the
369+ // client.
370+ Optional<std::chrono::seconds> latency;
371+ // If set to true, the ephemeris data returned to the client will only contain
372+ // data for the satellites which are likely to be visible from the
373+ // approximate position provided by the lat, lon, alt and pacc parameters.
374+ bool filter_on_pos = false;
375+ // List of u-blox gnssId:svId pairs. The ephemeris data
376+ // returned to the client will only contain data for the listed satellites.
377+ std::set<std::pair<GnssId, std::uint32_t>> filter_on_svs;
378+ };
379+
380+ explicit AssistNowOnlineClient(const std::shared_ptr<core::net::http::Client>& http_client);
381+ ~AssistNowOnlineClient();
382+
383+ // request_asisstance_data reaches out to the AssistNow service via the configured http client
384+ // with parameters, reporting the assistance data blob to cb (on success).
385+ void request_assistance_data(const Parameters& parameters, const std::function<void(const Result<std::string>&)>& cb);
386+
387+private:
388+ std::shared_ptr<core::net::http::Client> http_client;
389+ std::thread worker;
390+};
391+
392+bool operator<(AssistNowOnlineClient::DataType lhs, AssistNowOnlineClient::DataType rhs);
393+std::ostream& operator<<(std::ostream& out, AssistNowOnlineClient::DataType rhs);
394+
395+} // namespace _8
396+} // namespace ubx
397+} // namespace providers
398+} // namepsace location
399+
400+#endif // UBX_8_ASSIST_NOW_ONLINE_CLIENT_H_
401
402=== added file 'src/location/providers/ubx/_8/cfg/rst.cpp'
403--- src/location/providers/ubx/_8/cfg/rst.cpp 1970-01-01 00:00:00 +0000
404+++ src/location/providers/ubx/_8/cfg/rst.cpp 2017-03-29 20:24:25 +0000
405@@ -0,0 +1,43 @@
406+// Copyright (C) 2016 Thomas Voss <thomas.voss.bochum@gmail.com>
407+//
408+// This library is free software: you can redistribute it and/or modify
409+// it under the terms of the GNU Lesser General Public License as published
410+// by the Free Software Foundation, either version 3 of the License, or
411+// (at your option) any later version.
412+//
413+// This program is distributed in the hope that it will be useful,
414+// but WITHOUT ANY WARRANTY; without even the implied warranty of
415+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
416+// GNU General Public License for more details.
417+//
418+// You should have received a copy of the GNU Lesser General Public License
419+// along with this program. If not, see <http://www.gnu.org/licenses/>.
420+
421+#include <location/providers/ubx/_8/cfg/rst.h>
422+
423+#include <location/providers/ubx/bits.h>
424+#include <location/providers/ubx/_8/writer.h>
425+
426+#include <cstdint>
427+#include <iostream>
428+
429+namespace cfg = location::providers::ubx::_8::cfg;
430+
431+std::size_t cfg::Rst::size() const
432+{
433+ return 4;
434+}
435+
436+void cfg::Rst::write(Writer& writer) const
437+{
438+ writer.write_unsigned_short(bits);
439+ writer.write_unsigned_char(mode);
440+ writer.write_unsigned_char(0);
441+}
442+
443+std::ostream& cfg::operator<<(std::ostream& out, const cfg::Rst& rst)
444+{
445+ return out << "cfg-rst:" << std::endl
446+ << " bits: " << rst.bits << std::endl
447+ << " mode: " << rst.mode << std::endl;
448+}
449
450=== added file 'src/location/providers/ubx/_8/cfg/rst.h'
451--- src/location/providers/ubx/_8/cfg/rst.h 1970-01-01 00:00:00 +0000
452+++ src/location/providers/ubx/_8/cfg/rst.h 2017-03-29 20:24:25 +0000
453@@ -0,0 +1,84 @@
454+// Copyright (C) 2016 Thomas Voss <thomas.voss.bochum@gmail.com>
455+//
456+// This library is free software: you can redistribute it and/or modify
457+// it under the terms of the GNU Lesser General Public License as published
458+// by the Free Software Foundation, either version 3 of the License, or
459+// (at your option) any later version.
460+//
461+// This program is distributed in the hope that it will be useful,
462+// but WITHOUT ANY WARRANTY; without even the implied warranty of
463+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
464+// GNU General Public License for more details.
465+//
466+// You should have received a copy of the GNU Lesser General Public License
467+// along with this program. If not, see <http://www.gnu.org/licenses/>.
468+
469+#ifndef UBX_8_CFG_RST_H_
470+#define UBX_8_CFG_RST_H_
471+
472+#include <cstdint>
473+#include <iosfwd>
474+
475+namespace location
476+{
477+namespace providers
478+{
479+namespace ubx
480+{
481+namespace _8
482+{
483+
484+class Writer;
485+
486+namespace cfg
487+{
488+
489+struct Rst
490+{
491+ static constexpr std::uint8_t class_id{0x06};
492+ static constexpr std::uint8_t message_id{0x04};
493+
494+ enum Bits
495+ {
496+ eph = 1 << 0,
497+ alm = 1 << 1,
498+ health = 1 << 2,
499+ klob = 1 << 3,
500+ pos = 1 << 4,
501+ clkd = 1 << 5,
502+ osc = 1 << 6,
503+ utc = 1 << 7,
504+ rtc = 1 << 8,
505+ aop = 1 << 15,
506+
507+ hot_start = 0,
508+ warm_start = eph,
509+ cold_start = eph | alm | health | klob | pos | clkd | osc | utc | rtc | aop
510+ };
511+
512+ enum Mode
513+ {
514+ hardware_reset = 0x00,
515+ controlled_software_reset = 0x01,
516+ controlled_software_reset_gnss = 0x02,
517+ hardware_reset_after_shutdown = 0x04,
518+ controlled_gnss_stop = 0x08,
519+ controlled_gnss_start = 0x09
520+ };
521+
522+ std::size_t size() const;
523+ void write(Writer& writer) const;
524+
525+ Bits bits;
526+ Mode mode;
527+};
528+
529+std::ostream& operator<<(std::ostream& out, const Rst& rst);
530+
531+} // namespace cfg
532+} // namespace _8
533+} // namespace ubx
534+} // namespace providers
535+} // namespace location
536+
537+#endif // UBX_8_CFG_RST_H_
538
539=== modified file 'src/location/providers/ubx/_8/gnss_id.cpp'
540--- src/location/providers/ubx/_8/gnss_id.cpp 2017-03-29 20:24:25 +0000
541+++ src/location/providers/ubx/_8/gnss_id.cpp 2017-03-29 20:24:25 +0000
542@@ -16,6 +16,7 @@
543 #include <location/providers/ubx/_8/gnss_id.h>
544
545 #include <iostream>
546+#include <type_traits>
547
548 namespace _8 = location::providers::ubx::_8;
549
550@@ -25,12 +26,18 @@
551 {
552 case GnssId::gps: out << "gps"; break;
553 case GnssId::sbas: out << "sbas"; break;
554- case GnssId::galileo: out << "galileo"; break;
555- case GnssId::beidou: out << "beidou"; break;
556+ case GnssId::galileo: out << "gal"; break;
557+ case GnssId::beidou: out << "bds"; break;
558 case GnssId::imes: out << "imes"; break;
559 case GnssId::qzss: out << "qzss"; break;
560- case GnssId::glonass: out << "glonass"; break;
561+ case GnssId::glonass: out << "glo"; break;
562 }
563
564 return out;
565 }
566+
567+bool _8::operator<(GnssId lhs, GnssId rhs)
568+{
569+ using UT = typename std::underlying_type<GnssId>::type;
570+ return static_cast<UT>(lhs) < static_cast<UT>(rhs);
571+}
572
573=== modified file 'src/location/providers/ubx/_8/gnss_id.h'
574--- src/location/providers/ubx/_8/gnss_id.h 2017-03-29 20:24:25 +0000
575+++ src/location/providers/ubx/_8/gnss_id.h 2017-03-29 20:24:25 +0000
576@@ -41,6 +41,7 @@
577
578 };
579
580+bool operator<(GnssId lhs, GnssId rhs);
581 std::ostream& operator<<(std::ostream& out, GnssId gnss_id);
582
583 } // namespace _8
584
585=== modified file 'src/location/providers/ubx/_8/message.h'
586--- src/location/providers/ubx/_8/message.h 2017-03-29 20:24:25 +0000
587+++ src/location/providers/ubx/_8/message.h 2017-03-29 20:24:25 +0000
588@@ -20,6 +20,7 @@
589 #include <location/providers/ubx/_8/ack/nak.h>
590 #include <location/providers/ubx/_8/cfg/gnss.h>
591 #include <location/providers/ubx/_8/cfg/msg.h>
592+#include <location/providers/ubx/_8/cfg/rst.h>
593 #include <location/providers/ubx/_8/nav/pvt.h>
594 #include <location/providers/ubx/_8/nav/sat.h>
595
596@@ -39,6 +40,7 @@
597 ack::Nak,
598 cfg::Gnss,
599 cfg::Msg,
600+ cfg::Rst,
601 nav::Pvt,
602 nav::Sat
603 >;
604
605=== modified file 'src/location/providers/ubx/_8/nmea/scanner.cpp'
606--- src/location/providers/ubx/_8/nmea/scanner.cpp 2017-03-29 20:24:25 +0000
607+++ src/location/providers/ubx/_8/nmea/scanner.cpp 2017-03-29 20:24:25 +0000
608@@ -23,6 +23,7 @@
609 switch (state) {
610 case Expect::dollar:
611 if (c == '$') {
612+ ss = std::stringstream{};
613 ss << c;
614 state = Expect::more_data;
615 }
616@@ -58,8 +59,7 @@
617 if (state != Expect::nothing_more) throw std::runtime_error{"Incomplete"};
618
619 auto result = ss.str();
620- ss.str("");
621- ss.clear();
622+ ss = std::stringstream{};
623 state = Expect::dollar;
624
625 return result;
626
627=== modified file 'src/location/providers/ubx/_8/receiver.cpp'
628--- src/location/providers/ubx/_8/receiver.cpp 2017-03-29 20:24:25 +0000
629+++ src/location/providers/ubx/_8/receiver.cpp 2017-03-29 20:24:25 +0000
630@@ -26,6 +26,11 @@
631 {
632 return ubx::_8::encode_message(msg);
633 }
634+
635+ std::vector<std::uint8_t> operator()(const ubx::_8::cfg::Rst& rst) const
636+ {
637+ return ubx::_8::encode_message(rst);
638+ }
639 };
640
641 } // namespace
642@@ -44,11 +49,35 @@
643 auto result = ubx_scanner.update(*it);
644
645 if (std::get<0>(result) == Scanner::Expect::nothing_more)
646- monitor->on_new_ubx_message(ubx_scanner.finalize());
647- if (!std::get<1>(result))
648+ {
649+ try
650+ {
651+ monitor->on_new_ubx_message(ubx_scanner.finalize());
652+ }
653+ catch (...)
654+ {
655+ // Dropping the exception as there is hardly any reasonable measure
656+ // we can take. Both scanners are designed to recover from issues, and we
657+ // we just trap the exception here to guarantee that we keep on consuming the
658+ // entire buffer.
659+ }
660+ }
661+ else if (!std::get<1>(result))
662 {
663 if (nmea::Scanner::Expect::nothing_more == nmea_scanner.update(*it))
664- monitor->on_new_nmea_sentence(nmea::parse_sentence(nmea_scanner.finalize()));
665+ {
666+ try
667+ {
668+ monitor->on_new_nmea_sentence(nmea::parse_sentence(nmea_scanner.finalize()));
669+ }
670+ catch (...)
671+ {
672+ // Dropping the exception as there is hardly any reasonable measure
673+ // we can take. Both scanners are designed to recover from issues, and we
674+ // we just trap the exception here to guarantee that we keep on consuming the
675+ // entire buffer.
676+ }
677+ }
678 }
679 ++it;
680 }
681
682=== modified file 'src/location/providers/ubx/_8/receiver.h'
683--- src/location/providers/ubx/_8/receiver.h 2017-03-29 20:24:25 +0000
684+++ src/location/providers/ubx/_8/receiver.h 2017-03-29 20:24:25 +0000
685@@ -38,7 +38,7 @@
686 class Receiver
687 {
688 public:
689- using Buffer = std::array<char, 1024>;
690+ using Buffer = std::array<std::uint8_t, 4096>;
691
692 /// @brief Monitor provides calling code with means for monitoring
693 /// receiver operation.
694@@ -66,6 +66,9 @@
695 /// @brief send_message encodes and sends 'message' to the receiver.
696 void send_message(const Message& message);
697
698+ /// @brief send_encoded_message sends out data to the receiver.
699+ virtual void send_encoded_message(const std::vector<std::uint8_t> &data) = 0;
700+
701 protected:
702 /// @brief Receiver initializes a new instance with monitor
703 ///
704@@ -78,9 +81,6 @@
705 /// Calls out to a configured monitor instance for announcing results.
706 void process_chunk(Buffer::iterator it, Buffer::iterator itE);
707
708- /// @brief send_encoded_message sends out data to the receiver.
709- virtual void send_encoded_message(const std::vector<std::uint8_t>& data) = 0;
710-
711 private:
712 std::shared_ptr<Monitor> monitor;
713 ubx::_8::nmea::Scanner nmea_scanner;
714
715=== modified file 'src/location/providers/ubx/_8/scanner.cpp'
716--- src/location/providers/ubx/_8/scanner.cpp 2017-03-29 20:24:25 +0000
717+++ src/location/providers/ubx/_8/scanner.cpp 2017-03-29 20:24:25 +0000
718@@ -65,15 +65,12 @@
719 {
720 bool consumed = false;
721
722- // TODO(tvoss): This lacks a lot of validiation and verification.
723- // UBX allows us to partially parse while we scan and carry out online
724- // checksum calculation. Ideally, we would have a common class State that
725- // captures behavior and transition logic.
726 switch (next)
727 {
728 case Expect::sync_char_1:
729 if (c == sync_char_1)
730 {
731+ reset();
732 next = Expect::sync_char_2;
733 consumed = true;
734 }
735@@ -143,16 +140,27 @@
736 if (next != Expect::nothing_more)
737 throw std::logic_error{"Not ready for extraction."};
738
739+ struct Scope
740+ {
741+ Scope(Scanner& scanner) : scanner{scanner} {}
742+ ~Scope() { scanner.reset(); }
743+
744+ Scanner& scanner;
745+ } scope{*this};
746+
747 if (ck_a != checksum.ck_a() || ck_b != checksum.ck_b())
748- throw std::runtime_error{"Verification failed."};
749+ throw std::runtime_error("Failed to verify ubx protocol message integrity.");
750
751 auto it = factories.find(std::make_tuple(class_id, message_id));
752
753 if (it == factories.end())
754- throw std::runtime_error{"Could not decode message."};
755-
756- auto result = it->second(payload);
757-
758+ throw std::runtime_error{"Failed to decode ubx protocol message."};
759+
760+ return it->second(payload);
761+}
762+
763+void _8::Scanner::reset()
764+{
765 checksum = Checksum{};
766 next = Expect::sync_char_1;
767 class_id = 0;
768@@ -161,6 +169,4 @@
769 payload.clear();
770 payload_iterator = payload.end();
771 ck_a = ck_b = 0;
772-
773- return result;
774 }
775
776=== modified file 'src/location/providers/ubx/_8/scanner.h'
777--- src/location/providers/ubx/_8/scanner.h 2017-03-29 20:24:25 +0000
778+++ src/location/providers/ubx/_8/scanner.h 2017-03-29 20:24:25 +0000
779@@ -58,6 +58,9 @@
780 Message finalize();
781
782 private:
783+
784+ void reset();
785+
786 Expect next;
787 Checksum checksum;
788 std::uint8_t class_id;
789
790=== modified file 'src/location/providers/ubx/_8/serial_port_receiver.cpp'
791--- src/location/providers/ubx/_8/serial_port_receiver.cpp 2017-03-29 20:24:25 +0000
792+++ src/location/providers/ubx/_8/serial_port_receiver.cpp 2017-03-29 20:24:25 +0000
793@@ -15,51 +15,63 @@
794
795 ubx::_8::SerialPortReceiver::SerialPortReceiver(boost::asio::io_service& ios, const boost::filesystem::path& dev,
796 const std::shared_ptr<Monitor>& monitor)
797- : Receiver{monitor}, ios{ios}, sp{ios, dev.string().c_str()}
798+ : Receiver{monitor}, ios{ios}, serial_port{ios, dev.string().c_str()}
799 {
800+ serial_port.set_option(boost::asio::serial_port::baud_rate(9600));
801 }
802
803 void ubx::_8::SerialPortReceiver::send_encoded_message(const std::vector<std::uint8_t>& data)
804 {
805- ios.dispatch([this, data]()
806+ auto thiz = shared_from_this();
807+ std::weak_ptr<SerialPortReceiver> wp{thiz};
808+
809+ ios.dispatch([this, wp, data]()
810 {
811- boost::asio::write(sp, boost::asio::buffer(data), boost::asio::transfer_all());
812+ if (auto sp = wp.lock())
813+ boost::asio::write(serial_port, boost::asio::buffer(data), boost::asio::transfer_all());
814 });
815 }
816
817 void ubx::_8::SerialPortReceiver::start()
818 {
819+ ::tcflush(serial_port.lowest_layer().native_handle(), TCIFLUSH);
820 start_read();
821 }
822
823 void ubx::_8::SerialPortReceiver::stop()
824 {
825- sp.cancel();
826+ serial_port.cancel();
827 }
828
829 void ubx::_8::SerialPortReceiver::start_read()
830 {
831 auto thiz = shared_from_this();
832- boost::asio::async_read(sp, boost::asio::buffer(&buffer.front(), buffer.size()),
833- [thiz, this](const boost::system::error_code& ec, std::size_t transferred) {
834+ std::weak_ptr<SerialPortReceiver> wp{thiz};
835+
836+ boost::asio::async_read(serial_port, boost::asio::buffer(&buffer.front(), buffer.size()),
837+ [this, wp](const boost::system::error_code& ec, std::size_t transferred) {
838 if (ec == boost::asio::error::operation_aborted)
839 return;
840- if (not ec)
841+
842+ if (auto sp = wp.lock())
843 {
844- try
845- {
846- process_chunk(buffer.begin(), buffer.begin() + transferred);
847- }
848- catch(const std::exception& e)
849- {
850- LOG(WARNING) << "Error processing NMEA chunk: " << e.what();
851- }
852- catch(...)
853- {
854- LOG(WARNING) << "Error processing NMEA chunk.";
855- }
856+ if (not ec)
857+ {
858+ try
859+ {
860+ process_chunk(buffer.begin(), buffer.begin() + transferred);
861+ }
862+ catch(const std::exception& e)
863+ {
864+ LOG(WARNING) << "Error processing data chunk: " << e.what();
865+ }
866+ catch(...)
867+ {
868+ LOG(WARNING) << "Error processing data chunk.";
869+ }
870
871- start_read();
872+ start_read();
873+ }
874 }
875 });
876 }
877
878=== modified file 'src/location/providers/ubx/_8/serial_port_receiver.h'
879--- src/location/providers/ubx/_8/serial_port_receiver.h 2017-03-29 20:24:25 +0000
880+++ src/location/providers/ubx/_8/serial_port_receiver.h 2017-03-29 20:24:25 +0000
881@@ -21,6 +21,7 @@
882 #include <boost/filesystem.hpp>
883
884 #include <array>
885+#include <atomic>
886
887 namespace location
888 {
889@@ -44,10 +45,15 @@
890 void start();
891 void stop();
892
893-protected:
894 void send_encoded_message(const std::vector<std::uint8_t>& data) override;
895
896 private:
897+ enum class State
898+ {
899+ running,
900+ stopped
901+ };
902+
903 /// @brief Receiver initializes a new instance opening the serial port
904 /// located at path.
905 ///
906@@ -65,7 +71,7 @@
907
908 Receiver::Buffer buffer;
909 boost::asio::io_service& ios;
910- boost::asio::serial_port sp;
911+ boost::asio::serial_port serial_port;
912 };
913 }
914 }
915
916=== modified file 'src/location/providers/ubx/provider.cpp'
917--- src/location/providers/ubx/provider.cpp 2017-03-29 20:24:25 +0000
918+++ src/location/providers/ubx/provider.cpp 2017-03-29 20:24:25 +0000
919@@ -20,6 +20,7 @@
920
921 #include <location/logging.h>
922 #include <location/runtime.h>
923+#include <location/events/reference_position_updated.h>
924 #include <location/glib/runtime.h>
925
926 #include <location/providers/ubx/_8/cfg/gnss.h>
927@@ -27,8 +28,11 @@
928 #include <location/providers/ubx/_8/nav/pvt.h>
929 #include <location/providers/ubx/_8/nav/sat.h>
930
931+#include <core/net/http/client.h>
932 #include <core/posix/this_process.h>
933
934+#include <boost/lexical_cast.hpp>
935+
936 #include <fstream>
937 #include <iostream>
938 #include <iterator>
939@@ -37,31 +41,54 @@
940 namespace env = core::posix::this_process::env;
941 namespace ubx = location::providers::ubx;
942
943+namespace
944+{
945+
946+struct SettingsHelper
947+{
948+ template<typename T>
949+ static T get_value(std::string key, T&& default_value)
950+ {
951+ static const std::string snap_path = env::get("SNAP_DATA");
952+
953+ boost::filesystem::path path{snap_path};
954+ std::replace(key.begin(), key.end(), '.', '/');
955+ path /= key;
956+
957+ LOG(INFO) << "Reading setting from " << path.string();
958+
959+ std::ifstream in{path.string().c_str()};
960+ T value{default_value}; in >> value;
961+
962+ return value;
963+ }
964+};
965+
966+}
967+
968 std::string ubx::Provider::class_name()
969 {
970 return "ubx::Provider";
971 }
972
973-ubx::Provider::Monitor::Monitor(Provider* provider) : provider{provider}
974-{
975-}
976-
977 void ubx::Provider::Monitor::on_new_ubx_message(const _8::Message& message)
978 {
979 VLOG(1) << message;
980- if (provider->protocol == Provider::Protocol::ubx)
981- boost::apply_visitor(*this, message);
982+ if (auto sp = provider.lock())
983+ if (sp->configuration.protocol == Provider::Protocol::ubx)
984+ boost::apply_visitor(*this, message);
985 }
986
987
988 void ubx::Provider::Monitor::on_new_nmea_sentence(const _8::nmea::Sentence& sentence)
989 {
990 VLOG(1) << sentence;
991- if (provider->protocol == Provider::Protocol::nmea)
992- boost::apply_visitor(*this, sentence);
993+ if (auto sp = provider.lock())
994+ if (sp->configuration.protocol == Provider::Protocol::nmea)
995+ boost::apply_visitor(*this, sentence);
996 }
997
998-void ubx::Provider::Monitor::operator()(const _8::nmea::Gga& gga) const
999+void ubx::Provider::Monitor::operator()(const _8::nmea::Gga& gga)
1000 {
1001 if (gga.latitude && gga.longitude)
1002 {
1003@@ -82,14 +109,19 @@
1004 if (gga.hdop)
1005 position.accuracy().horizontal(gga.hdop.get() * 3. * units::meters);
1006
1007- glib::Runtime::instance()->dispatch([this, position]()
1008+ auto thiz = shared_from_this();
1009+ std::weak_ptr<ubx::Provider::Monitor> wp{thiz};
1010+
1011+ glib::Runtime::instance()->dispatch([position, wp]()
1012 {
1013- provider->updates.position(location::Update<location::Position>{position});
1014+ if (auto sp = wp.lock())
1015+ if (auto spp = sp->provider.lock())
1016+ spp->updates.position(location::Update<location::Position>{position});
1017 });
1018 }
1019 }
1020
1021-void ubx::Provider::Monitor::operator()(const _8::nav::Pvt& pvt) const
1022+void ubx::Provider::Monitor::operator()(const _8::nav::Pvt& pvt)
1023 {
1024 if (pvt.fix_type == _8::nav::Pvt::FixType::no_fix)
1025 return;
1026@@ -110,72 +142,218 @@
1027 units::Degrees heading = pvt.heading.vehicle * units::degrees;
1028 units::MetersPerSecond speed = pvt.speed_over_ground * 1e-3 * units::meters_per_second;
1029
1030- glib::Runtime::instance()->dispatch([this, position, heading, speed]()
1031+ auto thiz = shared_from_this();
1032+ std::weak_ptr<ubx::Provider::Monitor> wp{thiz};
1033+
1034+ glib::Runtime::instance()->dispatch([position, heading, speed, wp]()
1035 {
1036- provider->updates.position(location::Update<location::Position>{position});
1037- provider->updates.heading(location::Update<units::Degrees>{heading});
1038- provider->updates.velocity(location::Update<units::MetersPerSecond>{speed});
1039+ if (auto sp = wp.lock())
1040+ {
1041+ if (auto spp = sp->provider.lock())
1042+ {
1043+ spp->updates.position(location::Update<location::Position>{position});
1044+ spp->updates.heading(location::Update<units::Degrees>{heading});
1045+ spp->updates.velocity(location::Update<units::MetersPerSecond>{speed});
1046+ }
1047+ }
1048 });
1049 }
1050
1051-void ubx::Provider::Monitor::operator()(const _8::nmea::Gsa&) const
1052-{
1053- // Empty on purpose
1054-}
1055-
1056-void ubx::Provider::Monitor::operator()(const _8::nmea::Gll&) const
1057-{
1058- // Empty on purpose
1059-}
1060-
1061-void ubx::Provider::Monitor::operator()(const _8::nmea::Gsv&) const
1062-{
1063- // Empty on purpose
1064-}
1065-
1066-void ubx::Provider::Monitor::operator()(const _8::nmea::Rmc&) const
1067-{
1068- // Empty on purpose
1069-}
1070-
1071-void ubx::Provider::Monitor::operator()(const _8::nmea::Txt&) const
1072-{
1073- // Empty on purpose
1074-}
1075-
1076-void ubx::Provider::Monitor::operator()(const _8::nmea::Vtg& vtg) const
1077-{
1078- glib::Runtime::instance()->dispatch([this, vtg]()
1079+void ubx::Provider::Monitor::operator()(const _8::nmea::Gsa&)
1080+{
1081+ // Empty on purpose
1082+}
1083+
1084+void ubx::Provider::Monitor::operator()(const _8::nmea::Gll&)
1085+{
1086+ // Empty on purpose
1087+}
1088+
1089+void ubx::Provider::Monitor::operator()(const _8::nmea::Gsv&)
1090+{
1091+ // Empty on purpose
1092+}
1093+
1094+void ubx::Provider::Monitor::operator()(const _8::nmea::Rmc&)
1095+{
1096+ // Empty on purpose
1097+}
1098+
1099+void ubx::Provider::Monitor::operator()(const _8::nmea::Txt&)
1100+{
1101+ // Empty on purpose
1102+}
1103+
1104+void ubx::Provider::Monitor::operator()(const _8::nmea::Vtg& vtg)
1105+{
1106+ auto thiz = shared_from_this();
1107+ std::weak_ptr<ubx::Provider::Monitor> wp{thiz};
1108+
1109+ glib::Runtime::instance()->dispatch([vtg, wp]()
1110 {
1111- if (vtg.cog_true)
1112- provider->updates.heading(
1113- Update<units::Degrees>(
1114- vtg.cog_true.get() * units::degrees));
1115- if (vtg.sog_kmh)
1116- provider->updates.velocity(
1117- Update<units::MetersPerSecond>(
1118- vtg.sog_kmh.get() * 1000./3600. * units::meters_per_second));
1119+ if (auto sp = wp.lock())
1120+ {
1121+ if (vtg.cog_true)
1122+ if (auto spp = sp->provider.lock())
1123+ spp->updates.heading(
1124+ Update<units::Degrees>(
1125+ vtg.cog_true.get() * units::degrees));
1126+ if (vtg.sog_kmh)
1127+ if (auto spp = sp->provider.lock())
1128+ spp->updates.velocity(
1129+ Update<units::MetersPerSecond>(
1130+ vtg.sog_kmh.get() * 1000./3600. * units::meters_per_second));
1131+ }
1132 });
1133 }
1134
1135 location::Provider::Ptr ubx::Provider::create_instance(const location::ProviderFactory::Configuration& config)
1136 {
1137- std::string device_path = "/dev/ttyACM0";
1138- std::ifstream in(env::get("SNAP_DATA") + "/ubx/provider/path");
1139- in >> device_path;
1140-
1141- return location::Provider::Ptr{new ubx::Provider{
1142- Protocol::ubx, config.get<std::string>("device", device_path)}};
1143-}
1144-
1145-ubx::Provider::Provider(Protocol protocol, const boost::filesystem::path& device)
1146- : protocol{protocol},
1147+ Configuration configuration
1148+ {
1149+ Protocol::ubx,
1150+ config.get<std::string>(
1151+ "device", SettingsHelper::get_value<std::string>(
1152+ "ubx.provider.path",
1153+ "/dev/ttyACM1"
1154+ )
1155+ ),
1156+ {
1157+ SettingsHelper::get_value<std::string>(
1158+ "ubx.provider.assist_now.enable",
1159+ "false"
1160+ ) == "true",
1161+ SettingsHelper::get_value<std::string>(
1162+ "ubx.provider.assist_now.token",
1163+ ""
1164+ ),
1165+ boost::posix_time::seconds(
1166+ boost::lexical_cast<std::uint64_t>(
1167+ SettingsHelper::get_value<std::string>(
1168+ "ubx.provider.assist_now.acquisition_timeout",
1169+ "5"
1170+ )
1171+ )
1172+ )
1173+ }
1174+ };
1175+
1176+ return ubx::Provider::create(configuration);
1177+}
1178+
1179+// Create a new instance with configuration.
1180+std::shared_ptr<ubx::Provider> ubx::Provider::create(const Configuration& configuration)
1181+{
1182+ auto sp = std::shared_ptr<Provider>{new Provider{configuration}};
1183+ return sp->finalize_construction();
1184+}
1185+
1186+
1187+ubx::Provider::Provider(const Configuration& configuration)
1188+ : configuration{configuration},
1189 runtime{location::Runtime::create(1)},
1190- monitor{std::make_shared<Monitor>(this)},
1191- receiver{_8::SerialPortReceiver::create(runtime->service(), device, monitor)}
1192-{
1193+ monitor{std::make_shared<Monitor>()},
1194+ receiver{_8::SerialPortReceiver::create(runtime->service(), configuration.device, monitor)},
1195+ assist_now_online_client{std::make_shared<_8::AssistNowOnlineClient>(core::net::http::make_client())},
1196+ acquisition_timer{runtime->service()}
1197+{
1198 runtime->start();
1199
1200+ configure_gnss();
1201+ configure_protocol();
1202+}
1203+
1204+ubx::Provider::~Provider() noexcept
1205+{
1206+ deactivate();
1207+ runtime->stop();
1208+}
1209+
1210+void ubx::Provider::reset()
1211+{
1212+ receiver->send_message(_8::cfg::Rst{_8::cfg::Rst::Bits::cold_start, _8::cfg::Rst::Mode::controlled_software_reset_gnss});
1213+}
1214+
1215+void ubx::Provider::on_new_event(const Event&)
1216+{
1217+ // TODO(tvoss): Use incoming reference position updates
1218+ // to query assistance data.
1219+}
1220+
1221+location::Provider::Requirements ubx::Provider::requirements() const
1222+{
1223+ return Requirements::none;
1224+}
1225+
1226+bool ubx::Provider::satisfies(const location::Criteria&)
1227+{
1228+ return true;
1229+}
1230+
1231+void ubx::Provider::enable()
1232+{
1233+}
1234+
1235+void ubx::Provider::disable()
1236+{
1237+}
1238+
1239+void ubx::Provider::activate()
1240+{
1241+ receiver->start();
1242+
1243+ if (configuration.assist_now.enable)
1244+ {
1245+ auto thiz = shared_from_this();
1246+ std::weak_ptr<Provider> wp{thiz};
1247+
1248+ acquisition_timer.expires_from_now(configuration.assist_now.acquisition_timeout);
1249+ acquisition_timer.async_wait([this, wp](boost::system::error_code ec)
1250+ {
1251+ if (auto sp = wp.lock())
1252+ if (!ec) request_assist_now_online_data(Optional<Position>{});
1253+ });
1254+ }
1255+}
1256+
1257+void ubx::Provider::deactivate()
1258+{
1259+ receiver->stop();
1260+ acquisition_timer.cancel();
1261+}
1262+
1263+const core::Signal<location::Update<location::Position>>& ubx::Provider::position_updates() const
1264+{
1265+ return updates.position;
1266+}
1267+
1268+const core::Signal<location::Update<location::units::Degrees>>& ubx::Provider::heading_updates() const
1269+{
1270+ return updates.heading;
1271+}
1272+
1273+const core::Signal<location::Update<location::units::MetersPerSecond>>& ubx::Provider::velocity_updates() const
1274+{
1275+ return updates.velocity;
1276+}
1277+
1278+std::shared_ptr<ubx::Provider> ubx::Provider::finalize_construction()
1279+{
1280+ auto thiz = shared_from_this();
1281+ std::weak_ptr<ubx::Provider> wp{thiz};
1282+
1283+ updates.position.connect([wp](const Update<Position>&)
1284+ {
1285+ if (auto sp = wp.lock())
1286+ sp->acquisition_timer.cancel();
1287+ });
1288+
1289+ monitor->provider = wp;
1290+ return thiz;
1291+}
1292+
1293+void ubx::Provider::configure_gnss()
1294+{
1295 _8::cfg::Gnss::Gps gps;
1296 gps.l1ca = true;
1297 gps.enable = true;
1298@@ -207,8 +385,11 @@
1299 gnss.sbas = sbas;
1300
1301 receiver->send_message(gnss);
1302+}
1303
1304- if (protocol == Protocol::ubx)
1305+void ubx::Provider::configure_protocol()
1306+{
1307+ if (configuration.protocol == Protocol::ubx)
1308 {
1309 _8::cfg::Msg cfg_msg{ubx::_8::nav::Pvt::class_id, ubx::_8::nav::Pvt::message_id, { 0 }};
1310 cfg_msg.rate[_8::cfg::Msg::Port::usb] = 1;
1311@@ -221,55 +402,47 @@
1312 }
1313 }
1314
1315-ubx::Provider::~Provider() noexcept
1316-{
1317- deactivate();
1318- runtime->stop();
1319-}
1320-
1321-void ubx::Provider::on_new_event(const Event&)
1322-{
1323-}
1324-
1325-location::Provider::Requirements ubx::Provider::requirements() const
1326-{
1327- return Requirements::none;
1328-}
1329-
1330-bool ubx::Provider::satisfies(const location::Criteria&)
1331-{
1332- return true;
1333-}
1334-
1335-void ubx::Provider::enable()
1336-{
1337-}
1338-
1339-void ubx::Provider::disable()
1340-{
1341-}
1342-
1343-void ubx::Provider::activate()
1344-{
1345- receiver->start();
1346-}
1347-
1348-void ubx::Provider::deactivate()
1349-{
1350- receiver->stop();
1351-}
1352-
1353-const core::Signal<location::Update<location::Position>>& ubx::Provider::position_updates() const
1354-{
1355- return updates.position;
1356-}
1357-
1358-const core::Signal<location::Update<location::units::Degrees>>& ubx::Provider::heading_updates() const
1359-{
1360- return updates.heading;
1361-}
1362-
1363-const core::Signal<location::Update<location::units::MetersPerSecond>>& ubx::Provider::velocity_updates() const
1364-{
1365- return updates.velocity;
1366+void ubx::Provider::request_assist_now_online_data(const Optional<Position>& position)
1367+{
1368+ auto thiz = shared_from_this();
1369+ std::weak_ptr<Provider> wp{thiz};
1370+
1371+ _8::AssistNowOnlineClient::Parameters params;
1372+ params.token = configuration.assist_now.token;
1373+ params.gnss = {_8::GnssId::gps, _8::GnssId::glonass, _8::GnssId::galileo};
1374+ params.data_types =
1375+ {
1376+ _8::AssistNowOnlineClient::DataType::almanac,
1377+ _8::AssistNowOnlineClient::DataType::ephemeris
1378+ };
1379+ params.position = position;
1380+
1381+ assist_now_online_client->request_assistance_data(params, [this, wp](const Result<std::string>& result)
1382+ {
1383+ if (result)
1384+ {
1385+ if (auto sp = wp.lock())
1386+ {
1387+ LOG(INFO) << "Successfully queried assistance data, injecting into chipset now.";
1388+ receiver->send_encoded_message(
1389+ std::vector<std::uint8_t>(
1390+ result.value().begin(), result.value().end()));
1391+ }
1392+ }
1393+ else
1394+ {
1395+ try
1396+ {
1397+ result.rethrow();
1398+ }
1399+ catch (const std::exception& e)
1400+ {
1401+ LOG(WARNING) << "Failed to query AssistNow for aiding data: " << e.what() << std::endl;
1402+ }
1403+ catch (...)
1404+ {
1405+ LOG(WARNING) << "Failed to query AssistNow for aiding data." << std::endl;
1406+ }
1407+ }
1408+ });
1409 }
1410
1411=== modified file 'src/location/providers/ubx/provider.h'
1412--- src/location/providers/ubx/provider.h 2017-03-29 20:24:25 +0000
1413+++ src/location/providers/ubx/provider.h 2017-03-29 20:24:25 +0000
1414@@ -22,6 +22,7 @@
1415 #include <location/provider_factory.h>
1416 #include <location/runtime.h>
1417
1418+#include <location/providers/ubx/_8/assist_now_online_client.h>
1419 #include <location/providers/ubx/_8/serial_port_receiver.h>
1420
1421 #include <boost/filesystem.hpp>
1422@@ -43,9 +44,28 @@
1423 //
1424 // Configuration parameters:
1425 // - device[=/dev/ttyUSB1] serial device connecting to the receiver.
1426-class Provider : public location::Provider
1427+class Provider : public location::Provider, public std::enable_shared_from_this<Provider>
1428 {
1429 public:
1430+ enum class Protocol
1431+ {
1432+ ubx, // Rely on ubx.
1433+ nmea // Rely on nmea.
1434+ };
1435+
1436+ // Configuration bundles all construction time parameters.
1437+ struct Configuration
1438+ {
1439+ Protocol protocol; // The protocol used for communicating with the receiver.
1440+ boost::filesystem::path device; // Serial device used for communicating with the receiver.
1441+ struct
1442+ {
1443+ bool enable; // Whether or not the provider should use AssistNow.
1444+ std::string token; // Token for validating requests to the AssistNow service.
1445+ boost::posix_time::seconds acquisition_timeout; // Query assistance data after this many seconds.
1446+ } assist_now; // All parameters for configuring AssistNow go here.
1447+ };
1448+
1449 // For integration with the Provider factory.
1450 static std::string class_name();
1451 // Instantiates a new provider instance, populating the configuration object
1452@@ -53,17 +73,16 @@
1453 // for the list of known options.
1454 static Provider::Ptr create_instance(const ProviderFactory::Configuration&);
1455
1456- enum class Protocol
1457- {
1458- ubx, // Rely on ubx.
1459- nmea // Rely on nmea.
1460- };
1461+ // Create a new instance with configuration.
1462+ static std::shared_ptr<Provider> create(const Configuration& configuration);
1463
1464- // Creates a new provider instance talking via device to the ubx chipset.
1465- Provider(Protocol protocol, const boost::filesystem::path& device);
1466 // Cleans up all resources and stops the updates.
1467 ~Provider() noexcept;
1468
1469+ // Resets the chipset and drops all cached data.
1470+ // The next positioning request will be a cold start.
1471+ void reset();
1472+
1473 // From Provider
1474 void on_new_event(const Event& event) override;
1475
1476@@ -80,10 +99,8 @@
1477
1478 private:
1479 // Relays incoming sentences to a provider instance.
1480- struct Monitor : public _8::Receiver::Monitor, public boost::static_visitor<>
1481+ struct Monitor : public std::enable_shared_from_this<ubx::Provider::Monitor>, public _8::Receiver::Monitor, public boost::static_visitor<>
1482 {
1483- explicit Monitor(Provider* provider);
1484-
1485 // From Receiver::Monitor
1486 void on_new_ubx_message(const _8::Message& message) override;
1487 void on_new_nmea_sentence(const _8::nmea::Sentence& sentence) override;
1488@@ -91,23 +108,36 @@
1489 template<typename T>
1490 void operator()(const T&) const {}
1491
1492- void operator()(const _8::nav::Pvt& pvt) const;
1493-
1494- void operator()(const _8::nmea::Gga& gga) const;
1495- void operator()(const _8::nmea::Gsa& gsa) const;
1496- void operator()(const _8::nmea::Gll& gll) const;
1497- void operator()(const _8::nmea::Gsv& gsv) const;
1498- void operator()(const _8::nmea::Rmc& rmc) const;
1499- void operator()(const _8::nmea::Txt& txt) const;
1500- void operator()(const _8::nmea::Vtg& vtg) const;
1501-
1502- Provider* provider;
1503+ void operator()(const _8::nav::Pvt& pvt);
1504+
1505+ void operator()(const _8::nmea::Gga& gga);
1506+ void operator()(const _8::nmea::Gsa& gsa);
1507+ void operator()(const _8::nmea::Gll& gll);
1508+ void operator()(const _8::nmea::Gsv& gsv);
1509+ void operator()(const _8::nmea::Rmc& rmc);
1510+ void operator()(const _8::nmea::Txt& txt);
1511+ void operator()(const _8::nmea::Vtg& vtg);
1512+
1513+ std::weak_ptr<Provider> provider;
1514 };
1515
1516- Protocol protocol;
1517+ // Creates a new provider instance talking via device to the ubx chipset.
1518+ Provider(const Configuration& configuration);
1519+
1520+ std::shared_ptr<Provider> finalize_construction();
1521+
1522+ void configure_gnss();
1523+ void configure_protocol();
1524+
1525+ void request_assist_now_online_data(const Optional<Position>& position);
1526+
1527+ Configuration configuration;
1528 std::shared_ptr<location::Runtime> runtime;
1529 std::shared_ptr<Monitor> monitor;
1530 std::shared_ptr<_8::SerialPortReceiver> receiver;
1531+ std::shared_ptr<_8::AssistNowOnlineClient> assist_now_online_client;
1532+ boost::asio::deadline_timer acquisition_timer;
1533+
1534 struct
1535 {
1536 core::Signal<Update<Position>> position;
1537
1538=== modified file 'src/location/runtime_tests.cpp'
1539--- src/location/runtime_tests.cpp 2016-08-24 14:12:05 +0000
1540+++ src/location/runtime_tests.cpp 2017-03-29 20:24:25 +0000
1541@@ -20,12 +20,17 @@
1542 #include <location/runtime_tests.h>
1543
1544 #include <location/clock.h>
1545+#include <location/glib/runtime.h>
1546 #include <location/providers/gps/hardware_abstraction_layer.h>
1547+#include <location/providers/ubx/provider.h>
1548+
1549+#include <core/posix/this_process.h>
1550
1551 #include <boost/accumulators/accumulators.hpp>
1552 #include <boost/accumulators/statistics/stats.hpp>
1553 #include <boost/accumulators/statistics/mean.hpp>
1554 #include <boost/accumulators/statistics/variance.hpp>
1555+#include <boost/lexical_cast.hpp>
1556
1557 #include <cstdlib>
1558
1559@@ -35,6 +40,7 @@
1560 #include <mutex>
1561 #include <thread>
1562
1563+namespace env = core::posix::this_process::env;
1564 namespace gps = location::providers::gps;
1565
1566 namespace
1567@@ -172,14 +178,125 @@
1568 return 0;
1569 }
1570 #else
1571-int snr_and_ttff(std::ostream&, std::ostream&) { return 0; }
1572+int snr_and_ttff(const std::string& test_suite, std::ostream&, std::ostream&) { return 0; }
1573 #endif // LOCATION_PROVIDERS_GPS
1574+
1575+int ubx(std::ostream& cout, std::ostream& cerr)
1576+{
1577+ typedef boost::accumulators::accumulator_set<
1578+ double,
1579+ boost::accumulators::stats<
1580+ boost::accumulators::tag::mean,
1581+ boost::accumulators::tag::variance
1582+ >
1583+ > Statistics;
1584+
1585+ using boost::accumulators::mean;
1586+ using boost::accumulators::variance;
1587+
1588+ const unsigned int trials = boost::lexical_cast<unsigned int>(env::get("UBX_PROVIDER_TEST_TRIALS", "15"));
1589+
1590+ Statistics stats;
1591+ location::providers::ubx::Provider::Configuration configuration
1592+ {
1593+ location::providers::ubx::Provider::Protocol::ubx,
1594+ env::get_or_throw("UBX_PROVIDER_TEST_DEVICE"),
1595+ {
1596+ env::get("UBX_PROVIDER_TEST_ASSIST_NOW_ENABLE", "false") == "true",
1597+ env::get_or_throw("UBX_PROVIDER_TEST_ASSIST_NOW_TOKEN"),
1598+ boost::posix_time::seconds(
1599+ boost::lexical_cast<std::uint64_t>(
1600+ env::get("UBX_PROVIDER_TEST_ASSIST_NOW_ACQUISITION_TIMEOUT", "5")))
1601+ }
1602+ };
1603+ auto provider = location::providers::ubx::Provider::create(configuration);
1604+
1605+ struct State
1606+ {
1607+ State() : worker{[this]() { runtime.run(); }}, fix_received(false)
1608+ {
1609+ }
1610+
1611+ ~State()
1612+ {
1613+ runtime.stop();
1614+ if (worker.joinable())
1615+ worker.join();
1616+ }
1617+
1618+ bool wait_for_fix_for(const std::chrono::seconds& seconds)
1619+ {
1620+ std::unique_lock<std::mutex> ul(guard);
1621+ return wait_condition.wait_for(
1622+ ul,
1623+ seconds,
1624+ [this]() {return fix_received == true;});
1625+ }
1626+
1627+ void on_position_updated(const location::Position&)
1628+ {
1629+ fix_received = true;
1630+ wait_condition.notify_all();
1631+ }
1632+
1633+ void reset()
1634+ {
1635+ fix_received = false;
1636+ }
1637+
1638+ location::glib::Runtime runtime;
1639+ std::thread worker;
1640+ std::mutex guard;
1641+ std::condition_variable wait_condition;
1642+ bool fix_received;
1643+ } state;
1644+
1645+ provider->position_updates().connect([&state](const location::Update<location::Position>& update)
1646+ {
1647+ state.on_position_updated(update.value);
1648+ });
1649+
1650+ for (unsigned int i = 0; i < trials; i++)
1651+ {
1652+ provider->reset();
1653+
1654+ state.reset();
1655+ auto start = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
1656+ {
1657+ provider->activate();
1658+
1659+ // We expect a maximum cold start time of 15 minutes. The theoretical
1660+ // limit is 12.5 minutes, and we add up some grace period to make the
1661+ // test more robust (see http://en.wikipedia.org/wiki/Time_to_first_fix).
1662+ expect<true, std::runtime_error>(state.wait_for_fix_for(std::chrono::seconds{15 * 60}), "Wait for fix timed out.");
1663+ provider->deactivate();
1664+ }
1665+ auto stop = std::chrono::duration_cast<std::chrono::microseconds>(location::Clock::now().time_since_epoch());
1666+
1667+ stats((stop - start).count());
1668+ }
1669+
1670+ cout << "Mean time to first fix in [ms]: "
1671+ << std::chrono::duration_cast<std::chrono::milliseconds>(
1672+ std::chrono::microseconds(
1673+ static_cast<std::uint64_t>(mean(stats)))).count()
1674+ << std::endl;
1675+ cout << "Std.dev. in time to first fix in [ms]: "
1676+ << std::chrono::duration_cast<std::chrono::milliseconds>(
1677+ std::chrono::microseconds(
1678+ static_cast<std::uint64_t>(std::sqrt(variance(stats))))).count()
1679+ << std::endl;
1680+
1681+ return 0;
1682 }
1683
1684-int location::execute_runtime_tests(std::ostream& cout, std::ostream& cerr)
1685+} // namespace
1686+
1687+int location::execute_runtime_tests(const std::string& test_suite, std::ostream& cout, std::ostream& cerr)
1688 {
1689- Fixture fixture; // This throws in case of issues.
1690- auto rc = snr_and_ttff(cout, cerr); if (rc != 0) return rc;
1691- // Other runtime tests go here;
1692- return rc;
1693+ if (test_suite == "android-gps")
1694+ return snr_and_ttff(cout, cerr);
1695+ else if (test_suite == "ubx")
1696+ return ubx(cout, cerr);
1697+ return 0;
1698 }
1699
1700=== modified file 'src/location/runtime_tests.h'
1701--- src/location/runtime_tests.h 2016-06-30 09:13:21 +0000
1702+++ src/location/runtime_tests.h 2017-03-29 20:24:25 +0000
1703@@ -19,12 +19,13 @@
1704 #define LOCATION_RUNTIME_TESTS_H_
1705
1706 #include <iosfwd>
1707+#include <string>
1708
1709 namespace location
1710 {
1711 // execute_runtime_tests runs all configured runtime tests.
1712 // Returns 0 if successful.
1713-int execute_runtime_tests(std::ostream& cout, std::ostream& cerr);
1714+int execute_runtime_tests(const std::string& test_suite, std::ostream& cout, std::ostream& cerr);
1715 }
1716
1717 #endif // LOCATION_RUNTIME_TESTS_H_

Subscribers

People subscribed via source and target branches

to all changes: