22#This code is licensed under the BSD software license
33#
44
5+ from typing import Sequence
6+
7+ from .record import (
8+ AntennaPatternRecord ,
9+ ModulationType ,
10+ ModulationParametersRecord ,
11+ UnknownRadio ,
12+ UnknownAntennaPattern ,
13+ )
14+ from .stream import DataInputStream , DataOutputStream
515from .types import (
616 enum8 ,
717 enum16 ,
1727 struct16 ,
1828 struct32 ,
1929)
20- from .record import SpreadSpectrum
2130
2231
2332class DataQueryDatumSpecification :
@@ -669,22 +678,6 @@ def parse(self, inputStream):
669678 self .stationNumber = inputStream .read_unsigned_short ()
670679
671680
672- class ModulationParameters :
673- """Section 6.2.58
674-
675- Modulation parameters associated with a specific radio system. INCOMPLETE.
676- """
677-
678- def __init__ (self ):
679- pass
680-
681- def serialize (self , outputStream ):
682- """serialize the class"""
683-
684- def parse (self , inputStream ):
685- """Parse a message. This may recursively call embedded objects."""
686-
687-
688681class EulerAngles :
689682 """Section 6.2.33
690683
@@ -951,21 +944,28 @@ class VariableTransmitterParameters:
951944 Relates to radios. NOT COMPLETE.
952945 """
953946
954- def __init__ (self , recordType : enum32 = 0 , recordLength : uint16 = 4 ):
947+ def __init__ (self , recordType : enum32 = 0 , data : bytes = b"" ):
955948 self .recordType = recordType # [UID 66] Variable Parameter Record Type
956- """Type of VTP. Enumeration from EBV"""
957- self .recordLength = recordLength
958- """Length, in bytes"""
949+ self .data = data
950+
951+ def marshalledSize (self ) -> int :
952+ return 6 + len (self .data )
953+
954+ @property
955+ def recordLength (self ) -> uint16 :
956+ return self .marshalledSize ()
959957
960- def serialize (self , outputStream ) :
958+ def serialize (self , outputStream : DataOutputStream ) -> None :
961959 """serialize the class"""
962- outputStream .write_unsigned_int (self .recordType )
963- outputStream .write_unsigned_int (self .recordLength )
960+ outputStream .write_uint32 (self .recordType )
961+ outputStream .write_uint16 (self .recordLength )
962+ outputStream .write_bytes (self .data )
964963
965- def parse (self , inputStream ) :
964+ def parse (self , inputStream : DataInputStream ) -> None :
966965 """Parse a message. This may recursively call embedded objects."""
967- self .recordType = inputStream .read_unsigned_int ()
968- self .recordLength = inputStream .read_unsigned_int ()
966+ self .recordType = inputStream .read_uint32 ()
967+ recordLength = inputStream .read_uint16 ()
968+ self .data = inputStream .read_bytes (recordLength )
969969
970970
971971class Attribute :
@@ -2017,41 +2017,6 @@ def parse(self, inputStream):
20172017 self .communicationsNodeID .parse (inputStream )
20182018
20192019
2020- class ModulationType :
2021- """Section 6.2.59
2022-
2023- Information about the type of modulation used for radio transmission.
2024- """
2025-
2026- def __init__ (self ,
2027- spreadSpectrum : SpreadSpectrum | None = None , # See RPR Enumerations
2028- majorModulation : enum16 = 0 , # [UID 155]
2029- detail : enum16 = 0 , # [UID 156-162]
2030- radioSystem : enum16 = 0 ): # [UID 163]
2031- self .spreadSpectrum = spreadSpectrum or SpreadSpectrum ()
2032- """This field shall indicate the spread spectrum technique or combination of spread spectrum techniques in use. Bit field. 0=freq hopping, 1=psuedo noise, time hopping=2, reamining bits unused"""
2033- self .majorModulation = majorModulation
2034- """the major classification of the modulation type."""
2035- self .detail = detail
2036- """provide certain detailed information depending upon the major modulation type"""
2037- self .radioSystem = radioSystem
2038- """the radio system associated with this Transmitter PDU and shall be used as the basis to interpret other fields whose values depend on a specific radio system."""
2039-
2040- def serialize (self , outputStream ):
2041- """serialize the class"""
2042- outputStream .write_unsigned_short (self .spreadSpectrum )
2043- outputStream .write_unsigned_short (self .majorModulation )
2044- outputStream .write_unsigned_short (self .detail )
2045- outputStream .write_unsigned_short (self .radioSystem )
2046-
2047- def parse (self , inputStream ):
2048- """Parse a message. This may recursively call embedded objects."""
2049- self .spreadSpectrum = inputStream .read_unsigned_short ()
2050- self .majorModulation = inputStream .read_unsigned_short ()
2051- self .detail = inputStream .read_unsigned_short ()
2052- self .radioSystem = inputStream .read_unsigned_short ()
2053-
2054-
20552020class LinearSegmentParameter :
20562021 """Section 6.2.52
20572022
@@ -5460,20 +5425,18 @@ def __init__(self,
54605425 radioEntityType : "EntityType | None" = None ,
54615426 transmitState : enum8 = 0 , # [UID 164]
54625427 inputSource : enum8 = 0 , # [UID 165]
5463- variableTransmitterParameterCount : uint16 = 0 ,
54645428 antennaLocation : "Vector3Double | None" = None ,
54655429 relativeAntennaLocation : "Vector3Float | None" = None ,
54665430 antennaPatternType : enum16 = 0 , # [UID 167]
5467- antennaPatternCount : uint16 = 0 , # in bytes
54685431 frequency : uint64 = 0 , # in Hz
54695432 transmitFrequencyBandwidth : float32 = 0.0 , # in Hz
54705433 power : float32 = 0.0 , # in decibel-milliwatts
54715434 modulationType : "ModulationType | None" = None ,
54725435 cryptoSystem : enum16 = 0 , # [UID 166]
54735436 cryptoKeyId : struct16 = 0 , # See Table 175
5474- modulationParameterCount : uint8 = 0 , # in bytes
5475- modulationParametersList = None ,
5476- antennaPatternList = None ):
5437+ modulationParameters : ModulationParametersRecord | None = None ,
5438+ antennaPattern : AntennaPatternRecord | None = None ,
5439+ variableTransmitterParameters : Sequence [ VariableTransmitterParameters ] | None = None ):
54775440 super (TransmitterPdu , self ).__init__ ()
54785441 self .radioReferenceID = radioReferenceID or EntityID ()
54795442 """ID of the entity that is the source of the communication"""
@@ -5486,107 +5449,116 @@ def __init__(self,
54865449 self .relativeAntennaLocation = relativeAntennaLocation or Vector3Float (
54875450 )
54885451 self .antennaPatternType = antennaPatternType
5489- self .antennaPatternCount = antennaPatternCount
54905452 self .frequency = frequency
54915453 self .transmitFrequencyBandwidth = transmitFrequencyBandwidth
54925454 self .power = power
54935455 self .modulationType = modulationType or ModulationType ()
54945456 self .cryptoSystem = cryptoSystem
54955457 self .cryptoKeyId = cryptoKeyId
5496- # FIXME: Refactor modulation parameters into its own record class
5497- self .modulationParameterCount = modulationParameterCount
54985458 self .padding2 = 0
54995459 self .padding3 = 0
5500- self .modulationParametersList = modulationParametersList or []
5501- self .antennaPatternList = antennaPatternList or []
5502- # TODO: zero or more Variable Transmitter Parameters records (see 6.2.95)
5460+ self .modulationParameters = modulationParameters
5461+ self .antennaPattern = antennaPattern
5462+ self .variableTransmitterParameters = variableTransmitterParameters or []
5463+
5464+ @property
5465+ def antennaPatternLength (self ) -> uint16 :
5466+ if self .antennaPattern :
5467+ return self .antennaPattern .marshalledSize ()
5468+ else :
5469+ return 0
5470+
5471+ @property
5472+ def modulationParametersLength (self ) -> uint8 :
5473+ if self .modulationParameters :
5474+ return self .modulationParameters .marshalledSize ()
5475+ else :
5476+ return 0
55035477
55045478 @property
55055479 def variableTransmitterParameterCount (self ) -> uint16 :
5506- """How many variable transmitter parameters are in the variable length list.
5507- In earlier versions of DIS these were known as articulation parameters.
5508- """
5509- return len (self .modulationParametersList )
5480+ return len (self .variableTransmitterParameters )
55105481
5511- def serialize (self , outputStream ):
5512- """serialize the class"""
5482+ def serialize (self , outputStream : DataOutputStream ) -> None :
55135483 super (TransmitterPdu , self ).serialize (outputStream )
55145484 self .radioReferenceID .serialize (outputStream )
5515- outputStream .write_unsigned_short (self .radioNumber )
5485+ outputStream .write_uint16 (self .radioNumber )
55165486 self .radioEntityType .serialize (outputStream )
5517- outputStream .write_unsigned_byte (self .transmitState )
5518- outputStream .write_unsigned_byte (self .inputSource )
5519- outputStream .write_unsigned_short (
5520- self .variableTransmitterParameterCount )
5487+ outputStream .write_uint8 (self .transmitState )
5488+ outputStream .write_uint8 (self .inputSource )
5489+ outputStream .write_uint16 (self .variableTransmitterParameterCount )
55215490 self .antennaLocation .serialize (outputStream )
55225491 self .relativeAntennaLocation .serialize (outputStream )
5523- outputStream .write_unsigned_short (self .antennaPatternType )
5524- outputStream .write_unsigned_short ( len ( self .antennaPatternList ) )
5525- outputStream .write_long (self .frequency )
5526- outputStream .write_float (self .transmitFrequencyBandwidth )
5527- outputStream .write_float (self .power )
5492+ outputStream .write_uint16 (self .antennaPatternType )
5493+ outputStream .write_uint16 ( self .antennaPatternLength )
5494+ outputStream .write_uint64 (self .frequency )
5495+ outputStream .write_float32 (self .transmitFrequencyBandwidth )
5496+ outputStream .write_float32 (self .power )
55285497 self .modulationType .serialize (outputStream )
5529- outputStream .write_unsigned_short (self .cryptoSystem )
5530- outputStream .write_unsigned_short (self .cryptoKeyId )
5531- outputStream .write_unsigned_byte (len (self .modulationParametersList ))
5532- outputStream .write_unsigned_short (self .padding2 )
5533- outputStream .write_unsigned_byte (self .padding3 )
5534- for anObj in self .modulationParametersList :
5535- anObj .serialize (outputStream )
5498+ outputStream .write_uint16 (self .cryptoSystem )
5499+ outputStream .write_uint16 (self .cryptoKeyId )
5500+ outputStream .write_uint8 (self .modulationParametersLength )
5501+ outputStream .write_uint16 (self .padding2 )
5502+ outputStream .write_uint8 (self .padding3 )
55365503
5537- for anObj in self .antennaPatternList :
5538- anObj .serialize (outputStream )
5504+ # Serialize parameter records
55395505
5540- def parse (self , inputStream ):
5506+ ## Modulation Parameters
5507+ if self .modulationParameters :
5508+ self .modulationParameters .serialize (outputStream )
5509+
5510+ ## Antenna Pattern
5511+ if self .antennaPattern :
5512+ self .antennaPattern .serialize (outputStream )
5513+
5514+ ## Variable Transmitter Parameters
5515+ for vtp in self .variableTransmitterParameters :
5516+ vtp .serialize (outputStream )
5517+
5518+ def parse (self , inputStream : DataInputStream ) -> None :
55415519 """Parse a message. This may recursively call embedded objects."""
55425520 super (TransmitterPdu , self ).parse (inputStream )
55435521 self .radioReferenceID .parse (inputStream )
5544- self .radioNumber = inputStream .read_unsigned_short ()
5522+ self .radioNumber = inputStream .read_uint16 ()
55455523 self .radioEntityType .parse (inputStream )
5546- self .transmitState = inputStream .read_unsigned_byte ()
5547- self .inputSource = inputStream .read_unsigned_byte ()
5548- variableTransmitterParameterCount = inputStream .read_unsigned_short (
5549- )
5524+ self .transmitState = inputStream .read_uint8 ()
5525+ self .inputSource = inputStream .read_uint8 ()
5526+ variableTransmitterParameterCount = inputStream .read_uint16 ()
55505527 self .antennaLocation .parse (inputStream )
55515528 self .relativeAntennaLocation .parse (inputStream )
5552- self .antennaPatternType = inputStream .read_unsigned_short ()
5553- self . antennaPatternCount = inputStream .read_unsigned_short ()
5554- self .frequency = inputStream .read_long ()
5555- self .transmitFrequencyBandwidth = inputStream .read_float ()
5556- self .power = inputStream .read_float ()
5529+ self .antennaPatternType = inputStream .read_uint16 ()
5530+ antennaPatternLength = inputStream .read_uint16 ()
5531+ self .frequency = inputStream .read_uint64 ()
5532+ self .transmitFrequencyBandwidth = inputStream .read_float32 ()
5533+ self .power = inputStream .read_float32 ()
55575534 self .modulationType .parse (inputStream )
5558- self .cryptoSystem = inputStream .read_unsigned_short ()
5559- self .cryptoKeyId = inputStream .read_unsigned_short ()
5560- self .modulationParameterCount = inputStream .read_unsigned_byte ()
5561- self .padding2 = inputStream .read_unsigned_short ()
5562- self .padding3 = inputStream .read_unsigned_byte ()
5563- """Vendor product MACE from BattleSpace Inc, only uses 1 byte per modulation param"""
5564- """SISO Spec dictates it should be 2 bytes"""
5565- """Instead of dumping the packet we can make an assumption that some vendors use 1 byte per param"""
5566- """Although we will still send out 2 bytes per param as per spec"""
5567- endsize = self .antennaPatternCount * 39
5568- mod_bytes = 2
5569-
5570- if (self .modulationParameterCount > 0 ):
5571- curr = inputStream .stream .tell ()
5572- remaining = inputStream .stream .read (None )
5573- mod_bytes = (len (remaining ) -
5574- endsize ) / self .modulationParameterCount
5575- inputStream .stream .seek (curr , 0 )
5576-
5577- if (mod_bytes > 2 ):
5578- print ("Malformed Packet" )
5535+ self .cryptoSystem = inputStream .read_uint16 ()
5536+ self .cryptoKeyId = inputStream .read_uint16 ()
5537+ modulationParametersLength = inputStream .read_uint8 ()
5538+ self .padding2 = inputStream .read_uint16 ()
5539+ self .padding3 = inputStream .read_uint8 ()
5540+
5541+ # Parse parameter records
5542+
5543+ ## Modulation Parameters
5544+ if modulationParametersLength > 0 :
5545+ radio = UnknownRadio ()
5546+ radio .parse (inputStream , bytelength = modulationParametersLength )
5547+ self .modulationParameters = radio
55795548 else :
5580- for idx in range (0 , self .modulationParameterCount ):
5581- if mod_bytes == 2 :
5582- element = inputStream .read_unsigned_short ()
5583- else :
5584- element = inputStream .read_unsigned_byte ()
5585- self .modulationParametersList .append (element )
5586- for idx in range (0 , self .antennaPatternCount ):
5587- element = BeamAntennaPattern ()
5588- element .parse (inputStream )
5589- self .antennaPatternList .append (element )
5549+ self .modulationParameters = None
5550+
5551+ ## Antenna Pattern
5552+ if antennaPatternLength > 0 :
5553+ self .antennaPattern = UnknownAntennaPattern ()
5554+ self .antennaPattern .parse (
5555+ inputStream ,
5556+ bytelength = antennaPatternLength
5557+ )
5558+ else :
5559+ self .antennaPattern = None
5560+
5561+
55905562
55915563
55925564class ElectromagneticEmissionsPdu (DistributedEmissionsFamilyPdu ):
0 commit comments