示例#1
0
void plVertCoder::IEncodeNormal(hsStream* S, const unsigned char*& src) {
    if (S->getVer().isLive()) {
        S->writeByte(FloatToByte(((((float*)src)[0] + 1.0f) / 2.0f) * 256.0f));
        S->writeByte(FloatToByte(((((float*)src)[1] + 1.0f) / 2.0f) * 256.0f));
        S->writeByte(FloatToByte(((((float*)src)[2] + 1.0f) / 2.0f) * 256.0f));
    } else {
        S->writeShort(FloatToShort(((float*)src)[0] * 32767.0f));
        S->writeShort(FloatToShort(((float*)src)[1] * 32767.0f));
        S->writeShort(FloatToShort(((float*)src)[2] * 32767.0f));
    }
    src += sizeof(float) * 3;
}
示例#2
0
void 
Sampler::_fillBuffer(float *s, unsigned int len, SRC_TYPE typ)
{
	float * fb;
	if(len > bufferLen)
		SetBufferSize(len);
	usedBufferLen = len;
	fb = floatBuffer;
	len *= inChans;
	while(len--) 
		*(fb++) = *(s++);
	if(needShort)
		FloatToShort();
}
示例#3
0
static void ConvertFloatToInteger(void *outdata,
								  float **pcm,
								  int channels, size_t n)
{
	size_t j;
	int i;
	SYS_ASSERT(outdata);
	for(i=0;i<channels;i++)
	{
		const float *mono = pcm[i];
		ogg_int16_t *ptr = (ogg_int16_t *)outdata + i;
		for(j=0;j<n;j++,ptr+=channels,mono++)
		{
			*ptr = FloatToShort(*mono);
		}
	}
}
示例#4
0
bool tASPMaster::DataPacketReceived(const tASPInputParameters& inputParams, tReceivedSoundingInformation& info, tASPOutputData& ASPOutputData, bool sonarMaster, bool structureMaster, SonarCommon::eStructureTransducerType StructureXDCRType)
{
    tSonarFrequencyAndChannel soundingFrequency = info.m_SonarInfo.m_SonarFrequency;
    //eSonarFrequency SonarFrequency = FrequencyHzToSonarFrequency(PingConfigUsed.m_Frequency_Hz);
    Assert(soundingFrequency.m_Frequency >= 0 && soundingFrequency.m_Frequency < eSonarFrequency_MaxNumLocalFrequencies);

    //eSonarChannel Channel = IsPrimaryFreq ? eSonarChannel_Primary : eSonarChannel_Secondary;

    bool Debug = false;
    
    float ChartFeetPerRangeCell = (info.m_SonarInfo.m_LowerLimit - info.m_SonarInfo.m_UpperLimit) / info.m_SonarInfo.m_ChartRangeCells.size();
    float DigitalFeetPerRangeCell = info.m_SonarInfo.m_DigitalLimit / info.m_SonarInfo.m_DigitalRangeCells.size();
    bool AutoDepthRange = m_ASPMasterSettings.m_AutoDepthRange;
    
    //DbgPrintf("Dsize=%i, Csize=%i", DigitalRangeCells.size(), ChartRangeCells.size());
    eSonarChannel sonarChannel = info.m_SonarInfo.m_SonarChannel;
    if(sonarChannel >= eSonarChannel_NumChannels)
        sonarChannel = (eSonarChannel)(eSonarChannel_NumChannels - 1);
    if(sonarChannel < 0)
        sonarChannel = (eSonarChannel)0;

    tSonarFrequencyAndChannel desiredSonarFrequency;
    if(sonarChannel == eSonarChannel_Primary)
        desiredSonarFrequency = m_ASPMasterSettings.m_FrequencyIndex;
    else
        desiredSonarFrequency = m_ASPMasterSettings.m_AltFrequencyIndex;
    
    int customFreqHz = 0;
    if(desiredSonarFrequency.m_Frequency == eSonarFrequency_CustomDSP1)
    {
        if(sonarChannel == eSonarChannel_Primary)
        {
            customFreqHz = m_ASPMasterSettings.m_CustomFrequencyHz;
        }
        else
        {
            customFreqHz = m_ASPMasterSettings.m_AltCustomFrequencyHz;
        }
    }
    
    int IntegrationCycles = info.m_PingConfigUsed.m_IntegrationCycles;
    tHorizontalCorrelation::SmoothCompensationUgliness((unsigned char *)info.m_SonarInfo.m_ChartRangeCells.data(), info.m_SonarInfo.m_ChartRangeCells.size(), info.m_SonarInfo.m_ChartRangeCells[0], m_cReceiverA2DBits, m_cTotalReceiverDynamicRange_dB, IntegrationCycles);

    //~~~~~~~~~~~~~~ UMMM... what if we have 200kHz on two seperate channels or something??!
    ASPOutputData.m_AutosenseRangeCell = (tRangeCell)m_AutoSensitivity[soundingFrequency.m_Frequency].GetAutoSenseValue();
    ASPOutputData.m_DigitalAutosenseRangeCell = (tRangeCell)m_AutoSensitivity[soundingFrequency.m_Frequency].GetDigitalAutoSenseValue();

    eSonarFrequencyEnum frequency = soundingFrequency.m_Frequency;
    if( frequency == eSonarFrequency_CustomDSP1)
    {
        frequency = FrequencyHzToSonarFrequency(info.m_PingConfigUsed.m_Frequency_Hz);
    }
    int RVGValue = GetRVGValue(frequency, m_ASPMasterSettings.m_PrimaryLowerFt);

    bool sonarStopped = m_ASPMasterSettings.m_StopSonar;

    //DbgPrintf("SonarInfo: digitalLimit=%g, downLimit=%g", info.m_SonarInfo.m_DigitalLimit, info.m_SonarInfo.m_LowerLimit);

    bool DigitalColumnUsable = false;
    if(info.m_SonarInfo.m_DigitalLimit > 1 && sonarMaster && !sonarStopped)
        DigitalColumnUsable = true;
    bool ChartColumnUsable = false;
    if(info.m_SonarInfo.m_LowerLimit > 1 && sonarMaster)
        ChartColumnUsable = true;

    eSonarChannel channelUsedForDigital = sonarChannel;
    bool usingDownscanForDigital = false;

    if((!sonarMaster || sonarStopped) && structureMaster)    
        channelUsedForDigital = eSonarChannel_Downscan;

    bool LastDepthValid = m_DepthValid[channelUsedForDigital];

   if(DigitalColumnUsable)
    {
        //~~~~~~~~~~~~~~~~~~~        
        // Make digital smart enough to not use no burst length data...~~~~~~~~~~~
        // Incorporate SS data here too... Might be present when sonar is not and might have data when sonar does not...

        //LastDepthValid = m_DepthValid[sonarChannel];

        // Note - digital uses chart rangecells for debug only!
        m_DepthValid[sonarChannel] = m_DigitalDepth[sonarChannel].Scan(
            (tRangeCell *)info.m_SonarInfo.m_ChartRangeCells.data(), info.m_SonarInfo.m_ChartRangeCells.size(), 
            ChartFeetPerRangeCell, info.m_SonarInfo.m_UpperLimit, info.m_SonarInfo.m_LowerLimit, 
            soundingFrequency.m_Frequency, 
            (const tRangeCell *)info.m_SonarInfo.m_DigitalRangeCells.constData(), DigitalFeetPerRangeCell, 
            RVGValue, ASPOutputData.m_DigitalAutosenseRangeCell, 
            m_SpeedKn, m_SpeedValid,
            (unsigned char)info.m_SonarInfo.m_ChartRangeCells.data()[0],
            Debug,
            SearchDepthLimit(soundingFrequency.m_Frequency),
            m_BottomDepth[sonarChannel], m_SurfaceDepth[sonarChannel], m_TopBottomDepth[sonarChannel]
            );
        // TODO - If this depth is valid, lets setup the other digital classes with the same info...
        //DbgPrintf("----->Digital Limit:%f ",info.m_SonarInfo.m_DigitalLimit); 
        //DbgPrintf("Freq %i, Depth=%g (%c)", sonarFrequency, BottomDepth, m_DepthValid[Channel] ? 'y' : 'n');
    }
    else if(structureMaster && info.m_SonarInfo.m_DigitalLimit > 1)
    {
        tRangeCell structDownAutoSenseRangeCell = (tRangeCell)m_ASP2[info.m_StructureInfo.m_StructureDownFrequency.m_Frequency].StructureNoiseFloor();
        //LastDepthValid = m_DepthValid[eSonarChannel_Downscan];
        int structRVGValue = GetRVGValue(info.m_StructureInfo.m_StructureDownFrequency.m_Frequency, m_ASPMasterSettings.m_DownscanLowerFt);

        tRangeCellArray expandedDownRangeCells;
        expandedDownRangeCells.resize(tDigitalDepth::m_cDigitalDepthColumnHeight);
        
        // If the downscan chart column size is larger than the digital column size, then some assumptions
        // throughout the code have broken and we probably have a bigger problem than the digital depth not working.
        int DownScanDataSize = info.m_StructureInfo.m_DownRangecells.size();
        Assert(DownScanDataSize <= tDigitalDepth::m_cDigitalDepthColumnHeight);
        if(DownScanDataSize > tDigitalDepth::m_cDigitalDepthColumnHeight)
        {
            DownScanDataSize = tDigitalDepth::m_cDigitalDepthColumnHeight;
        }
        memcpy(expandedDownRangeCells.data(), info.m_StructureInfo.m_DownRangecells.data(), DownScanDataSize);
        ExpandRangeCellsInPlace(expandedDownRangeCells.size(), DownScanDataSize,(tRangeCell *)expandedDownRangeCells.data());

        float structDownFeetPerRangeCell = info.m_StructureInfo.m_DownRange / expandedDownRangeCells.size();

        float structChartFeetPerRangeCell = info.m_StructureInfo.m_DownRange / info.m_StructureInfo.m_DownRangecells.size();

        m_DepthValid[eSonarChannel_Downscan] = m_DigitalDepth[eSonarChannel_Downscan].Scan(
            (tRangeCell *)info.m_StructureInfo.m_DownRangecells.data(), info.m_StructureInfo.m_DownRangecells.size(), 
            structChartFeetPerRangeCell, 0, info.m_StructureInfo.m_DownRange, 
            info.m_StructureInfo.m_StructureDownFrequency.m_Frequency, 
            (const tRangeCell *)expandedDownRangeCells.constData(), structDownFeetPerRangeCell, 
            structRVGValue, structDownAutoSenseRangeCell, 
            m_SpeedKn, m_SpeedValid,
            (unsigned char)info.m_StructureInfo.m_DownRangecells.data()[0],
            Debug,
            SearchDepthLimit(info.m_StructureInfo.m_StructureDownFrequency.m_Frequency),
            m_BottomDepth[eSonarChannel_Downscan], m_SurfaceDepth[eSonarChannel_Downscan], m_TopBottomDepth[eSonarChannel_Downscan]
            );
        
        usingDownscanForDigital = true;

        int bottomIndex = FloatToLong(info.m_StructureInfo.m_DownRangecells.size() * (m_BottomDepth[eSonarChannel_Downscan] - 0) / (info.m_StructureInfo.m_DownRange - 0));

        m_ASP2[info.m_StructureInfo.m_StructureDownFrequency.m_Frequency].CalculateStructureNoiseFloor(info.m_StructureInfo.m_DownRangecells, bottomIndex, info.m_StructureInfo.m_DownRange < 50);
    }

    eSonarASP NoiseRejection = static_cast<eSonarASP>(m_ASPMasterSettings.m_NoiseRejection);

    float visibleLowerLimit = 0;
    float searchDepth = 0;
    if(sonarMaster && !sonarStopped)
    {
        searchDepth = m_DigitalDepth[sonarChannel].GetSearchDepth(desiredSonarFrequency.m_Frequency, visibleLowerLimit, SearchDepthLimit(desiredSonarFrequency.m_Frequency));
        switch(sonarChannel)
        {
        case eSonarChannel_Primary:
            visibleLowerLimit = m_ASPMasterSettings.m_PrimaryLowerFt;
            break;
        case eSonarChannel_Secondary:
            visibleLowerLimit = m_ASPMasterSettings.m_SecondaryLowerFt;
            break;
        default:
            Assert(false);
            break;
        }
    }
    else
    {
        tSonarFrequencyAndChannel desiredStructFrequency;
        if(sonarChannel == eSonarChannel_Primary)
            desiredStructFrequency = m_ASPMasterSettings.m_SidescanFrequencyIndex;
        searchDepth = m_DigitalDepth[eSonarChannel_Downscan].GetSearchDepth(desiredStructFrequency.m_Frequency, visibleLowerLimit, SearchDepthLimit(desiredStructFrequency.m_Frequency));
    }

    if(visibleLowerLimit < 5)
        visibleLowerLimit = 5;

    float sideLimit = 0;
    float downLimit = 0;
    float downRange = 0;
    float sideRange = 0;
    if(info.m_StructureInfo.m_DownRangecells.size() > 0 && structureMaster && !m_ASPMasterSettings.m_SidescanStopSonar && StructureXDCRType != SonarCommon::eStrucTransducerType_Invalid)
    {
        downRange = m_ASPMasterSettings.m_DownscanLowerFt;
        downLimit = SonarCommon::ChartSideToSideLimit(downRange);
        if(StructureXDCRType != SonarCommon::eStrucTransducerType_DownOnly)
        {
            sideRange = m_ASPMasterSettings.m_SidescanLowerFt;
            sideLimit = SonarCommon::ChartSideToSideLimit(sideRange);
        }
        
        //DbgPrintf("Struct XID voltage = %i", info.m_StructureInfo.m_XIDVoltage100x);
    }

    bool DigitalSoundingOnly;
    float SampleLowerLimit;
    float Depth;
    int PingSpeed = m_ASPMasterSettings.m_PingSpeed;  // The GetLimits call will modify this value if we're in search mode.  Cache this locally for use below since m_ASPMasterSettings.m_PingSpeed can change async. from this call.
    m_PingLimitsSelector.GetLimits(ASPOutputData.m_DigitalLimit_ft, 
                                   ASPOutputData.m_ChartLimit_ft, 
                                   SampleLowerLimit, 
                                   Depth, 
                                   DigitalSoundingOnly, 
                                   PingSpeed, 
                                   downLimit,
                                   visibleLowerLimit,
                                   searchDepth,
                                   channelUsedForDigital,
                                   m_BottomDepth[channelUsedForDigital], 
                                   m_DepthValid[channelUsedForDigital], 
                                   m_DigitalDepth[channelUsedForDigital].IsSolidLock(),
                                   sideLimit,
                                   m_ASPMasterSettings.m_ChartManualMode,
                                   m_ASPMasterSettings.m_SidescanChartManualMode);

    float ChartRange = m_ASPMasterSettings.m_PrimaryLowerFt - m_ASPMasterSettings.m_PrimaryUpperFt;

    // 12/07/31 -- CGB: When sonar is not running we need to set the range to the down limit otherwise we could be setting the ping rate too large (NSW-11259).
    if((!sonarMaster || sonarStopped) && structureMaster)
    {
        ChartRange = downLimit;
    }
    
    tShortArray NoiseData;
    NoiseData.resize(info.m_SonarInfo.m_NoiseRangeCells.size() / 2);
    memcpy(NoiseData.data(), info.m_SonarInfo.m_NoiseRangeCells.constData(), info.m_SonarInfo.m_NoiseRangeCells.size());

    int DigitalLine = m_DigitalDepth[sonarChannel].GetDigitalLine();
    int SNR_dB = (DigitalLine - m_ASP2[soundingFrequency.m_Frequency].NoiseFloor()) * m_cTotalReceiverDynamicRange_dB / 255;
    if(!m_DepthValid[channelUsedForDigital])
    {
        SNR_dB = 0;
    }

    bool limitGainClipDetect = (NoiseRejection == eSonarASP_Off) || m_ASPMasterSettings.m_ChartManualMode;

    int gain_dB = FloatToLong(info.m_PingConfigUsed.m_Gain_dB100x / 100.0F);
    int gainReductionFromMaxGain_dB = info.m_Hardware.m_MaxGain_dB - gain_dB;

    ASPOutputData.m_SideRange_ft = sideLimit;
    ASPOutputData.m_DownRange_ft = downLimit;

    //DbgPrintf("digitalLimit=%g, downLimit=%g", ASPOutputData.m_DigitalLimit_ft, downLimit);

    //~~~~~~~~~~~~~~ UMMM... what if we have 200kHz on two seperate channels or something??!
    ASPOutputData.m_SonarPingConfig = m_ASP2[desiredSonarFrequency.m_Frequency].SetupPing(inputParams, desiredSonarFrequency, !ChartColumnUsable, DigitalSoundingOnly, 
                       SampleLowerLimit, Depth, ChartRange, ASPOutputData.m_DigitalLimit_ft,
                       PingSpeed, NoiseRejection, info.m_Debug, limitGainClipDetect,
                       NoiseData, info.m_SonarInfo.m_ChartRangeCells, info.m_SonarInfo.m_UpperLimit, info.m_SonarInfo.m_LowerLimit, RVGValue, 
                       gainReductionFromMaxGain_dB, info.m_Hardware, 
                       m_SpeedKn, m_SpeedValid, SNR_dB,
                       downLimit, sideLimit, downRange, sideRange, m_ASPMasterSettings.m_SidescanFrequencyIndex, m_DCOffset,
                       customFreqHz, visibleLowerLimit, m_ASPMasterSettings.m_StopSonar, info.m_PingConfigUsed.m_CompensationOffset);

//            DbgPrintf("NoiseFloor=%i New=%i (LastAvg=%i)", m_ASP2[sonarFrequency].NoiseFloor(), NewAutoThreshold, ASPOutputData.m_AutosenseRangeCell);

    if(StructureXDCRType == SonarCommon::eStrucTransducerType_Invalid)
    {
        ASPOutputData.m_SonarPingConfig.m_StructureDownBurstLength_us = 0;
        ASPOutputData.m_SonarPingConfig.m_StructureSideBurstLength_us = 0;

        // Setting mimimum range, since a value of zero is discarded by CSM
        ASPOutputData.m_DownRange_ft = 5;
        ASPOutputData.m_SideRange_ft = 5;

    }
    else if(StructureXDCRType == SonarCommon::eStrucTransducerType_DownOnly)
    {
        ASPOutputData.m_SonarPingConfig.m_StructureSideBurstLength_us = 0;
        // Setting mimimum range, since a value of zero is discarded by CSM
        ASPOutputData.m_SideRange_ft = 5;
    }

    bool matchDownAndTraditionalAutorange = false;

    // AC~~~~~~~~~~  Move this somewhere else???
    if(structureMaster)
    {
        if(m_ASPMasterSettings.m_SidescanAutoRange && sideRange > 0)
        {
            ASPOutputData.m_UpdateSidescanLowerFt_FromAutoRange = m_RangeSelector.SelectSidescanRange(info.m_StructureInfo.m_LeftRangecells, info.m_StructureInfo.m_RightRangecells, info.m_StructureInfo.m_SideRange, m_BottomDepth[channelUsedForDigital], m_DepthValid[channelUsedForDigital], m_ASPMasterSettings.m_SidescanLowerFt, ASPOutputData.m_SidescanLowerFt_FromAutoRange);
        }
        // Why do we want to check if the ChartColumn is usable for the downscan auto range and not for the conv. sonar? 
        // the down chart is always usable when the conv. sonar data is used for the digital calculation
        if(((/*ChartColumnUsable &&*/ DigitalColumnUsable) || usingDownscanForDigital) && m_ASPMasterSettings.m_DownscanAutoRange && m_DepthValid[channelUsedForDigital])
        {
            // Only auto range off the secondary frequency if the primary depth is invalid...
            if(sonarChannel == eSonarChannel_Primary || !m_DepthValid[eSonarChannel_Primary])
            {
                // NSW-24479: Set flag to sync downscan range with traditional here, sync will be performed after traditional auto-range below, if traditional is not the server then it will not try to sync anyway
                matchDownAndTraditionalAutorange = true;
                if((sonarMaster == false) || ((sonarMaster == true) && (AutoDepthRange == false)) || (usingDownscanForDigital == true))
                {
                    ASPOutputData.m_UpdateDownscanLowerFt_FromAutoRange = m_RangeSelector.SelectDownscanRange(m_BottomDepth[channelUsedForDigital], m_ASPMasterSettings.m_DownscanLowerFt, ASPOutputData.m_DownscanLowerFt_FromAutoRange);
                }
            }
        }
        //else
        //{
        //    DbgPrintf("No conditions to do auto-downrange: ChartcolumnUsable=%i,DigitalColumnUsable=%i,UsingDownForDig=%i,AutoDepthRange=%i,DepthValid=%i", ChartColumnUsable ? 1 : 0, DigitalColumnUsable ? 1 : 0, usingDownscanForDigital ? 1 : 0, m_ASPMasterSettings.m_DownscanAutoRange() ? 1 : 0, m_DepthValid[channelUsedForDigital] ? 1: 0);
        //}
    }

    // Need to calculate SurfaceCellIndex before than AutoSense calculation..NSW-17342
    float fIndex;
    if(ChartColumnUsable == true)
    {
        fIndex = (m_SurfaceDepth[sonarChannel] - info.m_SonarInfo.m_UpperLimit) / ChartFeetPerRangeCell;
    }
    else
    {
        fIndex = 0;
    }
    ASPOutputData.m_SurfaceCellIndex = FloatToShort(qBound<float>(SHRT_MIN, fIndex, SHRT_MAX));
    ValidateAndLimitIntValue(ASPOutputData.m_SurfaceCellIndex, 0, info.m_SonarInfo.m_ChartRangeCells.size());
    //~~~~~~~~~~~~~~ UMMM... what if we have 200kHz on two separate channels or something??!
    ASPOutputData.m_AutosenseRangeCell = (tRangeCell)m_AutoSensitivity[soundingFrequency.m_Frequency].CalculateAutoSense(m_ASP2[soundingFrequency.m_Frequency].NoiseFloor(), m_ASP2[soundingFrequency.m_Frequency].ShortTimeNoiseFloor() , RVGValue, desiredSonarFrequency.m_Frequency, DigitalLine, 
        m_SpeedKn, m_SpeedValid, m_BottomDepth[sonarChannel], m_DepthValid[sonarChannel], m_cTotalReceiverDynamicRange_dB, ASPOutputData.m_SurfaceCellIndex, ChartRange);
    ASPOutputData.m_DigitalLine = (tRangeCell)m_DigitalDepth[sonarChannel].GetDigitalLine();
    
    //*******************************************************************/
    // Auto ranging code here
    //*******************************************************************/
    ASPOutputData.m_UpdatePrimaryLowerFt_FromAutoRange = false;
    ASPOutputData.m_UpdateSecondaryLowerFt_FromAutoRange = false;
    if(sonarMaster)
    {
        if(DigitalColumnUsable && AutoDepthRange && m_DepthValid[sonarChannel])
        {
            // Only auto range off the secondary frequency if the primary depth is invalid...
            if(sonarChannel == eSonarChannel_Primary || !m_DepthValid[eSonarChannel_Primary])
            {
                // Autoranging off the primary channel.
                ASPOutputData.m_UpdatePrimaryLowerFt_FromAutoRange = m_RangeSelector.SelectSonarRange(m_BottomDepth[sonarChannel], m_ASPMasterSettings.m_PrimaryLowerFt, ASPOutputData.m_PrimaryLowerFt_FromAutoRange);
                ASPOutputData.m_UpdateSecondaryLowerFt_FromAutoRange = m_RangeSelector.SelectSonarRange(m_BottomDepth[sonarChannel], m_ASPMasterSettings.m_SecondaryLowerFt, ASPOutputData.m_SecondaryLowerFt_FromAutoRange);

                if(matchDownAndTraditionalAutorange == true)
                {
                    float primaryLowerFt = m_ASPMasterSettings.m_PrimaryLowerFt;
                    if(ASPOutputData.m_UpdatePrimaryLowerFt_FromAutoRange == true)
                    {
                        primaryLowerFt = ASPOutputData.m_PrimaryLowerFt_FromAutoRange;
                    }
                    // Need to match whatever the primary is or will be...
                    ASPOutputData.m_UpdateDownscanLowerFt_FromAutoRange = m_RangeSelector.SelectDownscanRangeFromPrimaryAuto(primaryLowerFt, ASPOutputData.m_DownscanLowerFt_FromAutoRange);
                }

                if(ASPOutputData.m_UpdatePrimaryLowerFt_FromAutoRange && !LastDepthValid)
                {
                    // AC - 2/16/2010 - If we are just locking on and range change, then
                    // reset the ASP filters...
                    for(int i = 0; i < eSonarFrequency_MaxNumLocalFrequencies; i++)
                    {
                        m_ASP2[i].ResetFilters();
                    }
                }
            }
        }
    }

    //ASPOutputData.m_SurfaceCellIndex = FloatToShort((m_SurfaceDepth[sonarChannel] - info.m_SonarInfo.m_UpperLimit) / ChartFeetPerRangeCell);
    ASPOutputData.m_TopBottomCellIndex = 0;
    //if(m_DepthValid[Channel])
    {
        float fIndex = (m_TopBottomDepth[sonarChannel] - info.m_SonarInfo.m_UpperLimit) / ChartFeetPerRangeCell;
        ASPOutputData.m_TopBottomCellIndex = FloatToShort(qBound<float>(SHRT_MIN, fIndex, SHRT_MAX));
        if(ASPOutputData.m_TopBottomCellIndex <= 0)
            ASPOutputData.m_TopBottomCellIndex = 1;

        //DbgPrintf("TopBottom=%i", ASPOutputData.m_TopBottomCellIndex);
    }
    if(ASPOutputData.m_TopBottomCellIndex >= info.m_SonarInfo.m_ChartRangeCells.size())
        ASPOutputData.m_TopBottomCellIndex = info.m_SonarInfo.m_ChartRangeCells.size() - 1;
    if(ASPOutputData.m_SurfaceCellIndex < 0)
        ASPOutputData.m_SurfaceCellIndex = 0;
    if(ASPOutputData.m_SurfaceCellIndex > ASPOutputData.m_TopBottomCellIndex)
        ASPOutputData.m_SurfaceCellIndex = ASPOutputData.m_TopBottomCellIndex;

    //*******************************************************************/
    // Fish ID called here
    //*******************************************************************/
    if(ChartColumnUsable && channelUsedForDigital == sonarChannel)
    {
        if(m_DepthValid[sonarChannel])
        {
            m_FishID[sonarChannel].Setup(ChartFeetPerRangeCell, info.m_SonarInfo.m_UpperLimit);

            tRangeCell FishSense = ASPOutputData.m_AutosenseRangeCell;
            if(FishSense + RVGValue < 255)
                FishSense = (tRangeCell)(FishSense + RVGValue);
            else
                FishSense = 255;
            m_FishID[sonarChannel].FindFish((const tRangeCell *)info.m_SonarInfo.m_ChartRangeCells.constData(), info.m_SonarInfo.m_ChartRangeCells.size(), FishSense, ASPOutputData.m_SurfaceCellIndex, ASPOutputData.m_TopBottomCellIndex, true);
        }
    }

    // Debug stuff...
    //~~~~~~~~~~~~~~ UMMM... what if we have 200kHz on two seperate channels or something??!
    ASPOutputData.m_AverageOutOfBandNoise = m_ASP2[desiredSonarFrequency.m_Frequency].GetAverageOutOfBandNoise();
    ASPOutputData.m_InBandNoise = m_ASP2[desiredSonarFrequency.m_Frequency].GetInBandNoise();
    ASPOutputData.m_PeakOutOfBandNoise = m_ASP2[desiredSonarFrequency.m_Frequency].GetPeakOutOfBandNoise();
    ASPOutputData.m_PeakOutOfBandFrequencyAngle = m_ASP2[desiredSonarFrequency.m_Frequency].GetPeakOutOfBandFrequencyAngle();
    ASPOutputData.m_PingAdder_ms = m_ASP2[desiredSonarFrequency.m_Frequency].GetPingAdder_ms();

    Assert(info.m_SonarInfo.m_UpperLimit >= 0);
    Assert(info.m_SonarInfo.m_LowerLimit > info.m_SonarInfo.m_UpperLimit);

    if(/*usingDownscanForDigital*/channelUsedForDigital == eSonarChannel_Downscan && info.m_SonarInfo.m_LowerLimit > 1)
        ChartColumnUsable = true;

    return ChartColumnUsable;
}