Support Request: Adding parser for PEGASUS satellite with special formats

Hello together,

I am currently getting into Kaitai to implement a parser for our PEGASUS satellite in SatNOGS. I am failing on implementing some user defined decoding methods. PEGASUS e.g. uses the Q-format to pack its data. Additional information can be found in the Ham Operator Manual page 7.

E.g. the following HEX-number is stored in UFix 3.5 and shall be decoded as followed:
0x7F0b01111111 → 0*(2^2) + 1*(2^1) + 1*(2^0) + 1*(2^-1) + 1*(2^-2) + 1*(2^-3) + 1*(2^-4) + 1*(2^-5) = 3.96875 (readable telemtry data for V_PV1)

Any ideas on how to implement this into Kaitai? I have two ideas:

  1. Extract a raw-frame in seq, e.g.:
- id: v_pv1_raw
        type: u1

and then perform the decoding in an instance using value:, e.g.:

instances:
        v_pv1:
               value: *Ufix3.5 decoding method*
  1. Use an external code to perform the decoding, e.g. for decoding 1 byte in HEX-format, I have a MWE for a Python-function, which uses a string with the binary data as input:
def Ufix35(Bits='00000000'): 
    s = int(Bits[0])*(2**2) + int(Bits[1])*(2**1) + int(Bits[2])*(2**0) + int(Bits[3])*(2**-1) + int(Bits[4])*(2**-2) + int(Bits[5])*(2**-3) + int(Bits[6])*(2**-4) + int(Bits[7])*(2**-5)
    return s

data = '01111111' # 0x7f
x = Ufix35(data)
print(x)

Nevertheless, I am failing on implementing on both ideas. For idea one, I do not know how to get from 0x7F to 0b01111111 in Kaitai, in order to implement the decoding routine using value:. Idea two is titled with “advanced” within the Kaitai user guide. Moreover, there exist several ways to implement it.

I would appreciate any help for implementing this decoding routine, since I am very unexperienced within this field. :slight_smile:

For testing I am using data from a SatNOGS observation, which includes a 64 byte O1-Beacon (OBC data) of PEGASUS. Byte 0 is a HEX number and stores the PID (beacon identifier), Byte 1-6 is the CALLSIGN and Byte 7 stores the value for V_PV1 in Ufix3.5 format. For testing the data as well as my Kaitai-file can be downloaded here:
pegasus.zip (444 Bytes).

If needed, detailed information for decoding can be found in the Ham Operator Manual mentioned above.

Many thanks for any help! :slight_smile:
Alex

1 Like

Please open a merge request and mark it as Draft.
I’ll try to review that and I might have some ideas on how to perform the calculations.

All the best, 73
Patrick

1 Like

Hi Patrick,

many thanks. I have tried to submit a merge request here. Nevertheless, my mail is always rejected:

Unfortunately, your email message to GitLab could not be processed.
You are not allowed to perform this action. If you believe this is in error, contact a staff member.

73 de
Alex, OE3ALA

1 Like

Just open one on the gitlab project itself. If you need help with that we could have a private session on this…

1 Like

Many thanks for the fast reply! Concerning the MR I have contacted you via DM. I need to know:

  1. Which branch (master, 1.60.0) should I use for devlopement? Currently, I have forked the 1.60.0 branch and added my files to it.
  2. Based on that forked branch (source branch), which “target branch” should I select for the MR?

In the meantime I was able to solve my issue. I was able to implement a switch for the different beacon types. Moreover, I found a way on how to implement the Q-format using bit-sized integers and value instances, e.g.:

instances:
      v_pv1: # UFix 3.5
        value: (((v_pv1_raw & 0b10000000)/128)*4)
          + (((v_pv1_raw & 0b01000000)/64)*2)
          + (((v_pv1_raw & 0b00100000)/32)*1)
          + (((v_pv1_raw & 0b00010000)/16)*0.5)
          + (((v_pv1_raw & 0b00001000)/8)*0.25)
          + (((v_pv1_raw & 0b00000100)/4)*0.125)
          + (((v_pv1_raw & 0b00000010)/2)*0.0625)
          + (((v_pv1_raw & 0b00000001)/1)*0.03125)

From 1 unsigned integer byte (v_pv1_raw), I extract the relevant bit using logical operations (e.g. & 0b10000000 to extract bit 7). After that I divide it by its actual value (e.g. 2^7 = 128) and multiply it by its value according to the Q-format, in this case 2^2=4 for bit 7 in the UFix3.5 format.

Maybe there exists a smarter solution. :wink: I also tried using bit-shifts, but I have always ended up with an integer, which is not sufficient for telemetry data needed as a float number.

2 Likes

I can answer these questions :slight_smile:

  1. master is the latest branch, while 1.60 is the latest tag that is deployed. In other words the tag is a screenshot of master at a certain commit which is deployed. So, you should develop in master, however as currently master and 1.60 tag are on the same commit it will not matter.
  2. The target branch of the commit will be the master one.
