DSPRelated.com
Forums

How to simplify a rolling average in an FPGA

Started by garengllc September 30, 2015
On 10/1/2015 1:01 PM, Allan Herriman wrote:
> On Thu, 01 Oct 2015 11:22:07 -0400, rickman wrote: > >> On 10/1/2015 10:12 AM, Allan Herriman wrote: >>> On Thu, 01 Oct 2015 09:34:25 -0400, rickman wrote: >>> >>>> On 10/1/2015 8:41 AM, Allan Herriman wrote: >>>>> On Thu, 01 Oct 2015 12:13:26 +0000, Allan Herriman wrote: >>>>> >>>>>> On Wed, 30 Sep 2015 12:56:59 -0500, garengllc wrote: >>>>>> >>>>>>>> I know I may not be able to simplify things to give me a bit >>>>>>>> perfect >>>>>>> match >>>>>>>> to the C code, but is there a way to compute this rolling average >>> in an >>>>>>>> FPGA that is lightweight and a close approximation? >>>>>>>> >>>>>>>> >>>>>>>> --------------------------------------- >>>>>>>> Posted through http://www.DSPRelated.com >>>>>>> >>>>>>> I may have worked it out, but would still like to hear other's >>> opinions. >>>>>>> The code I ended up with is: >>>>>>> rolling_average = rolling_average - >>>>>>> (rolling_average-newValue)/rolling_average_num_max >>>>>>> >>>>>>> I am looking into converting rolling_average_num_max to the closest >>>>>>> power of two, and then using that to shift the >>>>>>> (rolling_average-newValue) numerator. >>>>>> >>>>>> >>>>>> You didn't specify VHDL or Verilog. Ok, toss a coin, VHDL it is. >>>>>> >>>>>> Converting the above C into VHDL gives something like this: >>>>>> >>>>>> >>>>>> library ieee; >>>>>> use ieee.std_logic_1164.all; >>>>>> use ieee.numeric_std.all; >>>>>> >>>>>> entity averager is >>>>>> generic ( >>>>>> g_width : positive; >>>>>> g_pole_shift : positive >>>>>> ); >>>>>> port ( >>>>>> clk : in std_logic; >>>>>> newValue : in signed(g_width-1 downto 0); >>>>>> rolling_average : out signed(g_width-1 downto 0) >>>>>> ); >>>>>> end entity averager; >>>>>> >>>>>> architecture comp_dsp of averager is >>>>>> >>>>>> begin -- architecture comp_dsp of entity averager >>>>>> >>>>>> naive_attempt : process (clk) >>>>>> constant width : positive := g_width + g_pole_shift; >>>>>> variable sum : signed(width-1 downto 0) := (others => >>> '0'); >>>>>> begin >>>>>> if rising_edge(clk) then >>>>>> sum := sum - (sum - newValue * 2**g_pole_shift) / >>> 2**g_pole_shift; >>>>>> rolling_average <= sum(width-1 downto width-g_width); >>>>>> end if; >>>>>> end process naive_attempt; >>>>>> >>>>>> end architecture comp_dsp; -- of entity averager >>>>>> >>>>>> >>>>>> The arithmetic will synthesise to two adders, despite the use of * >>>>>> and / in the source. >>>>>> >>>>>> I called this one "naive" because it's simple and obvious but really >>>>>> bad - the intermediate result has full precision but the output is >>>>>> just the most significant bits of the internal value. >>>>> >>>>> >>>>> And here's one that's slightly less suckful: >>>>> >>>>> (Same entity declaration as before) >>>>> >>>>> -- This one uses the fraction saving method -- described in the >>>>> Randy Yates / Richard Lyons -- DC Blocker paper. >>>>> -- Variable names used here mostly match the -- names used in >>>>> Figure 2 of that paper. fraction_saving : process (clk) >>>>> variable s : signed(g_width-1 downto 0); >>>>> variable u : signed(g_width+g_pole_shift-1 downto 0); >>>>> variable f_d : unsigned(g_pole_shift-1 downto 0) := >>> (others => '0'); >>>>> variable y : signed(g_width-1 downto 0) := (others => >>> '0'); >>>>> variable w : signed(g_width-1 downto 0); >>>>> constant tail : signed(g_pole_shift-1 downto 0) := >>>>> (others >>> => '0'); >>>>> begin >>>>> if rising_edge(clk) then >>>>> w := newValue; >>>>> u := signed('0' & f_d) + (y & tail) - y; >>>>> s := u(g_width+g_pole_shift-1 downto g_pole_shift); >>>>> f_d := unsigned(u(g_pole_shift-1 downto 0)); >>>>> y := w + s; >>>>> rolling_average <= y; >>>>> end if; >>>>> end process fraction_saving; >>>>> >>>>> >>>>> As before, I haven't tested this; don't use it for anything that >>>>> matters. >>>> >>>> I don't know the "fraction saving method", so I would have to work >>>> pretty hard to understand this code. If it implements the same >>>> algorithm as your first example it would not be an average. Your >>>> initial did not divide by the number of samples and in fact didn't >>>> keep track of the number of samples. I assume this one also doesn't >>>> divide by the number of samples because I don't see where you keep >>>> track of >>> the >>>> number of samples. That would explain why there is no division or >>>> multiplication required to implement them. >>> >>> >>> You might know fraction saving as error feedback, or perhaps delta >>> sigma modulation. >>> I recommend reading the Yates / Lyons paper. >>> >>> Based on the both the C code and the description, the OP seems to be >>> asking for a filter with a u(t).exp(-t) >>> shaped impulse response. >>> One does not need to waste logic keeping track of the number of samples >>> to do that. >>> >>> Of course, I may have interpreted the requirements wrongly. >> >> I believe you need to reexamine the original post and possibly others as >> well. The OP is not designing a filter. He is implementing an average >> that updates the result on every new sample arriving. Wikipedia calls >> this the cumulative moving average. It looks a bit like a filter >> because of the way he wrote the equations, but is actually is just a >> simple average. Of course, in the strict sense that is also a filter, >> but is simple enough that there is no need to treat it as a complex >> filter. The code for this is just >> >> avg_num = avg_num + 1 ; >> sum = sum + newValue ; >> avg = sum / avg_num ; >> >> Throw this in a clocked process (or an always @ (posedge clk) block) and >> you have your average. The only sticking point is a simple way to >> calculate the divide. Of course there needs to be initialization code >> and there may need to be code to restart the average if the OP needs >> that. >> >> The division can be done fairly easily for small values of avg_num using >> a look up table based on a block RAM. The reciprocal of avg_num would >> be obtained by the lookup table and then multiplied by the sum to get >> the average. I believe the OP indicated the max value of the sample >> count is 8000 which should not consume too many resources in most FPGAs >> for the reciprocal calculation. This avoids the accumulated errors from >> the form that looks like a filter. > > > Dang. That form looked so much like a single pole IIR filter I thought > it was one.
If the coefficients were constant, it would be the same.
> For the division, I was thinking that perhaps Newton-Raphson to find the > reciprocal might work well, as the reciprocal at any input sample will be > a good approximation for the next reciprocal, meaning that only a single > N-R iteration per input sample should give an accurate result. > Each N-R iteration would require two multiplications.
That would likely work for later values, but do you think it can go from a result of 1/2 to 1/3 in one iteration? I think it would be easy to code so that you don't need to calculate the first coefficient which would be 1 and initialize the "pipe" to give you 1/2 for the second round. But after that you have to calculate the series 1/3, 1/4, etc. A look up table would give good accuracy and not take up too much space in block RAM. -- Rick
On Thu, 01 Oct 2015 13:24:52 -0400, rickman wrote:

> On 10/1/2015 1:01 PM, Allan Herriman wrote: >> On Thu, 01 Oct 2015 11:22:07 -0400, rickman wrote: >> >>> On 10/1/2015 10:12 AM, Allan Herriman wrote: >>>> On Thu, 01 Oct 2015 09:34:25 -0400, rickman wrote: >>>> >>>>> On 10/1/2015 8:41 AM, Allan Herriman wrote: >>>>>> On Thu, 01 Oct 2015 12:13:26 +0000, Allan Herriman wrote: >>>>>> >>>>>>> On Wed, 30 Sep 2015 12:56:59 -0500, garengllc wrote: >>>>>>> >>>>>>>>> I know I may not be able to simplify things to give me a bit >>>>>>>>> perfect >>>>>>>> match >>>>>>>>> to the C code, but is there a way to compute this rolling >>>>>>>>> average >>>> in an >>>>>>>>> FPGA that is lightweight and a close approximation? >>>>>>>>> >>>>>>>>> >>>>>>>>> --------------------------------------- >>>>>>>>> Posted through http://www.DSPRelated.com >>>>>>>> >>>>>>>> I may have worked it out, but would still like to hear other's >>>> opinions. >>>>>>>> The code I ended up with is: >>>>>>>> rolling_average = rolling_average - >>>>>>>> (rolling_average-newValue)/rolling_average_num_max >>>>>>>> >>>>>>>> I am looking into converting rolling_average_num_max to the >>>>>>>> closest power of two, and then using that to shift the >>>>>>>> (rolling_average-newValue) numerator. >>>>>>> >>>>>>> >>>>>>> You didn't specify VHDL or Verilog. Ok, toss a coin, VHDL it is. >>>>>>> >>>>>>> Converting the above C into VHDL gives something like this: >>>>>>> >>>>>>> >>>>>>> library ieee; >>>>>>> use ieee.std_logic_1164.all; >>>>>>> use ieee.numeric_std.all; >>>>>>> >>>>>>> entity averager is >>>>>>> generic ( >>>>>>> g_width : positive; >>>>>>> g_pole_shift : positive >>>>>>> ); >>>>>>> port ( >>>>>>> clk : in std_logic; >>>>>>> newValue : in signed(g_width-1 downto 0); >>>>>>> rolling_average : out signed(g_width-1 downto 0) >>>>>>> ); >>>>>>> end entity averager; >>>>>>> >>>>>>> architecture comp_dsp of averager is >>>>>>> >>>>>>> begin -- architecture comp_dsp of entity averager >>>>>>> >>>>>>> naive_attempt : process (clk) >>>>>>> constant width : positive := g_width + g_pole_shift; >>>>>>> variable sum : signed(width-1 downto 0) := (others >>>>>>> => >>>> '0'); >>>>>>> begin >>>>>>> if rising_edge(clk) then >>>>>>> sum := sum - (sum - newValue * 2**g_pole_shift) / >>>> 2**g_pole_shift; >>>>>>> rolling_average <= sum(width-1 downto >>>>>>> width-g_width); >>>>>>> end if; >>>>>>> end process naive_attempt; >>>>>>> >>>>>>> end architecture comp_dsp; -- of entity averager >>>>>>> >>>>>>> >>>>>>> The arithmetic will synthesise to two adders, despite the use of * >>>>>>> and / in the source. >>>>>>> >>>>>>> I called this one "naive" because it's simple and obvious but >>>>>>> really bad - the intermediate result has full precision but the >>>>>>> output is just the most significant bits of the internal value. >>>>>> >>>>>> >>>>>> And here's one that's slightly less suckful: >>>>>> >>>>>> (Same entity declaration as before) >>>>>> >>>>>> -- This one uses the fraction saving method -- described in >>>>>> the Randy Yates / Richard Lyons -- DC Blocker paper. >>>>>> -- Variable names used here mostly match the -- names used >>>>>> in Figure 2 of that paper. fraction_saving : process (clk) >>>>>> variable s : signed(g_width-1 downto 0); >>>>>> variable u : signed(g_width+g_pole_shift-1 downto >>>>>> 0); >>>>>> variable f_d : unsigned(g_pole_shift-1 downto 0) := >>>> (others => '0'); >>>>>> variable y : signed(g_width-1 downto 0) := (others >>>>>> => >>>> '0'); >>>>>> variable w : signed(g_width-1 downto 0); >>>>>> constant tail : signed(g_pole_shift-1 downto 0) := >>>>>> (others >>>> => '0'); >>>>>> begin >>>>>> if rising_edge(clk) then >>>>>> w := newValue; >>>>>> u := signed('0' & f_d) + (y & tail) - y; >>>>>> s := u(g_width+g_pole_shift-1 downto g_pole_shift); >>>>>> f_d := unsigned(u(g_pole_shift-1 downto 0)); >>>>>> y := w + s; >>>>>> rolling_average <= y; >>>>>> end if; >>>>>> end process fraction_saving; >>>>>> >>>>>> >>>>>> As before, I haven't tested this; don't use it for anything that >>>>>> matters. >>>>> >>>>> I don't know the "fraction saving method", so I would have to work >>>>> pretty hard to understand this code. If it implements the same >>>>> algorithm as your first example it would not be an average. Your >>>>> initial did not divide by the number of samples and in fact didn't >>>>> keep track of the number of samples. I assume this one also doesn't >>>>> divide by the number of samples because I don't see where you keep >>>>> track of >>>> the >>>>> number of samples. That would explain why there is no division or >>>>> multiplication required to implement them. >>>> >>>> >>>> You might know fraction saving as error feedback, or perhaps delta >>>> sigma modulation. >>>> I recommend reading the Yates / Lyons paper. >>>> >>>> Based on the both the C code and the description, the OP seems to be >>>> asking for a filter with a u(t).exp(-t) >>>> shaped impulse response. >>>> One does not need to waste logic keeping track of the number of >>>> samples to do that. >>>> >>>> Of course, I may have interpreted the requirements wrongly. >>> >>> I believe you need to reexamine the original post and possibly others >>> as well. The OP is not designing a filter. He is implementing an >>> average that updates the result on every new sample arriving. >>> Wikipedia calls this the cumulative moving average. It looks a bit >>> like a filter because of the way he wrote the equations, but is >>> actually is just a simple average. Of course, in the strict sense >>> that is also a filter, but is simple enough that there is no need to >>> treat it as a complex filter. The code for this is just >>> >>> avg_num = avg_num + 1 ; >>> sum = sum + newValue ; >>> avg = sum / avg_num ; >>> >>> Throw this in a clocked process (or an always @ (posedge clk) block) >>> and you have your average. The only sticking point is a simple way to >>> calculate the divide. Of course there needs to be initialization code >>> and there may need to be code to restart the average if the OP needs >>> that. >>> >>> The division can be done fairly easily for small values of avg_num >>> using a look up table based on a block RAM. The reciprocal of avg_num >>> would be obtained by the lookup table and then multiplied by the sum >>> to get the average. I believe the OP indicated the max value of the >>> sample count is 8000 which should not consume too many resources in >>> most FPGAs for the reciprocal calculation. This avoids the >>> accumulated errors from the form that looks like a filter. >> >> >> Dang. That form looked so much like a single pole IIR filter I thought >> it was one. > > If the coefficients were constant, it would be the same. > > >> For the division, I was thinking that perhaps Newton-Raphson to find >> the reciprocal might work well, as the reciprocal at any input sample >> will be a good approximation for the next reciprocal, meaning that only >> a single N-R iteration per input sample should give an accurate result. >> Each N-R iteration would require two multiplications. > > That would likely work for later values, but do you think it can go from > a result of 1/2 to 1/3 in one iteration? I think it would be easy to > code so that you don't need to calculate the first coefficient which > would be 1 and initialize the "pipe" to give you 1/2 for the second > round. But after that you have to calculate the series 1/3, 1/4, etc.
N-R is renowned for slow convergence unless the initial guess is accurate. A single N-R round per sample (two multiplications) results in sub-100 ppm errors after about 100 samples. Doing two N-R rounds per sample (four multiplications) results in sub-ppm errors after 33 samples. I was going to suggest hard coding the first few results, as they won't be accurate. But if you're using a RAM to hold some results you might as well go the whole way...
> A look up table would give good accuracy and not take up too much space > in block RAM.
Given that a single (36k) block ram can store 2k x 18 bit reciprocals, that sounds like the best solution. Allan
On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote:

> All, thank you so much for all the feedback and explanations, I > appreciate it! > > I've read through everything once, but I think I need to go through it a > couple more times to make sure I am following everyone's > points/counterpoints. > > Some quick answers (so that you know that I didn't disappear into the > ether) is that I do need the output of rolling_average on every clock > (probably why the original C coder called it a rolling average even > though it isn't exactly acting like it).
As Rick pointed out, "Cumulative moving average" is probably a better name to use, if for no other reason than that's the name that everyone else uses.
> The rolling_average_num_max is an odd value to me. It is a constant > variable that will not change once the FPGA is built. What it looks like > to me is that rolling_average_num keeps track of the depth of window > average from [0,rolling_average_num_max]. So even in the beginning when > the average only has a few values in it, it will compute the average > based on that. Once the system has been running for a while, > rolling_average_num will always be equal to rolling_average_num_max, and > that variable in the averaging equation will be a constant. That make > sense?
Yes it does. The "pure" cumulative moving average would be optimal to estimate a value that never changes, but would eventually run into word- width limitations. Capping the divisor by a maximum will both help with the word-width limitations, and will allow the algorithm to follow a slowly moving (with respect to the sample rate) value. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On 10/1/2015 2:08 PM, Tim Wescott wrote:
> On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote: > >> All, thank you so much for all the feedback and explanations, I >> appreciate it! >> >> I've read through everything once, but I think I need to go through it a >> couple more times to make sure I am following everyone's >> points/counterpoints. >> >> Some quick answers (so that you know that I didn't disappear into the >> ether) is that I do need the output of rolling_average on every clock >> (probably why the original C coder called it a rolling average even >> though it isn't exactly acting like it). > > As Rick pointed out, "Cumulative moving average" is probably a better > name to use, if for no other reason than that's the name that everyone > else uses. > >> The rolling_average_num_max is an odd value to me. It is a constant >> variable that will not change once the FPGA is built. What it looks like >> to me is that rolling_average_num keeps track of the depth of window >> average from [0,rolling_average_num_max]. So even in the beginning when >> the average only has a few values in it, it will compute the average >> based on that. Once the system has been running for a while, >> rolling_average_num will always be equal to rolling_average_num_max, and >> that variable in the averaging equation will be a constant. That make >> sense? > > Yes it does. The "pure" cumulative moving average would be optimal to > estimate a value that never changes, but would eventually run into word- > width limitations. Capping the divisor by a maximum will both help with > the word-width limitations, and will allow the algorithm to follow a > slowly moving (with respect to the sample rate) value.
I'm not following what you mean by "capping". The OP sounds like he wants to use the max value in the calculations instead of the current value of the sample count. I don't see how that can produce anything useful. In fact, at that point it has become a low pass IIR filter. By "capping" do you mean to reset the count and sum and start a new average calculation? -- Rick
On Thu, 01 Oct 2015 15:52:09 -0400, rickman wrote:

> On 10/1/2015 2:08 PM, Tim Wescott wrote: >> On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote: >> >>> All, thank you so much for all the feedback and explanations, I >>> appreciate it! >>> >>> I've read through everything once, but I think I need to go through it >>> a couple more times to make sure I am following everyone's >>> points/counterpoints. >>> >>> Some quick answers (so that you know that I didn't disappear into the >>> ether) is that I do need the output of rolling_average on every clock >>> (probably why the original C coder called it a rolling average even >>> though it isn't exactly acting like it). >> >> As Rick pointed out, "Cumulative moving average" is probably a better >> name to use, if for no other reason than that's the name that everyone >> else uses. >> >>> The rolling_average_num_max is an odd value to me. It is a constant >>> variable that will not change once the FPGA is built. What it looks >>> like to me is that rolling_average_num keeps track of the depth of >>> window average from [0,rolling_average_num_max]. So even in the >>> beginning when the average only has a few values in it, it will >>> compute the average based on that. Once the system has been running >>> for a while, rolling_average_num will always be equal to >>> rolling_average_num_max, and that variable in the averaging equation >>> will be a constant. That make sense? >> >> Yes it does. The "pure" cumulative moving average would be optimal to >> estimate a value that never changes, but would eventually run into >> word- >> width limitations. Capping the divisor by a maximum will both help >> with the word-width limitations, and will allow the algorithm to follow >> a slowly moving (with respect to the sample rate) value. > > I'm not following what you mean by "capping". The OP sounds like he > wants to use the max value in the calculations instead of the current > value of the sample count. I don't see how that can produce anything > useful. In fact, at that point it has become a low pass IIR filter. > > By "capping" do you mean to reset the count and sum and start a new > average calculation?
https://en.wiktionary.org/wiki/cap#Verb, definition 5. Sorry if I'm being too obscure. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On 10/1/2015 4:27 PM, Tim Wescott wrote:
> On Thu, 01 Oct 2015 15:52:09 -0400, rickman wrote: > >> On 10/1/2015 2:08 PM, Tim Wescott wrote: >>> On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote: >>> >>>> All, thank you so much for all the feedback and explanations, I >>>> appreciate it! >>>> >>>> I've read through everything once, but I think I need to go through it >>>> a couple more times to make sure I am following everyone's >>>> points/counterpoints. >>>> >>>> Some quick answers (so that you know that I didn't disappear into the >>>> ether) is that I do need the output of rolling_average on every clock >>>> (probably why the original C coder called it a rolling average even >>>> though it isn't exactly acting like it). >>> >>> As Rick pointed out, "Cumulative moving average" is probably a better >>> name to use, if for no other reason than that's the name that everyone >>> else uses. >>> >>>> The rolling_average_num_max is an odd value to me. It is a constant >>>> variable that will not change once the FPGA is built. What it looks >>>> like to me is that rolling_average_num keeps track of the depth of >>>> window average from [0,rolling_average_num_max]. So even in the >>>> beginning when the average only has a few values in it, it will >>>> compute the average based on that. Once the system has been running >>>> for a while, rolling_average_num will always be equal to >>>> rolling_average_num_max, and that variable in the averaging equation >>>> will be a constant. That make sense? >>> >>> Yes it does. The "pure" cumulative moving average would be optimal to >>> estimate a value that never changes, but would eventually run into >>> word- >>> width limitations. Capping the divisor by a maximum will both help >>> with the word-width limitations, and will allow the algorithm to follow >>> a slowly moving (with respect to the sample rate) value. >> >> I'm not following what you mean by "capping". The OP sounds like he >> wants to use the max value in the calculations instead of the current >> value of the sample count. I don't see how that can produce anything >> useful. In fact, at that point it has become a low pass IIR filter. >> >> By "capping" do you mean to reset the count and sum and start a new >> average calculation? > > https://en.wiktionary.org/wiki/cap#Verb, definition 5. Sorry if I'm > being too obscure.
We aren't talking about dictionary definitions to describe signal processing events here. I asked what you meant by cap as a mathematical term exactly because the term is not clear in this context. If you don't wish to be clear, that's fine. -- Rick
On 10/1/2015 4:27 PM, Tim Wescott wrote:
> On Thu, 01 Oct 2015 15:52:09 -0400, rickman wrote: > >> On 10/1/2015 2:08 PM, Tim Wescott wrote: >>> On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote: >>> >>>> All, thank you so much for all the feedback and explanations, I >>>> appreciate it! >>>> >>>> I've read through everything once, but I think I need to go through it >>>> a couple more times to make sure I am following everyone's >>>> points/counterpoints. >>>> >>>> Some quick answers (so that you know that I didn't disappear into the >>>> ether) is that I do need the output of rolling_average on every clock >>>> (probably why the original C coder called it a rolling average even >>>> though it isn't exactly acting like it). >>> >>> As Rick pointed out, "Cumulative moving average" is probably a better >>> name to use, if for no other reason than that's the name that everyone >>> else uses. >>> >>>> The rolling_average_num_max is an odd value to me. It is a constant >>>> variable that will not change once the FPGA is built. What it looks >>>> like to me is that rolling_average_num keeps track of the depth of >>>> window average from [0,rolling_average_num_max]. So even in the >>>> beginning when the average only has a few values in it, it will >>>> compute the average based on that. Once the system has been running >>>> for a while, rolling_average_num will always be equal to >>>> rolling_average_num_max, and that variable in the averaging equation >>>> will be a constant. That make sense? >>> >>> Yes it does. The "pure" cumulative moving average would be optimal to >>> estimate a value that never changes, but would eventually run into >>> word- >>> width limitations. Capping the divisor by a maximum will both help >>> with the word-width limitations, and will allow the algorithm to follow >>> a slowly moving (with respect to the sample rate) value. >> >> I'm not following what you mean by "capping". The OP sounds like he >> wants to use the max value in the calculations instead of the current >> value of the sample count. I don't see how that can produce anything >> useful. In fact, at that point it has become a low pass IIR filter. >> >> By "capping" do you mean to reset the count and sum and start a new >> average calculation? > > https://en.wiktionary.org/wiki/cap#Verb, definition 5. Sorry if I'm > being too obscure.
Let *me* be clear on this. If you set an upper limit to just the divisor, you will have a corrupt calculation with a value that rises above the average. In other words you will have created an integrator, not a filter or an average. Is that what you are proposing? If I misunderstand, perhaps it would be better if you were more explicit. -- Rick
On
> > Is that what you are proposing? If I misunderstand, perhaps it would be > better if you were more explicit. > > -- > > Rick
I think what he means by capping is this: Lets say the cap =50. For the first 50 inputs, the output is the sum of all inputs / the number of inputs. (ie the average of all the inputs) Once you reach 50 inputs, the output is the sum of the LAST 50 inputs / 50. (ie the average of the last 50 inputs) I think this is how many instruments (scopes spec ans etc) implement averaging of the display where the user can select the cap value. Mark
On Thu, 01 Oct 2015 16:36:57 -0400, rickman wrote:

> On 10/1/2015 4:27 PM, Tim Wescott wrote: >> On Thu, 01 Oct 2015 15:52:09 -0400, rickman wrote: >> >>> On 10/1/2015 2:08 PM, Tim Wescott wrote: >>>> On Thu, 01 Oct 2015 08:11:38 -0500, garengllc wrote: >>>> >>>>> All, thank you so much for all the feedback and explanations, I >>>>> appreciate it! >>>>> >>>>> I've read through everything once, but I think I need to go through >>>>> it a couple more times to make sure I am following everyone's >>>>> points/counterpoints. >>>>> >>>>> Some quick answers (so that you know that I didn't disappear into >>>>> the ether) is that I do need the output of rolling_average on every >>>>> clock (probably why the original C coder called it a rolling average >>>>> even though it isn't exactly acting like it). >>>> >>>> As Rick pointed out, "Cumulative moving average" is probably a better >>>> name to use, if for no other reason than that's the name that >>>> everyone else uses. >>>> >>>>> The rolling_average_num_max is an odd value to me. It is a constant >>>>> variable that will not change once the FPGA is built. What it looks >>>>> like to me is that rolling_average_num keeps track of the depth of >>>>> window average from [0,rolling_average_num_max]. So even in the >>>>> beginning when the average only has a few values in it, it will >>>>> compute the average based on that. Once the system has been running >>>>> for a while, rolling_average_num will always be equal to >>>>> rolling_average_num_max, and that variable in the averaging equation >>>>> will be a constant. That make sense? >>>> >>>> Yes it does. The "pure" cumulative moving average would be optimal >>>> to estimate a value that never changes, but would eventually run into >>>> word- >>>> width limitations. Capping the divisor by a maximum will both help >>>> with the word-width limitations, and will allow the algorithm to >>>> follow a slowly moving (with respect to the sample rate) value. >>> >>> I'm not following what you mean by "capping". The OP sounds like he >>> wants to use the max value in the calculations instead of the current >>> value of the sample count. I don't see how that can produce anything >>> useful. In fact, at that point it has become a low pass IIR filter. >>> >>> By "capping" do you mean to reset the count and sum and start a new >>> average calculation? >> >> https://en.wiktionary.org/wiki/cap#Verb, definition 5. Sorry if I'm >> being too obscure. > > Let *me* be clear on this. If you set an upper limit to just the > divisor, you will have a corrupt calculation with a value that rises > above the average. In other words you will have created an integrator, > not a filter or an average.
No. Review your math. If you use any variation of the OP's code you'll start with a cumulative running average, and end up with a plain old 1st- order IIR lowpass.
> Is that what you are proposing? If I misunderstand, perhaps it would be > better if you were more explicit.
"Cap" = "set maximum limit", just like both the dictionary definition and the OP's post. I'm sorry that I confused things by using a different word. When I talked about "capping" I meant using exactly the algorithm as the OP described in C: you do all the math with his divisor variable, but you only allow the divisor equal the actual count up to the maximum value (i.e., the cap, because to most native English speakers "cap" and "maximum value" can easily mean the same thing), after which the divisor is held to the maximum value. By just about ever variation of the equation that he posted, the filter starts out as a cumulative running average, and then transitions to a simple linear IIR lowpass. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On 10/1/2015 5:26 PM, makolber@yahoo.com wrote:
> On >> >> Is that what you are proposing? If I misunderstand, perhaps it would be >> better if you were more explicit. >> >> -- >> >> Rick > > I think what he means by capping is this: > > Lets say the cap =50. > > For the first 50 inputs, the output is the sum of all inputs / the number of inputs. (ie the average of all the inputs) > > Once you reach 50 inputs, the output is the sum of the LAST 50 inputs / 50. > (ie the average of the last 50 inputs) > > I think this is how many instruments (scopes spec ans etc) implement averaging of the display where the user can select the cap value.
To do that you have to keep the last 50 samples and subtract out the one that falls off the end of the average and add in the new sample. With a block size of 8000 that would not be very practical, but I suppose it's possible. No bigger than the table look up I guess. -- Rick