CHU under OpenBSD (version 2)

This is the second version of this page. The original is here but some of the links won't work anymore.

This is about a program by William Rossi called just chu and getting it to work under OpenBSD. I ran across the program sometime around 2010 because it's in FreeBSD's ports collection and finally adapted it to run under OpenBSD (and maybe NetBSD).

The program will set your computer's clock if you tune a shortwave radio to radio station chu run by the National Research Council of Canada on 3.330, 7.850 or 14.670 MHz and connect the radio to a sound card. I was intrigued by it and got it working under OpenBSD, which meant mostly changing the way it handles sound input.

OpenBSD also has a function called adjfreq() (as opposed to adjtime()), which adjusts the rate the clock runs at so I changed it to use that and spent a bunch of time recording how it performed. It works well if you've got a good signal from chu and you leave the program running all the time. My signal from chu becomes unusable on any frequency for several hours a day and I don't plan to run the program all the time either.

Because adjfreq() has almost a steering effect on the clock, it performs badly if you run it until you lose signal. At times of weak signal both the adjtime() and adjfreq() versions get a fair bit of noise in the time measurements. With adjtime() this just causes the clock to be off a little until you get a good signal again and it resets. With adjfreq() this can leave the clock gaining or losing time wildly until the signal comes back.

I don't have any fancy clock to compare chu's performance against, the only thing I could use as a time reference was what chu was giving me as a time offset. This itself becomes inaccurate during times of noisy signal

Here's the performance in adjtime() mode over most of 24 hours. The system times and measured offsets were logged into a tab-delimited file generated by the program itself, and the program was run by a cron job every 15 minutes. The vertical scale is in microseconds, so this has a maximum span of about 16 milliseconds. At times of noisy signals on the radio the spikes were at their worst, most of the time the changes were under 2 milliseconds.

Much of the time the 3.330 MHz audio looks like this, and at those times it becomes almost totally unusable. It can still get offset values, but you'd be better off not using them: (this one's a thumbnail to a bigger version)

The adjfreq() mode should let you stay very close to the right time, in a perfect world. If you know the time offset and how long since you last adjusted it, you can calculate the frequency correction.

In adjfreq() mode this kind of thing keeps happening. The big spike was caused by a noisy signal right before the signal was lost for a few hours, so the clock drifted all the time there was no signal, then it came back, overshot, bounced around a little then settled down.

Somewhere William Rossi makes reference to attenuating the correction signal by a factor of 8 to see if that cuts down overshoot and oscillation. I cut it down by a factor of 2 to see if that helped, but it didn't. See for yourself:

One problem with adjfreq() mode is that any time the signal goes away, the previous frequency correction value was probably already wrong (due to noise), so while the signal's gone the clock drifts. If you could stabilize the frequency before the signal loss you could cut down this drift amount.

I also experimented with a version that did averaging of frequency correction values by writing the previous 5 or 10 values out to a file, averaging in the new one, then applying the averaged correction, but it still does about the same thing in slower motion. If you did this but had your cron job only set up to run during times of good signal that might work, but those times keep changing as propagation changes.

So my final patch is here. This version runs adjtime() by default, but if you specify -f on the command line it will use adjfreq() instead.

I changed the format of the tab-delimited file this makes since the previous version. The fields now are (1) system clock date and time in yyyy-mm-dd\thh:mm:ss format, (2) seconds since last correction, (3) Time offset in microseconds, and (4) the frequency correction value as it goes to adjfreq(). These gnuplot files should work with the changed format.

If you're curious about adjfreq(), one question I never really found an answer to is just what's the scope of it? Does it only change the frequency of the system clock while the machine's up and running? Or can it change the frequency of the hardware clock on the motherboard, the one that's run by a battery? My guess would be that maybe newer motherboards could be affected, which drastically changes the timeframe over which the thing's acting (unless it runs 24/7) and that has to be taken into account. The calculations would be simpler actually if it does affect the motherboard clock.

It seemed like my clock drifted faster after I had experimented with adjfreq() on it, so I wrote a simple little program called freqreset that runs adjfreq() with a correction of 0 ns/second to hopefully reset it. You shouldn't need that at all unless you tried adjfreq().

It seems fairly normal to have this run for the first minute or two without seeing much screen output. My guess is that stdout is buffered so nothing appears until the buffer fills. When the reception's good output appears in spurts of 10 or so offsets at once. Those can't all be real-time.

If it isn't making any time adjustment, watch the values it's reading for time offsets. If those differ much, it probably isn't getting enough good values to adjust the time to. If you're seeing "Close call" messages more than once or twice per run the signal's too noisy.

I'm still not doing anything with setting sound levels. Too much or too little audio is just about the same as too much noise. Probably the best bet is to use another application that provides some feedback on levels, like Fldigi, Gpsk31, or Gmfsk and save your mixer level settings in an /etc/mixerctl.conf file. There seems to be a considerable margin for error, but not an infinite one.

I was doing some fiddling with a modified version of Audread that I was trying to get to show PEP audio power and I recorded a CHU signal on 3.330 Mhz that looked like this: (scroll the frame horizontally)

This is a 1 minute recording and there were some thunderstorms around, but scroll over toward the 10 second mark for some CHU pulses. There's quite a variation in amplitude between pulses, and the static crashes dwarf the CHU pulses. There are some double pulses around 20 seconds.

The algorithm I used to calculate PEP audio power is below. I'm not sure how valid it is, I thought it up based on a picture in QST.

void rectify(void) { // more like PEP(?)
  int i;
  short int prail, nrail, last;
  printf("Calculating PEP\n");
  for (i=0; i<DATASIZE; i++) {
    if (buf[i] > 0) {
      prail = buf[i];  // positive rail
    } else {
      if (buf[i] < 0) {
        nrail = buf[i];  // negative rail
      } // if < 0
    }  // not > 0
    buf[i] = prail - nrail;  // keep envelope value
    if (buf[i] > 0)
      last = buf[i];  // keep a good positive
    if (buf[i] < 0) // no negative spikes
      buf[i] = last;  // use last value instead
  }  // for loop
}

old version of this page

AB1JX / toys