1 Like

Thanks @fredy!

1 Like

I have some questions concerning the doc: section of the parser. What fields do I specifiy here? Are that the fields / parameters which will be parsed and pushed to SatNOGS DB in order to visualize them using a SatNOGS Dashboard?

I have following MWE. Do I specify the fields correct?

meta:
  id: pegasus
  title: PEGASUS decoder struct
  endian: le
  
doc-ref: 'https://spacedatacenter.at/pegasus/img/hamoperatormanual12.pdf'

doc: |
  :field pid: tt64_frame.pid
  :field call: tt64_frame.beacon.o1_beacon.v_pv1

seq:
  # The beacon format is as follows:
  #  [ 1 byte | 6 bytes  | 39 bytes | 2 bytes (depending on deframer) | 16 bytes (depending on deframer)]
  #  [  PID   |   CALL   |   DATA   |               CRC               |               FEC               ]
  - id: tt64_frame
    type: tt64_frame
    
types:
  tt64_frame:
    seq:
      - id: pid
        type: u1
        doc: |
          Protocol Identifier:
          0xC0: STACIE Beacon
          0xC1: EPS Beacon
          0x53: OBC Beacon (1/2)
          0x56: OBC Beacon (2/2)
      - id: beacon
        type:
          switch-on: pid
          cases:
            0xC0: s_beacon
            0xC1: e_beacon
            0x53: o1_beacon
            0x56: o2_beacon

o1_beacon:
  # The O1-beacon format is as follows:
  #  [  1 byte   | 6 bytes  | 31 bytes | 5 bytes | 3 bytes | 2 bytes (depending on deframer) | 16 bytes (depending on deframer)]
  #  [ PID 0x53  |   CALL   |    EPS   | STACIE  |   OBC   |               CRC               |               FEC               ]
    seq:
      - id: call
        type: str
        size: 6
        encoding: ASCII
        doc: Satellite Callsign
      - id: v_pv1_raw
        type: u1
instances:
      v_pv1: # UFix 3.5
        value: (((v_pv1_raw & 0b10000000)/128)*4)
          + (((v_pv1_raw & 0b01000000)/64)*2)
          + (((v_pv1_raw & 0b00100000)/32)*1)
          + (((v_pv1_raw & 0b00010000)/16)*0.5)
          + (((v_pv1_raw & 0b00001000)/8)*0.25)
          + (((v_pv1_raw & 0b00000100)/4)*0.125)
          + (((v_pv1_raw & 0b00000010)/2)*0.0625)
          + (((v_pv1_raw & 0b00000001)/1)*0.03125)
        doc: Voltage at PV1-bus (Solarbus 1)

In that case, perhaps worth changing the order and putting the small (resulting in a float) calculation first, then adding the rest ?
Example on loosely defined style:
1 + 2 + 0.1 = 3 (first addition resulting in a inteter)
0.1 + 1 + 2 = 3.1 (first addition results in a float)
Another way is to define the variable in mV, that will then suffice with integers.

1 Like

Hi Daniel,

thanks for your suggestion! I have tried both ways (little endian), where v_pv1_raw is an uint8 or u1 in Kaitai notation:

value: (((v_pv1_raw & 0b10000000) >> 5)
          + ((v_pv1_raw & 0b01000000) >> 5)
          + ((v_pv1_raw & 0b00100000) >> 5)
          + ((v_pv1_raw & 0b00010000) >> 5)
          + ((v_pv1_raw & 0b00001000) >> 5)
          + ((v_pv1_raw & 0b00000100) >> 5)
          + ((v_pv1_raw & 0b00000010) >> 5)
          + ((v_pv1_raw & 0b00000001) >> 5))

and

value: (((v_pv1_raw & 0b00000001) >> 5)
          + ((v_pv1_raw & 0b00000010) >> 5)
          + ((v_pv1_raw & 0b00000100) >> 5)
          + ((v_pv1_raw & 0b00001000) >> 5)
          + ((v_pv1_raw & 0b00010000) >> 5)
          + ((v_pv1_raw & 0b00100000) >> 5)
          + ((v_pv1_raw & 0b01000000) >> 5)
          + ((v_pv1_raw & 0b10000000) >> 5))

both resutling in an integer.

I guess, that bitshifting. e.g. from

0b00000001 = 0 * 2^7 + 0 * 2^6 + 0 * 2^5 + 0 * 2^4 + 0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0

to

0b00000001 >> 1

does not correspond to

0b00000001 >> 1 = 0 * 2^6 + 0 * 2^5 + 0 * 2^4 + 0 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 + 1 * 2^-1

because there is no

2^-1

in bitshifts, or am I wrong?

Concerning your suggestion of using mV:
Our official database uses V in float, therefore I would like to stick to it, since I already found a way to calculate it in that way in Kaitai (even if it is very ugly from a programmers view :wink: )