a callback function accepting a the data object of the module's line handler is passed
or:
an array of tuples of lineHandlers and callbacks are passed.
Basically - for the implementation -
Paired / tuples - or chain - of line handlers and callbacks stored as handlers are used (implemented as array of 2-elements-arrays, with lineHandler as first and callback as second element of the 2-elements-arrays). A lineHandler looks at the line and calls the paired up callback with the handled line data. ggaHandler() - current module's handleGPSLine - is an example of a lineHandler. callback is an example of a callback and does the 'stuff' the GPS module user wants to have done for the line by the (paired up) lineHandler.
For each line from the GPS receiver, the module iterates over this array of handlers in handle() function and stop looping when handler did 'hit'. The handlers, such as ggaHandler() handling GGA tagged lines, return true when handling the line, otherwise return false (line 38).
The same data structure and algorithms are used for both 100% compatible basic and extended mode: if first tuple's lineHandler is null (evaluates to false), the modules basic ggaHandler() is reused for it (line 40).
Like line, handle() and handlers[] - and an extra enabled flag - are kept in the object returned by the connect() function
Btw, I would consider above approach a Lambda or functional approach, where after module load 'no' application nor object oriented object is (yet) available, but merely a bootstrap function, which is not even equally to a 'new' or a constructor. This 'into an object wrapped' function has first to be called in order to get a (useful) object (with state, and now also with function) back. - Btw, what is/was the rational of returning the {line:""} object on .connect()? - The module documentation http://www.espruino.com/Writing+Modules follows more the object-oriented approach, even though - due to the nature of JS - the 'class definition' is a function. For the GPS module I would like to have a similar approach chosen, even if the GPS stays a Singleton / would not have a constructor, so that already with require("GPS") an object is available for custom alteration. I will look into this option in a next phase while - of course - keeping 100% compatibility with current 'usage documentation'.
I still try to find what's right for Espruino. Having now lived many years - and still live - in a world of memory abundance, doing the right thing for Espruino is a welcome challenge. Not that I waste memory or cycles - I actually have the opposite habit - because I started out with having just 16KB (Kilo bytes) for application AND data AND ANY utility code - such as ISAM-create/read/update/delete, input routines, etc., and some of my Espruino-like hardware real world projects had just 256 bytes and 1KB EPROM and 4MHz 8-bit processing capabilities. Since then there is always the frugal guy in the back of my head watching over my spending of both memory and cycles, because even in today's memory and cycle paradise resources are limited... if not technically, then more so economically (currently building software for 15K+ concurrent transactional users - where user AND operator has to be kept happy: first one w/ snappy response times and latter one w/ spending 'no fancy' $s).
@Gordon, some pointers about the implementation of js in Espruino is very welcome here. I provide you an example in more detail at (see http://forum.espruino.com/conversations/255954) from code I'm working on: An array with a lot of small string elements in source code uses obviously more memory than one with few but long strings that later are chopped up into the small ones and stored again in the very same array. The short string are of the form of "keyNMO".
Back to the GPS discussion at hand:
Add the following code to the above one, send code to board, and use command go2() in the command pane to get the gps connected and handling lines (just) with the custom RMC line handler instead of the module's own ggaHandler. For simplicity, same already known callback is used.
var handlers =
[ [ function(line,callback) { // rmcHandler
var tag = line.substr(3,3);
if (tag=="RMC") {
var d = line.split(",");
callback(
{ timc: d[1].substr(0,6)
, latc: (parseInt(d[3].substr(0,2),10) + parseFloat(d[3].substr(2))/60) * (d[4]=="S"?-1:1)
, lonc: (parseInt(d[5].substr(0,3),10) + parseFloat(d[5].substr(3))/60) * (d[4]=="W"?-1:1)
, velc: parseFloat(d[7])
, angl: d[8]
, datc: d[9]
});
return true;
} else {
return false;
}
}
, callback
]
];
function go2() { // connects the gps and gets it handling the lines
console.log("gps.connect() >", gps = gps.connect(Serial4,handlers));
}
Output in console:
"timc": "103314",```
"angl": "",```
}```
"timc": "103315",```
"angl": "",```
}```
You may notice that the ggaHandler 'is lost' for good... (became inaccessible for good).
The module's valuable ggaHandler can though easily be reused/included in the custom handling. Just provide the first tuple of 'handler duos' with null (no) line handler. On connect, the module's ggaHandler will be pulled and paired up with the callback provided in the first handler duo. To make that obvious / visible, a different calback2 call back is provided.
Add the following code to the above one, send code to board, and use command go2() in the command pane to get the gps connected and handling now two lines, the GGA tagged line with the module's own line handler and callback2(), and the RMC tagged line with the custom line handler and initially established callback().
// special callback for in reuse of GPS's module own ggaHandler
var callback2 = function(data) {
console.log("cbk2 > ",data);
};
var handlers2 =
[ [ null // re-using GPS module's ggaHandler...
, callback2 // ...w/ callback2
]
, [ function(line,callback) { // rmcHandler
var tag = line.substr(3,3);
if (tag=="RMC") {
var d = line.split(",");
callback(
{ timc: d[1].substr(0,6)
, latc: (parseInt(d[3].substr(0,2),10) + parseFloat(d[3].substr(2))/60) * (d[4]=="S"?-1:1)
, lonc: (parseInt(d[5].substr(0,3),10) + parseFloat(d[5].substr(3))/60) * (d[4]=="W"?-1:1)
, velc: parseFloat(d[7])
, angl: d[8]
, datc: d[9]
});
return true;
} else {
return false;
}
}
, callback
]
];
function gox() { // connects the gps and gets it handling the lines
console.log("gps.connect() >", gps = gps.connect(Serial4,handlers2));
}
I'm not really sure about the overall value of enabled - but I liked the option to to shut up the console... - last but not least for copying console output to this post without having a nervously scrolling away text - or, in the target application - to quiet down the display... ;)
What should be discussed is the value of the try-catch-block. In different context I had issues with the process stopping after some time - less than one hour - and errors showing in the console. Especially with line handlers that do not take into account when the GPS is not yet providing complete data, for example, while still trying to get enough satellites for decently reliable position determination.
What I'm though confident about is:
100% compatibility
Easy, simple, but quite powerful and flexible extensibility
Handling can be modified even after being established
Extensibility of the extension showed in the example, for example, adding the a 3rd array element to name the tuples for addressable change or removal - preferably when disabled
...and many things more.
part 4 of 5 of: GPS Module, Extensions, and u-blox NEO-6M GPS receiver
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
part 4 of 5 of: GPS Module, Extensions, and u-blox NEO-6M GPS receiver
@Gordon, had some time to think... and code... under the influence of your input.
The code below is a first attempt.
Output in console:
"time": "09:22:57",```
data > {```
"lat": 41.22417449999, "lon": -102.87471283333, "fix": 2, "satellites": 8, "altitude": 74.8 }```
Basically - for the usage of the module - either:
or:
Basically - for the implementation -
Btw, I would consider above approach a Lambda or functional approach, where after module load 'no' application nor object oriented object is (yet) available, but merely a bootstrap function, which is not even equally to a 'new' or a constructor. This 'into an object wrapped' function has first to be called in order to get a (useful) object (with state, and now also with function) back. - Btw, what is/was the rational of returning the {line:""} object on .connect()? - The module documentation http://www.espruino.com/Writing+Modules follows more the object-oriented approach, even though - due to the nature of JS - the 'class definition' is a function. For the GPS module I would like to have a similar approach chosen, even if the GPS stays a Singleton / would not have a constructor, so that already with require("GPS") an object is available for custom alteration. I will look into this option in a next phase while - of course - keeping 100% compatibility with current 'usage documentation'.
I still try to find what's right for Espruino. Having now lived many years - and still live - in a world of memory abundance, doing the right thing for Espruino is a welcome challenge. Not that I waste memory or cycles - I actually have the opposite habit - because I started out with having just 16KB (Kilo bytes) for application AND data AND ANY utility code - such as ISAM-create/read/update/delete, input routines, etc., and some of my Espruino-like hardware real world projects had just 256 bytes and 1KB EPROM and 4MHz 8-bit processing capabilities. Since then there is always the frugal guy in the back of my head watching over my spending of both memory and cycles, because even in today's memory and cycle paradise resources are limited... if not technically, then more so economically (currently building software for 15K+ concurrent transactional users - where user AND operator has to be kept happy: first one w/ snappy response times and latter one w/ spending 'no fancy' $s).
@Gordon, some pointers about the implementation of js in Espruino is very welcome here. I provide you an example in more detail at (see http://forum.espruino.com/conversations/255954) from code I'm working on: An array with a lot of small string elements in source code uses obviously more memory than one with few but long strings that later are chopped up into the small ones and stored again in the very same array. The short string are of the form of "keyNMO".
Back to the GPS discussion at hand:
Add the following code to the above one, send code to board, and use command go2() in the command pane to get the gps connected and handling lines (just) with the custom RMC line handler instead of the module's own ggaHandler. For simplicity, same already known callback is used.
Output in console:
"timc": "103314",```
"angl": "",```
}```
"timc": "103315",```
"angl": "",```
}```
You may notice that the ggaHandler 'is lost' for good... (became inaccessible for good).
The module's valuable ggaHandler can though easily be reused/included in the custom handling. Just provide the first tuple of 'handler duos' with null (no) line handler. On connect, the module's ggaHandler will be pulled and paired up with the callback provided in the first handler duo. To make that obvious / visible, a different calback2 call back is provided.
Add the following code to the above one, send code to board, and use command go2() in the command pane to get the gps connected and handling now two lines, the GGA tagged line with the module's own line handler and callback2(), and the RMC tagged line with the custom line handler and initially established callback().
Output in console:
"timc": "124700",```
"angl": "",```
}```
"time": "12:47:01",
"lat": 41.2242155, "lon": -102.87470616666, "fix": 2, "satellites": 9, "altitude": 73.8 }```
I'm not really sure about the overall value of enabled - but I liked the option to to shut up the console... - last but not least for copying console output to this post without having a nervously scrolling away text - or, in the target application - to quiet down the display... ;)
What should be discussed is the value of the try-catch-block. In different context I had issues with the process stopping after some time - less than one hour - and errors showing in the console. Especially with line handlers that do not take into account when the GPS is not yet providing complete data, for example, while still trying to get enough satellites for decently reliable position determination.
What I'm though confident about is:
part 4 of 5 of: GPS Module, Extensions, and u-blox NEO-6M GPS receiver