srting/array parsing/processing question
sharpie
Posts: 150
Ok..... so now onto my next step and I'm hoping this will be an easy one for someone, but has been·a real pain for me...
I want to accept data from a serial line, pluck out certain parts of it and use those for variables in other subs..· Imagine something like an nmea sentence from a gps..· And I want to identify a certain sentence by the first few characters, then take the rest apart delimited by commas in that case and use altitude for this example in a calculation or something..·
Using $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 as an example...· Altitude is listed as the tenth value.. "545.4"
Where:
···· GGA········· Global Positioning System Fix Data
···· 123519······ Fix taken at 12:35:19 UTC
···· 4807.038,N·· Latitude 48 deg 07.038' N
···· 01131.000,E· Longitude 11 deg 31.000' E
···· 1··········· Fix quality: 0 = invalid
······························ 1 = GPS fix (SPS)
······························ 2 = DGPS fix
······························ 3 = PPS fix
······ 4 = Real Time Kinematic
······ 5 = Float RTK
······························ 6 = estimated (dead reckoning) (2.3 feature)
······ 7 = Manual input mode
······ 8 = Simulation mode
···· 08·········· Number of satellites being tracked
···· 0.9········· Horizontal dilution of position
···· 545.4,M····· Altitude, Meters, above mean sea level
···· 46.9,M······ Height of geoid (mean sea level) above WGS84
····················· ellipsoid
···· (empty field) time in seconds since last DGPS update
···· (empty field) DGPS station ID number
···· *47········· the checksum data, always begins with *
I would like to be able to accept the sentence as a whole string using the 'FullDuplexSerial' object, but it looks like I have to take it in byte by byte...· So, if I construct an array of say "SERIN[noparse][[/noparse]65]" and have each character in that array (leaving out the fact that the sentence will probably be different lengths at different times)..· And then I pluck out my "5" and "4" and "5", ".", "4", etc....· I've tried putting that into another array, and numerous failed attempts at consolidating it into a string, number, or whatever..·
Does anyone have a suggestion for making that a whole value I can use?· I've searched as much as my brain can take... The RFID posting was handy, but hasn't helped me solve it yet..
Thanks!
I want to accept data from a serial line, pluck out certain parts of it and use those for variables in other subs..· Imagine something like an nmea sentence from a gps..· And I want to identify a certain sentence by the first few characters, then take the rest apart delimited by commas in that case and use altitude for this example in a calculation or something..·
Using $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 as an example...· Altitude is listed as the tenth value.. "545.4"
Where:
···· GGA········· Global Positioning System Fix Data
···· 123519······ Fix taken at 12:35:19 UTC
···· 4807.038,N·· Latitude 48 deg 07.038' N
···· 01131.000,E· Longitude 11 deg 31.000' E
···· 1··········· Fix quality: 0 = invalid
······························ 1 = GPS fix (SPS)
······························ 2 = DGPS fix
······························ 3 = PPS fix
······ 4 = Real Time Kinematic
······ 5 = Float RTK
······························ 6 = estimated (dead reckoning) (2.3 feature)
······ 7 = Manual input mode
······ 8 = Simulation mode
···· 08·········· Number of satellites being tracked
···· 0.9········· Horizontal dilution of position
···· 545.4,M····· Altitude, Meters, above mean sea level
···· 46.9,M······ Height of geoid (mean sea level) above WGS84
····················· ellipsoid
···· (empty field) time in seconds since last DGPS update
···· (empty field) DGPS station ID number
···· *47········· the checksum data, always begins with *
I would like to be able to accept the sentence as a whole string using the 'FullDuplexSerial' object, but it looks like I have to take it in byte by byte...· So, if I construct an array of say "SERIN[noparse][[/noparse]65]" and have each character in that array (leaving out the fact that the sentence will probably be different lengths at different times)..· And then I pluck out my "5" and "4" and "5", ".", "4", etc....· I've tried putting that into another array, and numerous failed attempts at consolidating it into a string, number, or whatever..·
Does anyone have a suggestion for making that a whole value I can use?· I've searched as much as my brain can take... The RFID posting was handy, but hasn't helped me solve it yet..
Thanks!
Comments
Basically, this reads the serial line, allows a leading + or -, then a sequence of digits ended by some non-digit character which is ignored (like a comma), and returns the binary value. You can do the equivalent thing for floating point values using FloatMath for the arithmetic.
This basically accumulates an integer value, but keeps a count of the number of decimal places. It ignores decimal places beyond 9, partly because of simplicity, and partly because, in the usual case, they're not likely to be significant. If this is an issue, the routine can be changed to handle a wider range, but it is limited to 9 significant digits. The floating point format won't keep that many significant digits anyway.
Post Edited (Mike Green) : 7/24/2006 2:51:17 PM GMT
If I receive a sequence of data into an array...· We'll say "byte input[noparse][[/noparse]64]".. And we'll assume my sentence is the same length all the time, and all the segments are the same number of digits every time....· Using the sentence below, say I want to extract the "545" in bold·from it and use that for a value somewhere else in the program.
So, I know the positions of the characters I want to extract from the array...· Using code similar to the following, each character of the sentence is put into the array of "newArray" and "ndx" being the indexer...
·So, then now I know that in "newArray[noparse][[/noparse]47]" I have the number "5", and "newArray[noparse][[/noparse]48]" I have "4", and a "5" went into "newArray[noparse][[/noparse]49]"..· Great, now how can I consolidate these into a whole single number that I can feed maybe as another variable to a parameter for something like "svo.set(0, 4, IWANTMYNUMBERHERE)"?? Assuming that number would be a value usable for that parameter, and since these are examples and I'm not actually trying to dump my altitude from a gps into a servo position...
Mike, I am a little confused about your example and how it would relate to a sentence that doesn't contain 100% digits..· For example, the nmea sentence has "N" for north, and "E" for east... etc.....
Thanks for the help!!!
For the non-numeric input stuff ... you can do something like ...
So I have the "rcx" array containing the whole sentence...· and it sends that through your getFloat and returns my value?· I apologize for being so dense, but I think I am still missing something..· How do I specify which part of the sentence I need?· As I want to pluck out the "5", "4" and "5" from the sentence and get a variable back that equals a numeric 545...·
Previously, I was thinking of something like
or even
I'm a bit new to the prop and I'm still struggling with variables..· and some of the syntax..
·
To simplify my question... If I have an array with a length of say, 4 characters...· And I'd like to pass that array as a value to something like svo.set(0, 4, myVariable)
Or would something like this work?
I'm thinking of an object similar to a C struct or C++ class. Then you could just parse the string using the first character, commas, and last character as the delimiters.
This should allow you to access your values as MySentence.Altitude, etc.
It just seems clunky to store each individual character as an array element that deconstructs your logical units of information.
I'm having the same problem -- will post results.
I think it will help for you to understand the routines I've posted. Take the simpler one for integer input. Sit down with a piece of lined paper and put each variable name on a line (value, input, negative) and make up a proposed serial input sequence and write those values on a line labelled "ser.rx". Then go through the routine statement by statement and "execute" the SPIN statements yourself, not skipping or assuming anything, changing the values on the paper as you go along. If this seems excessive, I do it myself when I get stuck. Sometimes you make too many assumptions as you read programs and miss something simple.
I think you are getting hung up on this array thing. Most of the time, when you need to process an input stream, you can "do it on the fly" without saving the characters you've read into an array. The most common exception is when the input speed is too high for the program to do anything but save the characters for processing later. The FullDuplexSerial routines have a buffer already and the Propellor is fast enough that you are unlikely to have problems processing such a simple input stream "on the fly".
Here's a subroutine (match_inp) that will help you parse your strings. It's designed to look for incoming strings on the fly. The demo shows how it can be used with the $GPGGA sentence.
-Phil
Thanks so much to everyone... Mike, thanks for your input as well.. I am sure I will find a use for your suggestions when I get a better grasp on spin.
This is an excellent string extraction tool . I have a question about its repeated operation though. When I try to use it more than once for different variable, my values are set to the same value! It's probable my code, but if you have a moment here it is for review. I used your above code in the calls.
The screen displays:
0001.4322
00013.1433
104
Lat: 104
Lon: 104
Alt: 104
The top three lines are correct, but the assigned variables inthe next three lines are changed since their first display in the capture routing. And ideas?
Thanks,
Paul
Post Edited (Paul H) : 4/22/2007 4:57:54 PM GMT
The return value from match_inp is a pointer into the global array return_string. This array gets reused every time match_inp is called, so pointers from previous calls no longer point to valid data. To save your results for later display, you need to copy the pointed-to strings into their own separate arrays.
-Phil
Do have a look at the Extended Full Duplex Serial object from the Propeller Object Exchange. It's a wrapper object for the FullDuplexSerial driver that does some of this type of scanning and conversion for you. It may save you some work. This object also uses a private buffer whose address is returned from some of the calls and will be reused with the next call.
So I now understand the match_inp routine hands back a pointer (not a value) that I have been copying into my variables. The display shows the last value, because they all point to the same location. It's starting to make sense...
If I use a ByteFill (or ByteMove) to copy the data, doesn't that necessitate that I know the length of the data acquired by the match_inp routine? Is there a more direct method that avoids this, such as:
The data is not going to be zero terminated either.
I unfortunately am having difficulties dealing in the "pointer-to-variable" world and am more used to the "variable just is" in higher levels of programming. I am rereading the Extended Full Duplex serial to see if I can repurpose parts, specifically i'm looking at RxStr and RxDec. I guess I need to know the data type I am receiving (which I do) then apply the proper set of steps to assign to a global variable.
Comments?
Paul
Another thing you might consider: In your code, latitude and longitude come from two separate NMEA strings. It would be more accurate if both came from the same string, since you might change position between two of them. Once you've obtained the latitude, you can get the longitude just by scanning past two more commas, instead of scanning for the $GPRMC header again.
-Phil
I totally agree. Thats actually what I had started doing until I ran into the re-use of the match_inp array and kept getting the same values. I simplified it for my postings as this was the same issue issue either way. Though I hadnt started out with this goal, I thought I might put together a general purpose NMEA reader for the code library as I work this out specifically for my projects. It's funny - the more I understand the process, the less I see the need for a general purpose SPIN code, but then I remember that it still isnt working for me and there must be others I can help out down the road!
Mike
I'll try your ideas tonight - as soon as i understand where the 0 came from... And of course post the results
Thanks,
Paul
I've changed the code to read
Which should bytemove the first string to variable Lat, and the second to variable Lon (I see that the strsize(temp)+1 yields the next memory location past the captured values which is a zero, sh what I copy has a zero termination now).
But! - I am still getting the same values for both of my variables.
-Phil
I remarked that I was trying to use a single data string to capture all variables at once but there is a hitch using match_inp -- I cannot capture consecutive data values with your code because the delimiter (a comma) is used to both locate the data, then terminate its capture.
By process definition, the termination of one datum is the location of the next. So if I try to capture the 3rd and 4th delimited values, I get the 3rd comma, the 3rd data, a terminating comma (the 4th!), then search for the next comma (want the 4th, but it has already past) so I get the next comma seen (the 5th) and we capture the 5th data value.
If I place the bytemoves (once they work) into a match_inp derivitive, it can work. But you code is so much more elegant than mine!
Of course its all moot until i fix my overwrite issue! lol
Paul
I think I get it (or least it's working!)
It appears that I have been copying the data to the new memory locations, but was just not displaying it properly! I needed to add the @ Symbol Address to my output lines to see the actual data located at these addresses.
Thank you both!
Paul
It appears as tough this match_inp routine is keeping pointers between calls. Would it not be easier to come up with a routine that take in a delimiter to know when the sentence rx is complete, then parse it into it separate parts? I am way too new to Spin, but have tons of C++ and C# experience. All I need is some "pointers"...no pun intended.
Every time I try to get this right something else get whacky... right now the FixTime never shows up....
<code>
match_inp(string("$GPRMC"), 1)
match_inp(string(","), 1)
vga.str(string("Data: "))
tmp:=match_inp(string(","), 0)
bytemove(@fixTime,tmp,strsize(tmp)+1)
vga.str(string("FixTime: "))
vga.str(@fixTime)
vga.out(13)
tmp:=match_inp(string(","), 0)
bytemove(@isvalid,tmp,strsize(tmp)+1)
vga.str(string("Fix: "))
vga.str(@isvalid)
vga.out(13)
tmp:=match_inp(string(","), 0)
bytemove(@lat,tmp,strsize(tmp)+1)
vga.str(string("Lat: "))
vga.str(@lat)
vga.out(13)
tmp:=match_inp(string(","), 0)
bytemove(@ns,tmp,strsize(tmp)+1)
vga.str(string("N/S: "))
vga.str(@ns)
vga.out(13)
tmp:=match_inp(string(","), 0)
bytemove(@lon,tmp,strsize(tmp)+1)
vga.str(string("Lon: "))
vga.str(@lon)
vga.out(13)
tmp:=match_inp(string(","), 0)
bytemove(@ew,tmp,strsize(tmp)+1)
vga.str(string("E/W: "))
vga.str(@ew)
vga.out(13)
</code>