GPSNet
GPSNet.h
1 // GPSNet.h
2 // Author: Mike McCauley
3 // Copyright (C) 2013 Mike McCauley
4 // $Id: GPSNet.h,v 1.1 2013/02/20 06:43:38 mikem Exp mikem $
5 
6 /// \mainpage GPSNet library for Arduino
7 ///
8 /// This is the Arduino GPSNet library.
9 /// GPSNet is an Arduino library and example sketch that provides a radio broadcast mesh
10 /// network among a number of GPS+RFM22 radio equipped nodes, such that all nodes in the
11 /// network know the GPS position of all other nodes.
12 ///
13 /// This is intended to help organisations that need visibility of the location of
14 /// a number of independent mobile units (people, vehicles etc), such as might be
15 /// used by emergency services, cooperating fishing vessels etc.
16 ///
17 /// The provided sample sketch works with any serial GPS receiver and RF22 data
18 /// radio transceivers on the 433MHz ISR band.
19 ///
20 /// The version of the package that this documentation refers to can be downloaded
21 /// from http://www.airspayce.com/mikem/arduino/GPSNet/GPSNet-1.6.zip
22 /// You can find the latest version at http://www.airspayce.com/mikem/arduino/GPSNet
23 ///
24 /// \par Operation
25 ///
26 /// GPSnet forms a loosly connected radio mesh network based on broadcast data
27 /// messages. The main principles of operation are:
28 ///
29 /// - Every node has a GPS receiver and radio transceiver.
30 /// - Every node has a unique NodeID string (stored in EEPROM).
31 /// - Periodically every node broadcasts a message with its
32 /// NodeID, and GPS position and timestamp.
33 /// - Every node listens for broadcasts from other nodes, and keeps in memory
34 /// the most recent data from each node.
35 /// - Every node periodically rebroadcasts the position of nodes it knows
36 /// about, with the preference given to data that changed most recently
37 /// (either from its own GPS or updates heard from other nodes).
38 ///
39 /// Additionally:
40 /// - Each node throttles its message boadcast rate depending on how many other
41 /// nodes are within range.
42 /// - Different sets of nodes can operate independent of each other on different
43 /// radio channels.
44 /// - Each node can be connected to an external device responsible for displaying
45 /// the relative location of all known nodes, or transmitting them by some other
46 /// technology to a central location
47 ///
48 /// This means that every node will eventually receive the position of all the
49 /// other nodes it can hear (either directly or indirectly). The latest position
50 /// of each node will propagate through the entire connected network (irrespective
51 /// of the network topology), giving the most widespread possible knowledge of the
52 /// entire fleet to every node, along with the age of the data.
53 ///
54 /// Every node can be a source of information about the entire fleet to a locally
55 /// connected PC (or maybe locally connected smartphone devices etc).
56 ///
57 /// One could conceive of a gateway node that sends data about all other nodes out
58 /// on an internet connection for more widespread visibility of node positions,
59 /// say to a remote command post etc.
60 ///
61 /// Note that each node has no specific routing or connectivity knowledge, and
62 /// each node has no idea if or how well it is connected to the rest of the
63 /// network or even if it is connected at all. So if a node goes out of range of
64 /// all the others, it wont 'know' that until its data gets stale.
65 ///
66 /// Caution: There are performance limitations:
67 ///
68 /// Each message (with ID, timestamp, GPS position, altitude, course, speed etc)
69 /// is 27 octets of payload data. At 2000 bps this is a maximum possible air rate
70 /// of only 8 messages per second, and perhaps, realistically (after accounting
71 /// for collisions etc) 5 per second. This means that if there are many nodes
72 /// within radio listening range of each other, each node has to severely
73 /// throttle its broadcast rate. (On the plus side, if there are many nodes near
74 /// each other the need to rebroadcast positions is less important, so maybe it
75 /// balances out, since the nodes throttle their broadcast rate based on how busy
76 /// the channel is)
77 ///
78 /// Operation depends on each node being within range of at least one other node,
79 /// which may be difficult with low power radios in wooded or hilly
80 /// terrain. Similarly with GPS reception which can be poor in heavily wooded
81 /// areas.
82 ///
83 /// \par Software organisation
84 ///
85 /// The example sketch requires the following additional software components:
86 ///
87 /// - TinyGPS http://arduiniana.org/libraries/tinygps/
88 /// - RF22 http://www.open.com.au/mikem/arduino/RF22
89 /// - GPSNet This library
90 ///
91 /// GPSNet is the main class. It is responsible for holding GPS reports, and
92 /// transmitting periodic updates to other nodes. GPSNet class is generic in the
93 /// sense it can be made to work with any type of GPS receiver and and type of
94 /// radio transceiver. The example sketch provides the glue between GPSNet and the
95 /// GPS and Radio.
96 ///
97 /// The example sketch uses TinyGPS to receive and decode NMEA sentences from the
98 /// GPS receiver, and to give GPS reports of the position of the local node to
99 /// GPSNet. It also uses RF22 to receive broadcast reports from other nodes and
100 /// also give them to GPSNet.
101 ///
102 /// Periodically, GPSNet use an external callback function in the sketch to
103 /// broadcast a report to other nodes using RF22.
104 ///
105 /// \par How to connect up your hardware
106 ///
107 /// The supplied sketch works with an Arduino Mega. Mega is required for:
108 /// - Large SRAM of 8kb, allowing a large number of nodes to be stored in memory
109 /// - More than 1 hardware serial port, required for reliable GPS connection.
110 ///
111 /// The default configuration of the sketch is for
112 ///
113 /// Serial1 (Arduino RX1 pin D19 hardware serial port) is connected to the TX pin
114 /// of the GPS. GPS is configured for 4800 baud 8N1 (though this can be changed by
115 /// editing the sketch.
116 ///
117 /// Serial (Arduino pins D0 and D1 hardware serial) is connected (as is usual) to
118 /// the standard Arduino USB-Serial port. Configuration commands can be sent to
119 /// and position updates can be received from the Arduino over this USB-Serial
120 /// connection. See below.
121 ///
122 /// Pins D2, D50, D51, D52, D53 connected to the RF22 transceiver as described in
123 /// the documentation of the Arduino RF22
124 /// library. http://www.open.com.au/mikem/arduino/RF22
125 ///
126 /// Caution: IO level shifters are required between the Arduino and the RF22 to
127 /// accommodate the 3.3V RF22 module.
128 ///
129 /// This software is Copyright (C) 2011 Mike McCauley. Use is subject to license
130 /// conditions. The main licensing options available are GPL V2 or Commercial:
131 ///
132 /// \par GPSNetViewer
133 ///
134 /// This is a simple Qt program that displays all the node positions received by a GPSNet node
135 /// on a Google Map. Download from http://www.airspayce.com/mikem/GPSNetViewer/gpsnetviewer1.0.tar.gz
136 ///
137 /// Sample screenshot at http://www.airspayce.com/mikem/GPSNetViewer/snapshot3.png
138 ///
139 /// Requires Qt, QtCreator and the qextserialport library from http://code.google.com/p/qextserialport/
140 /// Runs on Windows, Linux and Mac.
141 /// After building the program with something like:
142 /// \code
143 /// tar zxvf gpsnetviewer1.0.tar.gz
144 /// cd gpsnetviewer1.0
145 /// qmake
146 /// make
147 /// Connect an arduino running the gpsnet.ino sketch to your host
148 /// then run it with
149 /// ./qpsnetviewer
150 /// Select File->Configure
151 /// choose the serial port the Arduino gpsnet.ino sketch is connected to
152 /// OK.
153 /// See the "GPSNet Connected" go green, the received nodes shown in the right hand list and plottted
154 /// on the map. Select 'Follow Me' to centre the map on postion of the conencted GPSNet node.
155 /// \endcode
156 ///
157 /// \par Open Source Licensing GPL V2
158 ///
159 /// This is the appropriate option if you want to share the source code of your
160 /// application with everyone you distribute it to, and you also want to give them
161 /// the right to share who uses it. If you wish to use this software under Open
162 /// Source Licensing, you must contribute all your source code to the open source
163 /// community in accordance with the GPL Version 2 when your application is
164 /// distributed. See http://www.gnu.org/copyleft/gpl.html
165 ///
166 /// \par Commercial Licensing
167 ///
168 /// This is the appropriate option if you are creating proprietary applications
169 /// and you are not prepared to distribute and share the source code of your
170 /// application. Contact info@airspayce.com for details.
171 ///
172 /// \par Revision History
173 ///
174 /// \version 1.0 Initial release
175 /// \version 1.1 Added README and Doxygen documentation of all classes
176 /// \version 1.2 LICENSE and first public release
177 /// \version 1.3 Improvements to example sketch serial interface to make it easier to parse
178 /// outputs: prints "\nGPSNet\n" when it starts;
179 /// All node reports are preficed by "Report:"
180 /// \version 1.4 Added GPSNetViewer description and instructions
181 /// \version 1.5 Fixed broken link to distribution
182 /// \version 1.6 Fixed broken link to distribution
183 
184 #ifndef GPSNet_h
185 #define GPSNet_h
186 
187 #include <Arduino.h>
188 
189 /// \brief Records all the relevant details of a GPS position report, plus some
190 /// utility functions for operating on them.
191 ///
192 /// The id member contains the node ID of the node that originated this report.
193 /// Node IDs are expected to be unique across the network.
194 typedef struct GPSReport
195 {
196 
197 /// \def GPSNET_REPORT_ID_LEN
198 /// The length of the node ID, in octets. All octets are significant
199 #define GPSNET_REPORT_ID_LEN 8
200 
201 /// \def GPSNET_MAGIC_VERSION_1
202 /// Magic number and Version number to identify this as a GPSReport type 1
203 #define GPSNET_MAGIC_VERSION_1 0xa1
204 
205  /// Contructor.
206  /// Clears all members to 0.
207  GPSReport();
208 
209  /// Tests whether the report has a a given node ID
210  /// Returns true if there is an exact match over all octets of the ID
211  /// \param[in] id Pointer to array of GPSNET_REPORT_ID_LEN octets of identifier
212  /// \return true if there is an exact match
213  boolean hasId(uint8_t* id);
214 
215  /// Tests whether 2 GPSReports are identical in all respects
216  /// \param[in] that Pointer to the other report to test against
217  /// \return true if all members match exactly
218  boolean isIdenticalTo(GPSReport* that);
219 
220  /// Tests whether 2 GPSReports are geographically close to each other,
221  /// with specified margins.
222  /// param[in] that Pointer to the other report to test against
223  /// param[in] latlongError Allowable angular difference in latitude or longitude, in 1/100000ths of a
224  /// degree (1.1112m at the equator).
225  /// param[in] altitudeError Allowable error in metres
226  /// \return true if the latitudes, longitudes and altitudes of the 2 positions are within the specified margins
227  /// of each other.
228  boolean isCloseTo(GPSReport* that, uint32_t latlongError = 100, uint32_t altitudeError = 10);
229 
230  /// Tests whether the timestamp (date and time) of the other rport is later than
231  /// this one
232  /// \param[in] that Pointer to the other report to test against
233  /// \return true if the time or date is later
234  boolean isLaterThan(GPSReport* that);
235 
236  /// Print details of the report
237  /// to the Serial port
238  void print();
239 
240  uint8_t magic; ///< Magic number and version of this report format == GPSNET_MAGIC_VERSION_1
241  uint8_t id[GPSNET_REPORT_ID_LEN];///< Unique ID of the node originating the report
242  uint32_t date; ///< Current date as an integer (yymmdd)
243  uint32_t time; ///< Current time as an integer (hhmmss)
244  int32_t latitude; ///< Current latitude in 100000ths of a degree
245  int32_t longitude; ///< Current longitude in 100000ths of a degree
246  int16_t altitude; ///< Current altitude in m
247  uint16_t speed; ///< Current speed in knot
248  uint16_t course; ///< Current course in degrees
249  uint8_t flags; ///< GPSnet internal flags (not used at present)
250  uint8_t status; ///< External application specific status flags
251  uint8_t hops; ///< Num of radio hops from originator, 0 at originator
252 } GPSReport;
253 
254 
255 #define GPSNET_NUM_REPORTS 10
256 
257 /////////////////////////////////////////////////////////////////////
258 /// \class GPSNet GPSNet.h <GPSNet.h>
259 /// \brief Manage reception, storage and update transmission of GPSReports
260 ///
261 /// GPSNet implements a radio broadcast mesh
262 /// network among a number of GPS+radio equipped nodes, such that all nodes in the
263 /// network know the GPS position of all other nodes.
264 class GPSNet
265 {
266 
267 /// \def GPSNET_STATISTICS_WINDOW_SECS
268 /// Size of the reception rate moving window in seconds, used to calculate
269 /// the average report reception rate
270 #define GPSNET_STATISTICS_WINDOW_SECS 60
271 
272 /// \def GPSNET_TRANSMIT_TIME_JITTER
273 /// Amount of random jitter to add to transmit timer, in ms. Used to
274 /// randomly vary the transmit time, to minimise recurring transmission collisions.
275 #define GPSNET_TRANSMIT_TIME_JITTER 1000
276 
277  /// \brief A doubly linked list of GPSReports for internal use by GPSNet
278  /// Allows GPSReport array to be acessed by a doubly linked list
279  /// linked in order of scheduled update transmissions
280  /// For internal use by GPSNet only
281  typedef struct GPSReportList
282  {
283  uint8_t prev; ///< Index of the previous report
284  uint8_t next; ///< Index of the next report
285  GPSReport report; ///< The report
286  } GPSReportList;
287 
288 
289 public:
290 
291  /// Constructor
292  /// Intialises most members to 0
293  GPSNet();
294 
295  /// Tells GPSNet about a new report from the local GPS
296  /// You should only call this when there is a new position report
297  /// that is significantly different from the previous position.
298  /// See GPSReport::isCloseTo() for assistance)
299  /// \param[in] report Pointer to the new report
300  void newReport(GPSReport* report);
301 
302  /// Tells GPSNet about a new report received by radio from a remote node.
303  /// Reports entered this way are used to update the reception rate statistics.
304  /// \param[in] report Pointer to the report
305  void receivedReport(GPSReport* report);
306 
307  /// The number of currently known node reports
308  /// \return The number of currently known node reports
309  uint8_t numReports();
310 
311  /// Fetch a report with a given node ID, and optionally its index
312  /// \param[in] id Pointer to the node ID to match
313  /// \param[in,out] index If index is not NULL it points to a uint8_t that is to be set to
314  /// the index of the matching node (if found)
315  /// \return Pointer to the matching report if found, else NULL
316  GPSReport* reportWithId(uint8_t* id, uint8_t* index = NULL);
317 
318  /// Fetch the Report with the given index
319  /// \param[in] index The index of the report inthe GPSNet report list
320  /// \return Pointer to the Report at that index, if present, else NULL
321  GPSReport* reportWithIndex(uint8_t index);
322 
323  /// Prints a basic ASCII dump of all
324  /// current reports to the Serial port
325  void printReports();
326 
327  /// Do the internal work of the GPSNet node
328  /// You \b must call this frequently and often, but at least once a second.
329  /// It is recomended that this be called in the idle loop of the main sketch.
330  /// The transmitFunction may be called from within poll().
331  void poll();
332 
333  /// Tells GPSNet which function to call to broadcast an update report.
334  /// The calling sketch should implement the transmitFunction in terms of its local
335  /// radio interface
336  /// \param[in] transmitFunction Pointer to a function to call when an update is to be transmitted.
337  /// The function will be passed a pointer to this instance of GPSNet and the GPSReport to transmit
338  void setTransmitFunction(void (*transmitFunction)(GPSNet* net, GPSReport* report));
339 
340 protected:
341 
342  /// Unlink a report from the report list
343  /// Disconnects the report from the doubly linked list of reports in _reports
344  /// \param[in] index Index of th report to unlink
345  void unlinkReportWithIndex(uint8_t index);
346 
347  /// Link a report into the report list
348  /// Connects the report from the doubly linked list of reports in _reports
349  /// so it apears after the report whose index is given as after
350  /// param[in] index The index of the report to link in
351  /// param[in]after the index of the report it is to appear after
352  void linkReportWithIndexAfterIndex(uint8_t index, uint8_t after);
353 
354  /// Schedules a report in the _reports list for the next update transmission
355  /// It does this by moving it to the beginning of the linked list after the one indicated by
356  /// _lastUpdatedReportIndex
357  void scheduleReportForUpdate(uint8_t index);
358 
359  /// Transmits the next report scheduled for update.
360  /// Calls transmitUpdate() and passes it the next report.
361  /// Updates _lastUpdatedReportIndex to indicate the recently transmitted report
362  void transmitNextUpdate();
363 
364  /// Transmit an update.
365  /// Caller can override this funciton, or else can set a callback function
366  /// with setTransmitFunction()
367  /// \param[in] report The report to be transmitted
368  virtual void transmitUpdate(GPSReport* report);
369 
370  /// Recalculate _updateTransmitInterval
371  /// Based on the average of the recent message arrival rate, with a moving window
372  /// over a time period given by GPSNET_STATISTICS_WINDOW_SECS.
373  void evaluateReceptionRate();
374 
375  /// Execute various housekeeping tasks
376  /// required to be done once per second. Called automatically by poll() once per second.
377  void perSecondTasks();
378 
379 private:
380  /// If not NULL, this specifies a function to call whemn a report is to be
381  /// transmitted
382  void (*_transmitFunction)(GPSNet* net, GPSReport* report);
383 
384  uint32_t _lastSecond; // millis
385 
386  uint32_t _updateTransmitInterval; // ms between update transmit times
387  uint32_t _lastUpdateTransmitTime; // millis
388 
389  uint8_t _receivedReportsInLastSecond; // Number of reports received in last second
390  float _receptionRate;
391 
392  uint8_t _numReports; // Number of reports in _reports
393  uint8_t _lastUpdatedReportIndex; // Index of the next report to transmit
394  GPSReportList _reports[GPSNET_NUM_REPORTS]; // All the GPS reports we know about in update sequence
395 };
396 
397 /// @example gpsnet.ino
398 /// Arduino sketch to provide a node on a GPSnet broadcast network to communicate
399 /// GPS positions of all nodes throughout the network.
400 /// Uses RF22 radios to preiodically broadcast GPS data
401 /// to nearby nodes, who in turn broadcast to nodes near them etc.
402 ///
403 /// Includes serial GPS interface, RF22 radio interface and USB-serial
404 /// interface for configuration and emitting position reports.
405 /// Saves configuration to EEPROM
406 /// You can use this with the PC based viewer program GPSNetViewer discussed in the main
407 /// documentation.
408 
409 
410 #endif
GPSReport * reportWithIndex(uint8_t index)
Definition: GPSNet.cpp:163
int32_t longitude
Current longitude in 100000ths of a degree.
Definition: GPSNet.h:245
uint8_t status
External application specific status flags.
Definition: GPSNet.h:250
void poll()
Definition: GPSNet.cpp:232
uint8_t magic
Magic number and version of this report format == GPSNET_MAGIC_VERSION_1.
Definition: GPSNet.h:240
void linkReportWithIndexAfterIndex(uint8_t index, uint8_t after)
Definition: GPSNet.cpp:259
void evaluateReceptionRate()
Definition: GPSNet.cpp:213
void printReports()
Definition: GPSNet.cpp:170
uint8_t numReports()
Definition: GPSNet.cpp:94
boolean hasId(uint8_t *id)
Definition: GPSNet.cpp:22
void unlinkReportWithIndex(uint8_t index)
Definition: GPSNet.cpp:250
uint8_t flags
GPSnet internal flags (not used at present)
Definition: GPSNet.h:249
void scheduleReportForUpdate(uint8_t index)
Definition: GPSNet.cpp:99
boolean isLaterThan(GPSReport *that)
Definition: GPSNet.cpp:51
GPSReport * reportWithId(uint8_t *id, uint8_t *index=NULL)
Definition: GPSNet.cpp:148
virtual void transmitUpdate(GPSReport *report)
Definition: GPSNet.cpp:187
void transmitNextUpdate()
Definition: GPSNet.cpp:193
uint16_t speed
Current speed in knot.
Definition: GPSNet.h:247
void receivedReport(GPSReport *report)
Definition: GPSNet.cpp:203
GPSNet()
Definition: GPSNet.cpp:82
uint32_t time
Current time as an integer (hhmmss)
Definition: GPSNet.h:243
GPSReport()
Definition: GPSNet.cpp:7
void print()
Definition: GPSNet.cpp:57
Manage reception, storage and update transmission of GPSReports.
Definition: GPSNet.h:264
boolean isCloseTo(GPSReport *that, uint32_t latlongError=100, uint32_t altitudeError=10)
Definition: GPSNet.cpp:43
uint8_t hops
Num of radio hops from originator, 0 at originator.
Definition: GPSNet.h:251
void perSecondTasks()
Definition: GPSNet.cpp:225
int32_t latitude
Current latitude in 100000ths of a degree.
Definition: GPSNet.h:244
void newReport(GPSReport *report)
Definition: GPSNet.cpp:108
Records all the relevant details of a GPS position report, plus some utility functions for operating ...
Definition: GPSNet.h:194
boolean isIdenticalTo(GPSReport *that)
Definition: GPSNet.cpp:27
uint32_t date
Current date as an integer (yymmdd)
Definition: GPSNet.h:242
uint16_t course
Current course in degrees.
Definition: GPSNet.h:248
void setTransmitFunction(void(*transmitFunction)(GPSNet *net, GPSReport *report))
Definition: GPSNet.cpp:269
int16_t altitude
Current altitude in m.
Definition: GPSNet.h:246