Question

In: Computer Science

A typical SMS protocol allows 128 distinct ASCII characters to be used in a message. However,...

A typical SMS protocol allows 128 distinct ASCII characters to be used in a message. However, to save bandwidth, instead of using straight 8-bit ASCII characters to transfer data (one byte per character), SMS uses 7 bits per character and compacts the bits such that it only requires 140 bytes to send a 160 character message, for an approximately 12.5% savings of bandwidth.

12.5% savings in bandwidth is not good enough. They would like a system that can save up to 25% of the bandwidth. Therefore, a 160 character message must use only 120 bytes of bandwidth. To do this, a character set that uses only 6 bits per character must be developed. This means that the maximum number of distinct characters that can be in the "alphabet" is 64. The Department of Linguistics was consulted, and they decided that a character set that does not contain any lower case letters, but includes nearly all other printable ASCII characters could be used without major issues. Therefore, the following code and values were developed as the alphabet to be used for the SMS System.

Standard Code for Information Interchange (SCII)

Dec Hex   Char Dec Hex   Char Dec Hex   Char Dec Hex   Char
0 00 NULL 16 10 G 32     20    W 48 30 ,
1 01 . 17 11       H 33 21 X 49 31 -
2 02 / 18    12   I 34 22    Y 50 32 0
3 03 : 19     13    J 35 23    Z 51 33    1
4    04 ; 20 14   K 36 24 Space 52 34    2
5      05 < 21    15    L 37 25 ! 53    35    3
6 06 = 22     16       M 38 26 " 54 36    4
7 07 > 23 17   N 39 27 # 55    37 5
8    08 ? 24    18   O 40 28 $ 56 38    6
9    09 @ 25     19    P 42    29 % 57 39 7
10 0A    A 26 1A Q 42    2A & 58 3A       8
11 0B B 27    1B      R 43    2B ' 59 3B 9
12 0C    C 28     1C    S 44 2C    ( 60 3C [
13 0D    D 29 1D    T 45    2D    ) 61    3D \
14 0E    E 30    1E U 46 2E    * 62 3E    ]
15 0F    F 31     1F V 47 2F + 63 3F     _

Note: All printable ASCII characters that are NOT shown in the above table (such as a..z) will be translated to the '#' character. All non-printable ASCII characters will be translated to NULL.


Packing Bits:
The following system is similar to the SMS 7-bit packing system, except that it packs 6-bit characters in an array. As an example, suppose a student wishes to send the following text via the SMS system:

CS IS GREAT!

Using the table above, each character can be given its corresponding Hexadecimal value:

0C 1C 24 12 1C 24 10 1B 0E 0A 1D 25
C   S I    S        G   R   E   A   T   !

Translating the above message into individal 8-bit bytes, we get:
00001100 00011100   00100100 00010010 00011100 00100100 00010000 00011011 00001110 00001010 00011101 00100101

Since we are going to only use six bits per character, we can remove the two Most Significant Bits (MSBs) with the following result:
001100 011100 100100 010010 011100 100100 010000 011011 001110 001010 011101 100101

We will pack the bits into 8-bit bytes. These bytes will be part of our Packed Array. To do this, we start at the Least Significant Bit (LSB) of the first character. In this case, it is the letter C with the bit pattern 001100. The LSB of this pattern is the last 0 (bolded) - 001100. This bit will become the lsb of the first element in the packed array. We continue, bit by bit, until we have put the entire character in the first element of the packed array. Note that there will be two bits that are not yet initialized:
- - 0 0 1 1 0 0 <- PackedArray[0] (started)

Now we start on the next character, or 011100 (The letter 'S'). Starting at the LSB, we put that bit in the next available bit of PackedArray[0]: - 0 0 0 1 1 0 0, then the next LSB after that:  0 0 0 0 1 1 0 0, and we have finished the first element of PackedArray. However, we have not finished packing the letter 'S'! We still have four bits left to encode! So, those four bits will then become the least significant bits of the next element of PackedArray:
- - - - 0 1 1 1 <- PackedArray[1] (started)

Continuing with the third character (the character '2'), we take the four LSBs of that character, and add them to the remaining bit locations:
0 1 0 0 0 1 1 1 <- PackedArray[1] (completed)

However, there are two more bits left to encode from the '2' character, so they will become the LSBs of the next element of PackedArray:
- - - - - - 1 1 <- PackedArray[2] (started)

This continues until all of the 6-bit characters have been packed or encoded into PackedArray. Any remaining bit locations in PackedArray that are left after all the bits from the message are encoded, will be set to 0.

So, after the message is encoded into the packed array, the bits in the array will look like:
00001100 01000111 11100011 00110100 00101001 01110001 00100100 10110100 00111001 01001010 01010111 00000010

Putting these binary bytes as hexadecimal gives:
0C 47 E3 34 29 71 24 B4 39 4A 57 02

So, the original 15 bytes of the message have now been packed into 12 bytes, a reduction by 20%.

Project Specifications:
For this project, you will create a program that will pack and unpack lines of SMS text. Your program will be menu driven and contain the following options:

[P]ack and save a line of text (options are 'P' or 'p')
[U]npack and print a line of text (options are 'U' or 'u')
[Q]uit (options are 'Q' or 'q')

If the Pack and save option is chosen, you will prompt the user for two items (in this order): 1) A filename in which to save the packed array, and 2) The line of text to pack and save.
If the Unpack option is chosen, you will prompt the user for a filename which contains a saved message in the packed format.
If the Quit option is chosen... Well, do whatever you think makes the most sense.

Detailed Specifications:

  • No unpacked message will be greater than 160 characters (including the NULL terminator).
  • When entering text from the console, the newline character must be removed from the input string. One method (of many) to do this is with the following line of code (assuming the input is contained in a char array named Buffer):
    • Buffer[strlen(Buffer) - 1] = '\0'; /* Since the '\n' character is right before NULL terminator, make it a NULL terminator */
  • No packed message will be greater than 120 characters (including NULL terminator)
  • If the Pack option is chosen from the menu, the user will be prompted for a filename. The program will then prompt the user for a line of text.
    • The program will not attempt to open the file until AFTER the line of text is entered.
    • If the file cannot be opened, an appropriate message is displayed to the user, and the program execution will return to the menu.
    • If the entered filename belongs to an already existing file, the existing file will be opened, and all data in the existing file will be overwritten with the new data without any prompting or warning.
    • If the line of text entered by the user is longer than 160 characters, the text will be truncated to 159 characters and the NULL terminator will be placed in the last element of the unpacked message array. Any characters input that are beyond this length will be discarded. (Note: You will have to handle flushing the input buffer in some manner if this occurs.)
  • If the Unpack option is chosen from the menu, the user will be prompted for a filename. This file will be opened and the packed char array will be read. This array will be unpacked, and the resulting unpacked cstring will be printed to the screen.
    • If the file cannot be opened, an appropriate message is displayed to the user, and the program execution will return to the menu.
  • All appropriate file operations (fopen()/fclose()) must be performed.
  • Packed array data should be saved to the files using binary format.
    • The entire packed array (120 bytes) should be written to and read from the binary file. This means that for shorter messages, a lot of 0's will be written to the file as well as the message data. But, the number of bytes (120) in the packed array will remain constant for all messages.
  • Any printable characters entered from the console that are not part of the SCII encoding will automatically be translated to the '#' character for encoding. This character will then be unencoded as the '#' character.
    • For example, the message "Hi There CS262!" will be encoded as "H# T#### CS262!"
    • Non printable characters (such as newline, escape or backspace) will be translated to the NULL character.
    • You may assume that any encoded file that you are given (e.g. for testing purposes) will not contain any characters that are not in the SCII encoding. Any such characters will have already been translated to the # symbol before encoding.
  • Constants for array sizes (such as 160 and 120) should not be found in the body of the code. #define macros should be used.
  • Constants necessary for bit shifting purposes (such as 6, 4 and 2) may be used directly in the code.

Solutions

Expert Solution

Program Files

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sms.h"

#define BIG 160

#define LIL 120

// Set all the elements in the array to null
void ClearArray(char *array, size_t arraySize)
{
int i;
// Loop until reach the end of the array
for (i = 0; i < arraySize; i++)
{
array[i] = 0; // Set element to null
}
}

