bearer: Create a generic GSMBitField helper, implement BearerCaps parsing
The single bearer cap octets are represented as classes and provide getter/setter to the underlying byte. The BearerCapFrom.. provides a way to parse and later generate the caps. Parse one example cap and dissect it.
This commit is contained in:
parent
c115abccca
commit
0cffe9f7ac
262
GSM48.st
262
GSM48.st
|
@ -503,6 +503,247 @@ GSM48SimpleTag subclass: GSMPriorityLevel [
|
|||
GSMPriorityLevel class >> elementId [ ^ 16r80 ]
|
||||
]
|
||||
|
||||
Object subclass: GSMBitField [
|
||||
| byte |
|
||||
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: 'I am a 7bit bitfield and provide nice access. I am
|
||||
commonly used with the GSM fields where the 8th indicate if things
|
||||
continue or not. I can automatically generate accessors based on
|
||||
a bit definition class'>
|
||||
|
||||
GSMBitField class >> fromByte: aByte [
|
||||
<category: 'creation'>
|
||||
^ self new
|
||||
byte: aByte;
|
||||
yourself
|
||||
]
|
||||
|
||||
GSMBitField class >> initialize [
|
||||
<category: 'protected'>
|
||||
|
||||
self bitDefinition do: [:each |
|
||||
self compile: '%1 [ ^ self bitsFrom: (%2 to: %3) ]'
|
||||
% {each first. each second. each third};
|
||||
compile: '%1: aVal [ self atBits: (%2 to: %3) put: aVal]'
|
||||
% {each first. each second. each third}.
|
||||
]
|
||||
]
|
||||
|
||||
data [
|
||||
<category: 'access'>
|
||||
^ byte
|
||||
]
|
||||
|
||||
byte: aByte [
|
||||
<category: 'private'>
|
||||
byte := aByte
|
||||
]
|
||||
|
||||
bitsFrom: interval [
|
||||
| res mask |
|
||||
<category: 'private'>
|
||||
|
||||
"Build the mask"
|
||||
mask := 0.
|
||||
interval do: [:each |
|
||||
mask := mask bitAt: each put: 1].
|
||||
|
||||
"And with the value and shift"
|
||||
res := (byte bitAnd: mask) bitShift: (interval first - 1) negated.
|
||||
^ res
|
||||
]
|
||||
|
||||
atBits: interval put: aVal [
|
||||
| shifted |
|
||||
aVal highBit > interval last ifTrue: [
|
||||
^ self error: 'Value bigger than interval'.
|
||||
].
|
||||
|
||||
shifted := aVal bitShift: interval first - 1.
|
||||
interval do: [:each |
|
||||
byte := byte bitAt: each put: (shifted bitAt: each)].
|
||||
]
|
||||
]
|
||||
|
||||
GSMBitField subclass: GSMBearerCapOctet3 [
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: '10.5.4.5 Octet3'>
|
||||
|
||||
GSMBearerCapOctet3 class [
|
||||
radioChReqReserved [ ^ 2r00 ]
|
||||
radioChReqFullRateOnly [ ^ 2r01 ]
|
||||
radioChReqDualHalfPref [ ^ 2r10 ]
|
||||
radioChReqDualFullPref [ ^ 2r11 ]
|
||||
|
||||
radioCodStdGSM [ ^ 2r0 ]
|
||||
radioCodStdReserved [ ^ 2r1 ]
|
||||
|
||||
transferModeCircuit [ ^ 2r0 ]
|
||||
transferModePacket [ ^ 2r1 ]
|
||||
|
||||
transferCapSpeech [ ^ 2r000 ]
|
||||
transferCapUnrest [ ^ 2r001 ]
|
||||
transferCap31khzPlmn [ ^ 2r010 ]
|
||||
transferCapFacsimileGroup3 [ ^ 2r011 ]
|
||||
transferCapOtherITC [ ^ 2r101 ] "see Octet 5a"
|
||||
transferCapReserved [ ^ 2r111 ]
|
||||
|
||||
bitDefinition [
|
||||
"Bit definition of Octet3"
|
||||
^ OrderedCollection new
|
||||
add: #('informationTransferCapability' 1 3);
|
||||
add: #('transferMode' 4 4);
|
||||
add: #('codingStandard' 5 5);
|
||||
add: #('radioChannelRequirement' 6 7);
|
||||
yourself
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
GSMBitField subclass: GSMBearerCapOctet3a [
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: '10.5.4.5 Octet3a MS to network'>
|
||||
|
||||
GSMBearerCapOctet3a class [
|
||||
codingForTransferCapability [ ^ 2r0 ]
|
||||
codingForOtherExtensions [ ^ 2r1 ]
|
||||
|
||||
ctmTextTelephonyNotSupported [ ^ 2r0 ]
|
||||
ctmTextTelephonySupported [ ^ 2r1 ]
|
||||
|
||||
speechFullRateVersion1 [ ^ 2r0000 ]
|
||||
speechFullRateVersion2 [ ^ 2r0010 ]
|
||||
speechFullRateVersion3 [ ^ 2r0100 ]
|
||||
speechHalfRateVersion1 [ ^ 2r0001 ]
|
||||
speechHalfRateVersion3 [ ^ 2r0101 ]
|
||||
|
||||
bitDefinition [
|
||||
^ OrderedCollection new
|
||||
add: #('speechVersionIndication' 1 4);
|
||||
add: #('spare' 5 5);
|
||||
add: #('ctm' 6 6);
|
||||
add: #('coding' 7 7);
|
||||
yourself
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
GSMBearerCapOctet3a subclass: GSMBearerCapOctet3b [
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: '10.5.4.5 Octet3b network to MS'>
|
||||
|
||||
GSMBearerCapOctet3b class [
|
||||
bitDefinition [
|
||||
"There is no CTM in this directtion"
|
||||
^ OrderedCollection new
|
||||
add: #('speechVersionIndication' 1 4);
|
||||
add: #('spare' 5 6);
|
||||
add: #('coding' 7 7);
|
||||
yourself
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
Object subclass: GSMBearerCapDecoderBase [
|
||||
| octet3 |
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: 'I am the base of BearerCapDecoder parsing. My children
|
||||
can parse the 10.5.4.5 bearer caps.'>
|
||||
|
||||
GSMBearerCapDecoderBase class >> parse: aStream [
|
||||
^ self new
|
||||
parse: aStream;
|
||||
checkEndOfStream: aStream;
|
||||
yourself
|
||||
]
|
||||
|
||||
octet3 [
|
||||
^ octet3
|
||||
]
|
||||
|
||||
parse: aStream [
|
||||
<category: 'protected'>
|
||||
^ self parseOctet3: aStream
|
||||
]
|
||||
|
||||
parseOctet3: aStream [
|
||||
| byte |
|
||||
"I return true if there is more for octet3"
|
||||
<category: 'protected'>
|
||||
octet3 := nil.
|
||||
|
||||
"nothing left to read"
|
||||
aStream atEnd ifTrue: [
|
||||
^ false].
|
||||
|
||||
octet3 := GSMBearerCapOctet3 fromByte: aStream next.
|
||||
^ (octet3 data bitAt: 8) = 0.
|
||||
]
|
||||
|
||||
parseOctets: aStream do: aBlock [
|
||||
<category: 'protected'>
|
||||
|
||||
[aStream atEnd] whileFalse: [
|
||||
| byte |
|
||||
|
||||
byte := aStream next.
|
||||
aBlock value: byte.
|
||||
|
||||
"Check if we are at an end here"
|
||||
(byte bitAt: 8) = 1 ifTrue: [
|
||||
^ self
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
checkEndOfStream: aStream [
|
||||
<category: 'private'>
|
||||
|
||||
aStream atEnd ifFalse: [
|
||||
^ self error: 'Bearercaps not fully consumed.'
|
||||
].
|
||||
]
|
||||
]
|
||||
|
||||
GSMBearerCapDecoderBase subclass: GSMBearerCapFromNetwork [
|
||||
| octet3b |
|
||||
<category: 'OsmoGSM'>
|
||||
|
||||
parse: aStream [
|
||||
<category: 'private'>
|
||||
octet3b := nil.
|
||||
(self parseOctet3: aStream) ifTrue: [
|
||||
self parseOctets: aStream do: [:each |
|
||||
self octet3b add: (GSMBearerCapOctet3b fromByte: each)]
|
||||
].
|
||||
]
|
||||
|
||||
octet3b [
|
||||
<category: 'accessing'>
|
||||
^ octet3b ifNil: [octet3b := OrderedCollection new]
|
||||
]
|
||||
]
|
||||
|
||||
GSMBearerCapDecoderBase subclass: GSMBearerCapFromMS [
|
||||
| octet3a |
|
||||
<category: 'OsmoGSM'>
|
||||
|
||||
parse: aStream [
|
||||
<category: 'private'>
|
||||
octet3a := nil.
|
||||
(self parseOctet3: aStream) ifTrue:[
|
||||
self parseOctets: aStream do: [:each |
|
||||
self octet3a add: (GSMBearerCapOctet3a fromByte: each)].
|
||||
]
|
||||
]
|
||||
|
||||
octet3a [
|
||||
<category: 'accessing'>
|
||||
^ octet3a ifNil: [octet3a := OrderedCollection new]
|
||||
]
|
||||
]
|
||||
|
||||
GSM48DataHolder subclass: GSMBearerCap [
|
||||
<category: 'OsmoGSM'>
|
||||
<comment: '10.5.4.5'>
|
||||
|
@ -511,23 +752,7 @@ GSM48DataHolder subclass: GSMBearerCap [
|
|||
GSMBearerCap class >> validSizes [ ^ 1 to: 13 ]
|
||||
|
||||
"GSM 04.08 Table 10.5.102. Strings depend on other attributes"
|
||||
GSMBearerCap class >> radioChReqReserved [ ^ 2r00 ]
|
||||
GSMBearerCap class >> radioChReqFullRateOnly [ ^ 2r01 ]
|
||||
GSMBearerCap class >> radioChReqDualHalfPref [ ^ 2r10 ]
|
||||
GSMBearerCap class >> radioChReqDualFullPref [ ^ 2r11 ]
|
||||
|
||||
GSMBearerCap class >> radioCodStdGSM [ ^ 2r0 ]
|
||||
GSMBearerCap class >> radioCodStdReserved [ ^ 2r1 ]
|
||||
|
||||
GSMBearerCap class >> transferModeCircuit [ ^ 2r0 ]
|
||||
GSMBearerCap class >> transferModePacket [ ^ 2r1 ]
|
||||
|
||||
GSMBearerCap class >> transferCapSpeech [ ^ 2r000 ]
|
||||
GSMBearerCap class >> transferCapUnrest [ ^ 2r001 ]
|
||||
GSMBearerCap class >> transferCap31khzPlmn [ ^ 2r010 ]
|
||||
GSMBearerCap class >> transferCapFacsimileGroup3 [ ^ 2r011 ]
|
||||
GSMBearerCap class >> transferCapOtherITC [ ^ 2r101 ] "see Octet 5a"
|
||||
GSMBearerCap class >> transferCapReserved [ ^ 2r111 ]
|
||||
]
|
||||
|
||||
GSM48DataHolder subclass: GSMFacility [
|
||||
|
@ -1703,4 +1928,9 @@ Eval [
|
|||
GSM48SSFacility initialize.
|
||||
GSM48SSRegister initialize.
|
||||
GSM48SSReleaseComplete initialize.
|
||||
|
||||
"single parts of the IEs"
|
||||
GSMBearerCapOctet3 initialize.
|
||||
GSMBearerCapOctet3a initialize.
|
||||
GSMBearerCapOctet3b initialize.
|
||||
]
|
||||
|
|
58
Tests.st
58
Tests.st
|
@ -293,6 +293,37 @@ TestCase subclass: GSM48Test [
|
|||
dec := GSM48MSG decode: inp.
|
||||
self assert: dec toMessage asByteArray = inp.
|
||||
]
|
||||
|
||||
testGSMBearerParser [
|
||||
| cap decoded |
|
||||
|
||||
cap := GSMBearerCap parseFrom: #(16r04 16r60 16r02 16r00 16r81).
|
||||
decoded := GSMBearerCapFromMS parse: cap data readStream.
|
||||
|
||||
self deny: decoded octet3 isNil.
|
||||
self assert: decoded octet3a size = 3.
|
||||
|
||||
self assert: decoded octet3 informationTransferCapability = GSMBearerCapOctet3 transferCapSpeech.
|
||||
self assert: decoded octet3 transferMode = GSMBearerCapOctet3 transferModeCircuit.
|
||||
self assert: decoded octet3 codingStandard = GSMBearerCapOctet3 radioCodStdGSM.
|
||||
self assert: decoded octet3 radioChannelRequirement = GSMBearerCapOctet3 radioChReqDualFullPref.
|
||||
|
||||
|
||||
self assert: decoded octet3a first speechVersionIndication = GSMBearerCapOctet3a speechFullRateVersion2.
|
||||
self assert: decoded octet3a first spare = 0.
|
||||
self assert: decoded octet3a first ctm = GSMBearerCapOctet3a ctmTextTelephonyNotSupported.
|
||||
self assert: decoded octet3a first coding = GSMBearerCapOctet3a codingForTransferCapability.
|
||||
|
||||
self assert: decoded octet3a second speechVersionIndication = GSMBearerCapOctet3a speechFullRateVersion1.
|
||||
self assert: decoded octet3a second spare = 0.
|
||||
self assert: decoded octet3a second ctm = GSMBearerCapOctet3a ctmTextTelephonyNotSupported.
|
||||
self assert: decoded octet3a second coding = GSMBearerCapOctet3a codingForTransferCapability.
|
||||
|
||||
self assert: decoded octet3a third speechVersionIndication = GSMBearerCapOctet3a speechHalfRateVersion1.
|
||||
self assert: decoded octet3a third spare = 0.
|
||||
self assert: decoded octet3a third ctm = GSMBearerCapOctet3a ctmTextTelephonyNotSupported.
|
||||
self assert: decoded octet3a third coding = GSMBearerCapOctet3a codingForTransferCapability.
|
||||
]
|
||||
]
|
||||
|
||||
SCCPHandler subclass: TestSCCPHandler [
|
||||
|
@ -671,3 +702,30 @@ TestCase subclass: GSMEncodingTest [
|
|||
self assert: #(49 217 140 86 179 221 26 13) asByteArray decodeUSSD7Bit = wanted.
|
||||
]
|
||||
]
|
||||
|
||||
TestCase subclass: BitfieldTest [
|
||||
<comment: 'I test the Bitfield class'>
|
||||
|
||||
testFromByte [
|
||||
| res |
|
||||
|
||||
res := GSMBitField fromByte: 16r5A.
|
||||
self assert: (res bitsFrom: (1 to: 1)) = 2r0.
|
||||
self assert: (res bitsFrom: (2 to: 2)) = 2r1.
|
||||
self assert: (res bitsFrom: (1 to: 2)) = 2r10.
|
||||
self assert: (res bitsFrom: (3 to: 4)) = 2r10.
|
||||
self assert: (res bitsFrom: (5 to: 8)) = 2r0101.
|
||||
self assert: (res bitsFrom: (5 to: 5)) = 2r1.
|
||||
]
|
||||
|
||||
testByteSet [
|
||||
| res |
|
||||
|
||||
res := GSMBitField fromByte: 16rFF.
|
||||
res atBits: (1 to: 2) put: 2r10.
|
||||
res atBits: (3 to: 4) put: 2r10.
|
||||
res atBits: (5 to: 8) put: 2r0101.
|
||||
|
||||
self assert: res data = 16r5A.
|
||||
]
|
||||
]
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<sunit>OsmoGSM.TestMessages</sunit>
|
||||
<sunit>OsmoGSM.SCCPHandlerTest</sunit>
|
||||
<sunit>OsmoGSM.GSMEncodingTest</sunit>
|
||||
<sunit>OsmoGSM.BitfieldTest</sunit>
|
||||
<filein>Tests.st</filein>
|
||||
</test>
|
||||
|
||||
|
|
Reference in New Issue