P1sec / pycrate

A Python library to ease the development of encoders and decoders for various protocols and file formats; contains ASN.1 and CSN.1 compilers.
GNU Lesser General Public License v2.1
381 stars 132 forks source link

Question on best way to chain decoding with 2 different grammars #179

Closed dbressan2 closed 2 years ago

dbressan2 commented 2 years ago

Hi

I am wondering about the best way to chain decoding when a first ASN.1 specification declares an OCTET STRING whose actual encoding is defined in a second ASN.1 specification.

To take a practical example, NR5G MeasResult as specified in 3GPP 38.331 includes a IE locationTimestamp-r16 defined as an OCTET STRING. The way to decode the content of this OCTET STRING is defined as a DisplacementTimeStamp-r15 structure in another 3GPP specification, namely 37.355

I compiled NR5G ASN.1 from 38.331 as NR5G_Dec_2021, and LPP ASN.1 from 37.355 as LPP_Dec_2021.

I first decode a MeasReport the flowwing way: dcch = NR5G_Dec_2021.NR_RRC_Definitions.UL_DCCH_Message dcch.from_uper(bytes.fromhex(message))

I get:

{ message c1 : measurementReport : { criticalExtensions measurementReport : { measResults { measId 3, measResultServingMOList { {

    },
    locationInfo-r16 {
      commonLocationInfo-r16 {
        gnss-TOD-msec-r16 'AC3580'H,
        locationTimestamp-r16 '00D64C983260E183168D9A33B4EB00'H,
        locationCoordinate-r16 '4307EC2CD636701A40'H,
        locationSource-r16 '00'H
      }
    }
  }
}

} }

I can now extract locationTimestamp-r16 and decode it.

ts = dcch()['message'][1][1]['criticalExtensions'][1]['measResults']['locationInfo-r16']['commonLocationInfo-r16']['locationTimestamp-r16']

timestamp = LPP_Dec_2021.LPP_PDU_Definitions.DisplacementTimeStamp_r15 timestamp.from_uper(ts) print (timestamp.to_asn1()) I get

utcTime-r15 : { utcTime-r15 "220208014643Z" -- Tue Feb 8 01:46:43 2022 --, utcTime-ms-r15 470 }

My question is how to chain the decoding operations from 2 different grammars such as to obtain something like:

{ message c1 : measurementReport : { criticalExtensions measurementReport : { measResults { measId 3, measResultServingMOList { {

    },
    locationInfo-r16 {
      commonLocationInfo-r16 {
        gnss-TOD-msec-r16 'AC3580'H,
        locationTimestamp-r16 utcTime-r15 : {
                              utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,
                              utcTime-ms-r15 470
                              }

        locationCoordinate-r16 '4307EC2CD636701A40'H,
        locationSource-r16 '00'H
      }
    }
  }
}

} }

3GPP specification references:

38.331

MeasResults ::= SEQUENCE { measId MeasId, measResultServingMOList MeasResultServMOList, measResultNeighCells CHOICE { measResultListNR MeasResultListNR, ..., measResultListEUTRA MeasResultListEUTRA, measResultListUTRA-FDD-r16 MeasResultListUTRA-FDD-r16 } OPTIONAL, ..., [[ measResultServFreqListEUTRA-SCG MeasResultServFreqListEUTRA-SCG OPTIONAL, measResultServFreqListNR-SCG MeasResultServFreqListNR-SCG OPTIONAL, measResultSFTD-EUTRA MeasResultSFTD-EUTRA OPTIONAL, measResultSFTD-NR MeasResultCellSFTD-NR OPTIONAL ]], [[ measResultCellListSFTD-NR MeasResultCellListSFTD-NR OPTIONAL ]], [[ measResultForRSSI-r16 MeasResultForRSSI-r16 OPTIONAL, locationInfo-r16 LocationInfo-r16 OPTIONAL, ul-PDCP-DelayValueResultList-r16 UL-PDCP-DelayValueResultList-r16 OPTIONAL, measResultsSL-r16 MeasResultsSL-r16 OPTIONAL, measResultCLI-r16 MeasResultCLI-r16 OPTIONAL ]]

}

LocationInfo-r16 ::= SEQUENCE { commonLocationInfo-r16 CommonLocationInfo-r16 OPTIONAL, bt-LocationInfo-r16 LogMeasResultListBT-r16 OPTIONAL, wlan-LocationInfo-r16 LogMeasResultListWLAN-r16 OPTIONAL, sensor-LocationInfo-r16 Sensor-LocationInfo-r16 OPTIONAL, ... }

CommonLocationInfo-r16 ::= SEQUENCE { gnss-TOD-msec-r16 OCTET STRING OPTIONAL, locationTimestamp-r16 OCTET STRING OPTIONAL, locationCoordinate-r16 OCTET STRING OPTIONAL, locationError-r16 OCTET STRING OPTIONAL, locationSource-r16 OCTET STRING OPTIONAL, velocityEstimate-r16 OCTET STRING OPTIONAL }

locationTimeStamp Parameter type DisplacementTimeStamp defined in TS 37.355 [49]. The first/leftmost bit of the first octet contains the most significant bit.

37.355

DisplacementTimeStamp-r15 ::= CHOICE { utcTime-r15 UTC-Time-r15, gnssTime-r15 MeasurementReferenceTime, systemFrameNumber-r15 SFN-r15, measurementSFN-r15 INTEGER(-8192..9214), ... }

Thanks in advance.

Best regards

p1-bmu commented 2 years ago

Thanks for your report. Could you share the buffer corresponding to the UL_DCCH_Message ? That will ease the testing and providing the best answer.

dbressan2 commented 2 years ago

Hi

Sure

Here you go:

00 c2 00 04 63 df 24 2b b0 10 7e 48 57 41 15 b7 5c d5 34 2c 0e bd c2 a8 fd 73 64 f1 ba c5 e7 a1 75 8d 53 43 ea d9 22 89 d5 30 44 08 91 21 1d 01 d6 1a c0 07 80 6b 26 4c 19 30 70 c1 8b 46 cd 19 da 75 80 04 a1 83 f6 16 6b 1b 38 0d 20 00 80 00

Best regards

D. Bressanelli

From: Benoit Michau @.> Sent: Friday, February 11, 2022 2:15 PM To: P1sec/pycrate @.> Cc: Dominique Bressanelli @.>; Author @.> Subject: Re: [P1sec/pycrate] Question on best way to chain decoding with 2 different grammars (Issue #179)

WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.

Thanks for your report. Could you share the buffer corresponding to the UL_DCCH_Message ? That will ease the testing and providing the best answer.

— Reply to this email directly, view it on GitHubhttps://github.com/P1sec/pycrate/issues/179#issuecomment-1036202376, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHUUMPKDG4JQCRLRESGUE7DU2UDVBANCNFSM5OEB2JBA. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub. You are receiving this because you authored the thread.Message ID: @.**@.>>

p1-bmu commented 2 years ago

One way to manage it is at the ASN.1 level. OCTET STRING (and BIT STRING) can have a CONTAINING constraint, which indicates another ASN.1 object to be decoded from the OCTET STRING (or BIT STRING) buffer.

E.g.:

ComplexObj ::= SEQUENCE {
    ...
    }
OctStr ::= OCTET STRING (CONTAINING ComplexObj)

I believe the simplest, and maybe only, way to do what you ask for is to build such a construct with your ASN.1 definitions. So you can modify your RRC NR specification, to enhance the definition of the locationTimestamp-r16 with such a (CONTAINING DisplacementTimeStamp-r15) declaration. You will also need to manage properly the import of the LPP object in the RRC NR definition. Otherwise, you can bind the LPP object to the RRC one within Python, doing something like this:

In [47]: M = NR_RRC_Definitions.UL_DCCH_Message                                                                                                           

In [48]: D = LPP_PDU_Definitions.DisplacementTimeStamp_r15                                                                                                

In [49]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo
    ...: -r16', 'locationTimestamp-r16'))._const_cont = D # this corresponds to the (CONTAINING ...) constraint                                           

In [50]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo
    ...: -r16', 'locationTimestamp-r16'))._const_cont_enc = None # this is to say the codec stays the same                                                

In [51]: M.from_uper(b'\x00\xc2\x00\x04c\xdf$+\xb0\x10~HWA\x15\xb7\\\xd54,\x0e\xbd\xc2\xa8\xfdsd\xf1\xba\xc5\xe7\xa1u\x8dSC\xea\xd9"\x89\xd50D\x08\x91!\x1
    ...: d\x01\xd6\x1a\xc0\x07\x80k&L\x190p\xc1\x8bF\xcd\x19\xdau\x80\x04\xa1\x83\xf6\x16k\x1b8\r \x00\x80\x00')                                          

In [52]: print(M.to_asn1())                                                                                                                               
{
  message c1 : measurementReport : {
    criticalExtensions measurementReport : {
      measResults {
        measId 3,
        measResultServingMOList {
          {
            servCellId 0,
            measResultServingCell {
              physCellId 99,
              measResult {
                cellResults {
                  resultsSSB-Cell {
                    rsrp 100,
                    rsrq 66,
                    sinr 93
                  }
                },
                rsIndexResults {
                  resultsSSB-Indexes {
                    {
                      ssb-Index 1,
                      ssb-Results {
                        rsrp 100,
                        rsrq 66,
                        sinr 93
                      }
                    }
                  }
                }
              }
            }
          }
        },
        measResultNeighCells measResultListNR : {
          {
            physCellId 86,
            measResult {
              cellResults {
                resultsSSB-Cell {
                  rsrp 46,
                  rsrq 53,
                  sinr 38
                }
              },
              rsIndexResults {
                resultsSSB-Indexes {
                  {
                    ssb-Index 0,
                    ssb-Results {
                      rsrp 47,
                      rsrq 56,
                      sinr 42
                    }
                  },
                  {
                    ssb-Index 7,
                    ssb-Results {
                      rsrp 46,
                      rsrq 54,
                      sinr 39
                    }
                  },
                  {
                    ssb-Index 6,
                    ssb-Results {
                      rsrp 44,
                      rsrq 47,
                      sinr 30
                    }
                  },
                  {
                    ssb-Index 2,
                    ssb-Results {
                      rsrp 44,
                      rsrq 53,
                      sinr 38
                    }
                  },
                  {
                    ssb-Index 3,
                    ssb-Results {
                      rsrp 43,
                      rsrq 50,
                      sinr 34
                    }
                  },
                  {
                    ssb-Index 4,
                    ssb-Results {
                      rsrp 42,
                      rsrq 48,
                      sinr 34
                    }
                  }
                }
              }
            }
          }
        },
        locationInfo-r16 {
          commonLocationInfo-r16 {
            gnss-TOD-msec-r16 'AC3580'H,
            locationTimestamp-r16 CHOICE: utcTime-r15 : {
              utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,
              utcTime-ms-r15 470
            },
            locationCoordinate-r16 '4307EC2CD636701A40'H,
            locationSource-r16 '00'H
          }
        }
      }
    }
  }
}

That's it.

dbressan2 commented 2 years ago

Many thanks

I was actually looking for a way to introduce the CONTAINING link programmatically, so binding the LPP object to the RRC one within Python is a good solution for me.

Thank you very much for your help

Best regards

Dominique

From: Benoit Michau @.> Sent: Friday, February 11, 2022 3:00 PM To: P1sec/pycrate @.> Cc: Dominique Bressanelli @.>; Author @.> Subject: Re: [P1sec/pycrate] Question on best way to chain decoding with 2 different grammars (Issue #179)

WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.

One way to manage it is at the ASN.1 level. OCTET STRING (and BIT STRING) can have a CONTAINING constraint, which indicates another ASN.1 object to be decoded from the OCTET STRING (or BIT STRING) buffer.

E.g.:

ComplexObj ::= SEQUENCE {

...

}

OctStr ::= OCTET STRING (CONTAINING ComplexObj)

I believe the simplest, and maybe only, way to do what you ask for is to build such a construct with your ASN.1 definitions. So you can modify your RRC NR specification, to enhance the definition of the locationTimestamp-r16 with such a (CONTAINING DisplacementTimeStamp-r15) declaration. You will also need to manage properly the import of the LPP object in the RRC NR definition. Otherwise, you can bind the LPP object to the RRC one within Python, doing something like this:

In [47]: M = NR_RRC_Definitions.UL_DCCH_Message

In [48]: D = LPP_PDU_Definitions.DisplacementTimeStamp_r15

In [49]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo

...: -r16', 'locationTimestamp-r16'))._const_cont = D # this corresponds to the (CONTAINING ...) constraint

In [50]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo

...: -r16', 'locationTimestamp-r16'))._const_cont_enc = None # this is to say the codec stays the same

In [51]: M.from_uper(b'\x00\xc2\x00\x04c\xdf$+\xb0\x10~HWA\x15\xb7\\xd54,\x0e\xbd\xc2\xa8\xfdsd\xf1\xba\xc5\xe7\xa1u\x8dSC\xea\xd9"\x89\xd50D\x08\x91!\x1

...: d\x01\xd6\x1a\xc0\x07\x80k&L\x190p\xc1\x8bF\xcd\x19\xdau\x80\x04\xa1\x83\xf6\x16k\x1b8\r \x00\x80\x00')

In [52]: print(M.to_asn1())

{

message c1 : measurementReport : {

criticalExtensions measurementReport : {

  measResults {

    measId 3,

    measResultServingMOList {

      {

        servCellId 0,

        measResultServingCell {

          physCellId 99,

          measResult {

            cellResults {

              resultsSSB-Cell {

                rsrp 100,

                rsrq 66,

                sinr 93

              }

            },

            rsIndexResults {

              resultsSSB-Indexes {

                {

                  ssb-Index 1,

                  ssb-Results {

                    rsrp 100,

                    rsrq 66,

                    sinr 93

                  }

                }

              }

            }

          }

        }

      }

    },

    measResultNeighCells measResultListNR : {

      {

        physCellId 86,

        measResult {

          cellResults {

            resultsSSB-Cell {

              rsrp 46,

              rsrq 53,

              sinr 38

            }

          },

          rsIndexResults {

            resultsSSB-Indexes {

              {

                ssb-Index 0,

                ssb-Results {

                  rsrp 47,

                  rsrq 56,

                  sinr 42

                }

              },

              {

                ssb-Index 7,

                ssb-Results {

                  rsrp 46,

                  rsrq 54,

                  sinr 39

                }

              },

              {

                ssb-Index 6,

                ssb-Results {

                  rsrp 44,

                  rsrq 47,

                  sinr 30

                }

              },

              {

                ssb-Index 2,

                ssb-Results {

                  rsrp 44,

                  rsrq 53,

                  sinr 38

                }

              },

              {

                ssb-Index 3,

                ssb-Results {

                  rsrp 43,

                  rsrq 50,

                  sinr 34

                }

              },

              {

                ssb-Index 4,

                ssb-Results {

                  rsrp 42,

                  rsrq 48,

                  sinr 34

                }

              }

            }

          }

        }

      }

    },

    locationInfo-r16 {

      commonLocationInfo-r16 {

        gnss-TOD-msec-r16 'AC3580'H,

        locationTimestamp-r16 CHOICE: utcTime-r15 : {

          utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,

          utcTime-ms-r15 470

        },

        locationCoordinate-r16 '4307EC2CD636701A40'H,

        locationSource-r16 '00'H

      }

    }

  }

}

}

}

That's it.

— Reply to this email directly, view it on GitHubhttps://github.com/P1sec/pycrate/issues/179#issuecomment-1036241970, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHUUMPLBIPV37RK7QPTMG23U2UI6VANCNFSM5OEB2JBA. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub. You are receiving this because you authored the thread.Message ID: @.**@.>>

dbressan2 commented 2 years ago

Hi

Small additional request

Is there a way that the leading ASN.1 tag (here CHOICE) may be omitted ?

Instead of

    locationInfo-r16 {

      commonLocationInfo-r16 {

        gnss-TOD-msec-r16 'AC3580'H,

        locationTimestamp-r16 CHOICE: utcTime-r15 : {

          utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,

          utcTime-ms-r15 470

        },

I would like to get:

    locationInfo-r16 {

      commonLocationInfo-r16 {

        gnss-TOD-msec-r16 'AC3580'H,

        locationTimestamp-r16 utcTime-r15 : {

          utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,

          utcTime-ms-r15 470

        },

Thank you very much for your help

Best regards

Dominique

From: Dominique Bressanelli Sent: Friday, February 11, 2022 5:05 PM To: P1sec/pycrate @.>; P1sec/pycrate @.> Cc: Author @.***> Subject: RE: [P1sec/pycrate] Question on best way to chain decoding with 2 different grammars (Issue #179)

Many thanks

I was actually looking for a way to introduce the CONTAINING link programmatically, so binding the LPP object to the RRC one within Python is a good solution for me.

Thank you very much for your help

Best regards

Dominique

From: Benoit Michau @.**@.>> Sent: Friday, February 11, 2022 3:00 PM To: P1sec/pycrate @.**@.>> Cc: Dominique Bressanelli @.**@.>>; Author @.**@.>> Subject: Re: [P1sec/pycrate] Question on best way to chain decoding with 2 different grammars (Issue #179)

WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.

One way to manage it is at the ASN.1 level. OCTET STRING (and BIT STRING) can have a CONTAINING constraint, which indicates another ASN.1 object to be decoded from the OCTET STRING (or BIT STRING) buffer.

E.g.:

ComplexObj ::= SEQUENCE {

...

}

OctStr ::= OCTET STRING (CONTAINING ComplexObj)

I believe the simplest, and maybe only, way to do what you ask for is to build such a construct with your ASN.1 definitions. So you can modify your RRC NR specification, to enhance the definition of the locationTimestamp-r16 with such a (CONTAINING DisplacementTimeStamp-r15) declaration. You will also need to manage properly the import of the LPP object in the RRC NR definition. Otherwise, you can bind the LPP object to the RRC one within Python, doing something like this:

In [47]: M = NR_RRC_Definitions.UL_DCCH_Message

In [48]: D = LPP_PDU_Definitions.DisplacementTimeStamp_r15

In [49]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo

...: -r16', 'locationTimestamp-r16'))._const_cont = D # this corresponds to the (CONTAINING ...) constraint

In [50]: M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo

...: -r16', 'locationTimestamp-r16'))._const_cont_enc = None # this is to say the codec stays the same

In [51]: M.from_uper(b'\x00\xc2\x00\x04c\xdf$+\xb0\x10~HWA\x15\xb7\\xd54,\x0e\xbd\xc2\xa8\xfdsd\xf1\xba\xc5\xe7\xa1u\x8dSC\xea\xd9"\x89\xd50D\x08\x91!\x1

...: d\x01\xd6\x1a\xc0\x07\x80k&L\x190p\xc1\x8bF\xcd\x19\xdau\x80\x04\xa1\x83\xf6\x16k\x1b8\r \x00\x80\x00')

In [52]: print(M.to_asn1())

{

message c1 : measurementReport : {

criticalExtensions measurementReport : {

  measResults {

    measId 3,

    measResultServingMOList {

      {

        servCellId 0,

        measResultServingCell {

          physCellId 99,

          measResult {

            cellResults {

              resultsSSB-Cell {

                rsrp 100,

                rsrq 66,

                sinr 93

              }

            },

            rsIndexResults {

              resultsSSB-Indexes {

                {

                  ssb-Index 1,

                  ssb-Results {

                    rsrp 100,

                    rsrq 66,

                    sinr 93

                  }

                }

              }

            }

          }

        }

      }

    },

    measResultNeighCells measResultListNR : {

      {

        physCellId 86,

        measResult {

          cellResults {

            resultsSSB-Cell {

              rsrp 46,

              rsrq 53,

              sinr 38

            }

          },

          rsIndexResults {

            resultsSSB-Indexes {

              {

                ssb-Index 0,

                ssb-Results {

                  rsrp 47,

                  rsrq 56,

                  sinr 42

                }

              },

              {

                ssb-Index 7,

                ssb-Results {

                  rsrp 46,

                  rsrq 54,

                  sinr 39

                }

              },

              {

                ssb-Index 6,

                ssb-Results {

                  rsrp 44,

                  rsrq 47,

                  sinr 30

                }

              },

              {

                ssb-Index 2,

                ssb-Results {

                  rsrp 44,

                  rsrq 53,

                  sinr 38

                }

              },

              {

                ssb-Index 3,

                ssb-Results {

                  rsrp 43,

                  rsrq 50,

                  sinr 34

                }

              },

              {

                ssb-Index 4,

                ssb-Results {

                  rsrp 42,

                  rsrq 48,

                  sinr 34

                }

              }

            }

          }

        }

      }

    },

    locationInfo-r16 {

      commonLocationInfo-r16 {

        gnss-TOD-msec-r16 'AC3580'H,

        locationTimestamp-r16 CHOICE: utcTime-r15 : {

          utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,

          utcTime-ms-r15 470

        },

        locationCoordinate-r16 '4307EC2CD636701A40'H,

        locationSource-r16 '00'H

      }

    }

  }

}

}

}

That's it.

— Reply to this email directly, view it on GitHubhttps://github.com/P1sec/pycrate/issues/179#issuecomment-1036241970, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AHUUMPLBIPV37RK7QPTMG23U2UI6VANCNFSM5OEB2JBA. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub. You are receiving this because you authored the thread.Message ID: @.**@.>>

p1-bmu commented 2 years ago

OK, I guess I went to fast. We should actually better mimic the compiler to produce a similar construct, what is not the case with what I provided before as an example. First, we need to build a wrapper around the DisplacementTimeStamp-r15 object:

In [69]: _cont_locationTimestamp_r16 = CHOICE(name='DisplacementTimeStamp-r15', mode=MODE_TYPE, typeref=ASN1RefType(('LPP-PDU-Definitions', 'DisplacementTimeStamp-r15')))

Then, we can bind the NR RRC object to it:

In [70]: S = M.get_at(('message', 'c1', 'measurementReport', 'criticalExtensions', 'measurementReport', 'measResults', 'locationInfo-r16', 'commonLocationInfo-r16', 'locationTimestamp-r16'))                                                                                                             

In [71]: S._const_cont = _cont_locationTimestamp_r16                                                                                                      

In [72]: S._const_cont_enc = None

Finally, we need to declare the new wrapper object into the ASN.1 module, and re-initialize the module:

In [73]: from pycrate_asn1rt.init import init_modules                                                                                                     

In [74]: NR_RRC_Definitions._all_.append(_cont_locationTimestamp_r16)                                                                                     

In [75]: init_modules(NR_RRC_Definitions) 

Now, we can use the NR RRC object as its inner definition would have been:

CommonLocationInfo-r16 ::= SEQUENCE {
    gnss-TOD-msec-r16          OCTET STRING     OPTIONAL,
    locationTimestamp-r16      OCTET STRING     (CONTAINING DisplacementTimeStamp-r15)    OPTIONAL,
    locationCoordinate-r16     OCTET STRING     OPTIONAL,
    locationError-r16          OCTET STRING     OPTIONAL,
    locationSource-r16         OCTET STRING     OPTIONAL,
    velocityEstimate-r16       OCTET STRING     OPTIONAL
}

This leads to the following decoding:

In [77]: M.from_uper(b'\x00\xc2\x00\x04c\xdf$+\xb0\x10~HWA\x15\xb7\\\xd54,\x0e\xbd\xc2\xa8\xfdsd\xf1\xba\xc5\xe7\xa1u\x8dSC\xea\xd9"\x89\xd50D\x08\x91!\x1d\x01\xd6\x1a\xc0\x07\x80k&L\x190p\xc1\x8bF\xcd\x19\xdau\x80\x04\xa1\x83\xf6\x16k\x1b8\r \x00\x80\x00')                                          

In [78]: print(M.to_asn1())                                                                                                                               
{
  message c1 : measurementReport : {
    criticalExtensions measurementReport : {
      measResults {
        measId 3,
        measResultServingMOList {
[...]
        measResultNeighCells measResultListNR : {
[...]
        locationInfo-r16 {
          commonLocationInfo-r16 {
            gnss-TOD-msec-r16 'AC3580'H,
            locationTimestamp-r16 DisplacementTimeStamp-r15: utcTime-r15 : {
              utcTime-r15 "220208014643Z" -- Tue Feb  8 01:46:43 2022 --,
              utcTime-ms-r15 470
            },
            locationCoordinate-r16 '4307EC2CD636701A40'H,
            locationSource-r16 '00'H
          }
        }
      }
    }
  }
}

In [79]: S._val                                                                                                                                           
Out[79]: 
('DisplacementTimeStamp-r15',
 ('utcTime-r15',
  {'utcTime-r15': ('22', '02', '08', '01', '46', '43', 'Z'),
   'utcTime-ms-r15': 470}))