hardbyte / python-can

The can package provides controller area network support for Python developers
https://python-can.readthedocs.io
GNU Lesser General Public License v3.0
1.31k stars 604 forks source link

USB2CAN gs_usb timestamp in received CAN messages incorrect #1491

Open GarethJoshua opened 1 year ago

GarethJoshua commented 1 year ago

Describe the bug

I have an application on Windows 10 that sends and receives CAN bus messages and I am using a USB2CAN module by Inno-maker having installed python-can[gs-usb]. The application runs successfully using can.Notifier except that the timestamps in the received CAN bus messages are not correct. The timestamps, which are floating point values, are low (e.g. 331, 42) rather than being the number of seconds since the epoch. Does anyone have any advice as to how to correct this? I can stamp the messages in my application when they are forwarded to my listener instances by my can.Notifier instance but this is not ideal. The received CAN bus message timestamps were correct when the application ran on a Raspberry PI using CAN bus type ‘socketcan_ctypes'. Are the timestamps normally set in hardware? I see that the implementation of the GsUsb start function https://github.com/jxltom/gs_usb/blob/master/gs_usb/gs_usb.py has the GS_CAN_MODE_HW_TIMESTAMP flag.

To Reproduce

Use Inno-maker USB2CAN module and python-can[gs-usb]. Timestamp in received CAN bus messages incorrect (date same as epoch)

Expected behavior

Timestamp should be seconds as float since epoch

Additional context

OS and version:Windows 10 Python version:Latest python-can version:4.1.0 python-can interface/s (if applicable):

Traceback and logs ```python def func(): return "hello, world!" ```
GarethJoshua commented 1 year ago

Python version:3.11.1 python-can version:4.1.0 python-can interface/s (if applicable):gs-usb 0.3.0, pyusb 1.2.1

davidva-ecco commented 1 year ago

Do you have a code example you could share on how you're setting up the usb2can interface in Python on Windows?

Could the timestamps be delta between frames instead of delta since epoch?

Do you see the same incorrect timestamps when using can.viewer provided with python-can? https://python-can.readthedocs.io/en/stable/scripts.html

I think the usage for usb2can would be python -m can.viewer -i usb2can -b <bitrate> -c <channel>

What about with the innomaker Windows utility? https://github.com/INNO-MAKER/usb2can/tree/master/For%20Windows/InnoMakerUsb2Can-v.1.2.3

Clarification question: what do you mean by python-can[gs_usb]?

GarethJoshua commented 1 year ago

Re questions : Do you have a code example you could share on how you're setting up the usb2can interface in Python on Windows?

Creating can.Bus :-

can0 = can.Bus(channel = 'can0', bustype = 'gs_usb', index = 0, bitrate = 500000)

can.Notifier usage:- filename = datetime.now().strftime("%Y%m%d-%H%M%S") self.output_to_file = can.Printer('main_logging_file' + filename + '.txt') self.notifier = can.Notifier(can0,[self.cmbr, self.output_to_file])

Could the timestamps be delta between frames instead of delta since epoch?

The timestamps in the received messages should be delta since epoch.

From Message - python-can 4.1.0 documentation timestamp TYPE float ‘The timestamp field in a CAN message is a floating point number representing when the message was received since the epoch in seconds. Where possible this will be timestamped in hardware.’

Do you see the same incorrect timestamps when using can.viewer provided with python-can? https://python-can.readthedocs.io/en/stable/scripts.html I think the usage for usb2can would be python -m can.viewer -i usb2can -b -c

Re can.viewer:- From Scripts - python-can 4.1.0 documentation ‘The first column is the number of times a frame with the particular ID that has been received, next is the timestamp of the frame relative to the first received message.’ The relative times for received messages look OK but it is the absolute times that are wrong so can.viewer does not help.

What about with the innomaker Windows utility? https://github.com/INNO-MAKER/usb2can/tree/master/For%20Windows/InnoMakerUsb2Can-v.1.2.3

I will look into using this tool and also Busmaster as described in the INNO-MAKER USB2CAN User Manual v.1.8

Clarification question: what do you mean by python-can[gs_usb]?

From Geschwister Schneider and candleLight - python-can 4.1.0 documentation Install: pip install "python-can[gs_usb]"

I installed gs-usb as well via pip install gs-usb.

GarethJoshua commented 1 year ago

I note that in the implementation of gs_usb_frame.py gs_usb/gs_usb_frame.py at master · jxltom/gs_usb · GitHub the timestamp is calculated as :-

def timestamp(self):

    return self.timestamp_us / 1000000.0

So presumably the value in self.timestamp_us is the number of microseconds since the epoch.

‘The timestamp field in a CAN message is a floating point number representing when the message was received since the epoch in seconds. Where possible this will be timestamped in hardware. ‘

However, the self.timestamp_us field is unpacked in gs_usb_frame.py assuming a hardware timestamp in :-

def unpack_into(frame, data: bytes, hw_timestamp): if (hw_timestamp == True): ( frame.echo_id, frame.can_id, frame.can_dlc, frame.channel, frame.flags, frame.reserved, frame.data, frame.timestamp_us, ) = unpack("<2I12BI", data) else: ( frame.echo_id, frame.can_id, frame.can_dlc, frame.channel, frame.flags, frame.reserved, frame.data, ) = unpack("<2I12B", data)

The string "<2I12BI" specifies field frame.timestamp_us as being an unsigned integer value (standard size 4 bytes). Could this be where the truncation is occurring? The number of microseconds since the epoch cannot be held in 4 bytes. e.g. 1674604805000000 is greater than 4,294,967,295

SolarSupremacy commented 1 year ago

The timestamp value (uint32) is from the device itself. I don't think the device has any way of knowing epoch time.

I think the timestamp it gives (which is in microseconds) is abstract and meant to be used as delta time between frames (because that can be important). For actual epoch timestamps, doing it after capturing the frame will still be close enough to be relevant, while the hardware timestamp can be used for accurate deltas of when the frame was captured.

GarethJoshua commented 1 year ago

The Timestamp property is implemented as follows in gs_usb_frame.py:-

@property def timestamp(self): return self.timestamp_us / 1000000.0

As mentioned above :- ‘The timestamp field in a CAN message is a floating point number representing when the message was received since the epoch in seconds. Where possible this will be timestamped in hardware. ‘

If this timestamp was a delta in seconds then the number of seconds since the last CAN message would be zero seconds plus a fractional number rather than non-zero values (e.g. 331, 42) plus a fractional number as a floating poimt.

If a CAN message is converted to text suitable for logging using this timestamp property the value should be the number of seconds as a floating point since the epoch. Because of this bug which affects the logging of CAN messages I decided not to use the ‘gs_usb’ implementation and adopt a Debian based ‘socketcan’ implementation instead which supports correct timestamps for logging.