neuromorphicsystems/neuromorphic-rs

64-bit Timetag

Closed this issue · 4 comments

Since adapters::evt3::Adapter::convert() takes in a closure of type FnMut(neuromorphic_types::DvsEvent<u64, u16, u16>), I assumed that meant that each DvsEvent would come with a 64-bit timetag. However, after setting up a simple program that just prints out events from a EVK4 camera and running that for an hour, I noticed the timetag consistently overflows at around t=8388608 (2^23). Looking through fn convert() in evt3.rs I found the following code:

// line 159
0b0110 => {
    let lsb_t = word & 0b111111111111;
    if lsb_t != self.previous_lsb_t {
        self.previous_lsb_t = lsb_t;
        let t = (((self.previous_lsb_t as u32)
            | ((self.previous_msb_t as u32) << 12))
            as u64)
            | ((self.overflows as u64) << 24);
        if t >= self.t {
            self.t = t;
        }
    }
}

which seems to imply that these cameras only support a 24-bit timetag, which is weird since they were providing me with a 32-bit timetag when I was using Metavision's libraries.

Is there a mechanism to provide a full 64-bit timetag from fn convert()? If not, then is there a built-in way to handle overflow? Or do we have to detect overflow ourselves in our user code?

Hi @Kamicosi,
EVT3 timestamps are encoded with 2 x 12 bits (the format uses 16 bits words, one of the words updates the 12 least significant bits of the timestamp whilst another updates the 12 most significant). This library and Prophesee's Metavision keep track of overflows to reconstruct timestamps with more bits (32 or 64).

The timestamps should not reset after 2^23, it looks like you've found a bug! Could you send me a copy of the test program that you are using?

Here is a minimally reproducible example:

use neuromorphic_drivers::devices;
use neuromorphic_drivers::event_loop_and_flag;
use neuromorphic_drivers::prophesee_evk4::{Biases, Clock, RateLimiter};
use neuromorphic_drivers::UsbDevice;
use neuromorphic_types::DvsPolarity;
use std::sync::mpsc;

fn main() {
    // Ctrl-c handler
    let (send_looping, recv_looping) = mpsc::channel();
    ctrlc::set_handler(move || send_looping.send(()).unwrap()).unwrap();

    let (flag, event_loop) = event_loop_and_flag().unwrap();
    let device = devices::prophesee_evk4::open(
        &Some("00050230"),
        devices::prophesee_evk4::Configuration {
            biases: Biases {
                pr: 0,
                fo: 0,
                hpf: 0,
                diff_on: 0,
                diff_off: 25,
                diff: 0,
                inv: 0,
                refr: 200,
                reqpuy: 0,
                reqpux: 0,
                sendreqpdy: 0,
                unknown_1: 0,
                unknown_2: 0,
            },
            x_mask: [0; 20],
            y_mask: [0; 12],
            pixel_mask: [0; 21],
            mask_intersection_only: false,
            enable_external_trigger: false,
            clock: Clock::Internal,
            rate_limiter: Some(RateLimiter {
                reference_period_us: 200,
                maximum_events_per_period: 1000,
            }),
            enable_output: true,
        },
        &devices::prophesee_evk4::DEFAULT_USB_CONFIGURATION,
        event_loop,
        flag,
    )
    .unwrap();

    // Print every 1,000th event
    let mut counter = 0;
    const MAX_COUNT: usize = 1000;
    loop {
        if recv_looping.try_recv().is_ok() {
            break;
        } else {
            match device.next_with_timeout(&std::time::Duration::new(1, 0)) {
                Some(event) => {
                    device.adapter().convert(
                        event.slice,
                        |dvs_event| {
                            // normal t, x, y, p events
                            if counter == MAX_COUNT {
                                let t = dvs_event.t;
                                let x = dvs_event.x;
                                let y = dvs_event.y;
                                println!(
                                    "{{{},{},{},{}}}",
                                    t,
                                    x,
                                    y,
                                    match dvs_event.polarity {
                                        DvsPolarity::Off => 0,
                                        DvsPolarity::On => 1,
                                    }
                                );
                                counter = 0;
                            } else {
                                counter = counter + 1;
                            }
                        },
                        |trigger_event| {
                            // trigger events
                            println!("{:?}", trigger_event);
                        },
                    );
                }
                None => {
                    panic!("No event");
                }
            }
        }
    }
}

With these crates in Cargo.toml:

ctrlc = "3.4"
neuromorphic-drivers = "0.13"
neuromorphic-types = "0.4"

Output:

--- snip ---
{8390211,1032,467,0}
{8390489,10,459,0}
{8390772,779,90,0}
{8391047,278,638,0}
{8391337,270,31,0}
{8391611,1046,574,0}
{8391889,19,304,0}
{3558,285,193,0}
{3839,281,374,0}
{4093,1034,140,0}
{4093,533,664,0}
{4093,522,222,0}
{4093,19,551,0}
{4093,781,401,0}
{4093,285,148,0}
{4093,269,356,0}
{4093,1041,59,0}
{4093,524,706,0}
{4093,1046,271,0}
{4093,25,473,0}
{4093,769,442,0}
{3396,1045,91,0}
{3673,31,633,0}
{3962,543,27,0}
{4095,797,568,0}
{4095,777,302,0}
{4095,770,516,0}
{4095,270,386,0}
{4095,1045,119,0}
{4095,540,644,0}
{4095,28,227,0}
--- snip ---

After overflow, the timetags get stuck <=4095 for a while before increasing again until the next overflow and repeating the cycle.

Hi @Kamicosi,

The function device.adapter() creates a new event decoder on each call, with a fresh state machine. I think that it causes (or at least contributes to) the issue since the overflow count gets erased on each loop iteration. Can you try to create the adapter before the loop and let me know if that fixes the issue? (I do not have an EVK4 on hand at the moment)

let mut adapter = device.adapter();
loop {
    ...
    if let Some(buffer_view) = device.next_with_timeout(&std::time::Duration::new(1, 0)) {
        adapter.convert(buffer_view.slice, |dvs_event| {
            ...
        });
    }
}

Aha! That was the issue. I made the change and re-ran for 1.5 hours and the timetag didn't overflow at 2^32. Thanks for your help.