const CV = packed struct {
    primary_address: u8,
    vstart: u8,
    acceleration_rate: u8 = 0,
    deceleration_rate: u8 = 0,
    vhigh: u8,
    vmid: u8,
    manufacturer_version: u8 = 0,
    manufacturer_id: u8 = 13,
    PWM_period: u8,
    EMF_feedback_cutout: u8, // CV10
    packet_timeout: u8,
    power_source_conversion: u8,
    AMFS_F1_F8: u8 = 0,
    AMFS_FL_F9_F12: u8 = 0,
    decoder_lock: u16 = 0,
    extended_address: u16,
    consist_address: u8 = 0,
    NMRA_reserved20: u8 = 0, // CV20
    consist_F1_F8: u8 = 0,
    consist_FL_F9_F12: u8 = 0,
    acceleration_adjustment: u8 = 0,
    deceleration_adjustment: u8 = 0,
    speed_table_select: u8 = 0, // TODO: not sure it worth the effort if speed PID is working...
    NMRA_reserved26: u8 = 0,
    decoder_automatic_stopping: u8 = 0, // TODO: disable setting these values if not implemented
    bidir_comm_config: u8 = 0, // TODO: disable setting these values if not implemented
    configuration_data: Configuration,
    error_information: u8 = 0, // CV30
    index_high_byte: u8,
    index_low_byte: u8,
    output_loc: u112 = 0, // 33..46 => 14*8=112
    manufacturer_unique47_64: u144 = 0, // 47..64 => 18*8=144
    kick_start: u8 = 0,
    forward_trim: u8 = 0,
    speed_table: u224 = 0, // 67..94 => 28*8=224
    reverse_trim: u8 = 0,
    NMRA_reserved96_104: u72 = 0, // 96..104 => 9*8=72
    user_identifier1: u8 = 0,
    user_identifier2: u8 = 0,
    NMRA_reserved107_111: u40 = 0, // 107..111 => 5*8=40
    manufacturer_unique112_256: u1160 = 0, // 112..256 => 145*8=1160
    /// Normally the CV table is 1024*8=8192 bits, but this part is unimplemented
    /// anyway, so we can just drop it for now for quarter size
    // indexed_area: u2048 = 0, // 257..512 => 256*8=2048
    // NMRA_reserved513_879: u2936 = 0, // 513..879 => 367*8=2936
    // NMRA_reserved880_891: u96 = 0, // 880..891 => 12*8=96
    // decoder_load: u8 = 0,
    // dynamic_flags: u8 = 0,
    // fuel_coal: u8 = 0,
    // water: u8 = 0,
    // SUSI: u1032 = 0, // 896..1024 => 129*8=1032
    const Configuration = packed struct {
        accessory_decoder: bool,
        reserved: u1 = 0,
        extended_addressing: bool,
        speed_table: u1,
        bidirectional_communication: bool,
        power_source_conversion: bool,
        FL_location: u1,
        direction: u1,
    };
    const default: CV = .{
        .primary_address = 3,
        .vstart = 8,
        .vhigh = 0, // not used yet
        .vmid = 0, // not used yet
        .PWM_period = 0b10010110,
        .EMF_feedback_cutout = 1,
        .packet_timeout = 0, // TODO: S-9.2.4 Section C recommends 20 seconds, not implemented yet
        .power_source_conversion = 0, // TODO Go to analog mode when CV1==0
        .extended_address = 0b1100001111101000,
        .index_high_byte = 0b00010000,
        .index_low_byte = 0b00000000,
        .configuration_data = .{
            .accessory_decoder = false,
            .extended_addressing = false,
            .speed_table = 0, // vstart-vmid-vhigh
            .bidirectional_communication = false,
            .power_source_conversion = false,
            .FL_location = 0, // TODO not implemented
            .direction = 0, // TODO not implemented
        },
    };
    /// Expects [0..1023], so for CV29 `cv` should be 28
    /// XXX: We cut the CVs, so actually it is [0..255]
    fn getCV(self: @This(), cv: u8) u8 {
        return @truncate(@as(u2048, @bitCast(self)) >> (@sizeOf(CV) - 1 - cv));
    }
    fn validate(self: @This()) void {
        std.debug.assert(self.primary_address < 128);
        std.debug.assert(self.extended_address >> 8 >= 11000000 and self.extended_address >> 8 <= 11100111);
        std.debug.assert(self.index_high_byte > 0b00001111); // 0b00000000..0b00001111 reserved by NMRA
    }
};