|
@@ -4,72 +4,9 @@ import math
|
|
|
import misc
|
|
import misc
|
|
|
from scipy.spatial import cKDTree
|
|
from scipy.spatial import cKDTree
|
|
|
from os import path
|
|
from os import path
|
|
|
|
|
+import tensorflow as tf
|
|
|
|
|
|
|
|
ALPHABET_DIR = "./alphabets"
|
|
ALPHABET_DIR = "./alphabets"
|
|
|
-# def _make_gray(n):
|
|
|
|
|
-# if n <= 0:
|
|
|
|
|
-# return []
|
|
|
|
|
-# arr = ['0', '1']
|
|
|
|
|
-# i = 2
|
|
|
|
|
-# while True:
|
|
|
|
|
-# if i >= 1 << n:
|
|
|
|
|
-# break
|
|
|
|
|
-# for j in range(i - 1, -1, -1):
|
|
|
|
|
-# arr.append(arr[j])
|
|
|
|
|
-# for j in range(i):
|
|
|
|
|
-# arr[j] = "0" + arr[j]
|
|
|
|
|
-# for j in range(i, 2 * i):
|
|
|
|
|
-# arr[j] = "1" + arr[j]
|
|
|
|
|
-# i = i << 1
|
|
|
|
|
-# return list(map(lambda x: int(x, 2), arr))
|
|
|
|
|
-#
|
|
|
|
|
-#
|
|
|
|
|
-# def _gen_mary_alphabet(size, gray=True, polar=True):
|
|
|
|
|
-# alphabet = np.zeros((size, 2))
|
|
|
|
|
-# N = math.ceil(math.sqrt(size))
|
|
|
|
|
-#
|
|
|
|
|
-# # if sqrt(size) != size^2 (not a perfect square),
|
|
|
|
|
-# # skip defines how many corners to cut off.
|
|
|
|
|
-# skip = 0
|
|
|
|
|
-# if N ** 2 > size:
|
|
|
|
|
-# skip = int(math.sqrt((N ** 2 - size) // 4))
|
|
|
|
|
-#
|
|
|
|
|
-# step = 2 / (N - 1)
|
|
|
|
|
-# skipped = 0
|
|
|
|
|
-# for x in range(N):
|
|
|
|
|
-# for y in range(N):
|
|
|
|
|
-# i = x * N + y - skipped
|
|
|
|
|
-# if i >= size:
|
|
|
|
|
-# break
|
|
|
|
|
-# # Reverse y every odd column
|
|
|
|
|
-# if x % 2 == 0 and N < 4:
|
|
|
|
|
-# y = N - y - 1
|
|
|
|
|
-# if skip > 0:
|
|
|
|
|
-# if (x < skip or x + 1 > N - skip) and \
|
|
|
|
|
-# (y < skip or y + 1 > N - skip):
|
|
|
|
|
-# skipped += 1
|
|
|
|
|
-# continue
|
|
|
|
|
-# # Exception for 3-ary alphabet, skip centre point
|
|
|
|
|
-# if size == 8 and x == 1 and y == 1:
|
|
|
|
|
-# skipped += 1
|
|
|
|
|
-# continue
|
|
|
|
|
-# alphabet[i, :] = [step * x - 1, step * y - 1]
|
|
|
|
|
-# if gray:
|
|
|
|
|
-# shape = alphabet.shape
|
|
|
|
|
-# d1 = 4 if N > 4 else 2 ** N // 4
|
|
|
|
|
-# g1 = np.array([0, 1, 3, 2])
|
|
|
|
|
-# g2 = g1[:d1]
|
|
|
|
|
-# hypershape = (d1, 4, 2)
|
|
|
|
|
-# if N > 4:
|
|
|
|
|
-# hypercube = alphabet.reshape(hypershape + (N-4, ))
|
|
|
|
|
-# hypercube = hypercube[:, g1, :, :][g2, :, :, :]
|
|
|
|
|
-# else:
|
|
|
|
|
-# hypercube = alphabet.reshape(hypershape)
|
|
|
|
|
-# hypercube = hypercube[:, g1, :][g2, :, :]
|
|
|
|
|
-# alphabet = hypercube.reshape(shape)
|
|
|
|
|
-# if polar:
|
|
|
|
|
-# alphabet = misc.rect2polar(alphabet)
|
|
|
|
|
-# return alphabet
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_alphabet(name, polar=True):
|
|
def load_alphabet(name, polar=True):
|
|
@@ -102,12 +39,12 @@ def load_alphabet(name, polar=True):
|
|
|
else:
|
|
else:
|
|
|
raise ValueError()
|
|
raise ValueError()
|
|
|
if 'd' in header:
|
|
if 'd' in header:
|
|
|
- p = y*math.pi/180
|
|
|
|
|
|
|
+ p = y * math.pi / 180
|
|
|
y = math.sin(p) * x
|
|
y = math.sin(p) * x
|
|
|
x = math.cos(p) * x
|
|
x = math.cos(p) * x
|
|
|
data.append((x, y))
|
|
data.append((x, y))
|
|
|
except ValueError:
|
|
except ValueError:
|
|
|
- raise ValueError(f"Alphabet {name} line {i+1}: '{row}' has invalid values")
|
|
|
|
|
|
|
+ raise ValueError(f"Alphabet {name} line {i + 1}: '{row}' has invalid values")
|
|
|
|
|
|
|
|
data2 = [None] * len(data)
|
|
data2 = [None] * len(data)
|
|
|
for i, d in enumerate(data):
|
|
for i, d in enumerate(data):
|
|
@@ -118,6 +55,30 @@ def load_alphabet(name, polar=True):
|
|
|
return arr
|
|
return arr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+class RFSignal(defs.Signal):
|
|
|
|
|
+ def __init__(self, array: np.ndarray):
|
|
|
|
|
+ self.amplitude = array[:, 0]
|
|
|
|
|
+ self.phase = array[:, 1]
|
|
|
|
|
+ self.frequency = array[:, 2]
|
|
|
|
|
+ self.symbols = array.shape[0]
|
|
|
|
|
+
|
|
|
|
|
+ @property
|
|
|
|
|
+ def rect(self) -> np.ndarray:
|
|
|
|
|
+ return misc.polar2rect(np.c_[self.amplitude, self.phase])
|
|
|
|
|
+
|
|
|
|
|
+ def set_rect_xy(self, x_mat: np.ndarray, y_mat: np.ndarray):
|
|
|
|
|
+ self.set_rect(np.c_[x_mat, y_mat])
|
|
|
|
|
+
|
|
|
|
|
+ def set_rect(self, mat: np.ndarray):
|
|
|
|
|
+ polar = misc.rect2polar(mat)
|
|
|
|
|
+ self.amplitude = polar[:, 0]
|
|
|
|
|
+ self.phase = polar[:, 1]
|
|
|
|
|
+
|
|
|
|
|
+ @property
|
|
|
|
|
+ def apf(self):
|
|
|
|
|
+ return np.c_[self.amplitude, self.phase, self.frequency]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
class BypassChannel(defs.Channel):
|
|
class BypassChannel(defs.Channel):
|
|
|
def forward(self, values):
|
|
def forward(self, values):
|
|
|
return values
|
|
return values
|
|
@@ -131,12 +92,17 @@ class AWGNChannel(defs.Channel):
|
|
|
super().__init__(**kwargs)
|
|
super().__init__(**kwargs)
|
|
|
self.noise = 10 ** (noise_level / 10)
|
|
self.noise = 10 ** (noise_level / 10)
|
|
|
|
|
|
|
|
- def forward(self, values):
|
|
|
|
|
- a = np.random.normal(0, 1, values.shape[0]) * self.noise
|
|
|
|
|
- p = np.random.normal(0, 1, values.shape[0]) * self.noise
|
|
|
|
|
- f = np.zeros(values.shape[0])
|
|
|
|
|
- noise_mat = np.c_[a, p, f]
|
|
|
|
|
- return values + noise_mat
|
|
|
|
|
|
|
+ def forward(self, values: RFSignal) -> RFSignal:
|
|
|
|
|
+ values.set_rect_xy(
|
|
|
|
|
+ values.rect_x + np.random.normal(0, 1, values.symbols) * self.noise,
|
|
|
|
|
+ values.rect_y + np.random.normal(0, 1, values.symbols) * self.noise,
|
|
|
|
|
+ )
|
|
|
|
|
+ return values
|
|
|
|
|
+
|
|
|
|
|
+ def forward_tensor(self, tensor: tf.Tensor) -> tf.Tensor:
|
|
|
|
|
+ noise = tf.random.normal([2], mean=0.0, stddev=1.0, dtype=tf.dtypes.float32, seed=None, name=None)
|
|
|
|
|
+ tensor += noise * self.noise
|
|
|
|
|
+ return tensor
|
|
|
|
|
|
|
|
|
|
|
|
|
class BPSKMod(defs.Modulator):
|
|
class BPSKMod(defs.Modulator):
|
|
@@ -145,12 +111,12 @@ class BPSKMod(defs.Modulator):
|
|
|
super().__init__(2, **kwargs)
|
|
super().__init__(2, **kwargs)
|
|
|
self.f = carrier_f
|
|
self.f = carrier_f
|
|
|
|
|
|
|
|
- def forward(self, binary: np.ndarray):
|
|
|
|
|
|
|
+ def forward(self, binary):
|
|
|
a = np.ones(binary.shape[0])
|
|
a = np.ones(binary.shape[0])
|
|
|
p = np.zeros(binary.shape[0])
|
|
p = np.zeros(binary.shape[0])
|
|
|
p[binary == True] = np.pi
|
|
p[binary == True] = np.pi
|
|
|
f = np.zeros(binary.shape[0]) + self.f
|
|
f = np.zeros(binary.shape[0]) + self.f
|
|
|
- return np.c_[a, p, f]
|
|
|
|
|
|
|
+ return RFSignal(np.c_[a, p, f])
|
|
|
|
|
|
|
|
|
|
|
|
|
class BPSKDemod(defs.Demodulator):
|
|
class BPSKDemod(defs.Demodulator):
|
|
@@ -167,11 +133,11 @@ class BPSKDemod(defs.Demodulator):
|
|
|
def forward(self, values):
|
|
def forward(self, values):
|
|
|
# TODO: Channel noise simulator for frequency component?
|
|
# TODO: Channel noise simulator for frequency component?
|
|
|
# for now we only care about amplitude and phase
|
|
# for now we only care about amplitude and phase
|
|
|
- ap = np.delete(values, 2, 1)
|
|
|
|
|
- ap = misc.polar2rect(ap)
|
|
|
|
|
|
|
+ # ap = np.delete(values, 2, 1)
|
|
|
|
|
+ # ap = misc.polar2rect(ap)
|
|
|
|
|
|
|
|
- result = np.ones(values.shape[0], dtype=bool)
|
|
|
|
|
- result[ap[:, 0] > 0] = False
|
|
|
|
|
|
|
+ result = np.ones(values.symbols, dtype=bool)
|
|
|
|
|
+ result[values.rect_x[:, 0] > 0] = False
|
|
|
return result
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@@ -196,7 +162,7 @@ class AlphabetMod(defs.Modulator):
|
|
|
a = values[:, 0]
|
|
a = values[:, 0]
|
|
|
p = values[:, 1]
|
|
p = values[:, 1]
|
|
|
f = np.zeros(reshaped.shape[0]) + self.f
|
|
f = np.zeros(reshaped.shape[0]) + self.f
|
|
|
- return np.c_[a, p, f] #, indices
|
|
|
|
|
|
|
+ return RFSignal(np.c_[a, p, f]) # , indices
|
|
|
|
|
|
|
|
|
|
|
|
|
class AlphabetDemod(defs.Demodulator):
|
|
class AlphabetDemod(defs.Demodulator):
|
|
@@ -211,11 +177,11 @@ class AlphabetDemod(defs.Demodulator):
|
|
|
self.ktree = cKDTree(self.alphabet)
|
|
self.ktree = cKDTree(self.alphabet)
|
|
|
|
|
|
|
|
def forward(self, binary):
|
|
def forward(self, binary):
|
|
|
- binary = binary[:, :2] # ignore frequency
|
|
|
|
|
- rbin = misc.polar2rect(binary)
|
|
|
|
|
- indices = self.ktree.query(rbin)[1]
|
|
|
|
|
|
|
+ # binary = binary[:, :2] # ignore frequency
|
|
|
|
|
+ # rbin = misc.polar2rect(binary)
|
|
|
|
|
+ indices = self.ktree.query(binary.rect)[1]
|
|
|
|
|
|
|
|
# Converting indices to bite array
|
|
# Converting indices to bite array
|
|
|
# FIXME: unpackbits requires 8bit inputs, thus largest demodulation is 256-QAM
|
|
# FIXME: unpackbits requires 8bit inputs, thus largest demodulation is 256-QAM
|
|
|
values = np.unpackbits(np.array([indices], dtype=np.uint8).T, bitorder='little', axis=1)
|
|
values = np.unpackbits(np.array([indices], dtype=np.uint8).T, bitorder='little', axis=1)
|
|
|
- return values[:, :self.N].reshape((-1,)).astype(bool) #, indices
|
|
|
|
|
|
|
+ return values[:, :self.N].reshape((-1,)).astype(bool) # , indices
|