Decode radio transmissions from devices on the ISM bands in OCaml
1(* Two-dimensional bit buffer *)
2
3let max_cols = 128
4let max_rows = 50
5
6type t = {
7 mutable num_rows : int;
8 mutable free_row : int;
9 bits_per_row : int array;
10 syncs_before_row : int array;
11 bb : bytes array;
12}
13
14let create () =
15 {
16 num_rows = 0;
17 free_row = 0;
18 bits_per_row = Array.make max_rows 0;
19 syncs_before_row = Array.make max_rows 0;
20 bb = Array.init max_rows (fun _ -> Bytes.make max_cols '\000');
21 }
22
23let clear t =
24 t.num_rows <- 0;
25 t.free_row <- 0;
26 Array.fill t.bits_per_row 0 max_rows 0;
27 Array.fill t.syncs_before_row 0 max_rows 0;
28 Array.iter (fun b -> Bytes.fill b 0 max_cols '\000') t.bb
29
30let add_bit t bit =
31 if t.free_row < max_rows then begin
32 let row = t.free_row in
33 let bit_pos = t.bits_per_row.(row) in
34 if bit_pos < max_cols * 8 then begin
35 let byte_pos = bit_pos / 8 in
36 let bit_in_byte = 7 - (bit_pos mod 8) in
37 if bit <> 0 then begin
38 let b = Bytes.get_uint8 t.bb.(row) byte_pos in
39 Bytes.set_uint8 t.bb.(row) byte_pos (b lor (1 lsl bit_in_byte))
40 end;
41 t.bits_per_row.(row) <- bit_pos + 1;
42 if t.num_rows = 0 then t.num_rows <- 1
43 end
44 end
45
46let add_row t =
47 if t.bits_per_row.(t.free_row) > 0 && t.free_row + 1 < max_rows then begin
48 t.free_row <- t.free_row + 1;
49 t.num_rows <- t.num_rows + 1
50 end
51
52let add_sync t =
53 if t.bits_per_row.(t.free_row) > 0 then add_row t
54 else t.syncs_before_row.(t.free_row) <- t.syncs_before_row.(t.free_row) + 1
55
56let num_rows t = t.num_rows
57let bits_per_row t row = if row < max_rows then t.bits_per_row.(row) else 0
58
59let syncs_before_row t row =
60 if row < max_rows then t.syncs_before_row.(row) else 0
61
62let get_row t row = if row < max_rows then t.bb.(row) else Bytes.empty
63
64let get_bit t ~row ~bit =
65 if row < max_rows && bit < t.bits_per_row.(row) then
66 let byte_pos = bit / 8 in
67 let bit_in_byte = 7 - (bit mod 8) in
68 (Bytes.get_uint8 t.bb.(row) byte_pos lsr bit_in_byte) land 1
69 else 0
70
71let get_byte t ~row ~bit_offset =
72 if row >= max_rows then 0
73 else
74 let b1 = Bytes.get_uint8 t.bb.(row) (bit_offset / 8) in
75 let b2 =
76 if bit_offset / 8 + 1 < max_cols then
77 Bytes.get_uint8 t.bb.(row) ((bit_offset / 8) + 1)
78 else 0
79 in
80 let shift = bit_offset mod 8 in
81 ((b1 lsl shift) lor (b2 lsr (8 - shift))) land 0xff
82
83let extract_bytes t ~row ~bit_offset ~len =
84 let num_bytes = (len + 7) / 8 in
85 let result = Bytes.make num_bytes '\000' in
86 for i = 0 to num_bytes - 1 do
87 Bytes.set_uint8 result i (get_byte t ~row ~bit_offset:(bit_offset + (i * 8)))
88 done;
89 result
90
91let invert t =
92 for row = 0 to t.num_rows - 1 do
93 let num_bytes = (t.bits_per_row.(row) + 7) / 8 in
94 for i = 0 to num_bytes - 1 do
95 Bytes.set_uint8 t.bb.(row) i (lnot (Bytes.get_uint8 t.bb.(row) i) land 0xff)
96 done
97 done
98
99let nrzs_decode _t = () (* TODO *)
100let nrzm_decode _t = () (* TODO *)
101
102let manchester_decode _t ~row ~start ~max =
103 ignore (row, start, max);
104 (create (), 0)
105
106let differential_manchester_decode _t ~row ~start ~max =
107 ignore (row, start, max);
108 (create (), 0)
109
110let search t ~row ~start ~pattern ~pattern_bits =
111 ignore (pattern, pattern_bits);
112 if row < max_rows then t.bits_per_row.(row) else start
113
114let compare_rows t ~row_a ~row_b ~max_bits =
115 ignore max_bits;
116 if row_a >= max_rows || row_b >= max_rows then 1
117 else if t.bits_per_row.(row_a) <> t.bits_per_row.(row_b) then 1
118 else Bytes.compare t.bb.(row_a) t.bb.(row_b)
119
120let count_repeats t ~row ~max_bits =
121 let count = ref 1 in
122 for i = 0 to t.num_rows - 1 do
123 if i <> row && compare_rows t ~row_a:row ~row_b:i ~max_bits = 0 then
124 incr count
125 done;
126 !count
127
128let find_repeated_row t ~min_repeats ~min_bits =
129 let rec check row =
130 if row >= t.num_rows then None
131 else if
132 t.bits_per_row.(row) >= min_bits
133 && count_repeats t ~row ~max_bits:0 >= min_repeats
134 then Some row
135 else check (row + 1)
136 in
137 check 0
138
139let find_repeated_prefix t ~min_repeats ~min_bits =
140 find_repeated_row t ~min_repeats ~min_bits
141
142let parse t _code = clear t (* TODO: implement parsing *)
143
144let row_to_string t row =
145 if row >= t.num_rows then ""
146 else
147 let bits = t.bits_per_row.(row) in
148 let num_bytes = (bits + 7) / 8 in
149 let hex = Rtl433_util.bytes_to_hex t.bb.(row) num_bytes in
150 Printf.sprintf "{%d} %s" bits hex
151
152let pp fmt t =
153 Format.fprintf fmt "Bitbuffer: %d rows@." t.num_rows;
154 for i = 0 to t.num_rows - 1 do
155 Format.fprintf fmt " [%d] %s@." i (row_to_string t i)
156 done