Set RTC from GPS

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
GJS
Posts: 5
Joined: Sat Apr 28, 2018 6:55 pm

Set RTC from GPS

Post by GJS » Sat Apr 28, 2018 7:16 pm

I am trying to accurately set the RTC of the PyBoard with a GPS.

My theory is to read the GPRMC sentence from the GPS until a valid fix is obtained then extract the time and date. Then wait for a pin which the PPS output of the GPS receiver is connected to to go high and then set the RTC to the time and date value plus one second. This seems to work fine.

I then want to adjust the calibration factor of the RTC according to the PPS out put of the GPS to keep the RTC accurate without any large slewing. So I set up an interupt on the PPS pin. In the ISR I want to read the RTC's current subseconds and then set the RTC calibration factor accordingly. This generates an exception because, apparently, reading the current value of the RTC causes a heap allocation. Here is some simplified minimum code to demonstrate the problem:

[code]
from pyb import ExtInt
from machine import Pin
import micropython

rtc = pyb.RTC()
micropython.alloc_emergency_exception_buf(100)
pin = Pin("X1", Pin.IN, Pin.PULL_DOWN)

def callback(line):
print(rtc.datetime())

#Set up ISR to handle PPS
extint = pyb.ExtInt(pin, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_DOWN, callback)

while True:
pyb.delay(100)


Uncaught exception in ExtInt interrupt handler line 0
Traceback (most recent call last):
File "main.py", line 15, in callback
MemoryError: memory allocation failed, heap is locked
[/code]

How can I work around this so that I can read the RTC value and set its calibration factor in response to the PPS from the GPS?

Any advice appreciated.

cefn
Posts: 230
Joined: Tue Aug 09, 2016 10:58 am

Re: Set RTC from GPS

Post by cefn » Tue May 01, 2018 12:14 pm

First of all, I've no experience of interrupt callbacks on this platform, so these are just things to have a go at as they cross my mind.

Try not creating a Datetime 8-tuple and printing it (which presumably triggers a heap allocation). The only thing you need in the interrupt is a momentary snapshot of the time.

If I were in your shoes, within the def callback I would try storing a value from ticks_us() or better, ticks_cpu() into a preallocated name
https://docs.micropython.org/en/latest/ ... utime.html

...which I'd then resolve to a datetime using ticks_diff() in my main loop in normal single-threaded operation (taking place outside the interrupt where I can freely make heap allocations).

No idea if this would actually work, but that would be the direction I would explore as a workaround.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Set RTC from GPS

Post by pythoncoder » Fri May 04, 2018 7:50 am

Another option is to use micropython.schedule - see the docs.
Peter Hinch
Index to my micropython libraries.

GJS
Posts: 5
Joined: Sat Apr 28, 2018 6:55 pm

Re: Set RTC from GPS

Post by GJS » Sat May 05, 2018 10:27 am

Thanks for your suggestion, Peter. I have tried this and it does work. However, I am concerned about the time it might take between execution of the ISR and execution of the scheduled function. How can I time this?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Set RTC from GPS

Post by pythoncoder » Tue May 08, 2018 9:15 am

Have a global variable set in the ISR to machine.ticks_us(). In your scheduled handler use machine.ticks_diff() to measure the elapsed time. You should see values on the order of 0-4ms depending on whether a garbage collection was running when the interrupt occurred.
Peter Hinch
Index to my micropython libraries.

GJS
Posts: 5
Joined: Sat Apr 28, 2018 6:55 pm

Re: Set RTC from GPS

Post by GJS » Tue May 08, 2018 9:27 am

Thanks, Peter, I will give this a try. Setting the RTC is not the main program function so I will get the rest of the code working as I expect this may well have an effect on the amount of GC going on and thus the average delay between the PPS pulse and the scheduled function. If larger delays are only occasional this should not be a problem as the clock speed (drift compensation) will only be changed briefly. Or, perhaps I could use the machine.ticks_diff() measurement to compensate.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Set RTC from GPS

Post by pythoncoder » Tue May 08, 2018 4:43 pm

GJS wrote:
Tue May 08, 2018 9:27 am
...Or, perhaps I could use the machine.ticks_diff() measurement to compensate.
Indeed, if you're expecting that level of accuracy. But the GPS sentences only offer one second precision. The GPS receiver and UART data transmission will have some latency so I'd be interested to hear how you intend to achieve millisecond level accuracy. Or are you only trying to remove variable sources of latency in the hope that other sources are constant?
Peter Hinch
Index to my micropython libraries.

GJS
Posts: 5
Joined: Sat Apr 28, 2018 6:55 pm

Re: Set RTC from GPS

Post by GJS » Tue May 08, 2018 5:34 pm

I am going to use the Pusle Per Second (PPS) output of the GPS receiver to sync the RTC, not the NMEA sentences received by the UART. The leading edge of the PPS signal is aligned with the whole second rollover to within 10ns accuracy according to the manufacturer's data sheet. My plan is:

1. Read the NMEA sentences as they arrive using uasyncio and store the current time.
2. When the first PPS arrives, set the RTC to the stored time + 1 second.
3. When the next and subsequent PPSs arrive, calculate the difference between (stored time + 1 second) and the RTC time (apply machine.ticks_diff() here).
4. Calculate "cal" to apply to RTC.calibration(cal) to gradually make the RTC running speed correct.
5. Rinse and repeat so that the cal factor will eventually be zero and the RTC is in-line with the GPS time.

I believe this is similar to how the Network Time Protocol (NTP) works, but without all the network latency stuff.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Set RTC from GPS

Post by pythoncoder » Wed May 09, 2018 8:29 am

After reading your previous post I checked the chip datasheet and realised that PPS was the way to go. You may need to investigate the relative timing of PPS and the arrival of NMEA sentences. I don't know if PPS can arrive in the middle of a sentence but it may need a little study.

I wrote some code here - look for DS3231 for calibrating the Pyboard RTC from an external time source, in this case a DS3231 precision RTC. I found that you can achieve excellent precision by measuring the drift over five minutes and calculating the calibration value from that. There was no practical gain in waiting longer owing to the granularity of the calibration constant. You might find this approach easier than an iterative method.
Peter Hinch
Index to my micropython libraries.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Set RTC from GPS

Post by pythoncoder » Mon May 14, 2018 6:21 am

I looked at this on a scope and the leading edge of the PPS signal always arrives before the first NMEA sentence. These arrive in a burst which completes here in a maximum of 400ms, although this could be longer depending on the number of satellites in view. With a repetition rate of 1Hz the serial data has ended before the arrival of the next PPS. So your approach can be expected to work.
Peter Hinch
Index to my micropython libraries.

Post Reply