Move lib out of ./simulations

This commit is contained in:
Eric-Teunis de Boone 2022-06-27 16:23:19 +02:00
parent 84b3a6dca9
commit 6763bbc64c
14 changed files with 10 additions and 0 deletions

2
lib/location/__init__.py Normal file
View file

@ -0,0 +1,2 @@
from .location import *
from .antenna import *

68
lib/location/antenna.py Normal file
View file

@ -0,0 +1,68 @@
from functools import partial
import copy
from typing import Union
from .location import Location
from ..signals import Signal
class Antenna(Location):
"""
A location able to interact with a signal.
Either emitting or receiving.
Optionally uses digitizer to transform the signal
when receiving.
"""
def __init__(self, x):
super().__init__(x)
def __repr__(self):
return "Antenna({}, {})".format(repr(self.x), repr(self.x))
def emit(self, signal: Union[Signal, callable]) -> Union[Signal, callable]:
"""
Emit signal from this antenna's location.
Note that this merely sets a default argument.
"""
if not isinstance(signal, Signal):
return partial(signal, x_0=self.x)
else:
new_signal = copy.copy(signal)
new_signal.x_0 = self.x
return new_signal
def recv(self, signal: Union[Signal, callable]) -> Union[Signal, callable]:
"""
Trace signal as a function of time at this antenna's
location.
Note that this merely sets a default argument.
"""
if not isinstance(signal, Signal):
return partial(signal, x_f=self.x)
else:
new_signal = copy.copy(signal)
new_signal.x_f = self.x
return new_signal
receive = recv
class Receiver(Antenna):
"""
An antenna which main purpose is to trace a signal over time.
Optionally applies a transformation to the traced signal.
"""
def __repr__(self):
return "Receiver({})".format(repr(self.x))
class Emitter(Antenna):
"""
An antenna which main purpose is to emit a signal.
"""
def __repr__(self):
return "Emitter({})".format(repr(self.x))

52
lib/location/example.py Executable file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
# fix package-internal importing
if __name__ == "__main__" and __package__ is None:
import sys
sys.path.append("../../")
__package__ = "lib.location"
from . import location as loc
from ..location.antenna import Receiver, Emitter
# 2D showcase
source = Emitter([1,1])
antennae = [
Receiver([2,3]),
Receiver([10,10]),
Receiver([-2,-3]),
Receiver([-10,0]),
]
fig, ax = plt.subplots()
loc.plot_geometry(ax, [source], antennae)
fig.show()
# 3D showcase
source = Emitter([1,1,1])
antennae = [
Receiver([2,3,0]),
Receiver([10,10,-5]),
Receiver([-2,-3,9]),
Receiver([-10,0,-5]),
]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_title("Geometry of Emitter(s) and Antennae")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
ax.plot([source.x[0]], *source.x[1:], '*', label="Emitter")
for j, ant in enumerate(antennae):
ax.plot([ant.x[0]], *ant.x[1:], '+', label="Antenna {}".format(j))
ax.legend()
plt.show()

110
lib/location/location.py Normal file
View file

@ -0,0 +1,110 @@
import numpy as np
from functools import partial
def distance(x1, x2):
"""
Calculate the Euclidean distance between two locations x1 and x2
"""
return np.sqrt( np.sum( (x1 - x2)**2, axis=-1) )
def plot_geometry(ax, emitters=[], antennae=[], unit='m'):
"""
Show the geometry of emitters and antennae in a square plot.
Parameters
----------
ax - matplotlib.Axes
The axis object to plot the geometry on.
emitters - list of Locations
The Emitter objects to plot.
antennae - list of Locations
The Receiver objects to plot.
Returns
-------
ax - matplotlib.Axes
The axis object containing the plotted geometry.
annots - dict of list of matplotlib.text.Annotation
The dictionary is split up into a list of annotations
belonging to the emitters, and one for the antennae.
"""
ax.grid()
ax.set_title("Geometry of Emitter(s) and Antennae")
ax.set_ylabel("y ({})".format(unit))
ax.set_xlabel("x ({})".format(unit))
ax.margins(0.3)
ax.set_aspect('equal', 'datalim') # make it a square plot
annots = {}
for k, locs in {"E": emitters, "A": antennae}.items():
if k == "E":
marker='*'
prefix = k
elif k == "A":
marker="o"
prefix = k
# create the list of annotations
if k not in annots:
annots[k] = []
# plot marker and create annotation
for j, loc in enumerate(locs):
label = "{}{}".format(prefix, j)
ax.plot(*loc.x, marker=marker, label=label)
annots[k].append(ax.annotate(label, loc.x))
return ax, annots
class Location:
"""
A location is a point designated by a spatial coordinate x.
Locations are wrappers around a Numpy N-dimensional array.
"""
def __init__(self, x):
self.x = np.asarray(x)
def __repr__(self):
return "Location({})".format(repr(self.x))
def __getitem__(self, key):
return self.x[key]
def __setitem__(self, key, val):
self.x[key] = val
def distance(self, other):
if isinstance(other, Location):
other = other.x
return distance(self.x, other)
# math
def __add__(self, other):
if isinstance(other, Location):
other = other.x
return self.__class__(self.x + other)
def __sub__(self, other):
if isinstance(other, Location):
other = other.x
return self.__class__(self.x - other)
def __mul__(self, other):
return self.__class__(self.x * other)
def __eq__(self, other):
if isinstance(other, Location):
other = other.x
return np.all(self.x == other)
# math alias functions
__radd__ = __add__
__rsub__ = __sub__
__rmul__ = __mul__