// Take each byte from the array named origArray, and pack them into the array named packedArray
void PackArray(unsigned char *packedArray, size_t packedArraySize, unsigned char *origArray, size_t origArraySize)
{
int i = 0;
int j = 0;
// Loop until reach the end of both arrays
while (i < packedArraySize && j < origArraySize)
{
packedArray[i] = origArray[j] | (origArray[j + 1] << 6); // A | B << 6
packedArray[i + 1] = (origArray[j + 1] >> 2) | (origArray[j + 2] << 4); // B >> 2 | C << 4
packedArray[i + 2] = (origArray[j + 2] >> 4) | (origArray[j + 3] << 2); // C >> 4 | D << 2
i += 3;
j += 4;
}
}

// Unpack the packed bits from the array named packedArray into individual bytes, and store each byte in the array named newArray
void UnpackArray(unsigned char *newArray, size_t newArraySize, unsigned char *packedArray, size_t packedArraySize)
{
int i = 0;
int j = 0;
// Loop until reach the end of both arrays
while (i < newArraySize && j < packedArraySize)
{
// 1 << 2, 1 >> 2
newArray[i] = packedArray[j] << 2; // Split shifts into two statements
newArray[i] = newArray[i] >> 2;
  
// 1 >> 6 | (2 << 4, 2 >> 2)
newArray[i + 1] = packedArray[j + 1] << 4; // Split shifts into two statements
newArray[i + 1] = newArray[i + 1] >> 2;   
newArray[i + 1] = (packedArray[j] >> 6) | newArray[i + 1];

// 2 >> 4 | (3 << 6, 3 >> 2)
newArray[i + 2] = packedArray[j + 2] << 6; // Split shifts into two statements
newArray[i + 2] = newArray[i + 2] >> 2;   
newArray[i + 2] = (packedArray[j + 1] >> 4) | newArray[i + 2];

// 3 >> 2
newArray[i + 3] = packedArray[j + 2] >> 2;
i += 4;
j += 3;
}
}

int main(int argc, char *argv[])
{
FILE *output, *input; // Create file variables
char buffer[BIG]; // Buffer to read input
char buffbuff[BIG]; // Extra buffer to clear the input stream
char filename[BIG]; // String to hold filenames
unsigned char unpackedArray[BIG];
unsigned char packedArray[LIL];
char choice = 0;
int on = 1;
int i;

while (on == 1)
{
printf("Pack and save a line of text...........P or p\n");
printf("Unpack and print a line of text........U or u\n");
printf("Quit the program.......................Q or q\n");
printf("\n");
printf("Enter a choice: "); // Prompt the user for valid input
fgets(buffer, BIG, stdin); // Read the input from the keyboard and store it in the input variable
sscanf(buffer, "%c", &choice); // Extract the character value from input and store it in choice
printf("\n");

switch (choice)
{
case 'P':
case 'p':
printf("Enter a filename in which to save the packed array: "); // Prompt the user for an output file
fgets(buffer, BIG, stdin); // Read the input from the keyboard and store it in the buffer
sscanf(buffer, "%s", filename); // Extract the string from buffer and store it in filename
  
ClearArray(buffbuff, sizeof(buffbuff)); // Clear the buffer
  
printf("\n");
printf("Enter a line of text to pack and save: "); // Prompt the user for a message
fgets(buffbuff, BIG, stdin); // Read the input from the keyboard and store it in the buffer
// Copy first 159 characters from buffbuff to buffer
for (i = 0; i < BIG; i++) {
buffer[i] = buffbuff[i];
}
buffer[strlen(buffer) - 1] = 0; // Set the newline character in buffer to null
// Clear the input stream
while (buffbuff[strlen(buffbuff) - 1] != '\n') {
fgets(buffbuff, BIG, stdin);
}
  
output = fopen(strcat(filename, ".sms"), "w"); // Open output file
if (output == NULL) { // Make sure file was opened properly
printf("Couldn't open the file...\n"); // Print error message
break;
}

// Convert ASCII to GMUSCII
for (i = 0; i < BIG; i++) {
unpackedArray[i] = CharToSMS((unsigned char) buffer[i]);
}

PackArray(packedArray, sizeof(packedArray), unpackedArray, sizeof(unpackedArray)); // Pack the message
fwrite(packedArray, sizeof(unsigned char), LIL, output); // Write to output file
  
printf("\n");
  
fclose(output); // Close the output file
break;
case 'U':
case 'u':
printf("Enter a filename which contains a packed array: "); // Prompt the user for an output file
fgets(buffer, BIG, stdin); // Read the input from the keyboard and store it in the buffer
sscanf(buffer, "%s", filename); // Extract the string from buffer and store it in filename
input = fopen(strcat(filename, ".sms"), "r"); // Open output file
if (input == NULL) { // Make sure file was opened properly
printf("Couldn't open the file...\n"); // Print error message
break;
}
  
ClearArray(buffer, sizeof(buffer)); // Clear the buffer

fread(packedArray, sizeof(unsigned char), LIL, input); // Read from input file
UnpackArray(unpackedArray, sizeof(unpackedArray), packedArray, sizeof(packedArray)); // Unpack the message
  
// Convert GMUSCII to ASCII
for (i = 0; i < BIG; i++) {
buffer[i] = SMSToChar(unpackedArray[i]);
}
  
printf("\n");
printf("%s\n", buffer); // Display the unpacked message
printf("\n");

fclose(input); // Close the input file
break;
case 'Q':
case 'q':
on = 0; // Exit the program

printf("Thanks for using GMU SMS System.\n");
break;   
default:
printf("Not a valid choice.\n"); // Display error message.
}
}
return 0;
}

