Features
Encodix is a CSN.1 encoder/decoder tool designed to handle the telecom protocols encoding/decoding process.
Often, to save bandwidth, protocols require the data to be packed up in tight bit streams that make the development of protocol stack expensive and tricky.
Most companies address this issue by writing a layer that manages the packed bit streams, doing the job of unpacking the data in programmer-friendly high level data structures and vice versa. These layers will manage all the control bits, checksums, formatting rules, length indicators, etcetera. As a rule, telecom companies develop such layers on their own.
The market usually offers the following solutions:
Encodix is in the family of the code generators; but, while the others handle formal specifications only, Encodix offers the generation for the informal specifications too.
For example, ETSI 04.08, which defines encoding/decoding format for GSM layer 3, has most of the messages and information elements specified with text, tables and figures meant to be read by a human being and not a machine; the few formal parts are written in CSN.1.
Encodix defines formal grammars that resemble the original specifications.
The user can enter definition using a syntax that follows closely the layout of the official documents. This allows quick and easy message input and maintenance: people dealing with message definition are not required to understand C or any other programming language (see Picture 1).
Encodix generates as a main output ANSI C code (C++ 100% compatible). It can also generate integration code for:
Encodix, after having read a message declaration source, produce several files as output. With the default modules, they generate:
You need to just declare the messages and use them in your target language (Picture 2).
Encodix C-Output module generates plain ANSI-C that is 100% portable on any platform:
Dafocus offers some pre-written Encodix sources implementing common standards, like ETSI/3GPP Mobile radio interface Layer 3 (24.008), 3GPP2 cdma2000 Layer 3 (C.S0005), etc. These sources save further time giving a solid starting point. See the the list of available sources or contact our sales representative to have further information.
Although their acronyms are similar, CSN.1 is substantially different from ASN.1.
ASN.1 defines an abstract data structure: in other words, an ASN.1 specification tells you, for example, that we have a structure with some fields in it, or a union, an array and so on.
In ASN.1 we describe our data structure as we do when we are declaring C structs.
When we need to actually send the data over the network, we follow the encoding rules we have choosen. These encoding rules, like BER o PER, tell us that a structure is always coded in a given way, an integer is always coded in another way and so on.
Therefore any ASN.1 tools can easily generate a C data structure and some C code able to take a BER or PER coded binary stream and decode it into that C structure.
CSN.1, instead, defines at bit-level the encoded stream. A CSN.1definition says, for example, that we expect a 1 followed by 8 more bits or a 0 followed by 4 bits and another 0:
< my data > ::= 1 <x: bit(8)> | 0 <y: bit(4)> 0;
Most of the CSN.1 tools read the CSN.1 specification file and produce a CSN.1 parser; this parser, once compiled, reads the encoded binary stream and when it recognize some valid elements in it invokes a user defined callback function.
In other words, it generates code that will say to you: hey, I found the 1 followed by the 8 bits you were expecting: the 8 bits are 11001001; do whatever you like.
This is better than decoding manually, but it still means that for every CSN.1 definition, you have to manually:
This means reading and understanding thoroughly each CSN.1 specification and manually updating the data structures and call back functions everytime the CSN.1 source changes.
Encodix has a unique approach on this issue. It contains an A.I. algorithm which is able to create the data structures automatically starting from the CSN.1 definition. It also creates all the related encoding/decoding functions.
Handling CSN.1 with Encodix is now really a matter of copy and pasting the source from the standard.
Encodix standard C module is designed to produce a static C structure meant to be manually accessed by the programmer.
This suites the needs of a standard application, where each field is treated by a specific piece of code which is implementing the protocol.
Other applications, instead, have different needs.
Let's think of the case of a "message editor". This application runs on a host machine and allows the user to compose test messages by setting all the values manually: the application then encodes such messages into their binary form on a file; viceversa, the same application must be able to take a binary message and to present it in readable form to the user, for examination or further editing.
The static structures produced by the standard Encodix C module would make the development of our "message editor" quite annoying.
In fact it would force us to manually writing specific code for each specific message, information element and down to each subfield, just because we have to access the structures. This approach, applied to our "message editor" example, could partially nullify the advantage of having the automatic code generation of Encodix, because it would force us to maintain both Encodix source files and the editor code.
Here is where the Access module comes in.
The Access module generates C++ code that implements two concepts: type description and data description. We define the following Encodix source file as an example:
gsm-0407 LOCATION_UPDATING_REQUEST { ProtocolDiscriminator = 0101; MessageType = xx001000; LocationUpdatingType M V 1/2 integer; CiphKeySequenceNum M V 1/2 integer; LocationAreaId M V 5 LocationAreaId; MobileStationClassmark M V 1 MobileStationClassmark1; MobileId M LV 2-9 MobileId; 33 MobStatClsMrkForUmts O TLV 5 MobileStationClassmark2; }; bit-field MobileStationClassmark1 { size: 1 octets; bit 8: spare void default = 0; bits 6-7: RevisionLevel integer; bit 5: EsInd boolean; bit 4: A5_1 boolean; bit 1-3: RfPowerCapability integer; }
Type description is a tree of C++ objects which describes the data types. For example, a type descriptor would say something like:
A LOCATION UPDATING REQUEST message is a structure containing the following fields: LocationUpdatingType, CiphKeySequenceNum, LocationAreaId, MobileStationClassmark, etcetera;
the LocationUpdatingType field contains a 4-bit integer;
the MobileStationClassmark, instead, is itself a structure: it contains RevisionLevel, a 2-bit integer, EsInd, a boolean etcetera.
The type descriptor above can be traversed by a program that has no knowledge of the "LOCATION UPDATING REQUEST"; our hypothetical "message editor" could use that description to dynamically create an appropriate dialog box for editing such messages.
If the description of the " LOCATION UPDATING REQUEST" message changes, we just need to re-run Encodix and recompile the "message editor" application.
Once we have dynamically designed our dialog box, the user can fill out all the fields. We still have a problem: ho do we transfer all this data to Encodix so it can encode it? And how do we access the data once Encodix has decoded a binary message?
This is addressed by the "data description". The "data description" is a tree of C++ objects describing the data itself; for example, a data descriptor would say something like:
We have a LOCATION UPDATING REQUEST message; the LocationUpdatingType field is 2, while the CiphKeySequenceNum is 7; the field EsInd of MobileStationClassmark is set to FALSE, while... etc.
Once again, the data description can be traversed by out program, which can use it to fill the dialog box fields.
The diagram below summarizes how Encodix works. Several advanced features and details are hidden for simplicity.
N. | Description |
1 | Encodix users write the messages description files. These files are expressed by using a specific formal syntax which resembles ETSI specification documents: this makes really easy to write and maintain message description files. In this way, all messages and information elements are specified down to bit-level detail. In this example, we declare a simple GSM-04.07 message: gsm-0407 MY_MESSAGE { ProtocolDiscriminator = 0101; MessageType = 1X001101; 4- foo M TV 1 integer; /* 4 bit integer */ 23 bar O TLV 3 MY_SUB_FIELD; /* see below */ } /* Custom sub field */ bit-field MY_SUB_FIELD { size: 1 octet; bits 1-5 partA integer; bits 6-8 partB integer; } |
2 | Encodix is an executable which reads the messages description file (1). It produces some .h and .c files (.pr files if SDL output is activated). |
3 | The data structure file is a .h file generated by Encodix (2) which contains one C typedef per each message and information element defined in the messages description file (1). typedef struct _c_MY_SUB_FIELD { int partA; int partB; } c_MY_SUB_FIELD; typedef struct _c_MY_MESSAGE { int foo; c_MY_SUB_FIELD bar; ED_BOOLEAN bar_Present; } c_MY_MESSAGE; |
4 | Encodix (2) generates an encoding C function per each message. long ENCODE_c_MY_MESSAGE (char* Buffer, const c_MY_MESSAGE* Source); |
5 | Encodix (2) generates an decoding C function per each message. long DECODE_c_MY_MESSAGE (const char* Buffer, c_MY_MESSAGE* Destin, long Length); |
6 | A char buffer must be provided when encoding and decoding in order to store the encoded version. See the examples below and above. |
7/8 |
Once code is generated, users can encode messages. char Buffer [50]; c_MY_MESSAGE myMsg; long LenInBits; myMsg.foo = 2; myMsg.bar.partA = 3; myMsg.bar.partB = 4; myMsg.bar_Present = ED_TRUE; /* Optional field: mark it as present! */ LenInBits = ENCODE_c_MY_MESSAGE (Buffer, &myMsg); /* Now "LenInBits" contains the length of the encoded message expressed in bits. Buffer contains an encoded message */ We had to specify only sensible data, without having care of writing protocol discriminator, skip indicator, information elements tags and lenghts and bit alignments: all these tasks are performed by the Encodix generated function. |
9/10 |
Same thing happens when we need to decode. /* Buffer is a const char* containing the message */ /* Len is a long containing the buffer length from the layer below */ c_MY_MESSAGE myMsg; DECODE_c_MY_MESSAGE (Buffer, &myMsg, Length); /* Now myMsg contains the decoded data: use it somehow */ printf ("foo is %d\n", myMsg.foo); |