CESSB stands for Controlled-Envelope Single-Sideband. It is a speech processing technique described by Hershberger in QEX in 2014. The short version: standard SSB loses 3-4 dB of average power because the Hilbert transform of your audio can phase-align with the original signal in ways that create envelope peaks higher than the audio alone. CESSB clips those peaks in the complex domain before the final bandpass filter, and because envelope clipping artifacts mostly land outside the SSB passband, the filter removes them cleanly. You get 3-4 dB more average power for the same peak envelope power and the same linearity. That is a meaningful difference -- it is the difference between 5W and about 10W in terms of how the other station hears you.
No commercial portable HF radio implements CESSB internally. I want to add it to the X6100.
The X6100 is a two-processor system. The Allwinner A33 ARM runs Linux and the GUI application -- that is the part I have been working with for POTASpotter and other features. The STM32 handles the actual RF: ADC, DAC, the SSB modulator, the DSP chain, the PA. The community firmware I have been working with is all A33-side. CESSB has to happen on the STM32, in the baseband DSP chain, right after the SSB modulator produces the I/Q signal.
The STM32 firmware is not open source. Xiegu does not publish it. The community has a patchset framework -- x6100_base_patch -- that injects C code into the OEM binary using code caves in unused flash regions. But to hook into the right place you have to find it first. That means Ghidra.
Before writing any code I set up a structured research plan with eleven named risks, in order of how badly each one would kill the project. The goal of the research phase is a go/no-go decision, not working firmware. If something fundamental does not work, I want to find out before I have spent weeks on implementation.
The risks that mattered most:
Before touching the radio at all, I built a Python implementation of CESSB and ran it on recorded voice audio. This confirmed the algorithm works as described -- about 2 dB of peak-to-average ratio improvement at the 99th percentile, 3.2 dB at the 97th percentile, matching Hershberger's published numbers. It also gave me a measurement framework I could reuse later when validating the actual radio output.
The STM32 TX chain runs at 12.5 kHz internally. Voice band is 0-3 kHz. That leaves about 3 kHz of headroom above the passband for the post-clip filter to work in. I was worried this would cut into the CESSB benefit significantly.
It does not. Running the Python prototype at 12.5 kHz actually outperformed 48 kHz by about 0.4 dB. The explanation is that at 12.5 kHz the bandpass filter is a much larger fraction of Nyquist, making it relatively sharper and better at removing the out-of-band clipping artifacts. That risk closed green before any Ghidra work started.
The OEM STM32 binary is a compiled ARM Cortex-M4 binary with no symbols, no debug info, and no source. Finding the SSB modulator in it means loading it into Ghidra and working through the disassembly until the structure becomes recognizable.
What I expected to find was a discrete Hilbert FIR filter feeding a mixer feeding a bandpass filter -- the classic textbook SSB modulator architecture. What I actually found was a single CMSIS-DSP biquad cascade call that does all three things at once. One call to arm_biquad_cascade_df1_f32 per sample, six filter sections, coefficient conjugation to select USB vs LSB. Simpler than expected and a clean hook target.
The hook point is a tail-call at the end of the modulator where all four modulation modes -- LSB, USB, CW, and LSB-D/USB-D -- converge before the signal goes downstream. Replacing one branch instruction there redirects execution to the code cave. At that point the I and Q values are sitting on the stack, the FPU registers are clean, and CESSB can read them, process them, write them back, and hand off to the original downstream chain.
Session 4 confirmed that the TX compander and ALC both operate on the audio input before the modulator -- they shape the audio signal, not the modulated I/Q. CESSB operates after the modulator on the I/Q output. They are in different stages of the chain and do not interact.
With the hook located and the architecture understood, I wrote the patch: a C file implementing the CESSB algorithm and an assembly stub that redirects execution from the hook address into the cave, runs CESSB, and returns to the original chain. It compiled clean against the Cortex-M4 hard-float target at 650 bytes total -- well within the available code cave space.
The CMSIS-DSP functions CESSB needs -- arm_biquad_cascade_df1_f32, arm_sqrt_f32, arm_fill_f32, arm_copy_f32 -- are all already linked in the OEM binary. The patch calls them directly by address. No new library code needed.
Sessions 1-6 targeted OEM firmware version V1.1.6_221112001. While that work was in progress, Georgy -- the maintainer of the community base_patch project -- replied to the GitHub discussion thread where I had posted about this work. He pointed out that the current upstream base_patch builds against V1.1.6_230307001, a later OEM version.
Same processor, same DSP architecture, same algorithm. Different addresses for everything. Every Ghidra offset, every hook address, the cave location, the CMSIS-DSP function addresses -- all of it needs to be re-derived against the newer firmware before the patch can be flashed to a radio running current firmware.
Georgy also provided the OEM Hilbert FIR coefficient file, confirmation of the CCMRAM layout for placing the CESSB state struct, and a debug trick using a reserved field in the radio's flow data structure to read diagnostic values from the Linux side without a JTAG debugger. All of that is going into the next session.
The next session is the address re-derivation -- loading 230307001 into Ghidra, finding the same structures in the new binary, and updating the patch. After that, re-targeting the compiled patch to the new addresses and wiring the init function into the right hook in the C side of base_patch. Then flashing and testing on the radio with a dummy load and the SDR watching the output.
If that goes well the go/no-go decision becomes easy. The only remaining real unknown is how the PA handles the increased average power at extended duty cycles. The X6100 PA runs warm at 10W as it is. CESSB at full depth could push dissipation up meaningfully. The plan is to test at light, medium, and heavy CESSB settings and see where the thermal headroom runs out.
This is the deepest I have gone into a closed firmware. Ghidra sessions, STM32 assembly, code cave injection, coordinating with the upstream maintainer over GitHub. All of it described to Claude, who did the actual work of reading the disassembly and writing the C. I pointed at structures and asked what they were. Claude read them and told me.
More when the flash test happens.