What is Signal to Noise and Distortion (SINAD)?

Signal to Noise and Distortion is just that, the ratio of the power of the signal to the power of the noise and distortion (everything that is not signal). We can write it as:

\[SINAD = 10 \log_{10} \left({\frac{P_\text{Signal}}{P_\text{Noise} + P_\text{Distortion}}}\right)\]

SINAD is measured with a single tone, a single input frequency. There are more measurements to test the performance with more than one input frequency.

How do we measure it?

SINAD is measured by applying a fixed frequency to the system, computing the power FFT, finding the signal’s power, and the power of everything else.

To eliminate spectral leakage, the ADC sampling needs to be locked to the function generator output. One channel of the function generator goes to the ADC input; the other channel provided a 48MHz clock to the ADC via a GPIO.

To ensure that the quantization noise spectrum is random and eliminate masking of distortion products, the data record should contain a prime number of waveform cycles. In the below equation, $M_C$ is the number of full cycles collected, and $M$ is the record length. In the data collection, I used a 995Hz signal, which captures 199 (a prime number) complete cycles of the waveform.

\[\frac{f_{\text {in }}}{f_{s}}=\frac{M_{C}}{M}\]

Test Methodology

A 995Hz sine wave was generated with a Rigol DG4000 function generator. I measured it’s SINAD as around 74dB, exceeding its minimum spec. The following code was used to sample the ADC at it’s full rate then dump the samples out the USB serial interface and lock the ADC clock to the function generator:

// Code modified from
#include <stdio.h>
#include <inttypes.h>
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "hardware/dma.h"

#define N_SAMPLES 100000
uint16_t sample_buf[N_SAMPLES];

int main() {

    // DMA setup:
    uint dma_chan = dma_claim_unused_channel(true);
    dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
    channel_config_set_read_increment(&cfg, false);
    channel_config_set_write_increment(&cfg, true);
    channel_config_set_dreq(&cfg, DREQ_ADC);

    adc_fifo_setup(true, true, 1, false, false);

    // Set SMPS_MODE Pin
    gpio_set_dir(23, GPIO_OUT);
    gpio_put(23, 1); // 0 for PSM; 1 for PWM

    // Configure clock input pin 20
    clock_configure_gpin(clk_adc, 20, clock_get_hz(clk_adc), clock_get_hz(clk_adc));

    // Output clk_adc / 10 to gpio 26
    clock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_ADC, 10);

    // Capture samples then ship them out over USB
    while (1) {
        dma_channel_configure(dma_chan, &cfg, sample_buf, &adc_hw->fifo, N_SAMPLES,true);
        for (int i = 0; i < N_SAMPLES; i = i + 1) {
            printf("%03x\n", sample_buf[i]);
    return 0;


Analysis code
def plot_spectrum_sinad(times, vals) -> None:
	Plot spectrum, calculate SINAD.

	:param wfm: Waveform to plot
	:return: None
	# processes a prime number of cycles
	vals = vals
	fft_out = np.fft.rfft(vals, norm="forward")[1:]
	fft_freq = np.fft.rfftfreq(len(vals), d=times[1] - times[0])[1:]
	fig = plt.figure()
		* np.log10(  # Convert from linear scale to dB
			# dB scale referenced to peak FFT term; assuming this is the carrier
			/ np.max(np.abs(fft_out))
		label=f"Waveform Spectrum",
	# plt.axhline(20 * np.log10(np.sort(np.abs(fft_out))[-2]/ np.max(np.abs(fft_out))))
	print(f"FS size: {np.abs(fft_out).max()}")
	print(f"SFDR: {-20 * np.log10(np.sort(np.abs(fft_out))[-2]/ np.max(np.abs(fft_out)))}dBc")
	plt.title("ADC Spectrum")
	plt.xlabel("Frequency (kHz)")
	plt.ylabel("Power dBc")
	with open("SINAD_Spectrum.html", "w") as f:
	power_spectrum = np.abs(fft_out**2) # Convert Vrms to power
	max_val = power_spectrum.max()
	rem_vals = power_spectrum
	rem_vals[np.argmax(power_spectrum)] = 0 # Remove fundamental
	sinad = 10 * np.log10(max_val / np.sum(rem_vals))
	print(f"SINAD = {sinad}")
	print(f"ENOB = {(sinad-1.78)/6.02}")
	plt.xlabel("Frequency (kHz)")
	plt.ylabel("Power (dBC)")
	plt.title("Spectrum of collected waveform")