deSwapNibbles = function (nibbles) {
var out = '';
for(var i = 0; i< nibbles.length; i=i+2) {
if(nibbles[i] === 'F') //Dont consider trailing F.
out += parseInt(nibbles[i+1], 16).toString(10);
else
out += parseInt(nibbles[i+1], 16).toString(10)+parseInt(nibbles[i], 16).toString(10);
}
return out;
}...
if(senderSize % 2 === 1)
senderSize++;
obj.sender_type = parseInt(buffer[2]).toString(16);
if (obj.sender_type === 'd0') {
obj.sender = this.decode7Bit(pdu.slice(cursor, cursor+senderSize), Math.floor(senderSize*4/7)).trim();
} else {
obj.sender = pduParser.deSwapNibbles(pdu.slice(cursor, cursor+senderSize));
}
cursor += senderSize;
var protocolIdentifier = pdu.slice(cursor, cursor+2);
cursor += 2;
var dataCodingScheme = pdu.slice(cursor, cursor+2);
...decode16Bit = function (data, length) {
//We are getting ucs2 characters.
var ucs2 = '';
for(var i = 0;i<=data.length-1;i=i+4) {
ucs2 += String.fromCharCode("0x"+data[i]+data[i+1]+data[i+2]+data[i+3]);
}
return ucs2;
}...
processed = true;
if (status === 1) { //cancel USSD session
this.sendCommand('AT+CUSD=2');
}
var encoding = Pdu.detectEncoding(dcs);
var text = '';
if (encoding === '16bit') {
text = Pdu.decode16Bit(data);
} else if (encoding === '7bit') {
text = Pdu.decode7Bit(data);
} else {
cb(new Error('Unknown encoding'));
return;
}
cb(undefined, text);
...decode7Bit = function (code, length, unPadding) {
//We are getting 'septeps'. We should decode them.
var binary = '';
for(var i = 0; i<code.length;i++)
binary += ('0000'+parseInt(code.slice(i,i+1), 16).toString(2)).slice(-4);
//This step is for 'unpadding' the padded data. If it has been encoded with 1 bit padding as it
//happens when the sender used a 7-bit message concatenation (cf http://mobiletidings.com/2009/02/18/combining-sms-messages/)
if (unPadding){
var binary2 = '';
binary = binary + '00000000';
for (var i=0; i<binary.length/8 - 1 ; i++)
{
binary2 += (binary.slice((i+1)*8+(8-unPadding), (i+2)*8) + binary.slice(i*8,i*8+(8-unPadding)));
}
binary = binary2;
}
var bin = Array();
var cursor = 0;
var fromPrevious = '';
var i = 0;
while(binary[i]) {
var remaining = 7 - fromPrevious.length;
var toNext = 8 - remaining;
bin[i] = binary.slice(cursor+toNext, cursor+toNext+remaining) + fromPrevious;
var fromPrevious = binary.slice(cursor, cursor+toNext);
if(toNext === 8)
fromPrevious = '';
else
cursor += 8;
i++;
}
var ascii = '';
var esc = false; //last character was a ESC
for(var i=0; i<length; i++){
var codeNum = parseInt(bin[i], 2);
if (codeNum == 0x1B){
esc = true;
continue;
}
if (esc)
ascii += sevenBitEsc[codeNum];
else
ascii += sevenBitDefault[codeNum];
esc = false;
}
return ascii;
}...
this.sendCommand('AT+CUSD=2');
}
var encoding = Pdu.detectEncoding(dcs);
var text = '';
if (encoding === '16bit') {
text = Pdu.decode16Bit(data);
} else if (encoding === '7bit') {
text = Pdu.decode7Bit(data);
} else {
cb(new Error('Unknown encoding'));
return;
}
cb(undefined, text);
}.bind(this);
...detectEncoding = function (dataCodingScheme) {
if (typeof dataCodingScheme === 'string') dataCodingScheme = parseInt(dataCodingScheme, 16);
var binary = ('00000000'+(dataCodingScheme.toString(2))).slice(-8);
if(binary == '00000000')
return '7bit';
// if(binary.slice(0, 2) === '00') {
var compressed = binary.slice(2, 1) === '1';
var bitsHaveMeaning = binary.slice(3, 1) === '1';
if(binary.slice(4,6) === '00')
return '7bit';
if(binary.slice(4,6) === '01')
return '8bit';
if(binary.slice(4,6) === '10')
return '16bit';
// }
}...
if (data.indexOf('OK') !== -1) {
var processed = false;
var USSDHandler = function (status, data, dcs) {
processed = true;
if (status === 1) { //cancel USSD session
this.sendCommand('AT+CUSD=2');
}
var encoding = Pdu.detectEncoding(dcs);
var text = '';
if (encoding === '16bit') {
text = Pdu.decode16Bit(data);
} else if (encoding === '7bit') {
text = Pdu.decode7Bit(data);
} else {
cb(new Error('Unknown encoding'));
...encode16Bit = function (inTextNumberArray) {
var out = '';
for(var i = 0; i<inTextNumberArray.length;i++) {
out += ('0000'+(inTextNumberArray[i].toString(16))).slice(-4);
}
return out;
}...
} else if(message.encoding === '8bit') {
}
var text = inTextNumberArray.slice(i*length, (i*length)+length);
var user_data;
if(message.encoding === '16bit') {
user_data = pduParser.encode16Bit(text);
var size = (user_data.length / 2);
if(parts > 1)
size += 6; //6 is the number of data headers we append.
} else if(message.encoding === '7bit') {
if(parts > 1){
...encode7Bit = function (inTextNumberArray, paddingBits)
{
//as explained here http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/
var paddingBits = paddingBits || 0;
var bits = 0;
var out = "";
if(paddingBits)
{
bits = 7 - paddingBits;
var octet = (inTextNumberArray[0] << (7 - bits)) % 256
out += ('00' + octet.toString(16)).slice(-2);
bits++;
}
for(var i = 0; i < inTextNumberArray.length; i++ )
{
if( bits == 7 )
{
bits = 0;
continue;
}
var octet = (inTextNumberArray[i] & 0x7f) >> bits;
if( i < inTextNumberArray.length - 1 )
{octet |= (inTextNumberArray[i + 1] << (7 - bits))%256;}
out += ('00' + octet.toString(16)).slice(-2);
bits++;
}
return out;
}...
var size = (user_data.length / 2);
if(parts > 1)
size += 6; //6 is the number of data headers we append.
} else if(message.encoding === '7bit') {
if(parts > 1){
user_data = pduParser.encode7Bit(text,1);
var size = 7 + text.length;
}
else {
user_data = pduParser.encode7Bit(text);
var size = text.length;
}
}
...generate = function (message) {
var smsc = '';
var smscPartLength = 0;
if (message.smsc!==undefined){
if (message.smsc_type!==undefined && (message.smsc_type==0x81 || message.smsc_type==0x91)){
smsc += message.smsc_type.toString (16);
} else {
smsc += '81';
}
smsc += this.swapNibbles(message.smsc);
var smsc_length = octetLength(smsc);
smsc = smsc_length + smsc;
} else {
smsc = '00';
}
var pdu = smsc;
smscPartLength = smsc.length;
var parts = 1;
var inTextNumberArray = this.messageToNumberArray(message);
if(message.encoding === '16bit' && inTextNumberArray.length > 70)
parts = inTextNumberArray.length / 66;
else if(message.encoding === '7bit' && inTextNumberArray.length > 160)
parts = inTextNumberArray.length / 153;
parts = Math.ceil(parts);
TPMTI = 1<<0; //(2 bits) type msg, 1=submit by MS
TPRD = 1<<2; //(1 bit) reject duplicates
TPVPF = 1<<3; //(2 bits) validaty f. : 0=not pres, 1=enhanc,2=relative,3=absolute
TPSRR = 1<<5; //(1 bit) want status reply
TPUDHI = 1<<6; //(1 bit) 1=header+data, 0=only data
TPRP = 1<<7; //(1 bit) reply-path
var submit = TPMTI;
if(parts > 1) //UDHI
submit = submit | TPUDHI;
if (message.request_status!==undefined && message.request_status)
submit = submit | TPSRR;
pdu += ('00'+submit.toString(16)).slice(-2);
pdu += '00'; //Reference Number;
var receiverSize = ('00'+(parseInt(message.receiver.length, 10).toString(16))).slice(-2);
var receiver = pduParser.swapNibbles(message.receiver);
//Destination MSISDN type
var receiverType;
if (message.receiver_type !== undefined && (message.receiver_type === 0x81 || message.receiver_type === 0x91)){
receiverType = message.receiver_type.toString(16);
} else {
receiverType = 81;
}
pdu += receiverSize.toString(16) + receiverType + receiver;
pdu += '00'; //TODO TP-PID
if(message.encoding === '16bit')
pdu += '08';
else if(message.encoding === '7bit')
pdu += '00';
var pdus = new Array();
var csms = randomHexa(2); // CSMS allows to give a reference to a concatenated message
for(var i=0; i< parts; i++) {
pdus[i] = pdu;
if(message.encoding === '16bit') {
/* If there are more than one messages to be sent, we are going to have to put some UDH. Then, we would have space only
* for 66 UCS2 characters instead of 70 */
if(parts === 1)
var length = 70;
else
var length = 66;
} else if(message.encoding === '7bit') {
/* If there are more than one messages to be sent, we are going to have to put some UDH. Then, we would have space only
* for 153 ASCII characters instead of 160 */
if(parts === 1)
var length = 160;
else
var length = 153;
} else if(message.encoding === '8bit') {
}
var text = inTextNumberArray.slice(i*length, (i*length)+length);
var user_data;
if(message.encoding === '16bit') {
user_data = pduParser.encode16Bit(text);
var size = (user_data.length / 2);
if(parts > 1)
size += 6; //6 is the number of data headers we append.
} else if(message.encoding === '7bit') {
if(parts > 1){
user_data = pduParser.encode7Bit(text,1);
var size = 7 + text.length;
}
else {
user_data = pduParser.encode7Bit(text);
var size = text.length;
}
}
pdus[i] += ('00'+parseInt(size).toString(16)).slice(-2);
// UDHI control header for concaterating message's parts
if(parts > 1) {
pdus[i] += '05';
pdus[i] += '00';
pdus[i] += '03';
pdus[i] += csms;
pdus[i] += ('00'+parts.toString(16)).slice(-2);
pdus[i] += ('0 ......
opts.smsc_type = 0x81;
}
if (opts.encoding === undefined) {
opts.encoding = isGSMAlphabet(opts.text) ? '7bit' : '16bit';
}
var encoded = Pdu.generate(opts);
var queue = new PartsSendQueue(this, encoded, cb);
queue.sendNext();
}
//TODO: make textmode
};
Modem.prototype.deleteAllSMS = function (cb) {
...messageToNumberArray = function (message) //sp
{
//7bit GSM encoding according to GSM_03.38 character set http://en.wikipedia.org/wiki/GSM_03.38
res = [];
for (var k=0; k<message.text.length; k++)
{
if (message.encoding == '7bit'){
var character = message.text[k];
for(var i=0;i<sevenBitDefault.length;i++)
{
if(sevenBitDefault[i] == character)
res.push(i);
if (sevenBitEsc[i] == character){
res.push(0x1B); //escape character
res.push(i);
}
}
}
else if (message.encoding == '16bit')
res.push(message.text.charCodeAt(k));
}
return res;
}...
} else {
smsc = '00';
}
var pdu = smsc;
smscPartLength = smsc.length;
var parts = 1;
var inTextNumberArray = this.messageToNumberArray(message);
if(message.encoding === '16bit' && inTextNumberArray.length > 70)
parts = inTextNumberArray.length / 66;
else if(message.encoding === '7bit' && inTextNumberArray.length > 160)
parts = inTextNumberArray.length / 153;
...parse = function (pdu) {
//Cursor points to the last octet we've read.
var cursor = 0;
var obj = parseSMSCPart (pdu);
obj.smsc_tpdu = pdu;
cursor += obj.length;
var buffer = new Buffer(pdu.slice(cursor,cursor+6), 'hex');
cursor += 6;
var smsDeliver = parseInt(buffer[0]);
var smsDeliverBits = ("00000000"+parseInt(smsDeliver).toString(2)).slice(-8);
var tp_mti = smsDeliverBits.slice(-2);
obj.tpdu_type = TP_MTI_To_String (tp_mti);
if (tp_mti=='10'){ //SMS-STATUS-REPORT
return this.parseStatusReport (pdu.slice(cursor-6), obj);
}
var udhi = smsDeliverBits.slice(1,2) === "1";
var senderSize = buffer[1];
if(senderSize % 2 === 1)
senderSize++;
obj.sender_type = parseInt(buffer[2]).toString(16);
if (obj.sender_type === 'd0') {
obj.sender = this.decode7Bit(pdu.slice(cursor, cursor+senderSize), Math.floor(senderSize*4/7)).trim();
} else {
obj.sender = pduParser.deSwapNibbles(pdu.slice(cursor, cursor+senderSize));
}
cursor += senderSize;
var protocolIdentifier = pdu.slice(cursor, cursor+2);
cursor += 2;
var dataCodingScheme = pdu.slice(cursor, cursor+2);
cursor = cursor+2;
obj.dcs = parseInt(dataCodingScheme, 16);
obj.encoding = pduParser.detectEncoding(dataCodingScheme);
obj.time = parseTS (pdu.slice(cursor, cursor+14));
cursor += 14;
var dataLength = parseInt(pdu.slice(cursor, cursor+2), 16).toString(10);
cursor += 2;
if(udhi) { //User-Data-Header-Indicator: means there's some User-Data-Header.
var udhLength = pdu.slice(cursor, cursor+2);
var iei = pdu.slice(cursor+2, cursor+4);
var headerLength, referenceNumber, parts, currentPart;
if(iei == "00") { //Concatenated sms.
headerLength = pdu.slice(cursor+4, cursor+6);
referenceNumber = pdu.slice(cursor+6, cursor+8);
parts = pdu.slice(cursor+8, cursor+10);
currentPart = pdu.slice(cursor+10, cursor+12);
}
if(iei == "08") { //Concatenaded sms with a two-bytes reference number
headerLength = pdu.slice(cursor+4, cursor+6);
referenceNumber = pdu.slice(cursor+6, cursor+10);
parts = pdu.slice(cursor+10, cursor+12);
currentPart = pdu.slice(cursor+12, cursor+14);
}
/*if(iei == '00')
cursor += (udhLength-2)*4;
else if(iei == '08')
cursor += ((udhLength-2)*4)+2;
else
cursor += (udhLength-2)*2;*/
cursor = cursor + (parseInt(udhLength, 16) + 1) * 2;
}
if(obj.encoding === '16bit')
var text = pduParser.decode16Bit(pdu.slice(cursor), dataLength);
else if(obj.encoding === '7bit')
if (udhi && iei=='00') var text = pduParser.decode7Bit(pdu.slice(cursor), dataLength-7, 1); //If iei ==0, then there is
some unpadding to do
else if (udhi && iei=='08') var text = pduParser.decode7Bit(pdu.slice(cursor), dataLength-8); //If no udhi or iei = 08 then
no unpadding to do
else var text = pduParser.decode7Bit(pdu.slice(cursor), dataLength);
else if(obj.encoding === '8bit')
var text = ''; //TODO
obj.text = text;
if(udhi) {
obj['udh'] = {
'length' : udhLength,
'iei' : iei,
};
if(iei == '00' || iei == '08') {
obj['udh']['reference_number'] = referenceNumber;
obj['udh']['parts'] = parseInt(parts);
obj['udh']['current_part'] = parseInt(currentPart);
}
}
return obj;
}...
for(var i=0; i<msg.udh.parts; i++) {
this.messageParts[msg.udh.reference_number].text.push("");
}
}
this.messageParts[msg.udh.reference_number].text[msg.udh.current_part - 1] = msg.text;
this.messageParts[msg.udh.reference_number].parts_remaining--;
if(this.messageParts[msg.udh.reference_number].parts_remaining === 0) {
var nmsg = JSON.parse(JSON.stringify(msg));
delete nmsg.smsc_tpdu;
delete nmsg.tpdu_type;
delete nmsg.udh;
nmsg.text = "";
...parseStatusReport = function (pdu, smsc_parsed) {
var cursor = 0;
var obj = smsc_parsed;
var header = parseInt(pdu.slice(cursor,cursor+2));
cursor += 2;
//TODO: maybe SMS-COMMAND here
obj.reference = parseInt(pdu.slice(cursor,cursor+2), 16);
cursor += 2;
var senderSize = parseInt(pdu.slice(cursor,cursor+2), 16);
if(senderSize % 2 === 1)
senderSize++;
cursor += 2;
obj.sender_type = parseInt(pdu.slice(cursor,cursor+2));
cursor += 2;
obj.sender = pduParser.deSwapNibbles(pdu.slice(cursor, cursor+senderSize));
cursor += senderSize;
obj.smsc_ts = parseTS(pdu.slice(cursor, cursor+14));
cursor += 14;
obj.discharge_ts = parseTS(pdu.slice(cursor, cursor+14));
cursor += 14;
obj.status = pdu.slice(cursor, cursor+2);
return obj;
}n/a
swapNibbles = function (nibbles) {
var out = '';
for(var i = 0; i< nibbles.length; i=i+2) {
if(typeof(nibbles[i+1]) === 'undefined') // Add a trailing F.
out += 'F'+parseInt(nibbles[i], 16).toString(10);
else
out += parseInt(nibbles[i+1], 16).toString(10)+parseInt(nibbles[i], 16).toString(10);
}
return out;
}...
if (message.smsc!==undefined){
if (message.smsc_type!==undefined && (message.smsc_type==0x81 || message.smsc_type==0x91)){
smsc += message.smsc_type.toString (16);
} else {
smsc += '81';
}
smsc += this.swapNibbles(message.smsc);
var smsc_length = octetLength(smsc);
smsc = smsc_length + smsc;
} else {
smsc = '00';
}
var pdu = smsc;
smscPartLength = smsc.length;
...ussdEncode = function (ussd) {
var arr = this.messageToNumberArray ({text:ussd,encoding:'7bit'});
return this.encode7Bit(arr).toUpperCase();
}...
};
/**
* Requests custom USSD
*/
Modem.prototype.getUSSD = function (ussd, cb) {
if (this.manufacturer.indexOf('HUAWEI') !== -1) {
ussd = Pdu.ussdEncode(ussd);
}
this.sendCommand('AT+CUSD=1,"' + ussd + '",15', function (data) {
if (data.indexOf('OK') !== -1) {
var processed = false;
var USSDHandler = function (status, data, dcs) {
processed = true;
if (status === 1) { //cancel USSD session
...