hzeller/txtempus

Very wrong frequency generated (1.6kHz instead of 60kHz)

TheGuv opened this issue · 24 comments

Hello H,

Many thanks for this project. I was looking for exactly this for my Casio watch since I moved countries.
It built fine and appears to run but generates very low frequencies on my RPi model B+ v1.2. I'm not 100% sure what Pi you are using so it could well be you have a different model.

Using './txtempus -s MSF' insead of 60kHz, my 'scope shows only 1.611kHz and of course no devices work. Same with WWVB option. Trying the DCF77 produces 2.081kHz instead of 77.5kHz.

Would you have any ideas?

'guv.

Interesting, maybe Rasbperry Pi changed the frequency of various internal oscillators between versions ?

You find the base assumptions of the various osciallator frequencies here:
https://github.com/hzeller/txtempus/blob/master/gpio.cc#L117

This chooses from various oscillators that would be best, I think it uses the 216Mhz HDMI frequency for 77.5kHz. If it is only a few kHz, this sounds like the oscillator is on a frequency in the few Mhz range. Is HDMI enabled on the Pi ? If not, maybe they have a different oscillator in place ? You probably find that setting in /boot/config.txt.

I have only tested this with a Rapsberry Pi 3 and a Raspberry Pi Zero W so far.

Do you have a picture of the scope-trace on GPIO4 output ?

You can also try to comment out the { 7, 216.0e6 } by adding // in front and see if that fixes it.

Thanks!
HDMI is indeed enabled at whatever the default resolution is.
I can provide a picture if needed, but it's a sensible looking square-wave at 1.611 KHz.
I commented out { 7, 216.0e6 } to see and now it's generating 54KHz so your hunch about the HDMI clock may be close :)

I took a guess based on the rounded ratio of 1.611kHz and 60kHz and changed line 120 to { 7, 5.8e6 } and bingo 60kHz generated and my clocks set. rapidly.

Wonderful, glad you got your watch setting.

I leave this bug open for now as we need to find a definitive source somewhere that describes the clock-sources for the older Pi you have, so that things can be automatically switched in this code. For now, I added a reference to this bug in the README.

Sounds OK to me. If I come across the information needed I'll report the details here.
Thanks again!

Hi Henner and Steve
I had exactly the same issue as Steve using the Raspberry Zero W. Fixed as suggested. I live in Australia where we don't have a Radio Time Signal. So I now have a MSF clock running on perfect time. I will never have an excuse to be late for work. Thanks so much for your contribution Henner; and Steve for helping to come up with a solution. Gerard

I still try to understand in which case this happens. Did you have connected a monitor by any chance? I suspect that might influence the hdmi oscillator.

Yes, the monitor was connected at the time.

I am running a RaspberryPi 3B+ with the latest Raspbian (ver 4.19.66-v7+) and using the -s wwvb option (I'm in the U.S.A.) the square waves generated on the GPIO pins 4 and 17 are exactly 1.6114 KHz or 1611.4 Hz. Hmmmm....

I was using an external HDMI display so I removed the display and tried re-booting the unit headless and now
----> I DO GET THE 60.000 KHz signal out on the GPIO pins! <-----

So having the HDMI display hooked up must grab and set the divider frequency and prevent you from setting it to what you want.

Conclusion: Run the unit headless and VNC into it.

Hope this helps.

John N8OBJ

I have a Rasperry Pi 3 model B+.
I have an HDMI connector connected displaying 1920x1080, I think at 60 Hz, and also got 2.081KHz.
Next I commented out line 114, and recompiled. //{ 7, 216.0e6 }, // HDMI
https://github.com/hzeller/txtempus/blob/master/gpio.cc#L114

Now I see in the console :getting 77500.227 Hz, and on my scope I see 77.74Khz

Cheers,
Cedric

Greetings!
First of all, I want to say thank you very much for this guide and program. Everything works great, without any errors. Even despite my crooked hands.

But there is one problem ... This is even most likely a feature of the watch, or something else, I don’t know ...
The fact is that with manual synchronization, when I start TXTEMPUS, and press the button on the clock to synchronize, they begin to receive a signal, after a minute the arrow starts to show a high (H) signal level. And after a few minutes, the synchronization completes successfully.

But, if I run this program at night, when the clock synchronizes in automatic mode, they cannot receive the signal and are not synchronized. The arrow shows RX and does not indicate H (high), M (middle) or L (low) level of signal.

Watch: citizen eco drive e650
Raspberry Pi model 3B+

Maybe someone faced a similar problem. I would really appreciate answers.

you probably want to open a separate issue with this as it doesn't fit the frequency topic.

-s JJY60 on a Pi 2b+ (code 000d from cat /proc/cpuinfo | grep Revision) doesn't work for me.

I have tried variants of:

  • no hdmi plugged in, and the HDMI clock source on line 120 commented out (7, above)
  • no hdmi plugged in, the modified multiplier discussed above
  • no hdmi plugged in, as-is

Citizen eco drive U680, GMT+10 so set to expect JJY on 40khz or 60khz

I am waiting for a Pi W+ to arrive this week. I will then also test setting my watch to a different timezone and the other encodings (MSF or WWV) so I can narrow down if this is e.g. the JJY encoding.

The Android "make RF happen by blasting sound on the headphones" apps work fine. I can set my watch from this hack, so I know the basic 40/60khz signal reception in that form of modulation is working fine.

Could you measure the frequency somehow ? Some multimeters have a frequency measure if you don't have an oscilloscope.

Assuming that it might be correct, try some more windings with your coil (also check out the video of this youtuber who added some amplification to it https://www.youtube.com/watch?v=6SHGAEhnsYk )

I got a pi zero W. I get "L" for low on a citizen watch during sync and then "no" at end. Its using JPN config.

If I switch to WWVB and make my watch think its in CHI(cago) it says "H" during sync and works fine setting the clock.

I want to try MSF next, but this suggests the pi zero W headless is making 60khz fine, its the encoding for JJY which isn't quite right

I got a pi zero W. I get "L" for low on a citizen watch during sync and then "no" at end. Its using JPN config.

If I switch to WWVB and make my watch think its in CHI(cago) it says "H" during sync and works fine setting the clock.

I want to try MSF next, but this suggests the pi zero W headless is making 60khz fine, its the encoding for JJY which isn't quite right

I have tried some alternate positioning on the antenna I made from 2mm stiff copper wire, and I can now get sync on JJY at 40Khz. So, I think you can disregard me: the sensitivity issues are mixed, the encoding worked at the lower frequency, so I do not think I have clear debug on this and probably, this is a side issue and not good evidence of a systematic problem one way or another.

And thank you for writing this code and providing guidance on how to construct an Antenna on a Pi Zero W!

I too had 1.6KHz instead of 60KHz with -s WWVB

You can also try to comment out the { 7, 216.0e6 } by adding // in front and see if that fixes it.

I can confirm this code edit was needed on a Pi Zero W r1.1 with Raspberry Pi OS 32-bit (Lite) dated 2021-03-04 updated 2021-05-09. There was no HDMI screen connected.

Martin
P.S: Thanks! This is a great "Plan C" (Plan B is an emulator phone app), Plan A is WWVB :-)

Did anybody get it working successfully on Raspberry Pi 3 Model B?

Oscilloscope shows very low frequency (~36 Hz) when running with JJY60 profile. No monitor or any other peripheral connected except network cable.

After commenting line with 7, 216 frequency goes to 366 Hz.

Thanks!

I've had the same problem of low DCF77 frequency with a Raspberry Pi 3 Model B Rev 1.2. I missed at first the HDMI frequency explanation because JJY40 worked perfectly well and no monitor was attached.

I've got a hint by changing #if 0 to #if 1 in line 161 of rpi-control.cc, which enable a message telling which clock is used. Then it shown that 19.2 MHz oscillator was used for JJY40, and HDMI 216 MHz for other frequencies.

But why HDMI clock was wrong without monitor ? It was because I had hdmi_force_hotplug=1 in /boot/config.txt, which activate HDMI output even if no HDMI monitor is detected.

Putting this parameter to zero and rebooting the Pi finally solves the issue : I can now use HDMI clock in an headless Raspberry pi.

Alternatively, as said above, if you need to use an HDMI monitor, commenting line 117 { 7, 216.0e6 } forces the use of the slower regular oscillator.

If someone knows a way to auto-detect the 'hdmi-is-enabled-of-sorts' - situation in the rpi-control.cc, I'd be happy about a pull request.

The command tvservice -l give the type of display :

Without hdmi :

1 attached device(s), display ID's are : 
Display Number 3, type Composite

With hdmi :

1 attached device(s), display ID's are : 
Display Number 2, type HDMI 0

Interesting problem! I've spent the weekend investigating ways to detect if HDMI is enabled and connected (I have some test applications for Raspberry Pi OS v9 and v12 - as they differ).

However, thinking about this problem more, I realised that maybe it is just better to remove the HDMI source all together if the error in carrier frequency generated isn't large/much of a problem.

So I wrote a small test app to generate all the clock frequencies and errors (copying relevant code from rpi-control.c to compare:

$ cat clk_test.c 
#include <stdio.h>
#include <math.h>
#include <assert.h>
double StartClock(double requested_freq) {
  // Figure out best clock source to get closest to the requested
  // frequency with MASH=1. We check starting from the highest frequency to
  // find lowest jitter opportunity first.

  static const struct { int src; double frequency; } kClockSources[] = {
    { 5, 1000.0e6 },   // PLLC
    { 6,  500.0e6 },   // PLLD
    { 7,  216.0e6 },   // HDMI  <- this can be problematic if monitor connected
    { 1,   19.2e6 },   // regular oscillator
  };

  int divI = -1;
  int divF = -1;
  int best_clock_source = -1;
  double smallest_error_so_far = 1e9;
  for (size_t i = 0; i < sizeof(kClockSources)/sizeof(kClockSources[0]); ++i) {
    double division = kClockSources[i].frequency / requested_freq;
    if (division < 2 || division > 4095)
    {
        printf("    Skipping %d (%.1f): division = %.1f\n", i, kClockSources[i].frequency /1e6, division);
        continue;
    }
    int test_divi = (int) division;
    int test_divf = (division - test_divi) * 1024;
    double freq = kClockSources[i].frequency / (test_divi + test_divf/1024.0);
    double error = fabsl(requested_freq - freq);
    printf("Error: %f\n", error);
    //if (error >= smallest_error_so_far) continue;
    smallest_error_so_far = error;
    best_clock_source = i;
    divI = test_divi;
    divF = test_divf;

      // There have been reports of different clock source frequencies. This
      // helps figuring out which source was picked.
      fprintf(stderr, "Choose clock %d at %.1fMHz / %.3f = %.3f\n",
              kClockSources[best_clock_source].src,
              kClockSources[best_clock_source].frequency / 1e6,
              divI + divF/1024.0,
              kClockSources[best_clock_source].frequency / (divI + divF/1024.0));
  }

  if (divI < 0)
    return -1.0;  // Couldn't find any suitable clock.

  assert(divI >= 2 && divI < 4096 && divF >= 0 && divF < 4096);


  return kClockSources[best_clock_source].frequency / (divI + divF/1024.0);
}

int main(void)
{
    printf("Starting\n");

    printf("--- 40000 ---\n");
    StartClock(40000);
    printf("--- 60000 ---\n");
    StartClock(60000);
    printf("--- 77500 ---\n");
    StartClock(77500);
    return 0;
}

Results are:

$ ./clk_test 
Starting
--- 40000 ---
    Skipping 0 (1000.0): division = 25000.0
    Skipping 1 (500.0): division = 12500.0
    Skipping 2 (216.0): division = 5400.0
Error: 0.000000
Choose clock 1 at 19.2MHz / 480.000 = 40000.000
--- 60000 ---
    Skipping 0 (1000.0): division = 16666.7
    Skipping 1 (500.0): division = 8333.3
Error: 0.000000
Choose clock 7 at 216.0MHz / 3600.000 = 60000.000
Error: 0.000000
Choose clock 1 at 19.2MHz / 320.000 = 60000.000
--- 77500 ---
    Skipping 0 (1000.0): division = 12903.2
    Skipping 1 (500.0): division = 6451.6
Error: 0.002628
Choose clock 7 at 216.0MHz / 2787.097 = 77500.003
Error: 0.226657
Choose clock 1 at 19.2MHz / 247.741 = 77500.227

Based on this, only clock source 1 is needed, and in-fact the HDMI source is only useful for carrier frequencies above 216MHz/4096 = ~52.7Khz.

So I propose to remove all the clock choosing code and just use source 1. However, if you disagree, I'd happily add my HDMI detection code in a pull request, although I fear it will add to the code complexity & testing/validation will also be complex.