aboutsummaryrefslogtreecommitdiff
//<>< (C) 2016 C. N. Lohr, MOSTLY Under MIT/x11 License.
//

#include "survive_internal.h"
#include <math.h> /* for sqrt */
#include <stdint.h>
#include <string.h>

static const float tau_table[33] = {0,
									0,
									0,
									1.151140982,
									1.425,
									1.5712213707,
									1.656266074,
									1.7110275587,
									1.7490784054,
									1.7770229476,
									1.798410005,
									1.8153056661,
									1.8289916275,
									1.8403044103,
									1.8498129961,
									1.8579178211,
									1.864908883,
									1.8710013691,
									1.8763583296,
									1.881105575,
									1.885341741,
									1.8891452542,
									1.8925792599,
									1.8956951735,
									1.8985352854,
									1.9011347009,
									1.9035228046,
									1.9057243816,
									1.9077604832,
									1.9096491058,
									1.9114057255,
									1.9130437248,
									1.914574735};

typedef struct {
	unsigned int sweep_time[SENSORS_PER_OBJECT];
	uint16_t sweep_len[SENSORS_PER_OBJECT]; // might want to align this to cache lines, will be hot for frequent access
} lightcaps_sweep_data;

typedef struct {
	int recent_sync_time;
	int activeLighthouse;
	int activeSweepStartTime;
	int activeAcode;

	//	int lh_pulse_len[NUM_LIGHTHOUSES];
	int lh_start_time[NUM_LIGHTHOUSES];
	int lh_max_pulse_length[NUM_LIGHTHOUSES];
	int8_t lh_acode[NUM_LIGHTHOUSES];
	int current_lh; // used knowing which sync pulse we're looking at.

} lightcap2_per_sweep_data;

typedef struct {
	double acode_offset;
	int sent_out_ootx_bits;
} lightcap2_global_data;

typedef struct {
	lightcaps_sweep_data sweep;
	lightcap2_per_sweep_data per_sweep;
	lightcap2_global_data global;
} lightcap2_data;

// static lightcap2_global_data lcgd = { 0 };

static int handle_lightcap2_getAcodeFromSyncPulse(SurviveObject *so, int pulseLen) {
	double oldOffset = ((lightcap2_data *)so->disambiguator_data)->global.acode_offset;

	int modifiedPulseLen = pulseLen - (int)oldOffset;

	double newOffset = (((pulseLen) + 250) % 500) - 250;

	((lightcap2_data *)so->disambiguator_data)->global.acode_offset = oldOffset * 0.9 + newOffset * 0.1;

// fprintf(stderr, "    %f\n", oldOffset);
#define ACODE_OFFSET 0
	if (pulseLen < 3250 - ACODE_OFFSET)
		return 0;
	if (pulseLen < 3750 - ACODE_OFFSET)
		return 1;
	if (pulseLen < 4250 - ACODE_OFFSET)
		return 2;
	if (pulseLen < 4750 - ACODE_OFFSET)
		return 3;
	if (pulseLen < 5250 - ACODE_OFFSET)
		return 4;
	if (pulseLen < 5750 - ACODE_OFFSET)
		return 5;
	if (pulseLen < 6250 - ACODE_OFFSET)
		return 6;
	return 7;
}

static uint8_t remove_outliers(SurviveObject *so) {
	return 0; // disabling this for now because it seems remove almost all the points for wired watchman and wired
			  // tracker.
	lightcap2_data *lcd = so->disambiguator_data;

	uint32_t sum = 0;
	uint8_t non_zero_count = 0;
	uint32_t mean = 0;

	uint16_t *min = NULL;
	uint16_t *max = NULL;
	uint8_t found_first = 0;

	// future: https://gcc.gnu.org/projects/tree-ssa/vectorization.html#vectorizab

	for (uint8_t i = 0; i < SENSORS_PER_OBJECT; i++) {
		sum += lcd->sweep.sweep_len[i];
		if (lcd->sweep.sweep_len[i] > 0)
			++non_zero_count;
	}

	if (non_zero_count == 0)
		return 0;

	mean = sum / non_zero_count;

	float standard_deviation = 0.0f;
	sum = 0;
	for (uint8_t i = 0; i < SENSORS_PER_OBJECT; i++) {
		uint16_t len = lcd->sweep.sweep_len[i];
		if (len > 0) {
			sum += (len - mean) * (len - mean);

			if (found_first == 0) {
				max = min = lcd->sweep.sweep_len + i;
				found_first = 1;
			} else {
				if (lcd->sweep.sweep_len[i] < *min)
					min = lcd->sweep.sweep_len + i;
				if (lcd->sweep.sweep_len[i] > *max)
					max = lcd->sweep.sweep_len + i;
			}
		}
	}
	standard_deviation = sqrtf(((float)sum) / ((float)non_zero_count));

	//	printf("%f\n", standard_deviation);

	float tau_test = standard_deviation;

	if (non_zero_count > 2)
		tau_test = standard_deviation * tau_table[non_zero_count];

	//	uint8_t removed_outliers = 0;

	uint32_t d1 = mean - *min;
	uint32_t d2 = *max - mean;

	if (d1 > d2) {
		if (d1 > tau_test) {
			*min = 0;
			return 1;
		}
	} else if (d2 > tau_test) {
		*max = 0;
		return 1;
	}

	return 0;
	/*
	  for (uint8_t i = 0; i < SENSORS_PER_OBJECT; i++)
	  {
	  uint16_t len = lcd->sweep.sweep_len[i];
	  if (len == 0) continue;

	  if ( abs(len-mean) > tau_test )
	  {
	  //			fprintf(stderr, "removing %d\n", len);
	  lcd->sweep.sweep_len[i] = 0;
	  removed_outliers = 1;
	  }
	  }
	*/
	//	return removed_outliers;
}

static void handle_lightcap2_process_sweep_data(SurviveObject *so) {
	lightcap2_data *lcd = so->disambiguator_data;

	while (remove_outliers(so))
		;

	// look at all of the sensors we found, and process the ones that were hit.
	// TODO: find the sensor(s) with the longest pulse length, and assume
	// those are the "highest quality".  Then, reject any pulses that are sufficiently
	// different from those values, assuming that they are reflections.
	{
		unsigned int longest_pulse = 0;
		unsigned int timestamp_of_longest_pulse = 0;
		(void)timestamp_of_longest_pulse;
		for (int i = 0; i < SENSORS_PER_OBJECT; i++) {
			if (lcd->sweep.sweep_len[i] > longest_pulse) {
				longest_pulse = lcd->sweep.sweep_len[i];
				timestamp_of_longest_pulse = lcd->sweep.sweep_time[i];
			}
		}

		int allZero = 1;
		for (int q = 0; q < 32; q++)
			if (lcd->sweep.sweep_len[q] != 0)
				allZero = 0;
		// if (!allZero)
		//	printf("a[%d]l[%d] ", lcd->per_sweep.activeAcode & 5,  lcd->per_sweep.activeLighthouse);
		for (int i = 0; i < SENSORS_PER_OBJECT; i++) {

			{
				static int counts[SENSORS_PER_OBJECT][2] = {0};
				(void)counts;

				//				if (lcd->per_sweep.activeLighthouse == 0 && !allZero)
				if (lcd->per_sweep.activeLighthouse > -1 && !allZero) {
					if (lcd->sweep.sweep_len[i] != 0) {
						// printf("%d  ", i);
						// counts[i][lcd->per_sweep.activeAcode & 1] ++;
					} else {
						counts[i][lcd->per_sweep.activeAcode & 1] = 0;
					}

					// if (counts[i][0] > 10 && counts[i][1] > 10)
					//{
					// printf("%d(%d,%d), ", i, counts[i][0], counts[i][1]);
					//}
				}
			}

			if (lcd->sweep.sweep_len[i] != 0) // if the sensor was hit, process it
			{
				// printf("%4d\n", lcd->sweep.sweep_len[i]);
				int offset_from =
					lcd->sweep.sweep_time[i] - lcd->per_sweep.activeSweepStartTime + lcd->sweep.sweep_len[i] / 2;

				// first, send out the sync pulse data for the last round (for OOTX decoding
				if (!lcd->global.sent_out_ootx_bits) {
					if (lcd->per_sweep.lh_max_pulse_length[0] != 0) {
						so->ctx->lightproc(
							so, -1, handle_lightcap2_getAcodeFromSyncPulse(so, lcd->per_sweep.lh_max_pulse_length[0]),
							lcd->per_sweep.lh_max_pulse_length[0], lcd->per_sweep.lh_start_time[0], 0, 0);
					}
					if (lcd->per_sweep.lh_max_pulse_length[1] != 0) {
						so->ctx->lightproc(
							so, -2, handle_lightcap2_getAcodeFromSyncPulse(so, lcd->per_sweep.lh_max_pulse_length[1]),
							lcd->per_sweep.lh_max_pulse_length[1], lcd->per_sweep.lh_start_time[1], 0, 1);
					}
					lcd->global.sent_out_ootx_bits = 1;
				}

				//				if (offset_from < 380000 && offset_from > 70000)
				{
					// if (longest_pulse *10 / 8 < lcd->sweep.sweep_len[i])
					{
						so->ctx->lightproc(so, i, lcd->per_sweep.activeAcode, offset_from, lcd->sweep.sweep_time[i],
										   lcd->sweep.sweep_len[i], lcd->per_sweep.activeLighthouse);
					}
				}
			}
		}
		// if (!allZero)
		//	printf("   ..:..\n");

		//		if (!allZero) printf("\n");
	}

	// clear out sweep data (could probably limit this to only after a "first" sync.
	// this is slightly more robust, so doing it here for now.
	memset(&(((lightcap2_data *)so->disambiguator_data)->sweep), 0, sizeof(lightcaps_sweep_data));
}

static void handle_lightcap2_sync(SurviveObject *so, LightcapElement *le) {
	// fprintf(stderr, "%6.6d %4.4d \n", le->timestamp - so->recent_sync_time, le->length);
	lightcap2_data *lcd = so->disambiguator_data;

	// static unsigned int recent_sync_time = 0;
	// static unsigned int recent_sync_count = -1;
	// static unsigned int activeSweepStartTime;
	int acode = handle_lightcap2_getAcodeFromSyncPulse(so, le->length); // acode for this sensor reading

	// Process any sweep data we have
	handle_lightcap2_process_sweep_data(so);

	int time_since_last_sync = (le->timestamp - lcd->per_sweep.recent_sync_time);

	//	fprintf(stderr, "            %2d %8d %d\n", le->sensor_id, time_since_last_sync, le->length);
	// need to store up sync pulses, so we can take the earliest starting time for all sensors.
	if (time_since_last_sync < 2400) {
		lcd->per_sweep.recent_sync_time = le->timestamp;
		// it's the same sync pulse;
		//		so->sync_set_number = 1;
		so->recent_sync_time = le->timestamp;

		//		lcd->per_sweep.lh_pulse_len[lcd->per_sweep.current_lh] = le->length;
		//		lcd->per_sweep.lh_start_time[lcd->per_sweep.current_lh] = le->timestamp;

		if (le->length > lcd->per_sweep.lh_max_pulse_length[lcd->per_sweep.current_lh]) {
			lcd->per_sweep.lh_max_pulse_length[lcd->per_sweep.current_lh] = le->length;
			lcd->per_sweep.lh_start_time[lcd->per_sweep.current_lh] = le->timestamp;
			lcd->per_sweep.lh_acode[lcd->per_sweep.current_lh] = acode;
		}

		/*
		//this stuff should probably be happening on the sweep so that we can filter out erroneous a codes
		if (!(acode >> 2 & 1)) // if the skip bit is not set
		{
		lcd->per_sweep.activeLighthouse = lcd->per_sweep.current_lh;
		lcd->per_sweep.activeSweepStartTime = le->timestamp;
		lcd->per_sweep.activeAcode = acode;
		}
		else
		{
		//this causes the master lighthouse to be ignored from the HMD
		lcd->per_sweep.activeLighthouse = -1;
		lcd->per_sweep.activeSweepStartTime = 0;
		lcd->per_sweep.activeAcode = 0;
		}
		*/
	} else if (time_since_last_sync < 24000) {
		lcd->per_sweep.activeLighthouse = -1;

		lcd->per_sweep.recent_sync_time = le->timestamp;
		// I do believe we are lighthouse B
		lcd->per_sweep.current_lh = 1;
		//		lcd->per_sweep.lh_pulse_len[lcd->per_sweep.current_lh] = le->length;
		lcd->per_sweep.lh_start_time[lcd->per_sweep.current_lh] = le->timestamp;
		lcd->per_sweep.lh_max_pulse_length[lcd->per_sweep.current_lh] = le->length;
		lcd->per_sweep.lh_acode[lcd->per_sweep.current_lh] = acode;

		/*
	  if (!(acode >> 2 & 1)) // if the skip bit is not set
	  {
	  if (lcd->per_sweep.activeLighthouse != -1)
	  {
	  static int pulseWarningCount=0;

	  if (pulseWarningCount < 5)
	  {
	  pulseWarningCount++;
	  // hmm, it appears we got two non-skip pulses at the same time.  That should never happen
	  fprintf(stderr, "WARNING: Two non-skip pulses received on the same cycle!\n");
	  }
	  }

	  lcd->per_sweep.activeLighthouse = 1;
	  lcd->per_sweep.activeSweepStartTime = le->timestamp;
	  lcd->per_sweep.activeAcode = acode;
	  }
		*/

	} else if (time_since_last_sync > 370000) {
		// XXX CAUTION: if we lose sight of a lighthouse then, the remaining lighthouse will default to master
		// this should probably be fixed. Maybe some kind of timing based guess at which lighthouse.

		// looks like this is the first sync pulse.  Cool!
		lcd->global.sent_out_ootx_bits = 0;

		// fprintf(stderr, "************************************ Reinitializing Disambiguator!!!\n");
		// initialize here.
		memset(&lcd->per_sweep, 0, sizeof(lcd->per_sweep));
		lcd->per_sweep.activeLighthouse = -1;

		for (uint8_t i = 0; i < NUM_LIGHTHOUSES; ++i) {
			lcd->per_sweep.lh_acode[i] = -1;
		}

		lcd->per_sweep.recent_sync_time = le->timestamp;
		// I do believe we are lighthouse A
		lcd->per_sweep.current_lh = 0;
		//		lcd->per_sweep.lh_pulse_len[lcd->per_sweep.current_lh] = le->length;
		lcd->per_sweep.lh_start_time[lcd->per_sweep.current_lh] = le->timestamp;
		lcd->per_sweep.lh_max_pulse_length[lcd->per_sweep.current_lh] = le->length;
		lcd->per_sweep.lh_acode[lcd->per_sweep.current_lh] = acode;

		//		int acode = handle_lightcap2_getAcodeFromSyncPulse(so, le->length);

		/*
	  if (!(acode >> 2 & 1)) // if the skip bit is not set
	  {
	  lcd->per_sweep.activeLighthouse = 0;
	  lcd->per_sweep.activeSweepStartTime = le->timestamp;
	  lcd->per_sweep.activeAcode = acode;
	  }
		*/
	}
	//	printf("%d %d\n", acode, lcd->per_sweep.activeLighthouse );
}

static void handle_lightcap2_sweep(SurviveObject *so, LightcapElement *le) {
	lightcap2_data *lcd = so->disambiguator_data;

	// If we see multiple "hits" on the sweep for a given sensor,
	// assume that the longest (i.e. strongest signal) is most likely
	// the non-reflected signal.

	// if (le->length < 80)
	//{
	//	// this is a low-quality read.  Better to throw it out than to use it.
	//	//fprintf(stderr, "%2d %d\n", le->sensor_id, le->length);
	//	return;
	//}
	// fprintf(stderr, "%2d %d\n", le->sensor_id, le->length);
	// fprintf(stderr, ".");

	lcd->per_sweep.activeLighthouse = -1;
	lcd->per_sweep.activeSweepStartTime = 0;
	lcd->per_sweep.activeAcode = 0;

	for (uint8_t i = 0; i < NUM_LIGHTHOUSES; ++i) {
		int acode = lcd->per_sweep.lh_acode[i];
		if ((acode >= 0) && !(acode >> 2 & 1)) {
			lcd->per_sweep.activeLighthouse = i;
			lcd->per_sweep.activeSweepStartTime = lcd->per_sweep.lh_start_time[i];
			lcd->per_sweep.activeAcode = acode;
		}
	}

	if (lcd->per_sweep.activeLighthouse < 0) {
		// fprintf(stderr, "WARNING: No active lighthouse!\n");
		// fprintf(stderr, "            %2d %8d %d %d\n", le->sensor_id,
		// le->length,lcd->per_sweep.lh_acode[0],lcd->per_sweep.lh_acode[1]);
		return;
	}

	if (lcd->sweep.sweep_len[le->sensor_id] < le->length) {
		lcd->sweep.sweep_len[le->sensor_id] = le->length;
		lcd->sweep.sweep_time[le->sensor_id] = le->timestamp;
	}
}

void DisambiguatorTurvey(SurviveObject *so, LightcapElement *le) {
	SurviveContext *ctx = so->ctx;

	if (so->disambiguator_data == NULL) {
		fprintf(stderr, "Initializing Disambiguator Data\n");
		so->disambiguator_data = malloc(sizeof(lightcap2_data));
		memset(so->disambiguator_data, 0, sizeof(lightcap2_data));
	}

	if (le->sensor_id > SENSORS_PER_OBJECT) {
		return;
	}

	if (le->length > 6750) {
		// Should never get a reading so high.  Odd.
		return;
	}
	//	if (le->length >= 2750)
	if (le->length >= 2500) {
		// Looks like a sync pulse, process it!
		handle_lightcap2_sync(so, le);
		return;
	}

	// must be a sweep pulse, process it!
	handle_lightcap2_sweep(so, le);
}

REGISTER_LINKTIME(DisambiguatorTurvey);