Compare commits

...

8 Commits

Author SHA1 Message Date
  John-Mark Gurney cc450a3488 add algorithm version of the lookup table. 1 month ago
  John-Mark Gurney 7aeaed651f add code that does binary to three level + clock recovery... 1 month ago
  John-Mark Gurney 455c410b89 rename so name is consistent... 1 month ago
  John-Mark Gurney e7e742150d add a working framework for doing 8b10b encoding/decoding.. 5 months ago
  John-Mark Gurney 88b5957bf5 comment which pins for programming to save me looking at the pdf.. 5 months ago
  John-Mark Gurney 900b0f2ae5 use family independent main.h to support l1.. 5 months ago
  John-Mark Gurney 39b1c229dc Merge commit 'ae0f979e1aa9ee734bd6b4b8d74bea8e7e112142' as 'blinkled/encdec8b10b' 5 months ago
  John-Mark Gurney ae0f979e1a Squashed 'blinkled/encdec8b10b/' content from commit 28ed9d5 5 months ago
18 changed files with 2717 additions and 1 deletions
Split View
  1. +2
    -0
      LORA-IRR.md
  2. +24
    -0
      blinkled/README.md
  3. +6
    -0
      blinkled/encdec8b10b/.gitignore
  4. +9
    -0
      blinkled/encdec8b10b/.travis.yml
  5. +21
    -0
      blinkled/encdec8b10b/LICENSE
  6. +26
    -0
      blinkled/encdec8b10b/Makefile
  7. +88
    -0
      blinkled/encdec8b10b/README.md
  8. +1
    -0
      blinkled/encdec8b10b/encdec8b10b/__init__.py
  9. +2219
    -0
      blinkled/encdec8b10b/encdec8b10b/core.py
  10. +16
    -0
      blinkled/encdec8b10b/requirements.txt
  11. +17
    -0
      blinkled/encdec8b10b/setup.py
  12. +0
    -0
      blinkled/encdec8b10b/tests/__init__.py
  13. +7
    -0
      blinkled/encdec8b10b/tests/context.py
  14. +110
    -0
      blinkled/encdec8b10b/tests/test_class.py
  15. +87
    -0
      blinkled/encdec8b10b/tests/test_suite.py
  16. +82
    -0
      blinkled/genseq.py
  17. +1
    -0
      blinkled/requirements.txt
  18. +1
    -1
      board/hal_init.c

+ 2
- 0
LORA-IRR.md View File

@@ -126,6 +126,8 @@ Pins

