Si4431 packet parsing script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0 R Device Type 0 0 0 dt[4] dt[3] dt[2] dt[1] dt[0] 111 | |
1 R Device Version 0 0 0 vc[4] vc[3] vc[2] vc[1] vc[0] 06h | |
2 R Device Status ffovfl ffunfl rxffem headerr reserved reserved cps[1] cps[0] - | |
3 R Interrupt Status 1 ifferr itxffafull itxffaem irxffafull iext ipksent ipkvalid icrcerror - | |
4 R Interrupt Status 2 iswdet ipreaval ipreainval irssi iwut ilbd ichiprdy ipor - | |
5 R/W Interrupt Enable 1 enfferr entxffafull entxffaem enrxffafull enext enpksent enpkvalid encrcerror 00h | |
6 R/W Interrupt Enable 2 enswdet enpreaval enpreainval enrssi enwut enlbd enchiprdy enpor 03h | |
7 R/W Operating & Function Control 1 swres enlbd enwt x32ksel txon rxon pllon xton 01h | |
8 R/W Operating & Function Control 2 antdiv[2] antdiv[1] antdiv[0] rxmpk autotx enldm ffclrrx ffclrtx 00h | |
9 R/W Crystal Oscillator Load Capacitance xtalshft xlc[6] xlc[5] xlc[4] xlc[3] xlc[2] xlc[1] xlc[0] 7Fh | |
0A R/W Microcontroller Output Clock Reserved Reserved clkt[1] clkt[0] enlfc mclk[2] mclk[1] mclk[0] 06h | |
0B R/W GPIO0 Configuration gpio0drv[1] gpio0drv[0] pup0 gpio0[4] gpio0[3] gpio0[2] gpio0[1] gpio0[0] 00h | |
0C R/W GPIO1 Configuration gpio1drv[1] gpio1drv[0] pup1 gpio1[4] gpio1[3] gpio1[2] gpio1[1] gpio1[0] 00h | |
0D R/W GPIO2 Configuration gpio2drv[1] gpio2drv[0] pup2 gpio2[4] gpio2[3] gpio2[2] gpio2[1] gpio2[0] 00h | |
0E R/W I/O Port Configuration Reserved extitst[2] extitst[1] extitst[0] itsdo dio2 dio1 dio0 00h | |
0F R/W ADC Configuration adcstart/adc-done adcsel[2] adcsel[1] adcsel[0] adcref[1] adcref[0] adcgain[1] adcgain[0] 00h | |
10 R/W ADC Sensor Amplifier Offset Reserved Reserved Reserved Reserved adcoffs[3] adcoffs[2] adcoffs[1] adcoffs[0] 00h | |
11 R ADC Value adc[7] adc[6] adc[5] adc[4] adc[3] adc[2] adc[1] adc[0] - | |
12 R/W Temperature Sensor Control tsrange[1] tsrange[0] entsoffs entstrim tstrim[3] tstrim[2] tstrim[1] tstrim[0] 20h | |
13 R/W Temperature Value Offset tvoffs[7] tvoffs[6] tvoffs[5] tvoffs[4] tvoffs[3] tvoffs[2] tvoffs[1] tvoffs[0] 00h | |
14 R/W Wake-Up Timer Period 1 Reserved Reserved Reserved wtr[4] wtr[3] wtr[2] wtr[1] wtr[0] 03h | |
15 R/W Wake-Up Timer Period 2 wtm[15] wtm[14] wtm[13] wtm[12] wtm[11] wtm[10] wtm[9] wtm[8] 00h | |
16 R/W Wake-Up Timer Period 3 wtm[7] wtm[6] wtm[5] wtm[4] wtm[3] wtm[2] wtm[1] wtm[0] 01h | |
17 R Wake-Up Timer Value 1 wtv[15] wtv[14] wtv[13] wtv[12] wtv[11] wtv[10] wtv[9] wtv[8] - | |
18 R Wake-Up Timer Value 2 wtv[7] wtv[6] wtv[5] wtv[4] wtv[3] wtv[2] wtv[1] wtv[0] - | |
19 R/W Low-Duty Cycle Mode Duration ldc[7] ldc[6] ldc[5] ldc[4] ldc[3] ldc[2] ldc[1] ldc[0] 00h | |
1A R/W Low Battery Detector Threshold Reserved Reserved Reserved lbdt[4] lbdt[3] lbdt[2] lbdt[1] lbdt[0] 14h | |
1B R Battery Voltage Level 0 0 0 vbat[4] vbat[3] vbat[2] vbat[1] vbat[0] - | |
1C R/W IF Filter Bandwidth dwn3_bypass ndec[2] ndec[1] ndec[0] filset[3] filset[2] filset[1] filset[0] 01h | |
1D R/W AFC Loop Gearshift Override afcbd enafc afcgearh[2] afcgearh[1] afcgearh[0] 1p5 bypass matap ph0size 40h | |
1E R/W AFC Timing Control swait_timer[1] swait_timer[0] shwait[2] shwait[1] shwait[0] anwait[2] anwait[1] anwait[0] 0Ah | |
1F R/W Clock Recovery Gearshift Override Reserved Reserved crfast[2] crfast[1] crfast[0] crslow[2] crslow[1] crslow[0] 03h | |
20 R/W Clock Recovery Oversampling Ratio rxosr[7] rxosr[6] rxosr[5] rxosr[4] rxosr[3] rxosr[2] rxosr[1] rxosr[0] 64h | |
21 R/W Clock Recovery Offset 2 rxosr[10] rxosr[9] rxosr[8] stallctrl ncoff[19] ncoff[18] ncoff[17] ncoff[16] 01h | |
22 R/W Clock Recovery Offset 1 ncoff[15] ncoff[14] ncoff[13] ncoff[12] ncoff[11] ncoff[10] ncoff[9] ncoff[8] 47h | |
23 R/W Clock Recovery Offset 0 ncoff[7] ncoff[6] ncoff[5] ncoff[4] ncoff[3] ncoff[2] ncoff[1] ncoff[0] AEh | |
24 R/W Clock Recovery Timing Loop Gain 1 Reserved Reserved Reserved rxncocomp crgain2x crgain[10] crgain[9] crgain[8] 02h | |
25 R/W Clock Recovery Timing Loop Gain 0 crgain[7] crgain[6] crgain[5] crgain[4] crgain[3] crgain[2] crgain[1] crgain[0] 8Fh | |
26 R Received Signal Strength Indicator rssi[7] rssi[6] rssi[5] rssi[4] rssi[3] rssi[2] rssi[1] rssi[0] - | |
27 R/W RSSI Threshold for Clear Channel Indicator rssith[7] rssith[6] rssith[5] rssith[4] rssith[3] rssith[2] rssith[1] rssith[0] 1Eh | |
28 R Antenna Diversity Register 1 adrssi1[7] adrssia[6] adrssia[5] adrssia[4] adrssia[3] adrssia[2] adrssia[1] adrssia[0] - | |
29 R Antenna Diversity Register 2 adrssib[7] adrssib[6] adrssib[5] adrssib[4] adrssib[3] adrssib[2] adrssib[1] adrssib[0] - | |
2A R/W AFC Limiter Afclim[7] Afclim[6] Afclim[5] Afclim[4] Afclim[3] Afclim[2] Afclim[1] Afclim[0] 00h | |
2B R AFC Correction Read afc_corr[9] afc_corr[8] afc_corr[7] afc_corr[6] afc_corr[5] afc_corr[4] afc_corr[3] afc_corr[2] 00h | |
2C R/W OOK Counter Value 1 afc_corr[9] afc_corr[9] ookfrzen peakdeten madeten ookcnt[10] ookcnt[9] ookcnt[8] 18h | |
2D R/W OOK Counter Value 2 ookcnt[7] ookcnt[6] ookcnt[5] ookcnt[4] ookcnt[3] ookcnt[2] ookcnt[1] ookcnt[0] BCh | |
2E R/W Slicer Peak Hold Reserved attack[2] attack[1] attack[0] decay[3] decay[2] decay[1] decay[0] 26h | |
2F Reserved | |
30 R/W Data Access Control enpacrx lsbfrst crcdonly skip2ph enpactx encrc crc[1] crc[0] 8Dh | |
31 R EzMAC status 0 rxcrc1 pksrch pkrx pkvalid crcerror pktx pksent - | |
32 R/W Header Control 1 bcen[3] bcen[2] bcen[1] bcen[0] hdch[3] hdch[2] hdch[1] hdch[0] 0Ch | |
33 R/W Header Control 2 skipsyn hdlen[2] hdlen[1] hdlen[0] fixpklen synclen[1] synclen[0] prealen[8] 22h | |
34 R/W Preamble Length prealen[7] prealen[6] prealen[5] prealen[4] prealen[3] prealen[2] prealen[1] prealen[0] 08h | |
35 R/W Preamble Detection Control preath[4] preath[3] preath[2] preath[1] preath[0] rssi_off[2] rssi_off[1] rssi_off[0] 2Ah | |
36 R/W Sync Word 3 sync[31] sync[30] sync[29] sync[28] sync[27] sync[26] sync[25] sync[24] 2Dh | |
37 R/W Sync Word 2 sync[23] sync[22] sync[21] sync[20] sync[19] sync[18] sync[17] sync[16] D4h | |
38 R/W Sync Word 1 sync[15] sync[14] sync[13] sync[12] sync[11] sync[10] sync[9] sync[8] 00h | |
39 R/W Sync Word 0 sync[7] sync[6] sync[5] sync[4] sync[3] sync[2] sync[1] sync[0] 00h | |
3A R/W Transmit Header 3 txhd[31] txhd[30] txhd[29] txhd[28] txhd[27] txhd[26] txhd[25] txhd[24] 00h | |
3B R/W Transmit Header 2 txhd[23] txhd[22] txhd[21] txhd[20] txhd[19] txhd[18] txhd[17] txhd[16] 00h | |
3C R/W Transmit Header 1 txhd[15] txhd[14] txhd[13] txhd[12] txhd[11] txhd[10] txhd[9] txhd[8] 00h | |
3D R/W Transmit Header 0 txhd[7] txhd[6] txhd[5] txhd[4] txhd[3] txhd[2] txhd[1] txhd[0] 00h | |
3E R/W Transmit Packet Length pklen[7] pklen[6] pklen[5] pklen[4] pklen[3] pklen[2] pklen[1] pklen[0] 00h | |
3F R/W Check Header 3 chhd[31] chhd[30] chhd[29] chhd[28] chhd[27] chhd[26] chhd[25] chhd[24] 00h | |
40 R/W Check Header 2 chhd[23] chhd[22] chhd[21] chhd[20] chhd[19] chhd[18] chhd[17] chhd[16] 00h | |
41 R/W Check Header 1 chhd[15] chhd[14] chhd[13] chhd[12] chhd[11] chhd[10] chhd[9] chhd[8] 00h | |
42 R/W Check Header 0 chhd[7] chhd[6] chhd[5] chhd[4] chhd[3] chhd[2] chhd[1] chhd[0] 00h | |
43 R/W Header Enable 3 hden[31] hden[30] hden[29] hden[28] hden[27] hden[26] hden[25] hden[24] FFh | |
44 R/W Header Enable 2 hden[23] hden[22] hden[21] hden[20] hden[19] hden[18] hden[17] hden[16] FFh | |
45 R/W Header Enable 1 hden[15] hden[14] hden[13] hden[12] hden[11] hden[10] hden[9] hden[8] FFh | |
46 R/W Header Enable 0 hden[7] hden[6] hden[5] hden[4] hden[3] hden[2] hden[1] hden[0] FFh | |
47 R Received Header 3 rxhd[31] rxhd[30] rxhd[29] rxhd[28] rxhd[27] rxhd[26] rxhd[25] rxhd[24] - | |
48 R Received Header 2 rxhd[23] rxhd[22] rxhd[21] rxhd[20] rxhd[19] rxhd[18] rxhd[17] rxhd[16] - | |
49 R Received Header 1 rxhd[15] rxhd[14] rxhd[13] rxhd[12] rxhd[11] rxhd[10] rxhd[9] rxhd[8] - | |
4A R Received Header 0 rxhd[7] rxhd[6] rxhd[5] rxhd[4] rxhd[3] rxhd[2] rxhd[1] rxhd[0] - | |
4B R Received Packet Length rxplen[7] rxplen[6] rxplen[5] rxplen[4] rxplen[3] rxplen[2] rxplen[1] rxplen[0] - | |
4C-4E Reserved | |
4F R/W ADC8 Control Reserved Reserved adc8[5] adc8[4] adc8[3] adc8[2] adc8[1] adc8[0] 10h | |
50-5F Reserved | |
60 R/W Channel Filter Coefficient Address Inv_pre_th[3] Inv_pre_th[2] Inv_pre_th[1] Inv_pre_th[0] chfiladd[3] chfiladd[2] chfiladd[1] chfiladd[0] 00h | |
61 Reserved | |
62 R/W Crystal Oscillator/Control Test pwst[2] pwst[1] pwst[0] clkhyst enbias2x enamp2x bufovr enbuf 24h | |
63-68 Reserved | |
69 R/W AGC Override 1 Reserved sgi agcen lnagain pga3 pga2 pga1 pga0 20h | |
6A-6C Reserved | |
6D R/W TX Power Reserved Reserved Reserved Reserved Ina_sw txpow[2] txpow[1] txpow[0] 18h | |
6E R/W TX Data Rate 1 txdr[15] txdr[14] txdr[13] txdr[12] txdr[11] txdr[10] txdr[9] txdr[8] 0Ah | |
6F R/W TX Data Rate 0 txdr[7] txdr[6] txdr[5] txdr[4] txdr[3] txdr[2] txdr[1] txdr[0] 3Dh | |
70 R/W Modulation Mode Control 1 Reserved Reserved txdtrtscale enphpwdn manppol enmaninv enmanch enwhite 0Ch | |
71 R/W Modulation Mode Control 2 trclk[1] trclk[0] dtmod[1] dtmod[0] eninv fd[8] modtyp[1] modtyp[0] 00h | |
72 R/W Frequency Deviation fd[7] fd[6] fd[5] fd[4] fd[3] fd[2] fd[1] fd[0] 20h | |
73 R/W Frequency Offset 1 fo[7] fo[6] fo[5] fo[4] fo[3] fo[2] fo[1] fo[0] 00h | |
74 R/W Frequency Offset 2 Reserved Reserved Reserved Reserved Reserved Reserved fo[9] fo[8] 00h | |
75 R/W Frequency Band Select Reserved sbsel hbsel fb[4] fb[3] fb[2] fb[1] fb[0] 75h | |
76 R/W Nominal Carrier Frequency 1 fc[15] fc[14] fc[13] fc[12] fc[11] fc[10] fc[9] fc[8] BBh | |
77 R/W Nominal Carrier Frequency 0 fc[7] fc[6] fc[5] fc[4] fc[3] fc[2] fc[1] fc[0] 80h | |
78 Reserved | |
79 R/W Frequency Hopping Channel Select fhch[7] fhch[6] fhch[5] fhch[4] fhch[3] fhch[2] fhch[1] fhch[0] 00h | |
7A R/W Frequency Hopping Step Size fhs[7] fhs[6] fhs[5] fhs[4] fhs[3] fhs[2] fhs[1] fhs[0] 00h | |
7B Reserved | |
7C R/W TX FIFO Control 1 Reserved Reserved txafthr[5] txafthr[4] txafthr[3] txafthr[2] txafthr[1] txafthr[0] 37h | |
7D R/W TX FIFO Control 2 Reserved Reserved txaethr[5] txaethr[4] txaethr[3] txaethr[2] txaethr[1] txaethr[0] 04h | |
7E R/W RX FIFO Control Reserved Reserved rxafthr[5] rxafthr[4] rxafthr[3] rxafthr[2] rxafthr[1] rxafthr[0] 37h | |
7F R/W FIFO Access fifod[7] fifod[6] fifod[5] fifod[4] fifod[3] fifod[2] fifod[1] fifod[0] - |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
import re | |
from enum import Enum | |
from collections import Iterable | |
from itertools import islice, izip | |
from decimal import Decimal | |
from bitarray import bitarray | |
from colorama import Fore, Back, Style | |
from math import modf | |
import sys | |
REGISTER_FILE = "datasheet-registers.txt" | |
#LOGIC_FILE = "startup-channel-9345.txt" | |
#LOGIC_FILE = "8050.txt" | |
class RegisterAccess(Enum): | |
Reserved = 0 | |
ReadOnly = 1 | |
ReadWrite = 2 | |
@classmethod | |
def from_str(cls, str): | |
if str == "R": | |
return cls.ReadOnly | |
elif str == "R/W": | |
return cls.ReadWrite | |
else: | |
return cls.Reserved | |
class Bit: | |
def __init__(self, name=None, bitfield_index=None): | |
self._name = name | |
self._bitfield_index = bitfield_index | |
@property | |
def name(self): | |
return self._name | |
@property | |
def is_reserved(self): | |
return not self.name | |
@property | |
def isPartOfBitfield(self): | |
return self._bitfield_index is not None | |
def __str__(self): | |
if self.isPartOfBitfield: | |
return "{0}[{1}]".format(self.name, self._bitfield_index) | |
elif self.name: | |
return self.name | |
else: | |
return "Reserved" | |
class Register: | |
def __init__(self, address, desc, access=RegisterAccess.Reserved, default=None, bits=[]): | |
self._address = "0x{:02X}".format(address) | |
self._access = access | |
self._desc = desc | |
self._bits = bits | |
self._data = None | |
if default: | |
self._data = default | |
def decode(self, t): | |
sys.stdout.write("\t") | |
assert len(list(izip(self.bits, t.data))) == 8 | |
for i, (b, d) in enumerate(izip(self.bits, t.data)): | |
if b.is_reserved: | |
sys.stdout.write("RESERVED") | |
else: | |
sys.stdout.write("{0}={1}".format(str(b), 1 if d else 0)) | |
if i < 7: | |
sys.stdout.write(", ") | |
sys.stdout.write("\n") | |
@property | |
def address(self): | |
return self._address | |
@property | |
def desc(self): | |
return self._desc | |
@property | |
def bits(self): | |
return self._bits | |
@property | |
def data(self): | |
return self._data | |
def __repr__(self): | |
return str(self._address) | |
class RegisterMap: | |
def __init__(self, registers=None): | |
self._registers = {r.address:r for r in registers} | |
def add(self, reg): | |
assert not reg.address in self._registers | |
self._registers[reg.address] = reg | |
def for_t(self, t): | |
address = "0x{:02X}".format(t.address) | |
return self._registers[address] | |
def decode(self, t): | |
self.for_t(t).decode(t) | |
@classmethod | |
def from_register_file(cls, filename): | |
registers = [] | |
with open(filename, "r") as fp: | |
for line in fp: | |
fields = [f.strip() for f in line.strip().split("\t")] | |
# Handle reserved registers (including ranges of reserved registers, e.g. 4C-4E) | |
if fields[1] == "Reserved": | |
if "-" in fields[0]: | |
low_address = int(fields[0][0:2], 16) | |
high_address = int(fields[0][3:5], 16) | |
else: | |
low_address = int(fields[0], 16) | |
high_address = low_address | |
# Iterate over the range of addresses covered by this line (in base 10) | |
for address in range(low_address, high_address + 1): | |
registers.append(Register(address, fields[1])) | |
continue | |
# Otherwise, handle an unreserved register | |
address = int(fields[0], 16) | |
access = RegisterAccess.from_str(fields[1]) | |
desc = fields[2] | |
bits = [] | |
for i in range(3, 11): | |
if fields[i] == "Reserved": | |
bits.append(Bit()) | |
else: | |
# Match the bit name, and optional a bitfield index | |
match = re.match(r"([^\[]+)(?:\[(\d+)\])?", fields[i]) | |
assert match | |
bits.append(Bit(name=match.group(1), bitfield_index=match.group(2))) | |
# Default value | |
default_raw = fields[11] | |
default = None | |
if not "-" in default_raw: | |
if "h" in default_raw: | |
# It's hex | |
default = bitarray(format(int(default_raw.translate(None, "h"), 16), "b")) | |
else: | |
# It's binary | |
default = bitarray(default_raw) | |
# Pad to a full byte. e.g. 111 becomes 00000111 | |
for _ in range(0, 8 - len(default)): | |
default.insert(0, 0) | |
registers.append(Register(address, desc, access=access, bits=bits, default=default)) | |
return cls(registers) | |
class Transaction: | |
def __init__(self, time_sec, address, is_write, data): | |
self._time_sec = time_sec | |
self._address = address | |
self._is_write = is_write | |
self._data = data | |
@property | |
def time_sec(self): | |
return self._time_sec | |
@property | |
def address(self): | |
return self._address | |
@property | |
def is_write(self): | |
return self._is_write | |
@property | |
def data(self): | |
return self._data | |
@classmethod | |
def from_capture_line(cls, line): | |
fields = [f.strip() for f in re.split(r"[,:;]", line)] | |
# 'fields' contains something like: | |
# ['2.002989291666667', 'SPI', 'MOSI', '0b 0000 0011 1111 1111', 'MISO', '0b 0000 0000 0010 0000'] | |
time_sec = Decimal(fields[0]) | |
# Split the MOSI data field by whitespace. We end up with 5 fields, with the first being useless (0b). | |
assert fields[1] == "SPI" and fields[2] == "MOSI" | |
mosi = bitarray("".join(fields[3].split()[1:])) | |
is_write = mosi[0] | |
address = int(mosi[1:8].to01(), 2) | |
if is_write: | |
data = mosi[8:16] | |
# Split the MISO data field by whitespace. We end up with 5 fields, with the first three being useless - the first is (0b), while the | |
# second and third are all garbage zeros. The actual data the slave responds with doesn't start until the master has finished transmitting the address, obviously. | |
assert fields[4] == "MISO" | |
if not is_write: | |
data = bitarray("".join(fields[5].split()[3:])) | |
return cls(time_sec, address, is_write, data) | |
def transaction_bursts(transactions, threshold=0.01): | |
last_time = None | |
burst_start = None | |
ret = [] | |
for t in transactions: | |
if not burst_start: | |
burst_start = t.time_sec | |
# If this transaction occurred at least 'threshold' after the previous packet, | |
# declare the burst over and return it. This packet begins the next burst. | |
if last_time and (t.time_sec - last_time > threshold): | |
yield (burst_start, ret) | |
ret = [] | |
burst_start = t.time_sec | |
last_time = t.time_sec | |
ret.append(t) | |
if ret: | |
yield (burst_start, ret) | |
import sys | |
def main(): | |
registers = RegisterMap.from_register_file(REGISTER_FILE) | |
# Now time to parse the capture file | |
transactions = [] | |
with open(sys.argv[1], "r") as fp: | |
# Skip the header line | |
for line in islice(fp, 1, None): | |
transactions.append(Transaction.from_capture_line(line)) | |
# Divide the transactions into bursts, i.e. clusters of transactions without long pauses in between. | |
# Note: This is NOT the same concept of "bursts" as described in the datasheet. | |
for b in transaction_bursts(transactions): | |
burst_start = b[0] | |
print Style.BRIGHT + Back.GREEN + "Burst beginning at {0}s, length {1} packet(s)".format(burst_start, len(b[1])) + Style.RESET_ALL | |
for t in b[1]: | |
print "{0} {1}".format(Back.RED + "write" + Style.RESET_ALL if t.is_write else Back.BLUE + "read" + Style.RESET_ALL, registers.for_t(t).desc) | |
registers.decode(t) | |
print "\n" | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment