gsmstack: add new unfinished GSM burst/channel demultiplex
This is some unfinished work on a new codebase for 'proper' demultiplex of all the various physical and logical channel types. Basically this includes everything needed to get from the differential-decoded bursts up to the MAC blocks that can be passed up to layer 2.
This commit is contained in:
parent
753c2fc0f6
commit
fd405f7994
|
@ -0,0 +1,483 @@
|
|||
|
||||
/* This file was taken from gsm-tvoid */
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "burst_types.h"
|
||||
#include "cch.h"
|
||||
#include "fire_crc.h"
|
||||
|
||||
/*
|
||||
* GSM SACCH -- Slow Associated Control Channel
|
||||
*
|
||||
* These messages are encoded exactly the same as on the BCCH.
|
||||
* (Broadcast Control Channel.)
|
||||
*
|
||||
* Input: 184 bits
|
||||
*
|
||||
* 1. Add parity and flushing bits. (Output 184 + 40 + 4 = 228 bit)
|
||||
* 2. Convolutional encode. (Output 228 * 2 = 456 bit)
|
||||
* 3. Interleave. (Output 456 bit)
|
||||
* 4. Map on bursts. (4 x 156 bit bursts with each 2x57 bit content data)
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Parity (FIRE) for the GSM SACCH channel.
|
||||
*
|
||||
* g(x) = (x^23 + 1)(x^17 + x^3 + 1)
|
||||
* = x^40 + x^26 + x^23 + x^17 + x^3 + 1
|
||||
*/
|
||||
|
||||
static const unsigned char parity_polynomial[PARITY_SIZE + 1] = {
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 0,
|
||||
0, 1, 0, 0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0, 0,
|
||||
1
|
||||
};
|
||||
|
||||
// remainder after dividing data polynomial by g(x)
|
||||
static const unsigned char parity_remainder[PARITY_SIZE] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
static void parity_encode(unsigned char *d, unsigned char *p) {
|
||||
|
||||
int i;
|
||||
unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
|
||||
|
||||
memcpy(buf, d, DATA_BLOCK_SIZE);
|
||||
memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE);
|
||||
|
||||
for(q = buf; q < buf + DATA_BLOCK_SIZE; q++)
|
||||
if(*q)
|
||||
for(i = 0; i < PARITY_SIZE + 1; i++)
|
||||
q[i] ^= parity_polynomial[i];
|
||||
for(i = 0; i < PARITY_SIZE; i++)
|
||||
p[i] = !buf[DATA_BLOCK_SIZE + i];
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static int parity_check(unsigned char *d) {
|
||||
|
||||
unsigned int i;
|
||||
unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q;
|
||||
|
||||
memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE);
|
||||
|
||||
for(q = buf; q < buf + DATA_BLOCK_SIZE; q++)
|
||||
if(*q)
|
||||
for(i = 0; i < PARITY_SIZE + 1; i++)
|
||||
q[i] ^= parity_polynomial[i];
|
||||
return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convolutional encoding and Viterbi decoding for the GSM SACCH channel.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convolutional encoding:
|
||||
*
|
||||
* G_0 = 1 + x^3 + x^4
|
||||
* G_1 = 1 + x + x^3 + x^4
|
||||
*
|
||||
* i.e.,
|
||||
*
|
||||
* c_{2k} = u_k + u_{k - 3} + u_{k - 4}
|
||||
* c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4}
|
||||
*/
|
||||
#define K 5
|
||||
#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1)
|
||||
|
||||
|
||||
/*
|
||||
* Given the current state and input bit, what are the output bits?
|
||||
*
|
||||
* encode[current_state][input_bit]
|
||||
*/
|
||||
static const unsigned int encode[1 << (K - 1)][2] = {
|
||||
{0, 3}, {3, 0}, {3, 0}, {0, 3},
|
||||
{0, 3}, {3, 0}, {3, 0}, {0, 3},
|
||||
{1, 2}, {2, 1}, {2, 1}, {1, 2},
|
||||
{1, 2}, {2, 1}, {2, 1}, {1, 2}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Given the current state and input bit, what is the next state?
|
||||
*
|
||||
* next_state[current_state][input_bit]
|
||||
*/
|
||||
static const unsigned int next_state[1 << (K - 1)][2] = {
|
||||
{0, 8}, {0, 8}, {1, 9}, {1, 9},
|
||||
{2, 10}, {2, 10}, {3, 11}, {3, 11},
|
||||
{4, 12}, {4, 12}, {5, 13}, {5, 13},
|
||||
{6, 14}, {6, 14}, {7, 15}, {7, 15}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Given the previous state and the current state, what input bit caused
|
||||
* the transition? If it is impossible to transition between the two
|
||||
* states, the value is 2.
|
||||
*
|
||||
* prev_next_state[previous_state][current_state]
|
||||
*/
|
||||
static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = {
|
||||
{ 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
|
||||
{ 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2},
|
||||
{ 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
|
||||
{ 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2},
|
||||
{ 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
|
||||
{ 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2},
|
||||
{ 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
|
||||
{ 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2},
|
||||
{ 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
|
||||
{ 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2},
|
||||
{ 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
|
||||
{ 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2},
|
||||
{ 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
|
||||
{ 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2},
|
||||
{ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1},
|
||||
{ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}
|
||||
};
|
||||
|
||||
|
||||
static inline unsigned int hamming_distance2(unsigned int w) {
|
||||
|
||||
return (w & 1) + !!(w & 2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static void conv_encode(unsigned char *data, unsigned char *output) {
|
||||
|
||||
unsigned int i, state = 0, o;
|
||||
|
||||
// encode data
|
||||
for(i = 0; i < CONV_INPUT_SIZE; i++) {
|
||||
o = encode[state][data[i]];
|
||||
state = next_state[state][data[i]];
|
||||
*output++ = !!(o & 2);
|
||||
*output++ = o & 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static int conv_decode(unsigned char *output, unsigned char *data) {
|
||||
|
||||
int i, t;
|
||||
unsigned int rdata, state, nstate, b, o, distance, accumulated_error,
|
||||
min_state, min_error, cur_state;
|
||||
|
||||
unsigned int ae[1 << (K - 1)];
|
||||
unsigned int nae[1 << (K - 1)]; // next accumulated error
|
||||
unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1];
|
||||
|
||||
// initialize accumulated error, assume starting state is 0
|
||||
for(i = 0; i < (1 << (K - 1)); i++)
|
||||
ae[i] = nae[i] = MAX_ERROR;
|
||||
ae[0] = 0;
|
||||
|
||||
// build trellis
|
||||
for(t = 0; t < CONV_INPUT_SIZE; t++) {
|
||||
|
||||
// get received data symbol
|
||||
rdata = (data[2 * t] << 1) | data[2 * t + 1];
|
||||
|
||||
// for each state
|
||||
for(state = 0; state < (1 << (K - 1)); state++) {
|
||||
|
||||
// make sure this state is possible
|
||||
if(ae[state] >= MAX_ERROR)
|
||||
continue;
|
||||
|
||||
// find all states we lead to
|
||||
for(b = 0; b < 2; b++) {
|
||||
|
||||
// get next state given input bit b
|
||||
nstate = next_state[state][b];
|
||||
|
||||
// find output for this transition
|
||||
o = encode[state][b];
|
||||
|
||||
// calculate distance from received data
|
||||
distance = hamming_distance2(rdata ^ o);
|
||||
|
||||
// choose surviving path
|
||||
accumulated_error = ae[state] + distance;
|
||||
if(accumulated_error < nae[nstate]) {
|
||||
|
||||
// save error for surviving state
|
||||
nae[nstate] = accumulated_error;
|
||||
|
||||
// update state history
|
||||
state_history[nstate][t + 1] = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get accumulated error ready for next time slice
|
||||
for(i = 0; i < (1 << (K - 1)); i++) {
|
||||
ae[i] = nae[i];
|
||||
nae[i] = MAX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// the final state is the state with the fewest errors
|
||||
min_state = (unsigned int)-1;
|
||||
min_error = MAX_ERROR;
|
||||
for(i = 0; i < (1 << (K - 1)); i++) {
|
||||
if(ae[i] < min_error) {
|
||||
min_state = i;
|
||||
min_error = ae[i];
|
||||
}
|
||||
}
|
||||
|
||||
// trace the path
|
||||
cur_state = min_state;
|
||||
for(t = CONV_INPUT_SIZE; t >= 1; t--) {
|
||||
min_state = cur_state;
|
||||
cur_state = state_history[cur_state][t]; // get previous
|
||||
output[t - 1] = prev_next_state[cur_state][min_state];
|
||||
}
|
||||
|
||||
// return the number of errors detected (hard-decision)
|
||||
return min_error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GSM SACCH interleaving and burst mapping
|
||||
*
|
||||
* Interleaving:
|
||||
*
|
||||
* Given 456 coded input bits, form 4 blocks of 114 bits:
|
||||
*
|
||||
* i(B, j) = c(n, k) k = 0, ..., 455
|
||||
* n = 0, ..., N, N + 1, ...
|
||||
* B = B_0 + 4n + (k mod 4)
|
||||
* j = 2(49k mod 57) + ((k mod 8) div 4)
|
||||
*
|
||||
* Mapping on Burst:
|
||||
*
|
||||
* e(B, j) = i(B, j)
|
||||
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
|
||||
* e(B, 57) = h_l(B)
|
||||
* e(B, 58) = h_n(B)
|
||||
*
|
||||
* Where h_l(B) and h_n(B) are bits in burst B indicating flags.
|
||||
*/
|
||||
|
||||
/*
|
||||
static void interleave(unsigned char *data, unsigned char *iBLOCK) {
|
||||
|
||||
int j, k, B;
|
||||
|
||||
// for each bit in input data
|
||||
for(k = 0; k < CONV_SIZE; k++) {
|
||||
B = k % 4;
|
||||
j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
|
||||
iBLOCK[B * iBLOCK_SIZE + j] = data[k];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#if 0
|
||||
static void decode_interleave(unsigned char *data, unsigned char *iBLOCK) {
|
||||
|
||||
int j, k, B;
|
||||
|
||||
for(k = 0; k < CONV_SIZE; k++) {
|
||||
B = k % 4;
|
||||
j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
|
||||
data[k] = iBLOCK[B * iBLOCK_SIZE + j];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
static void burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK,
|
||||
unsigned char hl, unsigned char hn) {
|
||||
|
||||
int j;
|
||||
|
||||
for(j = 0; j < 57; j++) {
|
||||
eBLOCK[j] = iBLOCK[j];
|
||||
eBLOCK[j + 59] = iBLOCK[j + 57];
|
||||
}
|
||||
eBLOCK[57] = hl;
|
||||
eBLOCK[58] = hn;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static void decode_burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK,
|
||||
unsigned char *hl, unsigned char *hn) {
|
||||
|
||||
int j;
|
||||
|
||||
for(j = 0; j < 57; j++) {
|
||||
iBLOCK[j] = eBLOCK[j];
|
||||
iBLOCK[j + 57] = eBLOCK[j + 59];
|
||||
}
|
||||
*hl = eBLOCK[57];
|
||||
*hn = eBLOCK[58];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transmitted bits are sent least-significant first.
|
||||
*/
|
||||
static int compress_bits(unsigned char *dbuf, unsigned int dbuf_len,
|
||||
unsigned char *sbuf, unsigned int sbuf_len) {
|
||||
|
||||
unsigned int i, j, c, pos = 0;
|
||||
|
||||
if(dbuf_len < ((sbuf_len + 7) >> 3))
|
||||
return -1;
|
||||
|
||||
for(i = 0; i < sbuf_len; i += 8) {
|
||||
for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++)
|
||||
c |= (!!sbuf[i + j]) << j;
|
||||
dbuf[pos++] = c & 0xff;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
int get_ns_l3_len(unsigned char *data, unsigned int datalen) {
|
||||
|
||||
if((data[0] & 3) != 1) {
|
||||
fprintf(stderr, "error: get_ns_l3_len: pseudo-length reserved "
|
||||
"bits bad (%2.2x)\n", data[0] & 3);
|
||||
return -1;
|
||||
}
|
||||
return (data[0] >> 2);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static unsigned char *decode_sacch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
|
||||
|
||||
int errors, len, data_size;
|
||||
unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS][iBLOCK_SIZE],
|
||||
hl, hn, decoded_data[PARITY_OUTPUT_SIZE];
|
||||
FC_CTX fc_ctx;
|
||||
|
||||
data_size = sizeof ctx->msg;
|
||||
if(datalen)
|
||||
*datalen = 0;
|
||||
|
||||
// unmap the bursts
|
||||
decode_burstmap(iBLOCK[0], burst, &hl, &hn); // XXX ignore stealing bits
|
||||
decode_burstmap(iBLOCK[1], burst + 116, &hl, &hn);
|
||||
decode_burstmap(iBLOCK[2], burst + 116 * 2, &hl, &hn);
|
||||
decode_burstmap(iBLOCK[3], burst + 116 * 3, &hl, &hn);
|
||||
|
||||
// remove interleave
|
||||
interleave_decode(&ctx->interleave_ctx, conv_data, (unsigned char *)iBLOCK);
|
||||
//decode_interleave(conv_data, (unsigned char *)iBLOCK);
|
||||
|
||||
// Viterbi decode
|
||||
errors = conv_decode(decoded_data, conv_data);
|
||||
if (errors) {
|
||||
DEBUGF("conv_decode: %d\n", errors);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check parity
|
||||
// If parity check error detected try to fix it.
|
||||
if (parity_check(decoded_data))
|
||||
{
|
||||
FC_init(&fc_ctx, 40, 184);
|
||||
unsigned char crc_result[224];
|
||||
if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0)
|
||||
{
|
||||
errors = -1;
|
||||
DEBUGF("error: sacch: parity error (%d)\n", errors);
|
||||
return NULL;
|
||||
} else {
|
||||
DEBUGF("Successfully corrected parity bits!\n");
|
||||
memcpy(decoded_data, crc_result, sizeof crc_result);
|
||||
errors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if((len = compress_bits(ctx->msg, data_size, decoded_data,
|
||||
DATA_BLOCK_SIZE)) < 0) {
|
||||
fprintf(stderr, "error: compress_bits\n");
|
||||
return NULL;
|
||||
}
|
||||
if(len < data_size) {
|
||||
fprintf(stderr, "error: buf too small (%d < %d)\n",
|
||||
sizeof(ctx->msg), len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(datalen)
|
||||
*datalen = (unsigned int)len;
|
||||
return ctx->msg;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* decode_cch
|
||||
*
|
||||
* Decode a "common" control channel. Most control channels use
|
||||
* the same burst, interleave, Viterbi and parity configuration.
|
||||
* The documentation for the control channels defines SACCH first
|
||||
* and then just keeps referring to that.
|
||||
*
|
||||
* The current (investigated) list is as follows:
|
||||
*
|
||||
* BCCH Norm
|
||||
* BCCH Ext
|
||||
* PCH
|
||||
* AGCH
|
||||
* CBCH (SDCCH/4)
|
||||
* CBCH (SDCCH/8)
|
||||
* SDCCH/4
|
||||
* SACCH/C4
|
||||
* SDCCH/8
|
||||
* SACCH/C8
|
||||
*
|
||||
* We provide two functions, one for where all four bursts are
|
||||
* contiguous, and one where they aren't.
|
||||
*/
|
||||
unsigned char *decode_cch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
|
||||
|
||||
return decode_sacch(ctx, burst, datalen);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
unsigned char *decode_cch(GS_CTX *ctx, unsigned char *e, unsigned int *datalen) {
|
||||
|
||||
return decode_sacch(ctx, e, e + eBLOCK_SIZE, e + 2 * eBLOCK_SIZE,
|
||||
e + 3 * eBLOCK_SIZE, datalen);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,143 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "gsmstack.h"
|
||||
|
||||
/* convert an 18byte 8-bit-per-byte burst to a 142byte 1bit-per-byte */
|
||||
static void bit_per_byte(unsigned char *dest, const unsigned char *src)
|
||||
{
|
||||
int bit, byte;
|
||||
|
||||
for (byte = 0; byte < BURST_BYTES; src++) {
|
||||
for (bit = 0; bit < 8; bit++) {
|
||||
if (src[byte] >> bit &= 0x01)
|
||||
*dest++ = 0x01;
|
||||
else
|
||||
*dest++ = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int add_burst_to_lchan(struct gsm_logi_chan *lchan,
|
||||
struct gsm_burst *burst)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* copy burst to burst buffer */
|
||||
memcpy(lchan->burst_buf[lchan->next_burst], burst, sizeof(*burst));
|
||||
lchan->next_burst++;
|
||||
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
/* FIXME */
|
||||
break;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
/* FIXME */
|
||||
default:
|
||||
if (lchan->next_burst == 4) {
|
||||
lchan->next_burst = 0;
|
||||
/* FIXME: decode the four bursts into a MAC block */
|
||||
|
||||
/* pass the resulting MAC block up the stack */
|
||||
rc = gsm_lchan_macblock()
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int gsm_rx_sdcch8(struct gsm_burst *burst)
|
||||
{
|
||||
struct gsm_phys_chan *pchan = burst->gsm_pchan;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (burst_type == GSM_BURST_DUMMY)
|
||||
return;
|
||||
|
||||
if (burst->type != GSM_BURST_NORMAL) {
|
||||
fprintf(stderr, "Burst type %u not allowed in SDCCH8\n",
|
||||
burst->type);
|
||||
/* FIXME: statistics */
|
||||
return rc;
|
||||
}
|
||||
|
||||
lchan = get_lchan(pchan, burst->fnr);
|
||||
return add_burst_to_lchan(lchan, burst);
|
||||
}
|
||||
|
||||
static int gsm_rx_tch(struct gsm_burst *burst)
|
||||
{
|
||||
struct gsm_phys_chan *pchan = burst->gsm_pchan;
|
||||
struct gsm_logi_chan *lchan;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (burst->type != GSM_BURST_NORMAL)
|
||||
return rc;
|
||||
|
||||
lchan = get_lchan(pchan, fnr);
|
||||
return add_burst_to_lchan(lchan, burst);
|
||||
}
|
||||
|
||||
/* input a new GSM Um burst on a CCCH */
|
||||
static int gsm_rx_ccch(struct gsm_burst *burst)
|
||||
{
|
||||
struct gsm_phys_chan *pchan = burst->gsm_pchan;
|
||||
struct gsm_logi_chan *lchan;
|
||||
int rc = -EINVAL;
|
||||
|
||||
switch (burst->type) {
|
||||
case GSM_BURST_FCCH:
|
||||
/* FIXME */
|
||||
/* obtain the frequency offset and report to caller */
|
||||
break;
|
||||
case GSM_BURST_SCH:
|
||||
/* obtain the RFN from the SCH burst */
|
||||
/* FIXME */
|
||||
break;
|
||||
case GSM_BURST_NORMAL:
|
||||
/* determine logical channel and append burst */
|
||||
lchan = get_lchan(pchan, burst->fnr);
|
||||
rc = add_burst_to_lchan(lchan, burst);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* input a new GSM Um interface burst into the stack */
|
||||
int gsm_rx_burst(struct gsm_burst *burst, int bits)
|
||||
{
|
||||
struct gsm_phys_chan *pchan;
|
||||
int rc = -EINVAL;
|
||||
|
||||
/* we assume the following fields have already been
|
||||
* filled-in by the caller:
|
||||
* phys_chan, rx_time, rx_frame_nr, decoded/decoded_bits */
|
||||
|
||||
pchan = burst->phys_chan;
|
||||
|
||||
if (!bits)
|
||||
bit_per_byte(burst->decoded_bits, burst->decoded);
|
||||
|
||||
pchan->stats.rx_total++;
|
||||
pchan->stats.rx_type[burst->type]++;
|
||||
|
||||
switch (pchan->config) {
|
||||
case GSM_PCHAN_CCCH:
|
||||
rc = gsm_rx_ccch(burst);
|
||||
break;
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
rc = gsm_rx_sdcch8(burst);
|
||||
break;
|
||||
case GSM_PCHAN_TCH_F:
|
||||
rc =
|
||||
break;
|
||||
case GSM_PCHAN_UNKNOWN:
|
||||
default:
|
||||
fprintf(stderr, "unknown pchan config (ts=%u\n)\n",
|
||||
pchan->timeslot);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
|
||||
/* helper routines to determine the logical channel type based on
|
||||
* physical channel configuration and frame number */
|
||||
|
||||
#include "gsmstack.h"
|
||||
#include "get_lctype.h"
|
||||
|
||||
#define GSM_FN_51 (fn / 51)
|
||||
|
||||
#define GSM_TC(fn) ((fn / 51) % 8)
|
||||
|
||||
|
||||
/* parameters determined from CCCH_CONF (octet 2 of control channel description
|
||||
* in System Information Type 3:
|
||||
* BS_CC_CHANS defines if we have CCCH on ts 2/4/6
|
||||
* BS_CCCH_SDCCH_COMB defines if we have SDCCH/8 SACCH/C8 on TS0
|
||||
* BS_AG_BLKS_RES defines which CCCH blocks are reserved for AGCH
|
||||
* if BCCH Ext. is used, BS_AS_BLKS_RES has to be non-zero
|
||||
*/
|
||||
|
||||
static int get_lctype_for_ccch(unsigned int fnr)
|
||||
{
|
||||
unsigned int fnr51 = GSM_FN_51(fnr);
|
||||
int lc_type;
|
||||
|
||||
if (fnr51 % 10 == 0)
|
||||
lc_type = GSM_LCHAN_FCCH;
|
||||
else if (fnr51 % 10 == 1)
|
||||
lc_type = GSM_LCHAN_SCH;
|
||||
else if (fnr51 >= 2 && fnr_mod_51 <= 5)
|
||||
lc_type = GSM_LCHAN_BCCH;
|
||||
else if (fnr51 >= 6 && fnr51 <= 9) {
|
||||
if (flags & CCCH_F_BCCH_EXT)
|
||||
lc_type = GSM_LCHAN_BCCH;
|
||||
else
|
||||
lc_ctype = GSM_LCHAN_PCH;
|
||||
} else
|
||||
lc_ctype = GSM_LCHAN_PCH;
|
||||
|
||||
/* FIXME: what about AGCH ? */
|
||||
/* FIXME: what about NCH ? */
|
||||
/* FIXME: what about CBCH ? */
|
||||
|
||||
return lc_ctype;
|
||||
}
|
||||
|
||||
static int get_lctype_for_sdcch8(unsigned int fnr)
|
||||
{
|
||||
unsigned int fnr51 = GSM_FN_51(fnr);
|
||||
unsigned int fnr102 = fnr % 102;
|
||||
int lc_type;
|
||||
|
||||
/* the lower 32 bursts are evenly divided between SDCCH8 0..7 */
|
||||
if (fnr51 % < 32) {
|
||||
lc_type = GSM_LCHAN_SDCCH8;
|
||||
subc = fnr51 / 4;
|
||||
} else {
|
||||
/* the upper 16 bursts are bundles of four bursts for
|
||||
* alternating either SACCH0..3 or SACCH4..7 */
|
||||
lc_type = GSM_LCHAN_SACCH8C;
|
||||
subc = (fnr51 - 32) / 4;
|
||||
if (fnr102 > 50)
|
||||
subc += 4;
|
||||
}
|
||||
|
||||
return lc_type;
|
||||
}
|
||||
|
||||
static int get_lctype_for_tch_f(unsigned int fnr)
|
||||
{
|
||||
unsigned int fnr52 = fnr % 52;
|
||||
int lc_type = GSM_LCHAN_TCH;
|
||||
|
||||
/* only burst number 12 and 51 are be SACCH */
|
||||
if (fnr52 == 12 || fnr52 == 51)
|
||||
lc_ctype = GSM_LCHAN_SACCH;
|
||||
/* burst number 26 and 39 are empty (for measurements) */
|
||||
else if (fnr52 == 26 || fnr52 == 39)
|
||||
lc_ctype = GSM_LCHAN_NONE;
|
||||
|
||||
return lc_type;
|
||||
}
|
||||
|
||||
int get_lctype(struct gsm_phys_chan *pchan, int fnr)
|
||||
{
|
||||
switch (pchan->config) {
|
||||
case GSM_PCHAN_CCCH:
|
||||
return get_lctype_for_ccch(fnr);
|
||||
break;
|
||||
case GSM_PCHAN_TCH_F:
|
||||
return get_lctype_for_tch_f(fnr);
|
||||
break;
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
return get_lctype_for_sdcch8(fnr);
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr)
|
||||
{
|
||||
int lctype = get_lctype(pchan, fnr);
|
||||
return pchan->logi_chan[lctype];
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef _GSM_LCTYPE_H
|
||||
#define _GSM_LCTYPE_H
|
||||
|
||||
extern int get_lctype(struct gsm_phys_chan *pchan, int fnr);
|
||||
extern struct gsm_logi_chan *get_lchan(struct gsm_phys_chan *pchan, int fnr);
|
||||
|
||||
#endif /* _GSM_LCTYPE_H */
|
|
@ -0,0 +1,31 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "gsmstack.h"
|
||||
|
||||
|
||||
struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn)
|
||||
{
|
||||
struct gsm_rf_chan *rf;
|
||||
int i;
|
||||
|
||||
rf = malloc(sizeof(*rf));
|
||||
if (!rf)
|
||||
return NULL;
|
||||
memset(rf, 0, sizeof(*rf));
|
||||
|
||||
rf->arfcn = arfcn;
|
||||
|
||||
for (i = 0; i < NR_TIMESLOTS; i++) {
|
||||
struct gsm_phys_chan *pchan;
|
||||
|
||||
pchan = rf->phys_chan[i];
|
||||
pchan->timeslot = i;
|
||||
pchan->rf_chan = rf;
|
||||
}
|
||||
|
||||
return rf;
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
#ifndef _GSMSTACK_H
|
||||
#define _GSMSTACK_H
|
||||
|
||||
#define NR_TIMESLOTS 8
|
||||
#define NR_ARFCN 1024
|
||||
|
||||
#define BURST_BITS (USEFUL_BITS/8 + 1)
|
||||
|
||||
#include "gsmtap.h"
|
||||
enum gsm_burst_type {
|
||||
GSM_BURST_UNKNOWN = GSMTAP_BURST_UNKNOWN,
|
||||
GSM_BURST_FCCH = GSMTAP_BURST_FCCH,
|
||||
GSM_BURST_PARTIAL_FCCH = GSMTAP_BURST_PARTIAL_FCCH,
|
||||
GSM_BURST_SCH = GSMTAP_BURST_SCH,
|
||||
GSM_BURST_CTS_SCH = GSMTAP_BURST_CTS_SCH,
|
||||
GSM_BURST_COMPACT_SCH = GSMTAP_BURST_COMPACT_SCH,
|
||||
GSM_BURST_NORMAL = GSMTAP_BURST_NORMAL,
|
||||
GSM_BURST_DUMMY = GSMTAP_BURST_DUMMY,
|
||||
GSM_BURST_ACCESS = GSMTAP_BURST_ACCESS,
|
||||
GSM_BURST_NONE = GSMTAP_BURST_NONE,
|
||||
_GSM_BURST_CNT
|
||||
};
|
||||
|
||||
|
||||
struct gsm_burst {
|
||||
/* time at which we were received */
|
||||
struct timeval rx_time;
|
||||
|
||||
/* the physical channel which we're part of.
|
||||
* always guaranteed to be != NULL */
|
||||
struct gsm_phys_chan *phys_chan;
|
||||
|
||||
/* the logical channel to which we belong.
|
||||
* only filled-in if we actually know about it */
|
||||
struct gsm_logi_chan *logi_chan;
|
||||
|
||||
/* the burst type (as per gsmtap.h) */
|
||||
unsigned char burst_type;
|
||||
|
||||
/* the relative receive TDMA frame */
|
||||
unsigned int rx_frame_nr;
|
||||
|
||||
/* the timeslot number is part of the phys_chan */
|
||||
|
||||
/* the burst after differential decode, 8 bit per byte */
|
||||
unsigned char decoded[BURST_BITS];
|
||||
|
||||
/* the burst after differential decode, 1 bit per byte */
|
||||
unsigned char decoded_bits[USEFUL_BITS];
|
||||
};
|
||||
|
||||
struct gsm_burst_stats {
|
||||
unsigned int rx_total;
|
||||
unsigned int rx_type[_GSM_BURST_CNT];
|
||||
};
|
||||
|
||||
enum gsm_logical_channel_type {
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
GSM_LCHAN_NONE,
|
||||
|
||||
/* CBCH */
|
||||
GSM_LCHAN_FCCH, /* Frequency Correction CH */
|
||||
GSM_LCHAN_SCH, /* Synchronization CH */
|
||||
GSM_LCHAN_BCCH, /* Broadcast Control CH */
|
||||
GSM_LCHAN_PCH, /* Paging CH */
|
||||
GSM_LCHAN_NCH, /* Notification CH */
|
||||
GSM_LCHAN_AGCH, /* Access Grant CH */
|
||||
GSM_LCHAN_CBCH, /* Cell Broadcast CH */
|
||||
|
||||
/* SDCCH */
|
||||
GSM_LCHAN_SDCCH8, /* Slow Dedicated Control CH */
|
||||
GSM_LCHAN_SACCH8C, /* Slow Associated Control CH */
|
||||
|
||||
/* TCH */
|
||||
GSM_LCHAN_TCH, /* Traffic CH */
|
||||
GSM_LCHAN_SACCH, /* Slow Associated Control CH */
|
||||
|
||||
/* uplink */
|
||||
GSM_LCHAN_RACH, /* Random Access CH */
|
||||
};
|
||||
|
||||
struct gsm_logi_chan {
|
||||
enum gsm_logical_channel_type type;
|
||||
/* here we aggregate the bursts for this logical channel
|
||||
* until we have found enough bursts for one MAC block */
|
||||
struct gsm_burst burst_buf[4];
|
||||
int next_burst;
|
||||
|
||||
struct gsm_burst_stats stats;
|
||||
};
|
||||
|
||||
enum gsm_phys_chan_config {
|
||||
GSM_PCHAN_UNKNOWN,
|
||||
GSM_PCHAN_CCCH,
|
||||
GSM_PCHAN_CCCH_WITH_SDCCH8,
|
||||
GSM_PCHAN_TCH_F,
|
||||
GSM_PCHAN_TCH_H,
|
||||
GSM_PCHAN_SDCCH8_SACCH8C,
|
||||
};
|
||||
|
||||
/* maximum logical channels for one physical channel */
|
||||
#define NR_LOGI_CHANS 16
|
||||
|
||||
/* A GSM physical channel configuration */
|
||||
struct gsm_phys_chan {
|
||||
/* in which timeslot is this channel? */
|
||||
unsigned int timeslot;
|
||||
/* to which RF channel do we belong? */
|
||||
struct gsm_rf_chan *rf_chan;
|
||||
/* how is our physical configuration */
|
||||
enum gsm_phys_chan_config config;
|
||||
/* those are our logical channels */
|
||||
struct gsm_logi_chan logi_chan[NR_LOGI_CHANS];
|
||||
int nr_logi_chans;
|
||||
|
||||
struct gsm_burst_stats stats;
|
||||
};
|
||||
|
||||
struct gsm_rf_chan {
|
||||
/* ARFCN (frequency) of the RF channel */
|
||||
unsigned int arfcn;
|
||||
|
||||
/* current RFN as determined by SCH + frame count */
|
||||
unsigned int rfn;
|
||||
|
||||
/* the physical channel for each timeslot */
|
||||
struct gsm_phys_chan phys_chan[NR_TIMESLOTS];
|
||||
};
|
||||
|
||||
|
||||
extern struct gsm_rf_chan *gsm_init_rfchan(unsigned int arfcn);
|
||||
|
||||
#endif /* _GSMSTACK_H */
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef _GSMSTACK_CHAN_H
|
||||
#define _GSMSTACK_CHAN_H
|
||||
|
||||
/* 51 multiframe for CCCH TS0 of a FCCH+SCH+BCCH+PACH */
|
||||
const char ccch[] =
|
||||
"FSBBBBPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPPFSPPPPPPPP_";
|
||||
|
||||
/* SDCCH/8 + SACCH/C8; A...D are alternating 0..3/4..7 */
|
||||
const char sdcch8[] =
|
||||
"00001111222233334444555566667777AAAABBBBCCCCDDDD___";
|
||||
|
||||
#endif /* _GSMSTACK_CHAN_H */
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef _GSMTAP_H
|
||||
#define _GSMTAP_H
|
||||
|
||||
/* gsmtap header, pseudo-header in front of the actua GSM payload*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define GSMTAP_VERSION 0x01
|
||||
|
||||
#define GSMTAP_TYPE_UM 0x01
|
||||
#define GSMTAP_TYPE_ABIS 0x02
|
||||
#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */
|
||||
|
||||
#define GSMTAP_BURST_UNKNOWN 0x00
|
||||
#define GSMTAP_BURST_FCCH 0x01
|
||||
#define GSMTAP_BURST_PARTIAL_SCH 0x02
|
||||
#define GSMTAP_BURST_SCH 0x03
|
||||
#define GSMTAP_BURST_CTS_SCH 0x04
|
||||
#define GSMTAP_BURST_COMPACT_SCH 0x05
|
||||
#define GSMTAP_BURST_NORMAL 0x06
|
||||
#define GSMTAP_BURST_DUMMY 0x07
|
||||
#define GSMTAP_BURST_ACCESS 0x08
|
||||
#define GSMTAP_BURST_NONE 0x09
|
||||
|
||||
struct gsmtap_hdr {
|
||||
u_int8_t version; /* version, set to 0x01 currently */
|
||||
u_int8_t hdr_len; /* length in number of 32bit words */
|
||||
u_int8_t type; /* see GSMTAP_TYPE_* */
|
||||
u_int8_t timeslot; /* timeslot (0..7 on Um) */
|
||||
|
||||
u_int16_t arfcn; /* ARFCN (frequency) */
|
||||
u_int8_t noise_db; /* noise figure in dB */
|
||||
u_int8_t signal_db; /* signal level in dB */
|
||||
|
||||
u_int32_t frame_number; /* GSM Frame Number (FN) */
|
||||
|
||||
u_int8_t burst_type; /* Type of burst, see above */
|
||||
u_int8_t antenna_nr; /* Antenna Number */
|
||||
u_int16_t res; /* reserved for future use (RFU) */
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/* PCAP related definitions */
|
||||
#define TCPDUMP_MAGIC 0xa1b2c3d4
|
||||
struct pcap_timeval {
|
||||
int32_t tv_sec;
|
||||
int32_t tv_usec;
|
||||
};
|
||||
|
||||
struct pcap_sf_pkthdr {
|
||||
struct pcap_timeval ts; /* time stamp */
|
||||
u_int32_t caplen; /* lenght of portion present */
|
||||
u_int32_t len; /* length of this packet */
|
||||
};
|
||||
|
||||
#endif /* _GSMTAP_H */
|
|
@ -0,0 +1,148 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pcap.h>
|
||||
|
||||
#include "gsmstack.h"
|
||||
#include "gsmtap.h"
|
||||
|
||||
/* source_pcap_burst.c: read a PCAP file with GSMTAP_UM_BURST packets
|
||||
* and feed them into the stack.
|
||||
*
|
||||
* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* TODO:
|
||||
* fix PCAP endianness
|
||||
*/
|
||||
|
||||
|
||||
static struct gsm_rf_chan rfchans[NR_ARFCN];
|
||||
|
||||
static int read_pcap_hdr(int fd)
|
||||
{
|
||||
int rc;
|
||||
struct pcap_file_header pfh;
|
||||
|
||||
rc = read(fd, &pfh, sizeof(pfh));
|
||||
if (rc < sizeof(pfh))
|
||||
return -EIO;
|
||||
|
||||
if (pfh.magic != TCPDUMP_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
if (pfh.linktype != LINKTYPE_GSMTAP)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_burst(struct gsmtap_hdr *gh, int burst_len,
|
||||
struct timeval *tv)
|
||||
{
|
||||
unsigned char *data = (unsigned char *)gh + sizeof(*gh);
|
||||
struct gsm_burst burst;
|
||||
struct gsm_rf_chan *rfchan;
|
||||
struct gsm_phys_chan *pchan;
|
||||
unsigned int arfcn;
|
||||
|
||||
arfcn = ntohs(gh->arfcn);
|
||||
|
||||
/* Create new RF channel structure if we've not seen this ARFCN
|
||||
* before */
|
||||
if (!rfchans[arfcn]) {
|
||||
rfchans[arfcn] = gsm_init_rfchan(arfcn);
|
||||
if (!rfchans[arfcn]) {
|
||||
fprintf(stderr, "cannot init rfchan ARFCN=%u\n",
|
||||
arfcn);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
rfchan = rfchans[arfcn];
|
||||
pchan = rfchan->phys_chan[gh->timeslot];
|
||||
|
||||
memset(&burst, 0, sizeof(burst));
|
||||
burst.phys_chan = pchan;
|
||||
burst.burst_type = gh->burst_type;
|
||||
burst.rx_frame_nr = ntohl(gh->frame_number);
|
||||
memcpy(&burst.rx_time, tv, sizeof(burst.rx_time));
|
||||
memcpy(burst.decoded, data, burst_len);
|
||||
|
||||
return gsm_rx_burst(&burst, 0);
|
||||
}
|
||||
|
||||
/* fills 'buf' with gsmtap_hdr and payload, returns length of payload */
|
||||
static int read_one_pkt(int fd, unsigned char *buf, int bufsize,
|
||||
struct timeval *tv)
|
||||
{
|
||||
struct pcap_sf_pkthdr pkthdr;
|
||||
struct gsmtap_hdr *gh = (struct gsmtap_hdr *) buf;
|
||||
int len, burstlen;
|
||||
|
||||
len = read(fd, &pkthdr, sizeof(pkthdr));
|
||||
if (len < sizeof(pkthdr))
|
||||
return -1;
|
||||
|
||||
/* FIXME: ntohl on caplen and len? */
|
||||
|
||||
if (pkthdr.caplen < sizeof(*gh))
|
||||
return -2;
|
||||
|
||||
if (pkthdr.len > pkthdr.caplen)
|
||||
return -3;
|
||||
|
||||
if (bufsize < pkthdr.len)
|
||||
return -4;
|
||||
|
||||
if (tv) {
|
||||
tv.tv_sec = pkthdr.tv_sec;
|
||||
tv.tv_usec = pkthdr.tv_usec;
|
||||
}
|
||||
|
||||
len = read(fd, buf, pkthdr.caplen)
|
||||
if (len < pkthdr.caplen)
|
||||
return -5;
|
||||
|
||||
if (gh->version != GSMTAP_VERSION ||
|
||||
gh->type != GSMTAP_TYPE_UM_BURST)
|
||||
return -6;
|
||||
|
||||
return pkthdr.caplen - sizeof(*gh);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *fname;
|
||||
int fd, rc;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "please specify pcap filename\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
fname = argv[1];
|
||||
|
||||
fd = open(fname, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = read_pcap_hdr(fd);
|
||||
if (rc < 0) {
|
||||
perror("pcap_hdr");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
unsigned char buf[1024];
|
||||
struct timeval tv;
|
||||
int burst_len;
|
||||
burst_len = read_one_pkt(fd, &tv, sbuf, sizeof(buf));
|
||||
if (burst_len < 0) {
|
||||
fprintf(stderr, "read_one_pkt() = %d\n", burst_len);
|
||||
exit(3);
|
||||
}
|
||||
rc = send_burst((struct gsmtap_hdr *)buf, burst_len, &tv);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue