rpps 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rpps-0.0.1/LICENSE +19 -0
- rpps-0.0.1/PKG-INFO +60 -0
- rpps-0.0.1/README.md +33 -0
- rpps-0.0.1/pyproject.toml +54 -0
- rpps-0.0.1/setup.cfg +4 -0
- rpps-0.0.1/src/rpps/__init__.py +20 -0
- rpps-0.0.1/src/rpps/coding/__init__.py +14 -0
- rpps-0.0.1/src/rpps/coding/_blk/__init__.py +1 -0
- rpps-0.0.1/src/rpps/coding/_blk/repetition.py +27 -0
- rpps-0.0.1/src/rpps/coding/_cnv/__init__.py +1 -0
- rpps-0.0.1/src/rpps/coding/_cnv/viterbi.py +77 -0
- rpps-0.0.1/src/rpps/coding/block.py +7 -0
- rpps-0.0.1/src/rpps/coding/coding.py +11 -0
- rpps-0.0.1/src/rpps/coding/convolutional.py +5 -0
- rpps-0.0.1/src/rpps/helpers/__init__.py +3 -0
- rpps-0.0.1/src/rpps/helpers/binary.py +43 -0
- rpps-0.0.1/src/rpps/helpers/bitarray.py +48 -0
- rpps-0.0.1/src/rpps/helpers/stream.py +107 -0
- rpps-0.0.1/src/rpps/meta.py +38 -0
- rpps-0.0.1/src/rpps/mod/__init__.py +11 -0
- rpps-0.0.1/src/rpps/mod/_psk/__init__.py +1 -0
- rpps-0.0.1/src/rpps/mod/_psk/qpsk.py +31 -0
- rpps-0.0.1/src/rpps/mod/constellation.py +239 -0
- rpps-0.0.1/src/rpps/mod/identify.py +3 -0
- rpps-0.0.1/src/rpps/mod/modulation.py +61 -0
- rpps-0.0.1/src/rpps/mod/psk.py +4 -0
- rpps-0.0.1/src/rpps/viz/__init__.py +9 -0
- rpps-0.0.1/src/rpps/viz/constellation.py +22 -0
- rpps-0.0.1/src/rpps/viz/generic.py +4 -0
- rpps-0.0.1/src/rpps.egg-info/PKG-INFO +60 -0
- rpps-0.0.1/src/rpps.egg-info/SOURCES.txt +32 -0
- rpps-0.0.1/src/rpps.egg-info/dependency_links.txt +1 -0
- rpps-0.0.1/src/rpps.egg-info/requires.txt +8 -0
- rpps-0.0.1/src/rpps.egg-info/top_level.txt +1 -0
rpps-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2024 Anonoei
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
rpps-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rpps
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: RF Parameter Processor Suite
|
|
5
|
+
Author-email: Anonoei <dev@anonoei.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/anonoei/RPPS
|
|
7
|
+
Project-URL: Documentation, https://anonoei.github.io/RPPS/
|
|
8
|
+
Project-URL: Repository, https://github.com/Anonoei/RPPS.git
|
|
9
|
+
Project-URL: Issues, https://github.com/Anonoei/RPPS/issues
|
|
10
|
+
Project-URL: Source, https://github.com/anonoei/RPPS
|
|
11
|
+
Keywords: anonoei,dsp,sdr
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Requires-Python: >=3.6
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: twine; extra == "dev"
|
|
22
|
+
Requires-Dist: build; extra == "dev"
|
|
23
|
+
Requires-Dist: black; extra == "dev"
|
|
24
|
+
Requires-Dist: bumpver; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: pdoc3; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# RPPS
|
|
29
|
+
RF Parameter Processor Suite
|
|
30
|
+
|
|
31
|
+
RPPS is a generic signal processor/generator library.
|
|
32
|
+
|
|
33
|
+
- [Documentation](https://anonoei.github.io/RPPS/)
|
|
34
|
+
- [PyPI](https://pypi.org/project/rpps/)
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
1. Run `python3 -m pip install rpps`
|
|
38
|
+
2. In your project, `import rpps as rp`
|
|
39
|
+
|
|
40
|
+
## Roadmap
|
|
41
|
+
- [ ] Pre-processing
|
|
42
|
+
- [ ] Modulation
|
|
43
|
+
- [ ] PSK
|
|
44
|
+
- [X] QPSK
|
|
45
|
+
- [ ] QAM
|
|
46
|
+
- [ ] APSK
|
|
47
|
+
- [ ] ASK
|
|
48
|
+
- [ ] FSK
|
|
49
|
+
- [ ] Coding
|
|
50
|
+
- [ ] Block
|
|
51
|
+
- [X] Repetition
|
|
52
|
+
- [ ] LDPC
|
|
53
|
+
- [ ] TPC
|
|
54
|
+
- [ ] Convolutional
|
|
55
|
+
- [ ] Viterbi
|
|
56
|
+
- [ ] De-scramble
|
|
57
|
+
- [ ] v.35
|
|
58
|
+
- [ ] De-frame
|
|
59
|
+
- [ ] HDLC
|
|
60
|
+
- [ ] PPP
|
rpps-0.0.1/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# RPPS
|
|
2
|
+
RF Parameter Processor Suite
|
|
3
|
+
|
|
4
|
+
RPPS is a generic signal processor/generator library.
|
|
5
|
+
|
|
6
|
+
- [Documentation](https://anonoei.github.io/RPPS/)
|
|
7
|
+
- [PyPI](https://pypi.org/project/rpps/)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
1. Run `python3 -m pip install rpps`
|
|
11
|
+
2. In your project, `import rpps as rp`
|
|
12
|
+
|
|
13
|
+
## Roadmap
|
|
14
|
+
- [ ] Pre-processing
|
|
15
|
+
- [ ] Modulation
|
|
16
|
+
- [ ] PSK
|
|
17
|
+
- [X] QPSK
|
|
18
|
+
- [ ] QAM
|
|
19
|
+
- [ ] APSK
|
|
20
|
+
- [ ] ASK
|
|
21
|
+
- [ ] FSK
|
|
22
|
+
- [ ] Coding
|
|
23
|
+
- [ ] Block
|
|
24
|
+
- [X] Repetition
|
|
25
|
+
- [ ] LDPC
|
|
26
|
+
- [ ] TPC
|
|
27
|
+
- [ ] Convolutional
|
|
28
|
+
- [ ] Viterbi
|
|
29
|
+
- [ ] De-scramble
|
|
30
|
+
- [ ] v.35
|
|
31
|
+
- [ ] De-frame
|
|
32
|
+
- [ ] HDLC
|
|
33
|
+
- [ ] PPP
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rpps"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "RF Parameter Processor Suite"
|
|
9
|
+
dependencies = []
|
|
10
|
+
requires-python = ">=3.6"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Anonoei", email="dev@anonoei.com"}
|
|
13
|
+
]
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
keywords = ["anonoei", "dsp", "sdr"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/anonoei/RPPS"
|
|
29
|
+
Documentation = "https://anonoei.github.io/RPPS/"
|
|
30
|
+
Repository = "https://github.com/Anonoei/RPPS.git"
|
|
31
|
+
Issues = "https://github.com/Anonoei/RPPS/issues"
|
|
32
|
+
Source = "https://github.com/anonoei/RPPS"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["twine", "build", "black", "bumpver", "pytest", "pdoc3"]
|
|
36
|
+
|
|
37
|
+
[tool.bumpver]
|
|
38
|
+
current_version = "0.0.1"
|
|
39
|
+
version_pattern = "MAJOR.MINOR.PATCH"
|
|
40
|
+
commit_message = "bump version {old_version} -> {new_version}"
|
|
41
|
+
tag_message = "{new_version}"
|
|
42
|
+
tag_scope = "default"
|
|
43
|
+
pre_commit_hook = ""
|
|
44
|
+
post_commit_hook = ""
|
|
45
|
+
commit = true
|
|
46
|
+
tag = true
|
|
47
|
+
push = false
|
|
48
|
+
|
|
49
|
+
[tool.bumpver.file_patterns]
|
|
50
|
+
"pyproject.toml" = [
|
|
51
|
+
'current_version = "{version}"',
|
|
52
|
+
'version = "{version}"'
|
|
53
|
+
]
|
|
54
|
+
"src/RPPS/__init__.py" = ["{version}"]
|
rpps-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Initialize RPPS
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.0.1"
|
|
6
|
+
__author__ = "Anonoei <dev@anonoei.com>"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from pyboiler.logger import Logger, Level
|
|
10
|
+
|
|
11
|
+
Logger("RPPS", Level.TRACE)
|
|
12
|
+
|
|
13
|
+
# Import helpers/globals
|
|
14
|
+
from .helpers import bitarray, Stream
|
|
15
|
+
from .meta import Meta
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from . import coding
|
|
19
|
+
from . import mod
|
|
20
|
+
from . import viz
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Import helpers/globals
|
|
2
|
+
from ..helpers.bitarray import bitarray
|
|
3
|
+
from ..helpers.stream import Stream
|
|
4
|
+
from ..meta import Meta
|
|
5
|
+
|
|
6
|
+
# -----
|
|
7
|
+
|
|
8
|
+
from .coding import Coding
|
|
9
|
+
|
|
10
|
+
from .block import Block
|
|
11
|
+
from .convolutional import Convolutional
|
|
12
|
+
|
|
13
|
+
from ._blk import *
|
|
14
|
+
from ._cnv import *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .repetition import Repetition
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ..block import Block
|
|
2
|
+
from .. import Stream
|
|
3
|
+
from .. import bitarray
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
class Repetition(Block):
|
|
8
|
+
def __init__(self, num, den):
|
|
9
|
+
super().__init__(num, den, den)
|
|
10
|
+
|
|
11
|
+
def encode(self, data: Stream):
|
|
12
|
+
encoded_data = bitarray()
|
|
13
|
+
for bit in data.bitarray:
|
|
14
|
+
for _ in range(self.length):
|
|
15
|
+
encoded_data.append(bit)
|
|
16
|
+
return encoded_data
|
|
17
|
+
|
|
18
|
+
def decode(self, encoded_data: bitarray):
|
|
19
|
+
decoded_data = bitarray()
|
|
20
|
+
for i in range(0, len(encoded_data), self.length):
|
|
21
|
+
bits = encoded_data[i:i+self.length]
|
|
22
|
+
diff = self.length - sum(bits)
|
|
23
|
+
if self.length/2 > diff:
|
|
24
|
+
decoded_data.append(1)
|
|
25
|
+
else:
|
|
26
|
+
decoded_data.append(0)
|
|
27
|
+
return decoded_data
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .viterbi import Viterbi
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from ..convolutional import Convolutional
|
|
2
|
+
from .. import Stream
|
|
3
|
+
from .. import bitarray
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
class Viterbi(Convolutional):
|
|
8
|
+
def encode(self, data: Stream):
|
|
9
|
+
"""
|
|
10
|
+
SOVA (Soft-Output Viterbi Algorithm) encoding
|
|
11
|
+
:param data: stream of arbitrary data
|
|
12
|
+
:param rate: encoding rate (e.g. 7/8)
|
|
13
|
+
:return: encoded data and trellis diagram
|
|
14
|
+
"""
|
|
15
|
+
# Calculate the number of systematic bits and redundant bits
|
|
16
|
+
N_systematic = int(self._rate * len(data.bitarray))
|
|
17
|
+
N_redundant = len(data.bitarray) - N_systematic
|
|
18
|
+
|
|
19
|
+
# Initialize the trellis diagram
|
|
20
|
+
T = np.zeros((2, 2), dtype=int)
|
|
21
|
+
T[0, :] = [1, 0] # initial state
|
|
22
|
+
|
|
23
|
+
# Encode the data
|
|
24
|
+
encoded_data = bitarray()
|
|
25
|
+
for i in range(len(data.bitarray)):
|
|
26
|
+
if i < N_systematic:
|
|
27
|
+
x = data.bitarray[i]
|
|
28
|
+
else:
|
|
29
|
+
x = np.random.randint(2) # add a random bit for redundancy
|
|
30
|
+
y = T[:, -1].copy() # get the previous state
|
|
31
|
+
T[:, -1] = [x, x ^ y[0]] # update the trellis diagram
|
|
32
|
+
encoded_data.append(int(y[0]))
|
|
33
|
+
encoded_data.append(int(y[1]))
|
|
34
|
+
#encoded_data.extend([int(y[0]), int(y[1])])
|
|
35
|
+
|
|
36
|
+
return encoded_data, T
|
|
37
|
+
|
|
38
|
+
def decode(self, encoded_data):
|
|
39
|
+
"""
|
|
40
|
+
SOVA (Soft-Output Viterbi Algorithm) decoding
|
|
41
|
+
:param encoded_data: encoded data
|
|
42
|
+
:param rate: encoding rate (e.g. 7/8)
|
|
43
|
+
:return: decoded data
|
|
44
|
+
"""
|
|
45
|
+
# Calculate the number of systematic bits and redundant bits
|
|
46
|
+
N_systematic = int(self._rate * len(encoded_data.bitarray))
|
|
47
|
+
N_redundant = len(encoded_data.bitarray) - N_systematic
|
|
48
|
+
|
|
49
|
+
# Initialize the trellis diagram
|
|
50
|
+
T = np.zeros((2, 2), dtype=int)
|
|
51
|
+
T[0, :] = [1, 0] # initial state
|
|
52
|
+
|
|
53
|
+
# Define the transition probabilities (trellis diagram)
|
|
54
|
+
P = np.array([[1/2, 1/2], [1/2, 1/2]]) # for a rate-1/2 convolutional code
|
|
55
|
+
P = np.kron(P, np.ones((2, 2))) # repeat the transition probabilities
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Initialize the likelihoods and state sequences
|
|
59
|
+
L = np.zeros((len(encoded_data.bitarray), 2)) # likelihoods
|
|
60
|
+
S = np.zeros(len(encoded_data.bitarray), dtype=int) # state sequence
|
|
61
|
+
|
|
62
|
+
# Iterate over the codeword
|
|
63
|
+
for i in range(len(encoded_data.bitarray)):
|
|
64
|
+
# Calculate the likelihoods for each possible state
|
|
65
|
+
#L[i, :] = P[T[:, -1].copy(), :] * (encoded_data.bitarray[i] == T[-1, :])
|
|
66
|
+
L[i, :] = P[:, S[i]] * (encoded_data.bitarray[i] == T[0, :])
|
|
67
|
+
|
|
68
|
+
# Update the state sequence and trellis diagram
|
|
69
|
+
S[i] = np.argmax(L[i])
|
|
70
|
+
T[:, -1] = T[S[i], :] # update the trellis diagram
|
|
71
|
+
|
|
72
|
+
# Extract the decoded data from the state sequence
|
|
73
|
+
decoded_data = bitarray()
|
|
74
|
+
for i in range(N_systematic):
|
|
75
|
+
decoded_data.append(S[N_systematic + i])
|
|
76
|
+
|
|
77
|
+
return decoded_data
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Encoding
|
|
2
|
+
|
|
3
|
+
def bytes_to_hex(data: bytes):
|
|
4
|
+
return data.hex()
|
|
5
|
+
|
|
6
|
+
def hex_to_bits(data):
|
|
7
|
+
bits = ""
|
|
8
|
+
for i in range(0, len(data), 2):
|
|
9
|
+
int_data = int(data[i:i+2], 16)
|
|
10
|
+
bit_data = bin(int_data)[2:]
|
|
11
|
+
padding = (len(bit_data) % 8)
|
|
12
|
+
if not padding == 0:
|
|
13
|
+
bit_data = "0" * (8 - padding) + bit_data
|
|
14
|
+
bits += bit_data
|
|
15
|
+
#input(f"0x{data[i:i+2]} = 0i{int_data}, 0b{bit_data}")
|
|
16
|
+
return bits
|
|
17
|
+
|
|
18
|
+
def bytes_to_bits(data):
|
|
19
|
+
return hex_to_bits(bytes_to_hex(data))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Decoding
|
|
23
|
+
|
|
24
|
+
def bits_to_hex(bits):
|
|
25
|
+
data_hex = ""
|
|
26
|
+
for i in range(0, len(bits), 8):
|
|
27
|
+
int_data = int(bits[i:i+8], 2)
|
|
28
|
+
hex_data = hex(int_data)[2:]
|
|
29
|
+
padding = (len(hex_data) % 2)
|
|
30
|
+
if not padding == 0:
|
|
31
|
+
hex_data = "0" * (2 - padding) + hex_data
|
|
32
|
+
data_hex += hex_data
|
|
33
|
+
#input(f"0b{bits[i:i+8]} = 0i{int_data}, 0x{hex_data}")
|
|
34
|
+
return data_hex
|
|
35
|
+
|
|
36
|
+
def hex_to_bytes(data):
|
|
37
|
+
return bytes.fromhex(data)
|
|
38
|
+
|
|
39
|
+
def bits_to_bytes(data):
|
|
40
|
+
return hex_to_bytes(bits_to_hex(data))
|
|
41
|
+
|
|
42
|
+
class Encoding:
|
|
43
|
+
pass
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
class bitarray:
|
|
4
|
+
def __init__(self, arr=None):
|
|
5
|
+
if arr is None:
|
|
6
|
+
arr = np.array([], dtype=np.bool)
|
|
7
|
+
self.arr = arr
|
|
8
|
+
|
|
9
|
+
def __str__(self):
|
|
10
|
+
return str(self.arr)
|
|
11
|
+
|
|
12
|
+
def __len__(self):
|
|
13
|
+
return len(self.arr)
|
|
14
|
+
|
|
15
|
+
def append(self, bit):
|
|
16
|
+
self.arr = np.append(self.arr, bit)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def from_encoding(enc):
|
|
20
|
+
return bitarray(
|
|
21
|
+
np.unpackbits(
|
|
22
|
+
np.array(bytearray(enc.bytes), dtype=np.uint8)
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def to_bytes(self) -> bytes:
|
|
27
|
+
return bytes(np.packbits(self.arr))
|
|
28
|
+
|
|
29
|
+
def bits(self):
|
|
30
|
+
return self.arr
|
|
31
|
+
|
|
32
|
+
def bytes(self):
|
|
33
|
+
return np.packbits(self.arr)
|
|
34
|
+
|
|
35
|
+
def astype(self, *args, **kwargs):
|
|
36
|
+
return self.arr.astype(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
def __iter__(self):
|
|
39
|
+
return self.arr.__iter__()
|
|
40
|
+
|
|
41
|
+
def __getattr__(self, key):
|
|
42
|
+
return self.arr
|
|
43
|
+
|
|
44
|
+
def __getitem__(self, key):
|
|
45
|
+
return self.arr[key]
|
|
46
|
+
|
|
47
|
+
def __setitem__(self, key, val):
|
|
48
|
+
self.arr[key] = val
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from .bitarray import bitarray
|
|
2
|
+
|
|
3
|
+
class Stream:
|
|
4
|
+
__slots__ = ("cache", "length")
|
|
5
|
+
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.cache = {
|
|
8
|
+
"bin": None,
|
|
9
|
+
"hex": None,
|
|
10
|
+
"bytes": None,
|
|
11
|
+
"int": None,
|
|
12
|
+
"bitarray": None,
|
|
13
|
+
}
|
|
14
|
+
self.length = None
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return f"0x{self.hex}"
|
|
18
|
+
|
|
19
|
+
def __repr__(self):
|
|
20
|
+
return f"<Encoded data>"
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def bin(self):
|
|
24
|
+
if self.cache["bin"] is None:
|
|
25
|
+
#self.cache["bin"] = Encoding._pad(bin(self.cache["int"])[2:], 8)
|
|
26
|
+
self.cache["bin"] = "".join([item for item in self.bitarray.astype(str)])
|
|
27
|
+
return self.cache["bin"]
|
|
28
|
+
|
|
29
|
+
@bin.setter
|
|
30
|
+
def bin(self, data):
|
|
31
|
+
self.cache["bin"] = data
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def hex(self):
|
|
35
|
+
if self.cache["hex"] is None:
|
|
36
|
+
self.cache["hex"] = Stream._pad(hex(self.cache["int"])[2:], 2)
|
|
37
|
+
return self.cache["hex"]
|
|
38
|
+
|
|
39
|
+
@hex.setter
|
|
40
|
+
def hex(self, data):
|
|
41
|
+
self.cache["hex"] = data
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def bytes(self):
|
|
45
|
+
if self.cache["bytes"] is None:
|
|
46
|
+
if self.cache["hex"] is None:
|
|
47
|
+
self.hex
|
|
48
|
+
self.cache["bytes"] = bytes.fromhex(self.cache["hex"])
|
|
49
|
+
return self.cache["bytes"]
|
|
50
|
+
|
|
51
|
+
@bytes.setter
|
|
52
|
+
def bytes(self, data):
|
|
53
|
+
self.cache["bytes"] = data
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def int(self):
|
|
57
|
+
return self.cache["int"]
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def bitarray(self):
|
|
61
|
+
if self.cache["bitarray"] is None:
|
|
62
|
+
self.cache["bitarray"] = bitarray.from_encoding(self)
|
|
63
|
+
return self.cache["bitarray"]
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def from_bin(binary, length = None):
|
|
67
|
+
enc = Stream()
|
|
68
|
+
enc.cache["bin"] = Stream._pad(binary, 8)
|
|
69
|
+
enc.cache["int"] = int(binary, 2)
|
|
70
|
+
enc.length = length
|
|
71
|
+
return enc
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_hex(hexadecimal, length = None):
|
|
75
|
+
enc = Stream()
|
|
76
|
+
enc.cache["hex"] = Stream._pad(hexadecimal, 2)
|
|
77
|
+
enc.cache["int"] = int(hexadecimal, 16)
|
|
78
|
+
enc.length = length
|
|
79
|
+
return enc
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def from_bytes(byte, length = None):
|
|
83
|
+
enc = Stream()
|
|
84
|
+
enc.cache["bytes"] = byte
|
|
85
|
+
enc.cache["int"] = int.from_bytes(byte)
|
|
86
|
+
enc.length = length
|
|
87
|
+
return enc
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def from_int(int, length = None):
|
|
91
|
+
enc = Stream()
|
|
92
|
+
enc.cache["int"] = int
|
|
93
|
+
enc.length = length
|
|
94
|
+
return enc
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _pad(data, mod, fill = "0"):
|
|
98
|
+
padding = len(data) % mod
|
|
99
|
+
if not padding == 0:
|
|
100
|
+
data = fill * (mod - padding) + data
|
|
101
|
+
return data
|
|
102
|
+
|
|
103
|
+
def slice(self, field, size):
|
|
104
|
+
idx = 0
|
|
105
|
+
while idx < len(self.cache[field]):
|
|
106
|
+
yield self.cache[field][idx:idx+size]
|
|
107
|
+
idx += size
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
class Meta:
|
|
4
|
+
mod = ""
|
|
5
|
+
fmt = ""
|
|
6
|
+
fec = ""
|
|
7
|
+
|
|
8
|
+
def serialize(self, data):
|
|
9
|
+
path = pathlib.Path(f"data/{self.to_name()}")
|
|
10
|
+
data.tofile(path) # Save to file
|
|
11
|
+
return path
|
|
12
|
+
|
|
13
|
+
def to_name(self):
|
|
14
|
+
fields = [
|
|
15
|
+
self.mod.lower(),
|
|
16
|
+
self.fmt,
|
|
17
|
+
"rpps",
|
|
18
|
+
"iq",
|
|
19
|
+
]
|
|
20
|
+
return ".".join(fields)
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def from_name(name: str, meta = None):
|
|
24
|
+
if meta is None:
|
|
25
|
+
meta = Meta()
|
|
26
|
+
fields = name.split(".")
|
|
27
|
+
meta.mod = fields[0].upper()
|
|
28
|
+
meta.fmt = fields[1]
|
|
29
|
+
return meta
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def from_file(path, meta = None):
|
|
33
|
+
import numpy as np
|
|
34
|
+
if isinstance(path, str):
|
|
35
|
+
path = pathlib.Path(path)
|
|
36
|
+
meta = Meta.from_name(path.name, meta)
|
|
37
|
+
symbols = np.fromfile(path, getattr(np, meta.fmt))
|
|
38
|
+
return meta, symbols
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Import helpers/globals
|
|
2
|
+
from ..helpers.bitarray import bitarray
|
|
3
|
+
from ..helpers.stream import Stream
|
|
4
|
+
from ..meta import Meta
|
|
5
|
+
|
|
6
|
+
# -----
|
|
7
|
+
|
|
8
|
+
from .modulation import Modulation
|
|
9
|
+
from .constellation import Mapping, Maps, Points, Constellation
|
|
10
|
+
|
|
11
|
+
from ._psk import *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .qpsk import QPSK
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from ..psk import PSK
|
|
2
|
+
|
|
3
|
+
from ..constellation import Mapping, Maps, Points
|
|
4
|
+
|
|
5
|
+
class QPSK(PSK):
|
|
6
|
+
points = Points(
|
|
7
|
+
[
|
|
8
|
+
.7 - .7j, -.7 - .7j,
|
|
9
|
+
.7 + .7j, -.7 + .7j
|
|
10
|
+
]
|
|
11
|
+
)
|
|
12
|
+
maps = Maps(
|
|
13
|
+
[
|
|
14
|
+
Mapping([
|
|
15
|
+
0, 1,
|
|
16
|
+
2, 3,
|
|
17
|
+
]),
|
|
18
|
+
Mapping([
|
|
19
|
+
0, 2,
|
|
20
|
+
1, 3,
|
|
21
|
+
]),
|
|
22
|
+
Mapping([
|
|
23
|
+
3, 1,
|
|
24
|
+
2, 0,
|
|
25
|
+
]),
|
|
26
|
+
Mapping([
|
|
27
|
+
3, 2,
|
|
28
|
+
1, 0,
|
|
29
|
+
]),
|
|
30
|
+
]
|
|
31
|
+
)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from pyboiler.logger import Logger, Level
|
|
5
|
+
|
|
6
|
+
from . import Stream
|
|
7
|
+
from . import Meta
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Mapping:
|
|
11
|
+
__slots__ = ("arr")
|
|
12
|
+
|
|
13
|
+
def __init__(self, map=None):
|
|
14
|
+
if isinstance(map, int):
|
|
15
|
+
map = np.array([0] * map)
|
|
16
|
+
elif not isinstance(map, np.ndarray):
|
|
17
|
+
map = np.array(map)
|
|
18
|
+
elif map is None:
|
|
19
|
+
map = np.array([])
|
|
20
|
+
self.arr = map
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def new(map):
|
|
24
|
+
return Mapping(map)
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def empty(length: int):
|
|
28
|
+
return Mapping(length)
|
|
29
|
+
|
|
30
|
+
def __len__(self):
|
|
31
|
+
return len(self.arr)
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return str(self.arr)
|
|
35
|
+
|
|
36
|
+
def __getitem__(self, item):
|
|
37
|
+
return self.arr[item]
|
|
38
|
+
|
|
39
|
+
def __setitem__(self, item, val):
|
|
40
|
+
self.arr[item] = val
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Maps:
|
|
44
|
+
__slots__ = ("maps")
|
|
45
|
+
|
|
46
|
+
def __init__(self, maps):
|
|
47
|
+
self.maps = maps
|
|
48
|
+
|
|
49
|
+
def __len__(self):
|
|
50
|
+
return len(self.maps)
|
|
51
|
+
|
|
52
|
+
def __getitem__(self, item):
|
|
53
|
+
return self.maps[item]
|
|
54
|
+
|
|
55
|
+
def __setitem__(self, item, val):
|
|
56
|
+
self.maps[item] = val
|
|
57
|
+
|
|
58
|
+
class Points:
|
|
59
|
+
__slots__ = ("arr")
|
|
60
|
+
|
|
61
|
+
def __init__(self, points):
|
|
62
|
+
self.arr = np.array(points)
|
|
63
|
+
|
|
64
|
+
def __len__(self):
|
|
65
|
+
return len(self.arr)
|
|
66
|
+
|
|
67
|
+
def __str__(self):
|
|
68
|
+
return str(self.arr)
|
|
69
|
+
|
|
70
|
+
def __getitem__(self, item):
|
|
71
|
+
return self.arr[item]
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, item, val):
|
|
74
|
+
self.arr[item] = val
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Constellation:
|
|
78
|
+
__slots__ = ("log", "_points", "_mapping", "_bps")
|
|
79
|
+
|
|
80
|
+
def __init__(self, points: Points, mapping = None, log=Logger().Child("Modulation")):
|
|
81
|
+
self.log = log.Child("Constellation", Level.WARN)
|
|
82
|
+
if not isinstance(points, Points):
|
|
83
|
+
points = Points(points)
|
|
84
|
+
self._points = points
|
|
85
|
+
|
|
86
|
+
if mapping is None:
|
|
87
|
+
mapping = Mapping()
|
|
88
|
+
self._mapping = mapping
|
|
89
|
+
|
|
90
|
+
self._bps = int(math.log2(len(self.points))) # Bits per symbol
|
|
91
|
+
|
|
92
|
+
def __str__(self) -> str:
|
|
93
|
+
return f"Points: {self._points}, Map: {self._mapping}"
|
|
94
|
+
|
|
95
|
+
def __repr__(self) -> str:
|
|
96
|
+
return f"<Constellation: {self._bps}>"
|
|
97
|
+
|
|
98
|
+
def __len__(self) -> int:
|
|
99
|
+
return len(self._points)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def points(self):
|
|
103
|
+
return self._points
|
|
104
|
+
|
|
105
|
+
@points.setter
|
|
106
|
+
def points(self, points):
|
|
107
|
+
self._points = np.array(points)
|
|
108
|
+
self._bps = len(self.points) // 2
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def mapping(self):
|
|
112
|
+
return self._mapping
|
|
113
|
+
|
|
114
|
+
@mapping.setter
|
|
115
|
+
def mapping(self, map):
|
|
116
|
+
self._mapping = np.array(map)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def bits_per_symbol(self):
|
|
120
|
+
return self._bps
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def modulate(self, data: Stream, meta: Meta, noise: bool = True):
|
|
124
|
+
indexes = self.index(data, meta)
|
|
125
|
+
points = self.map(indexes, meta)
|
|
126
|
+
symbols = self.to_symbols(points, meta, noise=noise)
|
|
127
|
+
return symbols
|
|
128
|
+
|
|
129
|
+
def demodulate(self, symbols, meta: Meta):
|
|
130
|
+
points = self.from_symbols(symbols, meta)
|
|
131
|
+
indexes = self.unmap(points, meta)
|
|
132
|
+
data = self.unindex(indexes, meta)
|
|
133
|
+
return data
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
##############################
|
|
137
|
+
# Modulate
|
|
138
|
+
##############################
|
|
139
|
+
|
|
140
|
+
def index(self, data: Stream, meta):
|
|
141
|
+
self.log.trace(f"Data is {data}")
|
|
142
|
+
self.log.trace(f"Data bitarray is {data.bitarray}")
|
|
143
|
+
|
|
144
|
+
padding = len(data.bin) % self._bps
|
|
145
|
+
if not padding == 0:
|
|
146
|
+
self.log.trace(f"Padding by {padding}")
|
|
147
|
+
for _ in range(0, (self._bps - padding)):
|
|
148
|
+
data.bitarray.append(0)
|
|
149
|
+
num_symbols = len(data.bitarray) // self._bps
|
|
150
|
+
|
|
151
|
+
self.log.trace(f"Data requires {num_symbols} indexes to encode")
|
|
152
|
+
|
|
153
|
+
indexes = np.split(data.bitarray.arr, num_symbols)
|
|
154
|
+
indexes = [int(f"{data[0]}{data[1]}", 2) for data in indexes]
|
|
155
|
+
#for i in range(0, len(data.bin), self._bps):
|
|
156
|
+
# bit_int = int(data.bin[i:i+self._bps], 2)
|
|
157
|
+
# indexes.append(int(bit_int))
|
|
158
|
+
|
|
159
|
+
self.log.trace(f"Indexes are {indexes}")
|
|
160
|
+
return indexes
|
|
161
|
+
|
|
162
|
+
def map(self, indexes, meta):
|
|
163
|
+
self.log.trace(f"Using mapping: {self.mapping}")
|
|
164
|
+
points = []
|
|
165
|
+
for idx in indexes:
|
|
166
|
+
points.append(
|
|
167
|
+
int(
|
|
168
|
+
np.where(self.mapping.arr == idx)[0][0]
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
self.log.trace(f"Points are {points}")
|
|
172
|
+
return points
|
|
173
|
+
|
|
174
|
+
def to_symbols(self, points, meta, noise: bool = True):
|
|
175
|
+
points = np.array(points)
|
|
176
|
+
symbols = self.points[points]
|
|
177
|
+
self.log.trace(f"Symbols are:\n{symbols}")
|
|
178
|
+
# Add noise
|
|
179
|
+
if noise:
|
|
180
|
+
n = (np.random.randn(len(symbols)) + 1j*np.random.randn(len(symbols)))/np.sqrt(2) # AWGN with unity power
|
|
181
|
+
symbols = symbols + n * np.sqrt(0.01) # noise power of 0.01
|
|
182
|
+
|
|
183
|
+
symbols = symbols.astype(np.complex64)
|
|
184
|
+
meta.fmt = type(symbols[0]).__name__
|
|
185
|
+
self.log.trace(f"Symbols are: {symbols}")
|
|
186
|
+
return symbols
|
|
187
|
+
|
|
188
|
+
##############################
|
|
189
|
+
# Demodulate
|
|
190
|
+
##############################
|
|
191
|
+
|
|
192
|
+
def from_symbols(self, symbols, meta, clean: bool=True):
|
|
193
|
+
#self.log.trace(f"Symbols are:\n{symbols}")
|
|
194
|
+
points = []
|
|
195
|
+
if clean:
|
|
196
|
+
syms = []
|
|
197
|
+
for symbol in symbols: # Clean up the symbols
|
|
198
|
+
diff = np.abs(self.points - symbol)
|
|
199
|
+
index = np.argmin(diff)
|
|
200
|
+
closest = self.points[index]
|
|
201
|
+
syms.append(closest)
|
|
202
|
+
syms = np.array(syms)
|
|
203
|
+
self.log.trace(f"Symbols are:\n{syms}")
|
|
204
|
+
|
|
205
|
+
for symbol in syms:
|
|
206
|
+
points.append(int(np.where(self.points == symbol)[0][0]))
|
|
207
|
+
else:
|
|
208
|
+
self.log.trace(f"Symbols are:\n{symbols}")
|
|
209
|
+
for symbol in symbols: # Clean up the symbols
|
|
210
|
+
diff = np.abs(self.points - symbol)
|
|
211
|
+
index = np.argmin(diff)
|
|
212
|
+
points.append(int(index))
|
|
213
|
+
self.log.trace(f"Points are {points}")
|
|
214
|
+
return points
|
|
215
|
+
|
|
216
|
+
def unmap(self, points, meta):
|
|
217
|
+
self.log.trace(f"Using mapping: {self.mapping}")
|
|
218
|
+
indexes = []
|
|
219
|
+
for pnt in points:
|
|
220
|
+
indexes.append(int(self.mapping[pnt]))
|
|
221
|
+
self.log.trace(f"Indexes are {indexes}")
|
|
222
|
+
return indexes
|
|
223
|
+
|
|
224
|
+
def unindex(self, indexes, meta):
|
|
225
|
+
bits = ""
|
|
226
|
+
for i in range(0, len(indexes), 1):
|
|
227
|
+
cur_bits = bin(indexes[i])[2:]
|
|
228
|
+
if len(cur_bits) == 1:
|
|
229
|
+
cur_bits = "0" + cur_bits
|
|
230
|
+
bits += cur_bits
|
|
231
|
+
|
|
232
|
+
data = Stream.from_bin(bits)
|
|
233
|
+
|
|
234
|
+
self.log.trace(f"Data bits are {data.bin}")
|
|
235
|
+
|
|
236
|
+
self.log.trace(f"Data hex is {data.hex}")
|
|
237
|
+
self.log.trace(f"Data is {data.bytes}")
|
|
238
|
+
|
|
239
|
+
return data
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pyboiler.logger import Logger, Level
|
|
4
|
+
|
|
5
|
+
from . import Meta
|
|
6
|
+
from . import Stream
|
|
7
|
+
|
|
8
|
+
from .constellation import Maps, Points
|
|
9
|
+
from .constellation import Constellation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Modulation:
|
|
13
|
+
__slots__ = ("log", "constellation", "map")
|
|
14
|
+
points = Points([])
|
|
15
|
+
maps = Maps([])
|
|
16
|
+
|
|
17
|
+
def __init__(self, map_idx: int = None): # type: ignore
|
|
18
|
+
self.log = Logger().Child("Modulation").Child(type(self).__name__)
|
|
19
|
+
self.map = map_idx
|
|
20
|
+
if self.map is not None:
|
|
21
|
+
self.constellation = Constellation(
|
|
22
|
+
type(self).points,
|
|
23
|
+
type(self).maps[self.map],
|
|
24
|
+
log=self.log)
|
|
25
|
+
else:
|
|
26
|
+
self.constellation = Constellation(
|
|
27
|
+
type(self).points,
|
|
28
|
+
log=self.log)
|
|
29
|
+
|
|
30
|
+
def demodulate(self, symbols: np.ndarray, meta=Meta()):
|
|
31
|
+
if self.map is None:
|
|
32
|
+
raise Exception("A map must be provided to demodulate!")
|
|
33
|
+
|
|
34
|
+
data = self.constellation.demodulate(symbols, meta)
|
|
35
|
+
|
|
36
|
+
return data, meta
|
|
37
|
+
|
|
38
|
+
def modulate(self, data: Stream, meta=Meta()):
|
|
39
|
+
if self.map is None:
|
|
40
|
+
raise Exception("A map must be provided to modulate!")
|
|
41
|
+
|
|
42
|
+
symbols = self.constellation.modulate(data, meta)
|
|
43
|
+
|
|
44
|
+
meta.mod = type(self).__name__
|
|
45
|
+
return symbols, meta
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ASK(Modulation):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FSK(Modulation):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class APSK(Modulation):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class QAM(Modulation):
|
|
61
|
+
pass
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
|
|
4
|
+
from . import Meta
|
|
5
|
+
|
|
6
|
+
def draw(data, meta: Meta):
|
|
7
|
+
fig = plt.figure()
|
|
8
|
+
ax = fig.add_subplot()
|
|
9
|
+
plt.plot(np.real(data), np.imag(data), '.')
|
|
10
|
+
|
|
11
|
+
plt.grid(True)
|
|
12
|
+
plt.title(f"{meta.mod} encoded data")
|
|
13
|
+
plt.xlim(1.2, -1.2)
|
|
14
|
+
plt.ylim(1.2, -1.2)
|
|
15
|
+
ax.set_aspect('equal', adjustable='box')
|
|
16
|
+
|
|
17
|
+
# Draw unit circle
|
|
18
|
+
angle = np.linspace( 0 , 2 * np.pi , 150 )
|
|
19
|
+
radius = 1
|
|
20
|
+
x = radius * np.cos( angle )
|
|
21
|
+
y = radius * np.sin( angle )
|
|
22
|
+
plt.plot(x, y)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: rpps
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: RF Parameter Processor Suite
|
|
5
|
+
Author-email: Anonoei <dev@anonoei.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/anonoei/RPPS
|
|
7
|
+
Project-URL: Documentation, https://anonoei.github.io/RPPS/
|
|
8
|
+
Project-URL: Repository, https://github.com/Anonoei/RPPS.git
|
|
9
|
+
Project-URL: Issues, https://github.com/Anonoei/RPPS/issues
|
|
10
|
+
Project-URL: Source, https://github.com/anonoei/RPPS
|
|
11
|
+
Keywords: anonoei,dsp,sdr
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Requires-Python: >=3.6
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: twine; extra == "dev"
|
|
22
|
+
Requires-Dist: build; extra == "dev"
|
|
23
|
+
Requires-Dist: black; extra == "dev"
|
|
24
|
+
Requires-Dist: bumpver; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: pdoc3; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# RPPS
|
|
29
|
+
RF Parameter Processor Suite
|
|
30
|
+
|
|
31
|
+
RPPS is a generic signal processor/generator library.
|
|
32
|
+
|
|
33
|
+
- [Documentation](https://anonoei.github.io/RPPS/)
|
|
34
|
+
- [PyPI](https://pypi.org/project/rpps/)
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
1. Run `python3 -m pip install rpps`
|
|
38
|
+
2. In your project, `import rpps as rp`
|
|
39
|
+
|
|
40
|
+
## Roadmap
|
|
41
|
+
- [ ] Pre-processing
|
|
42
|
+
- [ ] Modulation
|
|
43
|
+
- [ ] PSK
|
|
44
|
+
- [X] QPSK
|
|
45
|
+
- [ ] QAM
|
|
46
|
+
- [ ] APSK
|
|
47
|
+
- [ ] ASK
|
|
48
|
+
- [ ] FSK
|
|
49
|
+
- [ ] Coding
|
|
50
|
+
- [ ] Block
|
|
51
|
+
- [X] Repetition
|
|
52
|
+
- [ ] LDPC
|
|
53
|
+
- [ ] TPC
|
|
54
|
+
- [ ] Convolutional
|
|
55
|
+
- [ ] Viterbi
|
|
56
|
+
- [ ] De-scramble
|
|
57
|
+
- [ ] v.35
|
|
58
|
+
- [ ] De-frame
|
|
59
|
+
- [ ] HDLC
|
|
60
|
+
- [ ] PPP
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/rpps/__init__.py
|
|
5
|
+
src/rpps/meta.py
|
|
6
|
+
src/rpps.egg-info/PKG-INFO
|
|
7
|
+
src/rpps.egg-info/SOURCES.txt
|
|
8
|
+
src/rpps.egg-info/dependency_links.txt
|
|
9
|
+
src/rpps.egg-info/requires.txt
|
|
10
|
+
src/rpps.egg-info/top_level.txt
|
|
11
|
+
src/rpps/coding/__init__.py
|
|
12
|
+
src/rpps/coding/block.py
|
|
13
|
+
src/rpps/coding/coding.py
|
|
14
|
+
src/rpps/coding/convolutional.py
|
|
15
|
+
src/rpps/coding/_blk/__init__.py
|
|
16
|
+
src/rpps/coding/_blk/repetition.py
|
|
17
|
+
src/rpps/coding/_cnv/__init__.py
|
|
18
|
+
src/rpps/coding/_cnv/viterbi.py
|
|
19
|
+
src/rpps/helpers/__init__.py
|
|
20
|
+
src/rpps/helpers/binary.py
|
|
21
|
+
src/rpps/helpers/bitarray.py
|
|
22
|
+
src/rpps/helpers/stream.py
|
|
23
|
+
src/rpps/mod/__init__.py
|
|
24
|
+
src/rpps/mod/constellation.py
|
|
25
|
+
src/rpps/mod/identify.py
|
|
26
|
+
src/rpps/mod/modulation.py
|
|
27
|
+
src/rpps/mod/psk.py
|
|
28
|
+
src/rpps/mod/_psk/__init__.py
|
|
29
|
+
src/rpps/mod/_psk/qpsk.py
|
|
30
|
+
src/rpps/viz/__init__.py
|
|
31
|
+
src/rpps/viz/constellation.py
|
|
32
|
+
src/rpps/viz/generic.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rpps
|