QFFCSMSDEvaluationGetNFromFits::~QFFCSMSDEvaluationGetNFromFits()
{
    ProgramOptions::setConfigValue("QFFCSMSDEvaluationGetNFromFits/applyto", getApplyTo());
    ProgramOptions::setConfigValue("QFFCSMSDEvaluationGetNFromFits/getN", getN());
    ProgramOptions::setConfigValue("QFFCSMSDEvaluationGetNFromFits/getTriplet", getTriplet());
    ProgramOptions::setConfigValue("QFFCSMSDEvaluationGetNFromFits/chkGetFocus", getFocusParams());
    delete ui;
}
void Novatech409B::parseDeviceEvents(const RawEventMap& eventsIn, 
        SynchronousEventVector& eventsOut) throw(std::exception)
{

	//Make sure the current state of all channels is up to date.
	refreshLocallyStoredFrequencyChannels();

	cerr << endl << endl << "Table: " << endl;

	RawEventMap::const_iterator events;
	RawEventMap::const_iterator lastTableEvents;

	double holdoff = 0;
	double minimumEventSpacing = 0;
	double minimumAbsoluteStartTime = 0;
	double tableStartHoldoff_ns = 1000 * 1000;	//1 ms

	bool goodFormat = true;
	bool holdEventFound = false;
	bool channelZeroOrOne = false;

	FrequencyChannel currentTriplet;

	FrequencyChannel currentTriplet0 = frequencyChannels.at(0);
	FrequencyChannel currentTriplet1 = frequencyChannels.at(1);

	unsigned tableAddress = 0;
	double tableDwellTime_ns = 0;	//in ns
	double lastTableDwellTime_ns = 0;	//in ns

	std::string errorMessage;

	double eventTime;
	double previousTime = minimumAbsoluteStartTime;
	
	for(events = eventsIn.begin(); events != eventsIn.end(); events++)
	{
		eventTime = events->first - holdoff;
		
		if( (events->first - minimumEventSpacing) < previousTime)
		{
			if(events != eventsIn.begin())
				throw EventParsingException(events->second.at(0),
						"The Novatech needs " + 
						valueToString(minimumEventSpacing) + 
						" ns between events.");
			else
				throw EventParsingException(events->second.at(0),
						"The Novatech needs " + 
						valueToString(minimumAbsoluteStartTime) + 
						" ns at the beginning of the timing file.");
		}

		//Verify format. Parameter convention:  (freq, amplitude, phase, [Hold])
		for(unsigned i = 0; i < events->second.size(); i++)
		{
			goodFormat = events->second.at(i).value().getVector().size() == 3 || 
				events->second.at(i).value().getVector().size() == 4;

			for(unsigned k = 0; k < 3; k++)
			{
				goodFormat &= events->second.at(i).value().getVector().at(k).getType() == MixedValue::Double;
			}

			if(!goodFormat) {
				throw EventParsingException(events->second.at(i),
					"Bad format. Novatech commands must be of the form (frequency, amplitude, phase, [Hold]).");
			}

			currentTriplet.frequency = events->second.at(i).value().getVector().at(0).getDouble();
			currentTriplet.amplitude = events->second.at(i).value().getVector().at(1).getDouble();
			currentTriplet.phase     = events->second.at(i).value().getVector().at(2).getDouble();

			//Check parameter range limits
			if( !checkRanges(currentTriplet, errorMessage) ) {
				throw EventParsingException(events->second.at(i),
					"Out of range. Allowed ranges are (0-171.1276031) MHz; (0-100) percent; (0-360) degrees.");
			}
		}

		//Check for trigger hold command
		if(	isHoldEvent(events) ) {
			holdEventFound = true;	//always True after 1st hold event is found (switches to Table Mode).
		}

		//As long as their is no "Hold" event found, assume the events are soft timing and use PsuedoSynchronousEvents
		if(!holdEventFound) {
			eventsOut.push_back( 
				new STI_Device::PsuedoSynchronousEvent(events->first, events->second, this) );

			//Update channel 0 and 1 triplets to most recent event. This allows for a dummy event at the start
			//of table mode. The dummy event will be whatever value was last played on the channel.
			for(unsigned j = 0; j < events->second.size(); j++) {
				if(events->second.at(j).channel() == 0) {
					getTriplet(events->second.at(j).value(), currentTriplet0);
				}
				if(events->second.at(j).channel() == 1) {
					getTriplet(events->second.at(j).value(), currentTriplet1);
				}
			}
			
			//Register any measurements
			for(unsigned j = 0; j < events->second.size(); j++)
			{
				if( events->second.at(j).isMeasurementEvent() ) {
					eventsOut.back().addMeasurement( events->second.at(j) );
				}
			}
		}
		else {
			//A Hold event was found. Run the rest of the events in Table Mode.

			///////  Table Mode   ///////

			//Only channels 0 and 1 can be set using table mode. Check for violations.
			channelZeroOrOne = true;
			for(unsigned j = 0; j < events->second.size(); j++)
			{
				channelZeroOrOne = (events->second.at(j).channel() == 0 || events->second.at(j).channel() == 1);
				
				if( !channelZeroOrOne) {
					throw EventParsingException(events->second.at(j),
						std::string("Only Channels 0 and 1 can be changed after a 'Hold' command is given\n") +
							"('Table Mode' only supports setting channels 0 and 1).");
				}
			}

			//Now we can assume that the 'events' vector only contains events for channels 0 and/or 1.
			
			tableDwellTime_ns = eventTime - previousTime;
			
			if(tableAddress > 0) {
				// New table point; add the previous point

				if( tableDwellTime_ns > 25400000 ) {
					throw EventParsingException(lastTableEvents->second.at(0),
						std::string("Dwell time overflow.\n Maximum dwell time between events in a triggered table\n is 25.4 ms.\n")
						+ "The computed dwell time was " + STI::Utils::valueToString(tableDwellTime_ns / 1000000) + " ms.");
				}

				//Get triplets for this event. 
				//Possibly only one channel will be explicitly set. Make a dummy event for the other
				//because table commands must be made for both channels.
				for(unsigned j = 0; j < lastTableEvents->second.size(); j++) {
					if(lastTableEvents->second.at(j).channel() == 0) {
						getTriplet(lastTableEvents->second.at(j).value(), currentTriplet0);
					}
					if(lastTableEvents->second.at(j).channel() == 1) {
						getTriplet(lastTableEvents->second.at(j).value(), currentTriplet1);
					}
				}

//				bool temp = isHoldEvent(lastTableEvents);
				addTablePoint(currentTriplet0, currentTriplet1, tableAddress, tableDwellTime_ns, 
					 isHoldEvent(lastTableEvents));
				//When tableAddress==1 we ignore the explicit Hold event, since the dummy event at the
				//0th table entry already has the hold.  tableAddress != 1 &&
			}
			else {
				//first table point
				preTableCommands();

				//currentTriplet0;
				//First table point is a "dummy" point that maintains the current state. It asserts ff for the hold
				//so that the next point in the table will be reached after the trigger.
//				addTablePoint(currentTriplet0, currentTriplet1, 0, tableDwellTime_ns, true);

				eventsOut.push_back( new NovatechTableEvent(events->first - tableStartHoldoff_ns, this) );
			}

			lastTableDwellTime_ns = tableDwellTime_ns;
			lastTableEvents = events;
			tableAddress += 1;
		}
		previousTime = eventTime;
	}

	if(tableAddress > 0) {
		//Add final table point with dwell time of 0 us

		for(unsigned j = 0; j < lastTableEvents->second.size(); j++) {
			if(lastTableEvents->second.at(j).channel() == 0) {
				getTriplet(lastTableEvents->second.at(j).value(), currentTriplet0);
			}
			if(lastTableEvents->second.at(j).channel() == 1) {
				getTriplet(lastTableEvents->second.at(j).value(), currentTriplet1);
			}
		}
		
		addTablePoint(currentTriplet0, currentTriplet1, tableAddress, 0, isHoldEvent(lastTableEvents));

		addTablePoint(currentTriplet0, currentTriplet1, tableAddress, 0, false);	//end table with dwell time 00.

		postTableCommands();
	}

	
}