Manipulating and Displaying Variables

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
everest
Posts: 96
Joined: 31 May 2010, 9:01 AM

Post by everest »

Very cool! So it looks like a way to create an array of bytes that can be accessed a number of ways. . .via the individual elements defined (Degrees, FracMinutes, etc) or via the individual bytes. . .so there's no difference between using Degrees and LatData[1] if I'm understanding this properly.

-J
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

everest wrote:so there's no difference between using Degrees and LatData[1] if I'm understanding this properly.
Normally, you cannot access the individual bytes underlying a Structure data item. Rather, you are limited to accessing the structure members by name and the data type will be that of the particular member. In the example code that I provided, I needed to access the individual bytes beneath the structure (in order to swap the byte order and to convert to a Boolean) so I defined an Alias that superimposes a Byte array (latBytes) over the structure.

Code: Select all

Dim latBytes(1 to 5) as Byte Alias lat
An understanding of the Alias concept and the Structure concept is required in order to comprehend how the proposed subroutine does its work.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Here is an alternate implementation that might be more clear since it does not use the Alias concept. Rather, it retrieves the GPS data into a Byte array and then populates the structure from that data.

Code: Select all

Sub GPSGetLatAlternate(ByRef lat as LatData)
   Dim TimeOut as Boolean
   Dim latBytes(1 to 5) as Byte

   ' get the raw latitude bytes
   Call PutQueueStr (outQueue, "!GPS" & Chr(&H05))
   Call GetQueue (inQueue, latBytes, 5, 1.0, Timeout)

   If Timeout = True Then
      Debug.Print "Timed out reading from GPS"
   Else
      ' populate the structure from the returned data
      lat.Degrees = latBytes(1)
      lat.Minutes = latBytes(2)
      lat.FracMinutes = Shl(CUInt(LatBytes(3)), 8) + CUInt(latBytes(4))
      lat.North = (latBytes(5) = 0)
   End If
End Sub
One interesting aspect of this implementation is that the order of the members of the LatData structure doesn't have any direct relationship to the order of the bytes returned by the GPS unit. This separation is generally preferred, all things being equal, even though it requires slightly more code.
- Don Kinzer
codeferret
Posts: 12
Joined: 03 January 2012, 15:07 PM

Re: Manipulating and Displaying Variables

Post by codeferret »

I know that the original poster has had his question answered, but for what it's worth, I'll add my solution to the pool of general knowledge. I wanted a nice way to convert big to little endian data already in memory. I needed it for a few types, so I set options to allow overloading ("option overload", you could use "option objects" instead, which has a similar effect). But even with no options, you can copy just one of these functions into your code and it should just work.

Here is how to perform the conversion, once the functions below are in your code somewhere:

Code: Select all

' your_data is an existing unsigned integer, unsigned long, or single.
your_data = ConvertBigToLittleEndian( your_data )
Here are the functions. It would be trivial to convert these to little endian to big endian functionality.

Code: Select all

' function: ConvertBigToLittleEndian
' Description: Converts big-endian U16s to little-endian format.
' Params: BigEndianValue = The big-endian U16 to be converted.
' Returns: The little-endian U16 equivalent.
' Notes: This function is overloaded with functions for other type endian conversions.
public function ConvertBigToLittleEndian(byval BigEndianValue as UnsignedInteger) as UnsignedInteger

	dim BigEndianValueBytes(2) as byte alias BigEndianValue
	dim LittleEndianValueBytes(2) as byte alias ConvertBigToLittleEndian
	
	ConvertBigToLittleEndian = 0 'This is here just to satisfy the compiler.
	LittleEndianValueBytes(1) = BigEndianValueBytes(2)
	LittleEndianValueBytes(2) = BigEndianValueBytes(1)
end function

' function: ConvertBigToLittleEndian
' Description: Converts big-endian U32 to little-endian format.
' Params: BigEndianValue = The big-endian U32 to be converted.
' Returns: The little-endian U32 equivalent.
' Notes: This function is overloaded with functions for other type endian conversions.
public function ConvertBigToLittleEndian(byval BigEndianValue as UnsignedLong) as UnsignedLong

	dim BigEndianValueBytes(4) as byte alias BigEndianValue
	dim LittleEndianValueBytes(4) as byte alias ConvertBigToLittleEndian
	
	ConvertBigToLittleEndian = 0 'This is here just to satisfy the compiler.
	LittleEndianValueBytes(1) = BigEndianValueBytes(4)
	LittleEndianValueBytes(2) = BigEndianValueBytes(3)
	LittleEndianValueBytes(3) = BigEndianValueBytes(2)
	LittleEndianValueBytes(4) = BigEndianValueBytes(1)

end function

' function: ConvertBigToLittleEndian
' Description: Converts big-endian single-precision (32-bit) floats to little-endian format.
' Params: BigEndianValue = The big-endian single to be converted.
' Returns: The little-endian single equivalent.
' Notes: This function is overloaded with functions for other type endian conversions.
public function ConvertBigToLittleEndian(byval BigEndianValue as single) as single

	dim BigEndianValueBytes(4) as byte alias BigEndianValue
	dim LittleEndianValueBytes(4) as byte alias ConvertBigToLittleEndian
	
	ConvertBigToLittleEndian = 0.0 'This is here just to satisfy the compiler.
	LittleEndianValueBytes(1) = BigEndianValueBytes(4)
	LittleEndianValueBytes(2) = BigEndianValueBytes(3)
	LittleEndianValueBytes(3) = BigEndianValueBytes(2)
	LittleEndianValueBytes(4) = BigEndianValueBytes(1)

end function
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I added code tags for better presentation of the code.
- Don Kinzer
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

While I like overloading functions, they do use up valuable program space by duplicating large amounts of code.

I would have written the conversion routine in the following manner:

Pass the memory location of the variable to be converted, and the size of the variable using MemAddress() and SizeOf() to a subroutine.

Then manipulate the memory addresses by reversing the first and last bytes, then the second and next to last, and so on by using a variety of techniques such as MemCopy(). The general idea would be to read the lower byte and write to a temp byte. Read the higher byte and write to the lower byte. Read the temporary byte and write to the higher byte.

Another technique which may be less efficient would be to copy the bytes of the variable to a temporary array, then copy them back in reverse order. I think that this technique uses an extra read and write for each byte compared to the first technique. However this technique is probably easier to understand what is happening when reading the code.

With either of these algorithms, you save a lot of duplication of code compared to overloaded functions.

-Tony
Post Reply