Daylight Again – An all Analog Radio

What’s all this?

In 10 seconds, 

  • A high performance, 7MHz, 5 watt SSB rig
  • Draws just 24 mA of current
  • 90 dB dynamic range, 80 dB close-in dynamic range
  • 3D printed rock stable, slow tuning PTO with zero backlash.
  • 3D printed toroids
  • You have all the parts in the junk box or you can buy them tonight at the vendor night.
  • Very stable, slow tuning VFO with no backlash
  • Costs less than 20 dollars to build, even in new parts. 

 

The Circuit Diagram

Full Circuit Diagram

Download the circuit diagram in PDF | Download 3 D print files of the PTO

Isn’t analog radio an anachronism? Are we just being nostalgic? What’s wrong with the Si5351?

Last year, when we had to isolate ourselves for a week, I managed to grab some components, an Antuino (the RF lab in the box), some basic tools and set about making a slightly souped up version of Roy Lewallen’s Optimized Transceiver from our room. The experience of a well designed analog rig was truly enlightening. There were no spurs, the audio was crystal clear and with presence. 

When David NM0S and I were discussing this year’s FDIM, I suggested that we do a return to analog radios and he jumped at it! So here we are, talking analog radios in 2022. 

Here is the memo : The analog never died. The world is analog all the way, until you descend into Quantum madness. The antennas are analog, Maxwell died a content, analog man. Our radios, ultimately, are analog machines and we are all analog beasts too. Amateur Radio technology has evolved into the digital domain. However,  it has only made it easier for us to do analog with computers to simulate and print our circuits. 

So, it’s time to bid good bye to our Arduinos and Raspberry Pis and build an Analog Radio for ourselves. So let’s see what we can achieve in hindsight, a return to our native land and a rethink of our approaches.

The radio is called Daylight Again, a nod to being back at the FDIM in 2022 after a gap of two years. It is named after the Crosby, Stills, Nash and Young’s song that had been humming all the time while put this radio together, emerging after 2 years of lockdown.  This radio that took two days to come together, no actually two years! That’s: parts of it got built and stowed away, thoughts were struck in the shower, questions popped up during early morning cycle rides and notes and circuits were scribbled in the notebook. 

I must take the first of many diversion here: I hope you all maintain a notebook. Write down the date and whatever you thought or did on the bench and the result. Nothing is trivial enough to leave out. Wisdom comes to those who write notes. 

I started to build this on Saturday the 14th May and I checked into the local SSB net on Monday morning, the 16th May 2022.

Back to the radio.  What can an analog radio do that will appeal to us homebrewers? 

    • Simple to build (?) This is a myth as this paper will demonstrate. It may be easy to duplicate a design from the QRP Journal or the Sprat, but to design a radio that is stable, sensitive, selective and sounds sweet on air is not trivial. It needs thinking and revisiting our assumptions and habits (like the diode mixers).
    • Low power: The radio draws just 24 mAYes, an analog radio can be really really lean. Low power is not just a show-off. It has some real purpose. One can work for weeks from a single pack of AA cells. Working your rig off a battery pack eliminates a large species of noise and spurs; As things stay cool inside, the VFO drift is much lower and one could carry the rig on a cycle!
    • Low noise VFO.What’s the big deal here? All local oscillators are the same, isn’t it? Not really. Consider each VFO to be an AM transmitter that splatters around the carrier. As the VFO splatter mixes with an incoming signal that is strong and yet off your receiver frequency, to the listener it appears as if the signal has splatter. Actually, the signal would be entirely clean, it is your own local oscillator’s splatter that you are hearing.
    • Having clean VFO  is the most important way of increasing the dynamic range of your radio. A free running JEFT VFO that has sufficient power and a good Q components, will be unmatched by any synthesized or direct sampling radios. The math is all on the side of the free running VFO. We are talking -150 db/Hz at 10 KHz spacing, by comparison the Si5351 is -125 db/Hz, it is 300 times worse.
    • Stability of 2 Hz/15 second. Can it decode FT8? Actually yes, we have had regular decodes after 15 minutes of warm up. It stays stable enough for CW and SSB. WSJT-X suite is the main challenge. We need a stability of better than 2 Hz/15 second period for the WSJT-X suit to work. We do this by keeping the operating frequency of the VFO low, eliminating heat around the VFO and using sturdy but ugly, short and stiff point to point wiring.
    • Dynamic Range of 90 dB and close-in range of 80 dB. The real, tough, beef eating hams don’t care for dynamic range, we operate! Right? Wrong. The bands have changed. Signals tend to cluster within narrow pockets. All the QRPers hang out on 7040, the PSK gang is over at 7060, the FT8 kiddies are parked at 7074. The local charminar net at 7080, long after it is closed, has the hanger-ons late into the noon. We need radios where we can clearly make out a CW pileup where you can hear 20 different stations at once.The 90 dB is actually a very good number. The gains beyond this are marginal. Unless you are operating with full power stations within close neighbourhood on the same band as you, 80 dB of close-in dynamic range is enough to win you CW sweepstakes. Ask Rob Sherwood (https://www.youtube.com/watch?v=GNh0wP9PlsM).
    • Why SSB? Just because it is a more complex thing to build. It is easier to add CW to an SSB rig rather than add SSB to a CW rig. The amplifiers need to be linear, sideband needs to be suppressed etc. We may add CW to this rig later.
    • Undo the Lore.We homebrewers tend to stick to the beaten path. We build exactly the same radios over and over again. When was the last time you didn’t use a diode mixer? Or build radios that didn’t use 50 ohms interconnect? It is about time.

I must warn you there is mathematics ahead. Not too much, but enough to keep you on your toes.

The Daylight radio has a few big ideas that are interesting enough to be tried out. I am leaving out the bad ideas (like varactor tuning and using class E amplification) from the scope of this paper.

The Cascode Mixer

Remember the 70s when Doug, W1FB built those radios with 40673 dual-gate MOSFETs? Well, they are back! It is 2022 and they are exhibiting good dynamic range, we are rebooting the old heros. 

The dual-gate MOSFETs are, surprisingly enough, still being manufactured. Mouser lists tens of thousands of them, they are cheap but available only as SMD parts.  However, you can build a virtual dual-gate FET from two JFETs, it works all the same. A bag of 100 J310s is still sold for a few dollars at Dan’s (. With careful design, a cascode can provide us with +8dbm of input intercept and a gain of 10 db. This might not seem much to most but it is.

This is more than respectable, it is downright awesome. Here is why: The cascode mixer with +8 dBm of Input intercept(IIP3) and with 10 dB gain over it has an OIP3 (output intercept) a huge +18 dbm (calculated as IIP3 + gain).  

In comparison, the diode mixer with +15dbm input intercept has a loss of 7 db and noise figure of 7db (actually the loss is the noise figure). Thus a diode mixer’s output intercept is +8 dBm (again calculated as IIP3 + gain, that is negative loss in this case)  as compared to +18 dBm of the JFET cascode. 

There are several striking things about this mixer:

      1. Take very little VFO power The oscillator injection on Q1 is to the gate, it takes zero power of local oscillator injection (the current never flows into the JFET gate).  This results in the VFO running cooler and with less drift and less current consumption!
      2. Needs no post-mix amplifier This mixer is insensitive to termination and it is unidirectional. The signals will not flow back from the drain to the source. You don’t need to follow this up with a post mixer amplifier that will draw even higher current. Conventionally, a diode mixer needs 5 milliwatts of local oscillator, which means that the oscillator itself must draw about 100 milliwatts to produce it and the diode mixers invariably need a post mix amplifier with about 20 ma of standing current, which, at 12 v will amount to 12 x0.02 = 240 milliwatts of power. In effect, together, the diode mixer and its post-mixer amplifier up the receive budget by 340 milliwatts. People of the GQRP and QRPARCI clubs have attempted DXCC with those power levels!
      3. You can pick your own working impedance. To get upto 10 dB of gain, you will need to set the input and the output impedances to around 2000 ohms. However, at lower the impedance and the gain will decrease but the other mixer parameters will remain as they are! You can put this into LTSpice and smoke it. This makes the job of matching input to an RF band pass filter or the output to a crystal filter very easy.
      4. The output has an unsuppressed local oscillator. There is no balance, at least, in this simple design. We can add balance by using another similar mixer in push-pull configuration though as a receiver front-end mixer, our purpose is served without needing to suppress the local oscillator. The RF front-end can directly drive the crystal filter. However, the cascode mixer is unsuitable as a transmit mixer due to this oscillator leakage and we will use an NE612 for that as discussed later.
      5. Noise figure is 4 dB. This is really sensitive. You can build high grade VHF/UHF mixers with this sensitivity.
      6. Input Intercept of +8 dBm This needs some illustration. +8 dBm is about 7 milliwatts of power. When you overload the mixer with an unwanted signal of 7 milliwatts,  the distortion produces unwanted signals that appear to be 7 milliwatts too, it is where the distortion is equal to the signal itself. This is a theoretical limit.

Notbook calculations of the IMD

As you start backing off the overloading signal, for each db drop in the signal’s strength, the distortion drops by 3 dB. So, let’s imagine that we now drive the mixer with -7 dBm signal, the output, which is below 15 dB below the IIP3, accordingly the distortion would drop to  by 45 dB(15×3), now the distortion signals appear as (8 dBm- 45 dB) -37dBm signal.

Let me pick a more interesting RF signal of 7,000 uv, or -30 dBm. This is 38 db below the intercept level, accordingly, the IMD will be down by 38×3 =  114dB below the IIP3. The distortion is now at -106 dBm (1uV). Now, the distortion has gone into the noise floor. You can no longer hear the distortion. This is the dynamic range of the receiver. Impressive.

Controlled Gain, Block by Block

Our craft consists of stringing together filters, amplifiers, mixers and oscillators together.

One wonders : Is there a point in putting an IF amplifier at all? Should I add an RF amplifier?  Is it better to replace the diode mixer with an NE612? It is confusing all the way!

NR60, a homebrew design of the 1980s that wasn’t optimized for distortion

One assumed that more gain in a receiver meant better sensitivity. If the receiver sensitivity was improved, would it become much better? Actually, the converse is true. If the receiver is sensitive enough to be able to hear the atmospheric noise, then more gain will only overload the receiver. 

How does one really figure out which stage is overloading? How much gain is optimal in each stage?  How do we identify the offending weak stage in a radio’s line up of blocks? Some receivers turn out to be very sweet and clear even though they are simple while other simple ones don’t work just as well.

The art of building radios seems to be wrapped in some kind of black magic. 

Well, it is no black magic, it is simple mathematics. You can accurately predict how sensitive a receiver will be (by calculating the minimum discernable signal), how clear the transmission will be (by predicting the IMD : Intermodulation Distortion), how loud the audio will be etc., using a simple online calculator. This tool was first written in the 70s by Wes Hayward, W7ZOI which was subsequently published on the floppy disk that came with his book – Introduction to Radio Frequency Design. 

Diversion: This book is a classic, now available as a PDF. I would urge you to get it and take a print out and read it slowly, one page at a time. It is a far better investment than flipping through rigpix.com searching for your Radio porn. 

It works simply by filling in a table of columns, each column represents one stage of  either a transmitter or a receiver. For each stage you specify the noise figure, gain and output intercept (OIP3). Most RF chips, or circuit blocks do specify their performance in these three critical parameters.

[A Tip: In passive circuit blocks like the filters or diode mixers, the noise  figure is the same as the loss. So, if a bandpass filter has a loss of 2dB, consider that to be its noise figure as well. For passive filters you can assume the OIP3 intercept to be +50 dBm]

I wrote a web version of the same calculator. Once you have populated all the stages, you press the button and it gives you the results. It is available on https://www.vu2ese.com/index.php/2021/05/17/cascade-calculator/.

Here is a traditional superhet built the traditional way. Let’s examine it:

      1. We have a band pass filter in the front-end, followed by a diode ring mixer that has about 7 db loss, the diode mixer needs a post-mix amplifier to terminate it properly, 
      2. A 2N3904 biased at 20 mA would provide us with  a strong post-mix amplifier with +30 dBm OIP3,16 dB gain and 6 dB noise figure. 
      3. The crystal filter would have -2db loss
      4. A second 2N3904 as an IF amplifier with similar performance figures as the post-mix amplifier.
      5. The demodulator is yet another diode ring mixer with exactly the same performance as the front-end mixer.
      6. The audio preamp following the detector could be the common-base amplifier used for low impedance match with the diode mixer, it is traditionally biased for 0.5 mA of current, providing a good match but modest large signal capability.

You are encouraged you to visit this page(https://www.vu2ese.com/index.php/2021/05/17/cascade-calculator/)  and try out your own configurations. It is a bit like the Microsoft flight simulator, you strap the wings of a Boeing 777 on a Cessna and wonder if it will fly – It is highly edifying. 

“Simulation is the greater experiment” – Wes Hayward,W7ZOI

Here, as you can see, the two IF amplifiers will consume 20 mA of current each, the VFO and BFO outputs have be +7dbm (that is, 5 milliwatts) to drive the diode mixers properly. Each of those oscillators will consume 20 ma each. The buffer amplifiers that produce the 5 mW drive will also need to be biased with even higher current. I would put the total current consumption of this receiver at 100 mA. 

Notice that we are measuring how the signals progress and distort each other even beyond the crystal filter. That is, consider that there are two CW signals, one is a weak 1uV signal that demodulates as a 700 Hz audio. Right next to it, about 300 Hz away is another signal that is 1mV strong (about 60 db stronger). In the receiver we are discussing, the distortion will be very heavy and weak signals will be run all over by the strong one. 

On the other hand, if we use a really narrow crystal filter that will cut down the strong signal and keep it away from the stages following the crystal filter, we will have better luck snagging that DX. To simulate that, we just up the OIP3 of the stages that follow the crystal filter. The dynamic range is no longer just 67 db, we have a receiver with 93 db dynamic range!

This illustrates why some homebrew receivers will do much better operating PSK31 and FT8 where the entire band is within the 3 Khz audio bandwidth while others don’t. You have to design the radio such that all stages until the last audio amplifier are low distortion and high dynamic range.

Block diagram of the Daylight Radio

Let’s now look at our proposed block diagram above of the Daylight radio. Given that the cascode mixer has 10 dB of gain and it doesn’t need post-mix amplifiers, we can eliminate the post-mix as well as the IF amplifier. The receiver consists of just three active elements : the front-end RF mixer, the detector and the audio amplifier. 

I have chosen 8 db of gain instead of 10 db, this is because we will be using lower input and output impedances of 1000 ohms instead of the optimal 2000 ohms as we discussed earlier. This is to simplify some other design considerations. This receiver is providing a whopping 80 db of in-channel dynamic range. You could have your neighbour down the road with his IC7300 blasting a 10 mv signal right inside your crystal filter. If your ears survive it, you will still hear the weak 1uV signal behind it all the same. If we want to see how well it does with signals outside the crystal filter’s passband, we just increase the detector’s and audio amp’s OIP3 to +200 dBm and get a whopping  96 dB of dynamic range. This is an incredibly good figure. Ordinarily, it costs you at least 2000 dollars to buy this performance off-the-shelf. And the good news is not over yet. Each of these two cascode mixers will draw just 4 mA of power! 

You will note that we also assumed that the audio has an OIP 3 of +50 dBm. How is that possible? This is because we are no longer using the conventional common base NPN transistor as the audio pre-amp. This would overload quickly. Given that the cascode mixers have a high output impedance of around 2000 ohms, we can directly drive an ultra-low distortion,, ultra-linear operational amplifier that overcomes distortion by using very high gain with feedback. That is the good ol’ NE5532. 

Why the 5 MHz IF crystal filter changes everything

QER crystal filter

I have often based my own radios with 12 MHz or 10 MHz IF. Typically a 4 MHz or 3 MHz VFO was required to mix this IF to 7 MHz or 14 MHz. The IF choice is pretty critical. You can end up with bad birdies. Any harmonic of the VFO can mix with some harmonic of the BFO and scream at you as you tune past that point. 

We chose a 5 MHz IF for three reasons: 

We could build an SSB or a CW filter at 5 MHz easily. At higher frequencies, the losses with narrower filters are much more. This has to do with the Q of the filter. We will discuss this later in this paper.

At 5 MHz, we can use a 2 MHz VFO. With such low frequency, the VFO can be really dead stable;  enough even for the digital modes.

An SSB filter at 5 MHzwill need an impedance around 1000 ohms, something that will directly match the cascode mixers on either side of the crystal filter!

This is a break from the tradition of basing all our stages at 50 ohms working impedance, which serves well all the time. Our test instruments are all designed to work at 50 ohms, our antennas work at 50 ohms, our cables work at 50 ohms. However, if we can afford to step out of this comfort zone and in a controlled way use the 1000 ohms as interconnects between a few stages, we can build a  very optimal radio, very easily.

As this receiver was a rush job, we chose to quickly slap together  a QER filter. These filters are the simplest of crystal filters, all the coupling capacitors are the same value, you can use as many crystals as you want. The filter will get steeper and the losses will mount. 

An unintended consequence of too many crystals in a filter is that the group delays add up. Not all frequencies within the passband travel at the same speed through the quartz filter. It is as if the crystal filter is a highway; each lane travels at a different speed.  The longer the highway, the greater will be the added up differences between signals travelling in different lanes. This is the sound of overly filtered radios. Those who have used these ‘special’ filters in their commercial rigs will testify to the badly sounding yet costly radios. 

For us, about 5 stages are optimal for SSB and CW.

QER filter as swept on the Antuino

The QER filter can be built very simply if you have a NanoVNA or an Antuino. Please buy either of them today (Alert, I have interest in the company that produces the Antuino, further alert – the Antuino is out of stock). You start by physically building a crystal filter of the required stages with paralleled crystals at both ends. You chose a particular capacitor value, starting with 100 pF for all the coupling capacitors. Add 10 K variable resistors on both sides and sweep the filter. 

To reduce the bandwidth increase the coupling capacitance, to decrease the bandwidth, decrease the capacitors. To smoothen out the ripple in the passband, increase the resistors that act to vary the termination impedance. You can experimentally arrive at the impedance and coupling capacitance for the bandwidth you desire. The variable resistance can be read off using a VOM to determine the best impedance to operate the filter at.

Here is the time for another short digression. Often, many homebrewers ask on the forums “How do I measure the impedance of my crystal filter?”. You don’t. There is no characteristic impedance of any passive filter. Most filters are, what is known as, doubly terminated. This means that filters will exhibit strange and interesting passband shapes, phase distortions, SWR etc., when they are driven and terminated by different impedances. 

What we mean by the characteristic impedance of a filter is that the filter shape as advertised will happen only when it is driven and terminated at the specified impedances. A filter, by itself has no impedance.  End of the digression.

The Bandpass filter

We have traditionally used double tuned circuits. It kept away the broadcaster on the image frequencies, well, mostly. There were always the stations and birdies that tuned in the wrong direction. The image suppression was just about 50 dB. Some of the even more respectable amateur radio manufacturers of American heritage have chosen to use just a doubly tuned filter in the front-end. This is a false economy.

Addition of another capacitor and inductor makes this a triple tuned circuit with far better results. You can go crazy and make a four section or even a higher section bandpass filter but the performance gains will diminish for two reasons: 

      1. For more than 80 dB of out-of-band rejection you will need to control leakages around the filter rather than through it. You will have to box up the filter, use good cabling, shield each inductor from the other with an interstage shield, etc. 
      2. With each stage, the losses mount. The narrower the filter, the lossier it is. It has to do with the ratio of the loaded to unloaded Q of the filters. With that in mind, here is yet another distraction on designing the band-pass filters. I have simplified it to the extreme. But here it is all the same. 

Let’s start.

We have all heard of the Q factor (the Quality) for toroids, inductors, etc. Q is just the ratio of Center frequency to the bandwidth of a band pass filter. For instance, if you wanted a 100 KHz bandwidth from a 7 Mhz filter, that would be a Q of 7000/100 = 70. This is the loaded Q of the filter. As we discussed earlier, filters are specified at certain terminations. In our case, we are looking at a bandwidth of 500 KHz, 

So, the QL = 14 (7000/500).    (Eq 1)

What makes the Q high or low? It is the losses in a tuned circuit. Losses can be approximated by a parallel resistance too. This is where the impedance comes into play. Higher the impedance of the resonator in a filter, narrower the bandwidth. There is a simple formula to calculate that :

Rp = X * Q [ here X is the reactance of the inductor]

Let’s assume that we will use a 2 uH inductor, its’ reactance will be 

X = 6.28 * Frequency * Inductance = 87.92 ohms [or more accurately +j87.92] 

(We calculate reactance as an imaginary number, not important in this case)

  Rp = X * Q =  87.92 x 14 = 1232 ohms. 

This must be the impedance of each of our resonators. To resonate this at 7.1 MHz, we will need 250 pF capacitors in parallel to the inductor. 

Next, we choose coupling capacitors. They are given as:

  C =  K x (Co / QL

Here Co is the 250 pF capacitor used to resonate the inductor, K is a magic number. Different types of bandpass filters will provide different numbers, they relate to the polynomials the filters describe. Just trust me and use 0.7 here.

C = 0.7 x (250pf /14) = 12.5 pF

Instead, we will just use 10 pF.

There is a subtle detail now, the coupling capacitors add to the total capacitance seen by the inductors decreasing their effective frequency of resonance, we will need to reduce the 250 pf parallel capacitance by 25 pF to bring the resonance back to 7.1 MHz. So, each parallel capacitance is now 220 pF, this is excellent as we can buy 220 pF capacitors.

Bandpass filter, Designed.

Don’t reach for those disc ceramic capacitors yet. They are horribly lossy. Buy some decent NP0 capacitors, they are pretty cheap from the Mouser. If you buy a few hundred of them in 470 pF, 100pF and 10pF values, they will last you a lifetime of homebrewing on HF. If you are rich, order some 2.2pf as well. Remember NP0 or C0G only. Alternatively, if you can buy the polystyrene capacitors they are the best for the VFOs as well as bandpass and low pass filters. You can synthesize other values by just paralleling them up.

The bandpass filter will attach to the cascode mixer at one end that terminates in 1.2 K ohms (Actually I used 1K resistor). So, the inductor value will have to be adjusted again to resonate back to 7.1 MHz. The bandpass filter has to connect to the antenna at the other end at 50 ohms, we just have a capacitive voltage divider. The ratio of the 1000 ohms of the band pass filter to 50 ohms is 20:1. This would need a voltage division of Sqrt(20) = 4.4. Thus a 47 pf in series with a 220 pf would divide down the voltage. We will have to change the last inductor to again bring the central frequency back to 7.1 MHz. 

It is simply easier to change the inductor by varying the number of turns and keeping the capacitance constant. We don’t have to physically keep winding and unwinding the inductors. We can do it in LTSpice while watching the band pass shape or we can do it in GPLA, a program that comes with the EMRFD CD. Here is my tuned version of the triple tuned circuit with the inductor values needed. 

Bandpass filter simulated on GPLA

These tools are amazingly accurate in predicting the exact values. But how do we arrive at the exact values? We will have to use the Antuino or the NanoVNA. We build a simple jig that has RF connectors on two ends directly connected with a wire. We sweep this and see that there is no loss to begin with. Then we add a series trap of a known capacitor, say 220 pF and the unknown inductor and sweep it. It gives a deep notch at the resonating frequency. You know the frequency, you know the capacitance, now you can back calculate the inductance, very accurately. For instance, I needed a 2.1 uH inductor, with 220 pF this should give a notch at 7.4 MHz. In the first try with 40 turns the notch turned up at 7 MHz, I hit 7.4 MHz (after taking off 8 turns,) at 33 turns.

3D printed toroids

Yet another digression, this one will save you money … You can 3D print toroids!! We experimented with various sizes and finally landed up with 1 inch outer dimension toroids that reports a Q of about  150. Pretty reasonable for toroids that are almost free. You can print 10 of them at a time on the a home 3D printer. This was a big learning from this project.

We have used them in the bandpass filter as well as the low pass filter. They perform pretty well. To calculate the inductance, you have to square the number of turns and multiply with two.

Inductance [in nano henry] = 2 * (number of turns)2

The Receiver, then

Here is the receiver part of the radio in all its glory. 

Receiver Portion. Click on it for a larger version

The Audio filter

The active audio filter is a surprise. This is really a worthwhile addition. With less than 1 dollar worth parts, the receiver audio improved immensely. Instead of using op-amps, we just use 2N2222A as unity gain amplifiers, this is just to demonstrate that there are many ways to get there.

The audio filter has a cut-off around 2 KHz. SSB and CW, both sound warm and nice. The difference with and without the audio filter is dramatic, you want to put it back on again immediately if you throw the switch to bypass it. Note that the entire audio chain from the demodulator to the volume control is DC coupled. This needs the cascode demodulator’s audio output to have a DC operating voltage of around 8 volts so that there is enough head room for the last of the filter transistors to not bottom out at large signal levels. The audio cut-off is determined by the 3.3K resistance. I misplaced them in the shack and soldered 1K and 2.2K resistors in series. For CW work, you can reduce it to 1K.

The PTO

The VFO is another 3D printed part. Instead of hard to find variable capacitors, we use a variable inductor to tune the band. After all this is 2022. Slow motion drives are hard to find. They are messy to install, they exhibit backlash. Instead, we used a simple 3D printed PTO. It is essentially an air inductor about 5 cm long that can be horizontally mounted on the PCB with four M3 screws. It has two slots where you can insert nuts for 1/4-20 screws. A nice long brass 1/4-20 bolt is the main tuning element that moves in and out, guided by the two nuts held firmly in their slots. It provides around a decent, 25 KHz tuning per turn. It took 40 turns of 22 swg enamelled wire to fill the former end to end. The tap is at 10 turns. The 3D printed PTO is really easy to build. It prints in 15 minutes. It is dead stable at 2 MHz. To net to the exact frequency range, you can add or remove turns on the PTO former.

Three polystyrene capacitors of 470 pF each, in parallel, brought the tuning in the range of 2 MHz. A serendipitous advantage of 2 MHz PTO that tunes upwards is that a regular frequency counter can be used for accurate tuning. 

A 78L09 was used as the PTO’s voltage regulator. A simpler choice would have been to use a 9.1v Zener diode. We didn’t. The zener regulator works by sinking current that the VFO isn’ consuming. This current converts to heat which can drift the VFO. On the other hand, if we reduced the current, the Zener would starve and turns noisy and the regulation would suffer too. Instead, the free running VFOs are best powered by 78L09. They hardly sink any current, they are noise free if they are properly bypassed both at the input and the output. For a VFO you must also bypass it at audio to remove any hum modulation. A 1uf electrolytic capacitor mounted very close to the terminal output pin is recommended. 

The PTO has the minor visual disturbance of a knob that comes out and goes back in. It is best to leave it tightened against the chassis when not in use or that take it away with you to prevent other prying hams from using the radio!

We use a low power single JFET as a buffer. The cascode mixer and the transmit mixer hardly need any driving power and hence the bias current is kept low.

The PTO is built with just the component leads themselves. A box made from thin copper sheeting is soldered around it to keep it thermally stable and prevent the RF from the transmitter, etc from coupling into the PTO. Without the shielding, there was a transmit spur at 8 MHz, probably from the PTO’s 4th harmonic. It went away with the shielding and it was never fully investigated.

The Transmitter

Click on the image for a larger picture

We could have used separate transmit and receiver filters and only used the PTO and the BFO as the common elements. This has been the usual practice except for the BITX line of radios.  

A future plan to make the radio have full break-in will need a rebuild with a separate set of filters. To save time and space, we switched the filters between the receive and transmit sections.

The transmitter uses NE612 (also sold as SA602) as the modulator as well as the transmit mixer. Isn’t this a bit of a bummer. After all, isn’t NE612 thought to be an underperformer best used in boy scout direct-conversion receivers? 

The NE612 can’t match a diode mixer in a receiver application, but it has its own advantages in a transmitter. This is where science takes over the lore. 

The NE612 is a doubly balanced mixer with very good suppression of the injected oscillator as well as the input signal. It is hard to surpass it as a DSB modulator. It has an output intercept of -10 dBm, This would mean that if we extract a -20 dBm signal from it, then the IMD will be 30 dB down. Respectable level for the IMD is -26 dB below the peak tones. A -20 dBm signal is a strong 30 mV signal with IMD down by -30 dBc. You will just have to amplify it up. 

Additionally, the NE612 mixer input is sensitive enough to not need a microphone amplifier. A termination insensitive amplifier built with three transistors provides a 20 dB boost to produce a 0 dBM signal that drives a traditional amplifier made from 2N2222As and an IRF510. We used similar 3D printed toroids in the low pass filter as well. 

At first, a two section output low pass filter was tried. The 14 MHz harmonic was just below -43 dBC, another section brought it below the 50 db level. Now, the output harmonics are virtually unnoticeable. 

The T/R switching can be improved. An electronic T/R switch can be made that can be the the start of a full-break CW modification for the radio.

Full circuit (click to enlarge)

Concluding notes

There are some surprises here. Except for the transmit power chain, no broadband transformers were used anywhere in the design. This was an unstated design goal. They can be easily removed from the PA as well. An L network can easily substitute them. We will still need ferrites for the RF chokes. 

We added a frequency counter bought off the net for five dollars as a read-out. The receiver squeals each time it is connected due to the horrible RFI generated by the counter. Until a low noise frequency counter is homebrewed, we have an ‘arrangement’. There is a push button that switches on the frequency counter for you to ‘spot’ yourself. It is a little like the frequency markers of days gone by. Press the button to know where you are.

The receiver is really pleasing. I personally have an almost 99:1 ratio of receiver to transmit. I listen to bands almost daily, for hours while working. Receiver audio quality is important for me. The Daylight radio doesn’t wear you out. The active filter, very low phase noise PTO, broad crystal filter, make it a pleasure to use. 

The few contacts were interesting. We are able to regularly decode FT8. I am not sure if this is a happy accident or repeatable performance. We are yet to complete a two way FT8 QSO. Audio transmission is reported to be clear and intelligible.  

The Daylight Again radio is also an ode to the darkness of the last two years. To celebrate us who are gathered here again, to invoke the soldersmoke and our endeavours to get across to each other, entirely on our own. 

I owe a great deal to my fellow hams at Lamakaan Amateur Radio Club for their help. Radhakrishna “RK”, VU2EHR designed the 3D models of the toroids and the PTO which form the basis of this work

Venu, VU2BVB and Anil, VU2DXA helped with testing and making the lunchbox chassis. A big thank you to Wes, W7ZOI who wrote the seminal works that form the science of all that we do. His tool, Cascade, is central to planning any radio, analog or not. 

Much of the filter theory too is distilled from a close reading of the book Experimental Methods in RF Design. I have heard that the book will not be printed anymore. That’s tragic. All important texts should not be viewed primarily as commercial ventures. I hope some of us can impress upon the ARRL to continue to print it or release it out as a free/paid PDF. 

  

The sBitx –  The SDR for the homebrewer

The sBitx is a homebrewer’s SDR radio with integrated digital modes.

  • Maximum power output of 40 watts on 80M and 40M bands, goes down to 20 watts on 15M and 6 watts on 10M.
  • Based on a Raspberry Pi, a WM8731 codec and ordinary components found in your junkbox
  • Uses a hybrid superhet architecutre for high performance design
  • You can build it for less than $100 in new parts in addition to the Raspberry Pi and Display.
  • The circuit is simple to build without any FPGAs and expensive parts
  • The software is hackable, modular.

For those who can’t wait.

  1. Download circuit diagram and software from https://github.com/afarhan/sbitx
  2. Here is the Raspberry Pi Image for the sBitx. Download, unzip and burn it into a 32GB SD card.
  3. Read the Operating Manual (it is frequencly updated, don’t download and print!)

Why should we homebrew an SDR?

It is a good question. Considering that the author devoted most of his last five years to building and writing one, there should be a legitimate answer to this.

Because it is there

Was it George Mallory who, whe was asked why he wanted to summit Everest, replied “Because it is there”?

I am reminded of the work that early transistor pioneers like Wes Hayward, W7ZOI and Doug Demaw, W1FB did for the transistors. At that time, the transistors were unwieldy beasts, fragile and confusing. They had no similarity with the valves that were prevalent at the time. Their series of articles in the QST culminated in the book Solid State Design for the Radio Amateur. It changed everything and made the new technology accessible and easy. Furthermore, the prices dropped, valves went out of stock and the world became transistorized very quickly. The radio amateurs were already there.

Does this mean that the old analog radio is dead? Well, not at all. Analog electronics is the basis of all radio science, even the software defined radios. This project proves that our learning from the conventional superhet radio engineering can well be applied to the SDRs.

Increasingly, more and more radios will be built with software defined architectures. We must bring our craft right into the cutting edge of contemporary science rather than the trailing edge; especially when it comes laden with gifts.

Running CW on an SDR is perfect joy. Especially if it is a radio that you have made all by yourself. The filtering is ring-free; the sending is perfect with the macros and a computer keyboard, logging is transparent, recording your brag tapes happens with a touch, adding quirky features does not require you to drill holes in the front-panel or soldering!

The Learning

A large part of our hobby is to constantly learn new stuff. Amazingly, age is on your side.. The experience with building and using our equipment makes us better with age (Guilty as charged, I am on the better side of the 50s). However, when I set out  to study software defined radios, unlike staring at a circuit and ‘getting it’ within minutes, I realized that the software was thousands of lines of code, usually written to get the job done rather than to explain the job being done. It was impossible to study it, let alone modify or borrow from any of them. Hence, the need to write a new SDR that is easily understood and adapted, modified and extended to do more and more exciting things.

You can think of the sBitx as a stable start of your learning of the SDR. It has certainly been for the author who started with Zilch. The main source code to receive and transmit fit into a single page (just like a QRP radio’s circuit would), the actual circuit can be put together, from your junkbox!

Budget Performance

Usually, it takes a large investment and lots of time and work to build a station that matches a commercial radio. The fancy radios available for thousands of dollars has all the knobs and buttons that allow you to do smart things that make life simple. However, behind that front panel, everything is software, and the software can be free! That’s the hidden beauty of a software defined radio.

Consider that you built a super simple SSB rig, like Pete Juliano, N6CQ’s  PSST(https://www.n6qw.com/PSSST_20.html), It has all of seven devices. Now consider adding variable bandwidth filters, AGC, upper and lower sidebands, other voice modes, CW, RTTY, even FT8. The project will look enormous! However, it can all be added with just a Raspberry Pi and an audio chip!

Instead of looking at SDR as an expensive, complex approach to building radios, think of it as an easy way to add all the features of a professionally produced radio for no extra circuitry!

With the SDRs, the homebrewer can finally build radios that are as feature laden as the commercial radios, customize them to their hearts’ content and do it at a fraction of the cost of commercial radios.

Works the world from anywhere

Our last argument for an integrated SDR like the sBitx is that it allows you to quickly setup a station from anywhere without the entire paraphernalia of cables, computer interfaces, etc. and work the world on less than optimal antennas like mag-loops and whips because of the amazing software that works at digging signals right out of noise!

Setting up a station with loggers, data software like WSJT-X, Fldigi and homebrew radios is a mess of wires, configuration files and things going wrong.

An integrated radio like the sBitx is a first. Everything comes pre configured on a single SD card. It is built to a particular digital hardware (the Raspberry Pi with a stereo 96 ksps codec). Just switch on and go!

The Hybrid SDR

The central challenge to building an acceptable SDR radio is to choose an architecture and circuitry that will eliminate the image. For phasing SDR radios the image is just a few tens of KHz away, creating ghost signals. For direct conversion radios, the spurious responses are determined by the number of usable bits in the analog to digital and digital to analog conversion circuits. These can be very expensive and very hard to build in the homelab. Typically  these ADC have ball grid array pads below them that can only be soldered properly by pick and place machines.

Engineering is the art of compromise with science. It is where the practical considerations meet the hard truth of mathematics of physical laws.

The Hybrid SDR solves the image problem using the best analog way available: Use an aggressive crystal filter in a superhet architecture.. The hybrid approach to SDRs is a huge gain in terms of ease of build, performance. This compromise is the width of the waterfall : the sBitx limits itself to a maximum of 25 KHz of spectrum scope.

Is 25 KHz waterfall enough?

Those used to watching the waterfall of an entire band may think this is a limiting factor. Actually, it is not. Most of the CW operators, even in contests, operated within 10 KHz span at a time. This makes it easy to see closely spaced signals that will get clustered too closely even at 25 KHz widths. Much of DX hunting and expeditions happens in very small windows in a band.

The digital modes like the FT8 or JS8Call limit themselves to within 3 KHz. So, even 10 KHz of waterfall is wasted on them.

In a contest, if you are running (Calling CQ and waiting for others to return your call) you just stick to one frequency and listen a few kilohertz up and down. If you are tuning around in a contest, picking up the weak signals, it is best to have a magnified waterfall to see the faint traces of the individual signals.

The  Up-converting superhet

The sBitx is an up-converting superhet with the IF at 40 MHz. This eliminates the need for multiple filters, one for each band of operation. As our frequencies of interest are below 30 MHz and the local oscillator is above 40 MHz, the image frequencies are well above 80 MHz. These are easily filtered by a simple low pass filter for the entire HF band.

Central to the approach of the sBitx is that we can build a 25 KHz width crystal filter that has a very sharp image rejection. This is now possible with the easily available, low cost 40 MHz fundamental mode crystals (ABM10AIG-40.000MHZ-2Z-T from Mouser.com).

Homebrew-ability

With a superhet architecture, we can use our familiar techniques of building one in multiple ways. From using diode mixers and bipolar transistors to using passive FET front-ends without early amplifiers, these can be hand soldered, without needing PCBs.

Circuit Description

The Exciter

Figure 2

Figure 2 shows the entire circuit of the SDR’s exciter that is capable of power output of around -15 dBm and a full receiver from DC to 30 MHz.

This is a minimal, bidirectional superhet that is optimized for high performance from each stage.

On receive the signals pass through the low pass filter made of the L16 to L21 to a passive FET mixer that upconverts the RF signal to 40 MHz IF.

At 40 MHz IF, a 25 KHz wide crystal filter provides image rejection and the following IF amplifier and a diode mixer convert it down to a low IF of 24 KHz. A high dynamic range audio pre amplifier raises the signal to a level that can be digitized by the codec on the digital board, explained in the section on The Digital Part.

On transmit, the 24 KHz  transmit signal, generated in the digital board, is upconverted to 40 Mhz in a diode mixer, amplified and applied to the 40 MHz crystal filter before being downconverted in the same FET mixer to the HF band. The image is filtered by the same filter that was used in the receiver.

The Lower Pass filter

The main purpose of this filter is to provide a filter that is slightly more Chebychev rather than Butterworth by reducing the inductance of the ending inductors. This provides it with a sharper cut-off. The response of the low-pass filter is as follows (Figure 3):

Figure 3

The Q of the inductors need to be only higher than 100 in this filter as it is a fairly broad filter. Even air core formers can be used as long as they are well shielded from each other and from the Power amplifier circuitry. Stray coupling of these inductors with power amplifier transformers and filters can cause oscillations.

Very often, the LC filters that we build under-perform compared to their simulation. There are just three reasons for this :

  1. Low quality capacitors and inductors. It is best to buy all the RF filter capacitors from a reputed online retailer like mouser.com or digikey.com. The only ones that are useful are the NP0 or the C0G variety. For inductors, the micrometal toroids are recommended.
  2. Insufficient shielding: Very often, the inductors are close to each other and the unintended coupling between them can heavily compromise the filter performance. Use a physical layout where the inductors are all in a line and oriented perpendicular to their neighbors. Shield the entire filter with copper sheets.
  3. Bad grounding: The filters have to be built over a large ground plane with short connections between the circuit components and the ground. Each component should be grounded separately. Longer traces or wires to the ground will add stray inductance that compromise the filters’ performance.

The Front-End

We use a passive FET front-end with +28 dBm IIP3 that is as simple to build as a diode mixer. It uses a single trifilar transformer and a low cost FET analog switch. Experimenters can also use a pair  of switching mosfets like the BS170, etc.

This front-end was first described a the KISS mixer by Chris Trask, N7ZWY in the paper Mixer Musings and the KISS Mixer. You can read this highly informative paper on https://www.mikrocontroller.net/attachment/146369/Mixer_Musings.pdf.

Although Trask reported an astounding +40 dBm intercept point, he only measured it against a strongly terminated IF port. In our application, we directly drive it to (and during the transmit, from) a highly reactive crystal filter as a load which degrades the intercept point to some extent. The good news is that unlike a diode mixer, the local oscillator currents do not flow through the FET, making passive FETs more resilient to improper termination.

Our measured intercept was at +28 dBm, with a loss of 4dB. The figure 4 shows the IMD with an input of -10 dBm / tone(disregard the marker of 1R, it is an artifact from a previous reading). The output is attenuated by 10 dB to prevent overloading of the spectrum analyzer. The IMD is barely recognizable at even this high power level.

Figure 4

The Crystal Filter

Central to our radio is the 40 MHz crystal filter with a flat top and a very sharp upper side response. The filter begins as a Min-Loss Cohn filter with 2.2pF coupling capacitors and 2K termination impedance. We then, modify it by paralleling the ending crystals. This has the effect of doubling the motional capacitances and halving the motional inductance of the ending resonators, resulting in a relatively flat response.

The figure 5 shows the response of the filter as measured on the sBitx PCB. Given that the coupling capacitors are just 2.2pf, the layout becomes critical. The center of the passband is at 40.013 MHz and there is a steep attenuation on the higher side, this is where we will position the second oscillator; somewhere close to 40.036 MHz.

The local oscillator(s)

We chose to go with the low cost Si5351Bx that is readily available, both as a chip and a module. It can simultaneously output three clocks at different frequencies.

At a late stage in the design process, it was noticed that there is an internal ground bounce inside the chip that couples clock 0 output to clock 1 output.  This leakage was less pronounced between clock 2 and clock 1.

In the final design, we use clock 2 as the local oscillator to the front end. The clock 2 tunes from 40 MHz to 70 MHz, providing a coverage of 0 to 30 MHz. Using a slightly different front-end (for example, a diode mixer), it will be possible to extend the coverage to other VHF bands as well. The oscillator itself is capable of working in excess of 250 MHz.

The clock 1 is used as the second oscillator, it can also be viewed as a BFO, except that the output of the diode mixer is not demodulated SSB. The  clock 1 is about 25 KHz above the center of the filter’s passband, at 40.035 MHz. This results in a 25 KHz of RF signal being converted to the range of 11.5 KHz to 36.5 KHz, something that is easily handled by audio techniques and subsequently digitized using an inexpensive but high performance stereo audio codec.

The Si5351 has better than average phase noise but not stellar. It can be considerably improved by using a low noise reference input instead of the in-built crystal oscillator. Hence, a low-cost Temperature Controlled Crystal Oscillator (TCXO) is used as the reference oscillator.

This also eliminates the need for frequency calibration and alignment as the frequency is accurate enough for almost all amateur radio operations.

The IF  amplifier

The IF amplifier is a conventional feedback amplifier that uses BFR106. These are inexpensive and easily available transistors, though, alas only in SMD packaging. A strip of a hundred transistors will last you many projects.

The measured OIP3 of these amplifiers is +30dBm. The noise figure is possibly 3dB (as per the datasheet). The homelab currently lacks a calibrated noise generator to measure this accurately.

Until recently, the radio performance past the crystal filter was not very interesting as the dynamic range, etc. were all measured at 20 KHz spacing. The last quarter century has changed all this. The amateur operations are now clustered by modes within a few kilohertz of each other. All the FT8 work happens within 3 KHz bandwidth, most of the dxing is in the first 10 KHz of each band, the PSK31 happens in an equally narrow band etc.

A local station running 100 watts of FT8 calling CQ can drown out all the DX even if you have a radio with 100 dB dynamic range, measured at the standard 20 KHz spacing. The dynamic range of the receiver has to be good enough all the way to the audio stages.

Making feedback amplifiers bidirectional does seem to be a matter of simply putting them back to back. However, as the output of the active stage reaches levels exceeding -10 dbm, the base-emitter and base-collector diodes of the turned-off stage start to conduct and distort the signal. To isolate the turned off stage, three switching 2N7000 mosfets, the Q1, Q2 and Q5 are used as switches.

Second Mixer and the ‘Audio’ Preamplifier

Our expectations are of 80dB in-channel dynamic range. At first, we used a KISS mixer similar to the one in the front-end, however it was found unnecessary as the IF amplifier and the following audio pre amplifier’s performance dominated and limited the dynamic range. Instead, we use the low cost BAT54S diode pairs as matched diodes in a conventional diode mixer. You could also use 1N4148s in its place.

Note that we referred to the preamplifier as ‘Audio’. This is because the diode mixer output is at 24 KHz, not baseband audio. At this second IF, we can use standard audio techniques.

The conventional audio preamplifier that follows a diode detector has compromised dynamic range. To address this issue, Wes Hayward, described an adaption of his now famous Termination Insensitive Amplifier to audio frequencies with far better performance. This is described at http://w7zoi.net/audio-fba.pdf.

The preamp used in the sBitx follows the same architecture with very minor changes to accomodate what was available in the junkbox.

This concludes the analog port of the receiver.

The Analog Transmitter

The 24 Khz signal generated by the codec on the digital board is originally meant to drive 48 ohms earbuds. This is a good match to directly drive the diode mixer for upconversion to 40 MHz. However, as the codec is always on, it presents a potentially low impedance and at times noisy termination to the diode mixer in receive mode. Hence, it is isolated from the diode mixer on receive with yet another 2N7000 (at Q6)acting as an analog switch. The 40 MHz output is amplified by, and filtered in the crystal filter before being mixed down to the desired HF frequency in a reversal of the signal flow from the receive operation.

The Power Amplifier

Figure 6

The power amplifier of Figure 6 is a conventional power chain, probably a little sub-optimal. Two stages of pre-driver amplification with metal 2N2222As boost the signal level from -15 dBm to a little above 100 mW. The driver pair of IRF510s then boost it to upto 2 watts to drive a pair of IRFZ24N to produce upto 40 watts of power. The entire chain has a gain variation of more than 15dB from 3.5 MHz to 28 MHz. An advantage of using software defined radio is that controlling gain on a per band basis is very easy: we just multiply all the sampled RF output by a different number for each band.

The driver and the power amplifier are biased for 100 mA in each of the four transistors. There was little need to control each individual transistor’s bias current and hence, there is one common bias adjustment for the driver pair and another for the power amplifier pair.

The sBitx is capable of 40 watts on 80M and 40 M, 30 watts on 20M, 20 watts on 15M and 6 watts on 10 meters. A better and more expensive choice of transistors could possible give more uniform gain across the bands.

The three section low pass filters provide more than 43 db of suppression of harmonics and other spurs.

A few comments about the power stage are in order:

  1. The IRFZ24N gets so hot that the ordinary nylon bushing used to isolate the tab from the heatsink mounting screw can melt. Instead, we used a different scheme where a metal strip across the power transistors pressed them onto the heatsink with a mica washer in-between the transistors and the heatsink.
  2. We use a large heatsink that obviates the need for a noisy cooling fan. The same heatsink is also used to cool the LM338 that drops the 13.8v to 5V for the Raspberry Pi and the touch screen.

The Power Amplifier Transformer

We build the transformer for the power amplifier using 8 ordinary FT37-43s. Two cylinders of 4 FT37-43s were formed by slipping them over copper tubes found in air conditioning and refrigeration works.

Both the cylinders are then soldered onto small pieces of copper clad board that short the tubes on one end keep them open on the other end, forming a single U-turn primary. The shorted end is used as a mid-point that feeds the DC power to the amplifier.

The two open ends of the primary are connected with very short wires/tracks to the IRFZ24N drains. Three turns of teflon wire is used as a secondary to multiply the low, drain-to-drain impedance of the power amplifier to 50 ohms. The picture wrongly shows only two turns of the red-wire as the secondary, it should be 3 turns.

The Digital Circuit

Before we discuss the details of the digital circuit,

AN IMPORTANT CONCEPT:

The left line input channel of the WM8731  is connected to the receiver preamplifier. The received signal samples at an Intermediate frequency of 24 KHz are captured by the codec’s left channel and transferred to the Raspberry Pi that does the SDR magic. The demodulated audio is then played back through the left earphone output. In effect, the left channel is used for receiver input and output.

The right line input channel of the WM8731 is connected to a mic amplifier. The microphone input samples captured by the codec’s right channel are transferred to the Raspberry Pi that performs the SDR magic. The modulated 24 KHz signal is played through the right earphone output. The right channel’s input and output lines are used for transmitter input and output.

Figure 7

The Digital hardware (Figure 7) consists of a board that takes the Raspberry Pi, the interface to the audio codec, has a connector for the front-panel jacks of microphone, keyer, headphones and a separate connector for the two encoders that control the radio.

The design centers around an inexpensive WM8731 audio codec that costs about 4 USD. It has a direct interface to the Raspberry Pi using an audio specific, high speed I2S bus. Briefly, the codec samples left and right channels, taking 96,000 samples per second in the range of +- 1V. Though it measures 24 bits of precision, most of the lower bits are noise. Only the upper 16 bits are effective, providing a dynamic range of about 90 dB. This is plenty more than our target of 80 dB of spur and overload free dynamic range.

The Raspberry Pi connector has a number of pins used like on the Arduino platform. Four lines from the Raspberry Pi interface with the digitized audio stream that comes in and out of the Audio codec WM8731 using a protocol called the I2S. You don’t have to understand the I2S to use the chip. These lines are on pins 12,35,36 and 40. Operating parameters of the codec like volume, gain, etc., are controlled with another I2C bus from pins 3 and 5.

Apart from these, the radio needs to control the Si5351 clocks, the low pass filters, switch the harmonic filters, turn transmission on and off. It also needs to sense PTT, keyer. The logical naming/numbering of the digital lines is different from their physical position on the Raspberry Pi’s 40 pin connector:

Physical Pin on the Connector Name Purpose
11,13,15 GPIO0, GPIO2, GPIO3 Input lines from the main tuning knob encoder and it’s push button
19,21,23 GPIO12, GPIO13, GPIO14 input lines from the function knob encoder and its push button
31, 33 GPIO22, GPIO23 Implements a separate bidirectional 2C bus to control the Si5351 (see text)
7 GPIO7 Push to talk input
29 GPIO21 DASH input line of the morse paddle, PTT is parallely used as the DOT line of the paddle
16 GPIO4 T/R output line
18,22,24,26 GPIO5, GPIO6, GPIO10, GPIO11 Output lines to select 1 of the 4 low pass tx filters
32,36 GPIO26, GPIO27 Extra I/O lines

The in-built I2C of the Raspberry Pi turned is used by the WM8731. It turned out to be finicky when connected to the Si5351 as well. Hence, a separate I2C bus was implemented on the GPIO lines 22 and 23.

On the Raspberry Pi 4, the internal pull-up resistors didn’t work as expected and external pullup resistors of 2.2K were connected between the PTT and DASH lines to the 3.3V supply.

Another quirk of the board was in the way the linux driver for the WM8731 is written. The recommended reference clock is 12.228 MHz but the driver works only with 12 MHz crystal. Hence, the WM8731 uses a 12 MHz crystal with its reference oscillator.

The Software

The sBitx software is especially written to be understandable, hackable and extensible. It is also constantly being updated. You can grab the latest version from https://github.com/afarhan/sbitx. There are accompanying notes in install.txt if you are doing a fresh install.
The core SDR routines rx_process() and tx_process() are really simple and easy to understand. We will examine them in a broad way but the best way is to just read the source code and ask questions about it in the bitx20 forum at https://groups.io/g/bitx20
Unlike the Arduino/Teensy programming, the sBitx works on a very powerful, quad CPU ARM core with graphics accelerator, etc. It uses all the power of such a powerful operating system to build a simple and straightforward code base where readability is preferred over speed and optimization.

This is a Convolution SDR, WHAT!

Imagine you have a direct conversion radio with an audio bandwidth of just 1 Hz. You have an extraordinarily long piece of paper to write things on and a magical junkbox full of VFOs that can be set to any frequency and starting phase.
You tune across the band, stopping at every 1 Hz and noting the DC signal’s strength and phase. You can now use this list of frequencies and their strength and phase to plot the spectrum on a piece of paper bins drawn in them. This is how conventional analogue spectrum analyzers work. To use the correct jargon, this process has converted the signal from time-domain into frequency-domain. We will also refer to each entry that you make in the list as a ‘bin’.
Let’s reverse this experiment. You have your bins of frequencies and the amplitude/phase at each frequency. And you have a magical junk-box in which you have an unlimited supply of VFOs that you can set to any frequency and starting position (phase). To generate the entire band’s signal back, you take one VFO at a time, set the exact frequency and phase as per your list and switch them on at once. Viola! you have lit up the entire band of signals recreated as it was in the written down list! Rather, you have converted the frequency domain signals back to the time domain.
In the above thought  experiment, note that not just the CW, every signal can be written down as a series of measurements of amplitude and phase at different frequencies and reconstructed back using a series of frequency generators.

The above process can be done, mathematically and hence on a computer as well. How does this help us in generating or receiving, let’s say an SSB signal?

Now, let’s imagine that there is an upper side band signal exactly at 14.150 MHz. We started measuring the signal from 0 Hz and continued until 14.200 MHz, we have effectively filled up 14,200,000 bins with information, in frequency domain. We took 14,200,000 precise measurements of how much the amplitude and phase was at every 1 Hz step. You will see the reading values starting with frequency 14,150,000 Hz going up depending upon the instantaneous  voice modulation of the incoming signal. By the time you reach 14,153,000 Hz, the signal strength would have dropped as the SSB occupies less than 3000 Hz bandwidth, you nevertheless continue your assigned job and reach 14,200,000 Hz. Stop, massage your hand and have coffee while staring down at your clipboard.

Now, we take a new clipboard and starting copying the bins into it but we start at the bin of 14150000 Hz and write the value of that bin as bin 0 on the new clipboard, we move to the bin at 14150001 Hz and write that down as bin 2 and so on. By the time we have written 50,000th bin on the new list, we have reached the end of the old list at 14,200,000 Hz. We continue to fill the rest of the bins in the new list from bin 0 of the old list and continue all the way up to the list’s bin of 140,149,999. The new list has all the bins and readings of the old list except that is is rotated around such that the first element in the new list corresponds to the 14,150,000th element of the old list.

Next, we will set about doing some slash and burn (also called filtering). We will zero all the bins except the first 3000 that correspond to our signal. This eliminates any signals apart from the specific 3 KHz slice that we are interested in. Also note that we have eliminated the other sideband. The lower sideband image of the signal would have been in the bins of the frequencies from 14147000 Hz to 14150000 Hz and along with all the other bins, those were turned to zero as well.

Now, we take this list of frequencies and set about our VFOs to correspond to each of them and power them up!

Your SSB signal is now translated to audio, the lower sideband is eliminated, the rest of the signals are filtered away.

Let’s see how this is practically implemented in sBitx.

Receiving single sideband, Convolution!

There will be a number of implementational details that any working code has that distracts from its core work, on the other hand, the sBitx core SDR is written to be understandable and hackable. So, we will skip a few things in the code on the first pass.
Right now, just remember two things:
1. The sbitx handles a block of 1024 samples at a time. As both channels (left and right) are running all the time, the function rx_process() is called with the next set of received signals from the receiver circuitry as well as the microphone samples. We just ignore the microphone samples while running the receiver code.
2. The frequency of any signal can only be known if it is a continuous sine wave. When we start and stop recording any signal there is an abrupt beginning and an abrupt end to it that confuses any mathematical operation. To smoothen this out, we process not 1024 but 2048 samples at a time. The first of these 1024 were what we received in the earlier call to the rx_process() to this we add the newly arrived 1024 and run the FFT over the combined lot of 2048 samples. This way the distortion from the abrupt beginning happens on the samples of the first 1024 samples. We are not bothered about the distortion in those first 1024 samples because they have already been processed earlier. We simply discard them. This technique is called overlap-and-discard.
Now, we can read the code and understand it quickly.

void rx_process(int32_t *input_rx, int32_t *input_mic, 
 int32_t *output_speaker, int32_t *output_tx, int n_samples)
{
 int i, j = 0;
 double i_sample, q_sample;

 //STEP 1: first add the previous M samples to
 for (i = 0; i < MAX_BINS/2; i++)
 fft_in[i] = fft_m[i];

 //STEP 2: then add the new set of samples
 int m = 0;

 for (i= MAX_BINS/2; i < MAX_BINS; i++){
  i_sample = (1.0 *input_rx[j])/200000000.0;
  q_sample = 0;
  j++;
  __real__ fft_m[m] = i_sample;
  __imag__ fft_m[m] = q_sample;

  __real__ fft_in[i] = i_sample;
   __imag__ fft_in[i] = q_sample;
  m++;
 }

// STEP 3: convert the time domain samples to frequency domain
fftw_execute(plan_fwd);

struct rx *r = rx_list;
 
//STEP 4: we rotate the bins around by r-tuned_bin
 for (i = 0; i < MAX_BINS; i++){
   int b = i + r->tuned_bin;
   if (b >= MAX_BINS)
     b = b - MAX_BINS;
   if (b < 0)
     b = b + MAX_BINS;
   r->fft_freq[i] = fft_out[b];
 }

// STEP 5:zero out the other sideband
if (r->mode == MODE_LSB || r->mode == MODE_CWR)
  for (i = 0; i < MAX_BINS/2; i++){
    __real__ r->fft_freq[i] = 0;
    __imag__ r->fft_freq[i] = 0; 
}
else 
 for (i = MAX_BINS/2; i < MAX_BINS; i++){
    __real__ r->fft_freq[i] = 0;
    __imag__ r->fft_freq[i] = 0; 
 }

// STEP 6: apply the filter to the signal,
for (i = 0; i < MAX_BINS; i++)
  r->fft_freq[i] *= r->filter->fir_coeff[i];

//STEP 7: convert back to time domain 
fftw_execute(r->plan_rev);

//STEP 8 : AGC
agc2(r);
 
 //STEP 9: send the output back to where it needs to go
 int is_digital = 0;

 if (rx_list->output == 0)
   for (i= 0; i < MAX_BINS/2; i++){
     int32_t sample;
     sample = cimag(r->fft_time[i+(MAX_BINS/2)]);
     output_speaker[i] = sample;
     output_tx[i] = 0;
   }
}

Open the file at https://github.com/afarhan/sbitx/blob/main/sbitx.c and scroll down to the function that says rx_process(). It might even help to take a print out of just this section to read along with the explanation here.

We start with Step 1 of first copying 1024 samples of the previously received samples into the array fft_in[] and in Step2 append that with samples passed from the codec as input_rx).

Now in Step 3, we simply called the excellent fftw library to convert all the samples to frequency domain. No, we didn’t have to write that code ourselves. the fft_execute() function sets the frequency domain data in the fft_out[] array.

The variable tuned_bin is the bin of the signal that we are interested in listening to.

In step 4, we fil another array called the fft_freq[] with the data from the fft_out[] such that the fft_freq[0] has the starting at the tuned_bin.

In step 5, we zero the other sideband.

In step 6, we apply filtering. This is interesting and unusually simple. Radio amateurs are used to looking at filter graphs where the frequency is on the x axis and the response is on the y axis. We have plotted the crystal filter and the low pass filter shapes above in this paper in a similar way.

For filtering in frequency domain, we make a filter shape out of a set of frequency bins. We set to zero all the frequencies that we don’t want to pass and ‘1’ for all the frequencies that we wish to let through. Making a filter this way is a bit crude because there are edge frequencies between two bins that can introduce  distortion in the filtered output. Hence, we apply a windowing function to smoothen out the filter edges, that source code is in https://github.com/afarhan/sbitx/blob/main/fft_filter.c . It was borrowed from Phil Karn’s excellent radio project at https://github.com/ka9q.

Each time the filtering setting is changed, we generated a new filter as set of frequency domain bin values.

Applying the filter is just a matter of multiplying the filter bins with the frequency domain signal bins. It is beautifully simple.
Our signals are now in the frequency domain, they are shifted to the baseband, filtering has been applied.
In step 7, we convert the signal back to time-domain so that they can be played by the codec.
In step 8, the audio’s amplitude is normalized in the function agc2() so that a strong signal doesn’t blow your eardrums. We will not cover it here except to say that the agc2 looks for the maximum amplitude in the block of samples; if it exceeds the previous maximum, the gain is set to bring it  down to a set level. The  gain remains ‘hanging’ even after the strong signal disappears while agc_loop counts down to zero. The agc response can be slow or fast depending upon the value set in the agc_speed variable.
That’s all there is to it.

Transmitting is just the reverse

The transmission process happens in tx_process() function. It is very similar to the rx_process() though in reverse.

The block of baseband audio samples previously received from the microphone and the currently received samples are all assembled into a bin of fft_in[] array, converted to frequency domain with fftw_execute() and the resulting frequency domain signal is filtered to restrict the audio to permissible bandwidth.

The bins corresponding to the opposite sideband are set to zero.

The bins are now rotated to bring the signal to the desired frequency and converted back to time-domain with another called to fftw_excute().

Optionally, on USB and LSB, compression is applied to increase the average transmitted power and increase the intelligibility of the signal.

The Modems (aka Waveforms)

The power of sBitx comes from being able to handle many of the digital modulation methods transparently. The sBitx uses the excellent fldigi for all the keyboard to keyboard modes (including CW decoding) and Karlis Goba’s FT8 library for FT8 (available at https://github.com/kgoba).

Rather than a line by line exposition of the way the code works, we will provide an overview so that those interested in adding more modes can use this as a guide.

There are many kinds of modems that are in vogue. Some process all the samples together in a bunch like the WSJT-X suite of protocols. Others like PSK31, RTTY or even CW (it is a digital mode!) are handled one sample at a time.

The external modems are interfaced with the main routines like the rx_process() and tx_process() by reading and writing from a second virtual sound card. We create the virtual sound card by running a linux utility called snd-aloop. It creates three virtual sound cards and we use two of them. These virtual sound cards are named plughw1, plughw2, plughw3. Each one of them further has two subdevices as 0 and 1. These are unfortunate names handed out by the Linux operating system.

Along with playing the audio to the speaker, the sbitx also plays to the virtual device plughw:1,0. During transmission, instead of reading from the microphone, the samples are read from plughw:2,1. This is done transparently in the sbitx_sound.c. Accordingly, the modem software (like Fldigi) should set their record device to plughw:1,1 and play device to plughw:2,0.

The interface to the modems is through a simple set of functions implemented in modem.c. The important one is modem_poll(). It is called a few times every second. It monitors any change in the modes, transition from transmit to receive etc.

The other is modem_get_sample() which is used to generate the required waveforms, one sample at a time. This may be studied for how CW is generated from keystrokes or the paddle.

The excellent suite of modems in fldigi, written by Dave W1HKG and his team is integrated with sBitx as slave. The sBitx controls the fldigi using its XML-RPC interface : changing modes, reading decoded text or sending more text from the keyboard. The audio samples flow to and from using the virtual audio cable as mentioned above.

For FT8, we didn’t use the WSJT-X, instead we used the command line encoder and decoder written by Karlis Goba. The samples are collected in subsequent calls to modem_rx() and on every 15th second, it is written into a file and decoded by calls to the command line utility ft8_decode, the text output is displayed on the radio’s console. For transmitting FT8, the text is submitted to gen_ft8 which produces audio samples. These are picked up every 15th second and transmitted.

The User Interface

  1. The user interface of the radio has to satisfy many conditions:
  2. 1. It has to be equally usable from the front-panel with the Tuning and Function encoders and touch interface or the keyboard.
  3. 2. The user interface is delinked from the core sdr and a wholly different user interface can be substituted. The core sdr receives commands as text strings through sdr_request(). Any alternate program can generate these instead of the sbitx_gtk.c that is currently available.

Simple and adaptable, portable

The User interface has to be quickly programmable by experimenters who just want to get it done rather than study how GUIs are written. The current GUI takes minimal help of GTK and using primitives to :
a) draw lines, write text and fill rectangles.
b) Be notified of mouse events and keystrokes
From these basic routines, we build our own user interface that consists of a single user interface element described in the struct field.
The struct field, whenever it is updated, in turn automatically calls the functions to be executed with the new value or actions. The struct field member cmd is the actual text command to be executed when the field is changed or touched. Most of these commands are directly passed to the core sdr. Some of the commands that implement functional on top of the core SDR (like macros, etc.) have an asterisk ‘*’ in the beginning of their command name.
Some fields are special (like the waterfall) and they have to be drawn differently. For those cases, the default handling of their corresponding struct field is over-ridden by providing setting the fn member to a function that will handle the events and provide a custom drawing routine.

Text friendly

The commands entered from the keyboard are executed by the function cmd_exec().

It should be fairly easy to port it to other operating systems like Microsoft Windows or the Macintosh.
The base system works on text commands that can even be passed through a primitive telnet like interface. This makes it easy to adapt this to smaller displays or even an eyes-free implementation for the visually challenged. On the other hand it is also easy to build a remote, web-based front-end for the radio.
It is hoped that these features will come in very soon.

Read The Fine Manual!

The list of features and commands is ever growing. It is best to periodically download the latest The Operating Manualversion from https://docs.google.com/document/d/1fQXQoQSHD_YDnZYGjL6JCdR8Klfg4KE-FuN-5BE1OSQ/edit?usp=sharing

Alignment

As it is all in the software and the frequency is determined by a TCXO, there is very litte alignment needed. You just have to set the bias for the driver and the power amplifier.

  1. Set the PA and drive presets to fully  counter-clockwise. Add Ampere meter (your VOM should have a 10A range) to the power supply to monitor the radio’s current.
  2.  Switch to either LSB or USB and press the PTT without speaking (silence!).
  3. Now, carefully turn the DRIVE bias preset clockwise until the current starts to climb, set it to an increase of 200 mA (100mA per transistor).
  4. Next, carefully turn up the PA bias preset clockwise until the current starts to climb, set ti an increase of a further 200 mA.

You are done!

Hacking and modifying the sBitx

Here is how you can get started with experimenting with the sBitx software. We will see how the sBITX is laid out and how to quickly start changing things. The latest source code can always be found on https://github.com/afarhan/sbitx.

Quick Start

If you received the sBitx as a ready-built system, you will find the source code in /home/pi/sbitx directory. To build the system you execute the batch file build as thus:

cd /home/pi/sbitx (to change to sbitx directory)

To build the radio,

./build sbitx (remember the dot before the slash)

To start the radio,

./sbitx

How it is laid out

The sBitx is all about being able to find your way in the weirdness of software defined radios, hence every attempt is made to keep it simple and quick to understand. We assume a minimum familiarity with the C language and being able to use a text editor. The idea is to make it easy to carry the system from platform to platform.

The big idea of sBitx is that is NOT  a phasing SDR. It is a superhet with first IF at 40 MHz that is downconverted to a second IF of 24 KHz. The 24 KHz IF is digitized through a high speed, low-noise audio codec. In this specific case, it is the WM8731.

In broad strokes:

  1. The core SDR is on sbitx.c and it has no user interface around it. It gets samples from the audio system and writes them back to the audio system. It accepts commands  to change modes, volume, etc., as text strings when the user interface calls sdr_request(). This is where the samples from the mic arrive, get processed and sent to the radio (when it is transmitting) or they arrive from the radio, get processed and sent to the speaker (when it is receiving). These two routines are rx_process()  and tx_process().
  2. We use convolution method to do the actual modulation/demodulation. It is much simpler to understand. It is best explained by Gerlad Youngblood, K5SDR in his seminal papers called A Software Defined Radio for the Masses. Read them here and here. Better yet, print them out and read and re-read them until you understand the concept. A few tricky details like save-and-overlap convolution or wrap-around with imaginary frequencies are not required knowledge to hack. If you insist, read them on https://www.dspguide.com/.
  3. We do our own GUI. The regular GUIs of Linux/Mac/Windows don’t quite work for radios. Each is pretty complicated as well. We also imagine that in the future someone might want to port the sBitx to an embedded system. Besides, most of us have no time to learn intricacies of GUI programming. Instead, we took the basic ability to draw lines, rectangles and text and built our own simple user interface. All that is in sbitx_gtk.c.
  4. It is written in simple C language. in a particular style of using all smalls as the names of functions and variables and it hasunderscore between the words (do_cmd() instead of doCmd or DoCmd). The lines are formatted such that they can fit into the 7 inch Raspberry Pi screen. We use the opening brace on the same line as the if or while statement to make efficient use of the 7 inch display of the sBitx.
  5. Stick to 2 spaces per tab, braces in the same line as the if or while statement. This is not religion but utility. The compact display of the sBitx can’t show too many lines. We follow the Linux coding style.

The Sound System

The details of sound sytem are all hidden in sbitx_sound.c. There are a few things to remember

  1. The sampling rate is fixed at 96,000 samples per second. Each sample read or written to the hardware is a signed 32-bit integer. The audio hardware works in full duplex at all times.
  2. The digital IF is centered on 24 KHz, staritng from 12.5 KHz to 37.5 KHz.
  3. The left channel is dedicated to receive. It gets its samples from the demodulator (Actually, the downconverter that moves the 40 MHz IF to 24 KHz, which is kind of “audio” range, handled by the audio codecs). After processing it writes the samples back to the speaker.
  4. The right channel is dedicated to transmit. It gets its samples from the mic, processes them and writes the transmit signal samples to the right channel.

The GUI

The GUI and other platform specific details are implemented in sbitx_gtk.c.

The GUI is build around a single structure called the field. This represents a single element on the screen (like the waterfall or a button)

struct field {
  char *cmd;
  int (*fn)(struct field *f, cairo_t *gfx, int event);
  int x, y, width, height;
  char label[30];
  int label_width;
  char value[MAX_FIELD_LENGTH];
  char value_type; //NUMBER, SELECTION, TEXT, TOGGLE, BUTTON
  int font_index; //refers to font_style table
  char selection[1000];
  int min, max, step;
};

The field cmd holds the text string that is to be sent as the command to the core sbitx. Many functions are not core to the sbitx. For instance, RIT (Receiver incremental tuning) is implemented as user-level feature that changes the core sbitx frequency between receive/transmit. The commands that are not to be passed to the core sdr are prefixed with a ‘#‘.

There are a few kinds of controls defined. All of them are in square boxes that can be easily touched by stubby fingers. These are the different kinds of controls

  • Button: These have no ‘value’ just a label (Ex: Close)
  • Selection: These can have one the selection of texts given in the selection field. All values are text strings, separated by forward slash’/’. (Ex: Mode selector)
  • Toggle: Essentially a selection with two values, when you click on it, it changes (like VFO A/B, RIT On/Off, etc.)
  • Number: Like a volume control or gain control. It can take any value between min, max in jumps of step.
  • List: This is takes a long text that is wrapped around or broken with new line characters. The text scrolls past.
  • Text: You can enter text in these fields

While the default drawing and editing of these are handled by draw_field() and edit_field() functions, They can be over-ridden by a user supplied function fn. If fn is NULL, the default behaviour happens. For instance, in the tuning control, the step size is determined by STEP control and the final frequency depends upon RIT, VFO A/B, the current state of radio (transmit/receive). Hence it is handled in the funciton do_tuning().

Every time a control is changed, do_cmd() is called as a clearing house.

All the GUI commands are also available as text commands that maybe entered from the softkeyboard or a physical keyboard plugged into the USB port of the sBitx.

Before you make  any changes

Save the current snapshot of the source code using the git like this:

git commit -a -m “Saving the original, just in case, hehe”

This command commits (saves) the current snapshot. The -a means all of it, -m says, a message is to remember what the commit is all about.  To see the history of all the previous commits

git log

This lists the last few commits, Each commit is listed with the comment as well as a unique checksum. To revert to a previous commit,

git  checkout [checksum] (You can specify just the first few letters of the checksum)

This is the shortest introduction to git, learn more from the Internet.

Debugging

We use the standard linux debugger gdb. You can read all about it in the manual (Run : man gdb). However, here is a quick start for those who don’t read manuals.

Start it like this:

gdb sbitx

To run the sbitx, type the command

(gdb) r (Run the program)

To set a break-point

(gdb) b sbitx.c:100 (Sets breakpoint when execution reaches a particular line)

(gdb) b rx_process (Sets breakpoin when the execution reaches the function rx_process)

To see a variable

(gdb) p last_mouse_x (Prints the value of the variable)

(gdb) p *f  (Prints the structure/variable pointed by f)

To step inside the function on the current line

(gdb) s

To step over the current line to the next

(gdb) n

To resume execution of the sbitx after watching/stepping past a break-point

(gdb) c

To see the stack of called functions (back trace)

(gdb) bt

To get out of the debugger

(gdb) q

Learning more

There are some really amazing resources online  to help you understand how SDRs work and in particular how the sBitx works. Here is a starting list :
K5SDR’s seminal papers in QEX. These are avaiable from https://sites.google.com/site/thesdrinstitute/A-Software-Defined-Radio-for-the-Masses
Bob Larkin’s QST papers describing the DSP-10 Transceiver, the inspiration for sBitx.
http://www.arrl.org/software-defined-radio
Phil Karn’s SDR receiver project from which much of the filtering code has been borrowed.
https://github.com/ka9q/ka9q-radio
Wes Hayward, Rick Campbell and Bob Larkin’s book Experimental Methods in RF Design. Now out of print, grab a copy. It has the most radio ham friendly introduction to DSP and SDRs.
Steven Smith’s amazingly direct and in-depth treatment of DSP stuff
https://www.dspguide.com/
Wes Hayward’s website for more details on the high dynamic range AF pre-amp and other filter related papers.
https://w7zoi.net

Adapting to your own circuits

It is hoped that this work will encourage others to adapt this software to more radios. Three things need to be changed:

  1. Route the digital I/O lines that flip between tx/rx, change the tx filters and sense the PTT and keyer paddle to your radio’s circuit.
  2. In sbitx.c, change the routine that sets the local oscillator to your radio’s way of doing it.
  3. Rewrite the audio loop in sbitx_sound.c to suit the audio hardware that you are using.

Acknowledgments

A project as large as this has many contributions. I would like to first acknowledge the guidance and encouragement I received from three principle contributors to this project, Bob Larkin, W7PUA, Phil Karn, KA9Q and Wes Hayward, W7ZOI for mentoring me and helping me with techniques, codes and circuits.

I am grateful to the initial guinea pigs for this project : Arun Kumar, W8ARU, Roger Hayward, KA7EXM for their feedback. As a developer one has a huge blindspot to the bugs and idiosyncrasies of the software and the radio.

A big shout to Dave, W1HKJ who’s team developed the fabulous fldigi that works in the background with the sBitx.

To my friend and daily ragchew partner, Raj, VU2ZAP for all his suggestions and quirky experiments.

Finally, my friends at Lamakaan Amateur Radio Club : Sasi, VU2XZ, Thomas, VU2TJ and others. It is amazing to have this bunch of homebrewers all in the town who are ready to come by, share components, indulge in days of unending testing without getting bored.

The colleagues at HF Signals, Anil, VU3DXA and Venu, VU2BVB who did extensive testing of the radio in every mode in every way in their spare time and many Sundays.

This is indeed a team project that spanned years of effort from everyone mentioned.

Hacking the sBITX

Here is how you can get started with experimenting with the sBitx software. We will see how the sBITX is laid out and how to quickly start changing things. The latest source code can always be found on https://github.com/afarhan/sbitx.

Quick Start

If you received the sBitx as a ready-built system, you will find the source code in /home/pi/sbitx directory. To build the system you execute the batch file build as thus:

cd /home/pi/sbitx (to change to sbitx directory)

To build the radio,

./build sbitx (remember the dot before the slash)

To start the radio,

./sbitx

How it is laid out

The sBitx is all about being able to find your way in the weirdness of software defined radios, hence every attempt is made to keep it simple and quick to understand. We assume a minimum familiarity with the C language and being able to use a text editor. The idea is to make it easy to carry the system from platform to platform.

The big idea of sBitx is that it is NOT  a phasing SDR. It is a superhet with the first IF at 40 MHz that is downconverted to a second IF of 24 KHz. The 24 KHz IF is digitized through a high speed, low-noise audio codec. In this specific case, it is the WM8731.

In broad strokes:

  1. The core SDR is on sbitx.c and it has no user interface around it. It gets samples from the audio system and writes them back to the audio system. It accepts commands  to change modes, volume, etc., as text strings when the user interface calls sdr_request(). In sbitx.c, the samples from the mic arrive, get processed and sent to the radio (when it is transmitting) or they arrive from the radio, get processed and sent to the speaker (when it is receiving). These two routines are rx_process()  and tx_process().
  2. We use convolution method to do the actual modulation/demodulation. It is much simpler to understand compared to a phasing system. It is best explained by Gerlad Youngblood, K5SDR in his seminal papers called A Software Defined Radio for the Masses. Read them here and here. Better yet, print them out and read and re-read them until you understand the concept. A few tricky details like save-and-overlap convolution or wrap-around with imaginary frequencies are not required knowledge to hack. If you insist, read them on https://www.dspguide.com/.
  3. We do our own GUI. The regular GUIs of Linux/Mac/Windows don’t quite work for radios. Each is pretty complicated as well. We also imagine that in the future someone might want to port the sBitx to an embedded system. Besides, most of us have no time to learn intricacies of GUI programming. Instead, we took the basic ability to draw lines, rectangles and text and built our own simple user interface. All that is in sbitx_gtk.c.
  4. It is written in simple C language in a particular style of using all smalls as the names of functions and variables and it has underscore between the words (do_cmd() instead of doCmd or DoCmd). The lines are formatted such that they can fit into the 7 inch Raspberry Pi screen. We use the opening brace on the same line as the if or while statement to make efficient use of the 7 inch display of the sBitx.
    Stick to 2 spaces per tab, braces in the same line as the if or while statement. This is not religion but utility. The compact display of the sBitx can’t show too many lines. We follow the Linux coding style.

The Sound System

The details of sound sytem are all hidden in sbitx_sound.c. There are a few things to remember

  1. The sampling rate is fixed at 96,000 samples per second. Each sample read or written to the hardware is a signed 32-bit integer. The audio hardware works in full duplex at all times.
  2. The digital IF is centered on 24 KHz, staritng from 12.5 KHz to 37.5 KHz.
  3. The left channel is dedicated to receive. It gets its samples from the demodulator (Actually, the downconverter that moves the 40 MHz IF to 24 KHz, which is kind of “audio” range, handled by the audio codecs). After processing it writes the samples back to the speaker.
  4. The right channel is dedicated to transmit. It gets its samples from the mic, processes them and writes the transmit signal samples to the right channel.

The GUI

The GUI and other platform specific details are implemented in sbitx_gtk.c.

The GUI is build around a single structure called the field. This represents a single element on the screen (like the waterfall or a button)

struct field {
 char *cmd;
 int (*fn)(struct field *f, cairo_t *gfx, int event);
 int x, y, width, height;
 char label[30];
 int label_width;
 char value[MAX_FIELD_LENGTH];
 char value_type; //NUMBER, SELECTION, TEXT, TOGGLE, BUTTON
 int font_index; //refers to font_style table
 char selection[1000];
 int min, max, step;
};

The field cmd holds the text string that is to be sent as the command to the core sbitx. Many functions are not core to the sbitx. For instance, RIT (Receiver incremental tuning) is implemented as user-level feature that changes the core sbitx frequency between receive/transmit. The commands that are not to be passed to the core sdr are prefixed with a ‘#‘.

There are a few kinds of controls defined. All of them are in square boxes that can be easily touched by stubby fingers. These are the different kinds of controls

  • Button: These have no ‘value’ just a label (Ex: Close)
  • Selection: These can have one the selection of texts given in the selection field. All values are text strings, separated by forward slash’/’. (Ex: Mode selector)
  • Toggle: Essentially a selection with two values, when you click on it, it changes (like VFO A/B, RIT On/Off, etc.)
  • Number: Like a volume control or gain control. It can take any value between min, max in jumps of step.
  • List: This is takes a long text that is wrapped around or broken with new line characters. The text scrolls past.
  • Text: You can enter text in these fields

While the default drawing and editing of these are handled by draw_field() and edit_field() functions, They can be over-ridden by a user supplied function fn. If fn is NULL, the default behaviour happens. For instance, in the tuning control, the step size is determined by STEP control and the final frequency depends upon RIT, VFO A/B, the current state of radio (transmit/receive). Hence it is handled in the funciton do_tuning().

Every time a control is changed, do_cmd() is called as a clearing house.

All the GUI commands are also available as text commands that maybe entered from the softkeyboard or a physical keyboard plugged into the USB port of the sBitx.

Before you make  any changes

Save the current snapshot of the source code using the git like this:

git commit -a -m “Saving the original, just in case, hehe”

This command commits (saves) the current snapshot. The -a means all of it, -m says, a message is to remember what the commit is all about.  To see the history of all the previous commits

git log

This lists all the last few commits, Each commit is listed with the comment as well as a unique checksum. To revert to a previous commit,

git  checkout [checksum] (You can specify just the first few letters of the checksum)

This is the shorest introduction to git, learn more from the Internet.

Debugging

We use the standard linux debugger gdb. You can read all about it in the manual (Run : man gdb). However, here is a quick start for those who don’t read manuals.

Start it like this:

gdb sbitx

To run the sbitx, type the command

(gdb) r (Run the program)

To set a break-point

(gdb) b sbitx.c:100 (Sets breakpoint when execution reaches a particular line)

(gdb) b rx_process (Sets breakpoin when the execution reaches the function rx_process)

To see a variable

(gdb) p last_mouse_x (Prints the value of the variable)

(gdb) p *f  (Prints the structure/variable pointed by f)

To step inside the function on the current line

(gdb) s

To step over the current line to the next

(gdb) n

To resume execution of the sbitx after watching/stepping past a break-point

(gdb) c

To see the stack of called functions (back trace)

(gdb) bt

To get out of the debugger

(gdb) q

Cascade Calculator

This calculator computes the intercept, noise figure and gain of cascaded stages of an RF circuit.

It is based on the calculations set forth in Fig 2.69 of Experimental Methods in RF Design (Hayward, Campbell and Larkin, ARRL)

This calculator was developed by Wes Hayward, W7ZOI, the lead author of the above book. He wrote the first version in the seventies and adapted to the PC for the EMRFD’s accompaying CD. My own implementation is independently developed from the descriptions in the above reference and from his other book Introduction to RF Design.

Though these books are now out of print, they are available for online purchase and highly recommended source for the RF experimenter.

1 2 3 4 5 6 7 8 9 10
Function
Gain
Noise Figure
OIP3, dbm

Bandwidth(Hz):

Total Gain, dB
Total Noise Figure, dB
IIP3, dBm
OIP3, dBm
MDS, dBm
Two-tone Dynamic Range, dB
Receiver Factor, dB

Bluepill setup on Arduino on Ubuntu linux

This is a work in progress.

  1. STlinkV2 adapter’s pinout on the plastic case is WRONG. the actual pin details are printed on the PCB.
  2.  I installed the stlink (the stlink should be added to a special group that allows access to the ‘modem’/’usb’)
    git clone https://github.com/texane/stlink.git
    cd stlink/build
    cmake ..
    make && sudo make install
  3.  We run lsusb. It didn’t work on USB 3.0 port.  The ST-Link shows up now when plugged into a different USB port as:
    farhan@Farhan:~/Documents/ham/stlink$ lsusb
    Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 005: ID 06cb:00a2 Synaptics, Inc. 
    Bus 001 Device 004: ID 04f2:b604 Chicony Electronics Co., Ltd 
    Bus 001 Device 002: ID 046d:c534 Logitech, Inc. Unifying Receiver
    Bus 001 Device 020: ID 0483:3748 STMicroelectronics ST-LINK/V2
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  4. Started to install Arduino support for the blue pill :
    https://github.com/rogerclarkmelbourne/Arduino_STM32/wiki/Installation
  5. I tried running st-link from command line. It failed to enable it to find the libstlink,
    export LD_LIBRARY_PATH=/usr/local/lib
    sudo ldconfig
  6. Now, sudo stlink works!
  7.  Downloaded the bootloader from https://github.com/rogerclarkmelbourne/STM32duino- bootloader/blob/master/binaries/generic_boot20_pc13.bin as this has the LED on PC13.
  8. Next, we plug in the stlinkV2 adapter and probe for the chip ,then flash the progam into it.
    farhan@Farhan:~/Documents/ham/stm32$ sudo st-info --probe
    Found 1 stlink programmers
     serial: 2d0c02012612344d314b4e00
     hla-serial: "\x2d\x0c\x02\x01\x26\x12\x34\x4d\x31\x4b\x4e\x00"
     flash: 131072 (pagesize: 1024)
     sram: 20480
     chipid: 0x0410
     descr: F1xx Medium-density
    farhan@Farhan:~/Documents/ham/stm32$ st-flash --reset write generic_boot20_pc13.bin 0x8000000
    st-flash 1.6.0-392-g31b1fa1
    2020-06-30T15:42:38 INFO common.c: F1xx Medium-density: 20 KiB SRAM, 128 KiB flash in at least 1 KiB pages.
    file generic_boot20_pc13.bin md5 checksum: f6d7639c776ddc06d62a1b612ef7876, stlink checksum: 0x00192077
    2020-06-30T15:42:38 INFO common.c: Attempting to write 22268 (0x56fc) bytes to stm32 address: 134217728 (0x8000000)
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08000000 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08000400 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08000800 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08000c00 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08001000 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08001400 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08001800 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08001c00 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08002000 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08002400 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08002800 erased
    2020-06-30T15:42:38 INFO common.c: Flash page at addr: 0x08002c00 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08003000 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08003400 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08003800 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08003c00 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08004000 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08004400 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08004800 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08004c00 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08005000 erased
    2020-06-30T15:42:39 INFO common.c: Flash page at addr: 0x08005400 erased
    2020-06-30T15:42:39 INFO common.c: Finished erasing 22 pages of 1024 (0x400) bytes
    2020-06-30T15:42:39 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
    2020-06-30T15:42:39 INFO flash_loader.c: Successfully loaded flash loader in sram
     22/22 pages written
  9. This concludes installation of the bootloader and any other program into the Blue pill if we already have a .BIN file. Whew!

Part 2 Setting up the Arduino IDE

Here, we will figure out how to compile our blink with an output to the USB’s serial port.

  1. The Arduino_STM32 folder has to be copied into a newly created ‘hardware’ folder inside the ‘Arduino’ folder in the home directory.
  2. We checked that lsusb lists the port as
    farhan@Farhan:~/Documents/ham/stm32$ lsusb
    Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 005: ID 06cb:00a2 Synaptics, Inc. 
    Bus 001 Device 004: ID 04f2:b604 Chicony Electronics Co., Ltd 
    Bus 001 Device 002: ID 046d:c534 Logitech, Inc. Unifying Receiver
    Bus 001 Device 026: ID 1eaf:0004 
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  3.  Under tools > port, the Bluepill appears as /dev/ttyACM0 (Maple Mini)
  4. The blink was compiled, and uploaded through “STM32duino Bootloader” . The board had to be reset for it to be recognized, then it uploaded properly.
  5. Here is the test sketch (the potentiometer was connected to A7 and between GND and 3.3V).
    const int analogInPin = PA7; // Analog input pin that the potentiometer
     // is attached to
    
    const int pwmOutPin = 9; // PWM pin that the LED is attached to
    
    // These variables will change:
    int sensorValue = 0; // value read from the pot
    int outputValue = 0; // value output to the PWM
    
    void setup() {
     // Configure the ADC pin
     pinMode(analogInPin, INPUT_ANALOG);
     // Configure LED pin
     pinMode(pwmOutPin, PWM);
     Serial.begin(115200); // Ignored by Maple. But needed by boards using Hardware serial via a USB to Serial Adaptor
    }
    
    void loop() {
     // read the analog in value:
     sensorValue = analogRead(analogInPin);
     // map it to the range of the analog out:
     outputValue = map(sensorValue, 0, 1023, 0, 65535);
     // change the analog out value:
     pwmWrite(pwmOutPin, outputValue);
    
    // print the results to the serial monitor:
     Serial.print("sensor = " );
     Serial.print(sensorValue);
     Serial.print("\t output = ");
     Serial.println(outputValue);
    }
  6. The output came out on the serial port alright.
  7. While uploading be sure to press the RESET button the bluepill simultaneously with the upload button on the Arduino IDE, this will catch the bootstrap loader before the serial port opens itself up for debug messaging.

Step 3 – Measure the analog conversion rate

  1. The ADC example is on https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F1/libraries/STM32ADC/examples/MultiChannelContinuousConversion/MultiChannelContinuousConversion.ino
  2. It needs to kickstart the conversion with a startConversion call:
    uint8_t pins[] = {PA7, PA6};
    const int MAX_SAMPLES = 8;
    uint16_t data_points[MAX_SAMPLES];
    
    void setup() {
     // put your setup code here, to run once:
     my_adc.calibrate();
    
    for (unsigned int j = 0; j <8; j++) 
     pinMode(pins[j], INPUT_ANALOG);
     
     my_adc.setSampleRate(ADC_SMPR_1_5);//set the Sample Rate
     my_adc.setScanMode(); //set the ADC in Scan mode. 
     my_adc.setPins(pins, 2); //set how many and which pins to convert.
     my_adc.setContinuous(); //set the ADC in continuous mode.
    
    //set the DMA transfer for the ADC. 
    //in this case we want to increment the memory side and run it in circular mode
    //By doing this, we can read the last value sampled from the channels by reading the dataPoints array
     my_adc.setDMA(data_points, 2, (DMA_MINC_MODE | DMA_CIRC_MODE), NULL);
     my_adc.startConversion();
    
    Serial.begin(115200); // Ignored by Maple. But needed by boards using Hardware serial via a USB to S
    }

Hello world!

I have decided to move to wordpress to blog. This will assimilate content from my previous phonestack.com, the various blogs and bits and ends into  one place.