radiocli Home Blog GitHub

All posts

Not ASCII

Yesterday I wrote up how I pulled the SDS150’s display colors out of its menus in “Ask the Screen.” Today it’s the character set. The serial port hands you the scanner’s screen as lines of text, and most of it really is text. But the radio’s font keeps pictures at the codes below 0x20 and above 0x7E, and the display carries them inline like any other character. A scanning screen always shows at least two of them: 0x1A and 0x1B, the two halves of the arrow that tells you which way the scan is running, sitting in the two cells the screen map calls Direction. Anything that redraws this screen instead of just reading it has to know what all 256 codes look like. Finding out was its own little expedition, and the full result, every glyph transcribed as a pixel grid, is in glyphs.md.

The spec that cites a ghost

Uniden’s remote command specification, in the protocol notes under the STS command that returns the screen, offers exactly one sentence about all this:

See “Font Data Specification” for not ascii character code.

That sentence is the entire citation, and it points at a document Uniden never published. The document does exist: it’s the Font Data Specification for the SDS200 (UB384Z), version 1.00, issued August 2, 2018, and it draws every one of the 256 character codes as a pixel grid, page after page. It is not on Uniden’s site, not linked from any firmware or support page, and not referenced by the SDS series specification at all. The only other place this font lives is the radio’s firmware, and the firmware is encrypted; there’s more on that in the oddities file.

So I asked Grok to go find it. Plain search had gotten me nowhere, and the reason is worth understanding, because it decides which tool you reach for. I had no phrase from inside the document to search on. What I had was a description of a thing that should exist: a Uniden font specification for a scanner display, probably a scanned PDF, probably parked somewhere unofficial. Search engines match text you already have. Models match descriptions of text you don’t.

It came back with a hit, on one of those sprawling third-party manual archives that quietly mirror equipment documentation nobody official bothers to keep online. Somebody had uploaded it years ago and moved on. Every pixel grid in this post came out of that upload, so whoever runs that site and whoever fed it this file are the reason the rest of this write-up exists. Those archives are load-bearing infrastructure for anybody working on hardware older than the support page for it.

It is the SDS200’s document, though, and my radio is an SDS150. Every code on a live SDS150 screen lands where the document says it should, which is evidence the two models share a font. Evidence, not proof.

Getting bitmaps out of a drawing

The specification is a drawing, not a font file. Each glyph is a grid of filled and empty cells on a page, so how do you get the bitmaps out? You read the picture back. And here whoever drew the document did me a favor, deliberate or not: everything is rendered in three exact greys, one for the table’s own borders, one for the ruling inside a grid, and one for a filled pixel. That means the pixel lattice can be found by color instead of guessed from spacing. A filled cell paints over the ruling at its own edge, so each grid is placed from the borders that fence it, which no glyph can erase. That three-grey discipline is the kind of quiet craftsmanship that never gets thanked, so: thank you, whoever you are.

A machine transcription is only as good as its checks, and I ran two, both across all 256 codes rather than a sample. First, every code the specification names is drawn and every code it leaves unnamed is blank, with no disagreement anywhere in the table. Second, every printable ASCII glyph is legible as the character it’s supposed to be, with its bottom row clear, which matters because the bottom row is where a grid that’s off by a single cell shows itself first. When you transcribe by machine you don’t spot-check. You check everything, and you aim the checks at the failure you’d actually expect.

Two fonts that are one font

The document holds two complete tables, and on paper the radio has two fonts. The small one is a 16 by 16 cell, used for everything by default, thirty characters to a line. The large one is 16 by 32, used for the system, department and channel names, twenty-four to a line, and its doubled height is what the manual means when it says a large row occupies two lines. The DSP_FORM field in STS is what tells you which font drew a given line.

But I measured rather than assumed, and the large table is just the small one with every row drawn twice. Glyphs pulled out of the large pages agree with the small ones doubled to between 97 and 100 percent, E and F exactly, and every remaining pixel is explained by the sampling of my reader. So a renderer needs one table and a way to draw it at double height, and that’s all it needs. Both tables draw a glyph 16 pixels wide, but the radio gives a large character a 20 pixel cell and stretches the picture across it, which is how 24 large characters and 30 small ones both fill the panel’s 480 pixels exactly. Somebody did that arithmetic on purpose, and it’s tidy.

What the pictures actually are

Below 0x20, the codes an ordinary screen carries, you get the working parts of the display: the AM, FM, NFM and FMB modulation tags, a degree sign, the function-key indicators, and the volume and squelch bar as three pieces, left end, middle, right end, so a bar of any length is built by repeating the middle. Icons wider than one 16 pixel cell are split across consecutive cells, the direction arrow being the everyday example, and neither half means anything alone. Above 0x7F live the status icons: battery low, keypad lock, priority, hold, avoid, the five-step signal meter, GPS, mute, and a recording indicator that runs six cells wide. Several names repeat two, three or four codes in a row, which is how the radio draws one icon at different widths or in different states.

The transcription also records every empty code as empty, with a note instead of a grid. That’s deliberate. It keeps “nothing is drawn here” distinct from “this file does not say,” and if you’ve ever debugged against documentation that couldn’t tell you which of those it meant, you know why I bothered. Absence you’ve verified is data. Absence you haven’t is just a hole.

The document is the font

The pixel grids in glyphs.md are not a companion to the font. They are the font. Every glyph sits under a heading that names its code, drawn as a grid a person can read off the page and a program can parse without being clever, so anything that redraws this screen works from the same copy I proofread. There is no packed second version sitting somewhere with its own chance of drifting.

That idea has a pedigree. Donald Knuth called it literate programming back in 1984: the program and its explanation are one artifact, and the machine consumes the same text the human reads, which keeps the prose honest. Forty years on, most documentation still drifts, because it lives in a wiki three tabs away from the thing it describes. A font written as ASCII art you can read and a program can load doesn’t get the option.

The fossils in the font

Why does a radio with no WiFi carry WiFi icons? Because the font isn’t really this radio’s font. Codes 0xC0 through 0xC7 hold four WiFi indicators, an X and three signal steps, each in two halves, on a scanner that has no WiFi hardware and whose property table omits the WiFi field entirely. They belong to a sibling model, carried along because the platform shares one font across the family. A font is a fossil record that way. Long after the product lines diverge, the shapes stay in the table, waiting for a screen that will never call them.

Uniden wrote all of this down in 2018 and never posted it. Consider it posted.