basic.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import defs
  2. import numpy as np
  3. import math
  4. import misc
  5. from scipy.spatial import cKDTree
  6. from os import path
  7. ALPHABET_DIR = "./alphabets"
  8. # def _make_gray(n):
  9. # if n <= 0:
  10. # return []
  11. # arr = ['0', '1']
  12. # i = 2
  13. # while True:
  14. # if i >= 1 << n:
  15. # break
  16. # for j in range(i - 1, -1, -1):
  17. # arr.append(arr[j])
  18. # for j in range(i):
  19. # arr[j] = "0" + arr[j]
  20. # for j in range(i, 2 * i):
  21. # arr[j] = "1" + arr[j]
  22. # i = i << 1
  23. # return list(map(lambda x: int(x, 2), arr))
  24. #
  25. #
  26. # def _gen_mary_alphabet(size, gray=True, polar=True):
  27. # alphabet = np.zeros((size, 2))
  28. # N = math.ceil(math.sqrt(size))
  29. #
  30. # # if sqrt(size) != size^2 (not a perfect square),
  31. # # skip defines how many corners to cut off.
  32. # skip = 0
  33. # if N ** 2 > size:
  34. # skip = int(math.sqrt((N ** 2 - size) // 4))
  35. #
  36. # step = 2 / (N - 1)
  37. # skipped = 0
  38. # for x in range(N):
  39. # for y in range(N):
  40. # i = x * N + y - skipped
  41. # if i >= size:
  42. # break
  43. # # Reverse y every odd column
  44. # if x % 2 == 0 and N < 4:
  45. # y = N - y - 1
  46. # if skip > 0:
  47. # if (x < skip or x + 1 > N - skip) and \
  48. # (y < skip or y + 1 > N - skip):
  49. # skipped += 1
  50. # continue
  51. # # Exception for 3-ary alphabet, skip centre point
  52. # if size == 8 and x == 1 and y == 1:
  53. # skipped += 1
  54. # continue
  55. # alphabet[i, :] = [step * x - 1, step * y - 1]
  56. # if gray:
  57. # shape = alphabet.shape
  58. # d1 = 4 if N > 4 else 2 ** N // 4
  59. # g1 = np.array([0, 1, 3, 2])
  60. # g2 = g1[:d1]
  61. # hypershape = (d1, 4, 2)
  62. # if N > 4:
  63. # hypercube = alphabet.reshape(hypershape + (N-4, ))
  64. # hypercube = hypercube[:, g1, :, :][g2, :, :, :]
  65. # else:
  66. # hypercube = alphabet.reshape(hypershape)
  67. # hypercube = hypercube[:, g1, :][g2, :, :]
  68. # alphabet = hypercube.reshape(shape)
  69. # if polar:
  70. # alphabet = misc.rect2polar(alphabet)
  71. # return alphabet
  72. def load_alphabet(name, polar=True):
  73. apath = path.join(ALPHABET_DIR, name + '.a')
  74. if not path.exists(apath):
  75. raise ValueError(f"Alphabet '{name}' does not exist")
  76. data = []
  77. indexes = []
  78. with open(apath, 'r') as f:
  79. header = f.readline().lower()
  80. if 'd' not in header and 'r' not in header:
  81. raise ValueError(f"Alphabet {name} header does not specify valid format")
  82. for i, row in enumerate(f.readlines()):
  83. row = row.strip()
  84. if len(row) == 0:
  85. continue
  86. cols = row.split(',')
  87. try:
  88. if len(cols) == 3:
  89. indexes.append(int(cols[0], 2))
  90. x = float(cols[1])
  91. y = float(cols[2])
  92. elif len(cols) == 2:
  93. indexes.append(i)
  94. x = float(cols[0])
  95. y = float(cols[1])
  96. else:
  97. raise ValueError()
  98. if 'd' in header:
  99. p = y*math.pi/180
  100. y = math.sin(p) * x
  101. x = math.cos(p) * x
  102. data.append((x, y))
  103. except ValueError:
  104. raise ValueError(f"Alphabet {name} line {i+1}: '{row}' has invalid values")
  105. data2 = [None] * len(data)
  106. for i, d in enumerate(data):
  107. data2[indexes[i]] = d
  108. arr = np.array(data2, dtype=float)
  109. if polar:
  110. arr = misc.rect2polar(arr)
  111. return arr
  112. class BypassChannel(defs.Channel):
  113. def forward(self, values):
  114. return values
  115. class AWGNChannel(defs.Channel):
  116. def __init__(self, noise_level, **kwargs):
  117. """
  118. :param noise_level: in dB
  119. """
  120. super().__init__(**kwargs)
  121. self.noise = 10 ** (noise_level / 10)
  122. def forward(self, values):
  123. a = np.random.normal(0, 1, values.shape[0]) * self.noise
  124. p = np.random.normal(0, 1, values.shape[0]) * self.noise
  125. f = np.zeros(values.shape[0])
  126. noise_mat = np.c_[a, p, f]
  127. return values + noise_mat
  128. class BPSKMod(defs.Modulator):
  129. def __init__(self, carrier_f, **kwargs):
  130. super().__init__(2, **kwargs)
  131. self.f = carrier_f
  132. def forward(self, binary: np.ndarray):
  133. a = np.ones(binary.shape[0])
  134. p = np.zeros(binary.shape[0])
  135. p[binary == True] = np.pi
  136. f = np.zeros(binary.shape[0]) + self.f
  137. return np.c_[a, p, f]
  138. class BPSKDemod(defs.Demodulator):
  139. def __init__(self, carrier_f, bandwidth, **kwargs):
  140. """
  141. :param carrier_f: Carrier frequency
  142. :param bandwidth: demodulator bandwidth
  143. """
  144. super().__init__(2, **kwargs)
  145. self.upper_f = carrier_f + bandwidth / 2
  146. self.lower_f = carrier_f - bandwidth / 2
  147. def forward(self, values):
  148. # TODO: Channel noise simulator for frequency component?
  149. # for now we only care about amplitude and phase
  150. ap = np.delete(values, 2, 1)
  151. ap = misc.polar2rect(ap)
  152. result = np.ones(values.shape[0], dtype=bool)
  153. result[ap[:, 0] > 0] = False
  154. return result
  155. class AlphabetMod(defs.Modulator):
  156. def __init__(self, modulation, carrier_f):
  157. # if N < 2:
  158. # raise ValueError("M-ary modulator N value has to be larger than 1")
  159. self.alphabet = load_alphabet(modulation)
  160. super().__init__(self.alphabet.shape[0])
  161. self.f = carrier_f
  162. self.mult_mat = np.array([2 ** i for i in range(self.N)])
  163. def forward(self, binary):
  164. if binary.shape[0] % self.N > 0:
  165. to_add = self.N - binary.shape[0] % self.N
  166. binary = np.concatenate((binary, np.zeros(to_add, bool)))
  167. reshaped = binary.reshape((binary.shape[0] // self.N, self.N))
  168. indices = np.matmul(reshaped, self.mult_mat)
  169. values = self.alphabet[indices, :]
  170. a = values[:, 0]
  171. p = values[:, 1]
  172. f = np.zeros(reshaped.shape[0]) + self.f
  173. return np.c_[a, p, f] #, indices
  174. class AlphabetDemod(defs.Demodulator):
  175. def __init__(self, modulation, carrier_f):
  176. # if N < 2:
  177. # raise ValueError("M-ary modulator N value has to be larger than 1")
  178. self.alphabet = load_alphabet(modulation, polar=False)
  179. super().__init__(self.alphabet.shape[0])
  180. self.f = carrier_f
  181. # self.alphabet = _gen_mary_alphabet(self.alphabet_size, gray=gray, polar=False)
  182. self.ktree = cKDTree(self.alphabet)
  183. def forward(self, binary):
  184. binary = binary[:, :2] # ignore frequency
  185. rbin = misc.polar2rect(binary)
  186. indices = self.ktree.query(rbin)[1]
  187. # Converting indices to bite array
  188. # FIXME: unpackbits requires 8bit inputs, thus largest demodulation is 256-QAM
  189. values = np.unpackbits(np.array([indices], dtype=np.uint8).T, bitorder='little', axis=1)
  190. return values[:, :self.N].reshape((-1,)).astype(bool) #, indices