basic.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import defs
  2. import numpy as np
  3. import math
  4. import misc
  5. from scipy.spatial import cKDTree
  6. def _make_gray(n):
  7. if n <= 0:
  8. return []
  9. arr = ['0', '1']
  10. i = 2
  11. while True:
  12. if i >= 1 << n:
  13. break
  14. for j in range(i - 1, -1, -1):
  15. arr.append(arr[j])
  16. for j in range(i):
  17. arr[j] = "0" + arr[j]
  18. for j in range(i, 2 * i):
  19. arr[j] = "1" + arr[j]
  20. i = i << 1
  21. return list(map(lambda x: int(x, 2), arr))
  22. def _gen_mary_alphabet(size, gray=True, polar=True):
  23. alphabet = np.zeros((size, 2))
  24. N = math.ceil(math.sqrt(size))
  25. # if sqrt(size) != size^2 (not a perfect square),
  26. # skip defines how many corners to cut off.
  27. skip = 0
  28. if N ** 2 > size:
  29. skip = int(math.sqrt((N ** 2 - size) // 4))
  30. step = 2 / (N - 1)
  31. skipped = 0
  32. for x in range(N):
  33. for y in range(N):
  34. i = x * N + y - skipped
  35. if i >= size:
  36. break
  37. # Reverse y every odd column
  38. if x % 2 == 0 and N < 4:
  39. y = N - y - 1
  40. if skip > 0:
  41. if (x < skip or x + 1 > N - skip) and \
  42. (y < skip or y + 1 > N - skip):
  43. skipped += 1
  44. continue
  45. # Exception for 3-ary alphabet, skip centre point
  46. if size == 8 and x == 1 and y == 1:
  47. skipped += 1
  48. continue
  49. alphabet[i, :] = [step * x - 1, step * y - 1]
  50. if gray:
  51. shape = alphabet.shape
  52. d1 = 4 if N > 4 else 2 ** N // 4
  53. g1 = np.array([0, 1, 3, 2])
  54. g2 = g1[:d1]
  55. hypershape = (d1, 4, 2)
  56. if N > 4:
  57. hypercube = alphabet.reshape(hypershape + (N-4, ))
  58. hypercube = hypercube[:, g1, :, :][g2, :, :, :]
  59. else:
  60. hypercube = alphabet.reshape(hypershape)
  61. hypercube = hypercube[:, g1, :][g2, :, :]
  62. alphabet = hypercube.reshape(shape)
  63. if polar:
  64. alphabet = misc.rect2polar(alphabet)
  65. return alphabet
  66. class BypassChannel(defs.Channel):
  67. def forward(self, values):
  68. return values
  69. class AWGNChannel(defs.Channel):
  70. def __init__(self, noise_level, **kwargs):
  71. """
  72. :param noise_level: in dB
  73. """
  74. super().__init__(**kwargs)
  75. self.noise = 10 ** (noise_level / 10)
  76. def forward(self, values):
  77. a = np.random.normal(0, 1, values.shape[0]) * self.noise
  78. p = np.random.normal(0, 1, values.shape[0]) * self.noise
  79. f = np.zeros(values.shape[0])
  80. noise_mat = np.c_[a, p, f]
  81. return values + noise_mat
  82. class BPSKMod(defs.Modulator):
  83. def __init__(self, carrier_f, **kwargs):
  84. super().__init__(2, **kwargs)
  85. self.f = carrier_f
  86. def forward(self, binary: np.ndarray):
  87. a = np.ones(binary.shape[0])
  88. p = np.zeros(binary.shape[0])
  89. p[binary == True] = np.pi
  90. f = np.zeros(binary.shape[0]) + self.f
  91. return np.c_[a, p, f]
  92. class BPSKDemod(defs.Demodulator):
  93. def __init__(self, carrier_f, bandwidth, **kwargs):
  94. """
  95. :param carrier_f: Carrier frequency
  96. :param bandwidth: demodulator bandwidth
  97. """
  98. super().__init__(2, **kwargs)
  99. self.upper_f = carrier_f + bandwidth / 2
  100. self.lower_f = carrier_f - bandwidth / 2
  101. def forward(self, values):
  102. # TODO: Channel noise simulator for frequency component?
  103. # for now we only care about amplitude and phase
  104. ap = np.delete(values, 2, 1)
  105. ap = misc.polar2rect(ap)
  106. result = np.ones(values.shape[0], dtype=bool)
  107. result[ap[:, 0] > 0] = False
  108. return result
  109. class MaryMod(defs.Modulator):
  110. def __init__(self, N, carrier_f, gray=True):
  111. if N < 2:
  112. raise ValueError("M-ary modulator N value has to be larger than 1")
  113. super().__init__(2 ** N)
  114. self.f = carrier_f
  115. self.alphabet = _gen_mary_alphabet(self.alphabet_size, gray)
  116. self.mult_mat = np.array([2 ** i for i in range(self.N)])
  117. def forward(self, binary):
  118. if binary.shape[0] % self.N > 0:
  119. to_add = self.N - binary.shape[0] % self.N
  120. binary = np.concatenate((binary, np.zeros(to_add, bool)))
  121. reshaped = binary.reshape((binary.shape[0] // self.N, self.N))
  122. indices = np.matmul(reshaped, self.mult_mat)
  123. values = self.alphabet[indices, :]
  124. a = values[:, 0]
  125. p = values[:, 1]
  126. f = np.zeros(reshaped.shape[0]) + self.f
  127. return np.c_[a, p, f] #, indices
  128. class MaryDemod(defs.Demodulator):
  129. def __init__(self, N, carrier_f, gray=True):
  130. if N < 2:
  131. raise ValueError("M-ary modulator N value has to be larger than 1")
  132. super().__init__(2 ** N)
  133. self.f = carrier_f
  134. self.N = N
  135. self.alphabet = _gen_mary_alphabet(self.alphabet_size, gray=gray, polar=False)
  136. self.ktree = cKDTree(self.alphabet)
  137. def forward(self, binary):
  138. binary = binary[:, :2] # ignore frequency
  139. rbin = misc.polar2rect(binary)
  140. indices = self.ktree.query(rbin)[1]
  141. # Converting indices to bite array
  142. # FIXME: unpackbits requires 8bit inputs, thus largest demodulation is 256-QAM
  143. values = np.unpackbits(np.array([indices], dtype=np.uint8).T, bitorder='little', axis=1)
  144. return values[:, :self.N].reshape((-1,)).astype(bool) #, indices