## Character-string Histogram

I’m in the midst of counting glitches on a WWVB receiver, a glitch being any pulse with a duration that isn’t close to 200, 500, or 800 ms. It’s useful to know the pulse width distribution, which is what a histogram does for a living.

Rather than a graphic histogram, though, I’ve been using a character array (a string!) with one character for each possible pulse duration, measured in units of 20 ms. The value of each character indicates the number of pulses having that duration during the previous minute.

The counting sequence goes like this:

• No counts: . (a period)
• 1 to 9 counts: 1 .. 9
• 10 to 35 counts: A .. Z
• 36 to 61 counts: a .. z
• More than 61 counts: !

So a minute with no glitches (and a valid WWVB time code frame) looks like this:

```.........4S9............462............322.........
```

The three clusters show the three valid pulse widths. The pulse widths from the receiver have an inherent jitter, plus the usual ±1 jitter you get for free when you digitize something, plus (this being RF from half a continent away) whatever the Lords of Cosmic Jest do to the signal. So each width usually occupies two or three cells.

• The 200 ms binary zero pulses form the cluster on the left: that “S” is equivalent to 10 + 18 = 28 counts. Add in the 4 and 9 on either side and you get 41 binary zero pulses.
• The middle cluster has the 500 ms binary 1 pulses: 4 + 6 + 2 = 12.
• Each WWVB time code frame has exactly seven 800 ms frame markers, which form the cluster on the right end: 3+2+2 = 7.

Add them up: 41 + 12 + 7 = 60. That’s exactly the right number of pulses in a minute. What a pleasant surprise!

A minute with three out-of-spec glitches looks like this:

```1...1....6NC2............82.......1.....51.........
```

And a minute with very high noise that pretty much obliterates the WWVB signal:

```f!!jHC6AB746312.2121..2.1..........................
```

Here’s how all that works…

The histogram counters form a character array that’s also a string. There are 50 20-ms Jiffies in each second (given by the obvious constant), so the histogram has 52 entries: 50 valid counts (0-49), 1 for “more than that”, and 1 for the null byte at the end of the string.

```char    Durations[JIFFIES_PER_SECOND + 2];
```

Initialize each counter (character) with the starting value and jam a binary zero at the end:

```memset(Durations,'.',sizeof(Durations)-1);
Durations[sizeof(Durations) - 1] = 0;
```

And then tick the appropriate counter as each pulse arrives:

```Index = min(PWM_Width,sizeof(Durations)-2);

switch (Durations[Index]) {
case '.' :
Durations[Index] = '1';
break;
case '9' :
Durations[Index] = 'A';
break;
case 'Z' :
Durations[Index] = 'a';
break;
case 'z' :
Durations[Index] = '!';
break;
case '!' :
break;
default :
Durations[Index]++;
}
```

The switch statement maneuvers the counting sequence through the digits, uppercase and lowercase alphabet, then enforces a stall at the maximum count value of “!”. You can’t just increment each element without some checking, because you do not want unprintable control characters in the string.

Then you print the histogram as you would any ordinary string. If you’re using an Arduino, as I am, this will suffice:

```Serial.println(Durations);
```

All this depends on the ASCII character set’s numerical sequence. Ponder the charts there and all should become clear.

Here are the histograms from an hour of WWVB reception during the late afternoon of New Year’s Day: watch the noise floor rise up and eat the WWVB signal…

```
.........7OD............36............133..........
.........BID1...........37.............6.1.........
.1.......9O91..........262.............43..........
........27V2............272............421.........
.........4Y41...........46............124.........2
.........AP71...........2811..........123.........2
.........AN9............731...........1.6..........
12.111..28IB11.........1623.......2..1.42.....2....
1412...125Q911..........461.....1......41..........
1........8Q81..........137...1.........15..........
.12....119N912.........116..........1.132..........
1521..1.17O931...1......27...........1..5..........
.12....12BKB2...........332....1......132.......2..
25211.1.3AHB5......1...1143........1...14.........3
45412...4BDA4..12.......25111..1.....1..22.........
6A44322.26CI53.2.1...1..343...1.......1.11.........
5D75531113FG511...12..212313.1........1.2..1.......
1432.1.119GB41..........262.1...11.....14..........
.5211..115MB4...........441............14.2........
.4.1....25JB6...........442.........1..231.........
6B443...27I772..........2322.....1.....1221......2.
4555411.27HA321........3232......1....114..........
3232.1.129GI1...1.......4321...1.1.1...111.........
...1..1.19O8..1....11...262............24..........
11......2AN62...........622...........124..........
111.....2BJ551..1......1522........3..141........21
1421....29N611.......1.136............1231........2
1.12.....AIB3........1..38...1........312..........
7F42..1.28FE341.1.1.....351........1...12...1.....2
.23......8GG2....11....2423............33..........
112..13.1AK931.....2....142....2.11.1..3.1........2
423111..44JD4.2..1.....1433........11111...........
1413..1.3CL812..11......242..1....11...21.........2
474112.34AI84.2...3.1...16.2.....1...1.11..........
8LHC734657D857411..11...131......1..1.1...........2
JuSFDCCDDCD662412...1..............................
VmG376788AC8671.21.1..3.......1....................
IlCB4576CFE532131.11.1..1.1........................
KxN798EB98A7422...2.1...1..........................
AYL96853CF7742121311.13..21....1...................
8TDEB6649A7952..32.41..124.....1.....1............2
DeO78638C9GA6142.15111..........1........2.....2...
AcPC83426BD4823.122..11132.1.......................
7SF31121AHFB73..2.111...321..1.1...................
7F3221414GG5411.111121.133...1.1.1.................
6RD9669647F7361.31.2..1212...1.....................
6L71.2444ALB21...221.212321..2...1...1.............
4LHFD7638C9B12.31341.22113...2..........1..........
264321112EFA5.112.22.2.14111..1.....1...1.........2
235511445BEC3.12..1131.131..1.1........11..........
7UC741635AAC442.1..112.1..11311.......1.1.........2
EVLCAB3598E752252133.111.1.1..........1...........2
JnTE9913A59A452331.13..1222.............1..........
f!!jHC6AB746312.2121..2.1..........................
!!!iMB772.4532..1.1.1.1..1.........................
```

1. #1 by randomdreams on 2010-01-15 - 11:52

That’s a sweet visualization. You may have already answered this previously, but do you have an idea about what causes the noise that’s periodic and shows up as vertical bands in the plot?

• #2 by Ed on 2010-01-15 - 13:17

the noise that’s periodic and shows up as vertical bands in the plot

That’s not noise, that’s data!

The WWVB pulses have only three durations: 200, 500, or 800 ms. There’s a touch of jitter, so the decoded pulses are smeared over two or three characters: a binary zero (nominal 200 ms) might show up in any of the 180-200-220 ms cells.

So when you stack up successive histograms, one per minute, you wind up with vertical bars for the WWVB PWM data… and no vertical bars when the noise floor eats the signal.

Remember: left-to-right is pulse duration at 20 ms/character, vertical is elapsed time at 1 line/minute.

I must build a WWVB receiver so that I can get a look at the raw signal strength, because I can’t tell whether the RF is decreasing (so the same noise is louder) or the noise is increasing (so it drowns out the same RF). Most likely, it’s a combination of the two, because we know RF propagation is feeble during the daylight hours.

The C-Max receiver board does have an internal node where a filter cap smooths out the demodulated signal for AGC control, but I’m leary of just sticking a probe in there to see what the DC level might be: it’d inject fuzz right into the demodulator and I’d never trust the results.

• #3 by randomdreams on 2010-01-15 - 17:54

Gah, you did answer it and my poor reading skills conquered your explanation. Sigh. My brother built a similar WWVB receiver as part of an oldschool-meets-newschool grandfather clock we’re building, but since I can look out the window and see the WWV towers, and he’s only just down the road, radio noise hasn’t been one of his problems, daylight or not.

Would an active fet probe help with looking at the filter cap? or a battery osilloscope?

• #4 by Ed on 2010-01-15 - 21:04

Would an active fet probe help with looking at the filter cap? or a battery osilloscope?

It seems as though my ‘scope has 60 kHz sweep, near enough as makes no difference, and kills reception stone cold dead anywhere in the Basement Laboratory. Soooo, what I really need is a remote antenna, an actual receiver with visible innards, and that sounds a lot like a great Circuit Cellar column topic!

A stack of black acrylic and dark-gray polycarb just arrived, though, so building a case for the clock comes next. I must finish this thing off and send it to my friend before February gets too far along. Then I can think about what comes next…