Hi Uwe, sorry for my long time silence.
I've merged meter decoder into fresh timing decoder. So, here it is. 2016-04-13 10:47 GMT+03:00 Uwe Hermann <u...@hermann-uwe.de>: > Hi, > > On Fri, Mar 18, 2016 at 03:57:46PM +0200, Andriy Trotsenko wrote: >> I would like to submit my small decoder 'meter'. It was helpful for me in >> some protocols decoding, and seems to be some more functional than 'timing' >> decoder. >> >> Here is patch generated from latest git clone of libsigrokdecode repository >> and screenshot showing decoder options. > > Looks great, this seems useful indeed, thanks! > > While some of the functionality could also (and probably will) be > implemented in frontends directly (e.g. in PulseView) such a decoder is > still useful for other reasons and purposes, e.g. when used with > sigrok-cli or for scripting / automation etc. > > However, it does indeed seem that the functionality should be integrated > into the 'timing' decoder, which already has a very similar purpose. > It's getting confusing for users if we merge too many PDs with different > names but similar purpose. > > Could you please add your copyright line to the timing decoder and merge > your functionality into that decoder please? That would be great! > > > Cheers, Uwe. > -- > http://hermann-uwe.de | http://randomprojects.org | http://sigrok.org
commit 70b174c065b821990d9617f669125c88c08c19fd Author: Andriy Trotsenko <andriy.trotse...@gmail.com> Date: Sun Aug 28 15:40:31 2016 +0300 Timing decoder rework. Merging timing and new meter decoders diff --git a/decoders/timing/__init__.py b/decoders/timing/__init__.py index ee31509..1f08a74 100644 --- a/decoders/timing/__init__.py +++ b/decoders/timing/__init__.py @@ -2,6 +2,8 @@ ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2014 Torsten Duwe <d...@suse.de> +## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourde...@savoirfairelinux.com> +## Copyright (C) 2015 Andriy Trotsenko <andriy.trotse...@gmail.com> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by diff --git a/decoders/timing/pd.py b/decoders/timing/pd.py index 98677b9..fae3909 100644 --- a/decoders/timing/pd.py +++ b/decoders/timing/pd.py @@ -3,6 +3,7 @@ ## ## Copyright (C) 2014 Torsten Duwe <d...@suse.de> ## Copyright (C) 2014 Sebastien Bourdelin <sebastien.bourde...@savoirfairelinux.com> +## Copyright (C) 2015 Andriy Trotsenko <andriy.trotse...@gmail.com> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,42 +23,62 @@ import sigrokdecode as srd from collections import deque -class SamplerateError(Exception): +class SampleRateError(Exception): pass -def normalize_time(t): - if t >= 1.0: - return '%.3f s (%.3f Hz)' % (t, (1/t)) - elif t >= 0.001: - if 1/t/1000 < 1: - return '%.3f ms (%.3f Hz)' % (t * 1000.0, (1/t)) - else: - return '%.3f ms (%.3f kHz)' % (t * 1000.0, (1/t)/1000) - elif t >= 0.000001: - if 1/t/1000/1000 < 1: - return '%.3f μs (%.3f kHz)' % (t * 1000.0 * 1000.0, (1/t)/1000) - else: - return '%.3f μs (%.3f MHz)' % (t * 1000.0 * 1000.0, (1/t)/1000/1000) - elif t >= 0.000000001: - if 1/t/1000/1000/1000: - return '%.3f ns (%.3f MHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000) - else: - return '%.3f ns (%.3f GHz)' % (t * 1000.0 * 1000.0 * 1000.0, (1/t)/1000/1000/1000) +def format_number(n, units): + + extra_digits = 0 # Const. Precision. Number of extra digits at the end of number + + for u in units: + if n < 10**(extra_digits + 4): # Prefer 1234 over 1.23 + break + n /= 1000 + + if n < 1: + digits = 3 # .123 + elif n < 10: + digits = 2 # 1.23 + elif n < 100: + digits = 1 # 12.3 else: - return '%f' % t + digits = 0 # 123 + # 1234 + n = "%.*f" % (digits + extra_digits, n) + + if digits + extra_digits > 0: # Strip meaningless 0s after decimal point and decimal point itself if alone + n = n.rstrip("0").rstrip(".") + + return n + u # Add measurement units class Decoder(srd.Decoder): + api_version = 2 id = 'timing' name = 'Timing' longname = 'Timing calculation with frequency and averaging' desc = 'Calculate time between edges.' license = 'gplv2+' + + CONST_0 = '- 0 -' + CONST_1 = '- 1 -' + CONST_01 = '0 & 1' + CONST_0_1 = '0 - 1' + CONST_1_0 = '1 - 0' + inputs = ['logic'] outputs = ['timing'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) + #optional_channels = () + options = ( + {'id': 'metering', 'desc': 'Metering', 'default': CONST_0_1, + 'values': (CONST_0, CONST_1, CONST_01, CONST_0_1, CONST_1_0)}, + {'id': 'units', 'desc': 'Units', 'default': 'samples', + 'values': ('samples', 'seconds', 'hertz')}, + {'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 }, + ) annotations = ( ('time', 'Time'), ('average', 'Average'), @@ -66,58 +87,73 @@ class Decoder(srd.Decoder): ('time', 'Time', (0,)), ('average', 'Average', (1,)), ) - options = ( - { 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 }, - ) - def __init__(self): + + def __init__(self, **kwargs): + self.samplerate = None - self.oldpin = None - self.last_samplenum = None - self.last_n = deque() - self.chunks = 0 def metadata(self, key, value): + if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + def normalize_number(self, n): + + if self.options['units'] == 'seconds': + return format_number(n * 1000000 / self.samplerate, ("µ", "m", "")) + + elif self.options['units'] == 'hertz': + return format_number(self.samplerate / n, ("", "K", "M", "G")) + + else: # Samples + return "%d" % n + def decode(self, ss, es, data): - if not self.samplerate: - raise SamplerateError('Cannot decode without samplerate.') - for (self.samplenum, (pin,)) in data: - if self.oldpin is None: - self.oldpin = pin - self.last_samplenum = self.samplenum + if not self.samplerate or self.samplerate < 1: + raise SampleRateError('Cannot decode without samplerate.') + + lb = rb = "" + if self.options['units'] == 'hertz' and self.options['metering'] not in (self.CONST_0_1, self.CONST_1_0): + #self.options['metering'] = self.CONST_0_1 + lb = "(" + rb = ")" + + prev_pin = prev_samplenum = avg_prev_samplenum = None + prev_n = deque() + + for (samplenum, (pin, )) in data: + + # Ignore identical samples early on (for performance reasons) + if prev_pin == pin: + continue + + # Initialize previous values on the very first loop execution + if prev_pin is None: + prev_pin = pin + #if (self.options['metering'] in (self.CONST_0_1, self.CONST_1_0)): + prev_samplenum = samplenum + #self.prev_samplenum = self.avg_prev_samplenum = self.samplenum continue - if self.oldpin != pin: - samples = self.samplenum - self.last_samplenum - t = samples / self.samplerate - self.chunks += 1 - - # Don't insert the first chunk into the averaging as it is - # not complete probably. - if self.last_samplenum is None or self.chunks < 2: - # Report the timing normalized. - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [0, [normalize_time(t)]]) - else: - if t > 0: - self.last_n.append(t) - - if len(self.last_n) > self.options['avg_period']: - self.last_n.popleft() - - # Report the timing normalized. - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [0, [normalize_time(t)]]) - self.put(self.last_samplenum, self.samplenum, self.out_ann, - [1, [normalize_time(sum(self.last_n) / len(self.last_n))]]) - - # Store data for next round. - self.last_samplenum = self.samplenum - self.oldpin = pin + # Averaging logic + if avg_prev_samplenum is not None: # Comment out this line and shift left following indented lines to count from the very begining, not from the first front change + prev_n.append(samplenum - avg_prev_samplenum) + if len(prev_n) > self.options['avg_period']: + prev_n.popleft() + self.put(avg_prev_samplenum, samplenum, self.out_ann, [1, [self.normalize_number(sum(prev_n) / len(prev_n))]]) + avg_prev_samplenum = samplenum + + # Metering logic + if (prev_samplenum is not None) and ((pin == 0 and self.options['metering'] in (self.CONST_0_1, self.CONST_1, self.CONST_01)) or (pin == 1 and self.options['metering'] in (self.CONST_1_0, self.CONST_0, self.CONST_01))): + self.put(prev_samplenum, samplenum, self.out_ann, [0, [lb + self.normalize_number(samplenum - prev_samplenum) + rb]]) + + if (self.options['metering'] != self.CONST_0_1 or pin == 0) and (self.options['metering'] != self.CONST_1_0 or pin == 1): + prev_samplenum = samplenum + + prev_pin = pin
------------------------------------------------------------------------------
_______________________________________________ sigrok-devel mailing list sigrok-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/sigrok-devel