def derobertis(Sv, iax, jax, m, n, r, alpha, bgnmax=-125):
    """
    Estimate background noise as in:
        
        De Robertis and Higginbottom (2007) ‘A post-processing technique to 
        estimate the signal-to-noise ratio and remove echosounder background 
        noise’, ICES Journal of Marine Science, 64: 1282–1291.
    
    Args:
        Sv     (float)    : 2D array with Sv data (dB)
        iax    (int/float): 1D array with i axis (nsamples or metres)
        jax    (int/float): 1D array with j axis (npings, seconds, metres, etc)
        m      (int/float): i resampling length (nsamples or metres)
        n      (int/float): j resampling length (npings, seconds, metres, etc)
        r      (float)    : 1D array with range data (metres)
        alpha  (float)    : absorption coefficient value (dB m-1)
        bgnmax (int/float): maximum background noise estimation (dB)
    
    Returns:
        float: 2D numpy array with background noise estimation (dB)
        bool : 2D array with mask indicating valid noise estimation.
    """
    
    # calculate TVG 
    r_       = r.copy()
    r_[r<=0] = np.nan
    TVG      = 20*np.log10(r_) + 2*alpha*r_
    
    # subtract TVG from Sv  
    Sv_noTVG = Sv - np.vstack(TVG)
    
    # get resampled i/j axes    
    iaxrs       = np.arange(iax[0], iax[-1], m)
    jaxrs       = np.arange(jax[0], jax[-1], n)
    
    # proceed if length of resampled axes is greater than 1 
    if (len(iaxrs)>1) & (len(jaxrs)>1):
    
        # resample Sv_noTVG into m by n bins
        Sv_noTVGrs  = rs.twod(Sv_noTVG, iax, jax, iaxrs, jaxrs, log=True)[0]
        
        # compute background noise as the minimun value per interval in Sv_noTVGrs
        jbool                = np.isnan(Sv_noTVGrs).all(axis=0)
        Sv_noTVGrs [:,jbool] = 0
        bgn_noTVGrs          = np.nanmin(Sv_noTVGrs, 0)
        bgn_noTVGrs[  jbool] = np.nan
        bgn_noTVGrs          = np.tile(bgn_noTVGrs, [len(Sv_noTVGrs), 1])
            
        # Prevent to exceed the maximum background noise expected
        mask              = np.ma.masked_greater(bgn_noTVGrs, bgnmax).mask
        bgn_noTVGrs[mask] = bgnmax
        
        # resample background noise to previous Sv resolution, and add TVG
        bgn_noTVG, mask_ = rs.full(bgn_noTVGrs, iaxrs, jaxrs, iax, jax)
        bgn              = bgn_noTVG + np.vstack(TVG)
    
    # return background noise as NAN values otherwise    
    else:
        bgn   = np.ones_like(Sv)*np.nan
        mask_ = np.ones_like(Sv, dtype=bool)
        warnings.warn("unable to estimate background noise, incorrect resampling axes", RuntimeWarning)
    
    return bgn, mask_
Esempio n. 2
0
def ccamlr(raw, prepro=None, jdx=[0, 0]):
    """
    CCAMLR processing routine.
    
    Process EK60 raw data and returns its variables in a dictionary array.
    """
    #--------------------------------------------------------------------------
    # check for appropiate inputs
    if (isinstance(prepro, dict)) & (jdx[0] >= 0):
        raise Exception('Preceeding raw data needs appropiate j indexes')

    #--------------------------------------------------------------------------
    # Load variables
    rawfiles = raw['rawfiles']
    transect = raw['transect']
    alpha120 = raw['alpha']
    r120 = raw['r']
    t120 = raw['t']
    lon120 = raw['lon']
    lat120 = raw['lat']
    nm120 = raw['nm']
    km120 = raw['km']
    knt120 = raw['knt']
    kph120 = raw['kph']
    pitchmax120 = raw['pitchmax']
    rollmax120 = raw['rollmax']
    heavemax120 = raw['heavemax']
    Sv120 = raw['Sv']
    theta120 = raw['theta']
    phi120 = raw['phi']

    #--------------------------------------------------------------------------
    # join preceeding raw data, if there is continuity in the transect
    if prepro is not None:
        if prepro['transect'] == raw['transect']:
            t120 = np.r_[prepro['t'][jdx[0]:], t120]
            lon120 = np.r_[prepro['lon'][jdx[0]:], lon120]
            lat120 = np.r_[prepro['lat'][jdx[0]:], lat120]
            nm120 = np.r_[prepro['nm'][jdx[0]:], nm120]
            km120 = np.r_[prepro['km'][jdx[0]:], km120]
            knt120 = np.r_[prepro['knt'][jdx[0]:], knt120]
            kph120 = np.r_[prepro['kph'][jdx[0]:], kph120]
            Sv120 = np.c_[prepro['Sv'][:, jdx[0]:], Sv120]
            theta120 = np.c_[prepro['theta'][:, jdx[0]:], theta120]
            phi120 = np.c_[prepro['phi'][:, jdx[0]:], phi120]
        else:
            jdx[1] = 0
    else:
        jdx[1] = 0

    #--------------------------------------------------------------------------
    # report about the transects being processed
    trsct = np.arange(jdx[1], nm120[-1], 1)
    logger.info('Processing transect %03d : %2.2f - %2.2f nmi...' %
                (transect, trsct[0], trsct[-1]))

    #--------------------------------------------------------------------------
    # Clean impulse noise
    Sv120in, m120in_ = mIN.wang(Sv120,
                                thr=(-70, -40),
                                erode=[(3, 3)],
                                dilate=[(7, 7)],
                                median=[(7, 7)])
    #TODO: True is valid
    # -------------------------------------------------------------------------
    # estimate and correct background noise
    p120 = np.arange(len(t120))
    s120 = np.arange(len(r120))
    bn120, m120bn_ = gBN.derobertis(Sv120, s120, p120, 5, 20, r120, alpha120)
    Sv120clean = tf.log(tf.lin(Sv120in) - tf.lin(bn120))
    #TODO: True is valid
    # -------------------------------------------------------------------------
    # mask low signal-to-noise
    m120sn = mSN.derobertis(Sv120clean, bn120, thr=12)
    Sv120clean[m120sn] = -999

    # -------------------------------------------------------------------------
    # get mask for near-surface and deep data
    m120rg = mRG.outside(Sv120clean, r120, 19.9, 250)

    # -------------------------------------------------------------------------
    # get mask for seabed
    m120sb = mSB.ariza(Sv120,
                       r120,
                       r0=20,
                       r1=1000,
                       roff=0,
                       thr=-38,
                       ec=1,
                       ek=(3, 3),
                       dc=10,
                       dk=(3, 7))

    # -------------------------------------------------------------------------
    # get seabed line
    idx = np.argmax(m120sb, axis=0)
    sbline = r120[idx]
    sbline[idx == 0] = np.inf
    sbline = sbline.reshape(1, -1)
    sbline[sbline > 250] = np.nan

    # -------------------------------------------------------------------------
    # get mask for non-usable range
    m120nu = mSN.fielding(bn120, -80)[0]

    # -------------------------------------------------------------------------
    # remove unwanted (near-surface & deep data, seabed & non-usable range)
    m120uw = m120rg | m120sb | m120nu
    Sv120clean[m120uw] = np.nan

    # -------------------------------------------------------------------------
    # get swarms mask
    k = np.ones((3, 3)) / 3**2
    Sv120cvv = tf.log(
        convolve2d(tf.lin(Sv120clean), k, 'same', boundary='symm'))
    m120sh, m120sh_ = mSH.echoview(Sv120cvv,
                                   r120,
                                   km120 * 1000,
                                   thr=-70,
                                   mincan=(3, 10),
                                   maxlink=(3, 15),
                                   minsho=(3, 15))

    # -------------------------------------------------------------------------
    # get Sv with only swarms
    Sv120sw = Sv120clean.copy()
    Sv120sw[~m120sh & ~m120uw] = -999

    # -------------------------------------------------------------------------
    # resample Sv from 20 to 250 m, and every 1nm
    r120intervals = np.array([20, 250])
    nm120intervals = np.arange(jdx[1], nm120[-1], 1)
    Sv120swr, r120r, nm120r, pc120swr = rs.twod(Sv120sw,
                                                r120,
                                                nm120,
                                                r120intervals,
                                                nm120intervals,
                                                log=True)

    # -------------------------------------------------------------------------
    # remove seabed from pc120swr calculation, only water column is considered
    m120sb_ = m120sb * 1.0
    m120sb_[m120sb_ == 1] = np.nan
    pc120water = rs.twod(m120sb_, r120, nm120, r120intervals,
                         nm120intervals)[3]
    pc120swr = pc120swr / pc120water * 100

    # -------------------------------------------------------------------------
    # resample seabed line every 1nm
    sbliner = rs.oned(sbline, nm120, nm120intervals, 1)[0]

    # -------------------------------------------------------------------------
    # get time resampled, interpolated from distance resampled
    epoch = np.datetime64('1970-01-01T00:00:00')
    t120f = np.float64(t120 - epoch)
    f = interp1d(nm120, t120f)
    t120rf = f(nm120r)
    t120r = np.array(t120rf, dtype='timedelta64[ms]') + epoch

    t120intervalsf = f(nm120intervals)
    t120intervals = np.array(t120intervalsf, dtype='timedelta64[ms]') + epoch

    # -------------------------------------------------------------------------
    # get latitude & longitude resampled, interpolated from time resampled
    f = interp1d(t120f, lon120)
    lon120r = f(t120rf)
    f = interp1d(t120f, lat120)
    lat120r = f(t120rf)

    # -------------------------------------------------------------------------
    # resample back to full resolution
    Sv120swrf, m120swrf_ = rs.full(Sv120swr, r120intervals, nm120intervals,
                                   r120, nm120)
    #TODO: True is valid

    # -------------------------------------------------------------------------
    # compute Sa and NASC from 20 to 250 m or down to the seabed depth
    Sa120swr = np.zeros_like(Sv120swr) * np.nan
    NASC120swr = np.zeros_like(Sv120swr) * np.nan
    for i in range(len(Sv120swr[0])):
        if (np.isnan(sbliner[0, i])) | (sbliner[0, i] > 250):
            Sa120swr[0, i] = tf.log(tf.lin(Sv120swr[0, i]) * (250 - 20))
            NASC120swr[0, i] = 4 * np.pi * 1852**2 * tf.lin(
                Sv120swr[0, i]) * (250 - 20)
        else:
            Sa120swr[0, i] = tf.log(
                tf.lin(Sv120swr[0, i]) * (sbliner[0, i] - 20))
            NASC120swr[0, i] = 4 * np.pi * 1852**2 * tf.lin(
                Sv120swr[0, i]) * (sbliner[0, i] - 20)

    # -------------------------------------------------------------------------
    # return processed data outputs
    m120_ = m120in_ | m120bn_ | m120sh_ | m120swrf_
    #TODO: True is valid

    pro = {
        'rawfiles': rawfiles,  # list of rawfiles processed
        'transect': transect,  # transect number
        'r120': r120,  # range (m)
        't120': t120,  # time  (numpy.datetime64)
        'lon120': lon120,  # longitude (deg)
        'lat120': lat120,  # latitude (deg)
        'nm120': nm120,  # distance (nmi)
        'km120': km120,  # distance (km)
        'knt120': knt120,  # speed (knots)
        'kph120': kph120,  # speed (km h-1)
        'pitchmax120': pitchmax120,  # max value in last pitching cycle (deg)
        'rollmax120': rollmax120,  # max value in last rolling cycle (deg)
        'heavemax120': heavemax120,  # max value in last heave cycle (deg)
        'Sv120': Sv120,  # Sv (dB)
        'theta120': theta120,  # Athwart-ship angle (deg)
        'phi120': phi120,  # Alon-ship angle (deg)
        'bn120': bn120,  # Background noise (dB)
        'Sv120in': Sv120in,  # Sv without impulse noise (dB)
        'Sv120clean': Sv120clean,  # Sv without background noise (dB)          
        'Sv120sw': Sv120sw,  # Sv with only swarms (dB)
        'nm120r': nm120r,  # Distance resampled (nmi)
        'r120intervals': r120intervals,  # r resampling intervals
        'nm120intervals': nm120intervals,  # nmi resampling intervals
        't120intervals': t120intervals,  # t resampling intervals
        'sbliner': sbliner,  # Seabed resampled (m)
        't120r': t120r,  # Time resampled (numpy.datetime64)
        'lon120r': lon120r,  # Longitude resampled (deg)
        'lat120r': lat120r,  # Latitude resampled (deg)
        'Sv120swr': Sv120swr,  # Sv with only swarms resampled (dB)
        'pc120swr': pc120swr,  # Valid samples used to compute Sv120swr (%)
        'Sa120swr': Sa120swr,  # Sa from swarms, resampled (m2 m-2)
        'NASC120swr': NASC120swr,  # NASC from swarms, resampled (m2 nmi-2)
        'Sv120swrf':
        Sv120swrf,  # Sv with only swarms, resampled, full resolution (dB)         
        'm120_': m120_
    }  # Sv mask indicating valid processed data (where all filters could be applied)

    return pro
Esempio n. 3
0
ek60 = EK60.EK60()
ek60.read_raw(rawfile)

#------------------------------------------------------------------------------
# get 120 kHz data
raw120 = ek60.get_raw_data(channel_number=2)
Sv120 = np.transpose(raw120.get_Sv().data)
theta120 = np.transpose(raw120.angles_alongship_e)
t120 = raw120.get_Sv().ping_time
r120 = raw120.get_Sv().range

#------------------------------------------------------------------------------
# Resample Sv in 2D, every 10 m in range and 100 seconds in time.
r120rs = np.arange(r120[0], r120[-1], 10)
t120rs = np.arange(t120[0], t120[-1], np.timedelta64(100, 's'))
Sv120rs, Sv120rsper = rs.twod(Sv120, r120, t120, r120rs, t120rs, log=True)

# By typing "log=True" the algorith knows that the variable to be resampled
# is in logarithmic scale (Sv) and need to be converted into linear before
# resampling and back to logarithmic after the resampling.

# "Sv120rs" is the resampled array, and "Sv120per" is the percentage of valid
# samples used in each resampling bin. It's a proxy of data quality.

#------------------------------------------------------------------------------
# Resample theta in 1D, every 10 m in the vertical.
r120rs = np.arange(r120[0], r120[-1], 10)
theta120rs, theta120rsper = rs.oned(theta120, r120, r120rs, log=False)

# This time we set "log=False" becaue theta is already at linear scale.
# "log=False" is the default, so might skip this setting.