/**
 * Print formatted text to the Driver Station LCD text bufer.
 * 
 * Use UpdateLCD() periodically to actually send the test to the Driver Station.
 * 
 * @param line The line on the LCD to print to.
 * @param startingColumn The column to start printing to.  This is a 1-based number.
 * @param writeFmt The printf format string describing how to print.
 */
void DriverStationLCD::Printf(Line line, INT32 startingColumn, const char *writeFmt, ...)
{
	va_list args;
	UINT32 start = startingColumn - 1;
	INT32 maxLength = kLineLength - start;
	char lineBuffer[kLineLength + 1];

	if (startingColumn < 1 || startingColumn > kLineLength)
	{
		wpi_fatal(ParameterOutOfRange);
		return;
	}

	if (line < kMain_Line6 || line > kUser_Line6)
	{
		wpi_fatal(ParameterOutOfRange);
		return;
	}

	va_start (args, writeFmt);
	{
		Synchronized sync(m_textBufferSemaphore);
		// snprintf appends NULL to its output.  Therefore we can't write directly to the buffer.
		INT32 length = vsnprintf(lineBuffer, kLineLength + 1, writeFmt, args);
		if (length < 0) length = kLineLength;

		memcpy(m_textBuffer + start + line * kLineLength + sizeof(UINT16), lineBuffer, std::min(maxLength,length));
	}

	va_end (args);
}
Esempio n. 2
0
/**
 * Indicate that the packing is complete and commit the buffer to the DriverStation.
 * 
 * The packing of the dashboard packet is complete.
 * If you are not using the packed dashboard data, you can call Finalize() to commit the Printf() buffer and the error string buffer.
 * In effect, you are packing an empty structure.
 * Prepares a packet to go to the dashboard...
 * Pack the sequence number, Printf() buffer, the errors messages (not implemented yet), and packed dashboard data buffer.
 * @return The total size of the data packed into the userData field of the status packet.
 */
INT32 Dashboard::Finalize(void)
{
	if (*m_userStatus == NULL)
	{
		wpi_fatal(NullParameter);
		return 0;
	}
	if (!m_complexTypeStack.empty())
	{
		wpi_fatal(MismatchedComplexTypeClose);
		return 0;
	}

	INT32 size = 0;

	// Sequence number
	memcpy(*m_userStatus + size, &m_sequence, sizeof(m_sequence));
	size += sizeof(m_sequence);
	m_sequence++;

	// User printed strings
	INT32 printSize;
	{
		Synchronized sync(m_printSemaphore);
		printSize = strlen(m_localPrintBuffer);
		memcpy(*m_userStatus + size, &printSize, sizeof(printSize));
		size += sizeof(printSize);
		memcpy(*m_userStatus + size, m_localPrintBuffer, printSize);
		size += printSize;
		m_localPrintBuffer[0] = 0;
	}

	// Error Strings
	INT32 errorSize = 0;
	if (printSize + errorSize > kMaxDashboardDataSize)
	{
		wpi_fatal(DashboardDataOverflow);
		return 0;
	}
	memcpy(*m_userStatus + size, &errorSize, sizeof(errorSize));
	size += sizeof(errorSize);
	///< TODO: add error reporting strings.
	size += errorSize;

	// Dashboard Data
	INT32 dataSize = m_packPtr - m_localBuffer;
	if (printSize + errorSize + dataSize > kMaxDashboardDataSize)
	{
		wpi_fatal(DashboardDataOverflow);
		return 0;
	}
	memcpy(*m_userStatus + size, &dataSize, sizeof(dataSize));
	size += sizeof(dataSize);
	memcpy(*m_userStatus + size, m_localBuffer, dataSize);
	size += dataSize;
	m_packPtr = m_localBuffer;

	return size;
}
Esempio n. 3
0
/**
 * Free an allocated resource.
 * After a resource is no longer needed, for example a destructor is called for a channel assignment
 * class, Free will release the resource value so it can be reused somewhere else in the program.
 */
