{ "cells": [ { "cell_type": "markdown", "id": "470fa260-9e5c-41f4-b113-f6bac088ca8a", "metadata": {}, "source": [ "# Clock Setup\n", "\n", "## Overview\n", "This document describes the procedures and methods to setup a clock sequencer which is used to trigger a pulse sequence at a fixed frequency or event cycle. Clock sequencers are the backbone of measurement aquisition systems since they determine how often and when measurements are taken. As such, it is important to understand all the different configurations for clock sequencers, especially in the case for OpenSync.\n", "\n", "## Purpose\n", "To help users reach an optimal clock sequencer config, a set of examples and explanations are given for each major configuration mode. Since clock sequencer parameters are the main topic of this discussion, please note that the loading and executing of clock configs will not be discussed in this document." ] }, { "cell_type": "markdown", "id": "8e46606f-b5f9-4e9c-9f91-75235456eeeb", "metadata": {}, "source": [ "## Creating the Clock Config\n", "A basic clock config can be obtained from calling the function `get_clock_params` from the opensync namespace. All clock parameters are contained within this dictionary. It is typically not recommended to modify the dictionary of clock parameters and instead one should prioritize using helepr functions which check for invalid inputs. " ] }, { "cell_type": "code", "execution_count": 1, "id": "255fb2e2-a882-4fd3-b58d-dac0ed4f6a09", "metadata": {}, "outputs": [], "source": [ "import opensync" ] }, { "cell_type": "code", "execution_count": 2, "id": "6990df78-9a29-4ca7-ad94-e5189de5e7a2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'state': False,\n", " 'divider': 1,\n", " 'period': 0.0,\n", " 'bcounter': 0,\n", " 'pcounter': 1,\n", " 'ocounter': 0,\n", " 'trigger_mode': 'disabled',\n", " 'trigger_edge': 'rising',\n", " 'gate_mode': 'disabled',\n", " 'gate_level': 'low'}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "clock_config = opensync.get_clock_params()\n", "clock_config" ] }, { "cell_type": "markdown", "id": "4554712a-532c-4edc-b4b5-0d8c20134fb3", "metadata": {}, "source": [ "As you can see, there is a lot of different ways a clock sequencer can be configured. A clock sequencer is enabled by setting its `state` to True. The `divider` controlls the clock resolution of the clock sequencer. The repetition frequency is determined by its `period`, or how quickly it repeats. For instance, a one millisecond period results in a one kilohertz frequency.\n", "\n", "The clock sequencer contains three counters which determine how the clock sequencer runs. The number of acquisition events that are accepted is dictated by `bcounter`. This is particularly important for external trigger configurations as it allows one to select the number of triggers to accept before finishing program operation. `pcounter` is the number of pulses to perform per trigger. A `pcounter` of 10,0000 would perform 10,000 pulse sequencer triggers at the desired repetition frequency per accepted trigger. `ocounter` if paired with `pcounter` and is the number of internal events to skip that makes up the overal duty cycle program. Having a `pcounter` of 5 and `ocounter` of 3 would produce a total internal pulse train 8 pulses long where only the first 5 produces a trigger for the pulse sequencers.\n", "\n", "There are also external trigger and gate modes. `trigger_mode` selects where the internal trigger event occur. If set to `disabled`, the trigger can be generated via firmware. Conversly, setting the mode to `triggered` will require an external trigger to start each clock sequence. Gating can be used at the clock level to prevent signals from reaching the pulse sequencers. A gate mode of `pulse` ignores each trigger event until the logic level is satisfied. It does not decrement `bcounter` since the clock sequencer program is not executed. However, a gate mode of `output` will decrement `bcounter` as it inhibits the output of the trigger signal instead of inhibiting the program itself. Setting the gate mode to `channel` places the gating responsiblity to the pulse sequencers instead of the clock sequencers. The gate logic level can be selected as either `high` or `low`. Likewise, external trigger edges can also be `rising` or `falling`." ] }, { "cell_type": "markdown", "id": "25015ca0-0488-40b8-880a-ff33398d297f", "metadata": {}, "source": [ "## Example 1: Freerun Aquisition at 1 kHz for 1,000 Iterations\n", "For a simple particle image velocimetry (PIV) experiment, many do not use external triggers and simply want to capture a set of image pairs. In this example, 1,000 pulse sequence signals are sent at a frequency of one (1) kilohertz. With a suitable pulse config, this would result in 1,000 image pairs of high temporal resolution. The entire aquisition time would be around a single second." ] }, { "cell_type": "code", "execution_count": 3, "id": "df4b6bc7-bf24-487b-aa52-146bcf142324", "metadata": {}, "outputs": [], "source": [ "# Get default clock config\n", "clock_config = opensync.get_clock_params()" ] }, { "cell_type": "code", "execution_count": 4, "id": "19243764-7414-47a5-b787-908806c53e0e", "metadata": {}, "outputs": [], "source": [ "# Enable the clock sequencer\n", "clock_config = opensync.config_clock_state(\n", " clock_config,\n", " state=True\n", ")" ] }, { "cell_type": "code", "execution_count": 5, "id": "ef93ba7d-4b8e-4283-927c-189af2de5c11", "metadata": {}, "outputs": [], "source": [ "# Then change the clock frequency to 1 kHz\n", "clock_config = opensync.config_clock_period(\n", " clock_config,\n", " period=1.0, #0.001 second period = 1 kHz\n", " units='ms'\n", ")" ] }, { "cell_type": "code", "execution_count": 6, "id": "df9f8ae7-8cc5-48c8-a606-a84b59f93aff", "metadata": {}, "outputs": [], "source": [ "# Then change the total amount of clock reps to 1,000\n", "clock_config = opensync.config_clock_pcounter(\n", " clock_config,\n", " count=1000\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "id": "7adbfa67-e27d-46fa-9d4f-8f7bca4ef819", "metadata": {}, "outputs": [], "source": [ "# Make sure trigger mode is disabled\n", "clock_config = opensync.config_clock_trigger_mode(\n", " clock_config,\n", " trigger_mode='disabled'\n", ")" ] }, { "cell_type": "code", "execution_count": 8, "id": "f736e44e-f4a5-4a66-8054-ae056d268fdf", "metadata": {}, "outputs": [], "source": [ "# Make sure gate mode is disabled\n", "clock_config = opensync.config_clock_gate_mode(\n", " clock_config,\n", " gate_mode='disabled'\n", ")" ] }, { "cell_type": "markdown", "id": "229340ee-b80d-4a6e-b9ae-c92f3596e4cf", "metadata": {}, "source": [ "## Example 2: Freerun Aquisition at 1 kHz for 1,000 Iterations Only When Gate is High\n", "For a slighly more complicated experiment, there may be times where one does not want to aquire images while the internal clock is still running. For instance, lets say there is an experiment where a submersible is moving upstream in a water channel. When the submersible is not in the field of view of the measurement system, it may be a good idea to not acquire images in order to save memeory space. As such, an external trigger gated to the internal clock can be used to disable and enable pulse sequencer fire signals. This allows the acquisition system to only capture images/measurements when the submersible is in view and otherwise " ] }, { "cell_type": "markdown", "id": "751156be-7d4f-43d8-a92e-86d2cb675bc3", "metadata": {}, "source": [ "Expanding upon the previous example, we will be setting `gate_mode` to `pulse` and `gate_level` to `high`. Of course we may want a different acquisition frequence since one kilohertz is a little fast, but that is outside the scope of this example." ] }, { "cell_type": "code", "execution_count": 9, "id": "9d6b9168-2bc4-492e-86f5-7d7f479e39d7", "metadata": {}, "outputs": [], "source": [ "# Enable gating for the trigger signal\n", "clock_config = opensync.config_clock_gate_mode(\n", " clock_config,\n", " gate_mode='pulse'\n", ")" ] }, { "cell_type": "code", "execution_count": 10, "id": "0df1fd85-aec8-42da-b8f6-33328d3ba0b6", "metadata": {}, "outputs": [], "source": [ "# Only gate inhibit on high\n", "clock_config = opensync.config_clock_gate_level(\n", " clock_config,\n", " level='high'\n", ")" ] }, { "cell_type": "markdown", "id": "40f4bf2d-682b-4651-9930-66e052922609", "metadata": {}, "source": [ "## Example 3: Triggered Aquisition at 1 kHz for 1,000 Iterations Only When Gate is High\n", "The final examples expands upon the last two examples by using an external trigger to start the clock sequencer program. Only 1,000 triggers would be accepted before signaling that the acquisition is complete." ] }, { "cell_type": "code", "execution_count": 11, "id": "e9fb13d1-0cb5-4e36-abf6-5f2ddc3b323e", "metadata": {}, "outputs": [], "source": [ "# Enable external triggering\n", "clock_config = opensync.config_clock_trigger_mode(\n", " clock_config,\n", " trigger_mode='triggered'\n", ")" ] }, { "cell_type": "code", "execution_count": 12, "id": "c10759b0-7967-4f91-aa9f-a52c6637990c", "metadata": {}, "outputs": [], "source": [ "# Set external trigger edge to rising\n", "clock_config = opensync.config_clock_trigger_edge(\n", " clock_config,\n", " edge='rising'\n", ")" ] }, { "cell_type": "code", "execution_count": 13, "id": "450ab9bb-603b-4f88-aaf9-863b5bfcf13e", "metadata": {}, "outputs": [], "source": [ "# Set bcounter to 1,000 to only that amount of triggers are accepted\n", "clock_config = opensync.config_clock_bcounter(\n", " clock_config,\n", " count=1000\n", ")" ] }, { "cell_type": "code", "execution_count": 14, "id": "77d0ae6a-1432-461f-ae52-77b5c7c0b201", "metadata": {}, "outputs": [], "source": [ "# Set pcounter to 1 so only one pulse happens per trigger\n", "clock_config = opensync.config_clock_pcounter(\n", " clock_config,\n", " count=1\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" } }, "nbformat": 4, "nbformat_minor": 5 }