DIY Scalar Network Analyser with or without external ADC

Status
Not open for further replies.
Hi everyone,
First let me say that I am in no shape or form a professional electrical engineer (I am a chemist-turned-electronics-hobbyist).
I am working on a pcb project where I try to make my own scalar network analyzer to measure quartz crystal resonances.
It looks similar to the design of Brett Killion (https://hackaday.com/2016/03/06/arduino-rf-network-analyzer/)

In short I am using an AD9851 DDS chip to generate my sine waves which are then passed through a LPF(70MHz cut-off) onto a buffer amplifier. This is then sent through a quartz crystal and the signal coming out at the other end is analyzed by a logarithmic amplifier. This outputs a certain voltage based on the envelope of the sine at its input. The output is again buffered and read by an analog pin of the microcontroller. The microcontroller that I use is the Teensy 4.0 and it is communicating over USB with a Python DAQ script using PySerial. Thus far I have been able to get quite nice results. However, I am now looking into speeding up the measurements to get a high time resolution.

I have posted on this forum before about the slow speed of data transfer likely caused by using the PySerial library. Simply put, I was using a too high frequency resolution (1Hz) for my measurements (requiring tons of data points per frequency sweep). I have since changed tactics and spend more of the computing power on the Teensy side. I use less data points per sweep (~10Hz resolution) and I fit a spline function to this result with much higher resolution on the Python side. To be able to get a good enough fit (1Hz resolution) I must average the 12 bit ADC measurement values at least ~30 times per frequency and do this for roughly 500 frequencies. I have confirmed that I am now mostly waiting on the Teensy by checking the available bytes in the PySerial buffer (it is mostly 0 and sometimes 2 when the Teensy sends the measurement data of a point).

(Part of the Teensy code is attached below, basic function is receiving a string from the Python script about the frequency sweep, converting this to a frequency range the Teensy can work with and using a serial Set_Frequency() function described by Analog Devices in their datasheet, measuring analogRead() at each frequency for 32 times and sending the data back to Python. After each sweep, Python sends a new sweep command to keep updating the frequency response plot.)

Now here comes my question.. thanks for reading this far, haha.
As my main concern is the frequency resolution of <1Hz at higher time resolution (right now it is about 0,5s per sweep), I was wondering if it would be an interesting idea to use either a faster 12bit external ADC or maybe even a 13bit one to improve my time resolution. My thinking is as follows: A faster ADC might allow me to measure more averages in the same time frame possibly allowing for faster measurement sweeps. A 13bit ADC might also offer improved voltage resolution and perhaps make fitting more precise/constant so that a lower amount of averages can be used = faster measurements.

So, it boils down to the question of whether the Teensy 4.0 built-in ADC (I believe it is part of the main chip?) can be outperformed in terms of speed and resolution? And whether you think it would be possible for a 'beginner' like me.
If you have any other smart ideas, I would really appreciate new input as well :)
I thank you all very much in advance for the effort and hopefully we can come to some fruitful discussion.

Cheers,
Rens

EDIT1: Of course, if you think that there could be a lot to gain in terms of programming please feel free to request more of my code!

EDIT2: .. I should have thought about this post more carefully.. another plus I see in using an external ADC is that my board uses 5V for most of its components. With the Teensy ADC I am limited to 3.3V max, so I could also gain some resolution by using a 5V ADC (I can turn my buffer amplifier into a non-inverting amplifier and put some gain on the output of my logarithmic amplifier).

Code:
void loop()
{
  if (Serial.available()>0)
  {
    // reads the message sent, delimited by ;.
    String message_str = Serial.readStringUntil('\n');
    // buffer array for storing the message in char form temporarily
    char buffer_array[64];
    //Serial.println(message_str); // debugging line
    // the String format is converted to C-string and stored in the buffer array
    message_str.toCharArray(buffer_array, sizeof(buffer_array));

    // strtok destroys data so a copy is made before with strcpy
    strcpy(tempChars, buffer_array);
    // this is used by strtok() as an index
    char * strtokIndx;
    // gets first part of C-string until delimiter and saves to variable
    strtokIndx = strtok(tempChars,";");
    freq_start = atol(strtokIndx);
    // continues where last call left off and saves to variable
    strtokIndx = strtok(NULL,";");
    freq_stop = atol(strtokIndx);
    // ditto ^
    strtokIndx = strtok(NULL,";");
    freq_step = atoi(strtokIndx);
    new_command = 1;
  }

  if (new_command == 1)
  { 
    digitalWrite(13,LOW);
    Serial.write("b,");
    for (int i=freq_start; i<=freq_stop; i+= freq_step)
    {
      Set_Frequency(i);
      delayMicroseconds(10);
      int  AVG_MAG = 0;
      uint16_t reading = 0;
      if (AVERAGING == true) {
          
          for (int i = 0; i < AVERAGE_SAMPLE; i++) {
            AVG_MAG += analogRead(AD8310_MAG);
          }
          reading = AVG_MAG/AVERAGE_SAMPLE;.
      }
      uint8_t byte_array[2] = {reading >> 8, reading & 0xFF};
      Serial.write(byte_array, 2);
    }
    
    Serial.write("s");
    digitalWrite(13,HIGH);
    new_command = 0;
  }
}
 
