Bringing the Xiegu GSOC back from the dead
One HAM’s bench notes on why it broke and what works on a v1.81 body instead.
Code & docs: github.com/cjlawson02/xiegu-g90-research
A couple of years ago I bought a Xiegu GSOC, which in retrospect was either optimism or a mild personality disorder. I already loved the little G90. The companion head felt like the obvious next step: bigger picture of the band, less squinting at the stock display unit, and more “this is actually a HF station.”

Stock GSOC product photo (Radioddity).
Then the punch-in-the-face arrived early: even when I bought it, G90 firmware had already lapped the GSOC. Flash any modern firmware onto the radio and the controller becomes a very big paperweight. The forums had the workaround strategy nailed: stay on firmware ≤v1.78, which is a polite way of saying the vendor moved on and you’re now the curator of a firmware museum. (YO3HJV had the “COMM LOST” story years before anyone had a clean byte map.)
Nearly four years after the v1.79 firmware break, Xiegu still isn’t coming to save this thing. So I started reverse engineering the protocol myself.
Normally that means weeks of static analysis, wrong guesses, and forum posts. I used Ghidra and Cursor together, not to skip the hard parts, but to iterate faster on encode/decode rules and tests once the firmware diff pointed at the real change. The surprising part was how small the remaining problem became: one vocabulary jump at display 1.78b01 → 1.79b02, not a new wire format.
But what am I even trying to do here? Three goals, none finished… yet:
- GSOC working on current G90 firmware again
- Drive the body from a computer (or anything you choose) with more than CAT alone
- Open-source the library that supports both
What follows is the paved path so far: break map, codec, tests, live bench, not a shipped product. Goal #2 has a working lab path on a v1.81 MainUnit I had on the desk. I have not run this on your radio, and I have not touched GSOC hardware in this pass. Goal #3 is xiegu-g90-research. Goal #1 is still later.
Before we get started, here’s the obligatory: I’m not with Xiegu or Radioddity. This is my own radio, my own mistakes. I’m publishing protocol notes and original codec/control code, not any keys, nor decrypted firmware dumps.
Same wire, different dialect
The G90’s display↔body link on the front-panel DE-9 connector is a proprietary binary conversation. This is not the CI-V protocol on the CAT port. Community work (especially zeroping’s headprotocol notes) already had the wire format mostly right: small fixed-size messages both ways, each ending in a CRC-32 checksum (the MPEG-2 variant, a polynomial name, not video compression), at a boring 115200 serial bitrate.

DE-9 connector on the radio body: bottom left hole is pin 1, bottom right is pin 5.
When users upgraded the G90 past the last GSOC-compatible pair (display firmware 1.78b01 → 1.79b02, with a matched body), the meaning of the bytes changed: mode numbering, filter encoding, spectrum zoom. Stock GSOC firmware kept speaking v1.78 into a body that had moved on. Of course it faceplanted.
The DE-9 fields that actually brick the GSOC are mode, filter packing, and spectrum zoom. On v1.81 hardware I did not see those move again from 1.79 through 1.81; AM/NFM defaults and CI-V still churn per release, but the head-link vocabulary that matters here looked stable. (I’ve checked this with firmware RE on my side; the public repo defends the 1.81 alias with tests and fixtures, not a per-version capture matrix.)
And on my bench (one live MainUnit v1.81, stock head unplugged, cheap FT232 at 3.3 V pretending to be a display) I was able to:
- talk both directions without the radio hanging up on me
- change mode and tune, and watch the body actually do it (automated body-effect checks, not “the S-meter twitched”)
- draw the spectrum/waterfall the body was already sending back for free, relative bins, not calibrated dBm
Although this is not yet a “product”, not independent replication, and not “GSOC fixed“, it does introduce a light at the end of the tunnel. This is the first step towards a working GSOC on current G90 firmware, maybe even with an open-source UI/controls.
How a nice radio accessory became a brick
The GSOC is basically a tiny Linux PC (Allwinner A20) acting as a G90 head. gsoc-contrib already did the teardown justice (also props to Radioddity for blogging this!). The GSOC software that talks proprietary serial froze at FW v1.3. Good news: the hardware didn’t rot. Bad news: the software did. (See gsoc-contrib for teardown photos: hardware fine, software frozen.)
So, I opened the boxes myself and started reverse engineering the protocol. Importantly: none of the work happened with a soldering iron. It happened with firmware images, Ghidra, and an AI that could keep iterating while I steered.
First I pulled the official GSOC v1.3 Linux SD-card image (Radioddity firmware portal) and threw AI and Ghidra at it. Not to “rewrite the whole UI from scratch on day one,” just to find how this thing actually talks on the DE-9. Which serial port? What messages does it send? What does it expect back? A few years ago that archaeology would have burned weekends: chasing strings, renaming mystery functions, sketching “who calls whom,” getting lost in a stripped Linux app, coming back tomorrow. With AI in the loop it became boring in the best way: I would ask a question, get a map, check it against the binary, correct the bad guess, and ask the next one. The answer that mattered, for me, was boring: the GSOC is not doing magic. It’s speaking the same head↔body dialect the stock display unit speaks, frozen at the dialect it knew in 2021, on top of wire-format work zeroping and others had already sketched.
Then I pulled the official G90 v1.78 and v1.79 firmware packages for both the display unit and the radio body, decrypted them locally with the community-known method (keys already public elsewhere; not republished here), and compared them again with AI + Ghidra. Firmware decryption stayed on my machine for diffing only. It is not in the public repo and isn’t required to use the codec. If you decrypt vendor images, that’s your local legal calculus; xiegu-g90-research publishes behavior and clean-room code only.
That static reverse engineering is where the brick became a concrete, testable byte-level claim. With the frozen GSOC app on one hand, and G90 head/body firmware across the break on the other, the change became something you could point at in decomp instead of hand-wave on a forum. AI helped me walk the “how do we pack mode/filter/spectrum into a message?” paths on both ends, draft the encode/decode rules, generate test vectors, and revise them when a claim didn’t hold up. The cut that matters for GSOC owners is display firmware 1.78b01 → 1.79b02. Plain-English version:
| What changed | Old world (≤1.78 / stock GSOC) | New world (1.79+ … 1.81) |
|---|---|---|
| Mode numbers | Six modes, numbered the old way (USB was basically “1”) | Two digital modes shoved into the middle; every number after that shifted |
| Filter bandwidth | Edges packed in ~25 Hz steps (different hi/lo offsets) | Same idea, different math (~50 Hz steps, shared −50 Hz baseline) |
| Spectrum “zoom” | Manual gain or AUTO | AUTO only; the manual knob went away |

Same frame sizes and CRC; different packing for mode, filter, and spectrum zoom.

Clean-room codec demo: CRC still OK; the body would read L-D, not USB.
That is the interoperability brick for mode, filter, and spectrum zoom, spelled out instead of “protocol changed, good luck.” Vendor still wants matched head+body versions; this is the payload drift that makes frozen GSOC v1.3 toxic on a ≥1.79 body.
Before I trusted any of that on a cable, I encoded it as something a machine can fail: a dual-profile codec (1.78 for the old world, 1.79 for post-break), golden vectors, and pytest tests/ that must pass. AI helped with the boring loop (draft encoder, write test, run it, fix the claim), but the pass/fail gate was always the tests and then a live radio, not my confidence.

What I actually did with a cable
I didn’t start by tearing the GSOC board apart. I started by pretending to be a head.
Power off with the stock display unit fully unplugged, then connect a 3.3V FT232 on the front-panel DE-9 only, 115200 8N1, no flow control:
- pin 5 → ground
- pin 2 → adapter TX (laptop talking to the radio)
- pin 3 → adapter RX (radio talking back)
To power on the body without the stock head attached, I shorted pin 4 to pin 5 (ground) momentarily; treat that as “press the power button,” not a permanent strap. Your mileage may vary; see bench-setup for the conservative wiring notes.

Fake-head setup: GND→pin 5, adapter TX→pin 2, RX→pin 3; FT232 at 3.3 V logic. Stock display head unplugged from the DE-9.
Note: pin 1 is +8 V. If you wire that into your adapter because “power is power,” congratulations on adding your laptop to your existing paperweight collection. (The GSOC is already in there, right?). And again, make sure to set the FT232 jumper to 3.3 V before you drive TX. 5 V logic on pin 2 is how you also add the G90 to this exponentially growing paperweight collection.
From there the loop was deliberately unromantic:
- Listen until the radio’s replies looked like real, intact messages, not garbage (
g90-live-head). - Start sending “I’m a display unit” messages with profile
1.81(same wire map as1.79). Encode with the old1.78map on a ≥1.79 body and CRC still passes. That’s the classic brick: USB becomes L-D, and the radio politely misreads you forever. - Change mode and frequency on purpose, then demand proof the body actually moved: run
g90-effect-diffuntil mode step, frequency step, header delta, and FFT fingerprint all move, with Po=0.

RX-only ladder: mode and frequency steps moved body header + FFT; Po=0 throughout.
Then the fun part: those reply messages already carry a chunk of spectrum data the stock head uses for its little scope (372-byte body→head layout). I pointed a small plotter at them (g90-live-fft). It made a pretty picture on the desk, but only in relative bins (not dBm), which was enough to confirm the parse and not enough to call calibrated.

Vendor UI on the stock Xiegu display head, same 1.400 MHz AM carrier, for comparison.

Stock head unplugged; laptop on the DE-9. Animated waterfall from live body bins, pretty but not calibrated (relative units, not dBm).
AI helped write much of the codec, test, and plotter code, and that seems worth saying up front. The byte map, the fixtures, and a live MainUnit v1.81 passing g90-effect-diff were the parts I kept doubting until I saw them on the bench. A frame can pass CRC and still have the body on L-D when I meant USB, which is exactly the trap this work was trying to avoid.
So what’s actually new?
Zeroping’s headprotocol notes, gsoc-contrib’s teardown, years of forum posts saying “don’t flash past 1.78”: people already knew the GSOC broke when the G90 moved on. What was still missing was the byte-level why, and any proof you could talk to a current body again.
The break lands on display firmware 1.78b01 → 1.79b02: same 96-byte head frames, same 372-byte replies, same CRC, same 115200 serial, but mode numbers renumbered when L-D and U-D showed up, filter math changed, and spectrum zoom went AUTO-only. Stock GSOC still sends the old packing; a v1.79+ body misreads it. There is no magic version handshake on the wire; send the wrong mode byte and you lose every time.
I got as far as a live MainUnit v1.81 on my bench: stock head unplugged, FT232 on the DE-9, g90-effect-diff passing when I changed mode and frequency on purpose. Someone else repeating that on their hardware is the next step that actually matters. The body already ships spectrum bins in those 372-byte replies; g90-live-fft just draws them (relative bins, not dBm). All of that is in xiegu-g90-research with tests and an evidence matrix so you can see what CI vs bench actually proves.
I am not claiming stock-head Y-tap sniff, calibrated dBm, filter echo in every body byte, independent replication on other firmware builds, or a fixed GSOC. What is in the repo today is bench fake-head control on current firmware from one lab session. Putting the same dialect back on GSOC hardware comes later.
Where I want this to go
In the short term I want to keep the wire boring and write docs that match what the bench actually shows. The v1 control behavior is frozen in the control spec. What’s left on the bench are gaps I have not closed yet: some status/header bytes still need more time, and absolute spectrum units are uncalibrated, without pretending those block tuning and control.
In the medium term I would like a laptop (or any random board you like) to work as a credible GSOC-shaped frontend. It would have meters, tuning, remote scope, the daily-driver stuff, without begging for vendor support. All open-source, of course, and BYO device first; that path is closer. For now that means the bench Python in the repo, not a polished installable.
Longer term I would like to put that capability back onto GSOC hardware, with open-sourced UI/controls, extendibility, and scripting out of the box. And while we’re at it, I would also like a modern OS on the little A20 board; d3cker and others already proved that’s possible.
That’s a lot to sketch out, so I won’t call it a promise. I got into this because I didn’t want a museum piece on the desk. The G90 was never the bottleneck; the frozen conversation was. Static RE named the dialect, tests and a live body proved the tools worked, and “put it back on the GSOC” is starting to look like software instead of archaeology.
73 de KM6FTS
Thanks:
- zeroping for the headprotocol notes that made the DE-9 stop looking like noise
- Mike Dubinko / gsoc-contrib for the teardown and SD-card archaeology
- YO3HJV for documenting the version lock years before anyone had a clean byte map
- OpenHamradioFirmware / G90Tools for the firmware tools
- d3cker for proving you can put a real OS on a GSOC
- the XieguG90 TechnicalAnalysis crowd for arguing about scope streams while the rest of us were still stuck on CI-V
- and Radioddity for backing up the teardown instead of pretending the hardware was a black box