Author Topic: Idea for improving LTDZ Spectrum Analyzer & Tracking Generator 35MHz-4.4GHz  (Read 24279 times)

0 Members and 1 Guest are viewing this topic.

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
Is my assumtion correct that the numbers are the (signed) 12-bit ADC readings, multiplied by 16 to bring them into a signed 16-bit range?
I notice that the numbers modulo 16 are not zero. Is there alrady an offset added/subtracted?
I guess ADC reference voltage is 3.3V, is this correct?
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
I guess the same question applies to the Goertzel-detector as well? As Goertzel has a very narrow bandwidth, its impulse response will be very long too (actually it is a resonator as the pole is on the unit circle), thus its output will rise slowly until it reaches the steady state. So far I haven't seen any information which says that Goertzel-detector cannot be used to detect signals that are shorter than the Goertzel's impulse response or rise time. For the shorter signals the Goertzel's output power will be less than the maximum available after steady state, though. Please correct me if I am wrong here.

The idea is to use a bandpass IIR-filter to process the sampled signal buffer (4096 samples), and compute the RMS (energy) of the filter output over the all samples processed: kind of RMS-detector with a filter applied to its input signal. At least this seems to work in practice, but I do not know whether this works in theory.

Edit: Changed wording.

Edit 2: I do understand that when using a shorter signal than IIR filter's impulse response, the filter will not reach the steady-state. Now, if we change filter parameters (center frequency or bandwidth), the output energy between two filters will be different due to different rise-times, and it is necessary to compute a correction/calibration factor for each filter to be used. Since we are working in a digital domain, computing these calibration factors is quite trivial, though.

A regular FIR filter is based on linear convolution, which is rather supposed to be applied to an continuous infinite stream of samples. If you instead apply linear convolution to a finite number of samples, then steady state is reached only after the the length of the filter's impulse response, and the leading filtered samples are "garbage". For IIR, baiscally the same applies, but the impulse response length is actually infinite, so an arbitrary end of the impulse needs to be defined, at a point where it returns "close enough" to zero.

Goertzel is under the hood a DFT, calculated for only a single frequency (or a snapshot of a STFT, calculated for a single chunk of samples at a particular point in time, and calculated only for a single frequency).

DFT treats the samples as if they were circular. The window function smooths the wrap-around discontinunity between end and start, reducing spectral leakage.

But a DFT can be also interpreted as filter bank. According to the filter bank interpretation, the chosen window function is the impulse response of a prototype low-pass filter, which is under the hood converted to a bandpass and applied to each DFT frequency bin (see previous link). The DFT window has always the same size as of the number of samples, it cannot be longer. While there exist various commonly used window functions (Hann, Hamming, Kaiser,...), basically any FIR lowpass with N taps (where N is the DFT size -- number of samples) could be used as window function in order to give the filter bank the desired frequency response (of course, if you need special properties like "constant overlap add", this may limit the choice of suitable window functions, but this is not an issue here).

A window function with 4000 "FIR taps" (for a 4000 point DFT) is already quite a large number, thus enabling already a pretty narrow bandwidth. But the minimum feasible bandwidth is eventuall limited by the number of samples. And my feeling is that there is a trade-off between stop-band rejection (-> power of out of band frequencies leaking to the filter output) and ENBW (equivalent noise bandwidth -> i.e. noise power picked up inside the passband).

Integrating the power of the band-pass filtered signal is certainly a valid procedure (granted that the band-pass filtering is valid in the first place). Advantage of Goertzel is the implied bandpass filter, at low computational cost, and it collects both, amplitude and phase, so that phase differences between subsequent readings can be measured. Phase measurements are more sensitive to noise than the amplitude measurements, though. For phase confidence of 1° (standard deviation), you need an effective SNR of better than 30dB.

Quote
The effects of filter's coefficient quantization and available numeric range needs to be considered carefully as well if wanting to achieve very narrow filters. If the ratio of the filter's center frequency or filter's 3dB point relative to the sampling frequency (800+ kHz) is very small, is might be practical/necessary to perform some decimation prior filtering in order to get the filter coefficients into practical numeric range.

Sure, numeric ranges need to be planned carefully. I tried to check the effect of quantizaiton. Quantizing the coefficients of a 4k point Kaiser window to 16 bits seems to reduce the window's stop band attenuation to ~120dB. The question is whether this is enough or whether more than that is required? 32-bit Q31 arithmetic should not be a problem for the cortex M3. The accumulator can also be 64 bits if necessary. For real-time processing there are less than 84 cycles per sample available, which rather rules out too complex filtering - as you said yourself. Even a decimation filter with good stopband attenuation might be already too expensive. Goertzel applied to the undecimated data is computationally not so expensive, so it seems well feasible, OTOH.

Btw, could you post the raw data from the previous test?

Edit: You may be interested in this paper, too.

I would like to thank you very much once again for your excellent comments and insight for practical digital signal processing, signal analysis and filter design.  :-+

I wasn't really aware that in order to apply Goertzel in properly, it is necessary to apply windowing function to the samples before computing Goertzel. Now, thinking in terms of DFT, it is more obvious that it is important to apply windowing function in order to get good results. If it could be guaranteed that the received rx signal is always integer multiple of the block size to be analyzed, this would be easier as no windowing would be required. Since the MCU's ADC clock and the ADF4351's tg/rx may be a little out of sync, windowing function is required.

Applying a windowing function to the samples before computing a Goertzel begins to feel quite expensive operation in terms of Flash memory and computation cycles. If the block size is 4096 samples, the length of the windowing lookup-table needs to be also 4096 samples. In practice we can reduce the size of the windowing table into half, if we just make sure that the windowing function is symmetrical. Now, for each sample there will be a need to compute one multiply + one Goertzel-loop.

On the other hand, we could probably keep things very simple and just create a FIR filter with 4096 taps instead, so that each sample would require one multiply + one addition. This would be less expensive than windowing + Goertzel-loop for each sample. Making sure that the coefficients of this FIR filter would be symmetrical, the size of the lookup-table can be reduced to 2048. The FIR filter needs to be computed only once for each sample buffer of 4096 samples, so there won't be any problems with reaching the steady state. The only real concern is how many bits are required for the filter coefficients to achieve  90dB-100dB dynamic range for the FIR filter of 4096 taps.  Selecting the rx lo frequency wisely will reduce the precision requirements for the coefficients in order to get the wanted dynamic range.

