#include <stdio.h>
#include <unistd.h>
#include <assert.h>

#include "../hubolib.h"

using namespace HuboLib;

/*
Compile and link: 
	g++ AnalogRawInput.cpp -L../ -lhubo -lpthread -lrt -o AnalogRawInput.out
Run:
	sudo ./AnalogRawInput.out
Purpose:
	Sometimes it is necessary to follow an analog signal once it reaches a certain threshold. Since the Linux scheduler 
	cannot work efficient at higher rates than 1ms further decreasing of the cycle time would not help. Too much
	time would be wasted due to thread context changes...
	However, from within any of the callbacks provided by the library it is save to call Get_AI_Channel_Raw() in order
	to low level access the MCP3208/MCP3008 chips. You can for example poll the converters in a loop in order to 
	achieve a higher resolution or save values in an array for later evaluation (e.g. by the main thread).
	Make sure to not use Get_AI_Channel_Raw() from any other thread but the libraries background thread! 
	Maximum frequencies that can be achieved this way are around 8kHz. However, note that nearly no other application
	will run during the "blocking" of a callback function due to the high priority it runs at!
*/

void 			CycleTickCallback 	(unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues);
double 			DigitToVolt			(unsigned long digit);

int main(void)
{
	// Initialize the library once in your program.
	if (!Initialize())
	{
		printf ("Error: Initialize\n");
		return 1;
	}
	
	// Note - in order to access raw AD valued you do not need to setup oversampling!
	// This avoids overhead if for example just the voltage of a power supply should get monitored in the background!

	// Let's sample every 1s (1Hz).
	Set_Cycle_Time (1000);

	// Register the callback.
	Register_CycleTickCallback (CycleTickCallback);

	// Let the main thread run a while... in between use the cycle tick callback for latching analog values.
	for (int i=0; i<10; i++)
	{
		printf ("Main thread waiting...\n");
		usleep(1000000);
	}

	// Once not needed anymore the callbacks can be unregistered.
	Unregister_CycleTickCallback (CycleTickCallback);
	
	// Free library resources.
	Uninitialize();
	
	return 0;
}

void CycleTickCallback (unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues)
{
	// Usually we'd avoid lengthily operations in callback routines as this would lead the cycle tick to get broken.
	// However, this time we wait for an analog signal to be within the range of 1.4V and 1.6V. If we detect such a signal
	// We latch 1000 values as fast as we can and compute the average of them.
	
	printf ("CycleTickCallback called.\n");
	
	// Note that we do not need to have valid values in pADChannelValues nor do we need to access it (...while we could of cause...).
	unsigned short value = 0;
	if (!Get_AI_Channel_Raw (0, value))
	{
		printf ("Failed to retrieve analog value from MCP3x08.\n");
		assert (false);
		return;
	}

	double currentVolt = DigitToVolt(value);

	printf ("Rough value   = %lf\n", currentVolt);
	
	// Within the range of 1.4V to 1.6V we retrieve raw values as fast as we can.
	// Maybe because we need to follow a signal faster than our usual cycle time...
	if (currentVolt < 1.4 || currentVolt > 1.6)
		return;

	// In order to get here connect a 1.5V battery to analog input 0.
	
	unsigned long currentValue 	= 0;
	int 		  channel 		= 0;

	unsigned long long startTime = GetTime_MicroSeconds();

	// Latch 1000 samples.
	for (int i=0; i<1000; i++)
	{
		if (Get_AI_Channel_Raw (0, value))
			currentValue += value;
		else
		{
			printf ("Failed to retrieve analog value from MCP3x08.\n");
			assert (false);
			return;
		}
	}
	unsigned long long stopTime = GetTime_MicroSeconds();
		
	printf ("Precise value = %lf duration = %lfms (at 1000 samples).\n", DigitToVolt(currentValue)/1000.0, (stopTime-startTime)/1000.0);
	
	// Output of a 1.5V battery that has been observed.
	// Rough value   = 1.502805
	// Precise value = 1.502918 duration = 119.265000ms (at 1000 samples).
}

// Helper: Convert the digits into a voltage.
double DigitToVolt(unsigned long digit)
{
	// We have a resolution of 4096 digit for the MCP3208, 1024 digit for the MCP3008.
	return (((double)digit)*Get_VRef()/4096.0);
}