The [pinout guide for the Node151](https://resource.heltec.cn/download/LoRa_Node_151/LoRa_Node_151_Pinout_Diagram.pdf).

The pins PA13(SWDIO)/14(SWCLK) are used for programming.

The default pins PB5-7,9 are used as active low controls for the relays.
They are mapped to channels 0 through 3 respectively. The LED on PB8
is mapped to channel 4. This is useful for testing if a command works


+ 24
- 0
blinkled/README.md View File

@@ -0,0 +1,24 @@
Blink LED
=========

This project is to blink a LED, and have a standard cell phone camera
decode the data.


8b10b code
----------

Adding:
```
(cd ..; git subtree add -P blinkled/encdec8b10b --squash https://github.com/olagrottvik/encdec8b10b.git master)
```

Updating:
```
(cd ..; git subtree pull -P blinkled/encdec8b10b --squash https://github.com/olagrottvik/encdec8b10b.git master)
```

Info
----

[8b/10b encoding](https://en.wikipedia.org/wiki/8b/10b_encoding)

+ 6
- 0
blinkled/encdec8b10b/.gitignore View File

@@ -0,0 +1,6 @@
.idea
__pycache__
build/
venv/
dist/
*egg-info*

+ 9
- 0
blinkled/encdec8b10b/.travis.yml View File

@@ -0,0 +1,9 @@
language: python
python:
- "3.4"
- "3.5"
- "3.6"
install:
- pip install -e .
script:
- make test

+ 21
- 0
blinkled/encdec8b10b/LICENSE View File

@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Ola Grøttvik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 26
- 0
blinkled/encdec8b10b/Makefile View File

@@ -0,0 +1,26 @@
.PHONY: init freeze test test_verbose build_dist upload

venv:
python3 -m venv .env

init:
pip install -r requirements.txt

freeze:
pip freeze | grep -v "pkg-resources" > requirements.txt

test:
nosetests tests/*

test_verbose:
nosetests --nocapture tests/*

build_dist:
rm -rf dist/*
python3 setup.py sdist bdist_wheel

upload_test:
python3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*

upload:
python3 -m twine upload dist/*

+ 88
- 0
blinkled/encdec8b10b/README.md View File

@@ -0,0 +1,88 @@
[![Build Status](https://travis-ci.com/olagrottvik/encdec8b10b.svg?token=jVu3gMDvjaqfNCVgNVai&branch=master)](https://travis-ci.com/olagrottvik/encdec8b10b)

# encdec8b10b

Encode and decode 8B10B encoding

## Get

```
python3 -m pip install encdec8b10b
```

## Usage

### Encode Data Byte
```
from encdec8b10b import EncDec8B10B

running_disp = 0
byte_to_enc = 0xf
running_disp, encoded = EncDec8B10B.enc_8b10b(byte_to_enc, running_disp)
print(hex(encoded))
Output >> 0xba
```
### Encode Control Byte
```
from encdec8b10b import EncDec8B10B

running_disp = 0
byte_to_enc = 0xbc # comma
ctrl = 1
running_disp, encoded = EncDec8B10B.enc_8b10b(byte_to_enc, running_disp, ctrl)
print(hex(encoded))
Output >> 0x17c
```
### Decode Data Byte
```
from encdec8b10b import EncDec8B10B

byte_to_dec = 0xba
ctrl, decoded = EncDec8B10B.dec_8b10b(byte_to_dec)
print(hex(decoded))
Output >> 0xf
# ctrl variable confirm that it was a data byte
print(ctrl)
Output >> 0
```
### Decode Control Byte
```
from encdec8b10b import EncDec8B10B

byte_to_dec = 0x17c # comma encoded
ctrl, decoded = EncDec8B10B.dec_8b10b(byte_to_dec)
print(hex(decoded))
Output >> 0xbc
# ctrl variable confirm that it was a control byte
print(ctrl)
Output >> 1
```
### Verbosity
Both functions have a verbose-mode to make it easier to confirm everything that's happening:
```
from encdec8b10b import EncDec8B10B

running_disp = 0
byte_to_enc = 0xA0
running_disp, encoded = EncDec8B10B.enc_8b10b(byte_to_enc, running_disp, verbose=True)

Output >> Encoder - In: A0 - Encoded: 146 - Running Disparity: 0

ctrl, decoded = EncDec8B10B.dec_8b10b(encoded, verbose=True)

Output >> Decoded: A0 - Control: 0
```

## 8B10B
8B10B Encoding were implemented by Al Widmer and Peter Franaszek in 1983. It is still widely used in high-speed electronics.

- [Original article](https://ieeexplore.ieee.org/document/5390392)
- [Wikipedia](https://en.wikipedia.org/wiki/8b/10b_encoding)


### Thanks
- [Ryu Shinhyung](https://opencores.org/projects/async_8b10b_encoder_decoder) for creating the tables used in this module
- [Chuck Benz](http://asics.chuckbenz.com/) for creating awesome combinational 8B10B modules
- [Alex Forencich](http://www.alexforencich.com/wiki/en/scripts/matlab/enc8b10b) for his 8B10B Matlab script

+ 1
- 0
blinkled/encdec8b10b/encdec8b10b/__init__.py View File

@@ -0,0 +1 @@
from .core import EncDec8B10B as EncDec8B10B

+ 2219
- 0
blinkled/encdec8b10b/encdec8b10b/core.py
File diff suppressed because it is too large
View File


+ 16
- 0
blinkled/encdec8b10b/requirements.txt View File

@@ -0,0 +1,16 @@
bleach==3.1.4
certifi==2019.3.9
chardet==3.0.4
docutils==0.14
idna==2.8
nose==1.3.7
pkginfo==1.5.0.1
Pygments==2.3.1
readme-renderer==24.0
requests==2.21.0
requests-toolbelt==0.9.1
six==1.12.0
tqdm==4.31.1
twine==1.13.0
urllib3==1.24.2
webencodings==0.5.1

+ 17
- 0
blinkled/encdec8b10b/setup.py View File

@@ -0,0 +1,17 @@
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name='encdec8b10b',
version='1.0',
packages=setuptools.find_packages(),
url='https://github.com/olagrottvik/encdec8b10b',
license='MIT',
author='Ola Grøttvik',
author_email='olagrottvik@gmail.com',
description='8B10B Encoding and Decoding',
long_description=long_description,
long_description_content_type="text/markdown",
)

+ 0
- 0
blinkled/encdec8b10b/tests/__init__.py View File


+ 7
- 0
blinkled/encdec8b10b/tests/context.py View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import encdec8b10b

+ 110
- 0
blinkled/encdec8b10b/tests/test_class.py View File

@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-

from .context import encdec8b10b
from encdec8b10b import EncDec8B10B
import random

import unittest

import sys
if sys.version_info.major != 3:
raise RuntimeError('invalid major version of python')

class TestClass(unittest.TestCase):
def test_to10b(self):
to10b = EncDec8B10B._to10b

self.assertEqual(to10b(0), '0' * 10)
self.assertEqual(to10b(2**10-1), '1' * 10)
self.assertEqual(to10b(0x20f), '1' * 4 + '000001')
self.assertEqual(to10b(0x0ff), '1' * 8 + '00')

dispminmax = [ 2, 3, 2, 3, 2, 1, 2, 1, 2, 1 ]

def test_disparity(self):
failures = []
for i in range(512):
disp = i >> 8
din = i & 0xff
ndisp, out = EncDec8B10B.enc_8b10b(din, disp)
cnt = +1 if disp else -1

for j in range(10):
out, bit = divmod(out, 2)
cnt += +1 if bit else -1
minmax = self.dispminmax[j]
if cnt < -minmax or cnt > minmax:
failures.append((disp, din))

if cnt != 1 if ndisp else cnt != -1:
failures.append((disp, din))

if failures:
raise RuntimeError('failures(%d): %s' % (len(failures), repr(failures)))

def test_bitdecoding(self):
coder = EncDec8B10B()

with self.assertRaises(ValueError):
coder.decode('asioj')

self.assertIsNone(coder.decode(''))

self.assertFalse(coder.issyncd())
self.assertIsNone(coder.decode('101011011010101101'))

self.assertFalse(coder.issyncd())

self.assertEqual(coder.decode(coder.encode(EncDec8B10B.COMMA)), EncDec8B10B.COMMA)
self.assertTrue(coder.issyncd())

astr = coder.encode(b'a')

self.assertIsNone(coder.decode(astr[:5]))

self.assertEqual(coder.decode(astr[5:]), b'a')

self.assertEqual(coder.decode(coder.encode(b'abc123')), b'abc123')

chrctrlstr = coder.encode(b'xx') + coder.encode(EncDec8B10B.K_28_0)

self.assertIsNone(coder.decode(chrctrlstr[:5]))

self.assertEqual(coder.decode(chrctrlstr[5:]), b'xx')
self.assertEqual(coder.decode(''), EncDec8B10B.K_28_0)
self.assertEqual(coder.decode(coder.encode(EncDec8B10B.K_28_2)), EncDec8B10B.K_28_2)

# that when junk is delivered, it is ignored
self.assertIsNone(coder.decode('111111111000011111'))

# and is no longer synced
self.assertFalse(coder.issyncd())

commaastr = coder.encode(EncDec8B10B.COMMA) + \
coder.encode(b'a')

# But it will sync back up
self.assertEqual(coder.decode(commaastr), EncDec8B10B.COMMA)
self.assertEqual(coder.decode(''), b'a')

self.assertIsNone(coder.decode(''))

self.assertEqual(coder.decode(coder.encode(EncDec8B10B.COMMA)), EncDec8B10B.COMMA)

def test_bitencoding(self):
coder = EncDec8B10B()

self.assertEqual(coder.encode(b'a'), '0111010011')
# +
s = ''.join((
'1000101100', # a -
'1011010011', # b +
'1100010011', # c +
'1000111001', # 1 +
'0100111001', # 2 +
'1100101001', # 3 +
))

self.assertEqual(coder.encode(b'abc123'), s)
self.assertEqual(coder.encode(EncDec8B10B.COMMA), '1100000101')
self.assertEqual(coder.encode(EncDec8B10B.COMMA), '0011111010')

+ 87
- 0
blinkled/encdec8b10b/tests/test_suite.py View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-

from .context import encdec8b10b
from encdec8b10b import EncDec8B10B
import random

import unittest

verbose = True # Set to True to get test output

class TestSuite(unittest.TestCase):
"""All test cases"""

def test_comma(self):
print("Testing K28.5 Comma...")
running_disparity = 0

test_data = 0xBC
test_ctrl = 1
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(test_data, running_disparity, test_ctrl, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(data_decoded, running_disparity, 1, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)

assert data_decoded == test_data and ctrl == test_ctrl, "K28.5 Comma Test Failed"

def test_known_seq(self):
print("Testing Known Sequence...")
known_seq = [0xa0, 0x7a, 0xFF, 0xc2, 0x48, 0xda, 0x1b, 0x2e, 0x1f, 0x5b, 0xa5, 0x20, 0xb6, 0x10, 0xc3, 0x4d, 0xa0, 0x17, 0x83,
0x3b, 0x2e, 0x7d, 0x61, 0x73, 0x4d, 0xc5, 0x42, 0x59, 0x45, 0x7c, 0x12, 0x1c, 0x03, 0x52, 0xdd, 0x30, 0xa5]
encoded_seq = [0x146, 0xda, 0x235, 0x1ad, 0x298, 0x19a, 0x9b, 0x24e, 0xb5, 0x29b, 0x165, 0x246, 0x156, 0xb6, 0x1a3, 0x28d, 0x179, 0x368, 0x123,
0x25b, 0x24e, 0xe2, 0x32e, 0x313, 0x28d, 0x1a5, 0x292, 0x299, 0x2a5, 0x0dc, 0x372, 0x9c, 0x363, 0x2b2, 0x1a2, 0x276, 0x165]
result_encoded = list()
result_decoded = list()

# Manually set running disparity to known start
running_disparity = 1
test_ctrl = 0

for byte, encoded in zip(known_seq, encoded_seq):
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(byte, running_disparity, test_ctrl, verbose)
assert data_encoded == encoded, "Data Encoded (0x{:03X}) does not match known sequence (0x{:03X})".format(data_encoded, encoded)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
assert data_decoded == byte, "Data Decoded (0x{:02X}) does not match input byte (0x{:02X})".format(data_decoded, byte)

def test_rand_seq(self):
print("Testing Random Data Sequence...")
test_ctrl = 0

running_disparity = 0
for i in range(100000):
rand_byte = random.randint(0, 0xFF)
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(rand_byte, running_disparity, test_ctrl, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
assert rand_byte == data_decoded, "Data Decoded (0x{:02X}) does not match input byte (0x{:02X})".format(data_decoded, rand_byte)

running_disparity = 1
for i in range(100000):
rand_byte = random.randint(0, 0xFF)
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(rand_byte, running_disparity, test_ctrl, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
assert rand_byte == data_decoded, "Data Decoded (0x{:02X}) does not match input byte (0x{:02X})".format(data_decoded, rand_byte)

def test_ctrl_symbol(self):
print("Testing All Control symbols...")
test_ctrl = 1
ctrl_symbols = [0x1c, 0x3c, 0x5c, 0x7c, 0x9c,
0xbc, 0xdc, 0xfc, 0xf7, 0xfb, 0xfd, 0xfe]

running_disparity = 0
for symbol in ctrl_symbols:
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(
symbol, running_disparity, test_ctrl, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
assert symbol == data_decoded, "Data Decoded (0x{:02X}) does not match input byte (0x{:02X})".format(
data_decoded, symbol)

running_disparity = 1
for symbol in ctrl_symbols:
running_disparity, data_encoded = EncDec8B10B.enc_8b10b(symbol, running_disparity, test_ctrl, verbose)
ctrl, data_decoded = EncDec8B10B.dec_8b10b(data_encoded, verbose)
assert symbol == data_decoded, "Data Decoded (0x{:02X}) does not match input byte (0x{:02X})".format(data_decoded, symbol)


if __name__ == '__main__':
unittest.main()

+ 82
- 0
blinkled/genseq.py View File

@@ -0,0 +1,82 @@
from encdec8b10b import EncDec8B10B

a = b'Hello, this is a test of encoding.'

coder = EncDec8B10B()

encoded = coder.encode(EncDec8B10B.COMMA) + coder.encode(a)
print('8b10b encoded:', repr(encoded))

decoder = EncDec8B10B()

print(repr(decoder.decode(encoded)))
print('test decode:', repr(decoder.decode('')))

def jm3coder(bits, lastlvl=0):
'''Takes in a string of bits, and outputs a trinary level.
It is guaranteed that no two outputs repeat allowing for
easy clock recovery as long as the sampling rate is faster
than output rate. (e.g. 30Hz (33.3ms) sampling of a 29Hz
(34.5ms) signal.

Note that this does not do sync, so something like an 8b10b
encoder should be used on top of this, and then transmit three
dummy bits at the begining as the decoder. The three will
cycle through all three states help ensuring level detection

An optional paramter of lastlvl can be provided, which is
the previous signal level transmitted.'''

r = []
for i in bits:
v = int(i, 2)
lastlvl = (lastlvl + 1 + v) % 3
r.append('%d' % lastlvl)

return ''.join(r)

encoded = jm3coder('000' + encoded)

print('jm3coded:', repr(encoded))

# make a "stretched" bit string
stretched = ''.join(encoded[i:i + 50] + encoded[i:i + 50][-1] for i in range(0, len(encoded), 50))
print('stretched:', repr(stretched))

def jm3decoder(trits, lastlvl=0):
'''Decodes a string encoded w/ jm3coder.

lastlvl should/must be provided which is the last rx'd level
(it must be 0, 1 or 2).
'''

lookup = {
(0, 1): 0,
(0, 2): 1,
(1, 2): 0,
(1, 0): 1,
(2, 0): 0,
(2, 1): 1,
}
r = []
for i in trits:
lvl = int(i, 3)
if lvl == lastlvl:
continue

r.append('%d' % lookup[(lastlvl, lvl)])
#r.append('%d' % ((lvl - lastlvl + 2) % 3))
lastlvl = lvl

return ''.join(r)

decoder = EncDec8B10B()

stretched = jm3decoder(stretched)

print(repr(stretched))

print(repr(decoder.decode(stretched)))
print(repr(decoder.decode('')))

print('done')

+ 1
- 0
blinkled/requirements.txt View File

@@ -0,0 +1 @@
-e ./encdec8b10b

+ 1
- 1
board/hal_init.c View File

@@ -1,4 +1,4 @@
#include <stm32f1xx_hal.h>
#include <main.h>

#include <sysinit.h>



Loading…
Cancel
Save