Ejemplo n.º 1
0
        /// <summary>
        /// Encode the Tag EPC URI in SGTIN scheme into tag EPC memory bank.
        /// </summary>
        /// <param name="reader">Reference to the reader</param>
        /// <param name="tag">Reference to the singulated tag (reader-specific datum)</param>
        /// <param name="uri">The Tag URI</param>
        /// <returns>True if succeeded</returns>
        public static bool WriteEPC(this IReaderLowLevel reader, object tag, TagEPCURI_SGTIN uri)
        {
            // try to encode the EPC Tag URI

            // see 14.5.1, table 14-2
            // algorithm: 14.3.3
            byte[] res_control = new byte[4];
            byte[] res         = new byte[12];

            res_control[0] = 0; // CRC-16
            res_control[1] = 0; // CRC-16

            /*
             * The number of bits, N, in the EPC binary encoding determined
             * in Step 2 above, divided by 16, and rounded up to the next
             * higher integer if N was not a multiple of 16
             */
            var nbits      = 12 * 8; // full length of PC+EPC data, in bits
            int nbit_value = (nbits + 15) / 16;
            var UMI        = true;   // indicates if user memory bank is present
            var XI         = false;  // indicates if XPC is present
            var toggle     = false;  // indicates if bits 0x18-0x1F contain attribute bits and the remainder of EPC bank contains a binary encoded EPC

            res_control[2] =
//            var pc_epc_len = ((epc[2] >> 3) & 0x1F) * 2; // full length of PC+EPC data, in bytes
                (byte)((nbit_value << 3)
                       | (UMI? 0x4 : 0)
                       | (XI? 0x2 : 0)
                       | (toggle? 0x1 : 0));
            res_control[3] = 0;

            //Console.WriteLine("Control: {0}", BitConverter.ToString(res_control));

            // SGTIN-96

            // EPC header 1 byte = 00110000 (bin)
            // filter 3 bits (3 bit integer)
            // partition 3 bits (3 bit integer, see below)
            // GS1 Company Prefix 20-40 bits
            // Item Ref 24-4 bits
            // Serial 38 bits

            // write the header
            res[0] = 0x30;
            // write the filter
            res[1] = (byte)(res[1] | ((uri.Filter & 0x7) << 5));

            var  uri0      = uri.Identity.URI;
            byte partition = 0;
            var  C         = uri.Identity.GS1CompanyPrefix;
            var  D         = uri.Identity.ItemRef;
            // value P (partition value, 3 bits)
            // value C (of M-bit width)
            var Cdigits = uri.Identity.GS1CompanyPrefixLength;
            // value D (of N-bit width)
            var Ddigits = uri.Identity.ItemRefLength;

            // NOTE: Cdigits + Ddigits must be 13
            var sum = Cdigits + Ddigits;

            // there's a complication
            // a GTIN might be:
            // - GTIN-8 (8 digits)
            // - GTIN-12 (12 digits)
            // - GTIN-13 (13 digits)
            // - GTIN-14 (14 digits)
            // first of all, how to convert GTIN-8 to GTIN-12?
            // what about anything else? should we use strings instead of long integers?

            /*
             * see: https://www.gs1us.org/resources/standards/company-prefix
             * - GS1 Company Prefix is a string of digits
             * - GS1 Company Prefix Length is between 7 and 11 digits
             * - GS1 Company Prefix Length provides capacity in emitting fresh GTINs
             *   - prefix length = 7, capacity = 100 000
             *   - prefix length = 8, capacity = 10 000
             *
             * A GS1 Company Prefix and an Item Reference Number comprise a GTIN.
             *
             * http://www.envioag.com/wp-content/uploads/2013/05/GTIN_070313.pdf
             */

            if (Cdigits == 12 && Ddigits == 1)
            {
                partition = 0;
                // new SGTINPartInfo() { bitsM = 40, digitsL = 12, bitsN = 4, digits = 1 },
                res[1] |= (byte)((C >> 38) & 0x3);
                res[2] |= (byte)((C >> 30) & 0xFF);
                res[3] |= (byte)((C >> 22) & 0xFF);
                res[4] |= (byte)((C >> 14) & 0xFF);
                res[5] |= (byte)((C >> 6) & 0xFF);
                res[6] |= (byte)(((C >> 0) & 0x3F) << 2);

                res[6] |= (byte)((D >> 2) & 0x3);
                res[7] |= (byte)(((D >> 0) & 0x3) << 6);
            }
            else if (Cdigits == 11 && Ddigits == 2)
            {
                // 1
                return(false);
            }
            else if (Cdigits == 10 && Ddigits == 3)
            {
                partition = 2;
                res[1]   |= ((byte)((C >> 32) & 0x03));
                res[2]   |= ((byte)((C >> 24) & 0xFF));
                res[3]   |= ((byte)((C >> 16) & 0xFF));
                res[4]   |= ((byte)((C >> 8) & 0xFF));
                res[5]   |= ((byte)((C >> 0) & 0xFF));

                res[6] |= (byte)(D >> 2);
                res[7] |= (byte)(((D >> 0) & 0x03) << 6);
            }
            else if (Cdigits == 9 && Ddigits == 4)
            {
                // 3
                return(false);
            }
            else if (Cdigits == 8 && Ddigits == 5)
            {
                // 4
                return(false);
            }
            else if (Cdigits == 7 && Ddigits == 6)
            {
                partition = 5;
                res[1]   |= ((byte)((C >> 22) & 0x03));
                res[2]   |= ((byte)((C >> 14) & 0xFF));
                res[3]   |= ((byte)((C >> 6) & 0xFF));
                res[4]   |= ((byte)(((C >> 0) & 0x3F) << 2));

                res[4] |= ((byte)((D >> 18) & 0x03));
                res[5] |= ((byte)((D >> 10) & 0xFF));
                res[6] |= ((byte)((D >> 2) & 0xFF));
                res[7] |= ((byte)(((D >> 0) & 0x03) << 6));
            }
            else if (Cdigits == 6 && Ddigits == 7)
            {
                // 6
                return(false);
            }
            else
            {
                // unknown partition
                return(false);
            }

            res[1] = (byte)(res[1] | ((partition & 0x7) << 2));

            var S = uri.Identity.SerialNr;

            res[7]  = (byte)(res[7] | (byte)((S >> 32) & 0x3F));
            res[8]  = (byte)(res[8] | (byte)((S >> 24) & 0xFF));
            res[9]  = (byte)(res[9] | (byte)((S >> 16) & 0xFF));
            res[10] = (byte)(res[10] | (byte)((S >> 8) & 0xFF));
            res[11] = (byte)(res[11] | (byte)((S >> 0) & 0xFF));

            /*
             * var res_hexstring = BitConverter.ToString(res);
             * Console.WriteLine("result of WriteEPC: {0}", res_hexstring);
             */

            if (reader.ChunkSize > 0)
            {
                // try writing in blocks of ChunkSize
                var csize = reader.ChunkSize;
                var chunk = new byte[csize];
                var beg   = 0;
                var ofs   = 4;
                while (beg < res.Length)
                {
                    Buffer.BlockCopy(res, beg, chunk, 0, csize);
                    if (!reader.WriteBytes(tag, 0x01, ofs, csize, chunk))
                    {
                        return(false);
                    }
                    ofs += csize;
                    beg += csize;
                }
                return(true);
            }
            else
            {
                // skip the first 4 bytes
                return(reader.WriteBytes(tag, 0x01, 4, res.Length, res));
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Generate an Tag EPC URI based on a given template and the TID.
        /// </summary>
        /// <param name="tid">TID of the tag that the EPC is to be generated for.</param>
        /// <param name="epc">Template EPC, containing all non-serial information (e.g. GS-1 company prefix, item reference, etc.)</param>
        /// <param name="res">The resulting EPC</param>
        /// <returns>true if succeeded</returns>
        public static bool GenerateEPC(ref TID tid, ref TagEPCURI_SGTIN epc, out TagEPCURI_SGTIN res)
        {
            res = default(TagEPCURI_SGTIN);

            var serial = tid.Serial;

            if (serial == null || serial.Length == 0)
            {
                return(false);
            }

            var MCS_identifier = (tid.TMN >> 5) & 0x3F;
            var MCS_product    = (tid.TMN >> 3) & 0x7;
            var MCS_version    = tid.TMN & 0x7;
            // for Higgs-3: identifier 0x10, product 0x2, version 0x2
            // for Monza: identifier 0x4, product 0, version 5
            //                    System.Windows.Forms.MessageBox.Show(
            //                        string.Format("MCS: identifier {0:X}, product {1:X}, version {2:X}", MCS_identifier, MCS_product, MCS_version)
            //                    );

            // extract 35 least significant bits of desired serial number
            // from Serial
            var start = 0;

            if (serial.Length == 8) // 64 bits
            {
                switch (tid.TMN)
                {
                case 0x412:
                    // Alien Higgs-3: 64-bits of uniqueness
                    // but we only want 35 bits, and since they do
                    // support MCS, it should be OK
                    if (tid.MDID == 0x3)     // vendor: Alien, chip: Higgs-3
                    {
                        start = 3;
                    }
                    else
                    {
                        return(false);    // untested
                    }
                    break;

                default:
                    return(false);    // untested
                }
            }
            else if (serial.Length == 6) // 48 bits
            {
                switch (tid.TMN)
                {
                // TMN (hex) for Monza-4 chips (source: Monza Serialization, above)
                case 0x100:     // Monza 4D
                case 0x10C:     // Monza 4E
                case 0x105:     // Monza 4QT
                    // on Impinj Monza-4 chips, this gives the sought-for uniqueness
                    // or does it???
                    if (tid.MDID == 0x801)     // vendor: Impinj
                    {
                        start = 0;
                    }
                    else
                    {
                        start = 1;
                    }
                    break;

                case 0x412:              // Alien Higgs-3? 64-bits of uniqueness
                    if (tid.MDID == 0x3) // vendor: Alien, chip: Higgs-3
                    // NOTE: still, I found two tags which have the same UTID, byte-by-byte
                    {
                        start = 1;
                    }
                    else
                    {
                        start = 1;
                    }
                    break;

                default:
                    start = 1;     // untested
                    break;
                }
            }
            else if (serial.Length == 5) // 40 bits (e.g. some NXP tags)
            {
                start = 0;
            }
            else
            {
                return(false);
            }
            var newserial = 0L;

            newserial |= ((long)(serial[start + 0] & 0x07) << 32);
            newserial |= ((long)(serial[start + 1] & 0xFF) << 24);
            newserial |= ((long)(serial[start + 2] & 0xFF) << 16);
            newserial |= ((long)(serial[start + 3] & 0xFF) << 8);
            newserial |= ((long)(serial[start + 4] & 0xFF) << 0);

            // put the 3 bits identifying the chip vendor
            int vendor = 0;
            var MDID   = tid.MDID & ~0x800; // remove the XTID indicator

            switch (MDID)
            {
            case 0x003:        // Alien
                vendor = 0x06; // 110
                break;

            case 0x001:        // Impinj
                vendor = 0x05; // 101
                break;

            case 0x006:        // NXP
                vendor = 0x07; // 111
                break;

            default:
                return(false);    // unknown vendor, they may not support MCS!
            }
            newserial |= ((long)(vendor & 0x07) << 35);

            Debug.WriteLine(string.Format("serial: {0}, starting at {1}, newserial: {2}", BitConverter.ToString(serial), start, newserial));

            res = epc;
            // C# needs some Rust-style borrowing stuff for value types!
            // (or, some linear types would be even nicer, as they are simpler)
            var identity = epc.Identity;

            Debug.WriteLine(string.Format("identity EPC before serial assignment {0}", identity.URI));
            identity.SetSerial(newserial);
            res.Identity = identity;
            Debug.WriteLine(string.Format("identity EPC after serial assignment {0}", identity.URI));

            return(true);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Decode the EPC memory bank, which is assumed to be SGTIN-192, and return the Tag URI.
        /// </summary>
        /// <param name="reader">Reference to the reader that is used to obtain the TID memory bank contents</param>
        /// <param name="tag">Reference to the singulated tag (reader-specific datum)</param>
        /// <param name="uri">The resulting Tag URI</param>
        /// <returns>Success or failure</returns>
        public static DecodeError ReadEPC_SGTIN(this IReaderLowLevel reader, object tag, out TagEPCURI_SGTIN uri)
        {
            uri = default(TagEPCURI_SGTIN);

            // see also: http://www.rfidjournal.net/masterPresentations/rfid_live2012/np/traub_apr3_230_ITProf.pdf
            // what about the EPC?
            // bit 0x17 of EPC is the toggle
            // - we need to use SGTIN-96, and encode it
            //   - pure identity URI (e.g. urn:epc:id:sgtin:0614141.100743.401)
            //     - independent of RFID, so this is what the business apps should use
            //   - tag URI (e.g. urn:epc:tag:sgtin-96:3.0614141.100743.401) <-- contains control info of EPC memory bank as well
            //     - used when reading from a tag where the control info is of interest
            //     - used when writing to the EPC memory bank of an RFID tag, in order to fully specify the contents to be written
            //     - there exists 1:1 mapping between tag URI and binary representation
            // - what's the barcode equivalent?
            //   barcode:
            //      (01) 1 0614141 12345 2 (21) 401
            //      ^^^^ not included
            //   pure identity URI:
            // urn:epc:id:sgtin:0614141.112345.401
            // - also, need to learn how to encode SGTIN-96 to binary form and back!
            //
            // also, we might want to use the GS1 GIAI schema
            // - http://www.gs1.org/docs/idkeys/GS1_GIAI_Executive_Summary.pdf
            // - 1 2 ... n           n+1 n+2  ... <= 30
            //   GS1 company prefix| individual asset reference
            //   (numeric)         | (alphanumeric)
            //
            // what's in SGTIN? e.g. urn:epc:id:sgtin:0614141.112345.400
            // urn:epc:id:sgtin:CompanyPrefix.ItemRefAndIndicator.SerialNumber
            // - company prefix (assigned by GS1 to a managing entity or its delegates)
            // - item ref and indicator (assigned by the managing entity to a particular object class)
            // - serial number (assigned by the managing entity to an individual object)
            // what's in GIAI? e.g. urn:epc:id:giai:0614141.12345400
            // urn:epc:id:giai:CompanyPrefix.IndividulAssetReference
            // - company prefix (assigned by GS1 to a managing entity)
            // - individual asset reference (assigned uniquely by the managing entity to a specific asset)

            /*
             * how to assign?
             *
             * what's in the EPC
             * - 2 bytes of CRC (we don't usually write these, right?)
             * - 2 bytes: length (5 bits), UMI (1 bit), XI (1 bit), T (1 bit), attribs (8 bits)
             *     - length: nr of words for PC and EPC, combined
             * - 2 bytes: EPC header (1 byte), filter (3 bits), partition (3 bits), GTIN (2 bits)
             * - 2 bytes: GTIN
             * - 2 bytes: GTIN
             * - 2 bytes: GTIN (10 bits), serial (6 bits)
             * - 2 bytes: serial
             * - 2 bytes: serial
             * 16 bytes in total
             */

            // see also: http://www.rfidjournal.net/masterPresentations/rfid_live2012/np/traub_apr3_230_ITProf.pdf
            // what about the EPC?
            // bit 0x17 of EPC is the toggle
            // - we need to use SGTIN-96, and encode it
            //   - pure identity URI (e.g. urn:epc:id:sgtin:0614141.100743.401)
            //     - independent of RFID, so this is what the business apps should use
            //   - tag URI (e.g. urn:epc:tag:sgtin-96:3.0614141.100743.401) <-- contains control info of EPC memory bank as well
            //     - used when reading from a tag where the control info is of interest
            //     - used when writing to the EPC memory bank of an RFID tag, in order to fully specify the contents to be written
            //     - there exists 1:1 mapping between tag URI and binary representation
            // - what's the barcode equivalent?
            //   barcode:
            //      (01) 1 0614141 12345 2 (21) 401
            //      ^^^^ not included
            //   pure identity URI:
            // urn:epc:id:sgtin:0614141.112345.401
            // - also, need to learn how to encode SGTIN-96 to binary form and back!
            //
            // also, we might want to use the GS1 GIAI schema
            // - http://www.gs1.org/docs/idkeys/GS1_GIAI_Executive_Summary.pdf
            // - 1 2 ... n           n+1 n+2  ... <= 30
            //   GS1 company prefix| individual asset reference
            //   (numeric)         | (alphanumeric)
            //
            // what's in SGTIN? e.g. urn:epc:id:sgtin:0614141.112345.400
            // urn:epc:id:sgtin:CompanyPrefix.ItemRefAndIndicator.SerialNumber
            // - company prefix (assigned by GS1 to a managing entity or its delegates)
            // - item ref and indicator (assigned by the managing entity to a particular object class)
            // - serial number (assigned by the managing entity to an individual object)
            // what's in GIAI? e.g. urn:epc:id:giai:0614141.12345400
            // urn:epc:id:giai:CompanyPrefix.IndividulAssetReference
            // - company prefix (assigned by GS1 to a managing entity)
            // - individual asset reference (assigned uniquely by the managing entity to a specific asset)

            /*
             * how to assign?
             *
             * what's in the EPC
             * - 2 bytes of CRC (we don't usually write these, right?)
             * - 2 bytes: length (5 bits), UMI (1 bit), XI (1 bit), T (1 bit), attribs (8 bits)
             *     - length: nr of words for PC and EPC, combined
             * - 2 bytes: EPC header (1 byte), filter (3 bits), partition (3 bits), GTIN (2 bits)
             * - 2 bytes: GTIN
             * - 2 bytes: GTIN
             * - 2 bytes: GTIN (10 bits), serial (6 bits)
             * - 2 bytes: serial
             * - 2 bytes: serial
             * 16 bytes in total
             */

            EPC_PC pc;
            var    rr = ReadEPC_PC(reader, tag, out pc);

            if (rr != DecodeError.None)
            {
                return(rr);
            }

            var crc16      = pc.Crc16;
            var pc_epc_len = pc.PC_EPC_Length;
            var UMI        = pc.UMI;
            var XI         = pc.XI;
            var toggle     = pc.Toggle;
            var attribs    = pc.Attribs;

            // read full length of EPC (12 bytes or more)
            byte[] epc_payload = new byte[pc_epc_len];
            {
                const int chunkSize = 4;
                int       sizeRead  = 0;
                while (sizeRead < pc_epc_len)
                {
                    byte[] chunk;
                    if (!reader.ReadBytes(tag, 0x01, 4 + sizeRead, chunkSize, out chunk))
                    {
                        return(DecodeError.IO);
                    }
                    Buffer.BlockCopy(chunk, 0, epc_payload, sizeRead, chunkSize);
                    sizeRead += chunkSize;
                }
            }

            //var epc_payload_hex = BitConverter.ToString(epc_payload);
            //System.Windows.Forms.MessageBox.Show(BitConverter.ToString(epc_payload));

            // SGTIN-96
            var epc_header = epc_payload[0]; // must be 0x30 if that's SGTIN-96!

            if (epc_header != 0x30)
            {
                return(DecodeError.Invalid); // NOT SGTIN-96!
            }
            if (pc_epc_len < 10 /*96 bits*/)
            {
                return(DecodeError.Invalid); // wrong length of EPC! (see table 14.1 in EPC TDS 1.9)
            }
            var filter    = (epc_payload[1] >> 5) & 0x7;
            var partition = (epc_payload[1] >> 2) & 0x7;

            // 14.4.3
            var C       = 0L; // company prefix
            var Cdigits = 0;
            var D       = 0L; // indicator/item reference
            var Ddigits = 0;
            var S       = 0L;

            // Consider these M bits to be an unsigned binary integer, C.
            // The value of C must be less than 10^L, where L is the value
            // specified in the “GS1 Company Prefix Digits (L)” column of
            // the matching partition table row.
            //
            // There are N bits remaining in the input bit string, where
            // N is the value specified in the “Other Field Bits” column
            // of the matching partition table row. Consider these N bits
            // to be an unsigned binary integer, D. The value of D must be
            // less than 10^K, where K is the value specified in the
            // “Other Field Digits (K)” column of the matching partition
            // table row. Note that if K = 0, then the value of D must be zero.
            switch (partition)
            {
            case 0:
                // new SGTINPartInfo() { bitsM = 40, digitsL = 12, bitsN = 4, digits = 1 },
                C      |= ((long)(epc_payload[1] & 0x3) << 38);
                C      |= ((long)(epc_payload[2] & 0xFF) << 30);
                C      |= ((long)(epc_payload[3] & 0xFF) << 22);
                C      |= ((long)(epc_payload[4] & 0xFF) << 14);
                C      |= ((long)(epc_payload[5] & 0xFF) << 6);
                C      |= ((long)(((epc_payload[6] >> 2) & 0x3F)) << 0);
                Cdigits = 12;
                if ((ulong)C >= IntPow10((byte)Cdigits))
                {
                    return(DecodeError.Invalid);    // something is wrong with it!
                }
                D       = ((((epc_payload[6] & 0x3) << 2) | ((epc_payload[7] >> 6) & 0x3)) & 0xFF) << 0;
                Ddigits = 1;
                if ((ulong)D >= IntPow10((byte)1))
                {
                    return(DecodeError.Invalid);
                }
                break;

            /*
             *                              case 1:
             *                                  //{1, new SGTINPartInfo() { bitsM = 37, digitsL = 11, bitsN = 7, digits = 2 }},
             *                                  C |= ((long)(epc_payload[1] & 0x03) << 35);
             *                                  C |= ((long)(epc_payload[2] & 0xFF) << 27);
             *                                  C |= ((long)(epc_payload[3] & 0xFF) << 19);
             *                                  C |= ((long)(epc_payload[4] & 0xFF) << 11);
             *                                  C |= ((long)(epc_payload[5] & 0xFF) << 3);
             *                                  C |= ((long)(((epc_payload[6] >> 5) & 0x07)) << 0);
             *                                  Cdigits = 11;
             *                                  if ((ulong)C >= IntPow10((byte)Cdigits))
             *                                      continue; // something is wrong with it!
             *
             *                                  D |= ((long)(epc_payload[6] & 0x07) << 3); // 3 bits
             *                                  D |= ((((long)epc_payload[7] >> 4) & 0x0F) << 0); // 4 bits
             *                                  Ddigits = 2;
             *                                  if ((ulong)D >= IntPow10((byte)Ddigits))
             *                                      continue;
             *
             *                                  break;*/
            case 2:
                //{2, new SGTINPartInfo() { bitsM = 34, digitsL = 10, bitsN = 10, digits = 3 }},
                C      |= ((long)(epc_payload[1] & 0x03) << 32);
                C      |= ((long)(epc_payload[2] & 0xFF) << 24);
                C      |= ((long)(epc_payload[3] & 0xFF) << 16);
                C      |= ((long)(epc_payload[4] & 0xFF) << 8);
                C      |= ((long)(epc_payload[5] & 0xFF) << 0);
                Cdigits = 10;
                if ((ulong)C >= IntPow10((byte)Cdigits))
                {
                    return(DecodeError.Invalid);    // something is wrong with it!
                }
                D      |= ((long)epc_payload[6] << 2);
                D      |= ((long)((epc_payload[7] >> 6) & 0x03) << 0);
                Ddigits = 3;
                if ((ulong)D >= IntPow10((byte)Ddigits))
                {
                    return(DecodeError.Invalid);
                }
                break;

            /*
             * case 3:
             *  // {3, new SGTINPartInfo() { bitsM = 30, digitsL = 9, bitsN = 14, digits = 4 }},
             *  break;
             * case 4:
             * //            {4, new SGTINPartInfo() { bitsM = 30, digitsL = 9, bitsN = 14, digits = 4 }},
             */
            case 5:
                // {5, new SGTINPartInfo() { bitsM = 24, digitsL = 7, bitsN = 20, digits = 6 }},
                C      |= ((long)(epc_payload[1] & 0x03) << 22);
                C      |= ((long)(epc_payload[2] & 0xFF) << 14);
                C      |= ((long)(epc_payload[3] & 0xFF) << 6);
                C      |= ((((long)epc_payload[4] >> 2) & 0x3F) << 0);
                Cdigits = 7;
                if ((ulong)C >= IntPow10((byte)Cdigits))
                {
                    return(DecodeError.Invalid);    // something is wrong with it!
                }
                D      |= ((long)(epc_payload[4] & 0x03) << 18);
                D      |= ((long)(epc_payload[5] & 0xFF) << 10);
                D      |= ((long)(epc_payload[6] & 0xFF) << 2);
                D      |= ((long)((epc_payload[7] >> 6) & 0x03) << 0);
                Ddigits = 6;
                if ((ulong)D >= IntPow10((byte)Ddigits))
                {
                    return(DecodeError.Invalid);
                }
                break;

            /*
             * case 6:
             * //{6, new SGTINPartInfo() { bitsM = 20, digitsL = 6, bitsN = 24, digits = 7}},
             * C |= ((long)(epc_payload[1] & 0x03) << 18);
             * C |= ((long)(epc_payload[2] & 0xFF) << 10);
             * C |= ((long)(epc_payload[3] & 0xFF) << 2);
             * C |= ((((long)epc_payload[4] >> 2) & 0x3F) << 0);
             * Cdigits = 6;
             * if ((ulong)C >= IntPow10((byte)Cdigits))
             *  continue; // something is wrong with it!
             *
             * D |= ((long)(epc_payload[4] & 0x03) << 22);
             * D |= ((long)(epc_payload[5] & 0xFF) << 14);
             * D |= ((long)(epc_payload[6] & 0xFF) << 6);
             * D |= ((long)((epc_payload[7] >> 2) & 0x03) << 0);
             * Ddigits = 7;
             * if ((ulong)D >= IntPow10((byte)Ddigits))
             *  continue; // something is wrong with it!
             * break;*/

            default:
                return(DecodeError.Invalid);    // unable to parse this partition
            }

            // serial: 38 bits
            // NOTE: must be less than 274,877,906,944
            // 6 bits, and then some more
            S |= ((long)(epc_payload[7] & 0x3F) << 32);
            S |= ((long)(epc_payload[8] & 0xFF) << 24);
            S |= ((long)(epc_payload[9] & 0xFF) << 16);
            S |= ((long)(epc_payload[10] & 0xFF) << 8);
            S |= ((long)(epc_payload[11] & 0xFF) << 0);

            uri.Filter   = (byte)filter;
            uri.Identity = new IdentityEPCURI_SGTIN()
            {
                GS1CompanyPrefix       = C,
                ItemRef                = D,
                GS1CompanyPrefixLength = Cdigits,
                ItemRefLength          = Ddigits,
                SerialNr               = S
            };

            return(DecodeError.None);
        }