I have playing with a fixed-point implementation in C of the Goertzel. So far I have not been able to achieve full dynamic range of 96dB using 16 bits for the feed-back coefficients, and it seems that more bits are required for the coefficients. Implementing a Goertzel, or more general IIR bandpass filter using cascaded 2nd order sections, the precision of the coefficients is going to be critical if the ratio of the filter's center frequency and/or bandwidth to the Nyquist freqeuency is very small. Life would be too easy if the MCU would have a FPU even with single-precision floats (with some 24 bits for the mantissa).
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Is my assumtion correct that the numbers are the (signed) 12-bit ADC readings, multiplied by 16 to bring them into a signed 16-bit range?
I notice that the numbers modulo 16 are not zero. Is there alrady an offset added/subtracted?
I guess ADC reference voltage is 3.3V, is this correct?

Yes, the 12-bit ADC samples are left-aligned to 16 bits, and then the computed mean of the sample buffer is subtracted from each sample, effectively removing the ADC offset voltage (approx. 1.5V - 1.6V).

The MCU is running from 3.3V, so the ADC offset voltage needs to be somewhere near the midpoint of this 3.3V. The output amplitude of the rx mixer is quite small, so the current hardware design cannot fully utilize the full dynamic range of the 12-bit ADC. It could be possible to better match the dynamic range of the 12-bit ADC to the mixer output signal amplitude if the ADC's reference voltage would be reduced.
« Last Edit: June 05, 2021, 02:12:43 pm by Kalvin »
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
On the other hand, we could probably keep things very simple and just create a FIR filter with 4096 taps instead, so that each sample would require one multiply + one addition.

I'm afraid you misunderstand the computational complexity of a FIR filter. I costs one multiply and one add per sample and per tap! I.e. computational complexity is O(N*M), where N is the number of samples, and M is the number of taps. Goertzel, OTOH, is only O(N) where N is the number of samples; it costs only 3 multiply and 2 add per sample (granted that sin/cos and window come from pre-calculated tables). If window and sin/cos tables are combined, then one multiply per sample can be saved.

Edit:

Quote
Since the MCU's ADC clock and the ADF4351's tg/rx may be a little out of sync, windowing function is required.

Frequency offset (in your sample data) seems to be roughly 0.22Hz. Likely still close enough for a rectangular window (i.e no window function) if the noise were white. But it isn't, and there are also a couple of spurs in the spectrum which need to be eliminated. So I tend to use a Hamming window whose ENBW is still as low as 1.36 bins, while out of band rejection is much better. That's based on the given sample data, frequency plan, and a block size of 4000 samples. For a different configuration or different noise floor spectrum this may no longer apply.

I also wonder whether the noise floor spectrum looks always the same, or whether it changes, for instance when you tune the ADF4351s to different frequencies. In particular I don't know what's the origin of the spurs. Furthermore I wonder what's the apparent elevated noise near DC. Is it 1/f noise, or does the spectrum contain any deterministic components in this region, too? At least it is not helpful for SA use case if a low bandwidth is desired :(

Edit:

Quote
Applying a windowing function to the samples before computing a Goertzel begins to feel quite expensive operation in terms of Flash memory and computation cycles. If the block size is 4096 samples, the length of the windowing lookup-table needs to be also 4096 samples.

20k RAM is indeed pretty limited. But isn't quite some amount of flash memory available for a couple of tables?

Edit:

Quote
The output amplitude of the rx mixer is quite small, so the current hardware design cannot fully utilize the full dynamic range of the 12-bit ADC. It could be possible to better match the dynamic range of the 12-bit ADC to the mixer output signal amplitude if the ADC's reference voltage would be reduced.

If I understand correctly, the 48 pin package variant of the STM32F103 unfortunately does not expose the reference voltage, so it cannot be changed. Alternative would be a (variable gain?) IF amplifier. But what I wonder in the first place is the origin of the noise. Is it already present in the IF coming out from the mixer, or is it ADC noise? If it is already present in the IF, then amplification won't help much.
« Last Edit: June 05, 2021, 03:47:03 pm by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
On the other hand, we could probably keep things very simple and just create a FIR filter with 4096 taps instead, so that each sample would require one multiply + one addition.

I'm afraid you misunderstand the computational complexity of a FIR filter. I costs one multiply and one add per sample and per tap! I.e. computational complexity is O(N*M), where N is the number of samples, and M is the number of taps. Goertzel, OTOH, is only O(N) where N is the number of samples; it costs only 3 multiply and 2 add per sample (granted that sin/cos and window come from pre-calculated tables). If window and sin/cos tables are combined, then one multiply per sample can be saved.

I was thinking that since we have 4096 samples of data available (and there won't be any more data available for the given sweep), computing only one output sample for the band-pass FIR (ie. 4096 MAC-operations) should be enough to give the power estimate of the signal at the specific center frequency of this band-pass FIR. As far as understand, that operation would be similar to correlation between two signals.

However this concept is doomed to fail because the phases of two signals do not match, thus there would be a need to slide the buffer one sample at a time, compute the correlation again for each step performed, until the buffer has rotated for a half cycle of the signal to be detected, and get the maximum of the absolute values from the individual correlations. As the unknown phase difference between the signals is a problem, it should be possible to perform kind of quadrature correlation of the signals.

And yes, the concept would be similar to this one you described earlier:
One thing which does indeed not fit well is the combination of the AC coupled filter with the zero-bandwidth CW signal and a zero-IF receiver. Since the filter has a null at DC it can't detect the zero bandwidth signal which is down-mixed to exactly DC. Therefore I'd rather pick a non-zero IF for the NA use case (i.e. add an offset to the receiver LO). NanoVNA V2.2 for instance uses 12kHz IF, which is sampled @300kSa/s by the MCU , and 50 samples (two 12kHz periods) are correlated with a windowed complex sine wave to obtain one measurement (btw, since the NanoVNA is a VNA, the ADC needs to run continuously in order not to lose phase in the pause when the receiver is switched to a different signal source).

So, I am not sure any more whether is there a real need for a FIR filter or sine/cosine look-up table any more, because we could use a quadrature oscillator for the correlation. Then again, without proper frequency lock between ADF4351 and MCU's ADC clock, there will be a slight frequency error, and the correlation would not be be identical from one measurement to another due to real-world frequency drift between the clocks. That's why I was thinking that a simple band-pass filter would be nice because it would tolerate a slight frequency error. But since we have only 4096 samples available, and the IIR/FIR will reach the steady-state only after ....  |O
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Quote
Applying a windowing function to the samples before computing a Goertzel begins to feel quite expensive operation in terms of Flash memory and computation cycles. If the block size is 4096 samples, the length of the windowing lookup-table needs to be also 4096 samples.

20k RAM is indeed pretty limited. But isn't quite some amount of flash memory available for a couple of tables?

There is 64KB of Flash available, and currently the size of the firmware is quite small, so Flash won't be a problem at this point. Also many operations like windowing can be performed in-place, so no extra RAM is required. For 4096 samples the required RAM size is 8KB.

Quote
The output amplitude of the rx mixer is quite small, so the current hardware design cannot fully utilize the full dynamic range of the 12-bit ADC. It could be possible to better match the dynamic range of the 12-bit ADC to the mixer output signal amplitude if the ADC's reference voltage would be reduced.

If I understand correctly, the 48 pin package variant of the STM32F103 unfortunately does not expose the reference voltage, so it cannot be changed. Alternative would be a (variable gain?) IF amplifier. But what I wonder in the first place is the origin of the noise. Is it already present in the IF coming out from the mixer, or is it ADC noise? If it is already present in the IF, then amplification won't help much.

Yes, you are right that 48 pin package doesn't allow changing the ADC reference voltage. In the spirit of minimal hardware modifications, it could be possible to add an op amp [for example in a 5-pin SOT-23 package] so that the mixer/ RBW-filter's output amplitude would better match with ADC's input signal range. There are some op amps in SOT-23-5 package with fixed gain, so they would not even require external resistors for gain setting. At this point I am not too eager to do any other hardware modifications other than trying to adding extra bypass capacitors to the power supply rails. Like you have written above, there are some noise spurs that needs some investigation.
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
When using the device in network analyzer mode, we can pretty much select any convenient rx frequency offset we like. I have used 15 kHz rx offset in my measurements, although this choice has been more or less random (actually the original intention was to use 10kHz frequency offset, but due to error in the original ADF4351 configuration code, the offset became 15kHz instead), and it was based on assumption that there would be more noise closer to 100 kHz. As it has now turned out, the noise level will rise near 0Hz, so the frequency offset may not be too low either.

I will experiment adding extra bypass capacitors into power supply rails tomorrow, and see how they will affect the spurs/noise.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
So, I am not sure any more whether is there a real need for a FIR filter or sine/cosine look-up table any more, because we could use a quadrature oscillator for the correlation.

But it is exactly the (digital) quadrature oscillator, which requires the sin/cos lookup table (note, DFT contains under the hood a quadrature oscillator for each frequency bin, and Goertzel contains a quadrature oscillator for the frequency to be detected, too).

Quote
Then again, without proper frequency lock between ADF4351 and MCU's ADC clock, there will be a slight frequency error, and the correlation would not be be identical from one measurement to another due to real-world frequency drift between the clocks.

As long it is less than 1Hz it is negligible, at least for amplitude estimation. Phase measurements may need to be adjusted.

Quote
That's why I was thinking that a simple band-pass filter would be nice because it would tolerate a slight frequency error. But since we have only 4096 samples available, and the IIR/FIR will reach the steady-state only after ...

You are too much fixated on a conventional filter which is supposed to be applied to a stream ;). A Goertzel detector acts as bandpass, too, and while its bandwidth is indeed relatively narrow, it is not zero either. So it does tolerate a small frequency error, too. What's the difference at the end?

Quote
Also many operations like windowing can be performed in-place, so no extra RAM is required. For 4096 samples the required RAM size is 8KB.

Goertzel processing needs to retain only a complex-valued accumulator from sample to sample (i.e. just 8 or 16 bytes), and the index of the current sample in the current chunk. Buffers are only required for double-buffered DMA. Granted that the real-time processing is fast enough, the two buffers do not even need not to be very big, but just large enough to avoid the interrupt overhead for each single sample. Even (say) 100 samples per buffer may be enough to hide 90% or more of the interrupt overhead.

Edit: For 15kHz IF and current sampling rate, sin/cos table for the digital LO needs only 400 entries.

Quote
I have playing with a fixed-point implementation in C of the Goertzel. So far I have not been able to achieve full dynamic range of 96dB using 16 bits for the feed-back coefficients, and it seems that more bits are required for the coefficients.

I do not understand. Goertzel is not IIR and has no feedback :-// A window function table scaled to 0...65535 range (16 bits integer) seems to suffice for the noise floor of the provided sample data. I'd need to verify whether this applies to the sin/cos table, too.

I was basically thinking of something like this: https://godbolt.org/z/he6Kr5e73 (not tested). Gcc generates 9 instructions for the hot loop.
Code assumes that DMA collects double-buffered chunks of 400 samples each, which are processed while the other buffer fills-up in the background.

Edit: updated code
« Last Edit: June 05, 2021, 10:32:24 pm by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Quote
I have playing with a fixed-point implementation in C of the Goertzel. So far I have not been able to achieve full dynamic range of 96dB using 16 bits for the feed-back coefficients, and it seems that more bits are required for the coefficients.

I do not understand. Goertzel is not IIR and has no feedback :-// A window function table scaled to 0...65535 range (16 bits integer) seems to suffice for the noise floor of the provided sample data. I'd need to verify whether this applies to the sin/cos table, too.