sms.h

#ifndef SMS_H
#define SMS_H

unsigned char CharToSMS(unsigned char X);
unsigned char SMSToChar(unsigned char X);

#endif

sms.c

#include <stdio.h>
#include <stdlib.h>

unsigned char CharToSMS(unsigned char X)
{
unsigned char retVal = 0;

if (X < 32)
retVal = 0;
else if ((X >= 32) && (X <= 45))
retVal = X + 4;
else if ((X >= 46) && (X <= 47))
retVal = X - 45;
else if ((X >= 48) && (X <= 57)) // 0..9
retVal = X + 2;
else if ((X >= 58) && (X <= 64))
retVal = X - 55;
else if ((X >= 65) && (X <= 90)) // A..Z
retVal = X - 55;
else if ((X >= 91) && (X <= 93))
retVal = X - 31;
else if (X == 95)
retVal = 63; // underscore character
else
retVal = 39; // All chars not in alphabet are set to '#'

return retVal;
}

unsigned char SMSToChar(unsigned char X)
{
unsigned char retVal = 0;

if (X == 0)
retVal = 0;
else if ((X >= 1) && (X <= 2))
retVal = X + 45;
else if ((X >= 3) && (X <= 9))
retVal = X + 55;
else if ((X >= 10) && (X <= 35)) // A..Z
retVal = X + 55;
else if ((X >= 36) && (X <= 49))
retVal = X - 4;
else if ((X >= 50) && (X <= 59)) // 0..9
retVal = X - 2;
else if ((X >= 60) && (X <= 62))
retVal = X + 31;
else if (X == 63)
retVal = X + 32;
else
retVal = 0; // This statement should never be reached

return retVal;
}

output

Pack and save a line of text...........P or p
Unpack and print a line of text........U or u
Quit the program.......................Q or q

Enter a choice: p

Enter a filename in which to save the packed array: test

Enter a line of text to pack and save: Hello World

Pack and save a line of text...........P or p
Unpack and print a line of text........U or u
Quit the program.......................Q or q

Enter a choice: u

Enter a filename which contains a packed array: test

H#### W####

Pack and save a line of text...........P or p
Unpack and print a line of text........U or u
Quit the program.......................Q or q

Enter a choice: q

Thanks for using GMU SMS System.

Hope this helps!

Please let me know if any changes needed.

Thank you!


Related Solutions

IQ tests are sometimes used to assess children’s cognitive abilities. However, typical IQ tests draw on...
IQ tests are sometimes used to assess children’s cognitive abilities. However, typical IQ tests draw on reading skills, which may translate into lower scores for dyslexic children. Nonverbal IQ tests should circumvent this issue, allowing us to ask whether there is a relationship between reading skills and cognitive abilities in dyslexic children. The table below gives the reading skill scores (on a standardized scale) and the nonverbal IQ scores (also on a standardized scale) of 22 dyslexic children aged 7...
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT