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
}
};