void Resource::Free(UINT32 index)
{
	if (index >= m_size)
	{
		wpi_fatal(IndexOutOfRange);
		return;
	}
	if ( ! m_isAllocated[index] )
	{
		wpi_fatal(NotAllocated);
		return;
	}
	m_isAllocated[index] = false;
}
Esempio n. 4
0
/**
 * Allocate a specific resource value.
 * The user requests a specific resource value, i.e. channel number and it is verified
 * unallocated, then returned.
 */
UINT32 Resource::Allocate(UINT32 index)
{
	if (index >= m_size)
	{
		wpi_fatal(IndexOutOfRange);
		return 0;
	}
	if ( m_isAllocated[index] )
	{
		wpi_fatal(ResourceAlreadyAllocated);
		return 0;
	}
	m_isAllocated[index] = true;
	return index;
}
Esempio n. 5
0
/**
 * Validate that the data being packed will fit in the buffer.
 */
bool Dashboard::ValidateAdd(INT32 size)
{
	if ((m_packPtr - m_localBuffer) + size > kMaxDashboardDataSize)
	{
		wpi_fatal(DashboardDataOverflow);
		return false;
	}
	// Make sure printf is not being used at the same time.
	if (m_localPrintBuffer[0] != 0)
	{
		wpi_fatal(DashboardDataCollision);
		return false;
	}
	return true;
}
Esempio n. 6
0
/**
 * Verify that the solenoid channel number is within limits.
 */
bool SensorBase::CheckSolenoidChannel(UINT32 channel)
{
    if (channel > 0 && channel <= kSolenoidChannels)
        return true;
    wpi_fatal(IndexOutOfRange);
    return false;
}
Esempio n. 7
0
/**
 * Create a new instance of an digital module.
 * Create an instance of the digital module object. Initialize all the parameters
 * to reasonable values on start.
 * Setting a global value on an digital module can be done only once unless subsequent
 * values are set the previously set value.
 * Digital modules are a singleton, so the constructor is never called outside of this class.
 */
DigitalModule::DigitalModule(UINT32 slot)
	: Module(slot)
	, m_fpgaDIO (NULL)
{
	Resource::CreateResourceObject(&DIOChannels, tDIO::kNumSystems * kDigitalChannels);
	m_fpgaDIO = new tDIO(SlotToIndex(m_slot), &status);

	// Make sure that the 9403 IONode has had a chance to initialize before continuing.
	while(m_fpgaDIO->readLoopTiming(&status) == 0) taskDelay(1);
	if (m_fpgaDIO->readLoopTiming(&status) != kExpectedLoopTiming)
	{
		wpi_fatal(LoopTimingError);
		printf("DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&status), kExpectedLoopTiming);
	}
	m_fpgaDIO->writePWMConfig_Period(PWM::kDefaultPwmPeriod, &status);
	m_fpgaDIO->writePWMConfig_MinHigh(PWM::kDefaultMinPwmHigh, &status);

	// Ensure that PWM output values are set to OFF
	for (UINT32 pwm_index = 1; pwm_index <= kPwmChannels; pwm_index++)
	{
		SetPWM(pwm_index, PWM::kPwmDisabled);
		SetPWMPeriodScale(pwm_index, 3); // Set all to 4x by default.
	}

	// Turn off all relay outputs.
	m_fpgaDIO->writeSlowValue_RelayFwd(0, &status);
	m_fpgaDIO->writeSlowValue_RelayRev(0, &status);

	// Create a semaphore to protect changes to the relay values
	m_relaySemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);

	AddToSingletonList();
}
Esempio n. 8
0
/**
 * Verify that the solenoid module is correct.
 * Verify that the solenoid module is slot 8 (for now).
 */
bool SensorBase::CheckSolenoidModule(UINT32 slot)
{
    if (slot <= kChassisSlots && modulePopulation[slot] == 9472)
        return true;
    wpi_fatal(IndexOutOfRange);
    return false;
}
/**
 * Print formatted text to the Driver Station LCD text bufer. This function 
 * pads the line with empty spaces. 
 * 
 * Use UpdateLCD() periodically to actually send the test to the Driver Station.
 * 
 * @param line The line on the LCD to print to.
 * @param writeFmt The printf format string describing how to print.
 */
void DriverStationLCD::PrintfLine(Line line, const char *writeFmt, ...)
{
	va_list args;
	char lineBuffer[kLineLength + 1];

	if (line < kMain_Line6 || line > kUser_Line6)
	{
		wpi_fatal(ParameterOutOfRange);
		return;
	}

	va_start (args, writeFmt);
	{
		Synchronized sync(m_textBufferSemaphore);
		// snprintf appends NULL to its output.  Therefore we can't write directly to the buffer.
		INT32 length = std::min(vsnprintf(lineBuffer, kLineLength + 1, writeFmt, args), kLineLength);
		if (length < 0) length = kLineLength;

		// Fill the rest of the buffer
		if (length < kLineLength)
		{
			memset(lineBuffer + length, ' ', kLineLength - length);
		}
		
		memcpy(m_textBuffer + line * kLineLength + sizeof(UINT16), lineBuffer, kLineLength);
	}

	va_end (args);
}
Esempio n. 10
0
/**
 * Set the sample rate on the module.
 * 
 * This is a global setting for the module and effects all channels.
 * 
 * @param samplesPerSecond The number of samples per channel per second.
 */
void AnalogModule::SetSampleRate(float samplesPerSecond)
{
	// TODO: This will change when variable size scan lists are implemented.
	// TODO: Need float comparison with epsilon.
	//wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond);
	m_sampleRateSet = true;

	// Compute the convert rate
	UINT32 ticksPerSample = (UINT32)((float)kTimebase / samplesPerSecond);
	UINT32 ticksPerConversion = ticksPerSample / GetNumChannelsToActivate();
	// ticksPerConversion must be at least 80
	if (ticksPerConversion < 80)
	{
		wpi_fatal(SampleRateTooHigh);
		ticksPerConversion = 80;
	}

	// Atomically set the scan size and the convert rate so that the sample rate is constant
	tAI::tConfig config;
	config.ScanSize = GetNumChannelsToActivate();
	config.ConvertRate = ticksPerConversion;
	m_module->writeConfig(config, &status);

	// Indicate that the scan size has been commited to hardware.
	SetNumChannelsToActivate(0);

	wpi_assertCleanStatus(status);
}
Esempio n. 11
0
/**
 * Stop the compressor.
 * Stops the polling loop that operates the compressor. At this time the compressor will stop operating.
 */
void StopCompressor()
{
	if (compressor == NULL)
	{
		wpi_fatal(CompressorUndefined);
		return;
	}
	compressor->Stop();
}
Esempio n. 12
0
/**
 * Validate that the data being packed will fit in the buffer.
 */
bool Dashboard::ValidateAdd(INT32 size)
{
	if ((m_packPtr - m_localBuffer) + size > kMaxDashboardDataSize)
	{
		wpi_fatal(DashboardDataOverflow);
		return false;
	}
	return true;
}
Esempio n. 13
0
/**
 * Get the state of the enabled flag.
 * Return the state of the enabled flag for the compressor and pressure switch.
 *
 * @return The state of the compressor task's enable flag.
 */
bool CompressorEnabled()
{
	if (compressor == NULL)
	{
		wpi_fatal(CompressorUndefined);
		return false;
	}
	return compressor->Enabled();
}
Esempio n. 14
0
/**
 * Allocate resources for a compressor/pressure switch pair
 * Allocate the underlying object for the compressor.
 * @param pressureSwitchChannel The channel on the default digital module for the pressure switch
 * @param relayChannel The channel on the default digital module for the relay that controls the compressor
 */
void CreateCompressor(UINT32 pressureSwitchChannel, UINT32 relayChannel)
{
	if (compressor == NULL)
	{
		compressor = new Compressor(pressureSwitchChannel, relayChannel);
		return;
	}
	wpi_fatal(CompressorAlreadyDefined);
}
Esempio n. 15
0
/*
 * Invert a motor direction.
 * This is used when a motor should run in the opposite direction as the drive
 * code would normally run it. Motors that are direct drive would be inverted, the
 * Drive code assumes that the motors are geared with one reversal.
 * @param motor The motor index to invert.
 * @param isInverted True if the motor should be inverted when operated.
 */
void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted)
{
	if (motor < 0 || motor > 3)
	{
		wpi_fatal(InvalidMotorIndex);
		return;
	}
	m_invertedMotors[motor] = isInverted ? -1 : 1;
}
Esempio n. 16
0
/**
 * Provide tank steering using the stored robot configuration.
 * Drive the robot using two joystick inputs. The Y-axis will be selected from
 * each Joystick object.
 * @param leftStick The joystick to control the left side of the robot.
 * @param rightStick The joystick to control the right side of the robot.
 */
void RobotDrive::TankDrive(GenericHID *leftStick, GenericHID *rightStick)
{
	if (leftStick == NULL || rightStick == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	TankDrive(leftStick->GetY(), rightStick->GetY());
}
Esempio n. 17
0
/**
 * Convert a voltage to a raw value for a specified channel.
 * 
 * This process depends on the calibration of each channel, so the channel
 * must be specified.
 * 
 * @todo This assumes raw values.  Oversampling not supported as is.
 * 
 * @param channel The channel to convert for.
 * @param voltage The voltage to convert.
 * @return The raw value for the channel.
 */
INT32 AnalogModule::VoltsToValue(INT32 channel, float voltage)
{
	if (voltage > 10.0)
	{
		voltage = 10.0;
		wpi_fatal(VoltageOutOfRange);
	}
	if (voltage < -10.0)
	{
		voltage = -10.0;
		wpi_fatal(VoltageOutOfRange);
	}
	UINT32 LSBWeight = GetLSBWeight(channel);
	INT32 offset = GetOffset(channel);
	INT32 value = (INT32) ((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9));
	wpi_assertCleanStatus(status);
	return value;
}
Esempio n. 18
0
/**
 * DriverStation contructor.
 *
 * This is only called once the first time GetInstance() is called
 */
DriverStation::DriverStation()
 : m_controlData(NULL)
 , m_userControl(NULL)
 , m_userStatus(NULL)
 , m_digitalOut(0)
 , m_batteryChannel(NULL)
 , m_task("DriverStation",(FUNCPTR) DriverStation::InitTask)
 , m_dashboard(&m_userStatus)
{
    m_controlData = new FRCControlData;
    m_userControl = new char[USER_CONTROL_DATA_SIZE];
    m_userStatus = new char[USER_STATUS_DATA_SIZE];
    bzero(m_userStatus, USER_STATUS_DATA_SIZE);

    // initialize packet number and control words to zero;
    m_controlData->packetIndex = 0;
    m_controlData->control = 0;

    // set all joystick axis values to neutral; buttons to OFF
    m_controlData->stick0Axis1 = m_controlData->stick0Axis2 =
        m_controlData->stick0Axis3 = 0;
    m_controlData->stick1Axis1 = m_controlData->stick1Axis2 =
        m_controlData->stick1Axis3 = 0;
    m_controlData->stick2Axis1 = m_controlData->stick2Axis2 =
        m_controlData->stick2Axis3 = 0;
    m_controlData->stick3Axis1 = m_controlData->stick3Axis2 =
        m_controlData->stick3Axis3 = 0;
    m_controlData->stick0Axis4 = m_controlData->stick0Axis5 =
        m_controlData->stick0Axis6 = 0;
    m_controlData->stick1Axis4 = m_controlData->stick1Axis5 =
        m_controlData->stick1Axis6 = 0;
    m_controlData->stick2Axis4 = m_controlData->stick2Axis5 =
        m_controlData->stick2Axis6 = 0;
    m_controlData->stick3Axis4 = m_controlData->stick3Axis5 =
        m_controlData->stick3Axis6 = 0;
    m_controlData->stick0Buttons = 0;
    m_controlData->stick1Buttons = 0;
    m_controlData->stick2Buttons = 0;
    m_controlData->stick3Buttons = 0;

    // initialize the analog and digital data.
    m_controlData->analog1 = 0;
    m_controlData->analog2 = 0;
    m_controlData->analog3 = 0;
    m_controlData->analog4 = 0;
    m_controlData->dsDigitalIn = 0;

    m_batteryChannel = new AnalogChannel(kBatterySlot, kBatteryChannel);

    AddToSingletonList();

    if (!m_task.Start((INT32) this))
    {
        wpi_fatal(DriverStationTaskError);
    }
}
Esempio n. 19
0
/**
 * Set the accumulator's deadband.
 */
void AnalogChannel::SetAccumulatorDeadband(INT32 deadband)
{
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	m_accumulator->writeDeadband(deadband, &status);
	wpi_assertCleanStatus(status);
}
Esempio n. 20
0
/**
 * Resets the accumulator to the initial value.
 */
void AnalogChannel::ResetAccumulator()
{
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	m_accumulator->strobeReset(&status);
	wpi_assertCleanStatus(status);
}
Esempio n. 21
0
/**
 * Provide tank steering using the stored robot configuration.
 * This function lets you pick the axis to be used on each Joystick object for the left
 * and right sides of the robot.
 * @param leftStick The Joystick object to use for the left side of the robot.
 * @param leftAxis The axis to select on the left side Joystick object.
 * @param rightStick The Joystick object to use for the right side of the robot.
 * @param rightAxis The axis to select on the right side Joystick object.
 */
void RobotDrive::TankDrive(GenericHID *leftStick, UINT32 leftAxis,
		GenericHID *rightStick, UINT32 rightAxis)
{
	if (leftStick == NULL || rightStick == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis));
}
Esempio n. 22
0
/**
 * Read the accumulated value and the number of accumulated values atomically.
 * 
 * This function reads the value and count from the FPGA atomically.
 * This can be used for averaging.
 * 
 * @param value Pointer to the 64-bit accumulated output.
 * @param count Pointer to the number of accumulation cycles.
 */
void AnalogChannel::GetAccumulatorOutput(INT64 *value, UINT32 *count)
{
	status = 0;
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	if (value == NULL || count == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}

	tAccumulator::tOutput output = m_accumulator->readOutput(&status);
	*value = output.Value + m_accumulatorOffset;
	*count = output.Count;
	wpi_assertCleanStatus(status);
}
Esempio n. 23
0
/**
 * Set the center value of the accumulator.
 * 
 * The center value is subtracted from each A/D value before it is added to the accumulator. This
 * is used for the center value of devices like gyros and accelerometers to make integration work
 * and to take the device offset into account when integrating.
 * 
 * This center value is based on the output of the oversampled and averaged source from channel 1.
 * Because of this, any non-zero oversample bits will affect the size of the value for this field.
 */
void AnalogChannel::SetAccumulatorCenter(INT32 center)
{
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return;
	}
	m_accumulator->writeCenter(center, &status);
	wpi_assertCleanStatus(status);
}
Esempio n. 24
0
/**
 * Indicate the end of a cluster packed into the dashboard data structure.
 * 
 * After packing data into the cluster, call FinalizeCluster().
 * Every call to AddCluster() must have a matching call to FinalizeCluster().
 */
void Dashboard::FinalizeCluster(void)
{
	if (m_complexTypeStack.top() != kCluster)
	{
		wpi_fatal(MismatchedComplexTypeClose);
		return;
	}
	m_complexTypeStack.pop();
	AddedElement(kOther);
}
Esempio n. 25
0
/**
 * Encoder constructor.
 * Construct a Encoder given a and b channels as digital inputs. This is used in the case
 * where the digital inputs are shared. The Encoder class will not allocate the digital inputs
 * and assume that they already are counted.
 * @param aSource The source that should be used for the a channel.
 * @param bSource the source that should be used for the b channel.
 * @param reverseDirection represents the orientation of the encoder and inverts the output values
 * if necessary so forward represents positive values.
 * @param encodingType either k1X, k2X, or k4X to indicate 1X, 2X or 4X decoding. If 4X is
 * selected, then an encoder FPGA object is used and the returned counts will be 4x the encoder
 * spec'd value since all rising and falling edges are counted. If 1X or 2X are selected then
 * a counter object will be used and the returned value will either exactly match the spec'd count
 * or be double (2x) the spec'd count.
 */
Encoder::Encoder(DigitalSource *aSource, DigitalSource *bSource, bool reverseDirection, EncodingType encodingType)
{
	m_aSource = aSource;
	m_bSource = bSource;
	m_allocatedASource = false;
	m_allocatedBSource = false;
	if (m_aSource == NULL || m_bSource == NULL)
		wpi_fatal(NullParameter);
	else
		InitEncoder(reverseDirection, encodingType);
}
Esempio n. 26
0
/**
 * Read the accumulated value.
 * 
 * Read the value that has been accumulating on channel 1.
 * The accumulator is attached after the oversample and average engine.
 * 
 * @return The 64-bit value accumulated since the last Reset().
 */
INT64 AnalogChannel::GetAccumulatorValue()
{
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return 0;
	}
	INT64 value = m_accumulator->readOutput_Value(&status) + m_accumulatorOffset;
	wpi_assertCleanStatus(status);
	return value;
}
Esempio n. 27
0
/**
 * Get the value of the axis on a joystick.
 * This depends on the mapping of the joystick connected to the specified
 * port.
 *
 * @param stick The joystick to read.
 * @param axis The analog axis value to read from the joystick.
 * @return The value of the axis on the joystick.
 */
float DriverStation::GetStickAxis(UINT32 stick, UINT32 axis)
{
    if (axis < 1 || axis > kJoystickAxes)
    {
        wpi_fatal(BadJoystickAxis);
        return 0.0;
    }

    INT8 value;
    GetData();
    switch (stick)
    {
    case 1:
        value = m_controlData->stick0Axes[axis - 1];
        break;
    case 2:
        value = m_controlData->stick1Axes[axis - 1];
        break;
    case 3:
        value = m_controlData->stick2Axes[axis - 1];
        break;
    case 4:
        value = m_controlData->stick3Axes[axis - 1];
        break;
    default:
        wpi_fatal(BadJoystickIndex);
        return 0.0;
    }

    float result;
    if (value < 0)
        result = ((float)value) / 128.0;
    else
        result = ((float)value) / 127.0;
    wpi_assert(result <= 1.0 && result >= -1.0);
    if (result > 1.0)
        result = 1.0;
    else if (result < -1.0)
        result = -1.0;
    return result;
}
Esempio n. 28
0
/**
 * Read the number of accumulated values.
 * 
 * Read the count of the accumulated values since the accumulator was last Reset().
 * 
 * @return The number of times samples from the channel were accumulated.
 */
UINT32 AnalogChannel::GetAccumulatorCount()
{
	status = 0;
	if (m_accumulator == NULL)
	{
		wpi_fatal(NullParameter);
		return 0;
	}
	UINT32 count = m_accumulator->readOutput_Count(&status);
	wpi_assertCleanStatus(status);
	return count;
}
Esempio n. 29
0
/**
 * Handles errors generated by task related code.
 */
bool Task::HandleError(STATUS results)
{
	if (results != ERROR) return true;
	switch(errnoGet())
	{
	case S_objLib_OBJ_ID_ERROR:
		wpi_fatal(TaskIDError);
		break;
		
	case S_objLib_OBJ_DELETED:
		wpi_fatal(TaskDeletedError);
		break;
		
	case S_taskLib_ILLEGAL_OPTIONS:
		wpi_fatal(TaskOptionsError);
		break;
		
	case S_memLib_NOT_ENOUGH_MEMORY:
		wpi_fatal(TaskMemoryError);
		break;
		
	case S_taskLib_ILLEGAL_PRIORITY:
		wpi_fatal(TaskPriorityError);
		break;

	default:
		printErrno(errnoGet());
		wpi_fatal(TaskError);
	}
	return false;
}
Esempio n. 30
0
/**
 * Gyro constructor with a precreated analog channel object.
 * Use this constructor when the analog channel needs to be shared. There
 * is no reference counting when an AnalogChannel is passed to the gyro.
 * @param channel The AnalogChannel object that the gyro is connected to.
 */
Gyro::Gyro(AnalogChannel * channel)
{
    m_analog = channel;
    m_channelAllocated = false;
    if (channel == NULL)
    {
        wpi_fatal(NullParameter);
    }
    else
    {
        InitGyro();
    }
}