You are pulling my leg here :) I was referring to this generally known IIR-implementation of the Goertzel algorithm which will generate the required sinusoidal wave without lookup-table (taken from http://www.mstarlabs.com/dsp/goertzel/goertzel.html which explains also why windowing is important and required in practice):

Code: [Select]
realW = 2.0*cos(2.0*pi*k/N);
imagW = sin(2.0*pi*k/N);

d1 = 0.0;
d2 = 0.0;
for (n=0; n<N; ++n)
  {
    y  = x(n) + realW*d1 - d2;
    d2 = d1;
    d1 = y;
  }
resultr = 0.5*realW*d1 - d2;
resulti = imagW*d1;

Of course you can generate a lookup-table for the sine and cosine for the given frequency, and use a quadratic correlator for signal detection. Your suggested implementation will produce numerically stable implementation, but requires lookup-tables for sine and cosine. Since the Flash memory is not an issue at the moment, your implementation is very favorable indeed.

Quote
I was basically thinking of something like this: https://godbolt.org/z/he6Kr5e73 (not tested). Gcc generates 9 instructions for the hot loop.
Code assumes that DMA collects double-buffered chunks of 400 samples each, which are processed while the other buffer fills-up in the background.

Your implementation is very clean and straightforward.  :-+ As your initial analysis suggests, your power estimator algorithm should be able to run in real-time, thus only short DMA buffers required.

At this point I am ready to sacrifice more RAM in order to be able to capture all samples for one sweep step into a buffer (4096 samples, 8KB per buffer) before running the power estimator. In this way it is also possible to download the captured raw data to PC, which will help analyzing the signals using Matlab/GNU Octave, and provide means for developing the signal processing algorithms on PC. Using two 4096 sample buffers (one buffer for capturing new data, the other for holding the samples from previous sweep step and used for computing the power estimation) would provide almost real-time performance as well, at the expense of requiring 16KB of RAM for the buffers. Because the firmware is using currently very little RAM (the implementation is just a simple bare-metal super-loop without any tasks or OS), this extra RAM requirement will not be a problem at the moment.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
... but requires lookup-tables for sine and cosine. Since the Flash memory is not an issue at the moment, your implementation is very favorable indeed.

The big table is rather the tabulated window function (8000 bytes).
The sin/cos tables are only 1600 bytes (together). Unfortunately sin/cos table can't be shared, due to the involved prime factor 7 (which comes from the "odd" samling rate of 12/14 MSa/s).

Code: [Select]
costab[i] = floor(32767 * cos(i / 400.0 * 2 * pi * 7) + 0.5)
sintab[i] = floor(-32767 * sin(i / 400.0 * 2 * pi * 7) + 0.5)

Quote
At this point I am ready to sacrifice more RAM in order to be able to capture all samples for one sweep step into a buffer (4096 samples, 8KB per buffer) before running the power estimator. In this way it is also possible to download the captured raw data to PC, which will help analyzing the signals using Matlab/GNU Octave, and provide means for developing the signal processing algorithms on PC. Using two 4096 sample buffers (one buffer for capturing new data, the other for holding the samples from previous sweep step and used for computing the power estimation) would provide almost real-time performance as well, at the expense of requiring 16KB of RAM for the buffers. Because the firmware is using currently very little RAM (the implementation is just a simple bare-metal super-loop without any tasks or OS), this extra RAM requirement will not be a problem at the moment.

Sure, for offline analysis it is desired to have the complete (long) buffer available. A streaming detector OTOH allows to collect even more samples than available RAM (-> say 40000, instead of only 4000, or maybe even 400000, in order to dig even deeper into the noise floor). Btw, for the given frequency plan, block size should not be a power of two, but be rather an integral multiple of 400 (so that a block contains an integral number of 15kHz cycles).

Edit: Sorry, mistake. Number of collected samples needs to fit with the length of the window function. So collecting more samples than available memory were only possible if the tabulated window function would be interpoloated, or with a rectangular window.
« Last Edit: June 06, 2021, 10:43:21 am by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
... but requires lookup-tables for sine and cosine. Since the Flash memory is not an issue at the moment, your implementation is very favorable indeed.

The big table is rather the tabulated window function (8000 bytes).
The sin/cos tables are only 1600 bytes (together). Unfortunately sin/cos table can't be shared, due to the involved prime factor 7 (which comes from the "odd" samling rate of 12/14 MSa/s).

It is stated in STM32F103 datasheet that the ADC clock may not exceed 14 MHz. Currently the MCU's PLL is configured so that the MCU's clock is 72MHz (9*8MHz), and the ADC clock is 72MHz/6 = 12 MHz. As each ADC conversion requires 14 clock cycles, the Fs = 12 MHz / 14 = 857142.857142...Hz.

But we do not have to use 72 MHz clock. We can choose another MCU clock, such as 56MHz (7*8MHz). In this case the ADC clock can be set to 56MHz/4 = 14MHz, thus the ADC sample rate would become 1MHz exactly. The penalty in this case is that the MCU is running a bit slower.

In the original firmware source code it is said that the minimum ADF4351 frequency step size varies from 125Hz to 8kHz, depending on the output frequency: Above 2200MHz the minimum step size is 8kHz. The two ADF4351s are using the same 25MHz oscillator for their reference timebase, so their frequencies and phases are locked. I have not checked the ADF4351 datasheets what is the actual minimum step size at 2200MHz and above, though. Let's take for granted that the common minimum step size is 8kHz for all allowed frequencies between 35 MHz - 4400 MHz.

That means that the rx frequency offset needs to be N*8kHz. As the noise level increases close to 0Hz, selecting N=1 is not terribly good idea. On the other hand, the upper limit for N due to 120kHz RBW lowpass filter is 120kHz / 8kHz = 15. Basically we are free to choose whatever N between 2 and 15, but it is important to check that the rx offset won't end up in the spectrum which contains spurs or high noise level.

Edit: ADF4351 is quite flexible fractional-PLL, so it should be possible to use smaller step sizes than 8kHz even above 2200MHz. However, I would not like to open a can of worms at this point, so let's assume that 8kHz frequency step size is the common minimum steps size for frequency range 35MHz - 4400MHz.
« Last Edit: June 06, 2021, 12:19:04 pm by Kalvin »
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
The IF frequency must be an integral multiple of the step size too, in order that TG and LO can be tuned to the required frequency offset. So when 8kHz step size is chosen, the IF frequency could be 8kHz, 16kHz, 24kHz, etc. 24kHz does not look that bad; there seems to be a relatively spur-free region in the noise spectrum (unless the spurs would happen to change, when the frequency plan is changed or when the oscillators are swept -- I dont' know, this needs to be investigated). It's also not yet clear whether the observed spurs come from the ADF4351, or whether they have a different origin. ADF4351 also has a low-spur mode, which trades-off spurs agains phase noise. Is this mode enabed or disabled? [However, as long the spurs don't collide with the IF - at any TG/LO tuning frequency - they are not really a problem, and lower phase noise is rather preferred.] In relation to the sampling rate, seven 24kHz periods correspond to 250 samples, so granularity of buffer sizes (and thus sin/cos table size) were even lower than now, enabling also smaller tables.

Lowering the MCU clock speed would impact the CPU power available for real-time processing, unfortunately. And reducing the sampling rate slows down the measurements, and the anti-aliasing filter would require a lower cut-off as well. So better keep it as is if possible. I'm just a bit unsure whether the ADC's T&H sampling time of 1.5 ADCCLK cycles is suffucient, given that the filter output is not buffered - see AN2834, chapter 4.4. Admittedly I do not fully understand the complex interaction between the IF filter and the switched capacitor ADC input (even AN2834 says "A mathematical model is not accurate enough given the number of parameters and their non-linear characteristics. Only a complex design simulation can provide a very good estimate of the minimum TSMPL duration in various conditions.").

Quote
... due to 120kHz RBW lowpass filter ...

Btw, if the zero-IF filter's low-pass cut-off is 120 kHz, then the equivalent bandpass RBW (which applies to the received RF signal) is actually 2 * 120 = 240kHz.
« Last Edit: June 07, 2021, 05:13:12 am by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
The IF frequency must be an integral multiple of the step size too, in order that TG and LO can be tuned to the required frequency offset.

Sure, this is not a problem as the current minimum sweep step sizes are 8kHz, 4kHz, 2kHz, 1kHz, 500Hz, 250Hz and 125Hz, depending of the tg and rx lo frequency. 
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
I have now added extra 22uF ceramic decoupling capacitors to the 3V3 power supply lines (at the outputs of the two AMS1117 linear voltage regulators), and 10uF ceramic capacitor to the MCU VDDA voltage net. The device noise level seen by ADC has reduced quite a bit, and many of the extra spurs have mostly gone. There are still some spurs above 100kHz, but those can be filtered out with a digital filter.

Currently the ADC bias voltage (approx. 1.5V-1.6V) is derived from the AD8307 LOG-detector +5V power supply, which happens to be also the USB +5V with only simple LC-filter applied. I will change that in the next step. Hopefully the noise floor will improve a little, and at least some of the remaining spurs will be reduced.

I recreated the 15kHz test signals again with the same sample rate and attenuation steps as before, and the signals can be found as an attachment. Both ADF4351s were using low spur mode, as was the case with the previous signal sets. The file names contain attenuation used. File with name noisefloor is measured when tg was off giving baseline for the board's noise floor. File name noloop is measured when tg on but no loop connected (for measuring on-board signal crosstalk from tg to rx). The signal with 0dB attenuation may just start overdriving the rx, but the signal with 10dB attenuation is clean.
« Last Edit: June 07, 2021, 11:10:34 am by Kalvin »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Changing the ADC bias voltage power supply improved the board's noise floor a little. Here is the current device noise floor when tg is off (attachment #1), and when tg is on with 16dB attenuation between tg output and rx input (attachment #2). I think the hardware modifications are now successfully completed, and the next step is to proceed with the firmware itself implementing the required power detector for the network analyzer use-case, and the digital low-pass RBW-filter with adjustable bandwidth for the spectrum analyzer use-case.
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Lowering the MCU clock speed would impact the CPU power available for real-time processing, unfortunately. And reducing the sampling rate slows down the measurements, and the anti-aliasing filter would require a lower cut-off as well. So better keep it as is if possible. I'm just a bit unsure whether the ADC's T&H sampling time of 1.5 ADCCLK cycles is suffucient, given that the filter output is not buffered - see AN2834, chapter 4.4. Admittedly I do not fully understand the complex interaction between the IF filter and the switched capacitor ADC input (even AN2834 says "A mathematical model is not accurate enough given the number of parameters and their non-linear characteristics. Only a complex design simulation can provide a very good estimate of the minimum TSMPL duration in various conditions.").

(Bolding is mine) The output impedance of the new RBW-filter is designed to be 1.1Kohm, and the output capacitor of the RBW-filter is 33nF. With the current 72MHz system clock frequency, the ADC clock is 72MHz/6 = 12MHz, and the ADC sampling time time is 1.5 cycles ie. 125ns.

STM32F103 datasheet Table 47 tabulates the maximum allowed source impedance for 12-bit ADC and 1/4 LSB accuracy for different sampling times. For the 14MHz ADC clock and 1.5 cycle sampling time (107 ns) the maximum allowed source impedance is 0.4 Kohm. For 7.5 cycle sampling time the maximum allowed source impedance is 7.5 Kohm.

Currently we are using sampling time of 125ns, which will tolerate somewhat higher source impedance than 0.4 Kohm. Also, if we target only for 1/2 LSB accuracy, the source impedance can be higher.

Increasing the sampling time from the 125ns (1.5 cycles) to the next possible sampling time of 7.5 cycles would decrease the available ADC sample rate from 857Ks/s down to 600Ks/s.

The measurement data with different attenuation suggests that the current sampling time of 1.5 clock is probably pretty good compromise between ADC sampling rate and the ADC accuracy.
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
I have created a repository at Github for the LTDZ-firmware development https://github.com/kalvin2021/ltdz-dsp I will add documentation for building the code in near future. This code will be the baseline for the actual DSP algorithm development.

This firmware implements the LOG/RMS-detector using stm32f103's 12-bit ADC, replacing the AD8307 LOG-detector. The dynamic range is now similar to original AD8307 ie. 50dB at best, but it will improve as the work progress and new digital signal processing algorithms are added. It is anticipated that the dynamic range will increase to 70dB - 80dB in network analyzer mode. The sweep times will also improve in future.

This firmware version has a new feature which allows to sample RBW filter output into ADC buffer, and provide the sample buffer to PC, which can be then analyzed with Matlab/GNU Octave/Numpy etc..

Current firmware can also be used with existing PC applications WinNWT4, WinNWT5 and NWT4000lin.
« Last Edit: June 09, 2021, 02:54:33 pm by Kalvin »
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
The noise floor in each of your "-80dB", "noisefloor" and "noloop" sample data files is approx. 0.57mV RMS, equivalent to an IF level (at the ADC input) of -52 dBm (at full IF bandwidth).
[ In your "-80dB" samples, the 15kHz signal already drowns in the noise floor -- at this frequency I cannot see a statistically significant difference between your "-80dB", "noisefloor" and "noloop" samples, which would exced the variance of the noise floor. ]

The maximum possible signal level is limited by the mixer's compression point, which is specified in the datasheet with -6dBm. Your data show that -6dBm is not yet the limit, since your "0dB" samples show a compression of only ~1.1dB, compared to the "10dB" samples (whose ADC-measured IF level is -6.3dBm for the 15kHz fundamental). So a maximum IF level of about 3dBm seems feasible (at least for the RF frequency you did use when you created the data files -- maybe it's different at other RF frequencies).

If I define "dynamic range" as quotient between maximum signal level and noise floor level, then it is ~55dB, for the ADC-based power detector at full IF bandwidth. Due to summing/averaging the power of 4k samples, the variance of the noise floor is supposed to be relatively low (approx +/- 1dB), i.e. the averaging acts as VBW filter, but it can't lower the noise floor. A lower noise floor can only be obtained by reducing the noise bandwidth, of course.

OTOH, the AD8307 datasheet claims a noise floor of -78dBm, which should give a dynamic range of 81dB then. Multiple ADC sapmles of the AD8307 output can still be averaged to improve the variance of the measured noise floor (-> VBW filter). But I'm in fact surprised that you don't get a better DR from the AD8307 either, than from the ADC-based power detector. The transfer function depicted in the datasheet starts to flatten below -70dBm, but that's already close to the noise floor, so it is not assumed to explain a DR loss of  more than a couple of dB. The AD8307 measures of course anything presented to its input. Does the IF signal coming out from the mixer possibly already contain a lot of noise (i.e. significanly more than -78dBm)? If yes, then it could explain why the relatively large intrinsic DR of the AD8307's cannot be exploited. But I have no idea how much noise is actually coming out of the mixer. The noise in the ADC samples is the sum of noise already present in the IF signal and ADC noise, and I've no feeling which component dominates.

Regarding the spurs in the spectrum: The dominant spurs have a spacing of about 143kHz, which is a little bit less than fs/6. I don't worry so much about the spurs near fs/6, near fs/3 and close to Nyqusit, but they also appear close to DC, which is nasty for SA usage, since they are still inside the passband of the RBW filter, even with significantly reduced bandwith. I still wonder whether the frequency of 143kHz is just an unfortunate coincidence, or whether it is related to the sampling? If they were exactly at DC, fs/6, fs/3 and Nyquist, then they were likely sampling-related. But the spacing is a little bit less than fs/6. Do you find any signal source on the board which has an integral multiple of 143kHz, or pulse widths with an integral fraction of ~7us, or anything which could produce IM products of the said frequencies? What's actually the PS3120 IC on the board? I don't find a datasheet, but it seems to be a step-up converter (so I guess it has an oscillator, too). +5V from USB is also a potential candidate for noise and spurs. Was the receiver LO actually turned on, when you captured the "noisefloor" and "noloop" samples? Does it make a difference (regarding spurs) whether it is on or off?

Edit:

Quote
It is anticipated that the dynamic range will increase to 70dB - 80dB in network analyzer mode. The sweep times will also improve in future.

(Average) noise floor at 321 Hz noise bandwidth (-> 4000 samples, Hamming window, one bin) is about -83dBm (which would imply a DR of 86dB when the maximum level is 3dBm). Since the noise is not white, it is also frequency-dependent. But variance of the noisefloor (when you repeat the measurement multiple times) is rather high, say +/- 10dB standard deviation (just a rough initial guess), so the desired signal still needs to have a level quite above the average noise floor in oder to stick out with statistical significance. The 15kHz signal in your "-70dB" samples (which is about -66.8dBm) can be clearly distinguished from the noise floor, with a repeatability of about +/-1dB. Still it is too weak for estimating the phase. And the 15kHz signal in your "-80dB" samples is likely above average noise floor, too, but cannot be distinguished from noise due to the variance. For 4k samples, already the sampling limits the sweep time to < 200 readings per second. Tuning the PLLs takes some additional time. [ And vector measurements require at least two (1 port) or 3 (1.5-port) readings per frequency point, reducing the sweep rate further. ]

Edit:

Since the noise distributions are skewed in dB space, which is not so easy to calculate analytically, I've done monte carlo / bootstrap simulation to get closer estimates for the noise floors and their variability. Below are the results for 4000 samples. dBm level are absulute IF voltage levels at the ADC input (0dBm = 0.223607 VRMS).

Quote
Noise floor levels:

Full IF bandwidth RMS averaging (integrate power over samples):
dBm_mean = -51.446
dBm_95pct = -51.237
dBm_stdev =  0.12724

RMS averaging of filtered samples (642 Hz noise BW, NA mode):
dBm_mean = -84.318
dBm_median = -84.104
dBm_95pct = -79.171
dBm_stdev =  3.3625

Vector averaging (DFT-like) of filtered samples (642 Hz noise BW, NA mode):
dBm_mean = -85.697
dBm_median = -84.673
dBm_95pct = -78.324
dBm_stdev =  5.6498

Code: [Select]
N=10000;
NS=4000;
num_samples = NS
fs=72e6/6/14;
freq=15000;

t=[0:NS-1]/fs;

% quadrature LO
lo = exp(-1i * 2 * pi * freq * t);

% window function (lowpass)
w = hamming(NS, "periodic")';
w /= sum(w);

% corresponding bandpass fir filter centered at freq
bp = 2 * w .* cos(2 * pi * freq * t);

% bootstrap resampling of noise from given distribution
% still not perfect, since actual noise is not i.i.d.
nf1 = textread("tg-15kHz-test-signals/tg-15kHz-80dB-after-caps.dat");
nf2 = textread("tg-15kHz-test-signals/tg-15kHz-noisefloor-after-caps.dat");
nf3 = textread("tg-15kHz-test-signals/tg-15kHz-noloop-after-caps.dat");
nf = [ nf1 nf2 nf3 ];                     % combine all three
nf = nf(1:NS)' / 65536 * 3.3 / 0.223607;  % normalize to dBm
x = zeros(N,NS);
for i = 1:N
  x(i,:) = nf(randi(length(nf),1,NS));
end

% alternatively, Gaussian noise with same power
% normalized to dBm
% x = std(nf) * randn(N,NS);

% sine wave for checking correct scaling of calculation
% x = repmat(sqrt(2) * cos(2 * pi * flo * t), N, 1);

y = zeros(N,NS);    % bandpass-filtered samples
V = zeros(N,1);     % filtered vector average
for i = 1:N
  xi = x(i,:);
  y(i,:) = real(ifft(fft(xi) .* fft(bp)));
  V(i) = sum(xi .* lo .* w);
end

disp("")
disp("Noise floor levels:")

disp("")
disp("Full IF bandwidth RMS averaging (integrate power over samples):")
dBm_mean = mean(20*log10(std(x')))
dBm_95pct = sort(20*log10(std(x')))(floor(0.95*N)+1)
dBm_stdev = std(20*log10(std(x')))

disp("")
disp("RMS averaging of filtered samples (642 Hz noise BW, NA mode):")
dBm_mean = mean(20*log10(std(y')))
dBm_median = median(20*log10(std(y')))
dBm_95pct = sort(20*log10(std(y')))(floor(0.95*N)+1)
dBm_stdev = std(20*log10(std(y')))

disp("")
disp("Vector averaging (DFT-like) of filtered samples (642 Hz noise BW, NA mode):")
dBm_mean = mean(20*log10(abs(V) * sqrt(2)))
dBm_median = median(20*log10(abs(V) * sqrt(2)))
dBm_95pct = sort(20*log10(abs(V) * sqrt(2)))(floor(0.95*N)+1)
dBm_stdev = std(20*log10(abs(V) * sqrt(2)))


« Last Edit: June 12, 2021, 03:46:45 pm by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
gf, thank you for your excellent posting once again!  :-+ Your input to this project has been most valuable, and has given great insight for both theoretical and practical aspects of signal processing and signal analysis.

The noise floor in each of your "-80dB", "noisefloor" and "noloop" sample data files is approx. 0.57mV RMS, equivalent to an IF level (at the ADC input) of -52 dBm (at full IF bandwidth).
[ In your "-80dB" samples, the 15kHz signal already drowns in the noise floor -- at this frequency I cannot see a statistically significant difference between your "-80dB", "noisefloor" and "noloop" samples, which would exced the variance of the noise floor. ]

I made the measurements at approx 60MHz tracking generator carrier frequency and 15kHz LO offset frequency. The signal  "noisefloor" was measured with the tracking generator output disabled. The signal "noloop" was measured with tracking generator output enabled, but the RX was not connected to the tracking generator output. The -80dB signal was measured with tracking generator output enabled with 80dB attenuation from tracking generator output to RX input.

Quote
The maximum possible signal level is limited by the mixer's compression point, which is specified in the datasheet with -6dBm. Your data show that -6dBm is not yet the limit, since your "0dB" samples show a compression of only ~1.1dB, compared to the "10dB" samples (whose ADC-measured IF level is -6.3dBm for the 15kHz fundamental). So a maximum IF level of about 3dBm seems feasible (at least for the RF frequency you did use when you created the data files -- maybe it's different at other RF frequencies).

ADF4351 tracking generator produces output signal that is not sinusoidal, but more or less square-wave. The mixer datasheet states that the "RF Feedthrough at IF Port @RF=2 GHz, LO=1.75 GHz" is dBc –25, also figure 7. This may limit the dynamic range of the network analyzer even at the lower frequencies if the device/filter-under-test has high attenuation at the frequencies of interest, but contain only very little attenuation at harmonics of the tracking generator signal. At least this may be a problem in the original design with AD8307 and the new design where ADC is sampling the 120KHz (240kHz) IF signal bandwidth. However, this problem may be solved in future when applying the narrow band spectrum analysis.

Quote
If I define "dynamic range" as quotient between maximum signal level and noise floor level, then it is ~55dB, for the ADC-based power detector at full IF bandwidth. Due to summing/averaging the power of 4k samples, the variance of the noise floor is supposed to be relatively low (approx +/- 1dB), i.e. the averaging acts as VBW filter, but it can't lower the noise floor. A lower noise floor can only be obtained by reducing the noise bandwidth, of course.

OTOH, the AD8307 datasheet claims a noise floor of -78dBm, which should give a dynamic range of 81dB then. Multiple ADC sapmles of the AD8307 output can still be averaged to improve the variance of the measured noise floor (-> VBW filter). But I'm in fact surprised that you don't get a better DR from the AD8307 either, than from the ADC-based power detector. The transfer function depicted in the datasheet starts to flatten below -70dBm, but that's already close to the noise floor, so it is not assumed to explain a DR loss of  more than a couple of dB. The AD8307 measures of course anything presented to its input. Does the IF signal coming out from the mixer possibly already contain a lot of noise (i.e. significanly more than -78dBm)? If yes, then it could explain why the relatively large intrinsic DR of the AD8307's cannot be exploited. But I have no idea how much noise is actually coming out of the mixer. The noise in the ADC samples is the sum of noise already present in the IF signal and ADC noise, and I've no feeling which component dominates.

The problem with the AD8307 is that it is measuring signal power over the full 120kHz bandwidth. As there are spurs above the 120kHz frequency, yet the RBW-filter doesn't provide much attenuation at these higher frequencies, the noise floor will remain relatively high, resulting the maximum available dynamic range of 50dB or so. There is very little one can do about this, without redesigning the hardware. Averaging AD8307 output will smooth the signal alright, but it will not improve the noise floor nor improve the dynamic range.

Quote
Regarding the spurs in the spectrum: The dominant spurs have a spacing of about 143kHz, which is a little bit less than fs/6. I don't worry so much about the spurs near fs/6, near fs/3 and close to Nyqusit, but they also appear close to DC, which is nasty for SA usage, since they are still inside the passband of the RBW filter, even with significantly reduced bandwith. I still wonder whether the frequency of 143kHz is just an unfortunate coincidence, or whether it is related to the sampling? If they were exactly at DC, fs/6, fs/3 and Nyquist, then they were likely sampling-related. But the spacing is a little bit less than fs/6. Do you find any signal source on the board which has an integral multiple of 143kHz, or pulse widths with an integral fraction of ~7us, or anything which could produce IM products of the said frequencies? What's actually the PS3120 IC on the board? I don't find a datasheet, but it seems to be a step-up converter (so I guess it has an oscillator, too). +5V from USB is also a potential candidate for noise and spurs. Was the receiver LO actually turned on, when you captured the "noisefloor" and "noloop" samples? Does it make a difference (regarding spurs) whether it is on or off?

I can investigate this. It is quite easy to change the sampling frequency and see whether those 143kHz spurs remain in place or move along with different sampling frequencies. PS3120 is a switching-capacitor dc-dc converter / voltage doubler (see attachment). Its typical switching frequency is [low noise constant] 360 kHz, but I have not measured its actual frequency.

Quote
Quote
It is anticipated that the dynamic range will increase to 70dB - 80dB in network analyzer mode. The sweep times will also improve in future.

(Average) noise floor at 321 Hz noise bandwidth (-> 4000 samples, Hamming window, one bin) is about -83dBm (which would imply a DR of 86dB when the maximum level is 3dBm). Since the noise is not white, it is also frequency-dependent. But variance of the noisefloor (when you repeat the measurement multiple times) is rather high, say +/- 10dB standard deviation (just a rough initial guess), so the desired signal still needs to have a level quite above the average noise floor in oder to stick out with statistical significance. The 15kHz signal in your "-70dB" samples (which is about -66.8dBm) can be clearly distinguished from the noise floor, with a repeatability of about +/-1dB. Still it is too weak for estimating the phase. And the 15kHz signal in your "-80dB" samples is likely above average noise floor, too, but cannot be distinguished from noise due to the variance. For 4k samples, already the sampling limits the sweep time to < 200 readings per second. Tuning the PLLs takes some additional time. [ And vector measurements require at least two (1 port) or 3 (1.5-port) readings per frequency point, reducing the sweep rate further. ]

Very useful analysis, gf! As your analysis shows, the dynamic range of 70dB - 80dB is quite possible. Sweep rate and the dynamic range are trade-offs the user need make. If the user wants to go for faster sweep rate, the available dynamic range will be reduced. If the user wants to have better dynamic range, it is necessary to sweep slower. This is the main goal of this little project.

Quote
Since the noise distributions are skewed in dB space, which is not so easy to calculate analytically, I've done monte carlo / bootstrap simulation to get closer estimates for the noise floors and their variability. Below are the results for 4000 samples. dBm level are absulute IF voltage levels at the ADC input (0dBm = 0.223607 VRMS).

Noise floor levels:

Full IF bandwidth RMS averaging (integrate power over samples):
dBm_mean = -51.446
dBm_95pct = -51.237
dBm_stdev =  0.12724

RMS averaging of filtered samples (642 Hz noise BW, NA mode):
dBm_mean = -84.318
dBm_median = -84.104
dBm_95pct = -79.171
dBm_stdev =  3.3625

Vector averaging (DFT-like) of filtered samples (642 Hz noise BW, NA mode):
dBm_mean = -85.697
dBm_median = -84.673
dBm_95pct = -78.324
dBm_stdev =  5.6498

It is easy to see from your analysis that the available dynamic range will be approx. 50dB when using this device as a spectrum analyzer. When using this device as a [scalar] network analyzer, the dynamic range can be close to 80dB, giving about 30dB improvement over the original design.
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
I have implemented 4th order IIR lowpass-filters for different bandwidths (3.3kHz - 100kHz) to be used in spectrum analyzer operating mode. Currently the firmware selects the most suitable filter bandwidth based on the sweep step size in use. After a quick test, the available dynamic range is approx 50dB, which matches your analysis very well.

The ADC-based LOG-detector is very linear down few dB away from the noise floor.

It was necessary to use 32-bit Q31 data format for the filter coefficients as the ratio of the filter cut-off frequencies to Nyquist frequency became very small. I could not get working filters below 3300Hz due to coefficient quantization effects, but that is not a major problem at the moment. There are ways to go around these filter construction issues.

I will start experimenting with the network analyzer band-pass filter design, based on your ideas and suggestions. First, I will test it on PC with GNU Octave and C, and then port the implementation to the actual firmware when the design is working on a PC.
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
It was necessary to use 32-bit Q31 data format for the filter coefficients as the ratio of the filter cut-off frequencies to Nyquist frequency became very small. I could not get working filters below 3300Hz due to coefficient quantization effects, but that is not a major problem at the moment. There are ways to go around these filter construction issues.

A very low cut-off relaxes the requirement for a preceding decimator, so that even a 2-tap moving average filter would likely suffice for a decimation by two, i.e. average samples #1 and #2 to produce the first decimated sample, average samples #3 and #4 to produce the 2nd decimated sample, and so on. With 4 cascaded stages, you get a decimation by 16. Nyqist is still 26.7kHz then, i.e. still significantly higher than the final cut-off of say 1kHz. All frequencies which are folded back to near DC by the downsamling are located near the zeros of the moving average filters, and therefore significantly attenuated. See attachment for the frequency response of such a 4 stage decimation filter, and for comparison a Chebycheff filter with 3.3kHz cut-off. Btw, don't throw away the extra precision arising from the decimation filter (1 bit per stage).

Edit:

And don't be under the illusion that you can make the cut-off arbitrarily low if only 4k samples are available. If you want a low bandwidth, you need to capture a longer time interval. If the filter happens to be computationally too expensive to run in real-time, then I see at least the aim to do the decimation by factor R (say 16) in real-rime, so that a R times longer time interval can be captured and stored in the same amount of memory.

With decreasing cut-off, I'm also more and more worried about the estimation uncertainty at low frequencies (due to limited time interval being captured). And I'm also worried about ucertainty at DC, which suffers from offset errors and drift. While the zero IF prevents images, it introduces other problems.

Btw, how exacaly did you fit the new analog filter into the curcuit? I guess you turned the 1.1k output resister (from the rf-tools design) into a 2.2k + 2.2k divider to provide the bias for the ADC? Is the mixer output still AC coupled to the input of filter? If yes, what coupling capacitor value?
« Last Edit: June 13, 2021, 08:27:04 am by gf »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Btw, how exacaly did you fit the new analog filter into the curcuit? I guess you turned the 1.1k output resister (from the rf-tools design) into a 2.2k + 2.2k divider to provide the bias for the ADC? Is the mixer output still AC coupled to the input of filter? If yes, what coupling capacitor value?

I just replaced to original components with the new ones, so this modification is quite simple to do as the filter topology did not change. The AD8307 input impedance is 1.1Kohm, so the input is already terminated. I added 12kohm+12kohm divider between +3V3 and GND providing the required bias, which is good enough for impedance matching. Attachment #1 shows the designed filter. Attachment #2 shows simulated results of the new filter and the original filter.

The bias point is at the Vout of the new filter, which is also wired to the ADC input pin PA.2. In my LTDZ PCB, the unused MCU pins were left floating, so there weren't any problems when adding this jumper wire from Vout to the ADC input pin. Some other designs have the unused pins of the MCU are connected to GND, which requires some more effort to get this extra jumper wire connected to ADC input pin.

As the mixer output has currently a 1uF ceramic capacitor feeding the filter input, the frequencies near DC are attenuated by some dB (at 1kHz the attenuation is approx. 1.3dB). Increasing the capacitor value from 1uF to 10uF would reduce the attenuation, but the transition time after the mixer LO switches frequency would be longer. The component values of the new filter are quite convenient, and the 100uH inductors could be found from an inductor kit bought from eBay.

It might be possible to reduce the PCB's noise floor a little by placing the inductors at the soldering side of the PCB. The switched-capacitor dc-dc converter PS3120 is located close the inductors, so there is a possibility that some of the switching energy can get coupled to the inductors. I have not done that yet, but it might be worth trying. On the soldering side of the PCB there is one L-shaped trace close to one inductor, and I have seen one user adding a small copper shield above the trace in order to reduce any radiated energy from coupling to the nearby inductor.
« Last Edit: June 13, 2021, 11:46:48 am by Kalvin »
 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
The current hardware modifications can be found as an attachment.

 

Offline KalvinTopic starter

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Removed broken GNU Octave script. Thanks gf for pointing it out.
« Last Edit: June 15, 2021, 07:20:46 am by Kalvin »
 

Offline gf

  • Super Contributor
  • ***
  • Posts: 1132
  • Country: de
   
x_re = x .* cos(2.0 * pi * Fs_Hz * t)';
x_im = x .* sin(2.0 * pi * Fs_Hz * t)';

Sorry, I don't understand what this is supposed to do :-//
For the given t and Fs_Hz this is equivalent to x_re = x; x_im = x .* 0;
I guess you rather meant cos(2.0 * pi * Lo_Hz * t) and sin(2.0 * pi * Lo_Hz * t)?

But even then, why do you multiply with the complex sine wave at all, if you calculate a RMS sum, and not a vector sum?
Note that sum((x_re .* x_re) + (x_im .* x_im)) always gives the same result as sum(x .* x), for any frequency of the complex sine wave -- so why not directly calculate the latter?

The multiplication with the complex 15kHz sine wave makes only sense if you'd calculate the vector sum, i.e sum(x_re) .** 2 + sum(x_im)  .** 2 instead.
« Last Edit: June 14, 2021, 11:43:22 pm by gf »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf