anti-aliasing

J

John Larkin

Jan 1, 1970
0
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

John
 
T

Tim Wescott

Jan 1, 1970
0
John said:
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

John
Randomly varying the sample points will randomly vary any aliasing going
on, which means that instead of having a few really bad magic
frequencies you'll have only slightly bad, but broad magic frequency
ranges -- just how broad and how bad would depend on your sampling, and
could either be found by analysis or simulation.

If you couldn't live with the resulting error, sampling at two
distinctly different rates, overlapped, then checking for one of the set
of samples being aliased to DC may be more effective. The algorithm,
however, would be way more complex.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" gives you just what it says.
See details at http://www.wescottdesign.com/actfes/actfes.html
 
B

BobW

Jan 1, 1970
0
John Larkin said:
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

John

If the granularity of your "pseudo-random delay" is small enough, and you
take enough samples, then the answer will converge on the correct one (i.e.
no effects of aliasing will be seen).

How many samples to take as a function of delay granularity to give an
acceptable amount of error? That's the hard part.

You should probably repost on comp.dsp, or go to comp.arch.fpga and ask for
Ray Andraka's help. He's smarter than everyone here -- combined!

Bob
 
R

Robert Baer

Jan 1, 1970
0
John said:
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

John
Issue #1: What time period is going to be used for the sampling? If
the (presumedly) sine wave is of a sufficently low frequency WRT to full
sample time, then any given sub-section of that sine would be samples,
giving obvious errors.
Possible solution: trigger start and stop of sample period from
incoming signal, say at zero crossing + slope.
If it is a complex waveform signal, that might not be a useable solution.
Issue #2: if the input waveform is fast enough, the samples will
(again) represent only a small time section of the presumedly repeating
waveform, with the same obvious errors.
In either case, "small" dithering of the sampling periodicity will
not help; it might be useful when the input periodocity is near one of
the harmonics of the sampling rate.
For kicks, assume the nominal periods are the same; dithering would
only allow one to "slip" left ot right of the assumed synch point,
again achieving only part of the full waveform.
Hell, ASS-u-ME the sampling rate is *exactly* twice the input
waveform rate; i ask you the following nasty question: is it possible to
recover the input waveform? The answer might make one think that
Nyquist was a liar.
So.
If it is a given that the input frequency is unknown and possibly
wideband, then use two samplers, where the rates are decidedly NOT
harmonically related; maybe one sampling for a long time to recover low
frequency waveforms, and the other some generic high speed sampler for
comparison purposes.
Ratio? Maybe 11.5 if free-running (both stable, temp comp), possibly
to 31.5 (maybe more) if clocks generated from same crystal.
 
J

John Larkin

Jan 1, 1970
0
Issue #1: What time period is going to be used for the sampling? If
the (presumedly) sine wave is of a sufficently low frequency WRT to full
sample time, then any given sub-section of that sine would be samples,
giving obvious errors.

That's a separate issue. Obviously I need to sample for many cycles of
any waveform, to avoid getting just a slice. Let's assume I'll sample
for long enough to avoid that problem.
Possible solution: trigger start and stop of sample period from
incoming signal, say at zero crossing + slope.

Can't do that; my sample rate is generated in software and I have no
trigger hardware.
If it is a complex waveform signal, that might not be a useable solution.
Issue #2: if the input waveform is fast enough, the samples will
(again) represent only a small time section of the presumedly repeating
waveform, with the same obvious errors.

I don't understand that one. If the signal rate is well above the
sample rate, I'll be sampling over many cycles, so the only problem is
down-aliasing.
In either case, "small" dithering of the sampling periodicity will
not help; it might be useful when the input periodocity is near one of
the harmonics of the sampling rate.
For kicks, assume the nominal periods are the same; dithering would
only allow one to "slip" left ot right of the assumed synch point,
again achieving only part of the full waveform.
Hell, ASS-u-ME the sampling rate is *exactly* twice the input
waveform rate; i ask you the following nasty question: is it possible to
recover the input waveform? The answer might make one think that
Nyquist was a liar.

I'm not trying to recover the waveform. All I want to do is measure
its mean and the mean of its abs value.

In the 2:1 case, I'd always get a mean of zero, and an AC value (mean
of abs) that depends on the relative phases for that particular run.

So.
If it is a given that the input frequency is unknown and possibly
wideband, then use two samplers, where the rates are decidedly NOT
harmonically related; maybe one sampling for a long time to recover low
frequency waveforms, and the other some generic high speed sampler for
comparison purposes.

Sorry, I only have one adc. But if they differed, which one would you
pick to believe?
Ratio? Maybe 11.5 if free-running (both stable, temp comp), possibly
to 31.5 (maybe more) if clocks generated from same crystal.


I'm thinking that if I add a pseudorandom delay after each sample, so
that the sample period varies from the original to, say, twice as
much, the spectrum of the sampling impulses will be spread over about
a 2:1 range, so nothing can alias against it. It's a little easier to
think about if the added delays had a gaussian distribution, but
uniform might be OK too.

John
 
J

Jon Slaughter

Jan 1, 1970
0
John Larkin said:
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

John

I think you might be going about it the wrong way.

The DC terms are simply the integrals of the function.

i.e., for a signal f(t)

DC term = F(0) = int(f(t))
abs DC term = G(0) = int(|f(t)|)

If you cannot take the abs of a signal(by using rectification say, then you
can also do it by clipping.

F(0) + G(0) = int(f+(t))

where f+(t) is the positive terms of f(t)(which can be simply gotten by
rectification) and F(0) is simply determined by an integrator.

Seems like you can do all the hard work using an op amp or two and your adc
will simply digitize the result.

This even goes along the line that you should just LP your signal because
they do not effect the DC term. LP it enough so that your sample rate is 2x
the highest frequency. (I suppose there is an issue of practicality here if
your sample rate is extremely low... in that case just get a faster adc.)
 
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?

If you check the number of times each ADC output showed up - creating
a histogram - you could probably see when you were sampling at some
multiple or sub-multiple of the sampling frequency.

Enough completely random samples would give you an M-shaped histogram,
with every voltage interval between the peak and the minimum of the
sine wave eventually getting some hits. If your sampling is
synchronous (sampling at an exact multiple of the sine-wave frequency
or some integral fraction of the frequency), you won't get this
pattern, but will rather see a number of isolated peaks in the
histogram where the sampling hits at more or less the same phase time
after time.

If your sine wave amplitude could go to down to zero you wouldn't be
able to distinguish the single peak you'd see on the histogram from
the single peak that you'd get from perfectly synchronous sampling at
the same phase point, but you could check that by changing the
sampling frequency.

You haven't given enough detail for me to be sure that is would be a
practical approach, but it might give you what you seem to want.
 
F

Fred Bloggs

Jan 1, 1970
0
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Do you intend on using a sample zero mean, or closeness to it, as a
numerical criterion for confidence in the estimate of the mean of
abs(.)? Is there any analytical basis for this that you actually
understand? What range of confidence are you looking for? 75%? 90%?
99.9%? or what? Give us some numbers. There is a form of filtering
derived from principles of artificial intelligence that is more or less
applicable, it is called _____ _____ filtering and it's quite powerful.
Any ideas?

The very word "ideas" in this context actually means "guesses." That
would be in keeping with your methodology. The success rate will be a
function of something called the density of useful solutions...
 
V

Vladimir Vassilevsky

Jan 1, 1970
0
John said:
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

This will dither the error through the whole range. Although you will
avoid the worst cases, the average error will be higher.
Any ideas?

I would lock the sampling to the incoming frequency. That can be done
either by adjusting the sample rate directly or by resampling in the
software.


Vladimir Vassilevsky
DSP and Mixed Signal Design Consultant
http://www.abvolt.com
 
F

Fred Bloggs

Jan 1, 1970
0
Vladimir said:
This will dither the error through the whole range. Although you will
avoid the worst cases, the average error will be higher.



I would lock the sampling to the incoming frequency. That can be done
either by adjusting the sample rate directly or by resampling in the
software.

I'm pretty sure it is just a simplified version of sequential particle
filtering, and very simplified if he knows the input is sine, there are
no guesses about the form of the distribution whatsoever.
http://en.wikipedia.org/wiki/Particle_filter
 
J

John Larkin

Jan 1, 1970
0
This will dither the error through the whole range. Although you will
avoid the worst cases, the average error will be higher.

Right. It's sorta like a delta-sigma dac, where artifacts are
pulverized and scattered to become a broadband noise floor.
I would lock the sampling to the incoming frequency. That can be done
either by adjusting the sample rate directly or by resampling in the
software.

As I mentioned, the incoming frequency is unknown. I'm perfectly
willing to take, say, 1000 samples to get, say, 2% measurement
accuracy, if I can ensure that my sampling can't alias the sinewave
input. One of the checks I'm making on sinewave quality, and on the
signal chain producing it, is that there is actually a very small DC
offset. I'm not willing to run a bunch of trial runs and pick the best
one by some as-yet-unknown criterion; the BIST function I'm running
already takes 7 seconds to execute.

I was just wondering if the problem has come up before, and if anyone
had ideas or algorithms for sample time splattering that guaranteed no
aliasing.

John
 
J

John Larkin

Jan 1, 1970
0
Do you intend on using a sample zero mean, or closeness to it, as a
numerical criterion for confidence in the estimate of the mean of
abs(.)?

This is a BIST (self-test) function for a DDS synthesizer. Any
significant mean value would be an indicator that something is wrong
in the stuff that generates the sinewaves. That might be excess dc
offset, clipping, or distortion.


Is there any analytical basis for this that you actually
understand?

Certainly.

What range of confidence are you looking for? 75%? 90%?
99.9%? or what? Give us some numbers.

I'd like to verify that the sinewave amplitude is within, say, 2% of
my target (that's the system spec) and that a 20 volt p-p sine has
below, say, 200 mV of offset. If I don't have aliasing, I'm seeing
offset measurements in the 50 mV range. The BIST subsystem is pretty
simple: a relay diverts the signal from the customer connector into a
12-bit ADC, and the rest is all firmware, including the ADC triggers.


There is a form of filtering
derived from principles of artificial intelligence that is more or less
applicable, it is called _____ _____ filtering and it's quite powerful.

There's no filtering that can remove aliases once they're in the data.
The very word "ideas" in this context actually means "guesses." That
would be in keeping with your methodology. The success rate will be a
function of something called the density of useful solutions...

Where do you think new concepts come from? They come from generating a
great number of ideas, and riffs on those ideas, and filtering them
for quality. A filter with no input has no output.

I wouldn't expect ideas from you, because you are hostile to the
chaotic nature of the creative process. Some people are that way, and
can do good work in other ways, but they won't allow themselves to
design.

John
 
J

John Fields

Jan 1, 1970
0
Suppose one (actually, me) were firing an adc at some fixed rate,
ballpark 12 KHz in this case, and the input to the adc was a sine wave
of fixed but unknown frequency. The adc will take a bunch of samples,
ballpark 1000 maybe, and I want to compute the mean (ie, dc value) and
the mean of the abs value of the samples (ie, the ac value). That
works, but sometimes the input frequency aliases against the sample
rate and messes up the data, like gives a big average dc value when
there's really none there.

(This is not a Nyquist issue; the sample rate may be above or below
the sinewave frequency.)

So maybe I can fuzz up the sample rate so that it can't alias against
any constant sinewave frequency.

I could add a pseudo-random delay after every adc sample shot; but the
mean sample rate wouldn't change a lot. Or I could add successive
delays, essentially sweeping the sample rate down.

Any ideas?
 
T

Tim Wescott

Jan 1, 1970
0
-- snip --
Sorry, I only have one adc. But if they differed, which one would you
pick to believe?



I'm thinking that if I add a pseudorandom delay after each sample, so
that the sample period varies from the original to, say, twice as
much, the spectrum of the sampling impulses will be spread over about
a 2:1 range, so nothing can alias against it. It's a little easier to
think about if the added delays had a gaussian distribution, but
uniform might be OK too.
This should work. The added delays wouldn't have to be Gaussian, you'd
just want their spectrum to be fairly white.

As I pointed out in my response to your original post, though, this will
practically guarantee you some DC noise floor.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" gives you just what it says.
See details at http://www.wescottdesign.com/actfes/actfes.html
 
T

Tim Wescott

Jan 1, 1970
0
John said:
This is a BIST (self-test) function for a DDS synthesizer. Any
significant mean value would be an indicator that something is wrong
in the stuff that generates the sinewaves. That might be excess dc
offset, clipping, or distortion.


Is there any analytical basis for this that you actually

Certainly.

What range of confidence are you looking for? 75%? 90%?

I'd like to verify that the sinewave amplitude is within, say, 2% of
my target (that's the system spec) and that a 20 volt p-p sine has
below, say, 200 mV of offset. If I don't have aliasing, I'm seeing
offset measurements in the 50 mV range. The BIST subsystem is pretty
simple: a relay diverts the signal from the customer connector into a
12-bit ADC, and the rest is all firmware, including the ADC triggers.


There is a form of filtering

There's no filtering that can remove aliases once they're in the data.


Where do you think new concepts come from? They come from generating a
great number of ideas, and riffs on those ideas, and filtering them
for quality. A filter with no input has no output.

I wouldn't expect ideas from you, because you are hostile to the
chaotic nature of the creative process. Some people are that way, and
can do good work in other ways, but they won't allow themselves to
design.

John
Ooh. Testy.

Do you get to know what frequency the DDS is running at? If so, you
could adjust your sampling rate to match, so you got a good picture of
what it was doing. If the DDS always went fast enough, and the ADC
sample time could be set precisely enough, you could even build up
exactly one cycle of the output and analyze it for a heck of a lot more
than just DC content.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" gives you just what it says.
See details at http://www.wescottdesign.com/actfes/actfes.html
 
P

Phil Hobbs

Jan 1, 1970
0
Tim said:
Ooh. Testy.

Do you get to know what frequency the DDS is running at? If so, you
could adjust your sampling rate to match, so you got a good picture of
what it was doing. If the DDS always went fast enough, and the ADC
sample time could be set precisely enough, you could even build up
exactly one cycle of the output and analyze it for a heck of a lot more
than just DC content.
Another approach would be to do it twice, with sampling rate F and
F(1+1/N), where N is the number of samples. The lowest frequency that
aliases both of those to DC is (N+1)*F. If you use three frequencies
that are relatively prime, it'll be N**2 * F, which should be enough to
cover most uses. This has the advantage of relying only on theory you
can do in your sleep, which is important for a BIST routine.

Cheers,

Phil Hobbs
 
F

Fred Bloggs

Jan 1, 1970
0
John said:
This is a BIST (self-test) function for a DDS synthesizer. Any
significant mean value would be an indicator that something is wrong
in the stuff that generates the sinewaves. That might be excess dc
offset, clipping, or distortion.


Is there any analytical basis for this that you actually



Certainly.

What range of confidence are you looking for? 75%? 90%?



I'd like to verify that the sinewave amplitude is within, say, 2% of
my target (that's the system spec) and that a 20 volt p-p sine has
below, say, 200 mV of offset. If I don't have aliasing, I'm seeing
offset measurements in the 50 mV range. The BIST subsystem is pretty
simple: a relay diverts the signal from the customer connector into a
12-bit ADC, and the rest is all firmware, including the ADC triggers.


There is a form of filtering



There's no filtering that can remove aliases once they're in the data.




Where do you think new concepts come from? They come from generating a
great number of ideas, and riffs on those ideas, and filtering them
for quality. A filter with no input has no output.

I wouldn't expect ideas from you, because you are hostile to the
chaotic nature of the creative process. Some people are that way, and
can do good work in other ways, but they won't allow themselves to
design.

John

Well if your system is generating the sine wave then how does it come to
pass that you don't know the frequency? If you're admitting the
possibility of a harmonic or subharmonic output, what makes you think it
will even be constant or a sine. You may need some analog to steer your
processing in the large...like a filter bank or a voltage controlled
variable center frequency band pass or something or IF swept mixer
thing, dunno.

-mystified
 
J

John Larkin

Jan 1, 1970
0
Ooh. Testy.

Well, Fred keeps insulting me because I have ideas and design things.
He seems to think that the initial stages of a design, the explore
possibilities phase, is impure and beneath his dignity.

I've known lots of people who couldn't design because they feel
insecure in the early stages, the deliberate uncertainty phase. These
people, if thet are responsible for design (which they shouldn't be)
tend to look for well-analyzed prior art, and if they can't find any,
usually seize onto the first clumsy concept they can come up with, and
then settle into brute-force analysis and implementation of a bad
idea. Design is a creative, chaotic, psychological process that
uptight people don't like and seldom respect.

Do you get to know what frequency the DDS is running at? If so, you
could adjust your sampling rate to match, so you got a good picture of
what it was doing. If the DDS always went fast enough, and the ADC
sample time could be set precisely enough, you could even build up
exactly one cycle of the output and analyze it for a heck of a lot more
than just DC content.


When I run autonomous BIST, I test a range of frequencies. I suppose I
could fine-tune the sample rate, or the test frequencies, on a
case-by-case basis, or do more number crunching and find one magical
sample rate that won't alias *any* of my test frequencies. But there's
another mode where the customer can program a waveform and ask for it
to be measured (which we also use in testing) so it would be cool to
have a general solution.

John
 
J

John Larkin

Jan 1, 1970
0
Say, 2 KHz to 1 MHz. The flat-out sample rate is 12.4 KHz (software
loop, bit-banging a 12-bit serial ADC) which I can slow down on a
per-sample basis, randomly or swept or whatever. I usually take about
1000 samples and average the mean and the software-rectified mean.

John
 
J

John Larkin

Jan 1, 1970
0
Another approach would be to do it twice, with sampling rate F and
F(1+1/N), where N is the number of samples. The lowest frequency that
aliases both of those to DC is (N+1)*F. If you use three frequencies
that are relatively prime, it'll be N**2 * F, which should be enough to
cover most uses. This has the advantage of relying only on theory you
can do in your sleep, which is important for a BIST routine.

Cheers,

Phil Hobbs

For N=1000, (1+1/N) is only 1.001. That won't move an alias very far.
The alias doesn't have to be to DC to mess me up, it just has to be to
a low frequency relative to my total sampling run duration.

If I did it twice, which measurement would I trust? Not the one with
the lowest DC offset, because I'm trying to measure offset.

Even three runs (which would take a lot of time) would be tricky...
would I throw out the one that has the highest DC offset, and average
the other two? Or pick the two that agree best?

Too much work!

John
 
Top