#include "easiroc.h"
static const int SiTCP_PORT = 24;
SOCKET sock;
unsigned int slowdata[sizeByte_SC];
unsigned int ReadSCdata[sizeByte_RSC];
unsigned int Pindata[sizeByte_PIN];
unsigned int iniData[sizeByte_PIN + 2*sizeByte_SC + 2*sizeByte_RSC];
unsigned int Probedata[sizeByte_PSC];
int ForceStop = 0;
int EndADC = 0;
#include <windows.h>
void usleep(__int64 usec)
{
HANDLE timer;
LARGE_INTEGER ft;
ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time
timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
//------------------------------------------------------------------------------------------------------
int easiroc_Init(const char* SiTCP_MASTER_IP, unsigned int daq_mode)
{
// Start WinSock
printf("easiroc_Init --> Start.\n");
WORD wVersionRequested;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed with error!\n");
}
//Initialize -------------------------------------------------------------
sock = INVALID_SOCKET;
struct sockaddr_in SiTCP_ADDR;
const char* IP;
unsigned int port;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET) {
printf("Error: easiroc_Init::sock = %d\n", sock
);
return -1;
}
IP = SiTCP_MASTER_IP;
port = SiTCP_PORT;
SiTCP_ADDR.sin_family = AF_INET;
SiTCP_ADDR.sin_port = htons((unsigned short)port);
SiTCP_ADDR.sin_addr.s_addr = inet_addr(IP);
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
//Connection -------------------------------------------------------------
int con = connect(sock, (struct sockaddr*)&SiTCP_ADDR, sizeof(SiTCP_ADDR));
printf("easiroc_Init::connect = %d\n", con
);
if(0 > con){
printf("SiTCP Master :: Connection fail\n");
closesocket(sock);
return -1;
}
printf("SiTCP Master :: Connection Done\n\n");
// Auto Initialize -------------------------------------------------------
PrepareFPGA();
DebugFPGA(sock);
printf("\nASIS Initialize : Done\n\n");
//Sleep(1000);
WriteData(sock, 37888);
PrepareSC(1);
TransmitSC(sock);
printf("\nSlow Control chip1 : Done\n\n");
PrepareReadSC(1);
TransmitReadSC(sock);
printf("\nRead Slow Control chip1 : Done\n\n");
WriteData(sock, 21504);
PrepareSC(2);
TransmitSC(sock);
printf("\nSlow Control chip2 : Done\n\n");
PrepareReadSC(2);
TransmitReadSC(sock);
printf("\nRead Slow Control chip2: Done\n\n");
WriteData(sock, 5120);
{
unsigned int signal = 31;
signal = signal << 16;
unsigned int data = daq_mode;
data = data << 8;
signal += data;
if(-1 == WriteData(sock, signal)){
return -1;
}
printf("\n#D : DAQ mode is %d \n", daq_mode
);
}
return 0;
}
//------------------------------------------------------------------------------------------------------
void easiroc_Close()
{
closesocket(sock);
WSACleanup();
printf("easiroc_Close -> Done.\n");
}
//------------------------------------------------------------------------------------------------------
int WriteData(SOCKET sock, unsigned int data)
{
int esrcdbg = 0;
data += 128 << 24;;
if(esrcdbg
) printf("0x%X\n", data
);
send(sock, (char*)&data, sizeof(int), 0);
// this sleep is needed!
Sleep(WRITEDATA_DELAY);
unsigned int buf = 0;
unsigned int length = 4;
int ret = recv(sock, (char*)&buf, length, 0);
if ( ret > 0 ) {
if(esrcdbg
) printf("Bytes received: %d\n", ret
);
} else if ( ret == 0 ) {
printf("Connection closed\n");
return -1;
} else {
printf("recv failed: %d\n", WSAGetLastError
());
return -1;
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_LoadIni(const char *iniFile)
{
FILE
*fp
=fopen(iniFile
, "rt");
if(!fp) {
printf("easiroc::LoadIni() cannot open file %s !!!\n", iniFile
);
return 0;
}
char lbuff[256];
int counter = 0;
while(1) {
if( '#' == (char)c ) {
//printf("Found Comment line: %s", lbuff);
} else {
fscanf(fp
, "%u\n", &iniData
[counter
] );
//printf("Found value [%d] %u\n", counter, iniData[counter]);
counter++;
}
}
return 1;
}
//------------------------------------------------------------------------------------------------------
void PrepareFPGA()
{
Pindata[0] = iniData[0];
Pindata[1] = iniData[1];
}
//------------------------------------------------------------------------------------------------------
void PrepareSC(int chipNo)
{
if(chipNo == 1) {
slowdata[ 0] = iniData[sizeByte_PIN + 0];
slowdata[ 1] = iniData[sizeByte_PIN + 1];
slowdata[ 2] = iniData[sizeByte_PIN + 2];
slowdata[ 3] = iniData[sizeByte_PIN + 3];
slowdata[ 4] = iniData[sizeByte_PIN + 4];
slowdata[ 5] = iniData[sizeByte_PIN + 5];
slowdata[ 6] = iniData[sizeByte_PIN + 6];
slowdata[ 7] = iniData[sizeByte_PIN + 7];
slowdata[ 8] = iniData[sizeByte_PIN + 8];
slowdata[ 9] = iniData[sizeByte_PIN + 9];
slowdata[10] = iniData[sizeByte_PIN + 0];
slowdata[11] = iniData[sizeByte_PIN + 11];
slowdata[12] = iniData[sizeByte_PIN + 12];
slowdata[13] = iniData[sizeByte_PIN + 13];
slowdata[14] = iniData[sizeByte_PIN + 14];
} else {
slowdata[ 0] = iniData[sizeByte_PIN + 1*sizeByte_SC + 0];
slowdata[ 1] = iniData[sizeByte_PIN + 1*sizeByte_SC + 1];
slowdata[ 2] = iniData[sizeByte_PIN + 1*sizeByte_SC + 2];
slowdata[ 3] = iniData[sizeByte_PIN + 1*sizeByte_SC + 3];
slowdata[ 4] = iniData[sizeByte_PIN + 1*sizeByte_SC + 4];
slowdata[ 5] = iniData[sizeByte_PIN + 1*sizeByte_SC + 5];
slowdata[ 6] = iniData[sizeByte_PIN + 1*sizeByte_SC + 6];
slowdata[ 7] = iniData[sizeByte_PIN + 1*sizeByte_SC + 7];
slowdata[ 8] = iniData[sizeByte_PIN + 1*sizeByte_SC + 8];
slowdata[ 9] = iniData[sizeByte_PIN + 1*sizeByte_SC + 9];
slowdata[10] = iniData[sizeByte_PIN + 1*sizeByte_SC + 10];
slowdata[11] = iniData[sizeByte_PIN + 1*sizeByte_SC + 11];
slowdata[12] = iniData[sizeByte_PIN + 1*sizeByte_SC + 12];
slowdata[13] = iniData[sizeByte_PIN + 1*sizeByte_SC + 13];
slowdata[14] = iniData[sizeByte_PIN + 1*sizeByte_SC + 14];
}
}
//------------------------------------------------------------------------------------------------------
void PrepareReadSC(int chipNo)
{
if(chipNo == 1) {
ReadSCdata[0] = iniData[sizeByte_PIN + 2*sizeByte_SC + 0];
} else {
ReadSCdata[0] = iniData[sizeByte_PIN + 2*sizeByte_SC + 1];
}
}
//------------------------------------------------------------------------------------------------------
void easiroc_PrintData()
{
for(int i
=0; i
<sizeByte_PIN
; i
++) printf("%u\n", Pindata
[i
]);
for(int i
=0; i
<sizeByte_SC
; i
++) printf("%u\n", slowdata
[i
]);
for(int i
=0; i
<sizeByte_SC
; i
++) printf("%u\n", slowdata
[i
]);
printf(">>> ReadSCdata (1)\n");
for(int i
=0; i
<sizeByte_RSC
; i
++) printf("%u\n", ReadSCdata
[i
]);
printf(">>> ReadSCdata (2)\n");
for(int i
=0; i
<sizeByte_RSC
; i
++) printf("%u\n", ReadSCdata
[i
]);
}
//------------------------------------------------------------------------------------------------------
int DebugFPGA(SOCKET socket)
{
printf(">>> DebugFPGA Pindata:\n");
for(int i
=0; i
<sizeByte_PIN
; i
++) printf("%u\n", Pindata
[i
]);
unsigned int buffer = 0;
buffer += 0 << 16;
buffer += (Pindata[0] & 255) << 8;
if(-1 == WriteData(socket, buffer)){
return -1;
}
for(int i = 1 ; i<5; ++i){
buffer = 0;
if(i == 4){
buffer += 5 << 16;
}else{
buffer += i << 16;
}
buffer += ((Pindata[1] >> (i-1)*8) & 255) << 8;
if(-1 == WriteData(socket, buffer)){
return -1;
}
Sleep(1);
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int TransmitSC(SOCKET socket)
{
printf(">>> TransmitSC slowdata:\n");
for(int i
=0; i
<sizeByte_SC
; i
++) printf("%u\n", slowdata
[i
]);
unsigned int data = 0;
//Set SC mode -----------------------------------------------------------
data = 0;
data += 1 << 16;
data += 240 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
//SC start -------------------------------------------------------------
data = 0;
data += 10 << 16;
data += (slowdata[0] & 255) << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
for(int i = 1; i<15; ++i){
for(int shift = 0; shift<4; ++shift){
data = 0;
data += 10 << 16;
data += ((slowdata[i] >> 8*shift) & 255) << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
//std::cout<<"test"<<std::endl;
Sleep(1);
}
}
// Sleep(50000);
//StartCycle -----------------------------------------------------------
data = 0;
data += 1 << 16;
data += 242 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
data = 0;
data += 1 << 16;
data += 240 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
// Sleep(50000);
//Load SC --------------------------------------------------------------
data = 0;
data += 1 << 16;
data += 241 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
data = 0;
data += 1 << 16;
data += 240 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int TransmitReadSC(SOCKET socket)
{
printf(">>> TransmitReadSC ReadSCdata:\n");
for(int i
=0; i
<sizeByte_RSC
; i
++) printf("%u\n", ReadSCdata
[i
]);
//SCA read ---------------------------------------------------------------
unsigned int data = 0;
for(int i = 0; i<4; ++i){
data = 0;
data += 12 << 16;
data += ((ReadSCdata[0] >> i*8) & 255) << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
Sleep(1);
}
//StartCycle ------------------------------------------------------------
data = 0;
data += 1 << 16;
data += 242 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
data = 0;
data += 1 << 16;
data += 240 << 8;
if(-1 == WriteData(socket, data)){
return -1;
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int PreparePSC(int CurrentCh, int CurrentProbeType)
{
enum Asign{fs_all, ssh_16, ssh_0, pa_16, pa_0, sizeAsign};
enum DataName{fs, ssh_hg, ssh_lg, pa_hg, pa_lg, sizeDataName};
enum DataName CurrentProbe;
CurrentProbe = fs;
static unsigned int ProbeBuffer = 0;
int flag_rst = 0;
int flag_input = 1;
for(int i = 0; i<sizeByte_PSC; ++i){
Probedata[i] = 0;
}
switch(CurrentProbeType){
case 1:
CurrentProbe = pa_hg;
break;
case 2:
CurrentProbe = pa_lg;
break;
case 3:
CurrentProbe = ssh_hg;
break;
case 4:
CurrentProbe = ssh_lg;
break;
case 5:
CurrentProbe = fs;
break;
case 6:
break;
case 7:
break;
case 8:
flag_rst = 1;
CurrentCh = 0;
ProbeBuffer = 0;
CurrentProbe = fs;
break;
default:
break;
};
//printf("CurrentProbeType = %d | CurrentProbe = %d\n", CurrentProbeType, CurrentProbe);
if(flag_rst) {
return 0;
}
if(flag_input) {
ProbeBuffer = 1 << (31 - CurrentCh);
}
unsigned int buffer_16 = ProbeBuffer*ProbeBuffer;
unsigned int buffer_0 = (ProbeBuffer >> 16)*(ProbeBuffer >> 16);
if(CurrentProbe == ssh_hg || CurrentProbe == pa_hg) {
//printf("CurrentProbe == ssh_hg || CurrentProbe == pa_hg\n");
buffer_16 = buffer_16 << 1;
buffer_0 = buffer_0 << 1;
}
if(CurrentProbe == fs) {
//printf("CurrentProbe == fs\n");
Probedata[fs_all] = ProbeBuffer;
} else if(CurrentProbe == ssh_hg || CurrentProbe == ssh_lg) {
if(CurrentCh > 15) {
Probedata[ssh_16] = buffer_16;
} else {
Probedata[ssh_0] = buffer_0;
}
} else if(CurrentProbe == pa_hg || CurrentProbe == pa_lg) {
if(CurrentCh > 15){
Probedata[pa_16] = buffer_16;
}else{
Probedata[pa_0] = buffer_0;
}
} else {
}
for(int shift = 0; shift<32; ++shift) {
if(((Probedata[fs_all]) >> shift) & 1) {
}else{
}
}
for(int index = ssh_16; index <= ssh_0; ++index) {
for(int shift = 0; shift<32; ++shift) {
if(((Probedata[index]) >> shift) & 1) {
} else {
}
}
}
for(int index = pa_16; index <= pa_0; ++index) {
for(int shift = 0; shift<32; ++shift) {
if(((Probedata[index]) >> shift) & 1) {
}else{
}
}
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int TransmitProbe(SOCKET socket)
{
unsigned int data = 0;
//Set Probe mode --------------------------------------------------------
data += 1 << 16;
data += 208 << 8;
if(-1 == WriteData(socket, data)) {
return -1;
}
//Probe start ----------------------------------------------------------
for(int i = 0; i<sizeByte_PSC; ++i) {
for(int shift = 0; shift<4; ++shift) {
data = 0;
data += 10 << 16;
data += ((Probedata[i] >> 8*shift) & 255) << 8;
if(-1 == WriteData(socket, data)) {
return -1;
}
}
}
//StartCycle ------------------------------------------------------------
data = 0;
data += 1 << 16;
data += 210 << 8;
if(-1 == WriteData(socket, data)) {
return -1;
}
data = 0;
data += 1 << 16;
data += 208 << 8;
if(-1 == WriteData(socket, data)) {
return -1;
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_fTransmitSC()
{
if(WriteData(sock, 37888) != 0) return -1;
PrepareSC(1);
if(TransmitSC(sock) != 0) return -1;
printf("\nSlow Control chip1 : Done\n\n");
if(WriteData(sock, 21504) != 0) return -1;
PrepareSC(2);
if(TransmitSC(sock) != 0) return -1;
printf("\nSlow Control chip2 : Done\n\n");
if(WriteData(sock, 5120) != 0) return -1;
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_fTransmitReadSC()
{
if(WriteData(sock, 37888) != 0) return -1;
PrepareReadSC(1);
if(TransmitReadSC(sock) != 0) return -1;
printf("\nRead Slow Control chip1 : Done\n\n");
if(WriteData(sock, 21504) != 0) return -1;
PrepareReadSC(2);
if(TransmitReadSC(sock) != 0) return -1;
printf("\nRead Slow Control chip2 : Done\n\n");
if(WriteData(sock, 5120) != 0) return -1;
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_fAsicInitialize()
{
PrepareFPGA();
DebugFPGA(sock);
printf("\nASIS Initialize : Done\n\n");
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_fTransmitProbe(int CurrentCh, int CurrentProbe)
{
//printf("\neasiroc_fTransmitProbe: CurrentCh : %d | CurrentProbe: %d\n\n", CurrentCh, CurrentProbe);
if(CurrentCh < 32) {
if(WriteData(sock, 37888)!= 0) return -1;
} else if(CurrentCh > 31) {
if(WriteData(sock, 21504)!= 0) return -1;
CurrentCh = CurrentCh - 32;
}
//printf("\neasiroc_fTransmitProbe: Preparing PSC\n\n");
if(PreparePSC(CurrentCh, CurrentProbe)!= 0) {
printf("\neasiroc_fTransmitProbe: PreparingPSC ERROR !!!!!!!!!!!!!!!!!!!\n\n");
return -1;
}
//printf("\neasiroc_fTransmitProbe: TransmitProbe\n\n");
if(TransmitProbe(sock)!= 0) {
printf("\neasiroc_fTransmitProbe: TransmitProbe ERROR !!!!!!!!!!!!!!!!!!!\n\n");
return -1;
}
//WriteData(3, 5120);
//printf("\nTransmitProbe: Success!\n\n");
return 0;
}
//------------------------------------------------------------------------------------------------------
int ADCOneCycle_wHeader_ver2(SOCKET socket, FILE* file, int stopping)
{
static const int NofHeader = 3;
unsigned int DataBuffer[1000];
memset(DataBuffer
, 0, sizeof(DataBuffer
));
unsigned int *ptr_DataBuf = DataBuffer;
unsigned int TotalRecvByte = 0;
unsigned int TotalRecvEvent = 0;
unsigned int sizeHeader = NofHeader*sizeof(int);
unsigned int Header[] = {1, 1, 1};
int ret = 0;
ret = recv(sock, (char*)Header, sizeHeader, 0);
//if(ret <= 0) printf("Fatal ADC ERROR : recv\n");
if(ret <= 0 && EndADC == 1) return -1;
// sleep necessary! ERRORS happen if trigger rate lower than ~ sleep frequency!
usleep(200);
//Sleep(1);
//printf("Header1 (0xFFFFEA0C) : 0x%x | Header2 (0x00000100) : 0x%x\n", Header[0], Header[1]);
if(Header[0] != 0xFFFFEA0C) {
printf("Fatal ADC ERROR : HEADER\n");
printf("Header1 : 0x%x | Header2 : 0x%x | Header3 : 0x%x\n", Header
[0], Header
[1], Header
[2]);
return -1;
} else {
unsigned int sizeData = (sizeof(int)*Header[1] & 0xffff);
unsigned int NofWord = (Header[1] & 0xffff);
unsigned int OneData[NofWord];
recv(socket, (char*)OneData, sizeData, 0);
if(!stopping) {
for(int i
= 0; i
<NofWord
; ++i
) printf("[%d] 0x%x ", i
, OneData
[i
]);
}
memcpy(ptr_DataBuf
, Header
, sizeHeader
);
ptr_DataBuf += NofHeader;
memcpy(ptr_DataBuf
, OneData
, sizeData
);
ptr_DataBuf += sizeData/sizeof(int);
TotalRecvByte += sizeHeader + sizeData;
++TotalRecvEvent;
}
if(EndADC != 1 && file!=NULL){
for(unsigned int i = 0; i<TotalRecvByte/sizeof(int); ++i) {
unsigned int buffer = DataBuffer[i];
fwrite((char*)&buffer
, sizeof(int), 1, file
);
}
}
return 0;
}
//------------------------------------------------------------------------------------------------------
void ADCStop(SOCKET socket)
{
unsigned int signal = 0;
signal += 16 << 24;
signal += 100 << 16;
send(socket, (char*)&signal, sizeof(int), 0);
Sleep(1);
signal = 0;
signal += 100 << 16;
send(socket, (char*)&signal, sizeof(int), 0);
Sleep(10);
return;
}
//------------------------------------------------------------------------------------------------------
int ContinuousADC_ver2(SOCKET socket, char *file_name, int MaxCycleNum)
{
unsigned int signal = 0;
int EventNum = 0;
FILE *file = NULL;
file
= fopen(file_name
, "wb");
if(file==NULL) {
printf("ContinuousADC_ver2: ERROR opneing file %s\n", file_name
);
return -1;
}
signal += 32 << 24;
signal += 100 << 16;
send(socket, (char*)&signal, sizeof(int), 0);
Sleep(100);
while(EventNum < MaxCycleNum){
//Sleep(10);
ADCOneCycle_wHeader_ver2(socket, file, 0);
++EventNum;
if(0 == EventNum%1000){
printf("Event # %d\n", EventNum
);
}
if(0 || EventNum == MaxCycleNum|| ForceStop == 1) {
ADCStop(socket);
EndADC = 1;
while(0 == ADCOneCycle_wHeader_ver2(socket, file, 1)) {
Sleep(1);
ADCStop(socket);
}
//printf("dummy data\n");
}
EndADC = 0;
ForceStop = 0;
break;
}
}
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_fDAQ(char *file_name, int MaxCycleNum)
{
printf("easiroc_fDAQ: Starting DAQ with file_name = %s | MaxCycleNum = %d\n", file_name
, MaxCycleNum
);
if(ContinuousADC_ver2(sock, file_name, MaxCycleNum) != 0) return -1;
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_singleADCstart() {
unsigned int signal = 0;
signal += 32 << 24;
signal += 100 << 16;
send(sock, (char*)&signal, sizeof(int), 0);
Sleep(100);
return 0;
}
//------------------------------------------------------------------------------------------------------
int easiroc_singleADCevent(unsigned int *retData) {
static const int NofHeader = 3;
unsigned int DataBuffer[1000];
memset(DataBuffer
, 0, sizeof(DataBuffer
));
unsigned int sizeHeader = NofHeader*sizeof(int);
unsigned int Header[] = {1, 1, 1};
int ret = 0;
ret = recv(sock, (char*)Header, sizeHeader, 0);
//if(ret <= 0) printf("Fatal ADC ERROR : recv\n");
if(ret <= 0 && EndADC == 1) return -1;
//usleep(200);
//Sleep(1);
//printf("Header1 (0xFFFFEA0C) : 0x%x | Header2 (0x00000100) : 0x%x\n", Header[0], Header[1]);
if(Header[0] != 0xFFFFEA0C) {
printf("Fatal ADC ERROR : HEADER\n");
printf("Header1 : 0x%x | Header2 : 0x%x | Header3 : 0x%x\n", Header
[0], Header
[1], Header
[2]);
return -2;
} else {
unsigned int sizeData = (sizeof(int)*Header[1] & 0xffff);
unsigned int NofWord = (Header[1] & 0xffff);
unsigned int OneData[NofWord];
recv(sock, (char*)OneData, sizeData, 0);
//for(int i = 0; i<NofWord; ++i) printf("[%d] 0x%x ", i, OneData[i]);
//printf("\n");
if(NofWord
< EASIROC_MAX_READ_N_WORDS
) memcpy(retData
, OneData
, sizeData
);
return NofWord;
}
}
//------------------------------------------------------------------------------------------------------
int easiroc_singleADCstop() {
ADCStop(sock);
EndADC = 1;
while(0 == ADCOneCycle_wHeader_ver2(sock, NULL, 1)) {
Sleep(1);
ADCStop(sock);
}
//printf("dummy data\n");
}
EndADC = 0;
ForceStop = 0;
return 0;
}
//------------------------------------------------------------------------------------------------------