Is this code for calculating Code128 barcode check digits correct?
Asked Answered
G

2

6

Based on my understanding* of the check digit calculation for Code128 barcodes (which differs radically/drastically from most of the other standard barcode types), my code below is correct. However, I would appreciate a "sanity check" by those who may know better/have some empirical observations/mud from the trenches attached to their trench (or lab) coats.

  • This is my understanding:

Each character in the barcode, from the left, is first converted into its ASCII code (IOW, a barcode character of "1" is to be viewed as its ASCII Code of 49, etc.) and then that value is multiplied by its ordinal position in the array of chars.

e.g. for the fabricated barcode number "123456789" the "1" equates to 49, the "2" to 50, ... the "9" to 57.

Then, the ordinal position of each character is multiplied by its ASCII value. e.g., 1*49 == 49, 2*50==100, ... 9*57==513. You then sum all these up, to arrive at (in this case) 2,445.

The next step is to divide that number by the Code128 "magic number" of 103; the value you're interested in is the modulus. So, in this case, 2445 % 103 == 76.

Penultimate-finally, you convert that value (76), considering it to be an ASCII code, back the other direction to its "presentation" value, namely "L".

Finally, you append that calculated character to the original barcode. Verily and thus (notwithstanding further ado or adieux), you end up with a value of "123456789L"

If this is not the correct value, then I'm understanding something incorrectly.

Here are a few barcodes:

0) 123456789
1) 12345678
2) 1234567
3) 123456

...and how they should appear with their calculated check digits (and do, with the code below):

0) 123456789L
1) 12345678N
2) 1234567*
3) 123456E

Last but not least, here is the code I used to compute the Code128 check digits:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = 0;

    for (int i = barCode.Length - 1; i > -1; i--)
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    return valToConvertToASCII;
}

UPDATE

I'm not quite grokking Brian Anderson's answer; he may be right (probably is), but I would think the start and stop bits would be ignored as far as the check digit calculation goes. Aren't they there just so that the barcode scanner knows from which point to pay attention and thereafter from which point it can resume its electronic snoozing?

And then the math (provided Brian is correct about needing to subtract 32 from the ASCII vals) would be:

(17*1)+(18*2)+(19*3)+(20*4)+(21*5)+(22*6)+(23*7)+(24*8)+(25*9)
-or:
17 + 36 + 57 + 80 + 105 + 132 + 161 + 192 + 225 == 1005

Why would the start character be included in the calculation, but not the stop character?

Since 1005 % 103 == 78, the check digit would be ... "N" ... or would it be (78-32 == 46) "-"?

If both the stop and start characters were included, then of course that would change the solution, too...

UPDATE 2

I admit I'm not exactly an ogler of barcodes, so I may have seen such and just not noticed/paid attention, but can barcodes have such check digits as "-" and ""? It seems bizarre; I would expect them to always be alphanumerics if not just numerics. If my suspicion is correct, what is done when a calculation does end up with a squirrely check digit such as "-" or "" or "~", &c?

UPDATE 3

So, if I understand Brian and other sources I'v read correctly, I will read from the barcode scanner what semantically decodes to:

[startChar]123456789[checkDigit][stopChar]

...but which would logically strip out the stop char --since it is not part of the check digit calculation -- and (not quite so logically) also strip out the check digit, so what I actually get would be:

[startChar]123456789

...and massage it and calculate the check digit to to display a human-readable representation of the barcode:

123456789[checkDigit]

And since the start char has obviously (?) been seen if the barcode has scanned, I can just pre-add it to the runningTotal value to be computed; thus, my code is now:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = ConvertToASCIIInt(barcode[0]); // Start with the value of the start char; this should always be either 103 (Code128A), 104 (Code128B), or 105 (Code128C); 106 is the stop char

    for (int i = barCode.Length - 1; i > 0; i--) // now disregarding already calculated first element by ignoring element 0
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    const int ASCII_ADJUSTMENT_VAL = 32;
    return valToConvertToASCII-ASCII_ADJUSTMENT_VAL;
}
Glowing answered 10/9, 2013 at 21:3 Comment(3)
From a quick look the code seems match your description of the algorithm. Be aware that since you're getting this from a textbox, there is no guarantee that the characters in the string are part of the ASCII range. It won't crash or error out, but will produce invalid data without noticing the user. I can't verify that your checksum description is correct, unfortunately.Norseman
@C.Evenhuis: Thanks for czeching it out; no worries about the textbox value - that's just a utility for testing. In reality, the value will come from an actual scanned barcode.Glowing
Very intriguing. Surely there must be a definiton of the standard, e.g. Start character does or does not count, etc. Still, I love this coding work!Kirst
L
4

It looks like you might be missing the start character for code 128A (103), 128B (104) or 128C (105). This number is weighted with a '*1', just like the first character in your barCode string. I also think you have to do the math with the Code 128 values (i.e. ASCII_value_of_character - 32). So for the string "123456789" I get "104, 17, 18, 19, 20, 21, 22, 23, 24, 25, 79, 106", including the Start 128B (104), Stop (106) and a Checksum of 79 (1109 % 103).

Lovieloving answered 10/9, 2013 at 22:13 Comment(5)
I know it seems kind of squirrelly, but the start character is not fixed and determines the interpretation of the bar. What I thought was strange when I was learning about this was the fact that the Start character and the first data character both get a '1' rating. I mean, why not start the first data character off as multiplied by 2? You'll find an Excel app toward the bottom of the page on this website: notionovus.com/blog/barcodes/html/code-128-barcode that generates HTML Code 128 barcodes and it uses the "ASCII - 32" and "include the Start character" methods. They scan.Lovieloving
Okay, great; that will be a good resource for checking my calcs.Glowing
So it seems to me that when "I" (the app I'm maintaining) scans a barcode, I will have to use that value to calculate the check digit, but ignore/disregard the last character (the "stop bit"). Correct? So the weightization of the barcode will be: 1,1,2,3,...len-1,0Glowing
Correct. The Stop byte, which is always 106 is used by the scanner for orientation. It is only used to signal the right side of a barcode, in case the operator scans it upside-down. The unique set of bars and lines before the quiet zone is not found anywhere else in the symbology, so a scan cannot be accidentally terminated simply by having the bars and lines for 106 appear anywhere else. The byte immediately to the left of the Stop is the chksum, so that's how the hardware knows where the rest of the data is. The scanner then works from the left side back to the last byte before the chksum.Lovieloving
If interested, see my new, related post at #18749544Glowing
T
2

Here's a slightly different way of doing this in case it helps others. The changes are:

  • Pass in the start code for flexibility.
  • Do the work in a single method.
  • Reduce the number of stored values in cases where it's feasible and where they're used only once.
  • Add a try catch statement.

NOTE: the stop code is not used in the checksum calculation.

var checkSum = CalcCheckSum("TEST1234", 103);
public int CalcCheckSum(string barcodeValue, int startCode = 104)
{
    int checkSum = 0;

    try
    {
        //Add the startCode to the total. You could just add
        //the startCode value without checking for zero.
        var checkSumTotal = startCode == 0 ? 104 : startCode;

        //Split the string into an array of characters.
        char[] barcodeChars = barcodeValue.ToCharArray();

        int x = 1;

        //Loop through the array to get the ASCII value
        //by using type int and then subtract 32 to get
        //the Code 128 value. The resulting value gets
        //multiplied by its position x.
        foreach (var digit in barcodeChars)
        {
            checkSumTotal += ((int)digit - 32) * x;
            x++;
        }

        //Take the total and find the remainder when
        //you multiply by 103
        checkSum = checkSumTotal % 103;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    return checkSum;
}

Here's a link to the wiki about Code 128.

Telemetry answered 20/7, 2023 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.