Search code examples
pythonencodinghexosc

How do I decode data when I don't know the specific encoding


I'm receiving data using an OSC server and the data looks like this:

b'Muse-C46F/elements/alpha_absolute\x00\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'Muse-C46F/elements/alpha_relative\x00\x00\x00,ffff\x00\x00\x00\x7f\xc0\x00\x00\x7f\xc0\x00\x00\x7f\xc0\x00\x00\x7f\xc0\x00\x00'
b'Muse-C46F/elements/alpha_session_score\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'Muse-C46F/elements/alpha_absolute\x00\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

I'm trying to decode 4 floats from those encoded sequences.

This is my whole code:

print('Program Initiated')
UDP_IP = "192.168.2.57"
UDP_PORT = 4000

sock = socket.socket(socket.AF_INET,  # Internet
                socket.SOCK_DGRAM)  # UDP
sock.bind((UDP_IP, UDP_PORT))

while True:
    data, addr = sock.recvfrom(1024)  # buffer size is 1024 bytes
    if 'alpha' in str(data):
        print(struct.unpack('>32s8sffff', data))

I saw a similar question and used the print(struct.unpack('>32s8sffff', data)), but I get unpack requires a buffer of 56 bytesas error.


Solution

  • These OSC messages consists of three parts:

    • An address (which ought to start with b'/')
    • A type tag string, which starts with b',', and defines the type and number of arguments that follow
    • The messages arguments, as defined by the type tag string.

    In the data provided in the question, each message consists of 36 or 40 bytes of address, then 5 bytes of tag type string. Disregarding the initial comma, the tag type string consists of four 'f's, so we expect the arguments to consist of four floats, consisting of 16 bytes (4 per float).

    Each message has some extra bytes after the 16 required for the four floats; let's assume* that these are padding that can be discarded.

    Therefore the struct format is going to be: a variable number of address characters, five tag type characters and four floats.

    Then the code required to extract the data looks like this:

    $ cat osc.py

      import struct
    
      data = [
         b"Muse-C46F/elements/alpha_absolute\x00\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
         b"Muse-C46F/elements/alpha_relative\x00\x00\x00,ffff\x00\x00\x00\x7f\xc0\x00\x00\x7f\xc0\x00\x00\x7f\xc0\x00\x00\x
         b"Muse-C46F/elements/alpha_session_score\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
         b"Muse-C46F/elements/alpha_absolute\x00\x00\x00,ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
      ]
    
      if __name__ == "__main__":
          for msg in data:
              num_address_bytes = msg.index(b",")
              num_argument_bytes = len(msg) - (num_address_bytes + 5)
              num_extra_bytes = num_argument_bytes - 16
              address, type_, *floats = struct.unpack(
                  ">{}s5s4f".format(num_address_bytes), msg[:-num_extra_bytes]
              )
              print(address, type_, floats)
    

    This output is generated:

    b'Muse-C46F/elements/alpha_absolute\x00\x00\x00' b',ffff' [0.0, 0.0, 0.0, 0.0]
    b'Muse-C46F/elements/alpha_relative\x00\x00\x00' b',ffff' [1.7796490496925177e-43, -2.000030279159546, -2.000030279159546, -2.000030279159546]
    b'Muse-C46F/elements/alpha_session_score\x00\x00' b',ffff' [0.0, 0.0, 0.0, 0.0]
    b'Muse-C46F/elements/alpha_absolute\x00\x00\x00' b',ffff' [0.0, 0.0, 0.0, 0.0]
    

    * It's generally a bad idea to make assumptions. If the OSC server has documentation, check whether its formats deviate from the OSC spec.