Skip to content

Commit

Permalink
0.8.3: fix for output correction and reformatted files
Browse files Browse the repository at this point in the history
  • Loading branch information
spacemanspiff2007 committed Jul 23, 2021
1 parent 6a4ef9d commit 2b339ec
Show file tree
Hide file tree
Showing 17 changed files with 131 additions and 54 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ name: Publish Python distributions to PyPI
on:
release:
types: [published]

jobs:
build-n-publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
with:
Expand All @@ -19,7 +19,7 @@ jobs:

- name: Install setuptools
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade pip
python -m pip install --upgrade setuptools wheel twine
- name: Build a binary wheel and a source tarball
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
__pycache__
/conf
/build
/log.log
/log.log
/venv/
10 changes: 10 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[settings]
line_length = 120

src_paths=pyartnet, tests

ensure_newline_before_comments = True
force_alphabetical_sort_within_sections = True

balanced_wrapping = True
multi_line_output = 2
19 changes: 19 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
exclude: \.(?:pdf|svg)$
- id: trailing-whitespace

- repo: https://github.com/pycqa/isort
rev: 5.8.0
hooks:
- id: isort
name: isort (python)

- repo: https://gitlab.com/PyCQA/flake8
rev: '3.9.1'
hooks:
- id: flake8
14 changes: 8 additions & 6 deletions pyartnet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from . import errors, fades, output_correction
from .__version__ import __version__
from . import errors
from . import fades

from .dmx_channel import DmxChannel, DmxChannel16Bit, DmxChannel24Bit, DmxChannel32Bit
from .dmx_universe import DmxUniverse
from .artnet_node import ArtNetNode
# isort: split

from . import output_correction
from .dmx_channel import DmxChannel, DmxChannel16Bit, DmxChannel24Bit, DmxChannel32Bit
from .dmx_universe import DmxUniverse

# isort: split

from .artnet_node import ArtNetNode
2 changes: 1 addition & 1 deletion pyartnet/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.8.2'
__version__ = '0.8.3'
39 changes: 21 additions & 18 deletions pyartnet/dmx_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import pyartnet


log = logging.getLogger('pyartnet.DmxChannel')


class DmxChannel:
_CHANNEL_SIZE: int = 1 # Channel size in byte
_CHANNEL_SIZE: int = 1 # Channel size in byte
_CHANNEL_MAX: int = 256 ** _CHANNEL_SIZE - 1 # Max value of the channel

def __init__(self, universe: 'pyartnet.DmxUniverse', start: int, width: int):
self.width: int = width
Expand All @@ -32,7 +32,8 @@ def __init__(self, universe: 'pyartnet.DmxUniverse', start: int, width: int):
f'start: {self.start} width: {self.width} * {byte_width}bytes -> {self.stop}'
)

self.__val_act_i = [0 for _ in range(self.width)]
self.__val_raw_i = [0 for _ in range(self.width)] # uncorrected values
self.__val_act_i = [0 for _ in range(self.width)] # values after output correction

self.__fades: typing.List[typing.Optional[pyartnet.fades.FadeBase]] = [None for k in range(self.width)]
self.__fade_running = False
Expand All @@ -50,15 +51,6 @@ def __init__(self, universe: 'pyartnet.DmxUniverse', start: int, width: int):
self.callback_value_changed: typing.Optional[typing.Callable[[DmxChannel], typing.Any]] = None
self.callback_fade_finished: typing.Optional[typing.Callable[[DmxChannel], typing.Any]] = None

def __apply_output_correction(self, channel_val):
if self.output_correction is not None:
return self.output_correction(channel_val, 255 ** self._CHANNEL_SIZE)

if self.__universe.output_correction is not None:
return self.__universe.output_correction(channel_val, 255 ** self._CHANNEL_SIZE)

return channel_val

@property
def fade_running(self) -> bool:
return self.__fade_running
Expand Down Expand Up @@ -98,7 +90,7 @@ def add_fade(self, fade_list: typing.List[int], duration_ms: int, fade_class=pya

# calculate required values
for i, fade in enumerate(fade_list): # type: int, pyartnet.fades.FadeBase
fade.val_start = self.__val_act_i[i]
fade.val_start = self.__val_raw_i[i]
fade.val_current = fade.val_start
fade.initialize_fade(self.__step_max)
self.__fades[i] = fade
Expand All @@ -107,7 +99,7 @@ def add_fade(self, fade_list: typing.List[int], duration_ms: int, fade_class=pya
log._log(logging.DEBUG, f'Fade with {self.__step_max} steps:', [])
for i in range(self.width):
log._log(logging.DEBUG, 'CH {}: {:03d} -> {:03d} | {}'.format(
self.start + i, self.__val_act_i[i], self.__fades[i].val_target, self.__fades[i].debug_initialize()
self.start + i, self.__val_raw_i[i], self.__fades[i].val_target, self.__fades[i].debug_initialize()
), [])

self.__fade_running = True
Expand Down Expand Up @@ -135,7 +127,15 @@ def process(self):

# get next value
fade.calc_next_value()
self.__val_act_i[i] = round(self.__apply_output_correction(fade.val_current))
self.__val_raw_i[i] = current = round(fade.val_current)

# apply output correction
if self.output_correction is not None:
self.__val_act_i[i] = round(self.output_correction(current, self._CHANNEL_MAX))
elif self.__universe.output_correction is not None:
self.__val_act_i[i] = round(self.__universe.output_correction(current, self._CHANNEL_MAX))
else:
self.__val_act_i[i] = round(current)

running = True
self.__fade_running = running
Expand All @@ -156,12 +156,15 @@ def process(self):


class DmxChannel16Bit(DmxChannel):
_CHANNEL_SIZE: int = 2 # Channel size in byte
_CHANNEL_SIZE: int = 2 # Channel size in byte
_CHANNEL_MAX: int = 256 ** _CHANNEL_SIZE - 1 # Max value of the channel


class DmxChannel24Bit(DmxChannel):
_CHANNEL_SIZE: int = 3 # Channel size in byte
_CHANNEL_SIZE: int = 3 # Channel size in byte
_CHANNEL_MAX: int = 256 ** _CHANNEL_SIZE - 1 # Max value of the channel


class DmxChannel32Bit(DmxChannel):
_CHANNEL_SIZE: int = 4 # Channel size in byte
_CHANNEL_SIZE: int = 4 # Channel size in byte
_CHANNEL_MAX: int = 256 ** _CHANNEL_SIZE - 1 # Max value of the channel
1 change: 1 addition & 0 deletions pyartnet/dmx_universe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import typing

import pyartnet

from .errors import ChannelExistsError, ChannelNotFoundError, OverlappingChannelError

log = logging.getLogger('pyartnet.DmxUniverse')
Expand Down
2 changes: 1 addition & 1 deletion pyartnet/fades/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .fade_base import FadeBase
from .fade_linear import LinearFade
from .fade_linear import LinearFade
6 changes: 3 additions & 3 deletions pyartnet/output_correction.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@


def quadratic(val: float, max_val: int = 255):
def quadratic(val: float, max_val: int = 0xFF):
return (val ** 2) / max_val


def cubic(val: float, max_val: int = 255):
def cubic(val: float, max_val: int = 0xFF):
return (val ** 3) / (max_val * max_val)


def quadruple(val: float, max_val: int = 255):
def quadruple(val: float, max_val: int = 0xFF):
return (val ** 4) / (max_val * max_val * max_val)
19 changes: 12 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pyartnet is a python implementation of the ArtNet protocol using [asyncio](https
````python
from pyartnet import ArtNetNode

# Run this code in your async function
node = ArtNetNode('IP')
await node.start()

Expand All @@ -25,7 +26,7 @@ channel = universe.add_channel(start=1, width=3)

# Fade channel to 255,0,0 in 5s
# The fade will automatically run in the background
channel.add_fade([255,0,0], 5000)
channel.add_fade([255,0,0], 5000)

# this can be used to wait till the fade is complete
await channel.wait_till_fade_complete()
Expand All @@ -36,8 +37,8 @@ Created channels can be requested from the universe through the dict syntax or t
If no channel name is specified during creation the default name will be ``{START}/{WIDTH}``.

````python
channel = universe['1/3']
channel = universe.get_channel('1/3')
channel = universe['1/3']
channel = universe.get_channel('1/3')
````

## Callbacks
Expand All @@ -54,7 +55,7 @@ universe = node.add_universe(0)
channel = universe.add_channel(start=1, width=3)

def cb(ch: DmxChannel):
ch.add_fade([0] if ch.get_channel_values() == [255] else [255], 1000)
ch.add_fade([0, 0, 0] if ch.get_channel_values() == [255, 255, 255] else [255, 255, 255], 1000)

channel.callback_fade_finished = cb
channel.callback_value_changed = my_func2
Expand Down Expand Up @@ -86,7 +87,7 @@ linear (default when nothing is set), quadratic, cubic then quadruple
Quadratic or cubic results in much smoother and more pleasant fades when using LED Strips.

## Wider DMX Channels
The library supports wider dmx channels for advanced control.
The library supports wider dmx channels for advanced control.

````python
from pyartnet import ArtNetNode, DmxChannel16Bit
Expand All @@ -102,11 +103,15 @@ universe = node.add_universe(1)
channel = universe.add_channel(start=1, width=3, channel_type=DmxChannel16Bit)

# Notice the higher maximum value for the fade
channel.add_fade([0xFFFF,0,0], 5000)
channel.add_fade([0xFFFF, 0, 0], 5000)
````


# Changelog
#### 0.8.3 (23.07.2021)
- No more jumping fades when using output correction with bigger channels
- Reformatted files

#### 0.8.2 (14.03.2021)
- Using nonblocking sockets
- Added option to send frames to a broadcast address
Expand All @@ -115,7 +120,7 @@ channel.add_fade([0xFFFF,0,0], 5000)
- Fixed an issue with the max value for channels with 16bits and more

#### 0.8.0 (11.02.2021)
- Added support for channels with 16, 24 and 32bits
- Added support for channels with 16, 24 and 32bits


#### 0.7.0 (28.10.2020)
Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pre-commit==2.13.0
pytest==6.2.4
pytest-asyncio==0.15.1
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging

import pytest

import pyartnet
Expand Down
24 changes: 24 additions & 0 deletions tests/test_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import pytest

import pyartnet
from pyartnet import output_correction

from .conftest import PatchedArtNetNode


Expand Down Expand Up @@ -42,6 +44,28 @@ async def test_channel_double_step(running_artnet_node: PatchedArtNetNode):
assert running_artnet_node.values == [[128, 0], [255, 0], [128, 0], [0, 0]]


@pytest.mark.parametrize('corr, dst_val',
[(None, 128), (output_correction.quadratic, 64), (output_correction.cubic, 32),
(output_correction.quadruple, 16)])
@pytest.mark.asyncio
async def test_channel_output_correction_continue(running_artnet_node: PatchedArtNetNode, corr, dst_val):

universe = running_artnet_node.add_universe(0)
channel = universe.add_channel(1, 1)
channel.output_correction = corr

channel.add_fade([128], 0)
await channel.wait_till_fade_complete()
assert channel.get_channel_values() == [dst_val]

channel.add_fade([255], 50)
await channel.wait_till_fade_complete()
assert channel.get_channel_values() == [255]

for val, ign in running_artnet_node.values:
assert val >= dst_val, running_artnet_node.values


@pytest.mark.asyncio
async def test_channel_with_3(running_artnet_node: PatchedArtNetNode):

Expand Down
28 changes: 18 additions & 10 deletions tests/test_output_correction.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import pytest

from pyartnet.output_correction import quadratic, quadruple, cubic
from pyartnet import DmxChannel, DmxChannel16Bit, DmxChannel24Bit, DmxChannel32Bit
from pyartnet.output_correction import cubic, quadratic, quadruple


@pytest.mark.parametrize('corr', [quadratic, quadruple, cubic])
def test_endpoints(corr):
def test_endpoints_8bit(corr):
assert corr(0) == 0
assert corr(255) == 255

i = 0
while i <= 255:
assert 0 <= corr(i) <= 255
i += 0.001
assert corr(0xFF, max_val=DmxChannel._CHANNEL_MAX) == 0xFF


@pytest.mark.parametrize('corr', [quadratic, quadruple, cubic])
def test_endpoints_16bit(corr):
assert corr(0) == 0
assert corr(0xFFFF, max_val=0xFFFF) == 0xFFFF
assert corr(0, max_val=DmxChannel16Bit._CHANNEL_MAX) == 0
assert corr(0xFFFF, max_val=DmxChannel16Bit._CHANNEL_MAX) == 0xFFFF


@pytest.mark.parametrize('corr', [quadratic, quadruple, cubic])
def test_endpoints_24bit(corr):
assert corr(0, max_val=DmxChannel16Bit._CHANNEL_MAX) == 0
assert corr(0xFFFFFF, max_val=DmxChannel24Bit._CHANNEL_MAX) == 0xFFFFFF


@pytest.mark.parametrize('corr', [quadratic, quadruple, cubic])
def test_endpoints_32bit(corr):
assert corr(0, max_val=DmxChannel16Bit._CHANNEL_MAX) == 0
assert corr(0xFFFFFFFF, max_val=DmxChannel32Bit._CHANNEL_MAX) == 0xFFFFFFFF
4 changes: 3 additions & 1 deletion tests/test_universe.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest

from pyartnet import DmxUniverse, errors

from .conftest import PatchedArtNetNode
import pytest


def test_channel_add(artnet_node: PatchedArtNetNode):
Expand Down
4 changes: 1 addition & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ deps =
asynctest
-r{toxinidir}/requirements.txt

commands =
commands =
python -m pytest

[testenv:flake]
Expand All @@ -30,5 +30,3 @@ deps =
commands =
flake8 -v
# pydocstyle


0 comments on commit 2b339ec

Please sign in to comment.