Source file: /~heha/mb-iwp/Kleinkram/radariq.zip/src/os_sensor.cpp

#include "os_sensor.h"
#include <windows.h>	// CreateFile()
#include <shlwapi.h>	// wnsprintf()
#include "main.h"	// print()

Sensor::Sensor():
	handle(0),
	start_paddingbytes(0),
	stop_paddingbytes(0) {}
Sensor::~Sensor() {if (handle) CloseHandle((HANDLE)handle);}

// Nur Baudrate der Schnittstelle setzen
bool Sensor::setBaud(unsigned rate) const{
  DCB dcb;
  dcb.DCBlength=sizeof dcb;
  HANDLE h=(HANDLE)handle;
  if (!GetCommState(h,&dcb)) return false;
  dcb.BaudRate=rate;
  ((DWORD*)&dcb)[2]=1;	// Clear all flags except fBinary which must be set
  dcb.ByteSize=8;
  dcb.StopBits=ONESTOPBIT;
  dcb.Parity=NOPARITY;
  if (!SetCommState(h,&dcb)) {
   print("Kann Baudrate %u nicht setzen!",rate);
   return false;
  }
  return true;
 }

// Schnittstelle öffnen und Baudrate auf 19200 stellen
bool Sensor::connect(unsigned ComNr,unsigned flags) {
  this->~Sensor();
  TCHAR s[16];
  wnsprintf(s,16,TEXT("\\\\.\\COM%u"),ComNr+1);
  HANDLE h=CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,flags,0);
  if (h==INVALID_HANDLE_VALUE) h=0;
  handle=(size_t)h;
  if (!h) {
   print("COM%u lässt sich nicht öffnen!",ComNr+1);
   return false;
  }
  if (!setBaud()) return false;
  COMMTIMEOUTS to={0,0,100,0,100};
  return !!SetCommTimeouts(h,&to);
 }

int Sensor::read(void*v,unsigned l) const{
// TODO: Paddingbytes (gibt es sie überhaupt?) hier überlesen
  DWORD br;
  if (!ReadFile((HANDLE)handle,v,l,&br,0)) {
   print("Failed to %s data.","read");
   return -1;
  }
  if (br!=l) print("Partial %s, %u of %u bytes.","read",br,l);
  return br;
 }

int Sensor::write(const void*v,unsigned l) const{
 DWORD bw;
 if (!WriteFile((HANDLE)handle,v,l,&bw,0)) {
  print("Failed to %s data.","write");
  return -1;
 }
 if (bw!=l) print("Partial %s, %u of %u bytes.","write",bw,l);
 return bw;
}

bool Sensor::getValue(index_t index, unsigned&result, action_t action) const{
 char w[2]={char(action),char(index)};
 char r[5];
 if (!query(w,2,r,5)) return false;
 result=r[1]<<24|r[2]<<16|r[3]<<8|r[4]; // handmade byteswap avoids unaligned access
 return true;
}

// Optional third parameter allows suppressing answer checking,
// which is needed for baudrate changing (maybe badly implemented on device side)
bool Sensor::setValue(index_t index, unsigned value, bool expect_answer) const{
 char w[6]={
	char(WRITE_VAL),
	char(index),
	char(value>>24), // handmade byteswap avoids unaligned access
	char(value>>16),
	char(value>>8),
	char(value)};
 char r;
 return query(w,6,&r,expect_answer?1:0);
}

// Some high-level queries
unsigned Sensor::get_sensor_type() const{
 unsigned ret;
 if (!getValue(SENSOR_TYPE,ret)) {
  print("Get sensor type failed.");
  return 0;
 }
 return ret;
}

unsigned Sensor::get_result_data_selector() const{
 unsigned ret;
 if (!getValue(RESULT_DATA,ret)) {
  print("Get result data selector failed.");
  return 0;
 }
 return ret;
}

unsigned Sensor::get_baudrate() const{
 unsigned ret;
 if (!getValue(BAUDRATE,ret)) {
  print("Get baudrate failed.");
  return 0;
 }
 return ret;
}

unsigned Sensor::get_number_samples(unsigned*pro) const{
 unsigned r;
 if (!getValue(RADAR_PROFILE,r)) return 0;
 unsigned h = r/100, l = r%100;	// what an old-style f**k!!
 if (pro) *pro=l;		// return (and don't check) modulo-result
 else if (l!=BIGBW_MEDIUM) return 0;	// must be 16 (possibly other values are possible but undocumented)
 if (h>5) return 0;		// 0 upto 5 seem to be valid
 return 1U<<(10-h);		// 0:1024, 1:512, 2:256, 3:128, 4:64, 5:32 samples
}

bool Sensor::set_number_samples(unsigned n,unsigned pro) const{
 switch (n) {
  case 1024: break;
  case 512: pro+=100; break;
  case 256: pro+=200; break;
  case 128: pro+=300; break;
  case 64: pro+=400; break;
  case 32: pro+=500; break;
  default: return false;
 }
 return setValue(RADAR_PROFILE,pro);
}

// some functions returning strings
const char* Sensor::get_sensor_type_string() const{
 return get_sensor_type()==SENSOR_TYPE_REACH_RS485 ? "Reach RS485" : "unknown";
}

const char* Sensor::get_exact_sensor_type_string() const{
 unsigned r;
 if (getValue(EXACT_SENSOR_TYPE,r)) switch (r) {
  case EXACT_TYPE_REACH_GOD_MODE: return "EXACT_TYPE_REACH_GOD_MODE";
  case EXACT_TYPE_REACH_SR:	return "EXACT_TYPE_REACH_SR";
  case EXACT_TYPE_REACH_BE:	return "EXACT_TYPE_REACH_BE";
  case EXACT_TYPE_REACH_HS:	return "EXACT_TYPE_REACH_HS";
  case EXACT_TYPE_REACH_HP:	return "EXACT_TYPE_REACH_HP";
 }
 return "unknown";
}

char* Sensor::version_number_to_string(unsigned version_number) {
 static char buf[32];
 wnsprintfA(buf,sizeof buf,"%u.%u.%u%s",
	(version_number >> 24) & 0xff,	// major
	(version_number >> 16) & 0xff,	// minor
	(version_number >> 8) & 0xff,	// patch
	version_number & 0xff ? "_WIP" : "");
 return buf;
}

const char* Sensor::status_to_string(char error_code) {
 switch (error_code) {
  case API_SUCCESS:		return "API_SUCCESS";
  case API_SUCCESS_WEAK_SIGNAL:	return "API_SUCCESS_WEAK_SIGNAL";
  case API_ERROR:		return "API_ERROR";
  case API_ERROR_COMMAND:	return "API_ERROR_COMMAND";
  case API_ERROR_PARAMETER:	return "API_ERROR_PARAMETER";
  case API_ERROR_RANGE:		return "API_ERROR_RANGE";
  case API_ERROR_FORBIDDEN:	return "API_ERROR_FORBIDDEN";
  case API_ERROR_NO_TARGET:	return "API_ERROR_NO_TARGET";
 }
 return "UNKNOWN";
}

const char** Sensor::get_result_data_selector_string() const{
 const char**argv=new const char*[2];	// maximal 1 Argument und 1 Terminator
 int argc=0;
 unsigned result_data_selector = get_result_data_selector();
 if (result_data_selector & SELECT_IQDATA) argv[argc++]="SELECT_IQDATA";
 argv[argc]=0;	// Liste terminieren
 return argv;
}

// Some setters
bool Sensor::set_result_data_selector(unsigned measurement_type) const{
 return setValue(RESULT_DATA,measurement_type);
}

bool Sensor::set_baudrate(unsigned baudrate) const{
 if (!setValue(BAUDRATE,baudrate,false)) return false;
 Sleep(1);
 if (!setBaud(baudrate)) return false;
 Sleep(1);
 return true;
}

bool Sensor::save_parameters() const{
 char cmd=SAVE_ALL, r;
 return query(&cmd,1,&r,1);
}

bool Sensor::deserialize_iq_data(IqDataF&ret) const{
 char cmd=MEASURE;
 BYTE info[3];
 if (!query(&cmd,1,info,sizeof info)) return false;
 int nsamp = info[1]<<8 | info[2];
// Here, variable-length sample data follows the MEASURE query
 int l=nsamp<<1;	// Länge in Bytes
 BYTE*p=(BYTE*)_alloca(l);
// Die A/D-Daten kommen 80h-zentriert an, wie bei einer Steinzeit-Soundkarte.
 if (read(p,l)!=l) return false;
 if (!ret.commit_undefined(nsamp)) return false;
 float*q=ret.lineardata();
 if (l) do *q++=float(*p++-0x80); while(--l);	// zero-center and convert to float
 return true;
}

bool Sensor::get_measurement(IqDataF&ret,unsigned data_selector) const{
 if (!data_selector) data_selector = get_result_data_selector();
 if (data_selector & SELECT_IQDATA) return deserialize_iq_data(ret);
 return false;
}

void Sensor::disconnect() {
 this->~Sensor();
 handle=0;
}

bool Sensor::query(const void*w, int wlen, void*r, int rlen, bool check_result) const{
//	Basic query method. Can be used not-writing and not-reading.
//	Sends and returns fixed-size byte streams as-is.
//	Can check first byte of answer whether it's API_SUCCESS (=1) value
 if (wlen && write(w,wlen)!=wlen) return false;
 if (!rlen) return true;
 if (read(r,rlen)!=rlen) return false;	// incomplete reads are errors here
 if (check_result) {
  char ans=*((char*)r);
  if (ans!=API_SUCCESS) {
   print("Wrong answer byte %d = %s",ans,status_to_string(ans));
   PurgeComm((HANDLE)handle,15);	// As PurgeComm() may be slow, it's only used in case of error
   return false;
  }
 }
 return true;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded