280 lines
8.3 KiB
Python
280 lines
8.3 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
###############################################################################
|
|
# Copyright (C) 2017 Mike Zingman N4IRR
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
###############################################################################
|
|
|
|
'''
|
|
'''
|
|
from binascii import b2a_hex as ahex
|
|
from bitarray import bitarray
|
|
from bitstring import BitArray
|
|
from bitstring import BitString
|
|
|
|
__author__ = 'Mike Zingman, N4IRR and Cortney T. Buffington, N0MJS'
|
|
__copyright__ = 'Copyright (c) 2017 Mike Zingman N4IRR'
|
|
__credits__ = 'Cortney T. Buffington, N0MJS; Colin Durbridge, G4EML, Steve Zingman, N4IRS; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT'
|
|
__license__ = 'GNU GPLv3'
|
|
__maintainer__ = 'Cort Buffington, N0MJS'
|
|
__email__ = 'n0mjs@me.com'
|
|
__status__ = 'pre-alpha'
|
|
__version__ = '20170508'
|
|
|
|
##
|
|
# DMR AMBE interleave schedule
|
|
##
|
|
rW = [
|
|
0, 1, 0, 1, 0, 1,
|
|
0, 1, 0, 1, 0, 1,
|
|
0, 1, 0, 1, 0, 1,
|
|
0, 1, 0, 1, 0, 2,
|
|
0, 2, 0, 2, 0, 2,
|
|
0, 2, 0, 2, 0, 2
|
|
]
|
|
|
|
rX = [
|
|
23, 10, 22, 9, 21, 8,
|
|
20, 7, 19, 6, 18, 5,
|
|
17, 4, 16, 3, 15, 2,
|
|
14, 1, 13, 0, 12, 10,
|
|
11, 9, 10, 8, 9, 7,
|
|
8, 6, 7, 5, 6, 4
|
|
]
|
|
|
|
rY = [
|
|
0, 2, 0, 2, 0, 2,
|
|
0, 2, 0, 3, 0, 3,
|
|
1, 3, 1, 3, 1, 3,
|
|
1, 3, 1, 3, 1, 3,
|
|
1, 3, 1, 3, 1, 3,
|
|
1, 3, 1, 3, 1, 3
|
|
]
|
|
|
|
rZ = [
|
|
5, 3, 4, 2, 3, 1,
|
|
2, 0, 1, 13, 0, 12,
|
|
22, 11, 21, 10, 20, 9,
|
|
19, 8, 18, 7, 17, 6,
|
|
16, 5, 15, 4, 14, 3,
|
|
13, 2, 12, 1, 11, 0
|
|
]
|
|
|
|
|
|
# This function calculates [23,12] Golay codewords.
|
|
# The format of the returned longint is [checkbits(11),data(12)].
|
|
def golay2312(cw):
|
|
POLY = 0xAE3 #/* or use the other polynomial, 0xC75 */
|
|
cw = cw & 0xfff # Strip off check bits and only use data
|
|
c = cw #/* save original codeword */
|
|
for i in range(1,13): #/* examine each data bit */
|
|
if (cw & 1): #/* test data bit */
|
|
cw = cw ^ POLY #/* XOR polynomial */
|
|
cw = cw >> 1 #/* shift intermediate result */
|
|
return((cw << 12) | c) #/* assemble codeword */
|
|
|
|
# This function checks the overall parity of codeword cw.
|
|
# If parity is even, 0 is returned, else 1.
|
|
def parity(cw):
|
|
#/* XOR the bytes of the codeword */
|
|
p = cw & 0xff
|
|
p = p ^ ((cw >> 8) & 0xff)
|
|
p = p ^ ((cw >> 16) & 0xff)
|
|
|
|
#/* XOR the halves of the intermediate result */
|
|
p = p ^ (p >> 4)
|
|
p = p ^ (p >> 2)
|
|
p = p ^ (p >> 1)
|
|
|
|
#/* return the parity result */
|
|
return(p & 1)
|
|
|
|
# Demodulate ambe frame (C1)
|
|
# Frame is an array [4][24]
|
|
def demodulateAmbe3600x2450(ambe_fr):
|
|
pr = [0] * 115
|
|
foo = 0
|
|
|
|
# create pseudo-random modulator
|
|
for i in range(23, 11, -1):
|
|
foo = foo << 1
|
|
foo = foo | ambe_fr[0][i]
|
|
pr[0] = (16 * foo)
|
|
for i in range(1, 24):
|
|
pr[i] = (173 * pr[i - 1]) + 13849 - (65536 * (((173 * pr[i - 1]) + 13849) / 65536))
|
|
for i in range(1, 24):
|
|
pr[i] = pr[i] / 32768
|
|
|
|
# demodulate ambe_fr with pr
|
|
k = 1
|
|
for j in range(22, -1, -1):
|
|
ambe_fr[1][j] = ((ambe_fr[1][j]) ^ pr[k])
|
|
k = k + 1
|
|
return ambe_fr # Pass it back since there is no pass by reference
|
|
|
|
def eccAmbe3600x2450Data(ambe_fr):
|
|
ambe = bitarray()
|
|
|
|
# just copy C0
|
|
for j in range(23, 11, -1):
|
|
ambe.append(ambe_fr[0][j])
|
|
|
|
# # ecc and copy C1
|
|
# gin = 0
|
|
# for j in range(23):
|
|
# gin = (gin << 1) | ambe_fr[1][j]
|
|
#
|
|
# gout = BitArray(hex(golay2312(gin)))
|
|
# for j in range(22, 10, -1):
|
|
# ambe[bitIndex] = gout[j]
|
|
# bitIndex += 1
|
|
for j in range(22, 10, -1):
|
|
ambe.append(ambe_fr[1][j])
|
|
|
|
# just copy C2
|
|
for j in range(10, -1, -1):
|
|
ambe.append(ambe_fr[2][j])
|
|
|
|
# just copy C3
|
|
for j in range(13, -1, -1):
|
|
ambe.append(ambe_fr[3][j])
|
|
|
|
return ambe
|
|
|
|
# Convert a 49 bit raw AMBE frame into a deinterleaved structure (ready for decode by AMBE3000)
|
|
def convert49BitAmbeTo72BitFrames( ambe_d ):
|
|
index = 0
|
|
ambe_fr = [[None for x in range(24)] for y in range(4)]
|
|
|
|
#Place bits into the 4x24 frames. [bit0...bit23]
|
|
#fr0: [P e10 e9 e8 e7 e6 e5 e4 e3 e2 e1 e0 11 10 9 8 7 6 5 4 3 2 1 0]
|
|
#fr1: [e10 e9 e8 e7 e6 e5 e4 e3 e2 e1 e0 23 22 21 20 19 18 17 16 15 14 13 12 xx]
|
|
#fr2: [34 33 32 31 30 29 28 27 26 25 24 x x x x x x x x x x x x x]
|
|
#fr3: [48 47 46 45 44 43 42 41 40 39 38 37 36 35 x x x x x x x x x x]
|
|
|
|
# ecc and copy C0: 12bits + 11ecc + 1 parity
|
|
# First get the 12 bits that actually exist
|
|
# Then calculate the golay codeword
|
|
# And then add the parity bit to get the final 24 bit pattern
|
|
|
|
tmp = 0
|
|
for i in range(11, -1, -1): #grab the 12 MSB
|
|
tmp = (tmp << 1) | ambe_d[i]
|
|
tmp = golay2312(tmp) #Generate the 23 bit result
|
|
parityBit = parity(tmp)
|
|
tmp = tmp | (parityBit << 23) #And create a full 24 bit value
|
|
for i in range(23, -1, -1):
|
|
ambe_fr[0][i] = (tmp & 1)
|
|
tmp = tmp >> 1
|
|
|
|
# C1: 12 bits + 11ecc (no parity)
|
|
tmp = 0
|
|
for i in range(23,11, -1) : #grab the next 12 bits
|
|
tmp = (tmp << 1) | ambe_d[i]
|
|
tmp = golay2312(tmp) #Generate the 23 bit result
|
|
for j in range(22, -1, -1):
|
|
ambe_fr[1][j] = (tmp & 1)
|
|
tmp = tmp >> 1;
|
|
|
|
#C2: 11 bits (no ecc)
|
|
for j in range(10, -1, -1):
|
|
ambe_fr[2][j] = ambe_d[34 - j]
|
|
|
|
#C3: 14 bits (no ecc)
|
|
for j in range(13, -1, -1):
|
|
ambe_fr[3][j] = ambe_d[48 - j];
|
|
|
|
return ambe_fr
|
|
|
|
def interleave(ambe_fr):
|
|
bitIndex = 0
|
|
w = 0
|
|
x = 0
|
|
y = 0
|
|
z = 0
|
|
data = bytearray(9)
|
|
for i in range(36):
|
|
bit1 = ambe_fr[rW[w]][rX[x]] # bit 1
|
|
bit0 = ambe_fr[rY[y]][rZ[z]] # bit 0
|
|
|
|
|
|
data[bitIndex / 8] = ((data[bitIndex / 8] << 1) & 0xfe) | (1 if (bit1 == 1) else 0)
|
|
bitIndex += 1
|
|
|
|
data[bitIndex / 8] = ((data[bitIndex / 8] << 1) & 0xfe) | (1 if (bit0 == 1) else 0)
|
|
bitIndex += 1
|
|
|
|
w += 1
|
|
x += 1
|
|
y += 1
|
|
z += 1
|
|
return data
|
|
|
|
def deinterleave(data):
|
|
|
|
ambe_fr = [[None for x in range(24)] for y in range(4)]
|
|
|
|
bitIndex = 0
|
|
w = 0
|
|
x = 0
|
|
y = 0
|
|
z = 0
|
|
for i in range(36):
|
|
bit1 = 1 if data[bitIndex] else 0
|
|
bitIndex += 1
|
|
|
|
bit0 = 1 if data[bitIndex] else 0
|
|
bitIndex += 1
|
|
|
|
ambe_fr[rW[w]][rX[x]] = bit1; # bit 1
|
|
ambe_fr[rY[y]][rZ[z]] = bit0; # bit 0
|
|
|
|
w += 1
|
|
x += 1
|
|
y += 1
|
|
z += 1
|
|
|
|
return ambe_fr
|
|
|
|
def convert72BitTo49BitAMBE( ambe72 ):
|
|
ambe_fr = deinterleave(ambe72) # take 72 bit ambe and lay it out in C0-C3
|
|
ambe_fr = demodulateAmbe3600x2450(ambe_fr) # demodulate C1
|
|
ambe49 = eccAmbe3600x2450Data(ambe_fr) # pick out the 49 bits of raw ambe
|
|
return ambe49
|
|
|
|
def convert49BitTo72BitAMBE( ambe49 ):
|
|
ambe_fr = convert49BitAmbeTo72BitFrames(ambe49) # take raw ambe 49 + ecc and place it into C0-C3
|
|
ambe_fr = demodulateAmbe3600x2450(ambe_fr) # demodulate C1
|
|
ambe72 = interleave(ambe_fr); # Re-interleave it, returning 72 bits
|
|
return ambe72
|
|
|
|
def testit():
|
|
ambe72 = BitArray('0xACAA40200044408080') #silence frame
|
|
print('ambe72=',ambe72)
|
|
|
|
ambe49 = convert72BitTo49BitAMBE(ambe72)
|
|
print('ambe49=',ahex(ambe49))
|
|
|
|
ambe72 = convert49BitTo72BitAMBE(ambe49)
|
|
print('ambe72=',ahex(ambe72))
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Used to execute the module directly to run built-in tests
|
|
#------------------------------------------------------------------------------
|
|
|
|
if __name__ == '__main__':
|
|
testit()
|