# NI counter read task

> Learn how to acquire counter data from NI devices with Synnax.

This task follows the standard [task lifecycle](https://docs.synnaxlabs.com/reference/driver/task-basics#task-lifecycle).

## Task configuration reference

| Parameter     | Type   | Required | Default | Description                                                   |
| ------------- | ------ | -------- | ------- | ------------------------------------------------------------- |
| `sample_rate` | number | No       | `10`    | Samples per second (Hz), determined by module                 |
| `stream_rate` | number | No       | `5`     | Rate data is streamed to Synnax (Hz), must be ≤ `sample_rate` |
| `channels`    | array  | Yes      | -       | List of counter input channel configurations                  |

## Channel types reference

Frequency ([`ci_frequency`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1gad7f76a3c13cd1acceb5015cbb57c5743.html))

Measures the frequency of digital pulses.

| Parameter      | Type   | Required | Default      | Description                                                 |
| -------------- | ------ | -------- | ------------ | ----------------------------------------------------------- |
| `channel`      | number | Yes      | -            | Synnax channel key                                          |
| `device`       | string | Yes      | -            | Device identifier                                           |
| `port`         | number | Yes      | -            | Counter port (0-indexed)                                    |
| `min_val`      | number | No       | `2`          | Minimum frequency                                           |
| `max_val`      | number | No       | `100`        | Maximum frequency                                           |
| `units`        | enum   | No       | `Hz`         | `Hz`, `Ticks`                                               |
| `edge`         | enum   | No       | `Rising`     | `Rising`, `Falling`                                         |
| `meas_method`  | enum   | No       | `DynamicAvg` | `LowFreq1Ctr`, `HighFreq2Ctr`, `LargeRng2Ctr`, `DynamicAvg` |
| `meas_time`    | number | No       | `0.000006`   | Measurement time (seconds), used with `HighFreq2Ctr`        |
| `divisor`      | number | No       | `4`          | Integer divisor, used with `LargeRng2Ctr`                   |
| `terminal`     | string | No       | -            | PFI terminal for signal routing                             |
| `custom_scale` | object | No       | -            | Custom scaling                                              |

Edge count ([`ci_edge_count`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1ga898d4d3101b616a681bc31199ae37d5f.html))

Counts rising or falling edges on a digital signal.

| Parameter         | Type   | Required | Default   | Description                                    |
| ----------------- | ------ | -------- | --------- | ---------------------------------------------- |
| `channel`         | number | Yes      | -         | Synnax channel key                             |
| `device`          | string | Yes      | -         | Device identifier                              |
| `port`            | number | Yes      | -         | Counter port (0-indexed)                       |
| `active_edge`     | enum   | No       | `Rising`  | `Rising`, `Falling`                            |
| `count_direction` | enum   | No       | `CountUp` | `CountUp`, `CountDown`, `ExternallyControlled` |
| `initial_count`   | number | No       | `0`       | Starting count value                           |
| `terminal`        | string | No       | -         | PFI terminal for signal routing                |

Period ([`ci_period`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1gafff33171661e94431afbd5a0abf4f73e.html))

Measures the time between edges of a periodic signal.

| Parameter       | Type   | Required | Default      | Description                                                 |
| --------------- | ------ | -------- | ------------ | ----------------------------------------------------------- |
| `channel`       | number | Yes      | -            | Synnax channel key                                          |
| `device`        | string | Yes      | -            | Device identifier                                           |
| `port`          | number | Yes      | -            | Counter port (0-indexed)                                    |
| `min_val`       | number | No       | `0.000001`   | Minimum period                                              |
| `max_val`       | number | No       | `0.1`        | Maximum period                                              |
| `units`         | enum   | No       | `Seconds`    | `Seconds`, `Ticks`                                          |
| `starting_edge` | enum   | No       | `Rising`     | `Rising`, `Falling`                                         |
| `meas_method`   | enum   | No       | `DynamicAvg` | `LowFreq1Ctr`, `HighFreq2Ctr`, `LargeRng2Ctr`, `DynamicAvg` |
| `meas_time`     | number | No       | `0.001`      | Measurement time (seconds), used with `HighFreq2Ctr`        |
| `divisor`       | number | No       | `4`          | Integer divisor, used with `LargeRng2Ctr`                   |
| `terminal`      | string | No       | -            | PFI terminal for signal routing                             |
| `custom_scale`  | object | No       | -            | Custom scaling                                              |

Pulse width ([`ci_pulse_width`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1gab7c2c214f995c0b0a065940d739b3168.html))

Measures the width of pulses in a digital signal.

| Parameter       | Type   | Required | Default    | Description                     |
| --------------- | ------ | -------- | ---------- | ------------------------------- |
| `channel`       | number | Yes      | -          | Synnax channel key              |
| `device`        | string | Yes      | -          | Device identifier               |
| `port`          | number | Yes      | -          | Counter port (0-indexed)        |
| `min_val`       | number | No       | `0.000001` | Minimum pulse width             |
| `max_val`       | number | No       | `0.1`      | Maximum pulse width             |
| `units`         | enum   | No       | `Seconds`  | `Seconds`, `Ticks`              |
| `starting_edge` | enum   | No       | `Rising`   | `Rising`, `Falling`             |
| `terminal`      | string | No       | -          | PFI terminal for signal routing |
| `custom_scale`  | object | No       | -          | Custom scaling                  |

Semi period ([`ci_semi_period`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1ga7ee9e61ab68edc4c6c72e9bc347a623f.html))

Measures the half-period of a periodic signal.

| Parameter      | Type   | Required | Default    | Description                     |
| -------------- | ------ | -------- | ---------- | ------------------------------- |
| `channel`      | number | Yes      | -          | Synnax channel key              |
| `device`       | string | Yes      | -          | Device identifier               |
| `port`         | number | Yes      | -          | Counter port (0-indexed)        |
| `min_val`      | number | No       | `0.000001` | Minimum semi-period             |
| `max_val`      | number | No       | `0.1`      | Maximum semi-period             |
| `units`        | enum   | No       | `Seconds`  | `Seconds`, `Ticks`              |
| `terminal`     | string | No       | -          | PFI terminal for signal routing |
| `custom_scale` | object | No       | -          | Custom scaling                  |

Two edge separation ([`ci_two_edge_sep`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1ga48a4f962e8752504edea1a0de6e39c74.html))

Measures the time between two different edges of a signal.

| Parameter         | Type   | Required | Default    | Description                         |
| ----------------- | ------ | -------- | ---------- | ----------------------------------- |
| `channel`         | number | Yes      | -          | Synnax channel key                  |
| `device`          | string | Yes      | -          | Device identifier                   |
| `port`            | number | Yes      | -          | Counter port (0-indexed)            |
| `min_val`         | number | No       | `0.000001` | Minimum separation time             |
| `max_val`         | number | No       | `1`        | Maximum separation time             |
| `units`           | enum   | No       | `Seconds`  | `Seconds`, `Ticks`                  |
| `first_edge`      | enum   | No       | `Rising`   | `Rising`, `Falling`                 |
| `second_edge`     | enum   | No       | `Falling`  | `Rising`, `Falling`                 |
| `first_terminal`  | string | No       | -          | PFI terminal for first edge signal  |
| `second_terminal` | string | No       | -          | PFI terminal for second edge signal |
| `custom_scale`    | object | No       | -          | Custom scaling                      |

Velocity linear ([`ci_velocity_linear`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1gaffd68d2994c51dfd10ec8d61f723983f.html))

Measures linear velocity using a quadrature encoder.

| Parameter        | Type   | Required | Default | Description                         |
| ---------------- | ------ | -------- | ------- | ----------------------------------- |
| `channel`        | number | Yes      | -       | Synnax channel key                  |
| `device`         | string | Yes      | -       | Device identifier                   |
| `port`           | number | Yes      | -       | Counter port (0-indexed)            |
| `min_val`        | number | No       | `0`     | Minimum velocity                    |
| `max_val`        | number | No       | `1`     | Maximum velocity                    |
| `units`          | enum   | No       | `m/s`   | `m/s`, `in/s`                       |
| `decoding_type`  | enum   | No       | `X4`    | `X1`, `X2`, `X4`, `TwoPulse`        |
| `dist_per_pulse` | number | No       | `0.001` | Distance traveled per encoder pulse |
| `terminal_a`     | string | No       | -       | PFI terminal for encoder A signal   |
| `terminal_b`     | string | No       | -       | PFI terminal for encoder B signal   |
| `custom_scale`   | object | No       | -       | Custom scaling                      |

Velocity angular ([`ci_velocity_angular`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1ga6bf192a1dff614bfb0055bc94149c860.html))

Measures angular velocity (rotational speed) using a quadrature encoder.

| Parameter        | Type   | Required | Default | Description                             |
| ---------------- | ------ | -------- | ------- | --------------------------------------- |
| `channel`        | number | Yes      | -       | Synnax channel key                      |
| `device`         | string | Yes      | -       | Device identifier                       |
| `port`           | number | Yes      | -       | Counter port (0-indexed)                |
| `min_val`        | number | No       | `0`     | Minimum angular velocity                |
| `max_val`        | number | No       | `1`     | Maximum angular velocity                |
| `units`          | enum   | No       | `RPM`   | `RPM`, `Radians/s`, `Degrees/s`         |
| `decoding_type`  | enum   | No       | `X4`    | `X1`, `X2`, `X4`, `TwoPulse`            |
| `pulses_per_rev` | number | No       | `24`    | Number of encoder pulses per revolution |
| `terminal_a`     | string | No       | -       | PFI terminal for encoder A signal       |
| `terminal_b`     | string | No       | -       | PFI terminal for encoder B signal       |
| `custom_scale`   | object | No       | -       | Custom scaling                          |

Position linear ([`ci_position_linear`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels__position_1ga90a24201a424fd7f4a436f45c726c618.html))

Tracks linear position using a quadrature encoder.

| Parameter         | Type    | Required | Default      | Description                                        |
| ----------------- | ------- | -------- | ------------ | -------------------------------------------------- |
| `channel`         | number  | Yes      | -            | Synnax channel key                                 |
| `device`          | string  | Yes      | -            | Device identifier                                  |
| `port`            | number  | Yes      | -            | Counter port (0-indexed)                           |
| `units`           | enum    | No       | `Meters`     | `Meters`, `Inches`, `Ticks`                        |
| `decoding_type`   | enum    | No       | `X4`         | `X1`, `X2`, `X4`, `TwoPulse`                       |
| `dist_per_pulse`  | number  | No       | `0.000001`   | Distance traveled per encoder pulse                |
| `initial_pos`     | number  | No       | `0.0`        | Starting position                                  |
| `terminal_a`      | string  | No       | -            | PFI terminal for encoder A signal                  |
| `terminal_b`      | string  | No       | -            | PFI terminal for encoder B signal                  |
| `z_index_enabled` | boolean | No       | `false`      | Enable Z-index signal                              |
| `z_index_val`     | number  | No       | `0`          | Position value when Z-index pulse occurs           |
| `z_index_phase`   | enum    | No       | `AHighBHigh` | `AHighBHigh`, `AHighBLow`, `ALowBHigh`, `ALowBLow` |
| `terminal_z`      | string  | No       | -            | PFI terminal for Z-index signal                    |
| `custom_scale`    | object  | No       | -            | Custom scaling                                     |

Position angular ([`ci_position_angular`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels__position_1ga942ca714baeb63c64fc0136bf61f3c6b.html))

Tracks angular position (rotation angle) using a quadrature encoder.

| Parameter         | Type    | Required | Default      | Description                                        |
| ----------------- | ------- | -------- | ------------ | -------------------------------------------------- |
| `channel`         | number  | Yes      | -            | Synnax channel key                                 |
| `device`          | string  | Yes      | -            | Device identifier                                  |
| `port`            | number  | Yes      | -            | Counter port (0-indexed)                           |
| `units`           | enum    | No       | `Degrees`    | `Degrees`, `Radians`, `Ticks`                      |
| `decoding_type`   | enum    | No       | `X4`         | `X1`, `X2`, `X4`, `TwoPulse`                       |
| `pulses_per_rev`  | number  | No       | `24`         | Number of encoder pulses per revolution            |
| `initial_angle`   | number  | No       | `0.0`        | Starting angle                                     |
| `terminal_a`      | string  | No       | -            | PFI terminal for encoder A signal                  |
| `terminal_b`      | string  | No       | -            | PFI terminal for encoder B signal                  |
| `z_index_enabled` | boolean | No       | `false`      | Enable Z-index signal                              |
| `z_index_val`     | number  | No       | `0`          | Angle value when Z-index pulse occurs              |
| `z_index_phase`   | enum    | No       | `AHighBHigh` | `AHighBHigh`, `AHighBLow`, `ALowBHigh`, `ALowBLow` |
| `terminal_z`      | string  | No       | -            | PFI terminal for Z-index signal                    |
| `custom_scale`    | object  | No       | -            | Custom scaling                                     |

Duty cycle ([`ci_duty_cycle`](https://www.ni.com/docs/en-US/bundle/ni-daqmx-c-api-ref/page/group__ni-daqmx__c__functions__channel__configuration__creation__create__counter__input__channels_1ga038bacad83c2aa098da70bf6ce810c10.html))

Measures the duty cycle of a periodic signal.

| Parameter      | Type   | Required | Default  | Description                     |
| -------------- | ------ | -------- | -------- | ------------------------------- |
| `channel`      | number | Yes      | -        | Synnax channel key              |
| `device`       | string | Yes      | -        | Device identifier               |
| `port`         | number | Yes      | -        | Counter port (0-indexed)        |
| `min_val`      | number | No       | `2`      | Minimum duty cycle frequency    |
| `max_val`      | number | No       | `10000`  | Maximum duty cycle frequency    |
| `active_edge`  | enum   | No       | `Rising` | `Rising`, `Falling`             |
| `terminal`     | string | No       | -        | PFI terminal for signal routing |
| `custom_scale` | object | No       | -        | Custom scaling                  |

## Important rules

* **One task per module** -> Only one running task can claim a module at a time.
* **Unique ports** -> A counter port can appear only once in a task.
* **Stream rate** -> For sample rates under 50 Hz, set the stream rate equal to the sample rate. Above that, keep the stream rate under 50 Hz.

## How-to

**Console**

[](https://synnax.nyc3.cdn.digitaloceanspaces.com/docs/device-drivers/ni/counter-read-task/ni-counter-read-dark.mp4#t=0.001)

**Python**

Configure and run task

```python
import synnax as sy
from synnax import ni

client = sy.Synnax()

# Retrieve device
counter_dev = client.devices.retrieve(name="Mod1_Counter")

# Create index channel
ctr_time = client.channels.create(
    name="ctr_time",
    is_index=True,
    data_type=sy.DataType.TIMESTAMP,
    retrieve_if_name_exists=True,
)

# Create data channels
frequency_ch = client.channels.create(
    name="frequency",
    index=ctr_time.key,
    data_type=sy.DataType.FLOAT32,
    retrieve_if_name_exists=True,
)
edge_count_ch = client.channels.create(
    name="edge_count",
    index=ctr_time.key,
    data_type=sy.DataType.UINT32,
    retrieve_if_name_exists=True,
)
angular_pos_ch = client.channels.create(
    name="angular_position",
    index=ctr_time.key,
    data_type=sy.DataType.FLOAT32,
    retrieve_if_name_exists=True,
)

# Create and configure task
task = ni.CounterReadTask(
    name="NI Counter Read Task",
    sample_rate=sy.Rate.HZ * 100,
    stream_rate=sy.Rate.HZ * 25,
    channels=[
        ni.CIFrequencyChannel(
            channel=frequency_ch.key,
            device=counter_dev.key,
            port=0,
            min_val=1,
            max_val=10000,
            units="Hz",
            edge="Rising",
            meas_method="DynamicAvg",
        ),
        ni.CIEdgeCountChannel(
            channel=edge_count_ch.key,
            device=counter_dev.key,
            port=1,
            active_edge="Rising",
            count_direction="CountUp",
            initial_count=0,
        ),
        ni.CIPositionAngularChannel(
            channel=angular_pos_ch.key,
            device=counter_dev.key,
            port=2,
            units="Degrees",
            decoding_type="X4",
            pulses_per_rev=1024,
            initial_angle=0.0,
            z_index_enabled=False,
        ),
    ],
)

client.tasks.configure(task)

# Start task and read data
with task.run():
    with client.open_streamer(["frequency", "edge_count", "angular_position"]) as streamer:
        for _ in range(10):
            frame = streamer.read()
            print(frame)
```

Edit task configuration

```python
# Retrieve existing task
task = client.tasks.retrieve(name="NI Counter Read Task")
task = ni.CounterReadTask(internal=task)

# Update task-level configuration
task.config.auto_start = True
task.config.stream_rate = int(sy.Rate.HZ * 50)

# Update frequency channel configuration
task.config.channels[0].min_val = 10
task.config.channels[0].max_val = 5000
task.config.channels[0].meas_method = "LowFreq1Ctr"
task.config.channels[0].terminal = "PFI0"

# Update edge count channel configuration
task.config.channels[1].active_edge = "Falling"
task.config.channels[1].initial_count = 100
task.config.channels[1].terminal = "PFI1"

# Update angular position channel configuration
task.config.channels[2].pulses_per_rev = 2048
task.config.channels[2].initial_angle = 90.0
task.config.channels[2].z_index_enabled = True
task.config.channels[2].terminal_a = "PFI7"
task.config.channels[2].terminal_b = "PFI8"
task.config.channels[2].terminal_z = "PFI9"

# Apply changes
client.tasks.configure(task)
```

**TypeScript**

Configure and run task

```typescript
import { Synnax } from "@synnaxlabs/client";

const client = new Synnax();

// Retrieve device
const [counterDev] = await client.devices.retrieve({ names: ["Mod1_Counter"] });

// Create index channel
const ctrTime = await client.channels.create(
  {
    name: "ctr_time",
    isIndex: true,
    dataType: "timestamp",
  },
  { retrieveIfNameExists: true },
);

// Create data channels
const frequencyCh = await client.channels.create(
  {
    name: "frequency",
    index: ctrTime.key,
    dataType: "float32",
  },
  { retrieveIfNameExists: true },
);

const edgeCountCh = await client.channels.create(
  {
    name: "edge_count",
    index: ctrTime.key,
    dataType: "uint32",
  },
  { retrieveIfNameExists: true },
);

const angularPosCh = await client.channels.create(
  {
    name: "angular_position",
    index: ctrTime.key,
    dataType: "float32",
  },
  { retrieveIfNameExists: true },
);

// Create and configure task
const task = await client.tasks.create({
  name: "NI Counter Read Task",
  rack: counterDev.rack,
  type: "ni_counter_read",
  config: {
    sampleRate: 100,
    streamRate: 25,
    channels: [
      {
        type: "ci_frequency",
        channel: frequencyCh.key,
        device: counterDev.key,
        port: 0,
        minVal: 1,
        maxVal: 10000,
        units: "Hz",
        edge: "Rising",
        measMethod: "DynamicAvg",
      },
      {
        type: "ci_edge_count",
        channel: edgeCountCh.key,
        device: counterDev.key,
        port: 1,
        activeEdge: "Rising",
        countDirection: "CountUp",
        initialCount: 0,
      },
      {
        type: "ci_position_angular",
        channel: angularPosCh.key,
        device: counterDev.key,
        port: 2,
        units: "Degrees",
        decodingType: "X4",
        pulsesPerRev: 1024,
        initialAngle: 0.0,
        zIndexEnabled: false,
      },
    ],
  },
});

// Start task
await task.start();

// Read data
const streamer = await client.openStreamer([
  "frequency",
  "edge_count",
  "angular_position",
]);
for (let i = 0; i < 10; i++) {
  const frame = await streamer.read();
  console.log(frame);
}

// Stop task
await task.stop();
await streamer.close();
```

Edit task configuration

```typescript
import { ni } from "@synnaxlabs/client";

// Retrieve existing task
const task = await client.tasks.retrieve({ name: "NI Counter Read Task" });

// Parse and update configuration
const config = ni.counterReadConfigZ.parse(task.config);

// Update task-level configuration
config.autoStart = true;
config.streamRate = 50;

// Update frequency channel configuration
const frequency = config.channels[0] as ni.CIFrequencyChannel;
frequency.minVal = 10;
frequency.maxVal = 5000;
frequency.measMethod = "LowFreq1Ctr";
frequency.terminal = "PFI0";

// Update edge count channel configuration
const edgeCount = config.channels[1] as ni.CIEdgeCountChannel;
edgeCount.activeEdge = "Falling";
edgeCount.initialCount = 100;
edgeCount.terminal = "PFI1";

// Update angular position channel configuration
const angularPosition = config.channels[2] as ni.CIPositionAngularChannel;
angularPosition.pulsesPerRev = 2048;
angularPosition.initialAngle = 90.0;
angularPosition.zIndexEnabled = true;
angularPosition.terminalA = "PFI7";
angularPosition.terminalB = "PFI8";
angularPosition.terminalZ = "PFI9";

// Apply changes
await client.tasks.create({
  key: task.key,
  rack: task.rack,
  name: task.name,
  type: task.type,
  config,
});
```
