18uint16_t pc_c37118_crc(
const uint8_t *data,
size_t len)
26size_t pc_c37118_build_frame(uint8_t *buf,
size_t cap, uint8_t type, uint8_t version, uint16_t idcode, uint32_t soc,
27 uint32_t fracsec,
const uint8_t *payload,
size_t payload_len)
29 if (!buf || (payload_len && !payload))
33 size_t total = C37118_MIN_FRAME + payload_len;
34 if (total > 0xFFFF || total > cap)
39 buf[p++] = C37118_SYNC_LEADER;
40 buf[p++] = (uint8_t)(((type & C37118_TYPE_MASK) << C37118_TYPE_SHIFT) | (version & C37118_VERSION_MASK));
47 memcpy(buf + p, payload, payload_len);
50 uint16_t crc = pc_c37118_crc(buf, p);
55size_t pc_c37118_build_command(uint8_t *buf,
size_t cap, uint16_t idcode, uint32_t soc, uint32_t fracsec, uint16_t cmd)
59 return pc_c37118_build_frame(buf, cap, C37118_TYPE_CMD, C37118_VERSION_2011, idcode, soc, fracsec, payload, 2);
62bool pc_c37118_parse_frame(
const uint8_t *buf,
size_t len, C37118Frame *out)
64 if (!buf || !out || len < C37118_MIN_FRAME)
68 if (buf[0] != C37118_SYNC_LEADER)
73 if (framesize < C37118_MIN_FRAME || framesize > len)
77 uint16_t want = pc_c37118_crc(buf, (
size_t)framesize - 2);
78 uint16_t got =
pc_rd16be(buf + framesize - 2);
83 out->type = (uint8_t)((buf[1] >> C37118_TYPE_SHIFT) & C37118_TYPE_MASK);
84 out->version = (uint8_t)(buf[1] & C37118_VERSION_MASK);
85 out->framesize = framesize;
90 out->data_len = (size_t)framesize - C37118_MIN_FRAME;
94bool pc_c37118_parse_command(
const C37118Frame *f, uint16_t *cmd)
96 if (!f || f->type != C37118_TYPE_CMD || f->data_len < 2)
107bool pc_c37118_decode_stat(
const C37118Frame *f, C37118Stat *out)
109 if (!f || !out || f->type != C37118_TYPE_DATA || f->data_len < 2)
115 out->data_valid = (s & 0x8000u) == 0;
116 out->pmu_error = (s & 0x4000u) != 0;
117 out->in_sync = (s & 0x2000u) == 0;
118 out->sorted_by_arrival = (s & 0x1000u) != 0;
119 out->trigger = (s & 0x0800u) != 0;
120 out->config_change = (s & 0x0400u) != 0;
121 out->data_modified = (s & 0x0200u) != 0;
122 out->time_quality = (uint8_t)((s >> 6) & 0x07u);
123 out->unlocked_time = (uint8_t)((s >> 4) & 0x03u);
124 out->trigger_reason = (uint8_t)(s & 0x0Fu);
IEEE C37.118.2 synchrophasor frame codec (PC_ENABLE_C37118) - zero-heap frame builder + parser for th...
Parameterized CRC engine - one source of truth for every cyclic redundancy check.
constexpr pc_crc_params PC_CRC16_IBM_3740
CRC-16/IBM-3740 (often called CCITT-FALSE). check = 0x29B1.
uint32_t pc_crc(const pc_crc_params *p, const uint8_t *data, size_t len)
One-shot CRC of len octets at data.
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint32_t pc_rd32be(const uint8_t *p)
Read a big-endian u32 at p.
size_t pc_wr16be(uint8_t *p, uint16_t v)
Write v big-endian at p.
uint16_t pc_rd16be(const uint8_t *p)
Read a big-endian u16 at p.
size_t pc_wr32be(uint8_t *p, uint32_t v)
Write v big-endian at p.