Search code examples
cencodingasciigsmpdu

Convert ASCII String to 7-bit GSM coding scheme


A simple routine i wrote for converting ASCII string to corresponding 7-bit GSM coding scheme:

#include <stdio.h>
#include <process.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>

/* convert ascii input string to 7-bit GSM alphabet */
bool ascii_to_gsm(const char* in, uint8_t len, uint8_t* out, uint8_t start_indx) {
    if (in == NULL || out == NULL || len == 0)
        return false;

    uint8_t nshift = 7;
    memcpy(out + start_indx, in, len);
    for (size_t i = 0; i < len - 1; i++) {
        nshift = (nshift == 255) ? 7 : nshift;
        uint16_t l = out[start_indx + i];
        uint16_t h = out[start_indx + i + 1];
        h = (h << nshift--) | l;
        out[start_indx + i] = h;
        out[start_indx + i + 1] = h >> 8;
    }

    return true;
}

int main() {
    char data[] = "ASCIIASCII";
    uint8_t buff[sizeof(data) - 1];
    memset(buff, 0, sizeof(buff));
    ascii_to_gsm(data, sizeof(buff), buff, 0);
    for (size_t i = 0; i < sizeof(buff); i++) {
        printf("\n buff[%d]=%02x", i, buff[i]);
    }
    system("pause");

    return 0;
}

For strings like ASCII or TEST it's working fine and output is C1E9309904 and D4E2940Arespectively.

But for string ASCIIASCII some output byte is wrong: C1E930990C4E87498024

The result should be: C1E930990C4E87C924

Don't know what part, i'm wrong.

Concepts about GSM coding can be found here.

I use this online encoder to compare results


Solution

  • But for string ASCIIASCII some output byte is wrong:
    C1E930990C4E87498024
    The result should be:
    C1E930990C4E87C924

    OP's code does not take into account the output may have a shorter length than the input.

    If the input is 10 ASCII characters, that is 70 bits. The output needs to be ceiling(70/8) or 9 bytes. Also see @Steve Summit.


    A simplified code for reference that lacks a start_indx. Since input is a string ("converting ASCII string"), the input length is not needed.

    bool ascii_to_gsmA(const char* in, uint8_t* out) {
      unsigned bit_count = 0;
      unsigned bit_queue = 0;
      while (*in) {
        bit_queue |= (*in & 0x7Fu) << bit_count;
        bit_count += 7;
        if (bit_count >= 8) {
          *out++ = (uint8_t) bit_queue;
          bit_count -= 8;
          bit_queue >>= 8;
        }
        in++;
      }
      if (bit_count > 0) {
        *out++ = (uint8_t) bit_queue;
        }
      return true;
    }