Signal Cancellation for Spurs Works!

1,065 views
Skip to first unread message

Steve Haynal

unread,
Nov 16, 2015, 2:24:08 AM11/16/15
to Hermes-Lite
Hi List,

This weekend I was able to do some signal cancellation experiments to reduce the spur at 22.981 MHz when transmitting at 24.895 MHz. The summary is that it looks very possible to reduce this by 15 dB, -53 dBc down to -68 dBc! The 22.981 MHz spur is due to imperfections in the DAC, and is the 5th harmonic which occurs in the second Nyquist zone but aliased back into the first Nyquist zone. To enable experimentation, I added a small RAM memory "wave table" in the FPGA firmware. I hacked Quisk to load this wave table with whatever values I like. I can create sine waves with different levels and phases of predistortion with Python and then load them into the wave table. When the CW key is pressed, I can test the output. Because of the limited depth of the wave table, not all frequencies are possible, but enough to run experiments. I created a sine wave of the fundamental plus the 5th harmonic. I empirically varied the amplitude and phase of the 5th harmonic until I saw the best cancellation on my scope-based spectrum analyzer.  

The results appear very stable -- the required phase and amplitude do not seem to change much over time and other variations. I still need to test more variations, but I am convinced that it is stable enough not to require a fast loop on the FPGA. Software should be able to make adjustments every so often and do a good job. Unfortunately, I see different results from my spectrum analyzer compared to Quisk. In full-duplex, I can tune Quisk to look at the spurs with the receiver. I suspect that since the ADC and DAC are in the same package for the AD9866, there is less isolation between TX and RX when observing the AD9866 TX signal with the AD9866 RX. When I have the tap in place (not proximity OTA pickup), readings from Quisk and the spectrum analyzer may agree more.

I am quite excited about this result, and want to see if I can predistort to diminish the harmonics introduced by the IAMP and eventually other amplifiers. I will look at 40M this week with that in mind.

The challenge of making this practical is generating the fundamental with the proper predistortion. This is different than pure signal, as it is not distorting the audio (in the kHz) to improve IMD, but is distorting the fundamental (in the MHz) to overcome nonlinearities in the DAC and amplifiers. Still, I am optimistic it is possible and am thinking about three techniques:
  1. The fundamental is currently produced by a Cordic algorithm. I've studied the Cordic and think it is possible to generate not a pure sinusoid, but a sum of sinusoids, provided they are all harmonics. This requires computing something more complicated than an ArcTan, coming up with a different set of degree steps that map to simple integer divisions and not restricting the computation to a single quadrant. I have what to do all worked out in my head but need to write some code to test it. This would be the most direct replacement for what exists in the Hermes-Lite. I'd like to make the parameters programmable so that software can tune for various hardware and frequencies.
  2. An extension of what I am doing now is just to always use a wave table for transmission. I think it is possible to arrange a table deep enough and with variable depth used such that a rich set of IF frequencies can be generated. This could not be used by software that only supports 0 IF. I believe PowerSDR is in that bucket. Fortunately, Quisk and Alan's software allow for receivers that are not 0 IF. The wave table must have steps smaller than the minimum bandwidth (48 kHz) to cover the entire spectrum.
  3. The numerically controller oscillators (NCOs) provided by FPGA vendors usually have a range of options from designs requiring larger tables and few resources, to those requiring small tables and many resources. There are a number of these available, including several open source (1 2) IPs. I may need to study these to see what predistortion is possible.

73,

Steve
KF7O







in3otd

unread,
Nov 16, 2015, 3:07:37 AM11/16/15
to Hermes-Lite
Great work Steve!
I'm eager to test your improvements here; this weekend I finished assembling my H-L + frontend v1.2, everything seems to be working fine.

73 de Claudio, IN3OTD / DK1CG

Steve Dick

unread,
Nov 16, 2015, 8:04:25 AM11/16/15
to Steve Haynal, Hermes-Lite
Great job!! I came across this article related to your efforts:
 
“Digital Steve”, K1RF
--
You received this message because you are subscribed to the Google Groups "Hermes-Lite" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hermes-lite...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



Avast logo

This email has been checked for viruses by Avast antivirus software.
www.avast.com


Alan Hopper

unread,
Nov 16, 2015, 9:01:14 AM11/16/15
to Hermes-Lite
Steve,
great, this all sounds very promising. I wonder if a lookup table for phase and amplitude on the complex output of the final waveform (just before the real part is taken) would do the same job as option 1.  I guess with option 1 the frequencies used are simply the power series terms that describe the non linearity.

If I understand option two, the software would have to pick the closest frequency that the table could generate and frequency shift the iq data to compensate, not a problem.

73 Alan M6NNB

Rob Frohne

unread,
Nov 16, 2015, 12:00:39 PM11/16/15
to herme...@googlegroups.com
A great result Steve!

73,

Rob
KL7NA

John Laur

unread,
Nov 16, 2015, 5:49:57 PM11/16/15
to Hermes-Lite
Predistoring the LO is really something cool. The real question is how
it will hold up to stuff that isnt pure sinusoidal; have you been able
to test any IQ modulation yet?

I wonder if you could engage some type of sampling mode, calculate the
LO distortion table on the PC then send it back to populate the RAM.
How much RAM do you need? BeMicro is probably a non-issue but do you
anticipate needing an SRAM or something on the hermes lite? You could
continue to support zero IF this way if you can recalculate the
distortion table and reupload it after you change TX frequency. Plus
it would give you a lot more tuning options and rapid testing. I think
it might simplify the firmware a lot. But you're basically replacing
the CORDIC with a DDS arbitrary function generator at that point I
guess.

By the way, PowerSDR "likes" 0 IF but WDSP supports a frequency
shifting FIR so it could be modified to be non-zero IF for Tx. The
MultiRx feature implements a non-zero IF receiver.

73, John K5IT

Steve Haynal

unread,
Nov 17, 2015, 1:36:01 AM11/17/15
to Hermes-Lite, softerh...@gmail.com
Hi Steve,

This is the same article I linked to a few days ago and was the starting point for this experiment. I was hoping the amplitude computation for the cancellation signal would be accurate, but so far it is over estimating the required amplitude. With the 5th harmonic experiment, an amplitude variations involving only the 3 least significant bits were required.

73,

Steve
KF7O

Steve Haynal

unread,
Nov 17, 2015, 1:40:14 AM11/17/15
to Hermes-Lite
Hi Alan,

For option 2, a 512 entry table could cover frequencies down to 100 kHz and with the larger jump between two IFs of 52 kHz. Everything could be covered with at least a 96 kHz wide receiver.

73,

Steve
KF7O

Steve Haynal

unread,
Nov 17, 2015, 1:45:15 AM11/17/15
to Hermes-Lite
Hi John,

I don't foresee any problems with IQ. Do you have something specific in mind?

Yes, the basic idea is to have software sample periodically and update parameters or tables in the Hermes-Lite. This would happen during operation and frequency change.

You can't compute an arbitrary frequency with a finite table. A 512 entry table could cover frequencies down to 100 kHz and with the larger jump between two IFs of 52 kHz. Everything could be covered with at least a 96 kHz wide receiver.

I'm still optimistic about the modified Cordic as it would result in the least observable change from the outside.

73,

Steve
KF7O

Steve Haynal

unread,
Nov 17, 2015, 2:02:16 AM11/17/15
to Hermes-Lite
Hi List,

This evening I targeted the 3rd harmonic when transmitting at 7.03 MHz. With the IAMP set for 20 dBm out, I measure the 3rd harmonic at 40 dBc without any added cancellation term. I was able to reduce this harmonic to around 80 dBc at which point I stopped. Again, the required amplitude and required phase of the cancellation term felt very stable. The required phase agreed when minimizing with either Quisk or my scope-based spectrum analyzer. The required amplitude did not. Quisk required less amplitude to null the harmonic. I need to try the tap idea with the AD9866 RX as that is what will be used to minimize these harmonics in everyday use. 

I also remeasured the cancellation of yesterday's experiment right after I powered up as everything was cold. I still got 14-15 dB of cancellation as the spur was around 67 to 68 dBc. No adjustments of phase or amplitude were made from the settings determined yesterday.

This is getting closer to the dream of a filter-reduced QRP radio that covers all HF bands. We may be able to get by with just a good reconstruction filter in the low 30 MHz range. I want to try the TH6226 as an amp and see if its harmonics can also be cancelled. I will also see what happens with the version of John's PA that I have.

73,

Steve
KF7O

Alan Hopper

unread,
Nov 17, 2015, 2:17:34 AM11/17/15
to Hermes-Lite
Steve,
Brilliant, but why stop at 80dBc, have you no ambition :)
73 Alan M6NNB

Alan Hopper

unread,
Nov 17, 2015, 7:14:46 PM11/17/15
to Hermes-Lite
Steve,
Inspired by you success I got bolder with verilog and tried a lut on the dac data, I load the lut from my radio software by setting a number of power series coefficients.  I don't have a spectrum analyser so I tune to a low frequency and then open other receivers at 2f and 3f . I don't have any real measurements but see significant reductions in 2nd and 3rd harmonics just by tweaking the coefficients.  I think my next step is to feedback dac values directly (not through the receiver chain) and compare with the raw adc bandscope data, I suspect this data is needed to automate the process whatever method is used to remove the harmonics. 


 How are you sending table data? I'm putting an index and value in a cc packet but it is a bit slow.

73 Alan M6NNB

Steve Haynal

unread,
Nov 17, 2015, 7:22:51 PM11/17/15
to Hermes-Lite
Hi Alan,

Sounds great! It appears that the harmonics from the IAMP are the easiest to cancel. 

I am sending the data in the same way you are -- address in C1, values in C3,C4. It does take some time. This is an area for improvement.

I really like this description of NCOs.

73,

Steve
KF7O

Sid Boyce

unread,
Nov 17, 2015, 8:39:55 PM11/17/15
to herme...@googlegroups.com
Courtesy of N9VV.
http://www.ba4tb.qth.com/s9-c.html
Using the si570A.
73 ... Sid.

--
Sid Boyce ... Hamradio License G3VBV, Licensed Private Pilot
Emeritus IBM/Amdahl Mainframes and Sun/Fujitsu Servers Tech Support
Senior Staff Specialist, Cricket Coach
Microsoft Windows Free Zone - Linux used for all Computing Tasks

Alan Hopper

unread,
Nov 18, 2015, 1:58:00 PM11/18/15
to Hermes-Lite
Steve,
thanks for the nco link.  Just thought I'd share a pitfall that caught me out for a bit, if you monitor with a receiver that happens to be the pure signal dac feedback receiver(2 or 4) you get an inverse result as you see your own pre-distortion.

73 Alan M6NNB

Steve Haynal

unread,
Nov 18, 2015, 10:16:28 PM11/18/15
to Hermes-Lite
Hi John,

Here are details of why I am not worried about IQ.

The current Cordic can be thought of as rotating a vector (the IQ of the signal to TX: x,y) by some angle (the frequency to transmit on: phase W) to produce the data to send to the DAC (dac).

dac = x*cos(W) - y*sin(W)

Only the "real" part is used and the "imaginary" part, not shown above, is thrown away. See this presentation

For distortion, we are after dac+dacdistort where

dacdistort = x*An*cos(nW+U) - y*An*sin(nW+U)

for some small amplitude An, harmonic n and phase shift U. So,

dac+dacdistort = x*cos(W) - y*sin(W) + x*An*cos(nW+U) - y*An*sin(nW+U)

which can be rewritten simply as

dac+dacdistort = x*(cos(W)+An*cos(nW+U)) - y*(sin(W) + An*sin(nW+U))

the terms cos(W)+An*cos(nW+U) and sin(W) + An*sin(nW+U) are what I tried to produce with a modified cordic but will now store in tables of a NCO.

73,

Steve
KF7O





On Monday, November 16, 2015 at 2:49:57 PM UTC-8, John Laur K5IT wrote:

Steve Haynal

unread,
Nov 18, 2015, 11:12:26 PM11/18/15
to Hermes-Lite
Hi Alan,

Thanks for pointing out the difference with the pure signal receiver. It may explain some of my differences when using Quisk. Here is an update from my side:

My ideas to modify the Cordic did not work out. I wrote the code last night for what I had an mind and it works for the base case (single cos/sin terms), but not for any sum of cos/sin terms. I think my assumptions about the symmetry of the new sum of cos/sin was wrong. A unit circle is nice and smooth; 5 degrees of rotation looks the same from anywhere you start. What we have is a rough unit circle with little squiggles, and 5 degrees of rotation does not look the same from any starting point. 

I've spent enough time on the Cordic and am now focusing on a NCO with a software settable cos/sin table. With a fractional index, described here, and a table of 4096 to 8192 entries (about that much is available on the SDK, the tightest constraint), we should have good frequency resolution, but it probably will not be as good as the Cordic. There may be some frequencies we want to avoid. I think it will have similar characteristics to this NCO. I plan to run it at double the speed so one longer table can be used for both cos and sin and one multiplier can do the rotation. I may add an option for some dither as it can help as seen on page 4-3 of this document.

Regarding loading the table, what do you think of using the data intended for the audio out stream on the Hermes? We can specify a control word with address within the NCO RAM for accompanying 248 bytes of audio. The FPGA firmware will reroute the audio stream only when a valid "write block to NCO RAM" control is seen. It will take several packets, but at the 48kHz audio rate I estimate 50-100 ms to update the table. I don't think updates will have to happen frequently. Only before TX on a new band, or at the end of a TX every few minutes. 

I'm shooting to share firmware with this NCO and a hacked version of Quisk to update the NCO at the end of Thanksgiving vacation. This will still be manually setting and tweaking of the parameters but at least others will be able to try it out and share their experiences.

73,

Steve
KF7O

Alan Hopper

unread,
Nov 19, 2015, 10:51:40 AM11/19/15
to Hermes-Lite
Steve,
The unused audio packet idea is great, I'd forgotten we were sending all those zeros back and forwards.  I simply put index in the left channel and value in the right, not optimal but easy.  Changing my magic numbers now has a visibly instant effect.  I have played a little more, my very simple amplitude only output lut has a very significant effect on the 2nd harmonic but far less on the 3rd, I guess because I'm not correcting phase which will be more of a challenge.

Alan

John Laur

unread,
Nov 19, 2015, 1:58:57 PM11/19/15
to Steve Haynal, Hermes-Lite
Oh well yes I did not mean to say that doing the IQ modulation against
the arbitrary waveform would be much of a problem. I suppose it is
roughly the same technique that any modern DAC based signal generator
is doing. What I was more wondering about is how well the signal will
actually cancel the harmonics after the modulation is applied. CW is
the ideal case but SSB seems like really the worst case possible. For
a very highly linear amplifier such as the AD9866 IAMP I imagine it
would work well; for your average amateur 100W HF PA that starts being
driven into compression above 30W and the high PAPR of SSB it may
become a much more nuanced problem. However linearizing the driver
would still be worthwhile if the technique continues to hold up when
modulated.

I assume the final hardware would allow sampling for harmonic
cancellation with either the solid state RF switches before feeding a
high power PA or the AD9866 analog loopback, then allow for PureSignal
sampling to be done at the end of the PA chain, correct?

73, John K5IT

Steve Haynal

unread,
Nov 20, 2015, 12:24:18 AM11/20/15
to Hermes-Lite, softerh...@gmail.com
Hi John,

Sorry for not understanding your concern -- I thought you were concerned about how basic IQ operations would behave with a distorted LO. I agree that there is a lot left to understand and prove about this approach, especially with signals rich in amplitude and phase variation, or frequency content and change, or different amplifier gains, or reactive and varying loads, or hot or cold temperatures, or and the list can go on and on. But you have to start with small steps. Here is why I think this experimentation is worthwhile, even if doesn't hold up with all types of signal modulation.

First, I am someone who has only had 1 or 2 voice SSB QSOs in the last 20 years. My interests are in QRP digital modes, especially WSPR, JT9 and JT65, where constant amplitude signals with relatively simple frequency and phase content rule and amplifiers can be quite linear. There are many others like me, plus those who do CW, that can appreciate this type of signal correction, even if it never can correct anything more. 

Second, the first experiment I did was to reduce the 5th harmonic of the DAC aliased back into the first Nyquist zone -- not linear correction for gain stages. This is the only way I know of so far to reduce this nasty spur which will show up when using 12 or 10M.

Third, there are possibilities to try with more complex modes such as voice SSB. For example, don't completely eliminate a harmonic so that you have some headroom for other amplitudes. Separate the distortion table from the main table so that you can vary the applied distortion with the signal amplitude. In the most expensive case, you can have independent and adjustable signal sources to cancel each harmonic.


How is your perl scope software for the DS1054Z coming along?

73,

Steve
KF7O

James Ahlstrom

unread,
Nov 20, 2015, 10:33:05 AM11/20/15
to Hermes-Lite, softerh...@gmail.com
Hello,

I think these experiments are interesting.  It is always informative to see what can be done in software to solve troublesome real-world problems.  Harmonics and IMD have been a plague since the earliest days of radio.  But I share John's concerns here.  This is probably because I operate 90% SSB and have a special fondness for (ahem) AM at 7.290 MHz.

Also, I think we are looking at a non-linearity in the IAMP, not the DAC.  There is no reconstruction filter between the two, so the IAMP is subjected to high frequency products.  The 22.981 spur was reduced from -53 to -68 dBc by cancellation.  But at max DAC output (before the IAMP) of 5.6 dBm, the 22.981 spur is -71 dBm, or -77 dBc.  If we use the DAC output directly, our spur problems are reduced.

Of course, the cancellation can reduce harmonics, not just spurs.  So it is valuable if it can cancel harmonics from a self-contained QRP rig.  Maybe even to the extent of needing no Tx filters.

On a totally separate note, I don't usually announce new Quisk versions here because I don't want Quisk traffic to migrate to this group and annoy people.  But the next version 4.0.0 has two features of special note for Hermes-Lite users.  It will have the ability to manage multiple configurations internally, so that people with multiple hardwares, like Hermes-Lite and HiQSDR, can just pick a radio from a menu and go.  And it will have an optional narrow button layout that will be convenient for making a self-contained Hermes-Lite + Linux single board computer + touch screen radio.  I hope to release it by the end of the week.
 
Jim
N2ADR

Steve Haynal

unread,
Nov 20, 2015, 10:06:12 PM11/20/15
to Hermes-Lite, softerh...@gmail.com

Hi Jim,

This spur must be in the sampling portion of the chain as it produces an alias back into the first Nyquist zone -- it is the 5th harmonic of 24.895 aliased back. That can't happen in the IAMP alone. It is actually a textbook example of DAC distortion. I suspect having the IAMP in the chain loads the DAC and pushes it to distort and hence the differences in spur amplitudes. It will be interesting to see what happens when the raw DAC output is connected to a load.

73,

Steve
KF7O

in3otd

unread,
Nov 21, 2015, 5:50:54 PM11/21/15
to Hermes-Lite, softerh...@gmail.com

Hello,
while impatiently waiting for the new code with the spurs cancellation, I have automated the TX output spectrum measurements, using some routines from Quisk for controlling the H-L.

Here below is a sample image with the output spectra from an H-L without the reconstruction filter. Not sure this is adding any new data to the measurements already done, but the resulting image is looking quite nice to me, so I wanted to post it, hi.



the x axis is the TX frequency set in the H-L and the y axis is showing the corresponding spectrum measured by the spectrum analyzer.
The carrier output power was about 10 dBm and the noise floor at around -70 dBm. As expected, you can see all the usual products/harmonics.

Besides, I have done some measurements on the TX and RX circuits, details my website.

Steve Haynal

unread,
Nov 21, 2015, 9:52:41 PM11/21/15
to Hermes-Lite, softerh...@gmail.com
Hi Claudio,

Thanks for the plot. I really like how it presents the data. You can clearly see the spurs offset from 73.728 MHz that we were originally battling with a notch in the reconstruction filter. When we were using the 61.44 MHz oscillator, the spur was closer to the fundamental and impossible to filter as seen at TX near 32 MHz in your plot. I don't have any good explanation for why these spurs exist, except that they may be an artifact of the interpolation filter. Do you have any ideas?

Also, you can see the spur at 22.981 when transmitting at 24.895. This spur bounces back up as the 5th harmonic moves from the 2nd Nyquist zone to the 3rd. As we raise the sampling frequency, this spur will rise in frequency and may become a problem for 10M. This is one I'd really like to always cancel out.

I assume you are using the IAMP so we are seeing both harmonics from the IAMP as well as aliases from the DAC? What spectrum analyzer are you using to collect this data?


Thanks also for the experiments you posted on your website. Anyone who hasn't seen this website really should take a look. I will also add links to your website from the Hermes-Lite wiki unless you object. Admittedly, these types of experiments to test and improve the various analog blocks and filters at the RX and TX frontend have been sorely lacking. Jim Ahlstrom has done some good experiments with data too. John Williams and company have been doing good work on filtering post PA. If you and/or Jim want to redesign or tweak the frontend, please go right ahead. Your data on the reconstruction filter not performing well at higher frequencies agrees with Jim's. We'll be sure to take this into account in any future PCB revision, and also ask for your review.


I am working on the new NCO this weekend. Alan may be doing something too.

73,

Steve
KF7O

Alan Hopper

unread,
Nov 22, 2015, 4:58:56 AM11/22/15
to Hermes-Lite, softerh...@gmail.com
Claudio,
that is a great plot, for once a picture really does paint a 1000 words.

If anyone want to play with my very basic attempts at cancellation, there is a version of my software here http://www.ihopper.org/radio/hlradio_0_126.zip . You will need to build firmware with the attached hermes_lite_core.v, I've only compiled it on a cva9 so can't promise that it fits other fpgas.  This is very basic and just applies amplitude correction just before the dac at full speed.  The correction is adjusted from the radio properties pane '...' button. A starting point is -7E-07 in x^2 and 6E-08 in x^3.
The overall gain is adjusted to fit the adc range so you should not have to tweak the x value from 1.  The adjustment is applied when you move to another text box and should take effect in a few 10s of milliseconds.

The 'sig' option in the modulation drop down generates a simple carrier,

I don't have a spectrum analyzer so it would be great to know it it is having a real effect and I'm not fooling myself with the duplex feedback.

Please don't expect too much from this.

73 Alan M6NNB

 
hermes_lite_core.v

in3otd

unread,
Nov 22, 2015, 5:11:51 PM11/22/15
to Hermes-Lite, softerh...@gmail.com
Hello Steve,


Thanks for the plot. I really like how it presents the data. You can clearly see the spurs offset from 73.728 MHz that we were originally battling with a notch in the reconstruction filter. When we were using the 61.44 MHz oscillator, the spur was closer to the fundamental and impossible to filter as seen at TX near 32 MHz in your plot. I don't have any good explanation for why these spurs exist, except that they may be an artifact of the interpolation filter. Do you have any ideas?

Aren't these the original aliases which are not completely filtered by the TX DAC interpolation filter ? IIRC, the datasheet says this filter has "only" a 50 dB rejection ( did not check how much they are down actually)
 
I assume you are using the IAMP so we are seeing both harmonics from the IAMP as well as aliases from the DAC?

Yes, this was the IAMPN output. Later I'll try to measure the TXDAC output also.
 
What spectrum analyzer are you using to collect this data?

This was a R&S FSEA but I guess many other SAs could be used as long as they have a remote control interface. Also your scope-based SA might be used, likely; additionally you might get nice pictures of the artifacts added by the scope, hi.

I will also add links to your website from the Hermes-Lite wiki unless you object.

I actually thought to put everything on the Wiki at first, but then used my web pages since I thought I might have other, more general, stuff linked around in other pages there, so feel free to add a link in the Wiki.

Admittedly, these types of experiments to test and improve the various analog blocks and filters at the RX and TX frontend have been sorely lacking. Jim Ahlstrom has done some good experiments with data too. John Williams and company have been doing good work on filtering post PA. If you and/or Jim want to redesign or tweak the frontend, please go right ahead. Your data on the reconstruction filter not performing well at higher frequencies agrees with Jim's. We'll be sure to take this into account in any future PCB revision, and also ask for your review.

As you mentioned, there is already quite a lot of good work done already, I wanted to repeat some of the measurements to learn a bit more about the HW and also to have a comprehensive collection of data; sometimes I need to search back and forth in the groups messages to find out who measured what and with which results, since I tend to forgot things, so I'm using my website as a sort of "lab notebook" also, hi.

BTW, I have updated the website page with the spectra obtained adding a capacitor on the TxDAC output; as expected, it does something but surely the additional filtering effect is not enough.

Thanks and

in3otd

unread,
Nov 22, 2015, 5:17:00 PM11/22/15
to Hermes-Lite, softerh...@gmail.com
Hello Alan,
thanks for the new SW release; I do not have windows on the lab PC, so I have never used your SW up to now. I will maybe try to install on another window machine in the next days.

73 de Claudio, IN3OTD /DK1CG

Steve Haynal

unread,
Nov 23, 2015, 1:29:13 AM11/23/15
to Hermes-Lite, softerh...@gmail.com
Hi Alan and Claudio,

I wrote most of the new loadable NCO today. For small stuff like this I like to use myhdl as I can then simulate easily from Python and use the data with Python tools I know. I've been looking at pure 12-bit sine waves generated from the new NCO model and the old Cordic hardware in the frequency domain. This is RTL simulation, no real DAC involved. As expected, the Cordic is less noisy. I haven't added dither yet to the NCO so there are some spurs. Still, I think noise levels in both cases is well below what will matter with a 12-bit DAC. There are some plots below.

Phase adjustments were essential for me to see maximum cancelling.

Although for the first step I want to look only at constant amplitude signals, I think we might be able to split the distortion from the main expected LO and add them together in the FPGA to create a distorted LO. This would allow us to dynamically adjust the distortion amplitdue in the FPGA which is probably needed for SSB and AM. It still assumes the distortion frequency and phase profiles are constant to simplify things. This may be too much of a simplification.

Here is the current Cordic tuned to 24.895 MHz. This is a simulation of the real RTL.


Here is the new NCO without dither. The spurs will be cleaned up once dither is added. The noise floor will be a bit higher than the Cordic, but still make no difference for the 12-bit DAC.


  
73,

Steve
KF7O


 



Alan Hopper

unread,
Nov 23, 2015, 11:30:34 AM11/23/15
to Hermes-Lite, softerh...@gmail.com
Steve,
yes I believe phase adjustments are needed, I have got something working better on the 3rd and 5th harmonic by sending separate luts for I & Q  and then instead of just taking the real component for the dac I rotate I&Q after the cordic an extra 45 degrees(just an add and scale in the lut) so both luts affect the dac output and give some sort of phase control.  The nco approach looks very interesting and could well be the way to go, I just had to give the luts a try as it is so simple.  A very useful and educational exercise whatever happens, It has already uncovered a few bugs in my software.

73 Alan M6NNB

in3otd

unread,
Nov 26, 2015, 5:01:31 PM11/26/15
to Hermes-Lite
Steve,
regarding the spur cancellation for modulated signals, if you try to cancel an harmonic of the modulated carrier, e.g. the 2f_ck - 5*f_tx at 22.981 MHz when transmitting at 24.895 MHz, you need to generate also a distorted version of the baseband signal, no? The 5*f_tx term means that there was a 5th order non-linearity somewhere that generated this product, so you need to replicate this process also (and a n-th order product will occupy n times the original bandwidth...). Or am I missing something?


73 de Claudio, IN3OTD / DK1CG

Steve Haynal

unread,
Nov 27, 2015, 12:14:26 PM11/27/15
to Hermes-Lite
Hi Claudio,

For the spur at 22.981 MHz, I created a new sinusoid that was cos(W) + An*cos(5*W + U). I had to adjust An and U to get the best cancellation. This was not a distorted LO that I could use with any type of modulation, but a sinusoid wavetable that was sent directly to the DAC on CW key down. Although the second term is above the Nyquist frequency of the DAC, it was the easiest way to get a periodic signal of the same length that would have some cancellation.

I've had to change directions as phase dithering was not helping with the NCO. It has been very helpful to write a model in Python so that I can see quickly what the spurs are in the frequency domain. This technique has also identified some frequency settings for the current Cordic that are not too clean in the frequency domain. I will eventually post some plots. I have another NCO model that uses a bit of simple interpolation and optional amplitude dithering that looks very promising. I am in the process of converting the interpolation to model exactly what can be done easily in hardware to measure if additional truncation and quantization effects are acceptable.

73,

Steve
KF7O

in3otd

unread,
Nov 27, 2015, 1:02:52 PM11/27/15
to Hermes-Lite
Hello Steve,
thanks for the detailed explanation. Still I'm not sure I understand how to cancel the spurs in the general case. I assume the harmonic products come from a non-linearity of the DAC (or IAMP, does not matter here), so the TX chain gain actually depends on the (instantaneous) level.
As usual, for a 5th-order non-linearity. instead of having Vout=Gain*Vin you have something like Vout=Gain*Vin+b*Vin^5. If Vin=cos(wt) the output is just a combination of sinewaves at frequencies w, 3*w and 5*w and you can cancel them "easily".
But when Vin is not a pure sinusoid the product at 5*w will be modulated by Vin^5, not Vin so if you just mix the baseband Vin with a cos(5*wt) term they will not cancel, IMHO.

I will be interested in learning more about myhdl and the Python models you developed, even if I don't think I could help much with this; last time I did some digital design was more than 10 years ago (VHDL only) and I will need quite some time to relearn all that. Do you plan to put some more details about the NCO/Cordic modeling on the Wiki?


73 de Claudio, IN3OTD / DK1CG

Alan Hopper

unread,
Nov 27, 2015, 1:48:27 PM11/27/15
to Hermes-Lite
Claudio, Steve,
I've been a bit distracted by a problem with the tx noise floor as seen by duplex reception being somewhat variable(still a mystery),  this has made it hard to measure the effects of my cancellation .  My approach is to distort the dac output with simple power terms, when it is all behaving I can reduce 2nd, 3rd and 5th harmonics close to the noise floor and imd visibly reduces, I have seen harmonics on ssb voice reduce significantly.  I realise that generating a cancellation signal that works for a controlled test signal is a world apart from a general solution but it is a step in the right direction.  I have code that gives me phase and amplitude control of 3rd, 4th and 5th harmonics with just a lut each for I&Q but am struggling with the maths of controlling phase on 2nd harmoics near 0 degrees without adding a third lut. Once we can control test signals the real work starts in using feedback to control real signals.

73 Alan M6NNB

in3otd

unread,
Nov 27, 2015, 2:08:39 PM11/27/15
to Hermes-Lite
Hello Alan,


I've been a bit distracted by a problem with the tx noise floor as seen by duplex reception being somewhat variable(still a mystery),  this has made it hard to measure the effects of my cancellation.

I didn't try to use FD yet, but out of curiosity, the variability is in time or frequency or...both?
 
 My approach is to distort the dac output with simple power terms, when it is all behaving I can reduce 2nd, 3rd and 5th harmonics close to the noise floor and imd visibly reduces, I have seen harmonics on ssb voice reduce significantly.  I realise that generating a cancellation signal that works for a controlled test signal is a world apart from a general solution but it is a step in the right direction.  

Yes, here you distort the modulated signal and not only the LO, so I think this should work in general, with the modulation also. Nice that also the modulation harmonics are reduced, I thought that the actual non-linearity could be frequency-dependent, so what work to cancel e.g. the 3rd harmonic of the carrier (far away from the carrier itself) could not work so well to cancel the 3rd order of the modulation, which is still near the carrier.
 
I have code that gives me phase and amplitude control of 3rd, 4th and 5th harmonics with just a lut each for I&Q but am struggling with the maths of controlling phase on 2nd harmoics near 0 degrees without adding a third lut. Once we can control test signals the real work starts in using feedback to control real signals.

...here you've lost me, I thought you were multiplying the DAC codes by some power terms, why is the 2nd order different from the other terms?


73 de Claudio, IN3OTD / DK1CG

Steve Haynal

unread,
Nov 27, 2015, 2:30:12 PM11/27/15
to Hermes-Lite
Hi Claudio and Alan,

Claudio -- you may be correct that signals rich in frequency content will require more sophisticated techniques. I think you've better described the skepticism that others have expressed. I really am focusing on simple cases like JT9 and WSPR to begin with, and will be happy if these cancellation experiments only work for those modes.

Alan, sounds like great progress. When you refer to LUT, do you mean a RAM-based lookup table or FPGA LUT? Is the last code you sent up to date? I'd like to see what you are doing, as it may be a better approach than the NCO stuff I've been sucked into.

Also, thanks for the glimmer of hope with "I have seen harmonics on ssb voice reduce significantly." Even if these cancellation techniques are not 100% effective, they may still be useful to simplify and reduce filter requirements. 

73,

Steve
KF7O

Alan Hopper

unread,
Nov 27, 2015, 2:56:32 PM11/27/15
to Hermes-Lite
Claudio,
I'm still lost as to where my FD problems are, basically the noise floor jumps up and down during tx, apparently at random, both with my software and powersdr. I guess it is a bad joint in my setup but it would be good to know it is only me.

My current view on the long term approach is to measure the non linearity using the adc and try to correct for what ever distortion is measured but I can see all sorts of issues especially at the higher frequencies  as the maths has to understand what is folded back down from above the sampling frequency.

My problem with 2nd order phase control caught me as much by surprise as you and I'm afraid I can't describe the maths well enough to explain the problem( basically I^2+q^2 for a constant carrier cancels out an in phase correction).

73 Alan M6NNB

Alan Hopper

unread,
Nov 27, 2015, 3:52:57 PM11/27/15
to Hermes-Lite
Steve,
the luts I refer to are all in the fpga just before the dac, in the hope of correcting non linearities across the whole bandwidth.  The last software I posted only had a single lut that tried to correct  without phase terms,  I haven't posted the phase controlled version yet. As a fpga newbie  I have made the assumption that multiplies won't work at the dac output rate, life would be simpler if I am wrong  here..
I have bursts of it all working very well and then not, sometimes the 2nd harmonic starts up much stronger than the 3rd and other times much weaker, I feel I need to get a more consistent baseline before I loose all my marbles!

73 Alan M6NNB

in3otd

unread,
Nov 27, 2015, 4:55:42 PM11/27/15
to Hermes-Lite
Alan,
I'll try to look at the code to better understand what you implemented. I was thinking that what is needed is a (complex) look-up table indexed by the I/Q signal magnitude so that you can correct the TX chain gain and phase change with the output level. To correct phase changes over frequency ("memory effects") will be much more difficult, as it means you have to build a filter, taking into account also the IQ signal past values.

The multipliers "raw speed" could be enough, see page 44 of https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/cyclone-v/cv_51002.pdf , but of course this is only part of the whole story (timing). Just try and see what happens, hi.


73 de Claudio, IN3OTD / DK1CG

Steve Haynal

unread,
Nov 28, 2015, 2:14:09 AM11/28/15
to Hermes-Lite
Hi List,

I've settled on a reasonable NCO model that should lead to a resource-friendly implementation. Here are some plots to show what it does.

Below is the NCO generating a single 12-bit sinusoid at 7.1 MHz without interpolation or amplitude dithering. The worst spurs are at around 72 dBc. These results agrees with this similar design. The spurs are from the finite number of entries (4096) in the table which lead to phase quantization errors, and the finite number of amplitude bits which leads to amplitude quantization errors. 


The result is much cleaner after simple interpolation. Phase dithering never helped. The worst spur below is 85 dBc.


There are certain frequencies that lead to short repeating patterns when accessing the table. These cause spurs which are not removed by interpolation as seen below. Although the picture below looks bad, the worst spurs are still around 83 dBc.


  

By adding a bit of amplitude dither, these spurs can be reduced to around 95 dBc as seen below.

So we have a RAM-based NCO with arbitrary waveform loadable by software with worst spurs around 85 dBc. Shown below is the fundamental with the distortion spur that eliminates the 3rd harmonic for me on 40M.



I have similar plots for the current cordic used in the Hermes-Lite. I'm not including them here as there are already many pictures in this post. The cordic is a bit cleaner on most frequencies, fewer spurs and the worst spurs are in the 88-89 dBc range. For the problematic frequencies, the cordic is similar to the NCO, with many spurs in the 85 dBc range. For our 12-bit DAC, I don't anticipate problems with spurs even up to 74 dBc, the theoretical dynamic range of a 12-bit DAC. We see more dynamic range in these plots given the processing gains of the FFT. 


73,


Steve

KF7O




Steve Haynal

unread,
Nov 30, 2015, 1:36:17 AM11/30/15
to Hermes-Lite
Hi List,

Today I spent some time thinking about the point Claudio made, looking at Alan's work, and creating some Python models of single and dual tone SSB modulation with a distorted local oscillator. Here is what I learned:

  1. Even for single tone modes like WSPR, JT9 or JT65 that use SSB, the cancelling harmonics from the distorted LO won't match up to cancel the intended harmonic spurs. Consider a LO at 7.000 MHz with a USB JT9 tone at 1000 Hz. After modulation, the transmitted tone is at 7.001 MHz and the second harmonic that should be cancelled is at 14.002 MHz. The distorted LO adds a second harmonic at 14.000 MHz, but this modulates with the 1000 Hz signal to create the cancelling at 14.001 MHz, not the required 14.002 MHz. What can work is to shift the LO frequency directly for WSPR/JT9/JT65 as done with the ultimate3 or wsprryPi. This is a specialized enhancement and one I don't want to spend more time on now, but may eventually.
  2. For more than one sinusoid besides the carrier frequency, Claudio is correct that standard nonlinear distortion models lead to more spurs than exist in the baseband * n * LO product that need to be cancelled. Nonlinear distortion is described as a polynomial, Taylor series or Volterra series. These contain 2nd, 3rd, 4th, etc. order terms that create these additional IMD spurs. See the expansions in this review to understand the additional terms that appear.
  3. My understanding of Alan's experiments is that he is attempting to undo the nonlinear distortion terms as described above in point 2 immediately before the values are sent to the DAC. Since it is just a 12-bit DAC, he precomputes a 4096-entry lookup table that is essentially a function that when given an original DAC value input, produces an output which is that input value with the additional nonlinear distortion terms subtracted. Is this a correct understanding? There may be more than one table, and complex IQ entries, involved now. This seems like a very worthwhile direction to go. There are many hits on scholar.google.com when searching for digital predistortion rf amplifier. A quick scan makes me think that most work is using polynomials with memories at frequencies in the GHz range. We may be able to get by with something simpler like a few terms from the Taylor series in the HF range.

73,

Steve
KF7O


Alan Hopper

unread,
Nov 30, 2015, 1:58:06 AM11/30/15
to Hermes-Lite
Steve,

  1. My understanding of Alan's experiments is that he is attempting to undo the nonlinear distortion terms as described above in point 2 immediately before the values are sent to the DAC. Since it is just a 12-bit DAC, he precomputes a 4096-entry lookup table that is essentially a function that when given an original DAC value input, produces an output which is that input value with the additional nonlinear distortion terms subtracted. Is this a correct understanding? There may be more than one table, and complex IQ entries, involved now. This seems like a very worthwhile direction to go. There are many hits on scholar.google.com when searching for digital predistortion rf amplifier. A quick scan makes me think that most work is using polynomials with memories at frequencies in the GHz range. We may be able to get by with something simpler like a few terms from the Taylor series in the HF range.
Yep that is what I'm doing.  The published version only has one table and only creates in phase non linearities.  I hoped to post the version with phase control this weekend but was plagued by power cuts and emergency plumbing caused by son destroying a down pipe with a football! Thanks for the links, it is probably time I read a bit about this as I'm no doubt reinventing wheels at the moment.
73 Alan M6NNB

in3otd

unread,
Nov 30, 2015, 4:03:53 AM11/30/15
to Hermes-Lite
Hello Steve and Alan,
thanks for all the interesting work!
I think that DAC values predistortion is similar to what PureSignal does (but I didn't look at how it actually works). We are currently trying to cancel mainly the DAC/IAMP harmonics, so we need to correct directly the DAC codes, while PureSignal is correcting the I/Q baseband samples, I think. Maybe the two predistortions could coexist, as long as they try to minimize different products, the harmonics for the direct DAC predistortion and the in-band products for the PureSignal, even if the unwanted products have a common origin.
Regarding how to determine the DAC predistortion coefficients, I was thinking that since a n-th order nonlinearity generates up to the n-th harmonic, you might try to change one coefficient at a time, starting from the n-th to minimize the n-th harmonic, then keep this fixed and repeat for the n-1 coefficient and harmonic. You might not need to change the phase correction until the TX frequency is changed. Just thinking out loud here, I may be completely wrong, hi.


73 de Claudio, IN3OTD / DK1CG


Alan Hopper

unread,
Nov 30, 2015, 5:16:49 AM11/30/15
to Hermes-Lite
Claudio, Steve,
my belief is that pure signal is effectively doing the same thing but is limited to the baseband bandwidth, it may be that the pure signal code could be used to cancel harmonics and imd if given full rf bandwidth feedback and control of dac output.  My hope is that only one system is needed for IMD and harmonics as they have the same cause, but using two systems is interesting.  I've not given much thought to automating the coefficients, with full bandwidth dac and adc values to compare we might be able to just fit a polynomial to get the coefficients directly.

I wish I had more time to play with this as it is very addictive.  I had a quick glance through some of Steve's links, most of the ones I looked at are baseband or if solutions (like pure signal). Our problem is slightly different as the output of the predistortion is a real signal to send to our single dac rather than IQ to be sent to an analog mixer. I don't think some of the lut strategies presented will work for us as they use the magnitude of the iq signal to look up the distortion, for a constant carrier(without a following mixer) the magnitude is constant so no correction would be added.  I think we can do something simpler but I could have my maths wrong.  I'm working on a simple simulation to test my maths in isolation from the radio and my limited fpga skills.

Steve, you mentioned interpolation in your nco, do you have any rtl that could be used to interpolate my luts to help it fit the sdk? 

73 Alan M6NNB

in3otd

unread,
Nov 30, 2015, 6:19:48 AM11/30/15
to Hermes-Lite
you are right, Alan, we have just one DAC so there is no way to control the phase of the harmonics with predistortion. Maybe a dual-DAC solution, like the TI AFE7222 mentioned some time ago will allow this, but it will likely be for H-L v3.0, hi.
What can be done is probably just linearizing the DAC using the instantaneous value of the DAC code as the LUT index and see if this helps.


73 de Claudio, IN3OTD / DK1CG

On Monday, November 30, 2015 at 11:16:49 AM UTC+1, Alan Hopper wrote:
[snip]

Alan Hopper

unread,
Nov 30, 2015, 6:32:42 AM11/30/15
to Hermes-Lite
Claudio,
I believe we can still control phase with 1 dac, , just not using some of the methods used by baseband implementations.  In some ways our problem is simpler than the baseband problem.
Alan

Steve Haynal

unread,
Nov 30, 2015, 11:51:58 PM11/30/15
to Hermes-Lite
Hi Alan,

Since you are using the CVA9, you have a lot of resources at your disposal. The multipliers on that part are abundant (especially if you dial back the number of receivers) and easily fast enough to do a single multiply every clock tick at 73.728 MHz. One possibility is to just brute-force the design to understand what is needed. For example, for each term in the Taylor series you want to consider, take the complex output from the cordic, rotate it by some software-programmable angle, raise it to the proper power, scale it again by a programmable parameter, and then add it to the DAC stream. You'd need four of these instances in parallel to compute terms u^2 to u^5. You'd also have to pipeline the data to the DAC to keep things synchronized. It sounds like a lot but should be very doable on a CVA9 and could highlight exactly what works. I'm willing to help with the RTL if you want.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 1, 2015, 1:32:59 AM12/1/15
to Hermes-Lite
Steve,
 thanks for that,  It is very useful to have some idea of what is possible. My current solution that looks good in the simulator and gives smooth phase control of all terms uses two luts like this 
DAC= lut90[I]-lut90[Q]+lut0[I+Q]

lut90 has the out of phase components and lut0 the in phase, the phase is controlled by the ratio of in and out of phase terms.  I've just got to sort the scaling, so far I have been scaling to keep zero input at zero output, this leaves one end or other of the dac range unused, I'm not sure if this is best.

73 Alan M6NNB

Steve Haynal

unread,
Dec 1, 2015, 1:46:50 AM12/1/15
to Hermes-Lite
Hi Alan,

I just remembered you asked about interpolation. In the NCO and in the Cordic, there is a fractional part of the phase accumulator. I added an extra 3 bits to each entry in the NCO table that indicated if this fraction should be multiplied by 0,1,2,4 or 8. This scaled value is then added to the one retrieved from the table. This is essentially very rough linear interpolation between two samples in the wave. No multiplier is needed because only multiples that can be computed by shift are specified. The actual implementation centered the interpolation on a point, so that you would interpolate up to  -4,-2,1,0,1,2,4 times the fraction away from that point for a bit better resolution. Not sure if this will help you, but it did allow me to get the quality seen with larger tables with tables that were about 1/4 the size.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 3, 2015, 4:27:20 PM12/3/15
to Hermes-Lite
Steve,
I'm still having problems with the noise floor jumping up at random during full duplex tx. I can't find any bad joints and can't make it happen by wiggling things.  I don't know if the problem is in the tx or rx path.  I wonder if it could be happening in the fpga.  It always jumps up by the same amount so I wonder if the least significant 6bits in the duplex interface are being corrupted or associated with the wrong higher bits.  I really don't understand the timing side of rtl yet so am not sure the order in which things happen, for example in the rx code:-
always @(posedge ad9866_rxclk)
begin
if (ad9866_rxsync) begin
ad9866_rx_stage[5:0] <= ad9866_rx;
end else begin
ad9866_rx_stage[11:6] <= ad9866_rx;
ad9866_rx_input <= ad9866_rx_stage;
end
end
in the 'else' part what is written to ad9866_rx_input? Is it the value before or after the high bits have been written or is there a chance of uncertainty? Sorry for newbie questions but I've not found any good tutorials on this side of things.
 73 Alan M6NNB

Steve Haynal

unread,
Dec 4, 2015, 12:02:04 AM12/4/15
to Hermes-Lite
Hi Alan,

The two statements in the else branch are concurrent access to a flip-flop, not sequential as you'd expect in a programming language. It is behaving correctly to concatenate two 6-bit chunks arriving at double rate into a single 12-bit word. It it was incorrect, we'd really notice it.

There is one way that the ADC data could be corrupted. Just above this code you'll see two comments indicating that the two clocks (2x rate and 1x rate) are synchronous but I don't know the phase relation. I am punting here. There is a chance that the phase relation due to external delays and your particular compilation is such that the clock edge that reads rx_input occurs in the small time window that leads to corrupted results with the slower clock. To test if this is the case, switch to negedge where ad9866_rx_input is read, around line 453, just after the "Test sine wave" comment. This will change the phase relationship by 180 degrees.

If you ever want to simulate a snippet of RTL to learn what it does, take a look at  

73,

Steve
KF7O

Alan Hopper

unread,
Dec 4, 2015, 1:41:25 AM12/4/15
to Hermes-Lite
Steve,
thanks for that, I was not really suggesting it was wrong, I just wanted to understand how it was right. So to be clear is  the value of ad9866_rx_input that will be set, the value of ad9866_rx_stage before ad9866_rx_stage[11:6] <= ad9866_rx happens ? My confusion is the concurrent read and write of ad9866_rx_stage, is the read guaranteed to happen before the write through the magic of hardware?
73 Alan M6NNB

Steve Haynal

unread,
Dec 4, 2015, 1:49:08 AM12/4/15
to Hermes-Lite
Hi Alan,

During that clock tick, a new value is being written into the ad9866_rx_stage[11:6] flop bits, and the last value (not the new value just written) of ad9866_rx_stage is being read and written into ad9866_rx_input. It will be another clock tick of ad9866_rxclk until the [11:6] bits just written are passed to ad9866_rx_input. The clock ad9866_rxclk runs at twice the speed, so the effect is to have a new ad9866_rx_input every tick of the 73.728 MHz clock.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 4, 2015, 2:04:14 AM12/4/15
to Hermes-Lite
Steve, 
thanks, I did try moving the tx code to the negedge  as I wondered if changing it on the same edge that the ad9866 read it on was the problem, this appeared to make it more stable but I still see occasional smaller jumps, I've not tested it long enough to know if it has made a real difference or I'm being fooled by the random nature.
Alan M6NNB

Alan Hopper

unread,
Dec 4, 2015, 3:03:34 AM12/4/15
to Hermes-Lite
To any other fpga newbies, I just found these short tutorials http://www-inst.eecs.berkeley.edu/~cs150/sp13/resources/ that made things a lot clearer to me.
73 Alan M6NNB

Alan Hopper

unread,
Dec 4, 2015, 11:46:11 AM12/4/15
to Hermes-Lite
Steve,
I made some progress by changing
always @ (negedge AD9866clkX1)
DACD <= txsum[11:0]; // + {10'h0,lfsr[2:1]};
to posedge, If I've got things right this gives twice as long for the cordic and predistortion to work, things got worse as I made the predistortion more complex so I wonder if the completion was starting to clash with this line. I'm slowly undoing my other tweaks to see if this is all that was needed or if a happy coincidence with another tweak is making it work.

I have now had some good results with this on 2nd, 3rd and 5th harmonics simultaneously with phase and gain control of x^ x^3 & x^5 terms.  The tables (2 * 4096 * 13bit) currently only fit the cv and cv9 but I hope to fit them in the sdk with interpolation.  Hopefully I'll be able to post something this weekend.

73 Alan M6NNB

in3otd

unread,
Dec 4, 2015, 12:43:54 PM12/4/15
to Hermes-Lite
Good work, Alan!
nice to see that the phase can also be controlled, I thought that it couldn't be done by "simply" predistorting the DAC data.


73 de Claudio, IN3OTD / DK1CG

On Friday, December 4, 2015 at 5:46:11 PM UTC+1, Alan Hopper wrote:
[snip]

Steve Haynal

unread,
Dec 4, 2015, 4:25:32 PM12/4/15
to Hermes-Lite
Hi Alan,

Sounds like good progress.

If you need some more time between for computing txsum, you could also add a pipe stage and put your computations in that pipestage:

reg [11:0] preDACD;
always @(posedge AD9866clkX1)
  preDACD <= txsum[11:0]

// Add you stuff that uses preDACD as the value input to be computed in this clock period

always @(posedge AD98766clkX1)
  DACD <= <your result>



The SDK does have less RAM. You could reduce the number of receivers to 2 when using your predistortion. Instead of saving the entire new 12-bit DAC value in the table, you could save only the number of bits that must be added/subtracted with the true DAC I and Q. This should be less than 12-bits and could save space for your 3 tables.


I am really excited about what you are doing. Most people are not attempting to apply these techniques in the HF range, only GHz stuff. I am also warming up to the idea of adding predistortion to the LO for modes that have a single carrier (CW,JT9,JT65,WSPR, perhaps PSK, etc) but this would require adding a queue in the software that specifies frequency offset, duration and perhaps phase of a tone. This wouldn't be too hard to do. Perhaps after v2.0 is out. But, I really hope what you are doing works out as it is a more general solution.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 6, 2015, 9:29:45 AM12/6/15
to Hermes-Lite
Steve,
thanks for that, I've ended up with 2 stages of pipeline for reasons I don't understand.  Without the pipelines the ALM usage had shot up to 300% on the cv, what would cause that?

I like the idea of sending just the delta in the tables, once I know the real useful range I'll probably do that.

List,
there is a new version of my software here www.ihopper.org/radio/hlradio_0_127.zip that has the predistortion code in. It needs the firmware to be compiled with the attached file.  It only fits in the cv and cva9 for now.

The predistortion settings are in the radio properties bit (...), starting values for 80m are:-

6.5E-07 3.067
8.5E-09 1.022
0 0
9E-16 4.250

It would be great if someone with a spectrum analyzer could confirm that this is really having any effect.

This is in no way intended for real on the air use, it is just a small test to see if there is any value in this approach.

If anyone with a sdk has a real desire to test this, just shout and I'll move it up the list.

If anyone wishes to add this to Quisk or anything else there are some comments in the attached file line 926 and copied below:-

Lookup tables
These are sent continuously in the unused audio out packets sent to the radio.
The left channel is an index into the table and the right channel has the value.
Indexes 0-4097 go into DACLUTI and 4096-8191 go to DACLUTQ. 
The values are sent as signed 16bit numbers but the value is never bigger than 13 bits.

DACLUTI has the out of phase distortion and DACLUTQ has the in phase distortion.

The tables can represent arbitary functions, for now my console software just uses a power series

DACLUTI[x] = 0x + gain2*sin(phase2)*x^2 +  gain3*sin(phase3)*x^3 + gain4*sin(phase4)*x^4 + gain5*sin(phase5)*x^5
DACLUTQ[x] = 1x + gain2*cos(phase2)*x^2 +  gain3*cos(phase3)*x^3 + gain4*cos(phase4)*x^4 + gain5*cos(phase5)*x^5

The table indexes are signed so the tables are in 2's complement order ie. 0,1,2...2047,-2048,-2047...-1. 

The table values are scaled to keep the output of DACLUTI[I]-DACLUTI[Q]+DACLUTQ[(I+Q)/root2] to fit in 12 bits,
the intermediate values and table values can be larger. 
Zero input produces centre of the dac range output(signed 0) so with some settings one end or the other of the dac range is not used.


The predistortion is turned on and off by a new command and control packet, this follows the last of the 32 receiver frequencies.
There is a sub index so this can be used for many other things.
control cc packet

c0 101011x
c1 sub index 0 for predistortion control-
c2 mode 0 off 1 on, (higher numbers can be used to experiment without so much fpga recompilation).



73 Alan M6NNB
off for a post quartus pint!



hermes_lite_core.v

Alan Hopper

unread,
Dec 6, 2015, 9:30:03 AM12/6/15
to Hermes-Lite
Steve,
hermes_lite_core.v

John Williams

unread,
Dec 6, 2015, 10:12:51 AM12/6/15
to herme...@googlegroups.com
Well deserved ... cheers!

Steve Haynal

unread,
Dec 6, 2015, 9:41:34 PM12/6/15
to Hermes-Lite
Hi Alan,

Thanks for the code! RTL synthesis, place and route tools are notorious for blowing up the design due to overzealous and often hopeless optimization when trying to meet timing. I suspect you were on the timing edge without the pipelines and the tool went into a death spiral. 

With the distorted sine wave experiments I did, I only needed at most 4 to 5 lower significant bits to add helpful predistortion. You can probably get by with no more than 6 bits of delta data and cut your RAM usage in half.

I tried your modified firmware but think there is a problem. The overall noise floor from 0 Hz to the Nyquist frequency is high with this firmware. Granted, I do see more "bonus" artifacts with my scope/spectrum analyzer setup, but I think this is real. Below are plots with your firmware and with the last CVA9 release firmware, same software and hardware. I did try with the distortion enabled and disabled in your software and saw the same results for all settings (0,1,2 in the settings, what does 0 mean?). Quisk with your firmware also produced the same results. Since this is occurring up to the Nyquist frequency, noise is being introduced to the data sent to the DAC somehow. This could be anything from a bad timing path aggravated in my compilation of the firmware, a real timing path in any compilation, or a functional bug corrupting the data. Do you see anything similar? We can take a close look at the timing reports and RTL to debug. I should have some time later this week.

73,

Steve
KF7O


Predistortion Firmware:


Last CVA9 Firmware Release:

Alan Hopper

unread,
Dec 7, 2015, 2:18:29 AM12/7/15
to Hermes-Lite
Steve,
thanks for giving it a go. I thought the noise problem had gone but on a longer test I now see it sometimes, it comes and goes apparently at random.  Mode zero turns off the pre distortion, mode 1 turns it on, currently other modes are the same as zero. It looks like my fpga beginner's luck has run out.  All help gratefully received.  I shall do some reading on timing.
73 Alan M6NNB

Alan Hopper

unread,
Dec 7, 2015, 8:14:51 AM12/7/15
to Hermes-Lite
Steve,
I had a quick look in the timing analyser and there are recommendations relating to ad9866_txr to ad9866_tx for both my firmware and the current version in github for cva9 full duplex.  I also see occasional jumps in tx noise for the cva9fd jic in github.  I think I created the cva9 fd sdc file, I had no idea what I was doing so maybe I got something wrong there.  At least the fact that you see the problem on your scope confirms the problem is in the tx path.
73 Alan M6NNB

Steve Haynal

unread,
Dec 8, 2015, 1:00:03 AM12/8/15
to Hermes-Lite
Hi Alan,

I took a close look at your RTL tonight and didn't see anything horrible. Synthesis is generating reasonable results and there are no new timing violations. Sometimes the way a person writes RTL can lead to widely varying implementations after synthesis. Although your RAM use is very behavioral, it is mapping to 3 tables. Since you have two reads in one clock tick from one of the tables, it duplicates that table as expected.

I really suspect it is an undefined timing relation between the dac output pins and the clock to the ad9866. Since it is undefined, timing won't report anything. I'm seeing this in half-duplex. Synthesis will "borrow" some time from the next clock tick and throw off the previous relationship that accidentally existed between dac output pins and clock. Since I don't know exactly what the relationship should be (depends on PCB traces, AD9866 timing), I'll make some guesses and see what works out best. Probably tomorrow night.

73,

Steve
KF7O





On Monday, December 7, 2015 at 5:14:51 AM UTC-8, Alan Hopper wrote:
Steve,

Alan Hopper

unread,
Dec 8, 2015, 2:05:36 AM12/8/15
to Hermes-Lite
Steve,
thanks for looking at this. It hadn't occurred to me about reading from the same table twice, obvious now you point it out, I have a lot to learn.  Could I access the shared table on different clock edges to avoid the duplication?
73 Alan M6NNB

Steve Haynal

unread,
Dec 9, 2015, 3:11:52 AM12/9/15
to Hermes-Lite
Hi List,

Kudos to Alan for his brilliant work! We should all buy him another round. I managed to compile Alan's modified firmware with clean TX output. When transmitting on 7.1 MHz, I measured the 2nd harmonic at -46 dBc and the 3rd harmonic at -40 dBc. After applying Alan's predistortion, the 2nd harmonic is down to -70 dBc and the 3rd down to -66 dBc! This was through manual iterative minimization using my scope/spectrum analyzer. I don't think I found the true minimums as each iteration of collecting data with the scope takes a minute or two. I tried to minimize the 5th harmonic also but there is strong interaction with the 3rd harmonic and I never found a setting that minimized both. The signal was generated with "sig" in Alan's software. Below are spectrum plots without and with predistortion.

73,

Steve
KF7O


No predistortion:



With predistortion:


Alan Hopper

unread,
Dec 9, 2015, 4:13:54 AM12/9/15
to Hermes-Lite
Steve,
thank you very much for this, it has made my day, without proper test equipment I have had a nagging doubt that I might have been fooling myself. I also see the interaction between the terms, I've been setting it up with multiple receivers displayed in my software so you get instant feedback( beware of the pure signal rx that can be used unexpectedly as a result of the virtual rx feature!).  I guess the next step is to see how stable it is and how well it works with real signals and then try and automate it.

Very interested to see what made it behave for you.

73 Alan M6NNB  

Steve Dick

unread,
Dec 9, 2015, 7:54:05 AM12/9/15
to Steve Haynal, Hermes-Lite
This is an unbelievable accomplishment.  Some day it might make a nice article for QEX magazine
-Steve K1RF
--
You received this message because you are subscribed to the Google Groups "Hermes-Lite" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hermes-lite...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



Avast logo

This email has been checked for viruses by Avast antivirus software.
www.avast.com


Steve Haynal

unread,
Dec 10, 2015, 1:37:55 AM12/10/15
to Hermes-Lite
Hi Alan,

You may be able double pump the RAM by reading on both edges or using a 2x clock. The RAM blocks in the Cyclone V operate anywhere from 180 to 315 MHz. The current clock is 73.728 MHz. I'd try it and see what the resource reports say.

The problem with data corruption was the external paths to/from the AD9866. Joe reported this on the SDK full-duplex several months ago and I fixed it for the SDK full-duplex but the changes never made it to the other FPGAs. See lines 77 to 99 in Hermes_Lite_FD_SDK.sdc. You can copy and paste those lines in to Hermes_Lite_FD_CV.sdc and Hermes_Lite_FD_CVA9.sdc to fix for full-duplex. I still need to finalize a fix for half-duplex but hope to check in code this weekend.

I tested the settings when first powering up the Hermes-Lite this evening. They were about 10 dB worse than yesterday, so not entirely stable, but they seem to be stable for tens of minutes.

Do you have a two-tone test as used for IMD testing built into your software? A option where we could independently change the frequency, phase and amplitude of either tone would be useful to test how your predistortion holds up with more complex signals.

I've been wondering about what it means to use the RX to measure the TX. This is what a VNA does to measure reflected power. I'm not sure you can take a complex reading and just dial it in as it will vary depending on the load Z. I think an algorithm based on Newton's method should work well.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 10, 2015, 2:21:47 AM12/10/15
to Hermes-Lite
Steve,
there is a two tone option in 'sig', the second slider is the amplitude of the second tone and the text box is the frequency offset from the first tone, I could add a phase control.  The predistortion is active for all modes.

I had a quick look at the feedback, we already have the full bandwidth adc data in the band scope packet. I think we also need the corresponding dac data ( much like pure signal but not bandwidth limited through a receiver), for testing I think I might just alternate dac and adc samples in the bandscope packet.  Putting dac and adc in the same packet eliminates problems of sync, we could have a switch to enable this so as to maintain compatibility.

I'll try the sdc fix this evening.
73 Alan M6NNB

Alan Hopper

unread,
Dec 10, 2015, 5:50:05 PM12/10/15
to Hermes-Lite
Steve,
as far as I can see lines 77 to 99 are the same as lines 154 to 176 of Hermes_Lite_FD_CVA9.sdc I'm not sure what to change.
It's been a  long day so I'm possibly code blind.
73 Alan M6NNB

Steve Haynal

unread,
Dec 12, 2015, 2:30:13 AM12/12/15
to Hermes-Lite
Hi Alan,

You are right that those constraints are already in Hermes_Lite_FD_CVA9.sdc. The PCB layout for the CVA9 is different so it may be that the numbers need to be adjusted. Without the PCB layout data, it is mostly trial an error. The values are nanoseconds. The clock on this interface is 2X for full-duplex so a period is only 6.78 nanoseconds.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 13, 2015, 1:06:44 PM12/13/15
to Hermes-Lite
Steve,
I tried a few values for these lines

set_output_delay -add_delay -max -clock ad9866_rxclk 2.0 [get_ports {ad9866_txsync}]
set_output_delay -add_delay -min -clock ad9866_rxclk -0.3 [get_ports {ad9866_txsync}]

set_output_delay -add_delay -max -clock ad9866_rxclk 2.0 [get_ports {ad9866_tx[*]}]
set_output_delay -add_delay -min -clock ad9866_rxclk -0.3 [get_ports {ad9866_tx[*]}]

but did not get any improvement, but I really don't have a clue what I'm doing and maybe my jumping noise floor is not the same as the noise you saw.

List,
I now have dac demand data being sent back from the radio interleaved with the raw adc bandscope. The attached file has a set of 2048 points that should record the nonlinearity of the dac/iamp/adc chain. I have shifted the adc data by two samples but this is not precise enough to bring them into phase hence the fatness of the plot.  This was really just a sanity check for my fpga coding.

73 Alan M6NNB
adc_vs_dac.xlsx
Message has been deleted

Steve Haynal

unread,
Dec 14, 2015, 3:02:56 AM12/14/15
to Hermes-Lite
Hi Alan,

Sorry, but I didn't have much time to look at this this weekend, and didn't get far with the time I had. I'm trying first with half-duplex because my CVA9 is paired with a v1.21 currently.

73,

Steve
KF7O

Steve Haynal

unread,
Dec 16, 2015, 2:17:23 AM12/16/15
to Hermes-Lite
Hi Alan,

I checked in a few changes to github:

  1. hermes_lite_core.v uses generate blocks to enable/disable your experimental predistortion. See the parameter PREDISTORT.
  2. Just the DACD path to the external pins (not the DACD that is used internally) has an additional pipestage with negedge clock to match historical RTL.
  3. Hermes_Lite_CVA9.sdc has the constraints that sort of work for me for half-duplex. (My only full-duplex board needs the jumpers to be reconfigured for the CVA9.) Although it is not meeting timing, this is the version that I am able to run experiments on. Sometimes "pinning" the timing in one direction but still missing helps...
  4. Hermes_Lite_FD_CVA9.sdc has additional shifted clocks defined in lines 22-28. If you want and the other changes don't help, you can try these different clock phases and see if there is any improvement. I hope to take the board into work where there is an oscilloscope that can measure 147.6 MHz and I will eventually measure the phase relation between the two clocks

73,

Steve
KF7O

Alan Hopper

unread,
Dec 17, 2015, 1:26:26 PM12/17/15
to Hermes-Lite
Steve, thanks
this line 
create_clock -period 147.456MHz -waveform {5 8.391}  [get_ports ad9866_rxclk] -name ad9866_rxclk
appears to fix the jumping noise floor (so far) but seems to make the bandscope data report high values, (my adc level meter maxes out long before the overflow bit is set). The build times have jumped from 8mins to 75mins for 4 receivers!

73 Alan M6NNB

Steve Haynal

unread,
Dec 18, 2015, 2:01:23 AM12/18/15
to Hermes-Lite
Hi Alan,

Sorry about the long compile time. I noticed it too. It appears that two constraints are now fighting each other... I will go back to a version without your modification, see where the "good" IO timings landed, and try to write the constraints to force the tool to match those.

73,

Steve
KF7O

Alan Hopper

unread,
Dec 18, 2015, 4:46:03 AM12/18/15
to Hermes-Lite
Steve,
thanks for looking at this, I greatly appreciate it.  As a sanity check I just flashed the current cva9 fd bitfile from github. On a short test it appears to have the high noise floor all the time, but I believe in the past I have seen it jump, maybe my code is pushing something the right way but just not far enough. Maybe I just have faulty hardware. For another test I flashed the half duplex version before I realized that I had no way to see the noise! 

I then tried my cv with the latest full duplex bitfile, this has the lower noise floor, I'll try this again with my firmware, at one time I convinced myself it was doing the same as the cva9 but that was a while ago and could have been when I had other errors in my firmware.

I have tried a number of power supplies, these have had a visible effect on the noise but not the even increase that I see.

I have tested on powersdr and get the same results as with my software.  The default tx bandscope settings in powersdr hide this noise.

73 Alan M6NNB

Steve Haynal

unread,
Dec 21, 2015, 12:51:01 AM12/21/15
to Hermes-Lite
Hi Alan,

I haven't been able to check with the old firmware yet as I am away from my radios. Hopefully New Year's weekend. I will check the cva9 firmware that is posted.

73,

Steve
KF7O

Glenn P

unread,
Dec 24, 2015, 3:34:50 PM12/24/15
to Hermes-Lite
I watched a video on Utube last night from Phil Harmon, VK6APH who is heavily involved in HPSDR and about 51minutes thru the hour long video he gives a demo of removing spurs by a technique called "Randomize".

Now I am probably way off beam here for H-L but wondered if the technique is or can be used?

the utube video is "Episode 62 Part1: Designing the Mercury SDR receiver, from TAPR 2008"

glenn
vk3pe



On Monday, November 16, 2015 at 6:24:08 PM UTC+11, Steve Haynal wrote:
Hi List,

This weekend I was able to do some signal cancellation experiments to reduce the spur at 22.981 MHz when transmitting at 24.895 MHz. The summary is that it looks very possible to reduce this by 15 dB, -53 dBc down to -68 dBc! The 22.981 MHz spur is due to imperfections in the DAC, and is the 5th harmonic which occurs in the second Nyquist zone but aliased back into the first Nyquist zone. To enable experimentation, I added a small RAM memory "wave table" in the FPGA firmware. I hacked Quisk to load this wave table with whatever values I like. I can create sine waves with different levels and phases of predistortion with Python and then load them into the wave table. When the CW key is pressed, I can test the output. Because of the limited depth of the wave table, not all frequencies are possible, but enough to run experiments. I created a sine wave of the fundamental plus the 5th harmonic. I empirically varied the amplitude and phase of the 5th harmonic until I saw the best cancellation on my scope-based spectrum analyzer.  

The results appear very stable -- the required phase and amplitude do not seem to change much over time and other variations. I still need to test more variations, but I am convinced that it is stable enough not to require a fast loop on the FPGA. Software should be able to make adjustments every so often and do a good job. Unfortunately, I see different results from my spectrum analyzer compared to Quisk. In full-duplex, I can tune Quisk to look at the spurs with the receiver. I suspect that since the ADC and DAC are in the same package for the AD9866, there is less isolation between TX and RX when observing the AD9866 TX signal with the AD9866 RX. When I have the tap in place (not proximity OTA pickup), readings from Quisk and the spectrum analyzer may agree more.

I am quite excited about this result, and want to see if I can predistort to diminish the harmonics introduced by the IAMP and eventually other amplifiers. I will look at 40M this week with that in mind.

The challenge of making this practical is generating the fundamental with the proper predistortion. This is different than pure signal, as it is not distorting the audio (in the kHz) to improve IMD, but is distorting the fundamental (in the MHz) to overcome nonlinearities in the DAC and amplifiers. Still, I am optimistic it is possible and am thinking about three techniques:
  1. The fundamental is currently produced by a Cordic algorithm. I've studied the Cordic and think it is possible to generate not a pure sinusoid, but a sum of sinusoids, provided they are all harmonics. This requires computing something more complicated than an ArcTan, coming up with a different set of degree steps that map to simple integer divisions and not restricting the computation to a single quadrant. I have what to do all worked out in my head but need to write some code to test it. This would be the most direct replacement for what exists in the Hermes-Lite. I'd like to make the parameters programmable so that software can tune for various hardware and frequencies.
  2. An extension of what I am doing now is just to always use a wave table for transmission. I think it is possible to arrange a table deep enough and with variable depth used such that a rich set of IF frequencies can be generated. This could not be used by software that only supports 0 IF. I believe PowerSDR is in that bucket. Fortunately, Quisk and Alan's software allow for receivers that are not 0 IF. The wave table must have steps smaller than the minimum bandwidth (48 kHz) to cover the entire spectrum.
  3. The numerically controller oscillators (NCOs) provided by FPGA vendors usually have a range of options from designs requiring larger tables and few resources, to those requiring small tables and many resources. There are a number of these available, including several open source (1 2) IPs. I may need to study these to see what predistortion is possible.

73,

Steve
KF7O







Reply all
Reply to author
Forward
0 new messages