QDataStream

Does anyone have any experience and/or tips on using a QDataStream with Xojo?

https://doc.qt.io/archives/qt-4.8/qdatastream.html

This is in connection with WSJT-X.

The object is to interface with the network message stream.

https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/NetworkMessage.hpp

https://physics.princeton.edu/pulsar/k1jt/wsjtx.html

https://physics.princeton.edu/pulsar/k1jt/

Any help or feedback would be welcome.

Thanks

Just to listen and read messages ?
Or something more ?

Yes, just to listen and read messages.
At least for now.
I would be so grateful if you could help!

It’s used to serialize qt entities in binary format.
What exactly are you looking for?

I have written a routine to capture UDP messages with data in this format. I am looking for a way to decode and use the data.

Qt documentation explain how entities are represented in binary format.
Let me collect the related links.
I’ll post here when ready.
Nothing difficult to implement in Xojo.

THANK YOU!!!

No rush… it’s bed time here in Los Angeles and I’m done for today.

Hello Ed,
the informations stated in the comment section (the green colored part) of the source at https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/NetworkMessage.hpp are the only one required.
In Qt all types starts with the “q” letter so: qint32 is an int32 in Xojo and so on.
A QByteArray is very similar to a MemoryBlock.
No classes are serialized in a network message so things are simpler.
The important parts are:

  • all numeric values are represented in big endian format not little endian as used natively by x86/x64 Intel processors
  • all strings are in utf8 format without an end of string marker but prepended with an int32 value containing the string length
  • special string length values are 0 for an empty string and &hffffffff for a null string
  • all values are in binary format so an int32 is represented as 4 bytes (remember in big endian format)

The MemoryBlock LittleEndian=false property can be used to set/get numeric values in big endian.
Values can be set/get into/from frames contained in a MemoryBlock using offsets: this can be tedious.

A QDataStream is a class used to manipulate files/QByteArray contents in a simpler way instead of referring to the contained data using offsets.
Something very similar can be implemented in Xojo subclassing a MemoryBlock and adding methods to insert/pull data from the MemoryBlock contents and mantaining, as a private property, an offset index that increment accordingly to what is inserted/extracted from the MemoryBlock.

This is the fastest/shortest reply I can give you here just to start.
Reports here if you need more help.
Regards.

Oh yeah if this is all thats required they should be straight forward to decode

Maurizio, thank you. Also Norman!

Let me study this and if I need to I will ask you for further guidance.

Many thanks!

With your generous help I am making sense of all of this. Thank You!

I should probably be embarrassed to ask this, but here goes:

What is the most straightforward method for converting an array of bytes into a decimal value?

For instance, I have the following array of bytes representing the Dial Frequency as a quint64: 00 00 00 00 00 36 85 08
This becomes &h0000000000368508 which converts to 3573000 decimal.

I thought using

function decodeFreq (QBlock As MemoryBlock, firstByte As Integer) As String

Dim m As New MemoryBlock(8)
Dim i As Integer
Dim aVal As Int64

for i = 0 to 7
m.byte( i ) = QBlock.byte( firstByte + i )
Next

aVal = m.Int64Value( 0 )

Return str( aVal )

might give the value but instead I got 613956297833840640!

Make sure your memoryblock has the right endianness

'For instance, I have the following array of bytes representing the Dial Frequency as a quint64: 00 00 00 00 00 36 85 08
'This becomes &h0000000000368508 which converts to 3573000 decimal.

dim mb as new memoryblock(8)
mb.UInt8Value(0) =  &h00
mb.UInt8Value(1) =  &h00
mb.UInt8Value(2) =  &h00
mb.UInt8Value(3) =  &h00
mb.UInt8Value(4) =  &h00
mb.UInt8Value(5) =  &h36
mb.UInt8Value(6) =  &h85
mb.UInt8Value(7) =  &h08

mb.LittleEndian = true
dim leI64 as Uint64 = mb.UInt64Value(0)

mb.LittleEndian = false
dim beI64 as Uint64 = mb.UInt64Value(0)

break

Seems like a hard way to do it, but this works for me.

============
function decodeQ64 (QBlock As MemoryBlock, firstByte As Integer) As String

Const blockSize = 8
Const stringSize = 16

Dim m As New MemoryBlock(blockSize)
Dim i, hVal As Integer
Dim aVal As Int64
Dim pFactor( stringSize ) As Int64
Dim byteValue As integer
Dim s, h1 As String

for i = stringSize DownTo 1
pFactor( i ) = Pow( stringSize, stringSize - i )
Next

for i = 0 to blockSize - 1
m.Byte(i) = QBlock.Byte( firstByte + i )
Next

