From 17b2579a517489ce06756943529986921e194ef9 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 29 Jul 2023 16:59:43 +0200 Subject: rgb_led_ws281x: support more colour component orders (wire, and text) The 'type' option was not good enough. Replace it by 'wireorder' (order of colour components on the wire, depends on the RGB LED chip type), and 'textorder' (presentation to users in annotations). Support many more layouts of colour components on the wire. Cover all permutations of R, G, and B. Support a few RGB plus W layouts that are known to be in use. Adding more is just a matter of adding more choices in the option, the implementation transparently follows. Support a few text orders: Reflect the very order of bits on the wire. Automatic support for RGB with optional White, or fixed RGB or RGB-W variants (all are users' choices, default remains "RGB" for backwards compatibility). Support arbitrary combinations of wire order and text order in emitted annotations. Keep support for the weird RGWB text format, which the previous decoder implementation used for "all" RGBW types, and which is referenced by existing test cases. It is uncertain which chip type is supposed to generate this specific RGBW traffic. It is as uncertain why this text order was chosen, which neither is the human readable RGBW format nor matches the wire order. The previous implementation was introduced in commit 47ff9910f7e1, but neither commented nor referenced literature or external sources nor did the commit message contain any clues. This current implementation needs more tests and reviews, but lends itself better to maintenance, fixes and enhancements. --- decoders/rgb_led_ws281x/pd.py | 72 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/decoders/rgb_led_ws281x/pd.py b/decoders/rgb_led_ws281x/pd.py index 5f35594..7c8ead4 100644 --- a/decoders/rgb_led_ws281x/pd.py +++ b/decoders/rgb_led_ws281x/pd.py @@ -23,6 +23,9 @@ class SamplerateError(Exception): pass +class DecoderError(Exception): + pass + ( ANN_BIT, ANN_RESET, ANN_RGB, ) = range(3) class Decoder(srd.Decoder): @@ -48,8 +51,11 @@ class Decoder(srd.Decoder): ('rgb-vals', 'RGB values', (ANN_RGB,)), ) options = ( - {'id': 'type', 'desc': 'RGB or RGBW', 'default': 'RGB', - 'values': ('RGB', 'RGBW')}, + {'id': 'wireorder', 'desc': 'colour components order (wire)', + 'default': 'GRB', + 'values': ('BGR', 'BRG', 'GBR', 'GRB', 'RBG', 'RGB', 'RWBG', 'RGBW')}, + {'id': 'textorder', 'desc': 'components output order (text)', + 'default': 'RGB', 'values': ('wire', 'RGB[W]', 'RGB', 'RGBW', 'RGWB')}, ) def __init__(self): @@ -72,15 +78,32 @@ def putg(self, ss, es, cls, text): def handle_bits(self): if len(self.bits) < self.need_bits: return - grb = bitpack_msb(self.bits, 0) - if self.options['type'] == 'RGB': - rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff) - text = '#{:06x}'.format(rgb) - else: - rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0xff0000ff) - text = '#{:08x}'.format(rgb) ss_packet, es_packet = self.bits[0][1], self.bits[-1][2] - self.putg(ss_packet, es_packet, ANN_RGB, [text]) + r, g, b, w = 0, 0, 0, None + comps = [] + for i, c in enumerate(self.wireformat): + first_idx, after_idx = 8 * i, 8 * i + 8 + comp_bits = self.bits[first_idx:after_idx] + comp_ss, comp_es = comp_bits[0][1], comp_bits[-1][2] + comp_value = bitpack_msb(comp_bits, 0) + comp_item = (comp_ss, comp_es, comp_value) + comps.append(comp_item) + if c.lower() == 'r': + r = comp_value + elif c.lower() == 'g': + g = comp_value + elif c.lower() == 'b': + b = comp_value + elif c.lower() == 'w': + w = comp_value + wt = '' if w is None else '{:02x}'.format(w) + if self.textformat == 'wire': + rgb_text = ['{:02x}'.format(c[-1]) for c in comps] + rgb_text = '#' + ''.join(rgb_text) + else: + rgb_text = self.textformat.format(r = r, g = g, b = b, w = w, wt = wt) + if rgb_text: + self.putg(ss_packet, es_packet, ANN_RGB, [rgb_text]) self.bits.clear() def handle_bit(self, ss, es, value, ann_late = False): @@ -97,7 +120,34 @@ def handle_bit(self, ss, es, value, ann_late = False): def decode(self): if not self.samplerate: raise SamplerateError('Cannot decode without samplerate.') - self.need_bits = len(self.options['type']) * 8 + + # Preprocess options here, to simplify logic which executes + # much later in loops while settings have the same values. + wireorder = self.options['wireorder'].lower() + self.wireformat = [c for c in wireorder if c in 'rgbw'] + self.need_bits = len(self.wireformat) * 8 + textorder = self.options['textorder'].lower() + if textorder == 'wire': + self.textformat = 'wire' + elif textorder == 'rgb[w]': + self.textformat = '#{r:02x}{g:02x}{b:02x}{wt:s}' + else: + self.textformat = { + # "Obvious" permutations of R/G/B. + 'bgr': '#{b:02x}{g:02x}{r:02x}', + 'brg': '#{b:02x}{r:02x}{g:02x}', + 'gbr': '#{g:02x}{b:02x}{r:02x}', + 'grb': '#{g:02x}{r:02x}{b:02x}', + 'rbg': '#{r:02x}{b:02x}{g:02x}', + 'rgb': '#{r:02x}{g:02x}{b:02x}', + # RGB plus White. Only one of them useful? + 'rgbw': '#{r:02x}{g:02x}{b:02x}{w:02x}', + # Weird RGBW permutation for compatibility to test case. + # Neither used RGBW nor the 'wire' order. Obsolete now? + 'rgwb': '#{r:02x}{g:02x}{w:02x}{b:02x}', + }.get(textorder, None) + if self.textformat is None: + raise DecoderError('Unsupported text output format.') # Either check for edges which communicate bit values, or for # long periods of idle level which represent a reset pulse. -- cgit v1.2.3-54-g00ecf