
#include "dagapi.h"
#include "dagclarg.h"
#include "dag_platform.h"
#include "dagutil.h"
#include "dagerf.h"

#include "descramble.h"

#define NETWORK_RATE_BUFFER	8	// chars
#define FRAME_TYPE_BUFFER	16	// chars

#define ERF_HEADER		16	// bytes
#define ERF_EXT_HEADER		8	// bytes

#define INTERFACE_COUNT		2

#define A1	0xf6
#define A2	0x28
#define NDF_NORMAL	0x6
#define NDF_SET		0x9

#define FRAME_ROWS	9
#define FRAME_FRAGMENT_LEN	9720	// for oc48/oc192

// 127 bit 1+x^6+x^7: 11111110 00000100 00011000 01010001 11100100 01011001 11010100 11110100 00011100 01001001 10110101 10110111 10110001 10100101 11011100 0101010
#if 0
static uint32_t scramble_sequence[4] = { 0xfe041851, 0xe459d4f4, 0x1c49b5b7, 0xb1a5dc54 };	// NOTE: bit 0 is not used
#endif

// OC1 macros
enum
{
	OC1_OHEAD_COLUMNS = 3,	// bytes
	OC1_SPE_SIZE = 783	// bytes (including POH)
};

// Command line argument macros
enum
{
	CLA_HELP,
	CLA_PRINT_FRAME,
	CLA_NETWORK_RATE,
	CLA_FRAME_TYPE,
	CLA_BYPASS
};

// The variables of this enum map to the indices of the raw link array
enum
{
	TYPE_INVALID = -1,
	TYPE_SONET,
	TYPE_SDH
};

#if 0	//NOTE: Do we need this?
typedef struct raw_link_type
{
	uint8_t		value;
	const char	*name;
} raw_link_type_t;

static raw_link_type_t raw_link_type_array[] = {
	{TYPE_SONET, "sonet"},
	{TYPE_SDH, "sdh"},
};

static uint8_t raw_link_array_size = sizeof(raw_link_type_array)/sizeof(raw_link_type_t);
#endif

// The variables of this enum map to the indices of the raw record array
enum
{
	RATE_INVALID = -1,
	RATE_RESERVED,
//	RATE_OC1_STM0 = 0,
	RATE_OC3_STM1,
	RATE_OC12_STM4,
	RATE_OC48_STM16,
	RATE_OC192_STM64
};

enum 
{
	CONCATENATED,
	CHANNELISED
};

typedef struct raw_record
{
	uint8_t		value;
	const char	*rate;
	uint32_t	frame_size;		// bytes
	uint8_t		frame_count;		// count of channelised ocX frames
	uint16_t	framing_bytes_len;	// A1 and A2
	uint16_t	id_len;			// C1
	uint32_t	b1_loc;			// B1
	uint32_t	b2_loc;			// B2
	uint16_t	b2_len;
	uint32_t	h_ptr_loc;
	uint16_t	h1_len;
	uint16_t	transport_ohead_cols;
	uint8_t		oc1_factor;
} raw_record_t;

static raw_record_t raw_record_array[] =
{
	//value			rate		frame_size	frame_count   framing_bytes_len	id_len	b1_loc	b2_loc	b2_len	h_ptr_loc	h1_len	transport_cols	oc1_factor
	{RATE_RESERVED,		"reserved",	0,		0,		0,		0,	0,	0,	0,	0,		0,	0,		0},
//	{RATE_OC1_STM0,		"oc1/stm0",	810,		1, 		2,		1,	90,	360,	1,	270,		1,	3,		1},
	{RATE_OC3_STM1,		"oc3/stm1",	2430,		3, /*oc1*/	6,		3,	270,	1080,	3,	810, 		3,	9,		3},
	{RATE_OC12_STM4,	"oc12/stm4",	9720,		4, /*oc3*/	24,		12,	1080,	4320,	12,	3240,		12,	36,		12},
	{RATE_OC48_STM16,	"oc48/stm16",	38880,		4, /*oc12*/	96,		48,	4320,	17280,	48,	12960,		48,	144,		48},
	{RATE_OC192_STM64,	"oc192/stm64",	155520,		4, /*oc48*/	384,		192,	17280,	69120,	192,	51840,		192,	576,		192}
};

//static uint8_t raw_record_array_size = sizeof(raw_record_array)/sizeof(raw_record_t);

typedef struct payload_ptr
{
	uint8_t		ndf;	// New Data Found
	uint8_t		size;	// SDH only
	uint16_t	ptr_val;
} payload_ptr_t;

// Structure used for storing B1/2/3 bytes, used for channelised/concatenated
typedef struct parity_bytes_ptr
{
	uint8_t		*b1;
	uint8_t		*b2;
	uint8_t		*b3;
} parity_bytes_ptr_t;

// Internal testing macros
enum
{
	INVALID = -2,
	FAIL = -1,
	PASS = 0,
	READY = 1
};

// SONET payload pointer justification type macros
enum
{
	NEGATIVE_JUSTIFICATION = -1,
	NO_JUSTIFICATION = 0,
	POSITIVE_JUSTIFICATION = 1,
	PAYLOAD_POINTER_SET = 0
};

static char network_rate[NETWORK_RATE_BUFFER] = "";
static char frame_type[FRAME_TYPE_BUFFER] = "";

static int print = 0;
static int user_rate = 0;
static int user_type = 0;
static int bypass = 0;
static uint8_t iface = 0;

// Pointers to memory for received frames
static uint8_t *scrambled_next[INTERFACE_COUNT];
static uint8_t *scrambled_current[INTERFACE_COUNT];
static uint8_t *scrambled_previous[INTERFACE_COUNT];
static uint8_t *descrambled_next[INTERFACE_COUNT];
static uint8_t *descrambled_current[INTERFACE_COUNT];
static uint8_t *descrambled_previous[INTERFACE_COUNT];
static uint8_t *frame_buffer[INTERFACE_COUNT];

// Variables for use with fragmented ERF records at OC48/192
static uint8_t fragments = 0;
static uint8_t sequence_max = 0;

// The 8-bit scrambling code for the 1+x^6+x^7 descrambler
static uint8_t scramble_register = 0x00;

static uint32_t record_counter = 0;	// for ERF records

// Counter variables for final message printing
static uint32_t frame_counter = 0;	// for SONET frames
static uint32_t error_frames = 0;
static uint32_t b1_error_total = 0;
static uint32_t b2_error_total = 0;
static uint32_t b3_error_total = 0;

// Function prototypes
static void descramble_print_help (ClArgPtr clarg);
static uint8_t get_network_rate (char *rate);
static int set_frame_memory (int index);
static int preprocess_oc3_oc12(uint8_t *payload, uint16_t length);
static int preprocess_oc48_oc192 (uint64_t ext_hdr, uint8_t *payload, uint16_t length);
static int frame_copy_normal(uint8_t *payload, uint32_t length);
static int frame_copy_bypass(uint8_t *payload, uint32_t length);
static int descrambler (uint8_t *dest_frame_ptr, uint8_t *src_frame_ptr);
static int process_bip_bytes_concatenated (uint16_t length);
static int process_bip_bytes_channelised (uint16_t length);
static void print_frame (uint8_t *frame_ptr);
static uint32_t calculate_overhead_offset (uint16_t ptr_val);
static uint8_t *determine_b3_location (uint16_t ptr_val, int justification_type, uint8_t oc1_index);
static parity_bytes_ptr_t calculate_b1_b2_parity (parity_bytes_ptr_t parity);
static parity_bytes_ptr_t calculate_b3_parity_concatenated (parity_bytes_ptr_t parity, uint16_t ptr_val);
static parity_bytes_ptr_t calculate_b3_parity_channelised (parity_bytes_ptr_t parity, uint16_t *ptr_vals);


/*********************************************************************************************************************************
						START OF PLUGIN ENTRY POINTS
*********************************************************************************************************************************/
int descramble_init(char *params[], int param_cnt, global_params_t *global_params, test_printf f)
{
	ClArgPtr clarg = NULL;
	FILE* errorfile = NULL;
	int argindex = 0;
	int clarg_result = 0;
	int code = 0;

	clarg = dagclarg_init(param_cnt, (const char* const *) params);

	dagclarg_add(clarg, "Display help (this page)", "--help", 'h', CLA_HELP);
	dagclarg_add (clarg, "Display frames in a user friendly row-based format", "--print", 'p', CLA_PRINT_FRAME);
	dagclarg_add_string (clarg, "Network rate at which to run the test (accepts oc3, oc12, oc48 or oc192)", "--rate", 'r', "rate", network_rate, NETWORK_RATE_BUFFER, CLA_NETWORK_RATE);
	dagclarg_add_string (clarg, "Expected frame type (accepts concatenated or channelised)", "--type", 't', "type", frame_type, FRAME_TYPE_BUFFER, CLA_FRAME_TYPE);
	dagclarg_add (clarg, "Bypass the descrambling process and only calculate BIP error bytes, expects already descrambled frames as input", "--bypass", 'b', CLA_BYPASS);

	// Parse the command line options
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				descramble_print_help(clarg);
				return TEST_HELP;

			case CLA_PRINT_FRAME:
				print = 1;
				break;

			case CLA_NETWORK_RATE:
				// Do nothing
				break;

			case CLA_FRAME_TYPE:
				// Do nothing
				break;

			case CLA_BYPASS:
				bypass = 1;
				break;

			default:
				if (params[argindex][0] == '-')
				{
					// Unknown option.
					dagutil_error("unknown option %s\n", params[argindex]);
					return TEST_FAIL;
				}
				break;
	
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	// Check for errors in the arguments
	if (-1 == clarg_result)
	{
		if (argindex < param_cnt)
		{
			dagutil_error("while processing option: %s\n", params[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return TEST_FAIL;
	}

	// Use the network rate argument to allocate space to store the received frames
	user_rate = get_network_rate(network_rate);
	//printf("user provided network rate: %s\n", raw_record_array[user_rate].rate);
	if (user_rate != RATE_INVALID)
	{
		if (set_frame_memory(user_rate) == FAIL)
			return TEST_FAIL;
	}
	else
	{
 		dagutil_warning("invalid network rate: %s\n", network_rate);
		return TEST_FAIL;
	}

	// Use the frame type argument to determine whether frames under test are concatenated or channelised
	if (strcmp(frame_type, "concatenated") == 0)
		user_type = CONCATENATED;
	else if (strcmp(frame_type, "channelised") == 0)
		user_type = CHANNELISED;
	else 
	{
 		dagutil_warning("invalid frame type: %s (accepts 'concatenated' or 'channelised')\n", frame_type);
		return TEST_FAIL;
	}

	// When testing at oc48/192 we need to reassemble fragmented frames from multiple ERF records
	if (user_rate == RATE_OC48_STM16)
	{
		fragments = 1;
		sequence_max = 4;
	}
	else if (user_rate == RATE_OC192_STM64)
	{
		fragments = 1;
		sequence_max = 16;
	}

	return TEST_PASS;
}


int descramble_run(char *rec, int len, char* lastpkt, struct_protocol_t prot, uint64_t rec_cnt)
{
	dag_record_t* drec = (dag_record_t*) rec;
	uint64_t ext_hdr;
	uint16_t wlen = 0;
	uint8_t network_type;
	uint8_t network_rate;
	uint8_t *payload;
	int result = 0;
	static uint8_t first_time[INTERFACE_COUNT] = {1, 1};

	// Retrieve the interface information from the ERF header
	iface = drec->flags.iface;
	if ((iface >= INTERFACE_COUNT))
	{
 		dagutil_warning("incorrect interface value detected: %d (interface count is set to: %d)\n", iface, INTERFACE_COUNT);
		return TEST_FAIL;
	}

	// Check for raw link ERF type
	if ((drec->type & 0x7f) != ERF_TYPE_RAW_LINK)
	{
		dagutil_warning("interface %d: incorrect ERF type found: %d (expected raw link erf type: %d)\n", iface, drec->type, ERF_TYPE_RAW_LINK);
		return TEST_FAIL;
	}

	// Get length of frame from ERF record and check against expected frame length
	wlen = ntohs(drec->wlen);

	if ((user_rate == RATE_OC3_STM1) || (user_rate == RATE_OC12_STM4))
	{
		// For oc3/oc12, the wlen should match the expected frame length
		if (wlen != raw_record_array[user_rate].frame_size)
		{
			dagutil_warning("interface %d: incorrect record length detected: %d (expected %d at %s)\n", iface, wlen, raw_record_array[user_rate].frame_size, raw_record_array[user_rate].rate);
			return TEST_FAIL;
		}
	}
	else
	{
		// For oc48/oc192, the wlen for a frame fragment is 9720
		if (wlen != FRAME_FRAGMENT_LEN)
		{
			dagutil_warning("interface %d: incorrect frame fragment length detected: %d (expected %d at %s)\n", iface, wlen, FRAME_FRAGMENT_LEN, raw_record_array[user_rate].rate);
			return TEST_FAIL;
		}
	}

	// Check for the presence of extension headers
	if (dagerf_ext_header_count((uint8_t*)drec, len) == 0)
	{
		dagutil_warning("interface %d: no extension headers found!\n", iface);
		return TEST_FAIL;
	}
	ext_hdr = *((uint64_t *)(&rec[ERF_HEADER]));
	ext_hdr = bswap_64(ext_hdr);

#if 0	//NOTE: Do not need to check for correct extension header types as we will be receiving SONET traffic to descramble not raw link traffic
	// Check for raw link extension header
	if (((ext_hdr >> 24) & 0x7f) != EXT_HDR_TYPE_RAW_LINK)
	{
		dagutil_warning("interface %d: incorrect extension header type found: 0x%"PRIx64" (expected raw link ext header type: %d)\n", iface, ((ext_hdr >> 24) & 0x7f), EXT_HDR_TYPE_RAW_LINK);
		return TEST_FAIL;
	}
#endif
	// Check network type (SONET/SDH) from the extension header
	network_type = ext_hdr & 0xff;
	if ((network_type != TYPE_SONET) && (network_type != TYPE_SDH))
	{
		dagutil_warning("descramble test not supported on this network type: 0x%x (expected sonet, 0x%x or sdh, 0x%x)\n", network_type, TYPE_SONET, TYPE_SDH);
		return TEST_FAIL;
	}

	// Check network rate from extension header corresponds with user input option
	network_rate = (ext_hdr >> 8) & 0xff;
	if ((network_rate != user_rate))
	{
 		dagutil_warning("interface %d: incorrect raw link extension header rate detected: 0x%x (user provided rate: 0x%x)\n", iface, network_rate, user_rate);
		return TEST_FAIL;
	}

	// Get past the ERF and extension headers to get to the payload (i.e. start of the raw frame)
	payload = (uint8_t *)(&rec[ERF_HEADER + ERF_EXT_HEADER]);

	++record_counter;

	if (fragments)	// OC48/192, requires reassembly of ERF records based on extension header sequence numbers
		result = preprocess_oc48_oc192(ext_hdr, payload, wlen);
	else		// OC3/12, reassembly of ERF records not needed
		result = preprocess_oc3_oc12(payload, wlen);

	if (result == INVALID)	// still waiting for valid start of frame or frame memory spaces to be filled
	{
		return TEST_IGNORE;
	}
	if (result == PASS)
	{
		return TEST_PASS;
	}
	else if (result == FAIL)
	{
 		dagutil_warning("interface %d: preprocessing of received %s frame failed\n", iface, raw_record_array[user_rate].rate);
		return TEST_FAIL;
	}

	if (!bypass)	// Normal operation mode
	{
		// Initially, need to descramble the previous and current frames, then for all other times just copy over the previous and current descrambled frames
		if (first_time[iface])
		{
			first_time[iface] = 0;

			// Perform descrambling on the previous and current frames
			result = descrambler(descrambled_previous[iface], scrambled_previous[iface]);
			if (result == FAIL)
			{
				dagutil_warning("interface %d: descrambling of previous frame failed\n", iface);
				return TEST_FAIL;
			}
	
			result = descrambler(descrambled_current[iface], scrambled_current[iface]);
			if (result == FAIL)
			{
				dagutil_warning("interface %d: descrambling of current frame failed\n", iface);
				return TEST_FAIL;
			}
		}
		else
		{
			memcpy (descrambled_previous[iface], descrambled_current[iface], raw_record_array[user_rate].frame_size);
			memcpy (descrambled_current[iface], descrambled_next[iface], raw_record_array[user_rate].frame_size);
		}
	
		// Perform descrambling on the next frame
		result = descrambler(descrambled_next[iface], scrambled_next[iface]);
		if (result == FAIL)
		{
			dagutil_warning("interface %d: descrambling of next frame failed\n", iface);
			return TEST_FAIL;
		}
	}
	else	// Bypass descrambler mode
	{
		// Perform scrambling on the previous frame
		// NOTE: The descrambling process can also be used unchanged on descrambled frames in order to reproduce the original scrambled frames.
		// Here we only need to reverse the process on the previous descrambled frame as this is needed for B1 verification.
		// B2 and B3 are not a problem as they only require descrambled frames for verification
		result = descrambler(scrambled_previous[iface], descrambled_previous[iface]);
		if (result == FAIL)
		{
			dagutil_warning("interface %d: scrambling of previous frame failed\n", iface);
			return TEST_FAIL;
		}
	}

	// Calculate B1, B2, B3 parity bytes
	if (user_type == CONCATENATED)
		result = process_bip_bytes_concatenated (wlen);
	else if (user_type == CHANNELISED)
 		result = process_bip_bytes_channelised (wlen);

	if (result == PASS)
		return TEST_PASS;
	else
	{
		++error_frames;
		return TEST_FAIL;
	}
}


int descramble_err_msg(char *buf, int size)
{
	return TEST_PASS;
}


int descramble_final_msg(char *buf, int size)
{
	char msg[MAX_STR_LEN];

	snprintf(msg, MAX_STR_LEN, 
		"descramble_test: %s frames, total: %u\ndescramble_test: %s frames, w/error: %u\ndescramble_test: b1 errors total: %u\ndescramble_test: b2 errors total: %u\ndescramble_test: b3 errors total: %u\n",
		raw_record_array[user_rate].rate,
		frame_counter,
		raw_record_array[user_rate].rate,
		error_frames,
		b1_error_total, 
		b2_error_total, 
		b3_error_total);

    if (size > 0)
    {
	    strncpy(buf, msg, size);
	    buf[size-1] = '\0';
    }

	/* Test will fail if we had any B1, B2, B3 errors, otherwise it will pass */
	if ((b1_error_total > 0) || (b2_error_total > 0) || (b3_error_total > 0))
		return TEST_FAIL;
	else
		return TEST_PASS;
}


int descramble_cleanup()
{
	uint8_t i;

	// Free the space allocated for the previous/current (de)scrambled frames
	for (i = 0; i < INTERFACE_COUNT; i++)
	{
		free(scrambled_previous[i]);
		free(descrambled_previous[i]);
		free(scrambled_current[i]);
		free(descrambled_current[i]);
		free(scrambled_next[i]);
		free(descrambled_next[i]);
		free(frame_buffer[i]);
	}

	return TEST_PASS;
}

/*********************************************************************************************************************************
						END OF PLUGIN ENTRY POINTS
*********************************************************************************************************************************/

static void descramble_print_help (ClArgPtr clarg)
{
	printf ("Description:\n"
		"The descramble plugin performs descrambling on raw link ERF frames and calculates the B1, B2 and B3 parity fields to determine frame errors.\n"
		"This test is valid at SONET OC3/12/48/192 or SDH STM1/4/16/64 network rates.\n\n");

	dagclarg_display_usage(clarg, stdout);
}

static uint8_t get_network_rate (char *rate)
{
	if (strcmp(rate, "oc3") == 0 || strcmp(rate, "stm1") == 0)
	{
		return RATE_OC3_STM1;
	}
	else if (strcmp(rate, "oc12") == 0 || strcmp(rate, "stm4") == 0)
	{
		return RATE_OC12_STM4;
	}
	else if (strcmp(rate, "oc48") == 0 || strcmp(rate, "stm16") == 0)
	{
		return RATE_OC48_STM16;
	}
	else if (strcmp(rate, "oc192") == 0 || strcmp(rate, "stm64") == 0)
	{
		return RATE_OC192_STM64;
	}
	
	return RATE_INVALID;
}

/* This function allocates memory for the received scrambled frames and corresponding descrambled frames.
 * The memory space is initialised to 0.
 */
static int set_frame_memory(int index)
{
	uint8_t i;

	// Allocate storage space for a defined number of interfaces
	for (i = 0; i < INTERFACE_COUNT; i++)
	{
		scrambled_next[i] = malloc (raw_record_array[index].frame_size);
		if (scrambled_next[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for next scrambled frame.\n", i);
			return FAIL;
		}
		memset (scrambled_next[i], 0, raw_record_array[index].frame_size);
	
		descrambled_next[i] = malloc (raw_record_array[index].frame_size);
		if (descrambled_next[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for next descrambled frame.\n", i);
			return FAIL;
		}
		memset (descrambled_next[i], 0, raw_record_array[index].frame_size);
	
		scrambled_current[i] = malloc (raw_record_array[index].frame_size);
		if (scrambled_current[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for current scrambled frame.\n", i);
			return FAIL;
		}
		memset (scrambled_current[i], 0, raw_record_array[index].frame_size);
	
		descrambled_current[i] = malloc (raw_record_array[index].frame_size);
		if (descrambled_current[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for current descrambled frame.\n", i);
			return FAIL;
		}
		memset (descrambled_current[i], 0, raw_record_array[index].frame_size);
	
		scrambled_previous[i] = malloc (raw_record_array[index].frame_size);
		if (scrambled_previous[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for previous scrambled frame.\n", i);
			return FAIL;
		}
		memset (scrambled_previous[i], 0, raw_record_array[index].frame_size);
	
		descrambled_previous[i] = malloc (raw_record_array[index].frame_size);
		if (descrambled_previous[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for previous descrambled frame.\n", i);
			return FAIL;
		}
		memset (descrambled_previous[i], 0, raw_record_array[index].frame_size);
	
		frame_buffer[i] = malloc (raw_record_array[index].frame_size);
		if (frame_buffer[i] == NULL)
		{
			dagutil_error("interface %d: insufficient memory for frame buffer.\n", i);
			return FAIL;
		}
		memset (frame_buffer[i], 0, raw_record_array[index].frame_size);
	}

	return PASS;
}


static void print_frame(uint8_t *frame_ptr)
{
	uint8_t *frame_loc = frame_ptr;
	uint32_t byte;
	uint8_t row = 1;
	uint32_t next_line = 1;

	printf("Frame: %d, Interface: %d\n", frame_counter, iface);
	
	for (byte = 1; byte <= raw_record_array[user_rate].frame_size; byte++)
	{
		printf("%02x ", *frame_loc);

		if (next_line % 50 == 0)
			printf("\n");
		++next_line;

		if (byte % ((raw_record_array[user_rate].frame_size / FRAME_ROWS)) == 0)
		{
			printf("\n");
			next_line = 1;

			printf("%d, %d: End of row: %d\n", frame_counter, iface, row);
			++row;
		}

		++frame_loc;
	}

	printf("\n");
}


/* This function performs buffering on the previous, current and next scrambled frames at OC3 and OC12
 */
static int preprocess_oc3_oc12 (uint8_t *payload, uint16_t length)
{
	int result;

	//printf ("preprocessing oc3/oc12...\n");

	if (!bypass)
		result = frame_copy_normal(payload, length);
	else
		result = frame_copy_bypass(payload, length);

	return result;
}

/* This function performs buffering on the previous, current and next scrambled frames at OC48 and OC192.
 * As ERF records cannot fully contain a SONET frame at these rates, the frame is fragmented into multiple ERF records.
 * Note that OC48 frames are encapsulated in 4 ERF records and OC192 frames are encapsulated in 16 ERF records.
 */
static int preprocess_oc48_oc192 (uint64_t ext_hdr, uint8_t *payload, uint16_t length)
{
	static uint8_t invalid_fragment[INTERFACE_COUNT] = {1, 1};
 	uint8_t more_fragment = 0;
 	static uint16_t sequence_num_previous[INTERFACE_COUNT] = {0, 0};
 	static uint16_t sequence_num_current[INTERFACE_COUNT] = {0, 0};
	static uint32_t write_location[INTERFACE_COUNT] = {0, 0};
	int result;

	//printf ("preprocessing oc48/oc192...\n");

	more_fragment = (uint8_t)((ext_hdr >> 55) & 0x1);
	sequence_num_current[iface] = (uint16_t)((ext_hdr >> 16) & 0xffff);

	// Ignore invalid frame fragments while waiting for start of first frame
	if ((sequence_num_current[iface] != 0) && (invalid_fragment[iface] == 1))
	{
		dagutil_warning("interface %d: record %d: waiting for start of new frame, discarding initial invalid partial fragments. sequence num: %d \n", iface, record_counter, sequence_num_current[iface]);
		return INVALID;
	}

	// Check for acceptable sequence numbers	
	if (sequence_num_current[iface] >= sequence_max)
	{
		dagutil_warning("interface %d: record %d: invalid sequence number detected: %d (maximum: %d)\n", iface, record_counter, sequence_num_current[iface], sequence_max);
		sequence_num_previous[iface] = 0;
		return FAIL;
	}

	// Check for start of a valid frame, at this point all initial invalid fragments should have been filtered
	if (sequence_num_current[iface] == 0)	// Start of frame
	{
		invalid_fragment[iface] = 0;

		// Make sure 'more fragment' value is 1
		if (more_fragment != 1)
		{
			dagutil_warning("interface %d: record %d: start of new frame with incorrect 'more fragment' value: %d (expected: 1)\n", iface, record_counter, sequence_num_current[iface]);
			invalid_fragment[iface] = 1;
			return FAIL;
		}

		//printf ("Start of frame - more fragment: %d, sequence num: %d\n", more_fragment, sequence_num_current[iface]);

		memcpy(frame_buffer[iface], payload, length);
	}
	else	// Valid frame fragment
	{
		if (sequence_num_current[iface] != sequence_num_previous[iface] + 1)
		{
			dagutil_warning("interface %d: record %d: sequence number broken: %d (expected: %d)\n", iface, record_counter, sequence_num_current[iface], sequence_num_previous[iface] + 1);
			sequence_num_previous[iface] = 0;
			invalid_fragment[iface] = 1;
			return FAIL;
		}

		if ((sequence_num_current[iface] > 0) && (sequence_num_current[iface] < (sequence_max - 1)))
		{
			if (more_fragment != 1)
			{
				dagutil_warning("interface %d: record %d: invalid sequence number and/or more fragment value, sequence number: %d, more fragment: %d\n", iface, record_counter, sequence_num_current[iface], more_fragment);
				sequence_num_previous[iface] = 0;
				invalid_fragment[iface] = 1;
				return FAIL;
			}
		}

		if ((more_fragment == 0) && (sequence_num_current[iface] != sequence_max - 1))
		{
			dagutil_warning("interface %d: record %d: sequence number of last fragment broken: %d (expected: %d)\n", iface, record_counter, sequence_num_current[iface], sequence_max - 1);
			sequence_num_previous[iface] = 0;
			invalid_fragment[iface] = 1;
			return FAIL;
		}

		//printf ("Frame fragment - more fragment: %d, sequence num: %d\n", more_fragment, sequence_num_current[iface]);

		memcpy(frame_buffer[iface] + write_location[iface], payload, length);
	}

#if 0
	printf("more_fragment: %d, sequence_num_current[%d]: %d, sequence_num_previous[%d]: %d, invalid_fragment[%d]: %d, write_location[%d]: %d\n", 
		more_fragment,
		iface, sequence_num_current[iface],
		iface, sequence_num_previous[iface],
		iface, invalid_fragment[iface],
		iface, write_location[iface]);
#endif

	sequence_num_previous[iface] = sequence_num_current[iface];
	write_location[iface] += length;

	if (write_location[iface] >= raw_record_array[user_rate].frame_size)
	{
		write_location[iface] = 0;

		if (!bypass)
			result = frame_copy_normal(frame_buffer[iface], raw_record_array[user_rate].frame_size);
		else
			result = frame_copy_bypass(frame_buffer[iface], raw_record_array[user_rate].frame_size);

		return result;
	}
	else
		return PASS;
}

/* This function performs storing of the scrambled previous, current and next scrambled frames in normal mode
 */
static int frame_copy_normal(uint8_t *frame, uint32_t length)
{
	static uint8_t first_frame[INTERFACE_COUNT] = {1, 1};
	static uint8_t second_frame[INTERFACE_COUNT] = {1, 1};

	//printf ("frame copy normal...\n");

	// For the first frame, store it and exit, will save two memcpy() calls initially
	if (first_frame[iface])
	{
		memcpy(scrambled_next[iface], frame, length);
		first_frame[iface] = 0;
		return PASS;
	}
	// For the second frame, move the first frame and store the second, will save one memcpy() call
	else if (second_frame[iface])
	{
		memcpy(scrambled_current[iface], scrambled_next[iface], length);
		memcpy(scrambled_next[iface], frame, length);
		second_frame[iface] = 0;
		return PASS;
	}

	// Buffer the next, current and previous frame
	memcpy(scrambled_previous[iface], scrambled_current[iface], length);
	memcpy(scrambled_current[iface], scrambled_next[iface], length);
	memcpy(scrambled_next[iface], frame, length);

	return READY;
}

/* This function performs storing of the descrambled previous, current and next scrambled frames in bypass mode
 */
static int frame_copy_bypass(uint8_t *frame, uint32_t length)
{
	static uint8_t first_frame[INTERFACE_COUNT] = {1, 1};
	static uint8_t second_frame[INTERFACE_COUNT] = {1, 1};

	//printf ("frame copy bypass...\n");

	// For the first frame, store it and exit, will save two memcpy() calls initially
	if (first_frame[iface])
	{
		memcpy(descrambled_next[iface], frame, length);
		first_frame[iface] = 0;
		return PASS;
	}
	// For the second frame, move the first frame and store the second, will save one memcpy() call
	else if (second_frame[iface])
	{
		memcpy(descrambled_current[iface], descrambled_next[iface], length);
		memcpy(descrambled_next[iface], frame, length);
		second_frame[iface] = 0;
		return PASS;
	}

	// Buffer the next, current and previous frame
	memcpy(descrambled_previous[iface], descrambled_current[iface], length);
	memcpy(descrambled_current[iface], descrambled_next[iface], length);
	memcpy(descrambled_next[iface], frame, length);

	return READY;
}


/* This function performs the descrambling process on a given source frame and saves the descrambled output to a corresponding memory space for the destination frame
 */
static int descrambler(uint8_t *dest_frame_ptr, uint8_t *src_frame_ptr)
{
	uint32_t i;
	uint8_t *src_frame_loc = src_frame_ptr;
	uint8_t *dest_frame_loc = dest_frame_ptr;
	uint8_t bit_in = 0x0;
	uint8_t bit_out = 0x0;
	uint8_t bit0 = 0x0;
	uint8_t bit6 = 0x0;
	uint8_t bit7 = 0x0;
	uint8_t result_register = 0x00;
	
	uint32_t byte = raw_record_array[user_rate].framing_bytes_len + raw_record_array[user_rate].id_len;
	uint32_t bit = 0;

	//printf ("running descrambler...\n");

	// Check for appropriate number of A1 and A2 framing bytes, depending on network rate
	for (i = 0; i < raw_record_array[user_rate].framing_bytes_len; i++)
	{
		if (i < raw_record_array[user_rate].framing_bytes_len/2)
		{
			if (*src_frame_loc == A1)
				memcpy(dest_frame_loc, src_frame_loc, 1);
			else
			{
				dagutil_warning("required A1 framing byte not found (location %d)\n", i);
				return FAIL;
			}
		}
		else
		{
			if (*src_frame_loc == A2)
				memcpy(dest_frame_loc, src_frame_loc, 1);
			else
			{
				dagutil_warning("required A2 framing byte not found (location %d)\n", i);
				return FAIL;
			}
		}
		++src_frame_loc;
		++dest_frame_loc;
	}

	// Copy over SONET Section Trace J0/Z1 byte(s), no need for checking
	for (i = 0; i < raw_record_array[user_rate].id_len; i++)
	{
		memcpy(dest_frame_loc, src_frame_loc, 1);
		++src_frame_loc;
		++dest_frame_loc;
	}

	// Fill the 7-bit descrambler with all 1s
	scramble_register = 0x7f;

	//printf("descrambler bit iterations: %d\n", ((raw_record_array[user_rate].frame_size - raw_record_array[user_rate].framing_bytes_len - raw_record_array[user_rate].id_len) * 8));

	for (i = 0; i < ((raw_record_array[user_rate].frame_size - raw_record_array[user_rate].framing_bytes_len - raw_record_array[user_rate].id_len) * 8); i++)	// Iterate over bits not bytes
	{
		// Prepare descrambling variables
		bit_in = (*src_frame_loc & (0x1 << (7 - bit))) >> (7 - bit);	// Start from MSB
		bit7 = (scramble_register & 0x40) >> 6;
		bit6 = (scramble_register & 0x20) >> 5;
	
		// Perform descrambling bit operations
		bit_out = (bit_in & 0x1) ^ (bit7 & 0x1);
		bit0 = (bit6 & 0x1) ^ (bit7 & 0x1);
	
		// Store descrambled bit in the result register
		result_register	|= (bit_out & 0x1) << (7 - bit);

		//printf ("bit: %d, scramble register: 0x%x, bit in: 0x%x, bit 7: 0x%x, bit 6: 0x%x, bit out: 0x%x, bit 0: 0x%x, result register: 0x%x\n", bit, (scramble_register & 0x7f), bit_in, bit7, bit6, bit_out, bit0, result_register);
		
		scramble_register = (scramble_register << 1) & 0x7f;
		scramble_register |= (bit0 & 0x1);

		// Write the result register when full
		if ((i + 1) % 8 == 0)
		{
			memcpy(dest_frame_loc, &result_register, 1);
 #if 0
 			printf ("i: %d, byte: %d, scrambled_loc: %d, current_scrambled_byte: 0x%x,\t\tdescrambled_loc: %d, current_descrambled_byte: 0x%x\n",
 					i, byte,
 					(src_frame_loc - src_frame_ptr),
 					*src_frame_loc,
 					(dest_frame_loc - dest_frame_ptr),
 					*dest_frame_loc);
#endif
			++src_frame_loc;
			++dest_frame_loc;
			result_register = 0;

			++byte;
			bit = 0;
		}
		else
			++bit;
	}

	// At the end of the frame, fill descrambler with all 0s - need to do this?
	scramble_register = 0x00;

	if (print)
	{
		if (!bypass)
			print_frame(dest_frame_ptr);
		else
			print_frame(src_frame_ptr);
	}

	++frame_counter;

	return PASS;
}


/* Calculates the amount of line overhead offset bytes that needs to be added based on the frame size.
 */
static uint32_t calculate_overhead_offset (uint16_t ptr_val)
{
	uint32_t offset = 0;
	uint32_t frame_width = raw_record_array[user_rate].frame_size / FRAME_ROWS;
	uint32_t ohead_width = raw_record_array[user_rate].transport_ohead_cols;
	uint32_t spe_width = frame_width - ohead_width;

	offset = (ptr_val * raw_record_array[user_rate].oc1_factor) / spe_width;
	offset *= ohead_width;

	//printf("overhead offset: %d\n", offset);

	return offset;
}

/* This function calculates the B1 and B2 parity bytes for a SONET frame.
 * B1 parity is calculated over sections of the previous scrambled SONET frame except the A1/A2/Z0 bytes.
 * B2 parity is calculated over the previous descrambled SONET frame except the SOH section.
 * Applicable to both concatenated and channelised SONET traffic types.
 */
static parity_bytes_ptr_t calculate_b1_b2_parity (parity_bytes_ptr_t parity)
{
	uint32_t i;
	uint8_t *frame_byte_ptr_b1;
	uint8_t *frame_byte_ptr_b2;
	uint8_t row = 0;
	uint32_t column = 0;
	uint32_t b2_position = 0;

	//printf("calculating b1, b2 parity...\n");

	// Calculate B1 and B2 parity from previous scrambled frames
	for (i = 0; i < raw_record_array[user_rate].frame_size; i++)
	{
		frame_byte_ptr_b1 = scrambled_previous[iface] + i;
		frame_byte_ptr_b2 = descrambled_previous[iface] + i;

		// Calculate B1 parity
		//printf("iface: %d, row: %d, column: %d, parity b1: 0x%02x, frame_byte_ptr: 0x%02x\n", iface, row, column, *parity.b1, *frame_byte_ptr);
		*parity.b1 = *parity.b1 ^ *frame_byte_ptr_b1;

		// Calculate B2 parity
		if (row >= 3 || column >= raw_record_array[user_rate].transport_ohead_cols)
		{
			*(parity.b2 + b2_position)= *(parity.b2 + b2_position) ^ *frame_byte_ptr_b2;
#if 0
			printf("iface: %d, row: %d, column: %d, parity b2(0): 0x%02x, parity b2(1): 0x%02x, parity b2(2): 0x%02x, b2_position: %d, frame_byte_ptr: 0x%02x\n", 
				iface, row, column,
				*parity.b2,
				*(parity.b2 + 1),
				*(parity.b2 + 2),
				b2_position,
				*frame_byte_ptr);
#endif
			++b2_position;
			if (b2_position >= raw_record_array[user_rate].b2_len)
				b2_position = 0;
		}

		// Update row and column counters
		++column;
		if ((i + 1) % (raw_record_array[user_rate].frame_size / FRAME_ROWS) == 0)
		{
			++row;
			column = 0;
		}
	}

	return parity;
}

/* This function calculates the B3 parity byte for concatenated SONET traffic.
 * B3 parity byte is calculated over the SPE that overlaps the current descrambled and next descrambled SONET frames.
 */
static parity_bytes_ptr_t calculate_b3_parity_concatenated (parity_bytes_ptr_t parity, uint16_t ptr_val)
{
	uint32_t i, j = 0;
	uint8_t *spe_byte_ptr;
	uint32_t overhead_offset;
	uint8_t *pload_start;
	uint32_t spe_loc;
	uint8_t *descrambled_frame_ptr;
	uint32_t spe_overlap;
	uint8_t spe_start_previous = 0;

	//printf("calculating b3 parity concatenated...\n");

	overhead_offset = calculate_overhead_offset (ptr_val);

	pload_start = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].transport_ohead_cols;
	spe_loc = (ptr_val * raw_record_array[user_rate].oc1_factor) + overhead_offset;

	// Check whether the SPE starts in the current frame
	if ((pload_start - descrambled_previous[iface]) + spe_loc >= raw_record_array[user_rate].frame_size)
	{
		spe_overlap = ((pload_start - descrambled_previous[iface]) + spe_loc) - raw_record_array[user_rate].frame_size;
		spe_byte_ptr = descrambled_current[iface] + spe_overlap;
		descrambled_frame_ptr = descrambled_current[iface];
	}
	else
	{
		spe_byte_ptr = pload_start + spe_loc;
		descrambled_frame_ptr = descrambled_previous[iface];
		spe_start_previous = 1;
	}

	// Iterate through the entire concatenated SPE
	for (i = 0; i < (OC1_SPE_SIZE * raw_record_array[user_rate].oc1_factor); i++)
	{
		// Take into account overlapping into the following frame
		if ((spe_byte_ptr - descrambled_frame_ptr) > (raw_record_array[user_rate].frame_size))
		{
			if (spe_start_previous)	// SPE started in previous frame and overlaps into current frame
			{
				spe_byte_ptr = descrambled_current[iface] + raw_record_array[user_rate].transport_ohead_cols;
				descrambled_frame_ptr = descrambled_current[iface];
			}
			else	// SPE started in current frame and overlaps into next frame
			{
				spe_byte_ptr = descrambled_next[iface] + raw_record_array[user_rate].transport_ohead_cols;
				descrambled_frame_ptr = descrambled_next[iface];
			}
		}

		*parity.b3 = *parity.b3 ^ *spe_byte_ptr;

		//printf("i: %d, j: %d, b3: 0x%02x, spe_byte_ptr: 0x%02x\n", i, j, *parity.b3, *spe_byte_ptr);

		++spe_byte_ptr;
		++j;
			
		// Take into account new rows
		if ((spe_byte_ptr - descrambled_frame_ptr) % (raw_record_array[user_rate].frame_size / FRAME_ROWS) == 0)
		{
			//printf("new row\n");
			j = 0;
			spe_byte_ptr += raw_record_array[user_rate].transport_ohead_cols;
		}

	}

	return parity;
}

/* This function calculates the B3 parity bytes for concatenated SONET traffic.
 * The B3 parity bytes are calculated over the SPE that overlaps the current scrambled and next scrambled SONET frames.
 * The number of parity bytes depends on the number of OC1 frames multiplexed at that particuler SONET network rate.
 */
static parity_bytes_ptr_t calculate_b3_parity_channelised (parity_bytes_ptr_t parity, uint16_t *ptr_vals)
{
	uint32_t i, j;
	uint8_t *spe_byte_ptr;
	uint32_t overhead_offset;
	uint8_t *pload_start;
	uint32_t spe_loc;
	uint8_t *descrambled_frame_ptr;
	uint32_t spe_overlap;
	uint32_t channelised_frame_pload_size;
	uint8_t row = 0;
	uint8_t spe_start_previous = 0;

	//printf("calculating b3 parity channelised...\n");

	for (i = 0; i < raw_record_array[user_rate].frame_count; i++)
	{
		overhead_offset = calculate_overhead_offset (*(ptr_vals + i));
	
		pload_start = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].transport_ohead_cols;
		spe_loc = (*(ptr_vals + i) * raw_record_array[user_rate].oc1_factor) + overhead_offset + i;

		//printf("i: %d, spe_loc: %d\n", i, spe_loc);

		// Check whether the SPE starts in the current frame
		if ((pload_start - descrambled_previous[iface]) + spe_loc >= raw_record_array[user_rate].frame_size)
		{
			spe_overlap = ((pload_start - descrambled_previous[iface]) + spe_loc) - raw_record_array[user_rate].frame_size;
			spe_byte_ptr = descrambled_current[iface] + spe_overlap;
			descrambled_frame_ptr = descrambled_current[iface];
		}
		else
		{
			spe_byte_ptr = pload_start + spe_loc;
			descrambled_frame_ptr = descrambled_previous[iface];
			spe_start_previous = 1;
		}
	
		// NOTE: Iterate using the frame size one rate lower, e.g. there are four oc3 frames of (2430 frame size - 81 overhead) bytes in oc12 channelised
		channelised_frame_pload_size = raw_record_array[user_rate - 1].frame_size - (raw_record_array[user_rate - 1].transport_ohead_cols * FRAME_ROWS); //FIXME: This will fail for oc3 channelised as there is no lower reference rates
		//printf("channelised frame payload size: %d\n", channelised_frame_pload_size);

		for (j = 0; j < channelised_frame_pload_size; j++)
		{
			// Take into account overlapping into the following frame
			if ((spe_byte_ptr - descrambled_frame_ptr) > (raw_record_array[user_rate].frame_size))
			{
				//printf("overlap\n");
				if (spe_start_previous) // SPE started in previous frame and overlaps into current frame
				{
					spe_byte_ptr = descrambled_current[iface] + raw_record_array[user_rate].transport_ohead_cols + i;
					descrambled_frame_ptr = descrambled_current[iface];
				}
				else	// SPE started in current frame and overlaps into next frame
				{
					spe_byte_ptr = descrambled_next[iface] + raw_record_array[user_rate].transport_ohead_cols + i;
					descrambled_frame_ptr = descrambled_next[iface];
				}
			}

			*(parity.b3 + i) = *(parity.b3 + i) ^ *spe_byte_ptr;

			//printf("i: %d, j: %d, b3: 0x%02x, spe_byte_ptr: 0x%02x, spe_byte_ptr - descrambled_frame_ptr: %d\n", i, j, *(parity.b3 + i), *spe_byte_ptr, spe_byte_ptr - descrambled_frame_ptr);

			spe_byte_ptr += raw_record_array[user_rate].frame_count;
		
			// Take into account new rows
			if ((spe_byte_ptr - i - descrambled_frame_ptr) % (raw_record_array[user_rate].frame_size / FRAME_ROWS) == 0)
			{
				//printf("end of row: %d, spe_byte_ptr-i-descrambled_frame_ptr: %d, frame width: %d\n", row++, spe_byte_ptr - i - descrambled_frame_ptr, (raw_record_array[user_rate].frame_size / FRAME_ROWS));
				spe_byte_ptr += raw_record_array[user_rate].transport_ohead_cols;
			}
		}
		row = 0;
	}

	return parity;
}

/* This function determines the location of the B3 parity byte for a given pointer value, justification type and index.
 * The index is used to determine which B3 parity byte to locate
 * e.g. there are four OC3 frames in OC12 channelised frame, i.e. there are a total of four B3 bytes, so the index can have a value of 0-3.
 * For concatenated SONET traffic, there is only one B3 per SPE so the index is always 0.
 */
static uint8_t *determine_b3_location (uint16_t ptr_val, int justification_type, uint8_t index)
{
	uint8_t *b3_loc;
	uint32_t overhead_offset;
	uint32_t spe_overlap;
	uint8_t *pload_start;
	uint32_t spe_loc;
	uint32_t next_row = raw_record_array[user_rate].frame_size / FRAME_ROWS;

	overhead_offset = calculate_overhead_offset(ptr_val);
	pload_start = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].transport_ohead_cols + index;
	spe_loc = ((ptr_val + justification_type) * raw_record_array[user_rate].oc1_factor) + overhead_offset;

	// Check whether the SPE starts in the next frame
	if ((pload_start - descrambled_current[iface]) + spe_loc >= raw_record_array[user_rate].frame_size)
	{
		spe_overlap = ((pload_start - descrambled_current[iface]) + spe_loc) - raw_record_array[user_rate].frame_size;

		b3_loc = descrambled_next[iface] + spe_overlap + next_row;
		//printf ("index: %d, overlap: %d, spe_loc, %d, b3: 0x%02x\n", index, spe_overlap, spe_loc,*b3_loc);
	}
	else	// SPE starts in the current frame
	{
		b3_loc = pload_start + spe_loc + next_row;
		//printf ("index: %d, no overlap, spe_loc, %d, b3: 0x%02x\n", index, spe_loc, *b3_loc);
	}

	return b3_loc;
}

/* This function processes the B1, B2 and B3 comparison on scrambled SONET frames with concatenated payloads.
 * Firstly, these bytes are retrieved from the current descrambled SONET frame.
 * Then each of the bytes are calculated over the previous scrambled SONET frame.
 * The retrieved and calculated values are compared and an error counter is incremented if needed.
 */
static int process_bip_bytes_concatenated (uint16_t length)
{
	uint32_t i;

	parity_bytes_ptr_t parity_bytes_rx;
	uint8_t *h1_previous;
	uint8_t *h2_previous;
	uint8_t *h3_previous;
	uint8_t *h1_current;
	uint8_t *h2_current;
	uint8_t *h3_current;
	payload_ptr_t	pload_ptr_previous;
	payload_ptr_t	pload_ptr_current;
	static uint8_t justification[INTERFACE_COUNT] = {0, 0};

	parity_bytes_ptr_t parity_bytes_calc;

	// To store frame errors
	uint8_t b1_error_frame = 0;
	uint8_t b2_error_frame = 0;
	uint8_t b3_error_frame = 0;

	// Allocate memory for the received parity bytes structure and initialise
	parity_bytes_rx.b1 = (uint8_t *)malloc(1);
	*parity_bytes_rx.b1 = 0x00;
	parity_bytes_rx.b2 = (uint8_t *)malloc(raw_record_array[user_rate].b2_len);
	memset(parity_bytes_rx.b2, 0, raw_record_array[user_rate].b2_len);
	parity_bytes_rx.b3 = (uint8_t *)malloc(1);	// NOTE: Only one B3 for concatenated SONET
	*parity_bytes_rx.b3 = 0x00;

	pload_ptr_previous.ptr_val = 0;
	pload_ptr_current.ptr_val = 0;

	//printf ("processing bip bytes concatenated...\n");

	// Determine B1 and B2 locations from current descrambled frame
	parity_bytes_rx.b1 = descrambled_current[iface] + raw_record_array[user_rate].b1_loc;
	parity_bytes_rx.b2 = descrambled_current[iface] + raw_record_array[user_rate].b2_loc;

	//printf ("descrambled current[%d]: b1_loc: %d, b1_rx: 0x%x, b2_loc: %d, b2_rx: 0x%x\n", iface, parity_bytes_rx.b1 - descrambled_current[iface], *parity_bytes_rx.b1, parity_bytes_rx.b2 - descrambled_current[iface], *parity_bytes_rx.b2);

	// Determine NDF and payload pointer values of the previous descrambled frame
	h1_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc;
	h2_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].h1_len;
	h3_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + (raw_record_array[user_rate].h1_len * 2);
	//printf("descrambled previous[%d]: h1: 0x%x, h2: 0x%x, h3: 0x%x\n", iface, *h1_previous, *h2_previous, *h3_previous);

	pload_ptr_previous.ndf = (*h1_previous & 0xf0) >> 4;
	pload_ptr_previous.ptr_val |= (*h2_previous & 0xff);
	pload_ptr_previous.ptr_val |= (*h1_previous & 0x03) << 8;

	//printf("descrambled previous[%d]: ndf: %d, ptr_val: %d\n", iface, pload_ptr_previous.ndf, pload_ptr_previous.ptr_val);

	// Determine NDF and payload pointer values of the previous descrambled frame
	h1_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc;
	h2_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].h1_len;
	h3_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + (raw_record_array[user_rate].h1_len * 2);
	//printf("descrambled current[%d]: h1: 0x%x, h2: 0x%x, h3: 0x%x\n", iface, *h1_current, *h2_current, *h3_current);

	pload_ptr_current.ndf = (*h1_current & 0xf0) >> 4;
	pload_ptr_current.ptr_val |= (*h2_current & 0xff);
	pload_ptr_current.ptr_val |= (*h1_current & 0x03) << 8;
	//printf("descrambled current[%d]: ndf: %d, ptr_val: %d\n", iface, pload_ptr_current.ndf, pload_ptr_current.ptr_val);

	// Determine B3 location from H1, H2, H3 pointer calculation, depending on line rate
	if (pload_ptr_current.ndf == NDF_NORMAL)
	{
		//printf("iface: %d, NDF normal: 0x%x \n", iface, pload_ptr_current.ndf);

		// Check if frequency justification needs to be performed
		if (pload_ptr_current.ptr_val != pload_ptr_previous.ptr_val)
		{
			if (justification[iface] == 0)
			{
				justification[iface] = 1;

				// Check justification type
				if ((pload_ptr_current.ptr_val & 0x2) != (pload_ptr_previous.ptr_val & 0x2))	// Positive justification (I-bits inverted)
				{
					parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, POSITIVE_JUSTIFICATION, 0);
				}
				else if ((pload_ptr_current.ptr_val & 0x1) != (pload_ptr_previous.ptr_val & 0x1)) // Negative justification (D-bits inverted)
				{
					parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, NEGATIVE_JUSTIFICATION, 0);
				}
				else
				{
					dagutil_warning("interface %d: frame %d: frequency justification required but no justification type detected\n", iface, frame_counter);
					return FAIL;
				}
			}
			else	// Frequency justification was done previously
			{
				justification[iface] = 0;
			}
		}
		else	// No justification, previous pointer value is the same as current pointer value
		{
			parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, NO_JUSTIFICATION, 0);
		}
	}
	else if (pload_ptr_current.ndf == NDF_SET)
	{
		//printf("iface: %d, NDF set: 0x%x \n", iface, pload_ptr_current.ndf);
		parity_bytes_rx.b3 = determine_b3_location (pload_ptr_current.ptr_val, PAYLOAD_POINTER_SET, 0);
	}
	else
	{
		dagutil_warning("interface %d: frame %d: invalid NDF value detected: %d\n", iface, frame_counter, pload_ptr_current.ndf);
		return FAIL;
	}

	// Allocate memory for the calculated parity bytes structure and initialise
	parity_bytes_calc.b1 = (uint8_t *)malloc(1);
	*parity_bytes_calc.b1 = 0x00;
	parity_bytes_calc.b2 = (uint8_t *)malloc(raw_record_array[user_rate].b2_len);
	memset(parity_bytes_calc.b2, 0, raw_record_array[user_rate].b2_len);
	parity_bytes_calc.b3 = (uint8_t *)malloc(1);
	*parity_bytes_calc.b3 = 0x00;

	// Calculate B1 and B2 parity over the scrambled frame	
	parity_bytes_calc = calculate_b1_b2_parity (parity_bytes_calc);

#if 0
	printf("iface: %d, parity_bytes_calc.b1: 0x%02x\n", iface, *parity_bytes_calc.b1);
	for (i = 0; i < raw_record_array[user_rate].b2_len; i++)
		printf("iface: %d, parity_bytes_calc.b2(%d): 0x%02x\n", iface, i, *(parity_bytes_calc.b2 + i));
#endif

	// Calculate B3 parity over the scrambled frame
	parity_bytes_calc = calculate_b3_parity_concatenated (parity_bytes_calc, pload_ptr_previous.ptr_val);

	// Test B1 byte
	//printf("iface: %d, parity_bytes_rx.b1: 0x%02x, parity_byte_calc.b1: 0x%02x\n", iface, *parity_bytes_rx.b1, *parity_bytes_calc.b1);
	if (*parity_bytes_rx.b1 != *parity_bytes_calc.b1)
	{
		++b1_error_total;
		++b1_error_frame;
	}

	// Test B2 bytes
	for (i = 0; i < raw_record_array[user_rate].b2_len; i++)
	{
		//printf("iface: %d, i: %d, parity_bytes_rx.b2: 0x%02x, parity_byte_calc.b2: 0x%02x\n", iface, i, *(parity_bytes_rx.b2 + i), *(parity_bytes_calc.b2 + i));
		if (*(parity_bytes_rx.b2 + i) != *(parity_bytes_calc.b2 + i))
		{
			++b2_error_total;
			++b2_error_frame;
		}
	}

	// Test B3 byte
	//printf("iface: %d, parity_bytes_rx.b3: 0x%02x, parity_byte_calc.b3: 0x%02x\n", iface, *parity_bytes_rx.b3, *parity_bytes_calc.b3);
	if (*parity_bytes_rx.b3 != *parity_bytes_calc.b3)
	{
		++b3_error_total;
		++b3_error_frame;
	}

	if ((b1_error_frame > 0) || (b2_error_frame > 0) || (b3_error_frame > 0))
		return FAIL;
	else
		return PASS;
}

/* This function processes the B1, B2 and B3 comparison on scrambled SONET frames with channelised payloads.
 * Firstly, these bytes are retrieved from the current descrambled SONET frame.
 * Then each of the bytes are calculated over the previous scrambled SONET frame.
 * The retrieved and calculated values are compared and an error counter is incremented if needed.
 */
static int process_bip_bytes_channelised (uint16_t length)
{
	uint32_t i;

	parity_bytes_ptr_t parity_bytes_rx;
#ifndef _WINDOWS
	uint8_t *b3_rx_array[raw_record_array[user_rate].frame_count];	// Array of pointers
#else
	uint8_t *b3_rx_array[4];
#endif
	uint8_t *h1_previous;
	uint8_t *h2_previous;
	uint8_t *h3_previous;
	uint8_t *h1_current;
	uint8_t *h2_current;
	uint8_t *h3_current;
	payload_ptr_t	pload_ptr_previous;
	payload_ptr_t	pload_ptr_current;
	static uint8_t justification[INTERFACE_COUNT] = {0, 0};
#ifndef _WINDOWS
	uint16_t scrambled_spe_ptrs[raw_record_array[user_rate].frame_count]; // This array stores the pointer values for use in calculating B3s from the previous descrambled frames
#else
	uint16_t scrambled_spe_ptrs[4];
#endif

	parity_bytes_ptr_t parity_bytes_calc;

	// To store frame errors
	uint8_t b1_error_frame = 0;
	uint8_t b2_error_frame = 0;
	uint8_t b3_error_frame = 0;

	// Allocate memory for the received parity bytes structure and initialise
	parity_bytes_rx.b1 = (uint8_t *)malloc(1);
	*parity_bytes_rx.b1 = 0x00;
	parity_bytes_rx.b2 = (uint8_t *)malloc(raw_record_array[user_rate].b2_len);
	memset(parity_bytes_rx.b2, 0, raw_record_array[user_rate].b2_len);
	parity_bytes_rx.b3 = (uint8_t *)malloc(1);
	*parity_bytes_rx.b3 = 0x00;
	for (i = 0; i < raw_record_array[user_rate].frame_count; i++)
	{
		b3_rx_array[i] = (uint8_t *)malloc(1);
		*b3_rx_array[i] = 0x00;
	}

	pload_ptr_previous.ptr_val = 0;
	pload_ptr_current.ptr_val = 0;

	//printf ("processing bip bytes channelised...\n");

	// Retrieve B1 and B2 locations from current descrambled frame
	parity_bytes_rx.b1 = descrambled_current[iface] + raw_record_array[user_rate].b1_loc;
	parity_bytes_rx.b2 = descrambled_current[iface] + raw_record_array[user_rate].b2_loc;

	//printf ("descrambled current[%d]: b1_loc: %d, b1_rx: 0x%x, b2_loc: %d, b2_rx: 0x%x\n", iface, parity_bytes_rx.b1 - descrambled_current, *parity_bytes_rx.b1, parity_bytes_rx.b2 - descrambled_current, *parity_bytes_rx.b2);

	// Retrieve B3 location(s) from H1, H2, H3 pointer calculation, depending on line rate
	for (i = 0; i < raw_record_array[user_rate].frame_count; i++)
	{
		h1_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + i;
		h2_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].h1_len + i;
		h3_previous = descrambled_previous[iface] + raw_record_array[user_rate].h_ptr_loc + (raw_record_array[user_rate].h1_len * 2) + i;

		//printf("frame: %d, descrambled_previous[%d]: h1_previous: 0x%x, h2_previous: 0x%x, h3_previous: 0x%x\n", i, iface, *h1_previous, *h2_previous, *h3_previous);

		pload_ptr_previous.ndf = (*h1_previous & 0xf0) >> 4;
		pload_ptr_previous.ptr_val |= (*h2_previous & 0xff);
		pload_ptr_previous.ptr_val |= (*h1_previous & 0x03) << 8;

		//printf("frame: %d, descrambled_previous[%d]: prev_ndf: %d, prev_ptr_val: %d\n", i, iface, pload_ptr_previous.ndf, pload_ptr_previous.ptr_val);

		h1_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + i;
		h2_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + raw_record_array[user_rate].h1_len + i;
		h3_current = descrambled_current[iface] + raw_record_array[user_rate].h_ptr_loc + (raw_record_array[user_rate].h1_len * 2) + i;

		//printf("frame: %d, descrambled_current[%d]: h1_current: 0x%x, h2_current: 0x%x, h3_current: 0x%x\n", i, iface, *h1_current, *h2_current, *h3_current);

		pload_ptr_current.ndf = (*h1_current & 0xf0) >> 4;
		pload_ptr_current.ptr_val |= (*h2_current & 0xff);
		pload_ptr_current.ptr_val |= (*h1_current & 0x03) << 8;
		
		//printf("frame: %d, descrambled_current[%d]: curr_ndf: %d, curr_prt_val: %d\n", i, iface, pload_ptr_current.ndf, pload_ptr_current.ptr_val);

		if (pload_ptr_current.ndf == NDF_NORMAL)
		{
			//printf("iface: %d, frame: %d, NDF normal: 0x%x \n", iface, i, pload_ptr_current.ndf);

			// Check if frequency justification needs to be performed
			if (pload_ptr_current.ptr_val != pload_ptr_previous.ptr_val)
			{

				if (justification[iface] == 0)
				{	
					justification[iface] = 1;
	
					// Check justification type
					if ((pload_ptr_current.ptr_val & 0x2) != (pload_ptr_previous.ptr_val & 0x2))	// Positive justification (I-bits inverted)
					{
						parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, POSITIVE_JUSTIFICATION, i);
						b3_rx_array[i] = parity_bytes_rx.b3;
						scrambled_spe_ptrs[i] = pload_ptr_previous.ptr_val;
					}
					else if ((pload_ptr_current.ptr_val & 0x1) != (pload_ptr_previous.ptr_val & 0x1)) // Negative justification (D-bits inverted)
					{
						parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, NEGATIVE_JUSTIFICATION, i);
						b3_rx_array[i] = parity_bytes_rx.b3;
						scrambled_spe_ptrs[i] = pload_ptr_previous.ptr_val;
					}
					else
					{
						dagutil_warning("interface %d: frame %d: frequency justification required but no justification type detected\n", iface, frame_counter);
						return FAIL;
					}
				}
				else	// Frequency justification was done previously
				{
					justification[iface] = 0;
				}
			}
			else	// No justification
			{
				parity_bytes_rx.b3 = determine_b3_location (pload_ptr_previous.ptr_val, NO_JUSTIFICATION, i);
				b3_rx_array[i] = parity_bytes_rx.b3;
				scrambled_spe_ptrs[i] = pload_ptr_previous.ptr_val;
			}
		}
		else if (pload_ptr_current.ndf == NDF_SET)
		{
			//printf("frame: %d, NDF set: 0x%x \n", i, pload_ptr_current.ndf);
			parity_bytes_rx.b3 = determine_b3_location (pload_ptr_current.ptr_val, PAYLOAD_POINTER_SET, i);
			b3_rx_array[i] = parity_bytes_rx.b3;
			scrambled_spe_ptrs[i] = pload_ptr_current.ptr_val;
		}
		else
		{
 			dagutil_warning("interface %d: frame %d: invalid NDF value detected: %d\n", iface, frame_counter, pload_ptr_current.ndf);
			return FAIL;
		}
	}

	// Allocate memory for the calculated parity bytes structure and initialise
	parity_bytes_calc.b1 = (uint8_t *)malloc(1);
	*parity_bytes_calc.b1 = 0x00;
	parity_bytes_calc.b2 = (uint8_t *)malloc(raw_record_array[user_rate].b2_len);
	memset(parity_bytes_calc.b2, 0, raw_record_array[user_rate].b2_len);
	parity_bytes_calc.b3 = (uint8_t *)malloc(raw_record_array[user_rate].frame_count);
	memset(parity_bytes_calc.b3, 0, raw_record_array[user_rate].frame_count);

	parity_bytes_calc = calculate_b1_b2_parity (parity_bytes_calc);

#if 0
	printf("iface: %d, parity_bytes_calc.b1: 0x%02x\n", iface, *parity_bytes_calc.b1);
	for (i = 0; i < raw_record_array[user_rate].b2_len; i++)
		printf("iface: %d, parity_bytes_calc.b2(%d): 0x%02x\n", iface, i, *(parity_bytes_calc.b2 + i));
#endif


	parity_bytes_calc = calculate_b3_parity_channelised (parity_bytes_calc, scrambled_spe_ptrs);

	// Test B1 byte
	//printf("iface: %d, parity_bytes_rx.b1: 0x%02x, parity_byte_calc.b1: 0x%02x\n", iface, *parity_bytes_rx.b1, *parity_bytes_calc.b1);
	if (*parity_bytes_rx.b1 != *parity_bytes_calc.b1)
	{
		++b1_error_total;
		++b1_error_frame;
	}

	// Test B2 bytes
	for (i = 0; i < raw_record_array[user_rate].b2_len; i++)
	{
		//printf("iface: %d, i: %d, parity_bytes_rx.b2: 0x%02x, parity_byte_calc.b2: 0x%02x\n", iface, i, *(parity_bytes_rx.b2 + i), *(parity_bytes_calc.b2 + i));
		if (*(parity_bytes_rx.b2 + i) != *(parity_bytes_calc.b2 + i))
		{
			++b2_error_total;
			++b2_error_frame;
		}
	}

	// Test B3 bytes
	for (i = 0; i < raw_record_array[user_rate].frame_count; i++)
	{
		//printf("iface: %d, i: %d, b3_rx_array[%d]: 0x%02x, parity_byte_calc.b3: 0x%02x\n", iface, i, i, *b3_rx_array[i], *(parity_bytes_calc.b3 + i));
		if (*b3_rx_array[i] != *(parity_bytes_calc.b3 + i))
		{
			++b3_error_total;
			++b3_error_frame;
		}
	}

	if ((b1_error_frame > 0) || (b2_error_frame > 0) || (b3_error_frame > 0))
		return FAIL;
	else
		return PASS;
}


