<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Use single bit comparisons to RE a standard crc16
Step1:  crc diffs from observed omnipod single bit diff messages
Indexed by position of bit diff
Step2:  Look at single bit diffs with unshift_xor()
<ASSISTANT_TASK:>
Python Code:
poly = 0x104c1
crc = crcmod.mkCrcFun(poly, initCrc=0, xorOut=0x0, rev=False)
# Now consider two CRC values obtained from two 1-bit messages, 
# where the 1 bits are in adjacent positions. The resulting CRCs 
# will differ by just one shift-xor cycle. To be precise, if
# C1 corresponds to the message with a 1 in position i, and
# C2 corresponds to the message with a 1 in position i+1, then 
# C1 is derived from applying one shift-xor cycle to C2. 
# (If this seems backwards, it's because the further the 1 
# bit is from the end of the message, the more shift-xor cycles
# get applied to the CRC.)
# The unshift_xor() function tries to reverse a shift-xor cycle
def unshift_xor(a,b):
    return ((b << 1) ^ a) & 0xffff
def view_diff(msg, bit_to_flip):
    m1 = msg.decode('hex')
    m2 = flip_bit(m1,bit_to_flip)
    diff = xor_strings(m1,m2)
    print "m1   %s, crc = %s" % (bin_str(m1), bin_short(crc(m1)))
    print "m2   %s, crc = %s" % (bin_str(m2), bin_short(crc(m2)))
    print "diff %s, crc = %s" % (bin_str(diff), bin_short(crc(diff)))
    crc_diff = crc(m1) ^ crc(m2)
    print "                                xored crcs = %s" % bin_short(crc_diff)
    return crc_diff
msg = "deadbeef"
crc_diff4 = view_diff(msg, 4)
print "=" * 80
crc_diff5 = view_diff(msg, 5)
print "=" * 80
crc_diff6 = view_diff(msg, 6)
print "unshift_xor at 4 = %s" % bin_short(unshift_xor(crc_diff4, crc_diff5) & 0xffff)
print "unshift_xor at 5 = %s" % bin_short(unshift_xor(crc_diff5, crc_diff6) & 0xffff)
print "original poly    = %s" % bin_short(poly & 0xffff)
Explanation: Use single bit comparisons to RE a standard crc16
End of explanation
# The entry at crc_diff_dict[10][30] means we observed two 10 bytes messages that differed
# only by a single bit, and the value at that entry is the xor of their crcs. 
crc_diff_dict = {
    # Messages of length 10
    10: {
        30: 0b1000001010011011,
        31: 0b1000000101010001,
        34: 0b0000001110010010,
        35: 0b1000001100101000,
        36: 0b0000001100001110,
        37: 0b1000000100001011,
    },
    
    # Messages of length 3
    3: {
        30:  0b1000000100011111,
        31:  0b0000001000111100,
        34:  0b1000001011011100,
        35:  0b0000000111010111,
        36:  0b1000001101011001,
        37:  0b1000000000011000,
        38:  0b1000001010001110,
        116: 0b0000000000101010,
        117: 0b0000000011011010,
    }
}
Explanation: crc diffs from observed omnipod single bit diff messages
Indexed by position of bit diff
End of explanation
def crc_for(l,n):
    crc = crc_diff_dict[l][n]
    #crc = single_bit_crcs10[n]
    
    # collapse 5 bit 'hole'
    #crc = (crc & 0b1111111111) + ((crc >> 5) & 0b10000000000)
        
    # Drop separate high bit
    #crc = crc & 0b1111111111
    
    # Just look at lowest 8 bits
    #crc = crc & 0b11111111
    
    return crc
def diff_at(l,n):
    return unshift_xor(crc_for(l,n), crc_for(l,n+1))
def show_diff_at(l,n):
    print "crc(%d) = %s" % (n, bin_short(crc_for(l,n)))
    print "crc(%d) = %s" % (n+1, bin_short(crc_for(l,n+1)))
    d = diff_at(l,n)
    print "unshift_xor at %d = %s" % (n, bin_short(d))
    print "=" * 80
show_diff_at(3,30)
show_diff_at(3,34)
show_diff_at(3,35)
show_diff_at(3,36)
show_diff_at(3,37)
show_diff_at(3,116)
print "*" * 80
show_diff_at(10,30)
show_diff_at(10,34)
show_diff_at(10,35)
show_diff_at(10,36)
#df = pd.DataFrame(map(lambda x: [x, diff_at(x)], [30, 34, 35, 36]), columns=['idx', 'crc'])
#df.plot.scatter(x='idx', y='crc', marker='.')
Explanation: Look at single bit diffs with unshift_xor()
End of explanation
<END_TASK>