Quick Start Guide
This guide will help you get started with XClock quickly. We’ll cover basic usage patterns for both the Python library and command-line interface.
Prerequisites
Make sure you have:
Installed XClock (see Installation)
Installed your DAQ device drivers (e.g., LabJack LJM software)
Connected your DAQ device via USB
Your First Clock
Let’s create a simple clock that runs for 5 seconds at 100 Hz.
Using Python
from xclock.devices import LabJackT4
# Initialize device
t4 = LabJackT4()
# Get available output channels
channels = t4.get_available_output_clock_channels()
print(f"Available channels: {channels}")
# Add a 100 Hz clock for 5 seconds
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
duration_s=5.0,
)
# Start the clock and wait for completion
t4.start_clocks(wait_for_pulsed_clocks_to_finish=True)
print("Clock finished!")
t4.close()
Using CLI
xclock --clock-tick-rates 100 --duration 5 start
Multiple Synchronized Clocks
One of XClock’s key features is running multiple clocks simultaneously, all synchronized to the same base clock.
Python Example
from xclock.devices import LabJackT4
t4 = LabJackT4()
channels = t4.get_available_output_clock_channels()
# Add multiple clocks with different frequencies
t4.add_clock_channel(
clock_tick_rate_hz=60, # Camera 1 at 60 Hz
channel_name=channels[0],
duration_s=10.0,
)
t4.add_clock_channel(
clock_tick_rate_hz=100, # Camera 2 at 100 Hz
channel_name=channels[1],
duration_s=10.0,
)
# Start all clocks simultaneously
t4.start_clocks(wait_for_pulsed_clocks_to_finish=True)
t4.close()
CLI Example
xclock --clock-tick-rates 60,100 --duration 10 start
Recording Timestamps
XClock can record precise timestamps of all clock edges, which is useful for synchronizing data post-acquisition.
Python Example
from xclock.devices import LabJackT4
import pathlib
import numpy as np
t4 = LabJackT4()
channels = t4.get_available_output_clock_channels()
# Add clocks
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
duration_s=10.0,
)
# Start and record timestamps
output_file = pathlib.Path.home() / "Documents" / "XClock" / "timestamps.csv"
t4.start_clocks_and_record_edge_timestamps(
wait_for_pulsed_clocks_to_finish=True,
filename=output_file
)
print(f"Timestamps saved to: {output_file}")
# Load and inspect the data
data = np.loadtxt(output_file, dtype=np.int64, delimiter=",")
print(f"Recorded {len(data)} edges")
print(f"First few timestamps:\n{data[:5]}")
t4.close()
CLI Example
xclock --clock-tick-rates 100 --duration 10 --record-timestamps start
Timestamps are saved to ~/Documents/XClock/xclock_timestamps_<timestamp>.csv
Understanding the Timestamp Format
The CSV file contains three columns:
Timestamp (nanoseconds): Time since device start (relative timing)
Edge type: Indicates which clock and edge direction
Positive number (e.g.,
1,2): Rising edge on clock 1, 2, etc.Negative number (e.g.,
-1,-2): Falling edge on clock 1, 2, etc.
Unix timestamp (nanoseconds): Host system time (absolute timing)
Example:
1000000,1,1638360000000000000 # Rising edge on clock 1 at 1ms (device time)
1500000,-1,1638360000500000000 # Falling edge on clock 1 at 1.5ms
2000000,1,1638360001000000000 # Rising edge on clock 1 at 2ms
2000000,2,1638360001000000000 # Rising edge on clock 2 at 2ms (same device time)
The Unix timestamp (column 3) allows you to correlate events with other systems or absolute wall-clock time.
Continuous vs. Pulsed Clocks
XClock supports two modes:
Continuous Clocks
Runs indefinitely until manually stopped.
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
number_of_pulses=None, # None = continuous
)
t4.start_clocks(wait_for_pulsed_clocks_to_finish=False)
# Do your experiment...
import time
time.sleep(30)
# Stop when done
t4.stop_clocks()
Pulsed Clocks
Generates a specific number of pulses then stops automatically.
# Specify exact pulse count
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
number_of_pulses=1000, # Exactly 1000 pulses
)
# Or use duration (auto-calculates pulses)
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
duration_s=10.0, # 100 Hz * 10s = 1000 pulses
)
Trigger-Based Start
Start clocks when an external trigger signal is received.
Python Example
from xclock.devices import LabJackT4, EdgeType
t4 = LabJackT4()
channels = t4.get_available_output_clock_channels()
trigger_channels = t4.get_available_input_start_trigger_channels()
# Add clocks (but don't start yet)
t4.add_clock_channel(
clock_tick_rate_hz=100,
channel_name=channels[0],
duration_s=10.0,
)
print(f"Waiting for trigger on {trigger_channels[0]}...")
print("Send a rising edge to start the clock.")
# Wait for trigger
triggered = t4.wait_for_trigger_edge(
channel_name=trigger_channels[0],
timeout_s=30.0,
edge_type=EdgeType.RISING,
)
if triggered:
print("Trigger received! Starting clocks...")
t4.start_clocks(wait_for_pulsed_clocks_to_finish=True)
else:
print("Timeout waiting for trigger")
t4.close()
CLI Example
xclock --clock-tick-rates 100 --duration 10 --when on_trigger --timeout 30 start
Common Patterns
Pattern 1: Synchronize Two Cameras
from xclock.devices import LabJackT4
t4 = LabJackT4()
channels = t4.get_available_output_clock_channels()
# Behavior camera at 60 Hz
t4.add_clock_channel(60, channels[0], duration_s=300) # 5 minutes
# Imaging camera at 30 Hz
t4.add_clock_channel(30, channels[1], duration_s=300)
# Record all frame timestamps
t4.start_clocks_and_record_edge_timestamps(
wait_for_pulsed_clocks_to_finish=True,
filename="camera_sync.csv"
)
Pattern 2: Test Device Connectivity
from xclock.devices import LabJackT4
try:
t4 = LabJackT4()
print(f"✓ Connected to LabJack T4")
print(f"✓ Base clock: {t4.base_clock_frequency_hz} Hz")
print(f"✓ Available channels: {t4.get_available_output_clock_channels()}")
t4.close()
except Exception as e:
print(f"✗ Connection failed: {e}")
Pattern 3: Short Test Pulse
Useful for testing wiring and connections:
from xclock.devices import LabJackT4
t4 = LabJackT4()
channels = t4.get_available_output_clock_channels()
# Generate 10 pulses at 10 Hz (1 second total)
t4.add_clock_channel(
clock_tick_rate_hz=10,
channel_name=channels[0],
number_of_pulses=10,
)
t4.start_clocks(wait_for_pulsed_clocks_to_finish=True)
print("Test pulses complete!")
t4.close()
Next Steps
Now that you understand the basics:
Learn about Command-Line Interface for more command-line options
Read Device Support for device-specific features
Explore the ../api/devices for advanced usage
Common Issues
Clock frequency not exactly as requested
XClock calculates the closest achievable frequency based on the device’s base clock and divisor limitations. The actual frequency is returned in the ClockChannel.actual_sample_rate_hz field.
channel = t4.add_clock_channel(clock_tick_rate_hz=100, ...)
print(f"Requested: 100 Hz, Actual: {channel.actual_sample_rate_hz} Hz")
Device already in use
If you get an error about the device being in use:
Make sure you called
close()on previous device instancesCheck if another process is using the device
Disconnect and reconnect the device
Restart your Python kernel/terminal
No pulses generated
Common causes:
Clock not started with
start_clocks()number_of_pulsesset to 0Device not properly connected
Check the physical wiring and connections