for i = 0 to blockSize - 1
byteValue = m.Byte( i )
h1 = hex( byteValue )
if len( h1 ) = 1 Then h1 = “0” + h1
s = s + h1
Next

for i = 1 to stringSize

hVal = asc( mid( s, i, 1 ) )

Select case hVal
Case 48 to 57
hVal = hVal - 48
Case 65 to 70
hVal = ( hVal - 65) + 10
End

aVal = aVal + (hVal * pFactor( i ) )

Next
Return str( aVal )

Is there an easier way to do this???

[quote=429661:@Ed Stokes]Seems like a hard way to do it, but this works for me.

[/quote]
The first bunch of lines is just me setting up the memory block with the data you suggested to show how using LITTLEENDIAN and Uint64Value would do this for you

Literally you need 2 lines

  1. littlendian = false
  2. read the Uint64 from whatever offset
mb.LittleEndian = false
dim beI64 as Uint64 = mb.UInt64Value(0)

Is the code getting a string that is the hex representation ? ie “00” , “00” or BYTES with those values ?

IE/ would my little sample need to be


dim mb as new memoryblock(8)
mb.UInt8Value(0) = val(" &h" + "00")
mb.UInt8Value(1) = val(" &h" + "00")
mb.UInt8Value(2) = val(" &h" + "00")
mb.UInt8Value(3) = val(" &h" + "00")
mb.UInt8Value(4) = val(" &h" + "00")
mb.UInt8Value(5) = val(" &h" + "36")
mb.UInt8Value(6) = val(" &h" + "85")
mb.UInt8Value(7) = val(" &h" + "08")

mb.LittleEndian = false
dim beI64 as Uint64 = mb.UInt64Value(0)

break

I’m setting littlendian here in UDPSocket.DataAvailable

Dim d As DataGram
Dim mb1, mb As MemoryBlock

d = UDPSocket1.Read
mb = d.Data
if mb <> NIL Then
mb1 = mb
mb1.LittleEndian = False
end

HexDump( mb1 )

Then in HexDump I call my function

if wsjtxData.Size = 0 Then Return
.
{Other code to dump the hex of the dataGram)
.
if wsjtxData.Byte( 11 ) = 1 Then
s = decodeQ64( wsjtxData, 22 )
outPut.AppendText s + chr( 13 )
end

ah you want to see the bytes from the block in hex … I though you just wanted the decimal value from the memry block based on your first post and what I replied with
and that returns the decimal converted value with just the lines I posted
if you wanted to add that as a string you could just use STR or FORMAT on the value the two lines I posted return

Hi Norman

I need to stop trying to do it the other way and listen to your good advice.

Really I don’t care about the individual bytes. I DO want to return the decimal value.

Let me change gears and see if I can make it work the way you have shown.

My wife says I’m like a dog with a bone. Once I start chewing it’s hard to get me to pay attention.

Thanks for your help!

Norman wrote:

Is the code getting a string that is the hex representation ? ie “00” , “00” or BYTES with those values ?

Answer
the code is getting BYTES in a dataGram

Sample data (hex dump)

AD BC CB DA 00 00 00 02 00 00 00 01 00 00 00 06 . . . . . . . . . . . . . . . .
57 53 4A 54 2D 58 00 00 00 00 00 36 85 08 00 00 W S J T - X . . . . . 6 . . . .
00 03 46 54 38 00 00 00 06 4A 52 36 45 5A 45 00 . . F T 8 . . . . J R 6 E Z E .
00 00 01 33 00 00 00 03 46 54 38 01 00 01 00 00 . . . 3 . . . . F T 8 . . . . .
05 16 00 00 03 E8 00 00 00 05 57 31 4B 4F 4B 00 . . . . . . . . . . W 1 K O K .
00 00 04 44 4D 30 34 00 00 00 04 50 4D 35 33 00 . . . D M 0 4 . . . . P M 5 3 .
FF FF FF FF 00 . . . . .

yeah if the MB already has the bytes and you just want the decimal value then

dim beI64 as Uint64 = mb.UInt64Value(offset) /// start at whatever byte offset in the memory block you need <<<

will give you an unsigned int 64 (8 bytes) with one line of code

instead of

if wsjtxData.Byte( 11 ) = 1 Then
  s = decodeQ64( wsjtxData, 22 )
  outPut.AppendText s + chr( 13 )
end if

and all the code in decodeQ64 you can do

if wsjtxData.Byte( 11 ) = 1 Then
  dim beI64 as Uint64 = mb.UInt64Value(offset)
  outPut.AppendText s + str(beI64) + chr(13) // you might use end of line here
end