Shop OBEX P1 Docs P2 Docs Learn Events
Parallax RF Transceiver object using polynomial CRC method to reduce Data Error — Parallax Forums

Parallax RF Transceiver object using polynomial CRC method to reduce Data Error

Beau SchwabeBeau Schwabe Posts: 6,566
edited 2014-01-09 01:10 in Propeller 1
Updated latest version here


This is in the early stages, but I wanted to put a CRC object together that provided a little bit stronger
error detection than just addition or XORing each data byte on top of the adjacent neighbour. So I've put together a CRC engine that uses polynomials written in Propeller assembly. Once you tell it the size of the packet and where the Data is as well as the Polynomial key it will automatically update a CRC result that can be appended to your data transmission. On the receive end, if you have the same Polynomial CRC Driver running you must use the same Polynomial key. By comparing the Received CRC value verses the locally calculated CRC value, you can determine if a data packet is valid or not.

The main CORE program is called 'CRC_polynomial.Spin' ... There are a couple of example RX and TX programs that use the RF Transceivers, they still need some work but get the idea across. The main program to try to incorporate and use is the 'CRC_polynomial.Spin' file.


What is CRC anyway? A CRC is an error-detecting code. It stands for cyclic redundancy check and there are many forms of CRC. Some have very weak error detecting abilities while others have very strong error detecting abilities. An example of a week error detection scheme would be the use of the Parity bit in serial transmission. In this form The Parity simply represents the total number of Bits indicating if they are ODD or EVEN. Another approach is simply to ADD all of the data bytes together and look at the LSB 8-Bits of the result. With each of the approaches mentioned, the error detecting gradually gets better and better, you still can't beat polynomial division when it comes to CRC. The computation resembles a long division operation in which the quotient is discarded and the remainder becomes the result, with the important distinction that the arithmetic used is the carry-less arithmetic.

Example:




  Polynomial (Must match the sender as well as the receiver)
  
  │
  &#61602;         10101100  <-- quotient (discarded we don't care what this value is)
      _______________  
11011 ) 111001010000  <-- dividend (The DATA)
        11011
        -----
          1111010000
          11011
          -----
            10110000
            11011
            -----
             1101000
             11011
             -----
                 100  <-- CRC (remainder or what we append to the data sent)
 







Edit: Files updated on 11-05-2009

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe

IC Layout Engineer
Parallax, Inc.

