DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
j2735.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file j2735.cpp
6 * @brief SAE J2735 V2X UPER codec + BSMcore (see j2735.h).
7 */
8
10
11#if DETWS_ENABLE_J2735
12
13void uper_writer_init(UperWriter *w, uint8_t *buf, size_t cap)
14{
15 w->buf = buf;
16 w->cap = cap;
17 w->bit_pos = 0;
18 w->ok = (buf != nullptr && cap > 0);
19 // Zero the buffer so we can OR bits in - only when it is a real buffer (a null buf leaves ok=false).
20 if (buf)
21 for (size_t i = 0; i < cap; i++)
22 buf[i] = 0;
23}
24
25size_t uper_writer_finish(UperWriter *w)
26{
27 if (!w->ok)
28 return 0;
29 return (w->bit_pos + 7) / 8;
30}
31
32void uper_put_bits(UperWriter *w, uint32_t value, unsigned nbits)
33{
34 if (!w->ok || nbits == 0)
35 return;
36 if (w->bit_pos + nbits > w->cap * 8)
37 {
38 w->ok = false;
39 return;
40 }
41 for (unsigned i = 0; i < nbits; i++)
42 {
43 unsigned bit = (value >> (nbits - 1 - i)) & 1u;
44 if (bit)
45 {
46 size_t p = w->bit_pos + i;
47 w->buf[p / 8] |= (uint8_t)(0x80u >> (p % 8));
48 }
49 }
50 w->bit_pos += nbits;
51}
52
53void uper_put_bool(UperWriter *w, bool v)
54{
55 uper_put_bits(w, v ? 1 : 0, 1);
56}
57
58unsigned uper_cint_bits(int64_t lo, int64_t hi)
59{
60 if (hi <= lo)
61 return 0; // single value: encoded in zero bits
62 uint64_t range = (uint64_t)(hi - lo); // number of steps above lo is 0..range
63 unsigned bits = 0;
64 while (((uint64_t)1 << bits) <= range)
65 bits++;
66 return bits;
67}
68
69void uper_put_cint(UperWriter *w, int64_t value, int64_t lo, int64_t hi)
70{
71 unsigned bits = uper_cint_bits(lo, hi);
72 if (bits == 0)
73 return;
74 uint64_t off = (uint64_t)(value - lo);
75 // Values wider than 32 bits are not used by the J2735 fields here, but support up to 32.
76 uper_put_bits(w, (uint32_t)off, bits);
77}
78
79void uper_reader_init(UperReader *r, const uint8_t *buf, size_t nbits)
80{
81 r->buf = buf;
82 r->nbits = nbits;
83 r->bit_pos = 0;
84 r->ok = (buf != nullptr);
85}
86
87uint32_t uper_get_bits(UperReader *r, unsigned nbits)
88{
89 if (!r->ok || nbits == 0)
90 return 0;
91 if (r->bit_pos + nbits > r->nbits)
92 {
93 r->ok = false;
94 return 0;
95 }
96 uint32_t v = 0;
97 for (unsigned i = 0; i < nbits; i++)
98 {
99 size_t p = r->bit_pos + i;
100 unsigned bit = (r->buf[p / 8] >> (7 - (p % 8))) & 1u;
101 v = (v << 1) | bit;
102 }
103 r->bit_pos += nbits;
104 return v;
105}
106
107bool uper_get_bool(UperReader *r)
108{
109 return uper_get_bits(r, 1) != 0;
110}
111
112int64_t uper_get_cint(UperReader *r, int64_t lo, int64_t hi)
113{
114 unsigned bits = uper_cint_bits(lo, hi);
115 if (bits == 0)
116 return lo;
117 uint32_t off = uper_get_bits(r, bits);
118 return lo + (int64_t)off;
119}
120
121size_t detws_j2735_bsm_core_encode(const J2735BsmCore *c, uint8_t *out, size_t cap)
122{
123 if (!c || !out)
124 return 0;
125 UperWriter w;
126 uper_writer_init(&w, out, cap);
127 uper_put_cint(&w, c->msg_count, 0, 127);
128 uper_put_bits(&w, c->id, 32);
129 uper_put_cint(&w, c->sec_mark, 0, 65535);
130 uper_put_cint(&w, c->lat, -900000000, 900000001);
131 uper_put_cint(&w, c->lon, -1799999999, 1800000001);
132 uper_put_cint(&w, c->elev, -4096, 61439);
133 uper_put_cint(&w, c->speed, 0, 8191);
134 uper_put_cint(&w, c->heading, 0, 28800);
135 return uper_writer_finish(&w);
136}
137
138bool detws_j2735_bsm_core_decode(const uint8_t *in, size_t len, J2735BsmCore *c)
139{
140 if (!in || !c)
141 return false;
142 UperReader r;
143 uper_reader_init(&r, in, len * 8);
144 c->msg_count = (uint8_t)uper_get_cint(&r, 0, 127);
145 c->id = uper_get_bits(&r, 32);
146 c->sec_mark = (uint16_t)uper_get_cint(&r, 0, 65535);
147 c->lat = (int32_t)uper_get_cint(&r, -900000000, 900000001);
148 c->lon = (int32_t)uper_get_cint(&r, -1799999999, 1800000001);
149 c->elev = (int32_t)uper_get_cint(&r, -4096, 61439);
150 c->speed = (uint16_t)uper_get_cint(&r, 0, 8191);
151 c->heading = (uint16_t)uper_get_cint(&r, 0, 28800);
152 return r.ok;
153}
154
155size_t detws_j2735_spat_encode(const J2735MovementState *states, size_t count, uint8_t *out, size_t cap)
156{
157 if (!out || (count && !states) || count > 31)
158 return 0;
159 UperWriter w;
160 uper_writer_init(&w, out, cap);
161 uper_put_cint(&w, (int64_t)count, 0, 31);
162 for (size_t i = 0; i < count; i++)
163 {
164 uper_put_cint(&w, states[i].signal_group, 0, 255);
165 uper_put_cint(&w, states[i].phase, 0, 9);
166 uper_put_cint(&w, states[i].min_end_time, 0, 36000);
167 uper_put_cint(&w, states[i].max_end_time, 0, 36000);
168 }
169 return uper_writer_finish(&w);
170}
171
172bool detws_j2735_spat_decode(const uint8_t *in, size_t len, J2735MovementState *out_states, size_t max_states,
173 size_t *out_count)
174{
175 if (!in || !out_states || !out_count)
176 return false;
177 UperReader r;
178 uper_reader_init(&r, in, len * 8);
179 size_t count = (size_t)uper_get_cint(&r, 0, 31);
180 if (!r.ok || count > max_states)
181 return false;
182 for (size_t i = 0; i < count; i++)
183 {
184 out_states[i].signal_group = (uint8_t)uper_get_cint(&r, 0, 255);
185 out_states[i].phase = (uint8_t)uper_get_cint(&r, 0, 9);
186 out_states[i].min_end_time = (uint16_t)uper_get_cint(&r, 0, 36000);
187 out_states[i].max_end_time = (uint16_t)uper_get_cint(&r, 0, 36000);
188 }
189 if (!r.ok)
190 return false;
191 *out_count = count;
192 return true;
193}
194
195size_t detws_j2735_map_encode(const J2735MapIntersection *isect, const J2735Lane *lanes, size_t count, uint8_t *out,
196 size_t cap)
197{
198 if (!isect || !out || (count && !lanes) || count > 31)
199 return 0;
200 UperWriter w;
201 uper_writer_init(&w, out, cap);
202 uper_put_cint(&w, isect->intersection_id, 0, 65535);
203 uper_put_cint(&w, isect->ref_lat, 0, 65535);
204 uper_put_cint(&w, isect->ref_lon, 0, 65535);
205 uper_put_cint(&w, (int64_t)count, 0, 31);
206 for (size_t i = 0; i < count; i++)
207 {
208 uper_put_cint(&w, lanes[i].lane_id, 0, 255);
209 uper_put_bool(&w, lanes[i].is_ingress);
210 uper_put_cint(&w, lanes[i].node_x, -2048, 2047);
211 uper_put_cint(&w, lanes[i].node_y, -2048, 2047);
212 }
213 return uper_writer_finish(&w);
214}
215
216bool detws_j2735_map_decode(const uint8_t *in, size_t len, J2735MapIntersection *isect, J2735Lane *out_lanes,
217 size_t max_lanes, size_t *out_count)
218{
219 if (!in || !isect || !out_lanes || !out_count)
220 return false;
221 UperReader r;
222 uper_reader_init(&r, in, len * 8);
223 isect->intersection_id = (uint16_t)uper_get_cint(&r, 0, 65535);
224 isect->ref_lat = (uint16_t)uper_get_cint(&r, 0, 65535);
225 isect->ref_lon = (uint16_t)uper_get_cint(&r, 0, 65535);
226 size_t count = (size_t)uper_get_cint(&r, 0, 31);
227 if (!r.ok || count > max_lanes)
228 return false;
229 for (size_t i = 0; i < count; i++)
230 {
231 out_lanes[i].lane_id = (uint8_t)uper_get_cint(&r, 0, 255);
232 out_lanes[i].is_ingress = uper_get_bool(&r);
233 out_lanes[i].node_x = (int16_t)uper_get_cint(&r, -2048, 2047);
234 out_lanes[i].node_y = (int16_t)uper_get_cint(&r, -2048, 2047);
235 }
236 if (!r.ok)
237 return false;
238 *out_count = count;
239 return true;
240}
241
242#endif // DETWS_ENABLE_J2735
SAE J2735 V2X - ASN.1 UPER primitive codec + Basic Safety Message core (DETWS_ENABLE_J2735).