4

I'm writing a gem to detect tracking numbers (called tracking_number, natch). It searches text for valid tracking number formats, and then runs those formats through the checksum calculation as specified in each respective service's spec to determine valid numbers.

The other day I mailed a letter using USPS Certified Mail, got the accompanying tracking number from USPS, and fed it into my gem and it failed the validation. I am fairly certain I am performing the calculation correctly, but have run out of ideas.

The number is validated using USS Code 128 as described in section 2.8 (page 15) of the following document: http://www.usps.com/cpim/ftp/pubs/pub109.pdf

The tracking number I got from the post office was "7196 9010 7560 0307 7385", and the code I'm using to calculate the check digit is:

def valid_checksum?
  # tracking number doesn't have spaces at this point
  chars = self.tracking_number.chars.to_a
  check_digit = chars.pop

  total = 0
  chars.reverse.each_with_index do |c, i|
    x = c.to_i
    x *= 3 if i.even?
    total += x
  end

  check = total % 10
  check = 10 - check unless (check.zero?)
  return true if check == check_digit.to_i
end

According to my calculations based on the spec provided, the last digit should be a 3 in order to be valid. However, Google's tracking number auto detection picks up the number fine as is, so I can only assume I am doing something wrong.

Jeff Keen
  • 137
  • 2
  • 8

2 Answers2

10

From my manual calculations, it should match what your code does:

posn: 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2   sum  mult
even:  7     9     9     1     7     6     0     0     7     8    54   162
 odd:     1     6     0     0     5     0     3     7     3       25    25
                                                                       ===
                                                                       187

Hence the check digit should be three.

If that number is valid, then they're using a different algorithm to the one you think they are.

I think that might be the case since, when I plug the number you gave into the USPS tracker page, I can see its entire path.


In fact, if you look at publication 91, the Confirmation Services Technical Guide, you'll see it uses two extra digits, including the 91 at the front for the tracking application ID. Applying the algorithm found in that publication gives us:

posn: 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2   sum  mult
even:  9     7     9     9     1     7     6     0     0     7     8    63   189
 odd:     1     1     6     0     0     5     0     3     7     3       26    26
                                                                             ===
                                                                             215

and that would indeed give you a check digit of 5. I'm not saying that's the answer but it does match with the facts and is at least a viable explanation.

Probably your best bet would be to contact USPS for the information.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    I think you got it paxdiablo! awesome troubleshooting! – jcomeau_ictx Feb 22 '11 at 05:06
  • 1
    why would anybody have downvoted this? paxdiablo's answer is almost certainly correct. – jcomeau_ictx Feb 22 '11 at 05:16
  • 1
    Thanks for the helpful info. As of now this information has been moved to Section 9.0 Appendix E of Barcode, Package, Intelligent Mail® SPECIFICATION USPS2000508 CAGE CODE 27085 which can be currently found at https://postalpro.usps.com/mnt/glusterfs/2018-02/BarcodePackageIMSpec.pdf – Chaim Geretz Sep 22 '20 at 19:09
2

I don't know Ruby, but it looks as though you're multiplying by 3 at each even number; and the way I read the spec, you sum all the even digits and multiply the sum by 3. See the worked-through example pp. 20-21.

(later) your code may be right. this Python snippet gives 7 for their example, and 3 for yours:


#!/usr/bin/python
'check tracking number checksum'
import sys
def check(number = sys.argv[1:]):
 to_check = ''.join(number).replace('-', '')
 print to_check
 even = sum(map(int, to_check[-2::-2]))
 odd = sum(map(int, to_check[-3::-2]))
 print even * 3 + odd
if __name__ == '__main__':
 check(sys.argv[1:])

[added later] just completing my code, for reference:


jcomeau@intrepid:~$ /tmp/track.py 7196 9010 7560 0307 7385
False
jcomeau@intrepid:~$ /tmp/track.py 91 7196 9010 7560 0307 7385
True
jcomeau@intrepid:~$ /tmp/track.py 71123456789123456787
True
jcomeau@intrepid:~$ cat /tmp/track.py 
#!/usr/bin/python
'check tracking number checksum'
import sys
def check(number):
 to_check = ''.join(number).replace('-', '')
 even = sum(map(int, to_check[-2::-2]))
 odd = sum(map(int, to_check[-3::-2]))
 checksum = even * 3 + odd
 checkdigit = (10 - (checksum % 10)) % 10
 return checkdigit == int(to_check[-1])
if __name__ == '__main__':
 print check(''.join(sys.argv[1:]).replace('-', ''))
jcomeau_ictx
  • 37,688
  • 6
  • 92
  • 107
  • hmm, you may be right, I also get a total of 187 which would mean a check digit of 3 – jcomeau_ictx Feb 22 '11 at 04:28
  • I've posted it to my facebook page, hopefully one of my USPS buddies can get an answer for you. I used to be an electronics tech for the USPS years ago, but this wasn't one of the barcode specs I had to deal with. – jcomeau_ictx Feb 22 '11 at 04:57
  • 1
    FYI: Multiplying each number by 3 and then adding the result is the same as adding all the numbers together and then multiplying the total by 3. It's called the distributive property. – Jeff Keen Feb 22 '11 at 18:30
  • thanks jeffxl, I new that; but misread your code and thought you were mutliplying the accumulated total by 3 at each even number. I didn't go back to correct my comment because I was busy trying to solve the problem. – jcomeau_ictx Feb 22 '11 at 18:52
  • 1
    Thanks for the script. Note to run it in Python 3 you must place parentheses around the argument to print since it is a function, otherwise it will raise *SyntaxError: invalid syntax*. `print (check(''.join(sys.argv[1:]).replace('-', '')))` – Jay Dec 25 '20 at 00:08
  • There are a whole lot of things wrong with my code, and even my analysis, considering the associative property of addition and multiplication, i.e., 3(a + b + c) is the same as 3a + 3b + 3c. Glad it helped you. – jcomeau_ictx Dec 26 '20 at 08:00