import otherSupport.SettingsManager;
import comedi.ComediOptions;
import comediJNI.Comedi;
import comediJNI.ComediDef;
import comediJNI.ComediJNIException;

/*
 * NI general-purpose counter example.  Configures the counter to
 * produce a continuous pulse train.  The argument specifies the
 * number of nanoseconds the output pulse should remain high.
 * The example assumes the board has already been configured to
 * route the output signal of the counter to an appropriate
 * location (you may need to route it to a PFI output line
 * for example).  The choose_routing demo may be helpful to
 * set the routing of the counter's output signal.
 * Part of Comedilib
 *
 * Copyright (c) 2007 Frank Mori Hess <fmhess@users.sourceforge.net>
 *
 * This file may be freely modified, distributed, and combined with
 * other software, as long as proper attribution is given in the
 * source code.
 */
/*
 * Requirements:  A board with a National Instruments general-purpose
 * counter, and comedi driver version 0.7.74 or newer.
 */

public class gpct_pulse_generator {
	static { new SettingsManager("minerva", true); 	}

	private static int ni_gpct_start_pulse_generator(long dev, int subdevice, int period_ns, int up_time_ns) {
		
		int ret;
		int counter_mode;
		final int clock_period_ns = 50;	/* 20MHz clock */
		int up_ticks, down_ticks;

		ret = Comedi.reset(dev, subdevice);
		if(ret < 0)
			throw new ComediJNIException("reset");

		ret = Comedi.set_gate_source(dev, subdevice, 0, 0, ComediDef.NI_GPCT_DISABLED_GATE_SELECT | ComediDef.CR_EDGE);
		if(ret < 0)
			throw new ComediJNIException("set_gate_source");

		ret = Comedi.set_gate_source(dev, subdevice, 0, 1, ComediDef.NI_GPCT_DISABLED_GATE_SELECT | ComediDef.CR_EDGE);
		if(ret < 0) {
			System.err.println("Failed to set second gate source.  This is expected for older boards " +
								"(e-series, etc.) that don't have a second gate.\n");
		}

		counter_mode = ComediDef.NI_GPCT_COUNTING_MODE_NORMAL_BITS;
		// toggle output on terminal count
		counter_mode |= ComediDef.NI_GPCT_OUTPUT_TC_TOGGLE_BITS;
		// load on terminal count
		counter_mode |= ComediDef.NI_GPCT_LOADING_ON_TC_BIT;
		// alternate the reload source between the load a and load b registers
		counter_mode |= ComediDef.NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS;
		// count down
		counter_mode |= ComediDef.NI_GPCT_COUNTING_DIRECTION_DOWN_BITS;
		// initialize load source as load b register
		counter_mode |= ComediDef.NI_GPCT_LOAD_B_SELECT_BIT;
		// don't stop on terminal count
		counter_mode |= ComediDef.NI_GPCT_STOP_ON_GATE_BITS;
		// don't disarm on terminal count or gate signal
		counter_mode |= ComediDef.NI_GPCT_NO_HARDWARE_DISARM_BITS;
		
		ret = Comedi.set_counter_mode(dev, subdevice, 0, counter_mode);
		if(ret < 0)
			throw new ComediJNIException("set_counter_mode");

		/* 20MHz clock */
		ret = Comedi.set_clock_source(dev, subdevice, 0, ComediDef.NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS, clock_period_ns);
		if(ret < 0)
			throw new ComediJNIException("set_clock_source");

		up_ticks = (up_time_ns + clock_period_ns / 2) / clock_period_ns;
		down_ticks = (period_ns + clock_period_ns / 2) / clock_period_ns - up_ticks;
		
		/* set initial counter value by writing to channel 0 */
		ret = Comedi.data_write(dev, subdevice, 0, 0, 0, down_ticks);
		if(ret < 0)
			throw new ComediJNIException("data_write");

		/* set "load a" register to the number of clock ticks the counter output should remain low
		by writing to channel 1. */
		Comedi.data_write(dev, subdevice, 1, 0, 0, down_ticks);
		if(ret < 0)
			throw new ComediJNIException("data_write");

		/* set "load b" register to the number of clock ticks the counter output should remain high
		by writing to channel 2 */
		Comedi.data_write(dev, subdevice, 2, 0, 0, up_ticks);
		if(ret < 0)
			throw new ComediJNIException("data_write");

		ret = Comedi.arm(dev, subdevice, ComediDef.NI_GPCT_ARM_IMMEDIATE);
		if(ret < 0)
			throw new ComediJNIException("arm");

		return 0;
	}

	
	public static void main(String[] args) {
		
		long dev;
		int up_time;
		int period_ns;
		int retval;
		int subdevice_type;
		
		ComediOptions options = new ComediOptions();
		//init_parsed_options(&options);
		
		options.fileName = "/dev/comedi0_subd11";
		options.subdevice = 11;
		options.n_chan = 1;
		
		options.channel = 0;
		options.freq = 1000;
		options.value = -0.5;
		//parse_options(&options, argc, argv);
		
		period_ns = (int)(1e9 / options.freq + 0.5);
		if(options.value < 0.0) {
			double duty;
			if(-options.value < 1.0)
				duty = -options.value;
			else
				duty = 1.0 / -options.value;
			up_time = (int)(duty * period_ns + 0.5);
		}else{
			up_time = (int)(options.value + 0.5);
		}
		
		dev = Comedi.open(options.fileName);
		if(dev == 0)
			throw new ComediJNIException("comedi_open");

		subdevice_type = Comedi.get_subdevice_type(dev, options.subdevice);
		if(subdevice_type < 0)
			throw new ComediJNIException("comedi_get_subdevice_type");
		
		
		if(subdevice_type != ComediDef.COMEDI_SUBD_COUNTER)
			throw new RuntimeException("Subdevice is not a counter (type "+ComediDef.COMEDI_SUBD_COUNTER+
											"), but of type "+subdevice_type+".");	
			
		System.out.println("Generating pulse train on subdevice " + options.subdevice + ".");
		System.out.println("Period = "+period_ns+" ns.");
		System.out.println("Up Time = "+up_time+" ns.");
		System.out.println("Down Time = "+(period_ns - up_time)+" ns.");

		retval = ni_gpct_start_pulse_generator(dev, options.subdevice, period_ns, up_time);
		
	}

}