Last edited:
[ BTW I think what you have isn't technically a network analyzer, as you are not measuring reflected power(?) - what you have is
equivalent to a spectrum analyzer with tracking generator, only missing out the RF heterodyning and IF filters ]

I think all you need to do is run the ADC at full tilt and average as many readings as fall within the measurement period
(which is presumably 1s for 1Hz RBW and 10ms for 10Hz RBW?) You are doing a fixed number of readings irrespective
of the resolution bandwidth, which is going to greatly slow down the high RBW settings.

Sweep time should go as 1/(RBW^2) for a swept SA, if VBW=RBW

In a completely analog spectrum analyzer you'd have a filter with bandwidth set to the VBW to follow the log amp, but
often the VBW is locked to the RBW so this would be the equivalent digital version - average continually over the measurement
period, set measurement period to 1/RBW (modulo a constant)
 
Hi Mark,
Thank you for your response!
Your comment is indeed correct. The setup is more like a spectrum analyzer combined with a tracking generator.

With regard to your comment about running the ADC at full tilt, I tried setting the ADC_CONVERSION_SPEED and ADC_SAMPLING_SPEED to VERY_HIGH_SPEED as defined in the ADC library.
I also changed the amount of averages. These are my current settings:

Code:
  // ADC settings
  adc->adc0->setAveraging(32); // set number of averages
  adc->adc0->setResolution(12); // set bits of resolution
  adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED);
  adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);

These settings do not yield a sufficiently high measurement accuracy using my 10 Hz RBW and spline interpolation.
I am still forced to do extra averaging in the code like this, slowing down the sweep significantly:

Code:
for (int i=freq_start; i<=freq_stop; i+= freq_step)
    {
      Set_Frequency(i);
      delayMicroseconds(10);
      uint16_t reading = 0;
      uint16_t avg_measurement = 0;
      if (AVERAGING == true){
        for (int i=0; i<= AVERAGE_SAMPLE; i+=1){
          avg_measurement += adc->adc0->analogRead(AD8310_MAG);
          }
        }
      reading = avg_measurement/AVERAGE_SAMPLE;

      uint8_t byte_array[2] = {reading >> 8, reading & 0xFF};
      Serial.write(byte_array, 2);
    }

As I understand there is no way to increase the amount of averaging done by the hardware to a value above 32, right? Do you know what the default hardware averaging is set to?
I can follow the reasoning that sweep time is increasing as the RBW increases. That is why I chose to use a 10 Hz RBW and average over these samples and then fit a spline to still get some extra resolution by interpolation.

EDIT: The idea of using an external ADC with high sampling rate does not sound like a good idea to you?
 
Last edited:
Hi everyone,
First let me say that I am in no shape or form a professional electrical engineer (I am a chemist-turned-electronics-hobbyist).
I am working on a pcb project where I try to make my own scalar network analyzer to measure quartz crystal resonances.
It looks similar to the design of Brett Killion (https://hackaday.com/2016/03/06/arduino-rf-network-analyzer/)

In short I am using an AD9851 DDS chip to generate my sine waves which are then passed through a LPF(70MHz cut-off) onto a buffer amplifier. This is then sent through a quartz crystal and the signal coming out at the other end is analyzed by a logarithmic amplifier. This outputs a certain voltage based on the envelope of the sine at its input. The output is again buffered and read by an analog pin of the microcontroller. The microcontroller that I use is the Teensy 4.0 and it is communicating over USB with a Python DAQ script using PySerial. Thus far I have been able to get quite nice results. However, I am now looking into speeding up the measurements to get a high time resolution.

I bought one of these as a module from eBay. Haven’t played with it yet though. Could make the ADC side a bit more powerful, SPI interface should deal with the bandwidth. Allegedly it can do 30000 sps.

https://www.ti.com/lit/ds/symlink/a...=https%3A%2F%2Fwww.ti.com%2Fproduct%2FADS1256

https://www.ebay.co.uk/itm/20216341...LC6M1mq7As31nfo3Ggu986ost0VtO7c0aAuDnEALw_wcB

Steve
 
Status
Not open for further replies.
Back
Top