Skip to content

Commit

Permalink
Implement GetPPSTimestamp
Browse files Browse the repository at this point in the history
Summary:
WHAT?

Implement function to get time from activated PPS source

WHY?

Remove dependency on linuxptp by rewriting ts2phc.c (T196935923)

Considerations:

  - Why so many tests for seemingly trivial things?
    - Type conversions, state logic and magic math is very error prone

Differential Revision: D63091044
  • Loading branch information
crmdias authored and facebook-github-bot committed Sep 23, 2024
1 parent 4d61e17 commit 63298a7
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 75 deletions.
8 changes: 8 additions & 0 deletions phc/.mockery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mockname: "Mock{{.InterfaceName}}"
filename: "{{.InterfaceName | lower}}_mock.go"
inpackage: True
dir: "."
packages:
github.com/facebook/time/phc:
interfaces:
DeviceController:
108 changes: 108 additions & 0 deletions phc/devicecontroller_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 66 additions & 4 deletions phc/phc.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,23 @@ func Time(iface string, method TimeMethod) (time.Time, error) {
}
}

// PPSSource represents a PPS source
type PPSSource struct {
PHCDevice DeviceController
state PPSSourceState
peroutPhase int
}

// PPSSourceState represents the state of a PPS source
type PPSSourceState int

const (
// UnknownStatus is the initial state of a PPS source, which means PPS may or may not be configured
UnknownStatus PPSSourceState = iota
// PPSSet means the underlying device is activated as a PPS source
PPSSet
)

// Device represents a PHC device
type Device os.File

Expand Down Expand Up @@ -306,7 +323,7 @@ func (dev *Device) AdjFreq(freqPPB float64) error { return clockAdjFreq(dev, fre
func (dev *Device) Step(step time.Duration) error { return clockStep(dev, step) }

// ActivatePPSSource configures the PHC device to be a PPS timestamp source
func ActivatePPSSource(dev DeviceController) error {
func ActivatePPSSource(dev DeviceController) (*PPSSource, error) {
// Initialize the PTPPeroutRequest struct
peroutRequest := PTPPeroutRequest{}

Expand All @@ -319,7 +336,7 @@ func ActivatePPSSource(dev DeviceController) error {

ts, err := dev.Time()
if err != nil {
return fmt.Errorf("failed (clock_gettime) on %s", dev.File().Name())
return nil, fmt.Errorf("failed (clock_gettime) on %s", dev.File().Name())
}

// Set the index and period
Expand All @@ -343,8 +360,53 @@ func ActivatePPSSource(dev DeviceController) error {
log.Debugf("retrying PTP_PEROUT_REQUEST2 with DUTY_CYCLE flag unset for backwards compatibility")
peroutRequest.Flags &^= ptpPeroutDutyCycle
err = dev.setPTPPerout(peroutRequest)
return err

if err != nil {
return nil, err
}
}

return nil
return &PPSSource{PHCDevice: dev, state: PPSSet}, nil
}

// Timestamp returns the timestamp of the last PPS output edge from the given PPS source
// A Pointer is returned to avoid additional memory allocation
func (ppsSource *PPSSource) Timestamp() (*time.Time, error) {
if ppsSource.state != PPSSet {
return nil, fmt.Errorf("PPS source not set")
}

currTime, err := ppsSource.PHCDevice.Time()

if err != nil {
return nil, fmt.Errorf("error getting time (clock_gettime) on %s", ppsSource.PHCDevice.File().Name())
}

// subtract device perout phase from current time to get the time of the last perout output edge
// TODO: optimize section below using binary operations instead of type conversions
currTime = currTime.Add(-time.Duration(ppsSource.peroutPhase))
sourceTs := timeToTimespec(currTime)

/*
* As long as the kernel doesn't support a proper API for reporting
* back a precise perout timestamp, we have to assume that the current
* time on the PPS source is still within +/- half a second of the last
* perout output edge, and hence, we can deduce the current second
* (nanossecond is omitted) of this edge at the emitter based on the
* emitter's current time. We support only PHC sources, so we can ignore
* the NMEA source edge case described in ts2phc.c
*/
if sourceTs.Nsec > int64(nsPerSec)/2 {
sourceTs.Sec++
sourceTs.Nsec = 0
}

currTime = time.Unix(sourceTs.Sec, sourceTs.Nsec)
currTime = currTime.Add(time.Duration(ppsSource.peroutPhase))

return &currTime, nil
}

func timeToTimespec(time time.Time) (ts unix.Timespec) {
return unix.Timespec{Sec: time.Unix(), Nsec: int64(time.Nanosecond())}
}
Loading

0 comments on commit 63298a7

Please sign in to comment.