Post Edited (Beau Schwabe (Parallax)) : 4/7/2010 8:42:02 PM GMT

Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2009-11-04 09:57
    If someone is interested in more background info, I just found that: http://www.ross.net/crc/download/crc_v3.txt
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-04 16:14
    Thanks MagIO2, that's actually one of the articles I referenced when putting the code together.

    Here is the SPIN version of the CORE program before I converted it to Spin so you can get a better idea of what the Propeller assembly is doing...

    [b]CON[/b]
    
      [b]_CLKMODE[/b] = [b]XTAL1[/b] + [b]PLL16X[/b]
      [b]_XINFREQ[/b] = 5_000_000
    
      PacketLength  = 2
    
      Polinomial = %11011
    
    [b]OBJ[/b]
    
    Term        : "FullDuplexSerial.Spin"
    
    [b]VAR[/b]
    
    [b]byte[/b]  Dividend[noparse][[/noparse]&#173;PacketLength&#093; 'Data to Send
    
    [b]PUB[/b] MOD_Test|TempPoly,TempDividend,BitPoly,BitDividend,Remainder,Index
    
        Term.start(31, 30, 0, 38400)                        '' Initialize serial communication to the PC
    
        Dividend[noparse][[/noparse]&#173;0&#093; := %00001110    'MSB
        Dividend[noparse][[/noparse]&#173;1&#093; := %01010000    'LSB      0100
    
    '    Dividend[noparse][[/noparse]&#173;0&#093; := %00000001    'MSB
    '    Dividend[noparse][[/noparse]&#173;1&#093; := %00100111    'LSB       1110
    
    
        TempPoly := Polinomial
        TempDividend := Dividend[noparse][[/noparse]&#173;0&#093;
        BitPoly := BitCount(TempPoly)
    
        BitDividend := BitCount(TempDividend)
    '------------------------------------------------------
        [b]repeat[/b] Index [b]from[/b] 1 to PacketLength
          [b]repeat[/b] [b]while[/b] BitDividend => BitPoly
            TempPoly := Polinomial << (BitDividend-BitPoly)
            TempDividend ^= TempPoly
            BitDividend := BitCount(TempDividend)
          TempDividend := TempDividend << 8 | Dividend[noparse][[/noparse]&#173;Index&#093;
          BitDividend := BitCount(TempDividend)
     '------------------------------------------------------       
        Remainder := TempDividend >> 8         
    
    
    '--------------------------------------------------Display Output Data---------------------------------------------
        [b]repeat[/b]
    
          Term.tx(1)                                        ' Position cursur at the Home position
          Term.bin(Remainder,32)
          Term.[b]str[/b]([b]string[/b]("   "))
          Term.tx(13)                                       ' Send the RETURN key code to the DEBUG terminal
    
    
    [b]PUB[/b] BitCount(Number)|BitWeight                          'Get number of bits in Number
        [b]Result[/b] := 0
        BitWeight := 1
        [b]repeat[/b] [b]while[/b] BitWeight =< Number      
          [b]Result[/b]++
          BitWeight <<= 1
    
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • MagIO2MagIO2 Posts: 2,243
    edited 2009-11-04 20:00
    What about having a small table for the BitCount? bc[noparse][[/noparse] 0 ] to bc[noparse][[/noparse] 15 ] which stores the number of bits for a nibble.
    Then you can hardcode the summation of bits like:
    result += bc[noparse][[/noparse] number & $f ]
    result += bc[noparse][[/noparse] number>>4 & $f]
    .... 
    

    .... oh .... now I understand ... it does not give you the number of bits, but the position of the highest bit. Anyway ... BitCount is called several times, so it would be good to optimize.

    That sounds like a binary search would be helpful as for searching in·32 items·it only needs 5 steps.
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-04 20:40
    MagIO2,

    I think number of bits is correct, it's not the number of bits that are "1", it's the total minimal number of bits required to represent the number.

    Anyway, yes, I think a lookup table would work here, and there's probably a simple formula that could be applied that I just haven't messed with in detail yet instead of the iterative process I'm currently using.

    The main thing I was looking for was functionality with the idea in mind that the bandwidth of the RF is limited to about 10kBits/sec. With that in mind there is plenty of overhead when calculating the CRC value.


    Edit...

    This code reduces the iteration loop from 20 clocks to 12 clocks almost in half

    '-------------------------Detect Bit Width---------------------------------------------------------              
    DetectWidth            't1 contains the value we want to know the bit width of
                  cmp       t1,                     #0      wz
                  mov       _BitWidth,              #0              
            if_z  jmp       #DetectWidth_ret
                  mov       t2,                     #0
    '-----------------------------------------------------------------------------------------                 
    WidthLoop     rol       t1,                     #1      wc
                  add       t2,                     #1                   'increment BitWidth
            if_nc jmp       #WidthLoop             
    '-----------------------------------------------------------------------------------------
                  mov       _BitWidth,              #32
                  sub       _BitWidth,              t2
    DetectWidth_ret         ret
                            '_BitWidth returns the bit width
    '--------------------------------------------------------------------------------------------------
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/4/2009 10:32:23 PM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-05 08:24
    Files have been updated to incorporate Pseudo random packet data with CRC being generated on the fly.

    Revision History:
    Version 1.0.a - (10-30-2009) original file created
    Version 1.0.b - (11-04-2009) improved bit width routine
    Version 1.0.c - (11-05-2009) optimized flow of long division routine



    MagIO2,

    I have been thinking, and I'm not sure that a lookup table would help much since the BitWidth could technically be close to 16 Bits wide and higher as I change the program to allow polynomial keys wider than 16 Bits.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • jazzedjazzed Posts: 11,803
    edited 2009-11-05 10:47
    Hi Beau,

    You've probably been down the CRC16 table road, but it is worth mentioning.

    Here's a link to my table driven CCITT CRC16 Spin code. The table used is 512 bytes.
    http://forums.parallax.com/showthread.php?p=800272

    Of course CRC also has limits. CRC16 can have duplicate results for < 1000 byte packets.
    For packets > 1000 bytes, CRC32 should be considered.

    Wikipedia has a good article on CRC en.wikipedia.org/wiki/Cyclic_redundancy_check

    Cheers.

    Post Edited (jazzed) : 11/5/2009 10:52:11 AM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-05 15:59
    jazzed,

    I looked at your code, but I don't see the same polynomial listed here

    wikipedia shows that for CRC16 CCITT that the polynomial should be...

     16    12    5 
    x   + x   + x  + 1 
    
    ... or ...
    
    %10001000000100001
    
    






    I would be curious to compare the result of your CRC program with the program that I am running against the same 512 Byte data packet.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/5/2009 6:22:14 PM GMT
  • RaymanRayman Posts: 14,662
    edited 2009-11-05 16:04
    I've got CRC16 working in my YMODEM code...

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    My Prop Info&Apps: ·http://www.rayslogic.com/propeller/propeller.htm
  • jazzedjazzed Posts: 11,803
    edited 2009-11-05 16:33
    Beau Schwabe (Parallax) said...
    jazzed,

    I looked at your code, but I don't see the same polynomial listed
    ...
    Yes I find that all confusing too. There are different polynomials.
    The one in my code is CRC-CCITT. Using CRC16 poly instead works too.

    CRC-16         0x8005  x16 + x15 + x2 + 1
    CRC-CCITT    0x1021  x16 + x12 + x5 + 1
    
    
    


    You can test your results here to see if the CRC is as expected.
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-05 18:13
    jazzed,

    Ok, I understand there are several different polynomial forms that can be used (even called the same thing). Which helps the confusion factor.

    Thanks for the link


    I'm still trying to figure out how 0x1021 equates to ---> x16 + x12 + x5 + 1


    x16 + x12 + x5 + 1  =  %10001000000100001
    0x1021              =  %00001000000100001
                            ^  
                            |
                          Is the MSB assumed to be 1 ?  I guess it is, that makes sense.  So it would be 0x11021
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/5/2009 6:18:33 PM GMT
  • jazzedjazzed Posts: 11,803
    edited 2009-11-05 18:23
    Yea, confusing. According to the CRC test web page link I provided ....
    Just as a reference the polynomial functions for the most common CRC calculations.
    Please remember that the highest order term of the polynomal (x16 or x32) is not present
    in the binary number representation, but implied by the algorithm itself.
    
    CRC-16         0x8005  x16 + x15 + x2 + 1
    CRC-CCITT    0x1021  x16 + x12 + x5 + 1
    CRC-32, etc... omitted.
    
    
    

    Post Edited (jazzed) : 11/5/2009 6:28:11 PM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-13 07:44
    Update:

    I had to lay down a few basic principles and a few of them evolved in order to decide what the most logical approach to this would be. Some priority obligations have limited my output on this but I hope to have something very soon. The basic building blocks are mostly in play, they just need to be pieced together. Included is a flow diagram of how I think things should happen, and I've posted it in a few other locations and haven't had anybody knock it down >yet< as to some fundamental thing I might be overlooking, so for now I will use it as the code template for how it's going to work. Perhaps a future implementation would be bi-directional Full-duplex in continuous mode.

    Basically there are two modes: Normal and continuous

    TX side:
    - Will send the data you place in a buffer and append the CRC when you issue a SEND command (who da thunk?)
    - A small amount of handshaking will transpire and the TX unit will keep sending the data packet until an acknowledgement has been received or until a number of user set retries have expired.
    - If an acknowledgement is received and the TX unit is in continuous mode it just starts all over again, otherwise the TX command has completed.


    RX side:
    - As data is received it goes into one of two buffers (two buffers explained later) If the received CRC does not match the locally calculated CRC, the receiver goes into a wait for sync loop (beginning of Flow)... This in turn generates a timeout on the TX side so that hopefully the packet will be re-transmitted.

    - If the CRC is valid, the RX module goes into TX mode and sends an acknowledgement to the TX unit which 'should' be waiting in receive mode for the acknowledgement.

    - If the RX unit is in continuous mode it will return to the beginning of the flow where it will wait for the next incoming SYNC signal, otherwise the RX command has completed.



    Note:
          - Treat 'Normal' mode as a standard Serial connection in a single threaded application.
    
          - Treat 'Continuous' mode similar to a live wire mode, but apply that live wire analogy
            to a user defined group of BYTES that just magically appear in a specific RAM location.
    
          - Continuous mode should also be launched in its own cog
    
          - The reason for having two receive buffers is to ensure that at any given time the data
            in one of them is the most recent and valid.  If the data comes in, and the CRC is not
            valid, then we don't want to read the data from this buffer, but instead the other buffer...
            whichever one is NOT being written to before the CRC passes it.  Toggling this on every
            other valid CRC is a simple way to manage this.
    
    




    Revision History:
    Version 1.0.a - (10-30-2009) original file created
    
    Version 1.0.b - (11-04-2009) improved bit width routine
    
    Version 1.0.c - (11-05-2009) optimized flow of long division routine
    
    Version 1.0.d - (11-06-2009) adjusted CRC to return LONG value instead of BYTE value
                                 this allows for the full remainder to be returned for
                                 polynomials greater than 1 BYTE wide.
    
    Version 1.0.e - (11-13-2009) converted CRC engine driver for one shot mode
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/13/2009 8:10:09 AM GMT
    794 x 1123 - 98K
  • DroneDrone Posts: 433
    edited 2009-11-13 12:57
    Hi Beau, This is nice. IMHO It could be the framework for error-correction and error detection. A next step might be interleaving to thwart burst errors followed by and one of many error correction techniques available. Multiple cogs may pose a problem in the error correction scheme however due to limited cog ram and/or delays accessing main ram between cogs. I would really like to see convolutional/Viterbi realized in PASM. That would be something.

    Best Regards, David
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2009-11-18 19:58
    Here is a DEMO example using the Parallax RF Transceivers with polynomial CRC applied. Data is Half Duplex, but can easily be switched from one direction to the other as long as the other units know what you are doing. i.e. The Master (Transmitter) sends a command to a specific Slave (Receiver) to become the "new" Master and the previous Master becomes a "new" Slave. I'll leave that coding up to you.

    I tried to implement a Half Duplex mode with acknowledgement from the receiver to verify that Data had been received and was valid, but the RF Transceivers have problems switching modes mid stream from a Transmitter to a Receiver and then back to a Transmitter. By the time they re-negotiate it ends up being incredibly slow. In practice, with the CRC applied and the double buffering on the receiver, I have not received any data errors with any of the data that does get through.

    What you can do (left for the user to exercise) is to send packets in burst mode from point A to point B and then reverse the direction.

    The Code has been reduced to simple Send, Receive, Put, and Get commands. You basically just treat the Put and Get like an address "on the other end". Both Send and Receive update from one database to the other database via the Transceivers.


    As always, please provide feedback where applicable, and I will post this object to the OBEX after a trial period.

    Enjoy!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 11/19/2009 5:19:52 AM GMT
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2010-04-07 20:19
    Here is an updated version of the code above with String support into and out of the RX/TX buffer, making it easier to send data strings.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 4/27/2010 4:44:32 AM GMT
  • nmz787nmz787 Posts: 24
    edited 2014-01-09 01:10
    Does anyone know of C or Python code that would be able to perform the same math? Something I could basically drop a packet and key into to get out the CRC, which I would compare equally with the propeller assuming no data corruption?

    I'd like to do this for streaming ADC readings over USB using an FTDI chip (using either VCP or D2XX driver)
Sign In or Register to comment.