2323import sigrokdecode as srd
2424from functools import reduce
2525from enum import Enum
26+ from dshot .protoDshot import DshotCmd , DshotTelem , BitDshot , DshotSettings , Bit_DshotTelem
27+
2628
27- gcr_tables = {
28- "0b11001" : 0x0 ,
29- "0b11011" : 0x1 ,
30- "0b10010" : 0x2 ,
31- "0b10011" : 0x3 ,
32- "0b11101" : 0x4 ,
33- "0b10101" : 0x5 ,
34- "0b10110" : 0x6 ,
35- "0b10111" : 0x7 ,
36- "0b11010" : 0x8 ,
37- "0b1001" : 0x9 ,
38- "0b1010" : 0xa ,
39- "0b1011" : 0xb ,
40- "0b11110" : 0xc ,
41- "0b1101" : 0xd ,
42- "0b1110" : 0xe ,
43- "0b1111" : 0xf
44- }
4529
4630class SamplerateError (Exception ):
4731 pass
4832
4933class State (Enum ):
50- CMD = 1 ,
34+ RESET = 0
35+ CMD = 1
5136 TELEM = 2
5237
38+
5339class State_Telem (Enum ):
54- START = 1 ,
40+ RESET = 0
41+ START = 1
5542 RECV = 2
5643
44+
5745class Decoder (srd .Decoder ):
5846 api_version = 3
5947 id = 'dshot'
@@ -106,200 +94,100 @@ def __init__(self):
10694
10795 def reset (self ):
10896 self .state = State .CMD
109- self .samplerate = None
110-
111- self .debug = False
97+ self .state_telem = State_Telem .START
11298
99+ self .samplerate = None
113100 self .inreset = False
114- self .bidirectional = False
115- self .dshot_kbaud = 300e3
116- self .dshot_period = 3.33e-6
117- self .actual_period = None
118- self .halfbitwidth = None
119101 self .currbit_ss = None
120102 self .currbit_es = None
121- self .samples_after_motorcmd = None
122- self .samples_pp = None
123103
124- self .telem_start = None
125- self .state_telem = State_Telem .START
126- self .telem_baudrate_midpoint = 0
127- self .edt_force = False
104+ self .dshot_cfg = DshotSettings ()
105+
106+ self .debug = False
128107
129108 def start (self ):
130- self .bidirectional = True if self .options ['bidir' ] == 'True' else False
131- self .edt_force = True if self .options ['edt_force' ] == 'True' else False
132- self .dshot_kbaud = int (self .options ['dshot_rate' ])* 1000
133- self .dshot_period = 1 / self .dshot_kbaud
134- self .samples_pp = int (self .samplerate * self .dshot_period )
135- self .samples_after_motorcmd = self .samples_pp * 3
136- self .samples_after_telempkt = self .samples_pp * 3
109+ self .dshot_cfg .bidirectional = True if self .options ['bidir' ] == 'True' else False
110+ self .dshot_cfg .edt_force = True if self .options ['edt_force' ] == 'True' else False
111+ self .dshot_cfg .dshot_kbaud = int (self .options ['dshot_rate' ])* 1000
112+ self .dshot_cfg .samplerate = self .samplerate
113+ self .dshot_cfg .update ()
137114
138115 self .out_ann = self .register (srd .OUTPUT_ANN )
139- self .telem_baudrate_midpoint = int ((self .samplerate / (self .dshot_kbaud * (5 / 4 ))) / 2.0 )
140- if self .debug :
141- print ("telem_midpoint" ,self .telem_baudrate_midpoint )
142116
143117 def metadata (self , key , value ):
144118 if key == srd .SRD_CONF_SAMPLERATE :
145119 self .samplerate = value
146120
147- def handle_bits_dshot (self , results ):
148- #ss, es, bit
149- bits = [result [2 ] for result in results ]
150-
151- if len (bits ) == 16 :
152- dshot_value = int (reduce (lambda a , b : (a << 1 ) | b , bits [:11 ]))
153- telem_request = bits [11 ]
154- received_crc = int (reduce (lambda a , b : (a << 1 ) | b , bits [12 :]))
155-
156- value_tocrc = int (reduce (lambda a , b : (a << 1 ) | b , bits [:12 ]))
157-
158- if self .bidirectional :
159- calculated_crc = int ((~ (value_tocrc ^ (value_tocrc >> 4 ) ^ (value_tocrc >> 8 )))& 0x0F )
160- else :
161- calculated_crc = int (((value_tocrc ^ (value_tocrc >> 4 ) ^ (value_tocrc >> 8 )))& 0x0F )
162-
163- if received_crc == calculated_crc :
164- crc_ok = True
165- else :
166- crc_ok = False
167-
168- # TODO: Align this correctly
169- crc_startsample = results [12 ][0 ]
170-
171- # Split annotation based on value type
172- if dshot_value < 48 :
173- # Command
174- self .put (results [0 ][0 ], crc_startsample , self .out_ann ,
175- [1 , ['%04d' % dshot_value ]])
176- else :
177- # Throttle
178- self .put (results [0 ][0 ], crc_startsample , self .out_ann ,
179- [2 , ['%04d' % dshot_value ]])
180-
181- self .put (crc_startsample , results [15 ][1 ], self .out_ann , [3 , ['Calc CRC: ' + ('%04d' % calculated_crc )+ ' TXed CRC:' + ('%04d' % received_crc )]])
182- if not crc_ok :
183- self .put (crc_startsample , results [15 ][1 ], self .out_ann ,
184- [4 , ['CRC INVALID' ]])
185- return True
186- else :
187- return False
188-
189- def handle_bit_dshot (self , ss , es , nb_ss ):
190- period = nb_ss - ss
191- duty = es - ss
192- # Ideal duty for T0H: 33%, T1H: 66%.
193- bit_ = (duty / period ) > 0.5
194-
195- self .put (ss , nb_ss , self .out_ann ,
196- [0 , ['%d' % bit_ ]])
197- return [ss ,nb_ss ,bit_ ]
198-
199- def handle_telem_bit (self ,matched ):
200- # None to raise exception if no match
201- result = None
202-
203- # Low
204- if matched == (True , False ):
205- # 0 value
206- result = 0
207-
208- # High
209- elif matched == (False , True ):
210- # 1 value
211- result = 1
212- return result
213-
214- def process_telem_erpm (self ,packet ,start ,end ):
215- # Raw packet
216- self .put (start ,
217- end , self .out_ann ,
218- [6 , ['%23s' % bin (packet )]])
219- # XOR with next?
220- packet &= 0x0FFFFF
221- packet = (packet ^ (packet >> 1 ))
222- self .put (start ,
223- end , self .out_ann ,
224- [7 , ['%23s' % bin (packet )]])
225- # Undo GCR
226- output = 0b0
227-
228- nibbles = 4
229- bitmask = 0b11111 << ((nibbles - 1 )* 5 )
230-
231- for n in range (nibbles ):
232- gcr_n = bitmask & packet
233- ungcr = gcr_tables [bin (gcr_n >> (nibbles - (n + 1 )) * 5 )]
234- output = (output << 4 ) | ungcr
235- bitmask = (bitmask >> 5 )
236-
237- # Compare CRC
238- crc_received = output & 0xF
239- output = (output >> 4 ) & 0xFFF
240- crc_calc = ~ ((output ^ (output >> 4 ) ^ (output >> 8 ))) & 0x0F
241-
242-
243-
244- self .put (end - ((self .telem_baudrate_midpoint * 2 )* 4 ),
245- end , self .out_ann ,
246- [7 , ['%23s' % ("RX CRC: " + hex (crc_received )+ " Calc CRC: " + hex (crc_calc ))]])
247- if crc_calc != crc_received :
248- self .put (end - ((self .telem_baudrate_midpoint * 2 ) * 4 ),
249- end , self .out_ann ,
250- [8 , ['%23s' % ("CRC ERROR!" )]])
251- # The upper 12 bit contain the eperiod (1/erps) in the following bitwise encoding:
252- #
253- # e e e m m m m m m m m m
254- #
255- # The 9 bit value M needs to shifted left E times to get the period in micro seconds.
256- # This gives a range of 1 us to 65408 us. Which translates to a min e-frequency of 15.29 hz or for 14 pole motors 3.82 hz.
257- return
258- def process_telem_edt (self ,packet ,start ,end ):
259-
260- return
261- def process_telem (self ,packet ,start ,end ):
262- if self .edt_force :
263- self .process_telem_edt (packet ,start ,end )
264- else :
265- self .process_telem_erpm (packet ,start ,end )
266121
267- return
268122
123+ def display_dshot (self ,dshot ):
124+ crc_startsample = dshot .results [12 ].ss
125+
126+ # Split annotation based on value type
127+ if dshot .dshot_value < 48 :
128+ # Command
129+ self .put (dshot .results [0 ].ss , crc_startsample , self .out_ann ,
130+ [1 , ['%04d' % dshot .dshot_value ]])
131+ else :
132+ # Throttle
133+ self .put (dshot .results [0 ].ss , crc_startsample , self .out_ann ,
134+ [2 , ['%04d' % dshot .dshot_value ]])
135+
136+ self .put (crc_startsample , dshot .results [15 ].es , self .out_ann ,
137+ [3 , ['Calc CRC: ' + ('%04d' % dshot .crc_calc ) + ' TXed CRC:' + ('%04d' % dshot .crc_recv )]])
138+ if not dshot .crc_ok :
139+ self .put (crc_startsample , dshot .results [15 ].es , self .out_ann ,
140+ [4 , ['CRC INVALID' ]])
269141
270142
143+ def complete_DshotBit (self , * args ):
144+ bitseq = BitDshot (* args )
145+
146+ self .put (bitseq .ss , bitseq .es , self .out_ann ,
147+ [0 , ['%d' % bool (bitseq )]])
148+ return [bitseq ]
271149
272150 def decode (self ):
273151 if not self .samplerate :
274152 raise SamplerateError ('Cannot decode without samplerate.' )
275-
153+
154+ dshot_value = DshotCmd (self .dshot_cfg )
155+ telem_value = DshotTelem (self .dshot_cfg )
276156 results = []
277157 telem = 0b0
278158 tlm_start = 0
159+
160+ #bitseq = BitDshot()
279161 while True :
280162
281163 match self .state :
282164 case State .CMD :
283- if not self .bidirectional :
284- pins = self .wait ([{0 : 'r' }, {0 : 'f' }, {'skip' : self .samples_after_motorcmd }])
165+ if not self .dshot_cfg . bidirectional :
166+ pins = self .wait ([{0 : 'r' }, {0 : 'f' }, {'skip' : self .dshot_cfg . samples_after_motorcmd }])
285167 else :
286- pins = self .wait ([{0 : 'f' }, {0 : 'r' }, {'skip' : self .samples_after_motorcmd }])
168+ pins = self .wait ([{0 : 'f' }, {0 : 'r' }, {'skip' : self .dshot_cfg . samples_after_motorcmd }])
287169 #TODO: Increase skip to maximum time for effiency
288170 #TODO: Mark any changes in this time as errors? Option to reduce load?
289171
290172 if self .currbit_ss and self .currbit_es and self .matched [2 ]:
291173 # Assume end of packet if have seen start and end of a potential bit but no further change within 3 periods
292174 # TODO: Confirm wait period this works with spec
293- results += [self .handle_bit_dshot (self .currbit_ss , self .currbit_es ,
294- (self .currbit_ss + self .samples_pp ))]
175+
176+ args = self .currbit_ss , self .currbit_es , (self .currbit_ss + self .dshot_cfg .samples_pp )
177+ results += self .complete_DshotBit (* args )
295178 self .currbit_ss = None
296179 self .currbit_es = None
297-
180+ #print(results)
298181 # Pass results to decoder
299- result = self .handle_bits_dshot (results )
300- if result and self .bidirectional :
182+
183+ result = dshot_value .handle_bits_dshot (results )
184+ if result :
185+ self .display_dshot (dshot_value )
186+ if result and self .dshot_cfg .bidirectional :
301187 self .state = State .TELEM
188+
302189 results = []
190+ #dshot_value = DshotCmd(self.dshot_cfg)
303191
304192 if self .matched [0 ] and not self .currbit_ss and not self .currbit_es :
305193 # Start of bit
@@ -309,13 +197,17 @@ def decode(self):
309197 self .currbit_es = self .samplenum
310198 elif self .matched [0 ] and self .currbit_es and self .currbit_ss :
311199 # Have complete bit, can handle bit now
312- result = [ self .handle_bit_dshot ( self . currbit_ss , self .currbit_es , self .samplenum )]
313- # print(result )
314- results += result
200+ args = self .currbit_ss , self .currbit_es , self .samplenum
201+ results += self . complete_DshotBit ( * args )
202+
315203 self .currbit_ss = self .samplenum
316204 self .currbit_es = None
317205 case State .TELEM :
318206 match self .state_telem :
207+ case State_Telem .RESET :
208+ telem = 0b0
209+ self .state_telem = State_Telem .START
210+
319211 case State_Telem .START :
320212 # First wait for falling edge (idle high)
321213 pins = self .wait ([{0 : 'f' }])
@@ -327,35 +219,36 @@ def decode(self):
327219 case State_Telem .RECV :
328220 # First conditions skips half bit width and matches low
329221 # Second condition skips half bit width and matches high
330- pins = self .wait ([{0 : 'l' , 'skip' : self .telem_baudrate_midpoint },
331- {0 : 'h' , 'skip' : self .telem_baudrate_midpoint }])
222+ pins = self .wait ([{0 : 'l' , 'skip' : self .dshot_cfg . telem_baudrate_midpoint },
223+ {0 : 'h' , 'skip' : self .dshot_cfg . telem_baudrate_midpoint }])
332224
333225 # Append next bit
334- curr_bit = self .handle_telem_bit (self .matched )
335- self . put ( self . samplenum - self .telem_baudrate_midpoint ,
336- self .samplenum + self . telem_baudrate_midpoint ,
226+ args = ( self .samplenum - self . dshot_cfg . telem_baudrate_midpoint ), self . samplenum , (self .samplenum + self . dshot_cfg . telem_baudrate_midpoint )
227+ curr_bit = Bit_DshotTelem ( * args , self .matched )
228+ self .put ( curr_bit . ss , curr_bit . es ,
337229 self .out_ann ,
338- [5 , ['%04d' % curr_bit ]])
339- telem = telem | curr_bit
230+ [5 , ['%04d' % curr_bit .bit_ ]])
231+
232+ telem_value .add_bit (curr_bit )
233+
340234
341235 # Skip half bitwidth to end of bit
342- pins = self .wait ([{'skip' : self .telem_baudrate_midpoint }])
236+ pins = self .wait ([{'skip' : self .dshot_cfg . telem_baudrate_midpoint }])
343237
344- if telem . bit_length () >= 20 - 1 :
345- self .process_telem (telem , tlm_start , self . samplenum )
238+ if telem_value . bits . bit_length () >= 20 :
239+ telem_value .process_telem ()
346240
347241 # Reset
348- telem = 0b0
349- self . state_telem = State_Telem . START
242+ self . state_telem = State_Telem . RESET
243+ # Except Dshot packet next
350244 self .state = State .CMD
351- else :
352- # Shift for next bit
353- telem = telem << 1
245+
354246 # If not mark as error
355247
356248 # Then skip x samples and sample
357249 # Repeat for 21 bits (TBC)
358250
251+
359252 #TODO: What happens if it gets stuck in the wrong state?
360253
361254
0 commit comments