Skip to main content

Crate st12_1

Crate st12_1 

Source
Expand description

SMPTE ST 12-1:2014 “Time and Control Code” — the §9 Linear Time Code (LTC) 80-bit logical codeword.

This crate implements exactly the wire structure described in the curated spec transcription at st12-1/docs/st12-1.md (fetched directly from https://pub.smpte.org/pub/st12-1/st0012-1-2014.pdf) — cite that file, not this doc comment, as the field-semantics oracle.

  • LtcFrame — the 80-bit LTC codeword (§9.2): BCD hours/minutes/ seconds/frames, the drop-frame and color-frame flags, four rate-dependent flag bits (resolved via FrameRate), eight 4-bit binary groups (“user bits”), and the fixed synchronization word.
  • FrameRate — which of ST 12-1 Table 3’s three flag-bit-position columns (30-frame / 25-frame / 24-frame) applies; the codeword itself carries no self-describing frame-rate field.
  • BinaryGroupUsage / BinaryGroupFlags — Table 1’s classification of what the binary groups contain, from the three binary group flag bits.

Scope: this crate models only the already-demodulated logical 80-bit codeword — never the §9.3 biphase-mark-encoded physical/analog audio waveform LTC is carried as on a wire. That line-encoding/clock-recovery layer is out of scope for this project, the same way it never decodes PCM or AC-3 audio samples. See docs/st12-1.md’s “Scope” section.

Depends only on broadcast-common. #![no_std] when the std feature is disabled (this crate needs no heap allocation at all — every field is a fixed-size scalar).

§Examples

Build a frame and round-trip it:

use broadcast_common::{Parse, Serialize};
use st12_1::LtcFrame;

let frame = LtcFrame {
    hours: 1,
    minutes: 23,
    seconds: 45,
    frames: 13,
    drop_frame_flag: false,
    color_frame_flag: true,
    flag_bit_27: true,
    flag_bit_43: true,
    flag_bit_58: false,
    flag_bit_59: true,
    user_bits: [1, 2, 3, 4, 5, 6, 7, 8],
};
let mut bytes = [0u8; st12_1::FRAME_LEN];
frame.serialize_into(&mut bytes).unwrap();
assert_eq!(LtcFrame::parse(&bytes).unwrap(), frame);

§Runnable examples

Run with cargo run -p st12-1 --example <name>.

§build_frame

//! Build an LTC codeword from typed fields and serialize it to wire bytes —
//! SMPTE ST 12-1:2014 §9.2.
//!
//! Run with `cargo run -p st12-1 --example build_frame`.

use broadcast_common::Serialize;
use st12_1::LtcFrame;

fn main() {
    let frame = LtcFrame {
        hours: 1,
        minutes: 23,
        seconds: 45,
        frames: 13,
        drop_frame_flag: false,
        color_frame_flag: true,
        flag_bit_27: true,
        flag_bit_43: true,
        flag_bit_58: false,
        flag_bit_59: true,
        user_bits: [1, 2, 3, 4, 5, 6, 7, 8],
    };

    let mut bytes = [0u8; st12_1::FRAME_LEN];
    frame.serialize_into(&mut bytes).expect("serialize");

    println!("serialized {} bytes:", bytes.len());
    println!(
        "{}",
        bytes
            .iter()
            .map(|b| format!("{b:02x}"))
            .collect::<Vec<_>>()
            .join(" ")
    );
    println!(
        "time address: {:02}:{:02}:{:02}:{:02}",
        frame.hours, frame.minutes, frame.seconds, frame.frames
    );
    println!("drop_frame_flag:  {}", frame.drop_frame_flag);
    println!("color_frame_flag: {}", frame.color_frame_flag);
    println!("user bits: {:X?}", frame.user_bits);
}

§parse_frame

//! Parse a spec-derived LTC codeword (see `docs/st12-1.md`'s "Worked vector"
//! section), print its decoded fields — including the `FrameRate`-dependent
//! polarity-correction and binary-group-flag bits — then round-trip it back
//! to bytes and confirm the output is byte-identical.
//!
//! Run with `cargo run -p st12-1 --example parse_frame`.

use broadcast_common::{Parse, Serialize};
use st12_1::{FrameRate, LtcFrame};

fn main() {
    // Time Address = 01:23:45:13 (docs/st12-1.md's worked example), computed
    // independently from ST 12-1 Tables 2-5, not by round-tripping through
    // this crate's own serializer.
    #[rustfmt::skip]
    let bytes: [u8; st12_1::FRAME_LEN] =
        [0x13, 0x29, 0x35, 0x4C, 0x53, 0x6A, 0x71, 0x88, 0xFC, 0xBF];

    let frame = LtcFrame::parse(&bytes).expect("parse LTC codeword");
    println!(
        "time address: {:02}:{:02}:{:02}:{:02}",
        frame.hours, frame.minutes, frame.seconds, frame.frames
    );
    println!("drop_frame_flag:  {}", frame.drop_frame_flag);
    println!("color_frame_flag: {}", frame.color_frame_flag);
    println!("user bits: {:X?}", frame.user_bits);

    for rate in [FrameRate::Fps30, FrameRate::Fps25, FrameRate::Fps24] {
        let bgf = frame.binary_group_flags(rate);
        println!(
            "{rate}: polarity_correction={} binary_group_usage={}",
            frame.polarity_correction(rate),
            bgf.usage()
        );
    }

    let mut out = [0u8; st12_1::FRAME_LEN];
    frame.serialize_into(&mut out).expect("serialize");
    assert_eq!(out, bytes, "byte-identical round trip");
    println!("round trip byte-identical: OK ({} bytes)", out.len());
}

Structs§

BinaryGroupFlags
The three binary group flag bits (§8.3.3), resolved from an LtcFrame against a chosen FrameRate (see LtcFrame::binary_group_flags).
LtcFrame
A parsed (or to-be-serialized) 80-bit LTC codeword (§9.2): the BCD time address, drop/color frame flags, the four rate-dependent flag bits (polarity correction / BGF0 / BGF1 / BGF2 — see FrameRate), the eight 4-bit binary groups (“user bits”, §8.1), and the fixed synchronization word (always SYNC_WORD on serialize; validated on parse).

Enums§

BinaryGroupUsage
The meaning of the eight binary groups (“user bits”), per the three binary group flag bits BGF2/BGF1/BGF0 (§8.3.3, Table 1).
Error
An LTC codeword parse / serialize error.
FrameRate
Which of ST 12-1 Table 3’s three flag-bit-position columns applies.

Constants§

FRAME_LEN
Length of the LTC codeword: 80 bits (§9.1) packed 8 bits/byte.
MAX_BINARY_GROUP
Maximum value of one 4-bit binary group (“user bits”) nibble (§8.1/Table 4).
MAX_FRAMES
Maximum frame value across all supported frame rates: 30-frame counting (drop or non-drop, §5.2.1/§5.2.2) numbers frames 00 through 29, the widest of the three per-rate bounds (25-frame: 00-24 per §6.2; 24-frame: 00-23 per §7.2). The 80-bit codeword carries no self-describing frame-rate field, so this crate validates against the widest bound and leaves the tighter per-rate bound to a caller that knows its stream’s frame rate (see docs/st12-1.md §8.2).
MAX_HOURS
Maximum hour value — “24-hour clock … to 23 hours” (§5.2/§6.2/§7.2).
MAX_MINUTES_SECONDS
Maximum minute/second value — “… 59 minutes, and 59 seconds”.
SYNC_WORD
The fixed synchronization word (§9.2.5, Table 5), as the two bytes it occupies under this crate’s bit-to-byte packing (docs/st12-1.md’s “Byte packing convention”): byte 8 holds bits 64-71, byte 9 holds bits 72-79. This is the well-known LTC sync-word byte pair.

Type Aliases§

Result
Result alias for st12-1 parsing/serialization.