Author Topic: how to properly generate ideal sine test sample?  (Read 4789 times)

0 Members and 1 Guest are viewing this topic.

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
how to properly generate ideal sine test sample?
« on: May 31, 2022, 09:51:57 am »
Basically I need a test vector with ideal sine wave, it should be 14 bit integer format and consists of 16 samples (a single sine period with maximum amplitude). I'm expecting symmetric AES17 sample representation which means that the max negative value is not used (for symmetry) and mean value should be 0.

Previously I was used test vector provided by another people, I don't know how it was generated, but it seems that this vector provides better sine performance than I can generate. Here it is:
Code: [Select]
0
3134
5792
7567
8191
7567
5792
3134
0
-3134
-5792
-7567
-8191
-7567
-5792
-3134

And it's spectrum:
1499326-0

Now I'm trying to generate the same sine with code in the following way:
Code: [Select]
var bits = 14;
var halfRange = Math.Pow(2, bits-1);
for (var i = 0; i < 16; i++)
{
    double sine = Math.Sin(2 * Math.PI * i / 16);
    sample[i] = round( sine * (halfRange - 1D) );
}

sine * (halfRange - 1D) result:
Code: [Select]
0.0
3134.5599945024505
5791.91164469901
7567.49725079995
8191.0
7567.49725079995
5791.911644699011
3134.5599945024514
0.0000000000010030750644159092
-3134.5599945024496
-5791.91164469901
-7567.4972507999482
-8191.0
-7567.4972507999491
-5791.911644699012
-3134.5599945024555

and after round I get this:
Code: [Select]
0
3135
5792
7567
8191
7567
5792
3135
0
-3135
-5792
-7567
-8191
-7567
-5792
-3135

And it's spectrum:
1499332-1

It seems that my sine has a little worse performance, it has -87.91 dB for 3'rd harmonic, while first sine has -95.83 dB for 3'rd harmonic.

Both test vectors almost the same, the only difference is 3135 instead of 3134. But I have no idea how to obtain 3134 value, because 3134.5599945 should be rounded to 3135. And if I replace round with floor (cut-off fraction part), I will get incorrect value for the third value, because 5791.91 will be rounded to 5791, but it should be 5792...

So there are two questions:
1) Is the first sine really has better performance? (maybe my spectrum calculation is just not so accurate)
2) How to calculate it with proper rounding?
 

Offline asmi

  • Super Contributor
  • ***
  • Posts: 2730
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #1 on: May 31, 2022, 03:40:40 pm »
1. There is no such thing as "the ideal sine wave". It's a purely mathematical construct.
2. 14 bits gives a resolution of 1/(2^14) = 6.103515625 * 10^-5, which gives an error of 20 * log (6.103515625 * 10^-5) ~ -84.3 dB. So unless I screwed up the math somewhere (again ::) ), you can't really get anything much better than that with this format.

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: how to properly generate ideal sine test sample?
« Reply #2 on: May 31, 2022, 05:18:41 pm »
Indeed.
The fact you seem to be getting "better performance" with actual rounding errors (if I get what you said correctly) is marginal. It's probably the result of an additional rounding error from the spectrum analysis itself that happens to give a more favorable figure.
As asmi said, you can't get better with 14 bits.

 

Offline asmi

  • Super Contributor
  • ***
  • Posts: 2730
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #3 on: May 31, 2022, 05:57:01 pm »
As asmi said, you can't get better with 14 bits.
Technically you could've gotten two times better result (about -90.3 dB) if ADC would be able to do a proper rounding (round-to-nearest), but as far as I know all of them round down, which is why the max error is 1 LSD, not 0.5 LSD which would be the case for round-to-nearest rounding. But in any case ~-90.3 dB seems to be the absolute best case possible, so not sure how he got better numbers.
« Last Edit: May 31, 2022, 06:00:44 pm by asmi »
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #4 on: May 31, 2022, 08:54:39 pm »
1. There is no such thing as "the ideal sine wave". It's a purely mathematical construct.

I understand discrete signal dynamic range limit and quantization noise. But the goal is not to bypass this limitation. The goal is to get sine with minimum possible distortion for specific resolution.

2. 14 bits gives a resolution of 1/(2^14) = 6.103515625 * 10^-5, which gives an error of 20 * log (6.103515625 * 10^-5) ~ -84.3 dB. So unless I screwed up the math somewhere (again ::) ), you can't really get anything much better than that with this format.

Dynamic range 6.02*14 = 84.28 dB represents dynamic range of worst case for any possible signal. It is limited due to quantization noise at -84.28 dB level. But I'm talking about specific sine, which performance depends on error between aperture and actual sine points, so its actual performance will depends on a bit resolution and relation between sine frequency and sample rate.

I need a discrete sine wave which best fits with continuous sine for selected bit resolution. The criterion here is a minimum possible harmonics/distortion level after waveform reconstruction from discrete to continuous representation.

But in any case ~-90.3 dB seems to be the absolute best case possible, so not sure how he got better numbers.

Reconstructed wave dynamic range exceeds theoretical -84 dB noise floor because errors (due to misalignment between discrete points and aperture) is not maximum possible for selected combination of sine parameters, bit resolution and sample rate.

For example 14-bit 96 kS/s DC signal (constant sample value) has zero noise, so it has infinite dynamic range which is much better than mentioned 84.28 dB for 14 bit.  But you cannot get the same result for any kind of signal. While 84 dB limitation means guarantee that 14 bit resolution can represent at least 84 dB dynamic range signal in worst case for any signal. That limitation is for worst case. As mentioned, for DC signal it will be infinite.
« Last Edit: May 31, 2022, 10:02:21 pm by radiolistener »
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #5 on: June 01, 2022, 12:08:33 am »
Would you mind looking at this set of values?

Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134

If they are better than the original I'll share the code that found them...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: radiolistener

Offline macboy

  • Super Contributor
  • ***
  • Posts: 2254
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #6 on: June 01, 2022, 12:25:09 am »
Try scaling very slightly up or down (fraction of one full scale bit). You can find a point where it will round as you want.
Even better, add noise (yes, add) to achieve dithering which can dramatically reduce distortion, at the expense of random noise. If you need substantially less than 0.5 FS bandwidth, then push (shape) the noise up to high frequencies and filter it out.  This is how you can reproduce a -105 dB tone from a CD with theoretically only 96 dB of dynamic range.
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #7 on: June 01, 2022, 12:41:11 am »
An easy implementation in python is (you can use an online python interpreter):

Code: [Select]
import numpy as np
idx = np.arange(16) # Number of samples per period
sf = idx.max() / (2*np.pi) # scaling factor for the period
arr = idx/sf # create a new array that goes from 0 to 2 pi in N steps
arr = np.sin(arr) # calculate the sin value for each point
integer_arr = (arr*(2**13)).astype('int32') # Scale the array by 2^13 (the actual sine values go between -1 and 1, giving you an effective range of 2, so you need to scale by max int/2)
print(integer_arr)

this results in:
[    0  3331  6087  7791  8147  7094  4815  1703 -1703 -4815 -7094 -8147 -7791 -6087 -3331     0]
you can easily change the number of samples , add phase offset or change the scaling factor etc.

Edit: to change the phase offset just add it to line 5, for example you can do arr = np.sin(arr + (np.pi/2)) to add a pi/2 offset and turn this into a cos table
« Last Edit: June 01, 2022, 12:49:34 am by OM222O »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7726
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #8 on: June 01, 2022, 02:42:56 am »
this results in:
[    0  3331  6087  7791  8147  7094  4815  1703 -1703 -4815 -7094 -8147 -7791 -6087 -3331     0]
you can easily change the number of samples , add phase offset or change the scaling factor etc.

2 sequential samples with the value 0 in the 16 phase loop?
Wouldn't that generate substantial distortion?
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: how to properly generate ideal sine test sample?
« Reply #9 on: June 01, 2022, 02:48:43 am »
Yes, it's 16 samples while the period is computed over 15 samples. Looks like a typical off-by-one issue.

 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #10 on: June 01, 2022, 03:08:46 am »
Would you mind looking at this set of values?

Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134

If they are better than the original I'll share the code that found them...

wow, it has -105.25 dBc for 3'rd harmonic and -100.25 dBc for 5'th harmonic
Almost all harmonics below -100 dBc, except 7'th which is -99.05 dBc...

How to calculate it? :)
« Last Edit: June 01, 2022, 03:15:40 am by radiolistener »
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #11 on: June 01, 2022, 03:23:20 am »
this results in:
[    0  3331  6087  7791  8147  7094  4815  1703 -1703 -4815 -7094 -8147 -7791 -6087 -3331     0]
you can easily change the number of samples , add phase offset or change the scaling factor etc.

2 sequential samples with the value 0 in the 16 phase loop?
Wouldn't that generate substantial distortion?

Yes, it's 16 samples while the period is computed over 15 samples. Looks like a typical off-by-one issue.



0 and 2 pi are the same point effectively. If this is an issue you can set the array to be of lenght N+1 and ignore the last value
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #12 on: June 01, 2022, 03:28:15 am »
this results in:
[    0  3331  6087  7791  8147  7094  4815  1703 -1703 -4815 -7094 -8147 -7791 -6087 -3331     0]
you can easily change the number of samples , add phase offset or change the scaling factor etc.

2 sequential samples with the value 0 in the 16 phase loop?
Wouldn't that generate substantial distortion?

Yes, just -20 dBc for 2'nd harmonic and the carrier is not full scale, just -0.36 dBc:
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #13 on: June 01, 2022, 03:47:48 am »
Would you mind looking at this set of values?

Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134

If they are better than the original I'll share the code that found them...

wow, it has -105.25 dBc for 3'rd harmonic and -100.25 dBc for 5'th harmonic
Almost all harmonics below -100 dBc, except 7'th which is -99.05 dBc...

How to calculate it? :)

Just looked one either side of the original samples, for all samples, and picked which is best:

Code: [Select]
///////////////////////////////////////////
// sine_samples.c : hamster@snap.net.nz
//
// A quick hack to see if I can find better
// sine sample values for an ADC
//
///////////////////////////////////////////
#include <stdio.h>
#include <math.h>

static int data[] = {
   0,  3134,  5792,  7567,  8191,  7567,  5792,  3134,
   0, -3134, -5792, -7567, -8191, -7567, -5792, -3134
};

static double evaluate(int data[]) {
   double tc = 0.0, ts = 0.0, t_err = 0.0;

   // Single bin DFT
   for(int i = 0; i < 16; i++) {
      ts += data[i] * sin(i*M_PI*2.0/16);
      tc += data[i] * cos(i*M_PI*2.0/16);
   }
   ts /= 16/2;
   tc /= 16/2;

   // Remove the fundimental and total up RMS of what is left
   for(int i = 0; i < 16; i++) {
     double e = data[i] - sin(i*M_PI*2.0/16) * ts - cos(i*M_PI*2.0/16) * tc;
     t_err += e*e;
   }
   return t_err;
}

int main(void) {
   double min = 0.0;
   int limit = pow(3,16);
   // to pow(3,16) - to try every combination
   for(int i = 0; i < limit; i++) {
      int d[16];

      // Add either +1,0,-1 to every sample, based on i
      int x = i;
      for(int j = 0; j < 16; j++) {
         switch(x%3) {
            case 1: d[j] = data[j]-1; break;
            case 0: d[j] = data[j];   break;
            case 2: d[j] = data[j]+1; break;
         }
         x /= 3;
      }

      // Range check
      if(d[4] > 8191 || d[12] < -8191)
         continue;

      // See if this is any better or worse
      double this_noise = evaluate(d);
      if(i == 0 || this_noise < min) {
         min = this_noise;
         printf("%12.9f: ", this_noise);
         for(int k = 0; k < 16; k++) {
           printf("%5d ",d[k]);
         }
         printf("\n");
      }
   }

   // Print out the original for comparison
   printf("%12.9f: ", evaluate(data));
   for(int k = 0; k < 16; k++) {
       printf("%5d ",data[k]);
   }
   printf(" << original\n");
   return 0;
}

If I lack the skills to do it properly, I just brute-force it!  >:D

Here's the output:
Code: [Select]
$ ./main
 1.527446318:     0  3134  5792  7567  8191  7567  5792  3134     0 -3134 -5792 -7567 -8191 -7567 -5792 -3134
 1.516362543:     0  3135  5792  7567  8191  7567  5792  3134     0 -3134 -5792 -7567 -8191 -7567 -5792 -3134
 1.255278769:     0  3135  5792  7567  8191  7567  5792  3134     0 -3135 -5792 -7567 -8191 -7567 -5792 -3134
 1.188563146:     0  3135  5792  7568  8191  7567  5792  3134     0 -3135 -5792 -7568 -8191 -7567 -5792 -3134
 1.121847524:     0  3135  5792  7568  8191  7568  5792  3134     0 -3135 -5792 -7568 -8191 -7568 -5792 -3134
 1.110763749:     0  3135  5792  7568  8191  7568  5792  3135     0 -3135 -5792 -7568 -8191 -7568 -5792 -3134
 1.023786799:     1  3135  5792  7567  8191  7567  5791  3134    -1 -3135 -5792 -7567 -8191 -7567 -5791 -3134
 0.957071177:     1  3135  5792  7568  8191  7567  5791  3134    -1 -3135 -5792 -7568 -8191 -7567 -5791 -3134
 0.955140664:     0  3134  5791  7567  8190  7567  5791  3134     0 -3134 -5791 -7567 -8190 -7567 -5791 -3134
 0.920336789:     0  3134  5791  7566  8190  7566  5791  3134     0 -3134 -5791 -7566 -8190 -7567 -5791 -3134
 0.920336789:     0  3134  5791  7566  8190  7566  5791  3134     0 -3134 -5791 -7567 -8190 -7566 -5791 -3134
 0.920336789:     0  3134  5791  7566  8190  7567  5791  3134     0 -3134 -5791 -7566 -8190 -7566 -5791 -3134
 0.920336789:     0  3134  5791  7567  8190  7566  5791  3134     0 -3134 -5791 -7566 -8190 -7566 -5791 -3134
 0.506366571:     0  3134  5791  7566  8190  7566  5791  3134     0 -3134 -5791 -7566 -8190 -7566 -5791 -3134
 1.527446318:     0  3134  5792  7567  8191  7567  5792  3134     0 -3134 -5792 -7567 -8191 -7567 -5792 -3134  << original
« Last Edit: June 01, 2022, 03:49:47 am by hamster_nz »
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: oPossum, radiolistener

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #14 on: June 01, 2022, 03:53:24 am »

This might be a tiny bit better, but has a tiny phase shift:

Code: [Select]
1  3135  5792  7567  8190  7566  5790  3133    -1 -3135 -5792 -7567 -8190 -7566 -5790 -3133
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #15 on: June 01, 2022, 04:32:23 am »
I'm still not sure what the point of this topic is. There are an infinite number of phase shifts you can try and some may work better than others in specific test cases, but you always have some rounding errors. If you want to minimize quantization error that's pretty easy to do mathematically. If you want to generate a specific frequency and phase shift, that's also pretty easy to do, but like others said, there is no "ideal sine wave". You can re-write the program I gave you in any language and get the same results, but it seems pointless to worry about the harmonics noise as they can be filtered out in any good design. What's the main goal of generating the sine table?

The funny thing is that you don't even need a spectrum analyzer to test this, using any FFT analysis tool (numpy supports this) should give you the same results, so you can just simulate the whole thing and optimize it as much as you want, but again, I can't find a good reason for why you should.

Edit: I used 17 samples and ignoring the last 0, this results in almost exactly the same result as you posted initially:
[    0  3134  5792  7568  8192  7568  5792  3134     0 -3134 -5792 -7568 -8192 -7568 -5792 -3134     0]
« Last Edit: June 01, 2022, 04:43:24 am by OM222O »
 
The following users thanked this post: Someone

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #16 on: June 01, 2022, 04:32:58 am »
Would you mind looking at this set of values?

Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134

If they are better than the original I'll share the code that found them...

wow, it has -105.25 dBc for 3'rd harmonic and -100.25 dBc for 5'th harmonic
Almost all harmonics below -100 dBc, except 7'th which is -99.05 dBc...

How to calculate it? :)
If you dont mind losing some gain:
[0 3107 5741 7501 8119 7501 5741 3107 0 -3107 -5741 -7501 -8119 -7501 -5741 -3107]

Many ways to skin this cat!
 
The following users thanked this post: radiolistener

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #17 on: June 01, 2022, 04:35:36 am »
If you want to minimize quantization error that's pretty easy to do mathematically.
Except it isnt! The cost function has many local minima, so there is no easy method.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #18 on: June 01, 2022, 06:01:12 am »
Edit: I used 17 samples and ignoring the last 0, this results in almost exactly the same result as you posted initially:
[    0  3134  5792  7568  8192  7568  5792  3134     0 -3134 -5792 -7568 -8192 -7568 -5792 -3134     0]
... except 8192 and -8192 is out of range. :)

If your DAC's reconstruction filter is significantly  filtering the 3rd harmonic of f/16 sine wave it might be a little too aggressive... or you don't care about using the full requency performance of your DAC.
« Last Edit: June 01, 2022, 06:07:12 am by hamster_nz »
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: Someone

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #19 on: June 01, 2022, 06:12:49 am »
Edit: I used 17 samples and ignoring the last 0, this results in almost exactly the same result as you posted initially:
[    0  3134  5792  7568  8192  7568  5792  3134     0 -3134 -5792 -7568 -8192 -7568 -5792 -3134     0]
... except 8192 and -8192 is out of range. :)

If your DAC's reconstruction filter is significantly  filtering the 3rd harmonic of f/16 sine wave it might be a little too aggressive... or you don't care about using the full requency performance of your DAC.

I forgot to add a range check, but that's pretty easy:

Code: [Select]
integer_arr[np.where(integer_arr> HIGH)] = HIGH
integer_arr[np.where(integer_arr< LOW)] = LOW

where HIGH and LOW are the limits. This is another weird thing because ADCs don't accept negative values as far as I know, so typically I add 1 to the sine values to shift the range from (-1 to 1) to (0 to 2) and then scale by (2^bits)-1.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7726
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #20 on: June 01, 2022, 06:32:06 am »

I forgot to add a range check, but that's pretty easy:

Code: [Select]
integer_arr[np.where(integer_arr> HIGH)] = HIGH
integer_arr[np.where(integer_arr< LOW)] = LOW

where HIGH and LOW are the limits. This is another weird thing because ADCs don't accept negative values as far as I know, so typically I add 1 to the sine values to shift the range from (-1 to 1) to (0 to 2) and then scale by (2^bits)-1.
Once again, such patching distorts the sine wave.
Do the all the math properly and you will properly hit your targets.
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #21 on: June 01, 2022, 06:55:52 am »
Once again, such patching distorts the sine wave.
Do the all the math properly and you will properly hit your targets.

Code: [Select]
import numpy as np
idx = np.arange(17) # Number of samples per period
sf = idx.max() / (2*np.pi) # scaling factor for the period
arr = idx/sf # create a new array that goes from 0 to 2 pi in N steps
arr = np.sin(arr) # calculate the sin value for each point
integer_arr = np.round((arr*((2**13)-1))).astype('int32') # Scale the array by (2^13)-1 and then round the values(the actual sine values go between -1 and 1, giving you an effective range of 2, so you need to scale by max int/2)
print(integer_arr[:-1])

[    0  3135  5792  7567  8191  7567  5792  3135     0 -3135 -5792 -7567 -8191 -7567 -5792 -3135]

fun fact, removing the round produces EXACTLY what he initially posted, so I'm fairly sure that's how the table was made
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #22 on: June 01, 2022, 07:42:45 am »
I'm still not sure what the point of this topic is. There are an infinite number of phase shifts you can try and some may work better than others in specific test cases, but you always have some rounding errors.

The point of topic is how to generate sine wave for specific resolution (in my case 14 bit, 16 samples per period). To get as low as possible distortion/harmonics. I need it as a test vector. As you can see simple use of round or floor function doesn't provide best result.

If you want to minimize quantization error that's pretty easy to do mathematically. If you want to generate a specific frequency and phase shift, that's also pretty easy to do, but like others said, there is no "ideal sine wave".

Quantization error and phase shift doesn't matter for me. The goal is to get minimum possible sine distortion/harmonics level. I need a test vector for testing DSP block. DSP block works with much higher resolution than 14 bit of ADC source. I want to get maximum possible from 14 bit source.

Since I want to get ideal sine for specific conditions - 14-bit and 16 samples per period, ideal sample with sine definitely exists. Because one sample is better than other. So, some sample is the best. And I needs the way to calculate it.

You can re-write the program I gave you in any language and get the same results

I tried to use your program with online python compiler, include fix for sample length. But it produce dirty sine, I have no idea what is the reason for that.

but it seems pointless to worry about the harmonics noise as they can be filtered out in any good design. What's the main goal of generating the sine table?

No it cannot be filtered, I need a clean test vectors for testing DSP block. DSP block has 64-bit fixed point resolution and I want to see every small artifact which happens in signal processing. Such as spurs/harmonics.

The funny thing is that you don't even need a spectrum analyzer to test this, using any FFT analysis tool (numpy supports this) should give you the same results, so you can just simulate the whole thing and optimize it as much as you want, but again, I can't find a good reason for why you should.

Spectrum analyzer which I use is FFT based and is a part of software/gateware which I want to test with these test vectors :)

By the way, my FFT can have some errors, because I'm using some speed optimizations. I tested it and it seems that errors are below noticeable level, but who knows...
« Last Edit: June 01, 2022, 08:04:55 am by radiolistener »
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #23 on: June 01, 2022, 07:53:46 am »
fun fact, removing the round produces EXACTLY what he initially posted, so I'm fairly sure that's how the table was made

No, just tested your code with removed round on online python compiller, the result is:
Code: [Select]
0  3134  5791  7567  8191  7567  5791  3134     0 -3134 -5791 -7567 -8191 -7567 -5791 -3134
But original sample has 5792 instead 5791

Here is original sample:
Code: [Select]
0 3134 5792 7567 8191 7567 5792 3134 0 -3134 -5792 -7567 -8191 -7567 -5792 -3134
Are you sure that your code produce the same?
« Last Edit: June 01, 2022, 08:01:21 am by radiolistener »
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #24 on: June 01, 2022, 08:04:51 am »
you're right, I missed the 1 difference since I was having a quick look, but for all intends and purposes that is HOW to generate a sine table. rounding, phase offset, scaling, etc. is the parameters you can play with if you want to optimize something
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #25 on: June 01, 2022, 08:11:22 am »
you're right, I missed the 1 difference since I was having a quick look, but for all intends and purposes that is HOW to generate a sine table. rounding, phase offset, scaling, etc. is the parameters you can play with if you want to optimize something

I already played with it a long time, and cannot find the way how version which starts from 0 3134 5792 is calculated. But this version has more clean sine than version calculated with simple round or without round.

Original sample has  -95.83 dBc for 3'rd harmonic. While round version has -87.91 dBc which is worse for almost 8 dB.

And now hamster_nz provided even more clean sine:
Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134
it has third harmonic -105 dBc!  :-+

The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

Any idea why usual calculation with round produce result which is not the best?
« Last Edit: June 01, 2022, 08:22:58 am by radiolistener »
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #26 on: June 01, 2022, 08:35:03 am »
I'll leave the PC looking for better for a while, but this one seems to be quite good!

Code: [Select]
1598  4551  6811  8034  8034  6811  4551  1598 -1598 -4551 -6811 -8034 -8034 -6811 -4551 -1598

About 1/20th the  RMS error of the original. Be interested what your design makes of it.

(note that it is still scaled by 8181, but because of the phase the max value doesn't appear in the numbers...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: radiolistener

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #27 on: June 01, 2022, 08:53:19 am »
Any idea why usual calculation with round produce result which is not the best?

It is because of interplay between the rounding alters the level of the fundamental.

That and we are truly playing around it the weeds...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #28 on: June 01, 2022, 09:13:03 am »
I'll leave the PC looking for better for a while, but this one seems to be quite good!

Code: [Select]
1598  4551  6811  8034  8034  6811  4551  1598 -1598 -4551 -6811 -8034 -8034 -6811 -4551 -1598

About 1/20th the  RMS error of the original. Be interested what your design makes of it.

(note that it is still scaled by 8181, but because of the phase the max value doesn't appear in the numbers...

Yes. All harmonics are bellow -103 dBc!   :-+
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #29 on: June 01, 2022, 09:21:13 am »
you're right, I missed the 1 difference since I was having a quick look, but for all intends and purposes that is HOW to generate a sine table. rounding, phase offset, scaling, etc. is the parameters you can play with if you want to optimize something

I already played with it a long time, and cannot find the way how version which starts from 0 3134 5792 is calculated. But this version has more clean sine than version calculated with simple round or without round.

Original sample has  -95.83 dBc for 3'rd harmonic. While round version has -87.91 dBc which is worse for almost 8 dB.

And now hamster_nz provided even more clean sine:
Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134
it has third harmonic -105 dBc!  :-+

The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

Any idea why usual calculation with round produce result which is not the best?

To be honest I've always hated math and avoided it like the plague, but there's a much better method than brute force. For each offset (the variable) you can add up the decimal part of each sampling point (cost) and use something like stochastic gradient decent with momentum or some other optimization / approximation algorithm to minimize the cost. there are lots of examples of SGD with python online, so give those a shot. Brute force algorithm is bad because there are an infinite number of phase offsets you can try, but optimization algorithms converge fairly quickly. This minimizes quantization noise, but again, I'm not sure why you would want to do this.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #30 on: June 01, 2022, 10:01:04 am »
you're right, I missed the 1 difference since I was having a quick look, but for all intends and purposes that is HOW to generate a sine table. rounding, phase offset, scaling, etc. is the parameters you can play with if you want to optimize something

I already played with it a long time, and cannot find the way how version which starts from 0 3134 5792 is calculated. But this version has more clean sine than version calculated with simple round or without round.

Original sample has  -95.83 dBc for 3'rd harmonic. While round version has -87.91 dBc which is worse for almost 8 dB.

And now hamster_nz provided even more clean sine:
Code: [Select]
0  3134  5791  7566  8190  7566  5791  3134  0  -3134  -5791  -7566  -8190  -7566  -5791  -3134
it has third harmonic -105 dBc!  :-+

The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

Any idea why usual calculation with round produce result which is not the best?

To be honest I've always hated math and avoided it like the plague, but there's a much better method than brute force. For each offset (the variable) you can add up the decimal part of each sampling point (cost) and use something like stochastic gradient decent with momentum or some other optimization / approximation algorithm to minimize the cost. there are lots of examples of SGD with python online, so give those a shot. Brute force algorithm is bad because there are an infinite number of phase offsets you can try, but optimization algorithms converge fairly quickly. This minimizes quantization noise, but again,...
Talk is cheap - feel free to find the optimal answer :). I know there is at least one better set of values out there for you to find, and I would be very interested in seeing your code.

Quote
... I'm not sure why you would want to do this.

Sometimes just playing with numbers is fun - and it is exploring a bit of the landscape where some of my own problems sometimes live.

It might come in useful one day, for exactly the same sort of reasons it is useful for Radiolistener. Then again it might not  :-//
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: Someone, 2N3055

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #31 on: June 01, 2022, 10:18:11 am »
The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

Any idea why usual calculation with round produce result which is not the best?
To be honest I've always hated math and avoided it like the plague, but there's a much better method than brute force. For each offset (the variable) you can add up the decimal part of each sampling point (cost) and use something like stochastic gradient decent with momentum or some other optimization / approximation algorithm to minimize the cost. there are lots of examples of SGD with python online, so give those a shot. Brute force algorithm is bad because there are an infinite number of phase offsets you can try, but optimization algorithms converge fairly quickly. This minimizes quantization noise, but again, I'm not sure why you would want to do this.
As already said:

If you want to minimize quantization error that's pretty easy to do mathematically.
Except it isnt! The cost function has many local minima, so there is no easy method.
If you'd like to propose a solution to this problem that has a convex solution we're all ears. But quantisation noise is still an unsolved optimisation problem, gradient descent fails hard.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #32 on: June 01, 2022, 10:43:45 am »
The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

That's mathematically impossible. The best you can do is search a state space systematically for good candidates, then do a 2D gradient descent on amplitude and phase from the best candidates to find their local optimums.
 
The following users thanked this post: Someone, rstofer

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #33 on: June 01, 2022, 11:05:19 am »
The only problem is that it was generated with brute-force method. I'm looking the way to generate it with math expression.

Any idea why usual calculation with round produce result which is not the best?
To be honest I've always hated math and avoided it like the plague, but there's a much better method than brute force. For each offset (the variable) you can add up the decimal part of each sampling point (cost) and use something like stochastic gradient decent with momentum or some other optimization / approximation algorithm to minimize the cost. there are lots of examples of SGD with python online, so give those a shot. Brute force algorithm is bad because there are an infinite number of phase offsets you can try, but optimization algorithms converge fairly quickly. This minimizes quantization noise, but again, I'm not sure why you would want to do this.
As already said:

If you want to minimize quantization error that's pretty easy to do mathematically.
Except it isnt! The cost function has many local minima, so there is no easy method.
If you'd like to propose a solution to this problem that has a convex solution we're all ears. But quantisation noise is still an unsolved optimisation problem, gradient descent fails hard.
A very quick and dirty plot of the rms error with phase going from 0 to pi in 1000 steps shows a clear repeating pattern:


So realistically you only need to consider the first 100 steps or so, after that all of the answers are repeated and the same. I changed it to calculate with 10000 steps. 9358th value (phase offset of 2.9399024052293283 radians) had the lowest RMS error (0.5637751689365491) and the actual values were:

[ 1641 -1555 -4514 -6786 -8025 -8042 -6835 -4587 -1641  1555  4514  6786 8025  8042  6835  4587]

if you want to get an even better result, you can keep repeating this over a smaller and smaller range but I'll leave it here. The actual noise of the DAC and filter components will have a much higher impact in any real design, so this is all entirely pointless.

Edit: code for those who are curious
Code: [Select]
import numpy as np
from matplotlib import pyplot as plt

def create_table(gain, phase, points):
    idx = np.arange(points+1)
    sf = idx.max() / (2*np.pi)
    arr = idx[:-1]/sf
    arr = np.sin(arr + phase)*gain
    integer_arr = np.round(arr).astype('int32')
    return (arr, integer_arr)

def calculate_error(arr, integer_arr):
    diff = integer_arr - arr
    err = np.sqrt((diff**2).sum())
    return err

n = 10000
errors = []
increment = np.pi/n
for i in range(n):
    arr, integer_arr = create_table((2**13)-1, increment*i, 16)
    errors.append(calculate_error(arr, integer_arr))
errors = np.array(errors)
   
plt.figure(figsize=(16,9))
plt.plot(np.arange(len(errors)),errors)
plt.show()

arr, integer_arr = create_table((2**13)-1, increment*np.argmin(errors), 16)
print(np.argmin(errors))
print(increment*np.argmin(errors))
print(calculate_error(arr, integer_arr))
print(integer_arr)

If you want to run it online, remove the matplotlib import and the 3 plot lines (plt.figure, plt.plot and plt.show)

Edit 2: [ 1641  4587  6835  8042  8025  6786  4514  1555 -1641 -4587 -6835 -8042 -8025 -6786 -4514 -1555] is even better (100k steps over the first 0.4 radians)
« Last Edit: June 01, 2022, 11:21:18 am by OM222O »
 
The following users thanked this post: radiolistener

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7726
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #34 on: June 01, 2022, 04:46:00 pm »
Quote
... I'm not sure why you would want to do this.

Sometimes just playing with numbers is fun - and it is exploring a bit of the landscape where some of my own problems sometimes live.

It might come in useful one day, for exactly the same sort of reasons it is useful for Radiolistener. Then again it might not  :-//

Here is a use: Using this sine table as a reference for multimode digital QAM modulator, every DB you can extract in purity especially with high quantization levels, the sturdier the SNR of your decoded data on the other side.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: how to properly generate ideal sine test sample?
« Reply #35 on: June 01, 2022, 05:11:15 pm »
this results in:
[    0  3331  6087  7791  8147  7094  4815  1703 -1703 -4815 -7094 -8147 -7791 -6087 -3331     0]
you can easily change the number of samples , add phase offset or change the scaling factor etc.

2 sequential samples with the value 0 in the 16 phase loop?
Wouldn't that generate substantial distortion?

Yes, it's 16 samples while the period is computed over 15 samples. Looks like a typical off-by-one issue.



0 and 2 pi are the same point effectively. If this is an issue you can set the array to be of lenght N+1 and ignore the last value

Of course, but that means you only have 15 effective samples per period, and not 16.
 

Online rstofer

  • Super Contributor
  • ***
  • Posts: 9889
  • Country: us
Re: how to properly generate ideal sine test sample?
« Reply #36 on: June 01, 2022, 05:32:05 pm »
Edit: code for those who are curious

I was!  I am beginning to think there is some merit to Python, specifically in the libraries.  numpy and matplotlib are great examples.
 

Offline OM222O

  • Frequent Contributor
  • **
  • Posts: 768
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #37 on: June 01, 2022, 09:21:44 pm »
Edit: code for those who are curious

I was!  I am beginning to think there is some merit to Python, specifically in the libraries.  numpy and matplotlib are great examples.

For data analysis / scientific computing Python is the go-to language. It's extremely flexible and if performance is an issue for large computations, you can create C extensions for it using something like Cython. Numpy is actually written in C because it's used to crunch numbers.

I realized there is a small mistake in the code I posted. To calculate the RMS error, I didn't divide by N .The actual line should be:
 
Code: [Select]
err = np.sqrt((diff**2).sum()/len(arr))but this doesn't actually change the results.
« Last Edit: June 01, 2022, 10:23:22 pm by OM222O »
 
The following users thanked this post: radiolistener

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #38 on: June 01, 2022, 11:53:46 pm »
Edit 2: [ 1641  4587  6835  8042  8025  6786  4514  1555 -1641 -4587 -6835 -8042 -8025 -6786 -4514 -1555] is even better (100k steps over the first 0.4 radians)

thanks, your version has -101.02 dBc for 3'rd harmonic.
And version generated with fixed code has -101.02 dBc for 3'rd harmonic.

By the way, hamster_nz provided me (in private message) with version which is even more clean: -109.13 dBc for 3'rd harmonic :)
« Last Edit: June 02, 2022, 12:30:17 am by radiolistener »
 

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #39 on: June 02, 2022, 12:37:10 am »
Edit 2: [ 1641  4587  6835  8042  8025  6786  4514  1555 -1641 -4587 -6835 -8042 -8025 -6786 -4514 -1555] is even better (100k steps over the first 0.4 radians)

thanks, your version has -101.02 dBc for 3'rd and -105.96 dBc for 7'th harmonic.
And version generated with fixed code has -101.02 dBc for 3'rd and -105.97 dBc for 7'th harmonic.

By the way, hamster_nz provided me (in private message) with version which is even more clean: -109.13 dBc for 3'rd and -105.97 dBc for 7'th harmonic :)
Still not surpassing the earlier example:
[0 3107 5741 7501 8119 7501 5741 3107 0 -3107 -5741 -7501 -8119 -7501 -5741 -3107]
If you have a preference for maximum suppression in the 3rd harmonic it can be pushed at least -148dB (likely further).
 
The following users thanked this post: radiolistener

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #40 on: June 02, 2022, 06:13:13 am »
If you have a preference for maximum suppression in the 3rd harmonic it can be pushed at least -148dB (likely further).

How? It will be useful for filter testing.
« Last Edit: June 02, 2022, 07:03:38 am by radiolistener »
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #41 on: June 02, 2022, 06:52:06 am »
Still not surpassing the earlier example:
[0 3107 5741 7501 8119 7501 5741 3107 0 -3107 -5741 -7501 -8119 -7501 -5741 -3107]

wow, I didn't noticed this one. It is not full scale -0.08 dBFS, but its SFDR is 119.56 dB :)
How did you generated it?

PS: I was changed approach to find the harmonic index, previously I was used search local extremum around expected index, but such way is buggy, because it catch noise peaks near harmonic position. Now I just use N*index, where index is the first harmonic index. So, now it displays NaN dB for even harmonics (missing harmonic). The same value for even harmonics it displays for other sine examples provided it this topic. Odd harmonics levels with a new approach is almost the same.
« Last Edit: June 02, 2022, 06:58:44 am by radiolistener »
 

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #42 on: June 02, 2022, 07:15:01 am »
Still not surpassing the earlier example:
[0 3107 5741 7501 8119 7501 5741 3107 0 -3107 -5741 -7501 -8119 -7501 -5741 -3107]
wow, I didn't noticed this one. It is not full scale -0.08 dBFS, but its SFDR is 119.56 dB :)
How did you generated it?
That one is relatively quick to find with a simple sweep of amplitude selecting the lowest RMS error, optimisation with phase or directly on the points finds the same answer.
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 4946
  • Country: si
Re: how to properly generate ideal sine test sample?
« Reply #43 on: June 02, 2022, 08:10:35 am »
If you are using this to test DAC then looping a single sine wave sample is the wrong way to do it.

This runs the converter trough the same cycle of codes, so it can give a false idea of the performance as it might hit codes that are particularly good or ones that are particularly bad.

The proper way requires generating a sinewave that is not divisible with the sample rate. In that case each wave of the sine wave passes trough different values, this exercises many more unique DAC codes. The spectral analysis can then be done over a window of 1000s of these sine waves to capture the average spectrum.

Generating the test data for this is quite a bit harder tho. You either have to hold a really long waveform in memory or calculate the sinewave on the fly. This involves things like CORDIC algorithms. I would generally just cheat here and use the Altera NCO IP Block that implements all of this (Including showing you a FFT of its performance while you are adjusting the design constraints)
« Last Edit: June 02, 2022, 08:13:01 am by Berni »
 
The following users thanked this post: Someone

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #44 on: June 02, 2022, 09:46:27 pm »
Still not surpassing the earlier example:
[0 3107 5741 7501 8119 7501 5741 3107 0 -3107 -5741 -7501 -8119 -7501 -5741 -3107]
wow, I didn't noticed this one. It is not full scale -0.08 dBFS, but its SFDR is 119.56 dB :)
How did you generated it?
That one is relatively quick to find with a simple sweep of amplitude selecting the lowest RMS error, optimisation with phase or directly on the points finds the same answer.

That is very impressive! I'm going to file that away,

it may be useful as a practical joke to play on somebody....  If I tested my FFT code on that vector and saw those results I would swear that it was broken!
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #45 on: June 03, 2022, 04:58:10 am »
Here's a signal with low harmonics, and amplitude > +/-8191, but all samples within the range of +/- 8191.

Code: [Select]
1236,  4249,  6615,  7974,  8119,  7028,  4867,  1965, -1236, -4249, -6615, -7974, -8119, -7028, -4867, -1965

Might make an interesting way to look for odd math overflows in a DSP design.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: Someone, radiolistener

Offline cbutlera

  • Regular Contributor
  • *
  • Posts: 104
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #46 on: June 06, 2022, 04:22:34 pm »
If you are using this to test DAC then looping a single sine wave sample is the wrong way to do it.

This runs the converter trough the same cycle of codes, so it can give a false idea of the performance as it might hit codes that are particularly good or ones that are particularly bad.

The proper way requires generating a sinewave that is not divisible with the sample rate. In that case each wave of the sine wave passes trough different values, this exercises many more unique DAC codes. The spectral analysis can then be done over a window of 1000s of these sine waves to capture the average spectrum.

Generating the test data for this is quite a bit harder tho. You either have to hold a really long waveform in memory or calculate the sinewave on the fly. This involves things like CORDIC algorithms. I would generally just cheat here and use the Altera NCO IP Block that implements all of this (Including showing you a FFT of its performance while you are adjusting the design constraints)

Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.
 
The following users thanked this post: Berni, BrianHG, radiolistener, fourfathom

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #47 on: June 07, 2022, 05:38:23 am »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 7726
  • Country: ca
Re: how to properly generate ideal sine test sample?
« Reply #48 on: June 07, 2022, 05:48:47 am »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?
I think we covered this in an old MCU thread on how to generate a sine wave without any floating point or multiplication hardware.  After LUT and interpolation, we finally reached something similar to this, but with only 16 bit ints.
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 4946
  • Country: si
Re: how to properly generate ideal sine test sample?
« Reply #49 on: June 07, 2022, 06:38:43 am »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?

Nice trick there. The results are impressive, Cheap to implement in a FPGA too.

By the looks of it this is two integrators looped into each other. This sort of setup oscillates infinitely as the values move from one integrator to the other and back again (much like a ideal LC circuit with 0 damping). Similar to how a NOT gate ring oscillator works except this is in "analog values"
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4028
  • Country: nz
Re: how to properly generate ideal sine test sample?
« Reply #50 on: June 07, 2022, 07:13:48 am »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?

Nice trick there. The results are impressive, Cheap to implement in a FPGA too.

By the looks of it this is two integrators looped into each other. This sort of setup oscillates infinitely as the values move from one integrator to the other and back again (much like a ideal LC circuit with 0 damping). Similar to how a NOT gate ring oscillator works except this is in "analog values"

It's a rotation matrix, sneakily using the already-updated value of x for the -sin(theta) bit. Or two shearing matrix concatenated.
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #51 on: June 07, 2022, 07:49:00 am »
I see different initial value doesn't affects frequency, but affects amplitude in a non-obvious way.
How to select initial value for that algorithm?

Is there any way to change frequency but keep low distortions?
 

Offline cbutlera

  • Regular Contributor
  • *
  • Posts: 104
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #52 on: June 07, 2022, 07:56:37 am »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?

This is an algorithm that I discovered while playing around with numbers back in the 1980s.  I was writing DSP code for voice-band modems at the time, on quite limited hardware, so tricks like this looked interesting.

There is nothing special about the initial value for x.  It just sets the amplitude, although some choices can result in a little more variation in the amplitude with time than others.  There is also nothing special about the right shift.  It is just a 32 bit multiply by 0.5.

An example of the more general algorithm that can generate any arbitrary frequency with no significant long term amplitude drift is:

Code: [Select]
     p = 2 * sin(pi / n)
     x = 1;
     a = -p / 2;
     for (;;) {
          x += a * p;
          a -= x * p;
     }

Here x has an amplitude of 1, and n is the number of samples per cycle of the generated sine-wave.  The accumulated amplitude error over time is typically less than 1:1012 after 1012 samples, with all variables as IEEE 80-bit extended precision floating point.

The algorithm actually works with any fixed point or floating point number system.

Note that x and a are not orthogonal if regarded as simultaneous samples.  However, they are orthogonal as time interleaved samples, where the in-phase and quadrature DACs are alternately updated.

The key to the amplitude stability of this algorithm is that the frequency is set by a single constant, so the fact that this constant is of finite precision has no impact.  The only errors are those due to finite arithmetic, and those errors have no intrinsic bias to either increase or decrease the amplitude.  The accumulated amplitude error has the appearance of a random walk, so the area of the annulus containing the orthogonalised equivalent to x and a (let's call these x and y) only increases with the square root of the number of steps.  Therefore over time, the probability of the algorithm landing on an x, y pair that it has visited before increases.  Once this happens there will obviously be no further increase in the peak to peak variation of the amplitude.

Here is the maths (If I haven't fouled up the MathJax).

Assuming an amplitude of 1, a starting phase of zero and an angular increment of \$\theta\$, at iteration n we have:
$$
x_n=sin(n \theta) \\
y_n=cos(n \theta - \theta / 2)
$$
Using the standard expansion of \$cos(A+B)\$, \$y_n\$ can be written as
$$
y_n=cos(n \theta)cos(\theta/2 ) + sin(n \theta)sin(\theta/2)
$$
Similarly, \$y_{n+1}\$ can be written as:
$$
y_{n+1}=cos(n \theta + \theta / 2) \\
y_{n+1}=cos(n \theta)cos(\theta/2 ) - sin(n \theta)sin(\theta/2)
$$
So \$y_{n+1}\$ can therefore be calculated from \$y_n\$, \$x_n\$ and the constant \$2sin(\theta/2) \$:
$$
y_{n+1}=y_n - 2 sin(\theta/2)x_n
$$
Using the standard expansion of \$sin(A+B) \$, \$x_n \$ can be written as:
$$
x_n=sin(n \theta)=sin(n \theta + \theta / 2 - \theta / 2) \\
x_n=sin(n \theta + \theta / 2)cos(\theta / 2) - cos(n \theta + \theta / 2)sin(\theta / 2)
$$
Similarly, \$x_{n+1} \$ can be written as:
$$
x_{n+1}=sin(n \theta + \theta / 2 + \theta / 2) \\
x_{n+1}=sin(n \theta + \theta / 2)cos(\theta / 2) + cos(n \theta + \theta / 2)sin(\theta / 2)
$$
So \$x_{n+1} \$ can therefore be calculated from \$x_n \$, \$y_{n+1} \$ and the constant \$ 2sin(\theta/2) \$:
$$
x_{n+1}=x_n + 2sin(\theta/2)y_{n+1}
$$
 
The following users thanked this post: oPossum, Bassman59, hamster_nz, 2N3055, BrianHG, radiolistener

Offline cbutlera

  • Regular Contributor
  • *
  • Posts: 104
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #53 on: June 07, 2022, 08:24:00 am »
I see different initial value doesn't affects frequency, but affects amplitude in a non-obvious way.
How to select initial value for that algorithm?

With reference to the more general algorithm.

Code: [Select]
     p = 2 * sin(pi / n)
     x = 1;
     a = -p / 2;
     for (;;) {
          x += a * p;
          a -= x * p;
     }

If a is initialised to 0 the initial value for x is the required amplitude divided by cos(pi/n).  If a is initialised as above, then the initial value for x is just the amplitude.

That set's the overall amplitude, but the precise initialisation value can then be adjusted a little by trial and error, to minimise the amplitude noise.

The precise value also changes the cycle time of the algorithm before it repeats the same sequence of a/x pairs.  In the case of the first example I posted, with a starting x of 2079038281, the cycle time is a little over 500 million samples.
 
The following users thanked this post: hamster_nz, radiolistener

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: how to properly generate ideal sine test sample?
« Reply #54 on: June 07, 2022, 09:20:17 am »
.
« Last Edit: August 19, 2022, 05:31:19 pm by emece67 »
 
The following users thanked this post: cbutlera, radiolistener

Offline cbutlera

  • Regular Contributor
  • *
  • Posts: 104
  • Country: gb
Re: how to properly generate ideal sine test sample?
« Reply #55 on: June 07, 2022, 11:05:42 am »
AFAIK this algorithm is called the state-variable method. There was a recent thread here where it appeared https://www.eevblog.com/forum/programming/compact-sin(x)-which-gets-to-within-0-1-x-0-to-2xpi/msg3899078/#msg3899078

IMHO, its good behaviour here is due to its period (expressed as samples) being quite high. I'm referring here not to the period of the sampled sine, but the period of the sequence (how many samples it takes to repeat itself).

Thanks for finding that mention of the algorithm.  Do you know of a reference that investigates its stability?

The algorithm itself is almost trivial.  The interesting thing that I discovered back in 1980s was its long-term stability.  At the time, I assumed that what I had discovered was well known.  I'm still sure that I couldn't possibly have been the first to discover its long-term stability, but it would be nice to have a reference that dates from before the mid 1980s, so that I could know for sure.

After reading this thread, I tried to search for a reference to it myself, and all that I could find was a possible link to a 1996 paper by John Edwards https://www.embedded.com/whats-your-sine-finding-the-right-algorithm-for-digital-frequency-synthesis-on-a-dsp/

In the mid to late 1980s, John Edwards was a student on summer placement at the company where I was developing DSP code for voice-band modems.  He was assisting me with DSP code and algorithm modelling, so I would almost certainly have discussed what I had found with him.

EDIT:
Since writing the above, I have just been in touch with John Edwards https://www.numerix-dsp.com/.  It seems that the solution to the problem of error accumulation with recursive rotation that he proposed, and that was mentioned in the above link, is not the algorithm that I have been talking about.
« Last Edit: June 08, 2022, 08:02:39 pm by cbutlera »
 

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: how to properly generate ideal sine test sample?
« Reply #56 on: June 07, 2022, 01:46:39 pm »
.
« Last Edit: August 19, 2022, 05:31:28 pm by emece67 »
 
The following users thanked this post: cbutlera

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: how to properly generate ideal sine test sample?
« Reply #57 on: June 07, 2022, 03:06:49 pm »
.
« Last Edit: August 19, 2022, 05:31:34 pm by emece67 »
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14445
  • Country: fr
Re: how to properly generate ideal sine test sample?
« Reply #58 on: June 07, 2022, 05:57:22 pm »
Here is a simple algorithm that will generate a sine-wave with 12.433075... samples per cycle.  The amplitude of the sine-wave will vary a little over time, but will never differ by more than 1ppm from its initial value, no matter how long it runs.  So more than good enough to test a 14 bit DAC, and exercise almost every code.

Code: [Select]
     x = 2079038281;
     a = 0;
     for (;;) {
          x += (a >> 1);
          a -= (x >> 1);
     }

'x' and 'a' are 32 bit signed integers. The DAC samples are the upper 14 bits of either 'x' or 'a' and have an amplitude of 8191. To avoid a 1/2 LSB DC offset in the output, add 0x20000 to the value in 'x' or 'a' before extracting the upper 14 bits.

this is a real magic  :o

How it works?

Nice trick there. The results are impressive, Cheap to implement in a FPGA too.

By the looks of it this is two integrators looped into each other. This sort of setup oscillates infinitely as the values move from one integrator to the other and back again (much like a ideal LC circuit with 0 damping). Similar to how a NOT gate ring oscillator works except this is in "analog values"

It's a rotation matrix, sneakily using the already-updated value of x for the -sin(theta) bit. Or two shearing matrix concatenated.

Which in its given form, I also consider a particular case of CORDIC.
 
The following users thanked this post: Someone

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #59 on: June 07, 2022, 11:18:29 pm »
this is simply to test the idea that higher period sequences do behave better.
Longer sequences can have lower noise in any given bin, but its just processing gain:
https://ccrma.stanford.edu/~jos/sasp/Processing_Gain.html
That is not a guarantee the noise will be lower just by having a longer sequence.

The OP was trying to work out how to find the idealized rounding, there is not a direct solution. Rounding to integers usually introduces correlated noise, which does not disappear through processing gain.
 

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4525
  • Country: au
    • send complaints here
Re: how to properly generate ideal sine test sample?
« Reply #60 on: June 07, 2022, 11:43:52 pm »
It's a rotation matrix, sneakily using the already-updated value of x for the -sin(theta) bit. Or two shearing matrix concatenated.
Which in its given form, I also consider a particular case of CORDIC.
Expanding out the equation I didnt get the classical Toeplitz symmetry of a rotation matrix:
cos(a) -sin(a)
sin(a)  cos(a)


but instead, expanding the given code:
the more general algorithm.
Code: [Select]
     p = 2 * sin(pi / n)
     x = 1;
     a = -p / 2;
     for (;;) {
          x += a * p;
          a -= x * p;
     }
gives something decidedly not symmetric:
  1   p
-p  1-p^2


* sorry, cant be bothered to work out mathjax formatting!
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #61 on: June 08, 2022, 10:14:35 am »
despite the fact it has no harmonics, the noise (at noise floor) is very synthetic and contains a lot of a small spurs.
Is it possible to add some dithering in order to get "soft" and flat noise floor?
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 4946
  • Country: si
Re: how to properly generate ideal sine test sample?
« Reply #62 on: June 08, 2022, 11:15:42 am »
despite the fact it has no harmonics, the noise (at noise floor) is very synthetic and contains a lot of a small spurs.
Is it possible to add some dithering in order to get "soft" and flat noise floor?

14bit only has has a dynamic range of 86dB so the noise floor being down there in the -125dB range means it is performing many times better than you wanted in the original post.
 

Offline radiolistenerTopic starter

  • Super Contributor
  • ***
  • Posts: 3343
  • Country: ua
Re: how to properly generate ideal sine test sample?
« Reply #63 on: June 08, 2022, 11:22:12 am »
14bit only has has a dynamic range of 86dB so the noise floor being down there in the -125dB range means it is performing many times better than you wanted in the original post.

Actually it has -111 dBFS noise floor at FFT size 8192.
So, there is process gain about 10*log10(8192/2)=36.12 dB and normalized SNR = 111-36 = 75 dBFS.

The picture above shows RMS noise for a small piece of bandwidth = 2.7 kHz, while ADC sample rate is 96000 kHz, so it shows noise level with process gain 10*log10(48000/2.7) = 42.5 dB. And normalized SNR is about 126-42.5 = 83.5 dBFS.

But I want to get rid of these spurs, to not confuse it with images from signal processing... its not an issue if the noise will be a little higher to hide these spurs.
« Last Edit: June 08, 2022, 11:41:20 am by radiolistener »
 

Offline Berni

  • Super Contributor
  • ***
  • Posts: 4946
  • Country: si
Re: how to properly generate ideal sine test sample?
« Reply #64 on: June 08, 2022, 11:44:17 am »
You can sum in some random white noise to mask it using a larger flat noise floor.

Still this is a good result on actual hardware. Getting any significantly better noise floors out of it likely involves narrow bandpass filters to select just the frequency you want.
 
The following users thanked this post: SiliconWizard


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf