精华内容
下载资源
问答
  • hash函数解析

    千次阅读 2011-12-01 16:31:31
    Hash函数在多个领域均有应用,而在数字签名和数据库实现时又用的最多,比如基于hash的索引,是最好的单值查找索引; 同时,在当前数据爆炸的场景下,执行相似item的查找时,在内存受限时,均可以采取LSH(local ...

    Hash函数在多个领域均有应用,而在数字签名和数据库实现时又用的最多,比如基于hash的索引,是最好的单值查找索引;

    同时,在当前数据爆炸的场景下,执行相似item的查找时,在内存受限时,均可以采取LSH(local sensitive hash)进行分段处理。

    具体用途很多,不赘述,下面介绍一些常用的知识:

    1、hash函数本质;

    2、简单的hash函数生成法;

    3、hash的冲突消解;

    另外:分享一个国外牛人的个人网站,有非常全面的hash算法列表:http://burtleburtle.net/bob/hash/

    主要内容:

    1、hash的本质


    Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间。在考虑使用Hash函数之前,需要明白它的几个限制:

            (1). Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
      (2). 由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
      (3). 不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。

    Hash函数好坏非评判标准:简单和均匀。
         简单指散列函数的计算简单快速;
         均匀指对于关键字集合中的任一关键字,散列函数能以等概率将其映射到表空间的任何一个位置上。也就是说,散列函数能将子集K随机均匀地分布在表的地址集{0,1,…,m-1}上,以使冲突最小化。


    2、常用hash生成方法

    

    常用的散列函数构造有6种方法,1,直接定址法; 2,数字分析法; 3 ,平方取中法;4,折叠法;5,除留余数法;6,伪随机数法

    (1)平方取中法
         具体方法:先通过求关键字的平方值扩大相近数的差别,然后根据表长度取中间的几位数作为散列函数值。又因为一个乘积的中间几位数和乘数的每一位都相关,所以由此产生的散列地址较为均匀。
     【例】将一组关键字(0100,0110,1010,1001,0111)平方后得
        (0010000,0012100,1020100,1002001,0012321)
     若取表长为1000,则可取中间的三位数作为散列地址集:
        (100,121,201,020,123)。
    相应的散列函数用C实现很简单:
    int Hash(int key){ //假设key是4位整数
    key*=key; key/=100; //先求平方值,后去掉末尾的两位数
    return key%1000; //取中间三位数作为散列地址返回
    }

    (2)除余法
         该方法是最为简单常用的一种方法。它是以表长m来除关键字,取其余数作为散列地址,即 h(key)=key%m
         该方法的关键是选取m。选取的m应使得散列函数值尽可能与关键字的各位相关。m最好为素数。
     【例】若选m是关键字的基数的幂次,则就等于是选择关键字的最后若干位数字作为地址,而与高位无关。于是高位不同而低位相同的关键字均互为同义词。
     【例】若关键字是十进制整数,其基为10,则当m=100时,159,259,359,…,等均互为同义词。

    (3)相乘取整法
         该方法包括两个步骤:首先用关键字key乘上某个常数A(0<A<1),并抽取出key.A的小数部分;然后用m乘以该小数后取整。即:
             
         该方法最大的优点是选取m不再像除余法那样关键。比如,完全可选择它是2的整数次幂。虽然该方法对任何A的值都适用,但对某些值效果会更好。Knuth建议选取
                 
         该函数的C代码为:
    int Hash(int key){
    double d=key *A; //不妨设A和m已有定义
    return (int)(m*(d-(int)d));//(int)表示强制转换后面的表达式为整数
    }

    (4)随机数法
         选择一个随机函数,取关键字的随机函数值为它的散列地址,即
             h(key)=random(key)
     其中random为伪随机函数,但要保证函数值是在0到m-1之间。


    3、hash的冲突消解

    1)冲突是如何产生的?
    上文中谈到,哈希函数是指如何对关键字进行编址的规则,这里的关键字的范围很广,可视为无限集,如何保证无限集的原数据在编址的时候不会出现重复呢?规则本身无法实现这个目的。举一个例子,仍然用班级同学做比喻,现有如下同学数据
    张三,李四,王五,赵刚,吴露.....
    假如我们编址规则为取姓氏中姓的开头字母在字母表的相对位置作为地址,则会产生如下的哈希表

    位置 字母 姓名  
    0 a    
    1 b    
    2 c    
    ...
    10    L     李四  

    ...
    22 W 王五,吴露  
    ..
    25  张三,赵刚  

    我们注意到,灰色背景标示的两行里面,关键字王五,吴露被编到了同一个位置,关键字张三,赵刚也被编到了同一个位置。老师再拿号来找张三,座位上有两个人,"你们俩谁是张三?"
    2)如何解决冲突问题
    既然不能避免冲突,那么如何解决冲突呢,显然需要附加的步骤。通过这些步骤,以制定更多的规则来管理关键字集合,通常的办法有:
    a)开放地址法
    开放地执法有一个公式:Hi=(H(key)+di) MOD m i=1,2,...,k(k<=m-1)
    其中,m为哈希表的表长。di 是产生冲突的时候的增量序列。如果di值可能为1,2,3,...m-1,称线性探测再散列。
    如果di取1,则每次冲突之后,向后移动1个位置.如果di取值可能为1,-1,2,-2,4,-4,9,-9,16,-16,...k*k,-k*k(k<=m/2) 
    称二次探测再散列。如果di取值可能为伪随机数列。称伪随机探测再散列。仍然以学生排号作为例子,
    现有两名同学,李四,吴用。李四与吴用事先已排好序,现新来一名同学,名字叫王五,对它进行编制
    10.. .... 22 .. .. 25
    李四.. .... 吴用 .. .. 25
       赵刚未来之前
    10.. .. 22 23 25
    李四..   吴用 王五  
       (a)线性探测再散列对赵刚进行编址,且di=1
    10... 20 22 .. 25
    李四.. 王五 吴用    
       (b)二次探测再散列,且di=-2
    1... 10... 22 .. 25
    王五.. 李四.. 吴用    

       (c)伪随机探测再散列,伪随机序列为:5,3,2

    b)再哈希法 
    当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。
    比如上面第一次按照姓首字母进行哈希,如果产生冲突可以按照姓字母首字母第二位进行哈希,再冲突,第三位,直到不冲突为止
    c)链地址法
    将所有关键字为同义词的记录存储在同一线性链表中。如下:

    因此这种方法,可以近似的认为是筒子里面套筒子

     

    d.建立一个公共溢出区(比较常见于实际操作中)
    假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,另外设立存储空间向量OverTable[0..v]用以存储发生冲突的记录。
    经过以上方法,基本可以解决掉hash算法冲突的问题。
    注:之所以会简单得介绍了hash,是为了更好的学习lzw算法,学习lzw算法是为了更好的研究gif文件结构,最后,我将详细的阐述一下gif文件是如何构成的,如何高效操作此种类型文件。

    除了上诉的几种方法,还有许多用于散列表的方法,比如散列函数不好或装填因子过大,都会使堆积现象加剧。为了减少堆积的发生,不能像线性探查法那样探查一个顺序的地址序列(相当于顺序查找),而应使探查序列跳跃式地散列在整个散列表中。衍生出二次探查法,双重散列表法。

    展开全文
  • GPS从入门到放弃(二十六) --- RTKLIB函数解析

    千次阅读 多人点赞 2020-06-07 21:57:21
    GPS从入门到放弃(二十六) — RTKLIB函数解析 为了贴合这个系列的标题“从入门到放弃”,在入门之后现在就要放弃此方向了。虽然感觉遗憾,暂时也没有办法。在此附上此系列最后篇,希望能给大家一些帮助。 此文中...

    GPS从入门到放弃(二十六) — RTKLIB函数解析

    为了贴合这个系列的标题“从入门到放弃”,在入门之后现在就要放弃此方向了。虽然感觉遗憾,暂时也没有办法。在此附上此系列最后一篇,希望能给大家一些帮助。

    此文中一些函数解析参考了 https://www.cnblogs.com/taqikema/p/8819798.html,在此表示感谢!

    rtksvrthread

    void *rtksvrthread(void *arg)
    
    • 所在文件:rtksvr.c
    • 功能说明:rtk服务线程。
    • 参数说明:无
    • 处理过程:
    1. 检查svr->state,若为0,线程结束。
    2. 从input stream 读取数据。
    3. 写入到 log stream。
    4. 若为精密星历,调用 decodefile 解码;否则调用 decoderaw 解码。
    5. 对每一个观测数据,调用 rtkpos 进行定位计算。
    6. 若定位成功,调整时间,写solution;若没成功,写solution。
    7. 若有需要,发送 nmea request 给 base/nrtk input stream。
    8. 休眠等下一个cycle。

    rtkpos

    int rtkpos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
    
    • 所在文件:rtkpos.c
    • 功能说明:根据观测数据和导航信息,计算接收机的位置、速度和钟差。
    • 参数说明:
    函数参数,4个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    nav_t    *nav      I   导航数据
    返回类型:
    int                O   (0:no solution,1:valid solution)
    
    • 处理过程:
    1. 设置基准站位置,记录观测值数量。
    2. 调用 pntpos 进行接收机单点定位。若为单点定位模式,输出,返回。
    3. 若为 PPP 模式,调用 pppos 进行精密单点定位,输出,返回。
    4. 若无基准站观测数据,输出,返回。
    5. 若为移动基站模式,调用 pntpos 进行基站单点定位,并加以时间同步;否则只计算一下差分时间。
    6. 调用 relpos 进行相对基站的接收机定位,输出,返回。

    pntpos

    int pntpos (const obsd_t *obs, int n, const nav_t *nav, const prcopt_t *opt,
                sol_t *sol, double *azel, ssat_t *ssat, char *msg)
    
    • 所在文件:pntpos.c
    • 功能说明:依靠伪距和多普勒频移测量值来进行单点定位,给出接收机的位置、速度和钟差。
    • 参数说明:
    函数参数,8个:
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    nav_t    *nav      I   导航数据
    prcopt_t *opt      I   处理过程选项
    sol_t    *sol      IO  solution
    double   *azel     IO  方位角和俯仰角 (rad) (NULL: no output)
    ssat_t   *ssat     IO  卫星状态            (NULL: no output)
    char     *msg      O   错误消息
    返回类型:
    int                O   (1:ok,0:error)
    
    • 处理过程:
    1. 当处理选项 opt 中的模式不是单点模式时,电离层校正采用 broadcast 模型,即Klobuchar模型,对流层校正则采用 Saastamoinen 模型;相反,当其为单点模式时,对输入参数 opt 不做修改。
    2. 调用 satposs 计算卫星们位置、速度、时钟
    3. 调用 estpos 根据伪距估计接收机位置,其中会调用 valsol 进行 χ2\chi^2 检验和 GDOP 检验。
    4. 若3中的检验没通过,调用 raim_fde 进行接收机自主完好性监测,判决定位结果的有效性,并进行错误排除。
    5. 调用 estvel 根据多普勒频移测量值计算接收机的速度。
    6. 赋值给卫星状态结构体ssat。

    satposs

    void satposs(gtime_t teph, const obsd_t *obs, int n, const nav_t *nav,
                 int ephopt, double *rs, double *dts, double *var, int *svh)
    
    • 所在文件:ephemeris.c
    • 功能说明:按照所观测到的卫星顺序计算出每颗卫星的位置、速度、{钟差、频漂}
    • 参数说明:
    函数参数,9个:
    gtime_t  teph      I   time to select ephemeris (gpst)
    obsd_t   *obs      I   观测量数据
    int      n         I   观测量数据的数量
    nav_t    *nav      I   导航数据
    int      ephopt    I   星历选项 (EPHOPT_???)
    double   *rs       O   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      O   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *var      O   卫星位置和钟差的协方差 (m^2)
    int      *svh      O   卫星健康标志 (-1:correction not available)
    返回类型:
    • 处理过程:
    1. 大循环针对每一条观测数据,按顺序处理。
    2. 首先初始化,将对当前观测数据的 rs、dts、var和svh数组的元素置 0。
    3. 通过判断某一频率下信号的伪距是否为 0,来得到此时所用的频率个数。注意,频率个数不能大于 NFREQ(默认为 3)。
    4. 用数据接收时刻减去伪距信号传播时间,得到卫星信号的发射时刻。
    5. 调用 ephclk 函数,由广播星历计算出当前观测卫星与 GPS 时间的钟差 dt。注意,此时的钟差是没有考虑相对论效应和 TGD 的。
    6. 用 4 中的信号发射时刻减去 5 中的钟差 dt,得到 GPS 时间下的卫星信号发射时刻。
    7. 调用 satpos 函数,计算信号发射时刻卫星的位置(ecef,m)、速度(ecef,m/s)、钟差((s|s/s))。注意,这里计算出的钟差是考虑了相对论效应的了,只是还没有考虑 TGD。
    8. 如果没有精密星历,则用广播星历的钟差替代。
    • 注意:
    1. 4中的公式原理:由 ρ=c(tuts)\rho = c(t_u-t_s) 可得 ts=tuρ/ct_s=t_u-\rho/c

    ephclk

    int ephclk(gtime_t time, gtime_t teph, int sat, const nav_t *nav, double *dts)
    
    • 所在文件:ephemeris.c
    • 功能说明:通过广播星历来确定卫星钟差
    • 参数说明:
    函数参数,5个:
    gtime_t  time      I   信号发射时刻
    gtime_t  teph      I   用于选择星历的时刻 (gpst)
    int      sat       I   卫星号 (1-MAXSAT)
    nav_t    *nav      I   导航数据
    double   *dts      O   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    返回类型:
    int                O     (1:ok,0:error)
    
    • 处理过程:
    1. 调用 satsys 函数,根据卫星编号确定该卫星所属的导航系统和该卫星在该系统中的 PRN编号。
    2. 对于 GPS导航系统,调用 seleph 函数来选择最接近 teph 的那个星历。
    3. 调用 eph2clk 函数,通过广播星历和信号发射时间计算出卫星钟差。
    • 注意:
    1. 此时计算出的卫星钟差是没有考虑相对论效应和 TGD的。

    eph2clk

    int eph2clk (gtime_t time, const eph_t *eph)
    
    • 所在文件:ephemeris.c
    • 功能说明:根据信号发射时间和广播星历,计算卫星钟差
    • 参数说明:
    函数参数,2个
    gtime_t  time    I   time by satellite clock (gpst)
    eph_t    *eph    I   broadcast ephemeris
    返回类型:
    double    satellite clock bias (s) without relativeity correction
    
    • 处理过程:
      1. 计算与星历参考时间的偏差 dt = t-toc。
      2. 利用二项式校正计算出卫星钟差,从 dt中减去这部分,然后再进行一次上述操作,得到最终的 dt。(这一部分不知道是为什么?)
      3. 使用二项式校正得到最终的钟差。

    satpos

    int satpos(gtime_t time, gtime_t teph, int sat, int ephopt, const nav_t *nav,
               double *rs, double *dts, double *var, int *svh)
    
    • 所在文件:ephemeris.c
    • 功能说明:计算信号发射时刻卫星的 P(ecef,m)、V(ecef,m/s)、C((s|s/s))
    • 参数说明:
    函数参数,9个:
    gtime_t time      I   time (gpst)
    gtime_t teph      I   用于选择星历的时刻 (gpst)
    int     sat       I   卫星号
    nav_t   *nav      I   导航数据
    int     ephopt    I   星历选项 (EPHOPT_???)
    double  *rs       O   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double  *dts      O   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double  *var      O   卫星位置和钟差的协方差 (m^2)
    int     *svh      O   卫星健康标志 (-1:correction not available)
    返回类型:
    int               O     (1:ok,0:error)
    
    • 处理过程:
    1. 根据不同的星历选项的值调用不同的处理函数,如果星历选项是 EPHOPT_BRDC,调用 ephpos 函数,根据广播星历计算出算信号发射时刻卫星的 P、V、C。如果星历选项是 EPHOPT_PREC,调用 pephpos 函数,根据精密星历和时钟计算信号发射时刻卫星的 P、V、C。
    • 注意:
    1. 此时计算出的卫星钟差考虑了相对论,还没有考虑 TGD。

    ephpos

    int ephpos(gtime_t time, gtime_t teph, int sat, const nav_t *nav,
               int iode, double *rs, double *dts, double *var, int *svh)
    
    • 所在文件:ephemeris.c
    • 功能说明:根据广播星历计算出算信号发射时刻卫星的 P、V、C
    • 参数说明:
    函数参数,9个:
    gtime_t  time      I   transmission time by satellite clock
    gtime_t  teph      I   time to select ephemeris (gpst)
    int      sat       I   卫星号 (1-MAXSAT)
    nav_t    *nav      I   导航数据
    int      iode      I   星历数据期号
    double   *rs       O   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      O   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *var      O   卫星位置和钟差的协方差 (m^2)
    int      *svh      O   卫星健康标志 (-1:correction not available)
    返回类型:
    int                O    (1:ok,0:error)
    
    • 处理过程:
    1. 调用 satsys 函数,确定该卫星所属的导航系统。
    2. 调用 seleph 函数来选择广播星历。
    3. 根据选中的广播星历,调用 eph2pos 函数来计算信号发射时刻卫星的 位置、钟差和相应结果的误差。
    4. 在信号发射时刻的基础上给定一个微小的时间间隔,再次计算新时刻的 P、V、C。与3结合,通过扰动法计算出卫星的速度和频漂。
    • 注意:
    1. 这里是使用扰动法计算卫星的速度和频漂,并没有使用那些位置和钟差公式对时间求导的结果。
    2. 由于是调用的 eph2pos 函数,计算得到的钟差考虑了相对论效应,没有考虑 TGD。

    eph2pos

    void eph2pos(gtime_t time, const eph_t *eph, double *rs, double *dts, double *var)
    
    • 所在文件:ephemeris.c
    • 功能说明:根据广播星历计算出算信号发射时刻卫星的位置和钟差
    • 参数说明:
    函数参数,5个
    gtime_t  time      I   transmission time by satellite clock
    eph_t    *eph      I   广播星历
    double   *rs       O   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      O   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *var      O   卫星位置和钟差的协方差 (m^2)
    返回类型:无
    
    • 处理过程:
    1. 通过卫星轨道半长轴 A 判断星历是否有效,无效则返回。
    2. 计算规化时间 tk。
    3. 根据不同卫星系统设置相应的地球引力常数 mu 和 地球自转角速度 omge。
    4. 计算平近点角 M。
    5. 用牛顿迭代法来计算偏近点角 E。参考 RTKLIB manual P145 (E.4.19)。
    6. 计算升交点角距 u。
    7. 计算摄动校正后的升交点角距 u、卫星矢径长度 r、轨道倾角 i。
    8. 计算升交点赤经 O。
    9. 计算卫星位置存入 rs 中。
    10. 计算卫星钟差,此处考虑了相对论效应,没有考虑 TGD,也没有计算钟漂。
    11. 用 URA 值来标定误差方差,具体对应关系可在 ICD-GPS-200H 20.3.3.3.1.3 SV Accuracy 中找到。

    estpos

    int estpos(const obsd_t *obs, int n, const double *rs, const double *dts,
               const double *vare, const int *svh, const nav_t *nav,
               const prcopt_t *opt, sol_t *sol, double *azel, int *vsat,
               double *resp, char *msg)
    
    • 所在文件:pntpos.c
    • 功能说明:通过伪距实现绝对定位,计算出接收机的位置和钟差,顺带返回实现定位后每颗卫星的{方位角、仰角}、定位时有效性、定位后伪距残差。
    • 参数说明:
    函数参数,13个:
    obsd_t   *obs      I   观测量数据
    int      n         I   观测量数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *vare     I   卫星位置和钟差的协方差 (m^2)
    int      *svh      I   卫星健康标志 (-1:correction not available)
    nav_t    *nav      I   导航数据
    prcopt_t *opt      I   处理过程选项
    sol_t    *sol      IO  solution
    double   *azel     IO  方位角和俯仰角 (rad)
    int      *vsat     IO  卫星在定位时是否有效
    double   *resp     IO  定位后伪距残差 (P-(r+c*dtr-c*dts+I+T))
    char     *msg      O   错误消息
    返回类型:
    int                O   1表示成功,0表示出错
    
    • 处理过程:
    1. 初始化:将 sol->rr 的前 3 项位置信息(ECEF)赋值给 x 数组。
    2. 开始迭代定位计算,首先调用 rescode 函数,计算当前迭代的伪距残差 vv、几何矩阵 HH、伪距残差的方差 var、所有观测卫星的方位角和仰角 azel、定位时有效性 vsat、定位后伪距残差 resp、参与定位的卫星个数 ns 和方程个数 nv。
    3. 确定方程组中方程的个数要大于未知数的个数。
    4. 以伪距残差的标准差的倒数作为权重,对 HHvv 分别左乘权重对角阵,得到加权之后的 HHvv
    5. 调用 lsq 函数,根据 dx=(HHT)1Hvdx=(HH^T)^{-1}HvQ=(HHT)1Q=(HH^T)^{-1},得到当前 x 的修改量 dx 和定位误差协方差矩阵中的权系数阵 QQ
    6. 将 5 中求得的 dx 加入到当前 x 值中,得到更新之后的 x 值。
    7. 如果 5 中求得的修改量 dx 小于截断因子(目前是10410^{-4}),则将 6 中得到的 x 值作为最终的定位结果,对 sol 的相应参数赋值,之后再调用 valsol 函数确认当前解是否符合要求(伪距残差小于某个 χ2\chi^2值和 GDOP 小于某个门限值,参考 RTKLIB Manual P162, E.6.33, E.6.34)。否则,进行下一次循环。
    8. 如果超过了规定的循环次数,则输出发散信息后,返回 0。
    • 注意:
    1. 关于第 1步,如果是第一次定位,即输入的 sol 为空,则 x 初值为 0;如果之前有过定位,则通过 1 中操作可以将上一历元的定位值作为该历元定位的初始值。
    2. 关于定位方程,RTKLIB中的 HH 相当于定位方程解算中的 GTG^T,即 H=GTH=G^T
    3. 关于加权最小二乘,这里的权重值是对角阵,这是建立在假设不同测量值的误差之间是彼此独立的基础上的。大部分资料上这里都是把权重矩阵 W 保留到方程的解的表达式当中,而这里是直接对 H 和 v 分别左乘权重对角阵,得到加权之后的 H 和 v,其表示形式像是没有加权一样。
    4. 解方程时的 dtr 单位是 m,是乘以了光速之后的,解出结果后赋给 sol->dtr 时再除以光速。
    5. sol->time 中存储的是减去接收机钟差后的信号观测时间。

    rescode

    int rescode(int iter, const obsd_t *obs, int n, const double *rs,
                const double *dts, const double *vare, const int *svh,
                const nav_t *nav, const double *x, const prcopt_t *opt,
                double *v, double *H, double *var, double *azel, int *vsat,
                double *resp, int *ns)
    
    • 所在文件:pntpos.c
    • 功能说明:计算当前迭代的伪距残差 v、几何矩阵 H、伪距残差的方差 var、所有观测卫星的方位角和仰角 azel、定位时有效性 vsat、定位后伪距残差 resp、参与定位的卫星个数 ns 和方程个数 nv。
    • 参数说明:
    函数参数,17int      iter      I   迭代次数
    obsd_t   *obs      I   观测量数据
    int      n         I   观测量数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *vare     I   卫星位置和钟差的协方差 (m^2)
    int      *svh      I   卫星健康标志 (-1:correction not available)
    nav_t    *nav      I   导航数据
    double   *x        I   本次迭代开始之前的定位值
    prcopt_t *opt      I   处理过程选项
    double   *v        O   定位方程的右端部分,伪距残差
    double   *H        O   定位方程中的几何矩阵
    double   *var      O   参与定位的伪距残差的方差
    double   *azel     O   对于当前定位值,所有观测卫星的 {方位角、高度角} (2*n)
    int      *vsat     O   所有观测卫星在当前定位时是否有效 (1*n)
    double   *resp     O   所有观测卫星的伪距残差,(P-(r+c*dtr-c*dts+I+T)) (1*n)
    int      *ns       O   参与定位的卫星的个数
    返回类型:
    int                O   定位方程组的方程个数
    
    • 处理过程:
    1. 将之前得到的定位解信息赋值给 rr 和 dtr 数组,以进行关于当前解的伪距残差的相关计算。
    2. 调用 ecef2pos 函数,将上一步中得到的位置信息由 ECEF 转化为大地坐标系。
    3. 将 vsat、azel 和 resp 数组置 0,因为在前后两次定位结果中,每颗卫星的上述信息都会发生变化。
    4. 调用 satsys 函数,验证卫星编号是否合理及其所属的导航系统。
    5. 检测当前观测卫星是否和下一个相邻数据重复;重复则不处理这一条,去处理下一条。
    6. 调用 geodist 函数,计算卫星和当前接收机位置之间的几何距离 rr 和接收机到卫星方向的观测矢量。然后检验几何距离是否 >0。此函数中会进行地球自转影响的校正(Sagnac效应)。
    7. 调用 satazel 函数,计算在接收机位置处的站心坐标系中卫星的方位角和仰角;若仰角低于截断值,不处理此数据。
    8. 调用 prange 函数,得到经过DCB校正后的伪距值 ρ\rho
    9. 可以在处理选项中事先指定定位时排除哪些导航系统或卫星,这是通过调用 satexclude 函数完成的。
    10. 调用 ionocorr 函数,计算电离层延时 II (m)。所得的电离层延时是建立在 L1 信号上的,当使用其它频率信号时,依据所用信号频组中第一个频率的波长与 L1 波长的关系,对上一步得到的电离层延时进行修正。
    11. 调用 tropcorr 函数,计算对流层延时 TT (m)。
    12. ρ(r+dtrcdts+I+T)\rho-(r+dt_r-c·dt_s+I+T),计算出此时的伪距残差。
    13. 组装几何矩阵 HH,前 3 行为 6 中计算得到的视线单位向量的反向,第 4 行为 1,其它行为 0。
    14. 处理不同系统(GPS、GLO、GAL、CMP)之间的时间偏差,修改矩阵 HH
    15. 将参与定位的卫星的定位有效性标志设为 1,给当前卫星的伪距残差赋值,参与定位的卫星个数 ns 加 1。
    16. 调用 varerr 函数,计算此时的导航系统误差,然后累加计算用户测距误差(URE)。
    17. 为了防止不满秩的情况,把矩阵 HH 补满秩了。
    • 注意:
    1. 输入参数x的size为7*1,前3个是本次迭代开始之前的定位值,第4个是钟差,后三个分别是gps系统与glonass、galileo、北斗系统的钟差。但是从代码看,后三个没有用上。
    2. 返回值 v和 resp的主要区别在于长度不一致, v是需要参与定位方程组的解算的,维度为 nv*1;而 resp仅表示所有观测卫星的伪距残余,维度为 n*1,对于没有参与定位的卫星,该值为 0。

    raim_fde

    int raim_fde(const obsd_t *obs, int n, const double *rs,
                 const double *dts, const double *vare, const int *svh,
                 const nav_t *nav, const prcopt_t *opt, sol_t *sol,
                 double *azel, int *vsat, double *resp, char *msg)
    
    • 所在文件:pntpos.c
    • 功能说明:使用伪距残差判决法对计算得到的定位结果进行接收机自主正直性检测(RAIM),每次舍弃一颗卫星测量值,用剩余的值组成一组进行定位运算,选择定位后伪距残差最小的一组作为最终结果。这样如果只有一个异常观测值的话,这个错误可以被排除掉;有两个或以上错误则排除不了。注意这里只会在对定位结果有贡献的卫星数据进行检测。
    • 参数说明:
    函数参数,13个:
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double   *vare     I   卫星位置和钟差的协方差 (m^2)
    int      *svh      I   卫星健康标志 (-1:correction not available)
    nav_t    *nav      I   导航数据
    prcopt_t *opt      I   处理过程选项
    sol_t    *sol      IO  solution
    double   *azel     IO  方位角和俯仰角 (rad)
    int      *vsat     IO  卫星在定位时是否有效
    double   *resp     IO  定位后伪距残差 (P-(r+c*dtr-c*dts+I+T))
    char     *msg      O   错误消息
    返回类型:
    int                O   (1:ok,0:error)
    
    • 处理过程:
    1. 大循环是每次舍弃第 i 颗卫星。
    2. 舍弃第 i 颗卫星后,将剩下卫星的数据复制到一起,调用 estpos 函数计算使用剩下卫星进行定位的定位值。
    3. 累加使用当前卫星实现定位后的伪距残差平方和与可用卫星数目,如果 nvsat<5,则说明当前卫星数目过少,无法进行 RAIM_FDE 操作。
    4. 计算伪距残差平方和的标准差,如果小于 rms,则说明当前定位结果更合理,将 stat 置为 1,重新更新 sol、azel、vsat(当前被舍弃的卫星,此值置为0)、resp等值,并将当前的 rms_e更新到 rms 中。
    5. 继续弃用下一颗卫星,重复 2-4 操作。总而言之,将同样是弃用一颗卫星条件下,伪距残差标准平均值最小的组合所得的结果作为最终的结果输出。
    6. 如果 stat不为 0,则说明在弃用卫星的前提下有更好的解出现,输出信息,指出弃用了哪颗卫星。
    • 注意:
    1. 源码中有很多关于 i、j、k的循环。其中,i表示最外面的大循环,每次将将第 i颗卫星舍弃不用,这是通过 if (j==i) continue实现的;j表示剩余使用的卫星的循环,每次进行相应数据的赋值;k表示参与定位的卫星的循环,与 j一起使用。

    estvel

    void estvel(const obsd_t *obs, int n, const double *rs, const double *dts,
                const nav_t *nav, const prcopt_t *opt, sol_t *sol,
                const double *azel, const int *vsat)
    
    • 所在文件:pntpos.c
    • 功能说明:依靠多普勒频移测量值计算接收机的速度,用牛顿迭代法。
    • 参数说明:
    函数参数,9个:
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    nav_t    *nav      I   导航数据
    prcopt_t *opt      I   处理过程选项
    sol_t    *sol      IO  solution
    double   *azel     IO  方位角和俯仰角 (rad)
    int      *vsat     IO  卫星在定位时是否有效
    返回类型:
    int                O     (1:ok,0:error)
    
    • 处理过程:
    1. 在最大迭代次数限制内,调用 resdop,计算定速方程组左边的几何矩阵和右端的速度残余,返回定速时所使用的卫星数目。
    2. 调用最小二乘法 lsq 函数,解出{速度、频漂}的步长 dx,累加到 x 中。
    3. 检查当前计算出的步长的绝对值是否小于 1E-6。是,则说明当前解已经很接近真实值了,将接收机三个方向上的速度存入到 sol->rr 中;否,则进行下一次循环。
    • 注意:
    1. 最终向 sol_t 类型存储定速解时,并没有存储所计算出的接收器时钟频漂。
    2. 这里不像定位时,初始值可能为上一历元的位置(从 sol 中读取初始值),这里定速的初始值直接给定为 0.

    resdop

    int resdop(const obsd_t *obs, int n, const double *rs, const double *dts,
               const nav_t *nav, const double *rr, const double *x,
               const double *azel, const int *vsat, double *v, double *H)
    
    • 所在文件:pntpos.c
    • 功能说明:计算定速方程组左边的几何矩阵和右端的速度残余,返回定速时所使用的卫星数目
    • 参数说明:
    函数参数,11个:
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    nav_t    *nav      I   导航数据
    double   *rr       I   接收机位置和速度,长度为6{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *x        I   本次迭代开始之前的定速值,长度为4{vx,vy,vz,drift}
    double   *azel     IO  方位角和俯仰角 (rad)
    int      *vsat     I   卫星在定速时是否有效
    double   *v        O   定速方程的右端部分,速度残差
    double   *H        O   定速方程中的几何矩阵
    返回类型:
    int                O   定速时所使用的卫星数目
    
    • 处理过程:
    1. 调用 ecef2pos 函数,将接收机位置由 ECEF 转换为大地坐标系。
    2. 调用 xyz2enu 函数,计算此时的坐标转换矩阵。
    3. 去除在定速时不可用的卫星。
    4. 计算当前接收机位置下 ENU中的视向量,然后转换得到 ECEF 中视向量的值。
    5. 计算 ECEF 中卫星相对于接收机的速度。
    6. 计算考虑了地球自转的用户和卫星之间的几何距离变化率,校正公式见 RTKLIB manual P159 (F.6.29),此公式可由 P140 (E.3.8b) 对时间求导得到。
    7. 根据公式计算出定速方程组右端项的多普勒残差,构建左端项的几何矩阵,最后再将观测方程数增 1.
    • 注意:
    1. 这里与定位不同,构建几何矩阵时,就只有 4个未知数,而定位时是有 NX个。
    2. 多普勒定速方程中几何矩阵 G 与定位方程中的一样。
    3. 第7步中计算多普勒残差 b 与很多资料不同,因为 estvel 中用的是牛顿迭代法,其最小二乘法并不是直接求解x,而是求解dx,再加到x上。下面式子可以参考,其中 xryrx_r、y_r 分别为接收机位置的x,y分量,xsysx_s、y_s 分别为卫星s位置的x,y分量,rsr_s 为接收机到卫星s的距离。注意在计算 G 的时候忽略了地球自转校正项。
      x=[v,cδt˙]T=[vx,vy,vz,cδt˙]T \boldsymbol{x} = [\boldsymbol{v},\:c\cdot\dot{\delta_t}]^T=[v_x,\:v_y,\:v_z,\:c\cdot\dot{\delta_t}]^T

    rs=(vsv)es+ωec(vs,yxr+ysvxvs,xyrxsvy) r_{s} = (\boldsymbol{v_s}-\boldsymbol{v})\cdot\boldsymbol{e_s} + \frac{\omega_e}{c}(v_{s,y}x_r+y_sv_x-v_{s,x}y_r-x_sv_y)

    y=λfd=h(x)=rs+cδt˙cδ˙t,s y = -\lambda f_d = h(x) = r_{s} + c\cdot\dot{\delta_t} - c\cdot\dot{\delta}_{t,s}

    G=hx=[e1,k1e2,k1e3,k1e4,k1] \boldsymbol{G} = \frac{\partial h}{\partial x} = \left[ \begin{array}{cc} -\boldsymbol{e}_{1,k} & 1\\ -\boldsymbol{e}_{2,k} & 1\\ -\boldsymbol{e}_{3,k} & 1\\ -\boldsymbol{e}_{4,k} & 1 \end{array} \right]

    b=yh(x)=λfd(rs+cδt˙cδ˙t,s) b = y-h(x) = -\lambda f_d - (r_{s} + c\cdot\dot{\delta_t} - c\cdot\dot{\delta}_{t,s})

    pppos

    void pppos(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
    
    • 所在文件:ppp.c
    • 功能说明:精确点定位
    • 参数说明:
    函数参数,4个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    nav_t    *nav      I   导航数据
    返回类型:
    • 处理过程:
    1. 调用 udstate_ppp 更新状态值 rtk->x 及其误差协方差 rtk->P。
    2. 调用 satposs 计算卫星位置和钟差。
    3. 若有需要排除观测的卫星,调用 testeclipse 进行排除。
    4. 根据设置的滤波迭代次数,循环调用 res_ppp 计算载波相位和伪距残差 v 和观测矩阵 H,以及测量误差的协方差 R,并将残差值存入 rtk->ssat[sat-1].resc 和 rtk->ssat[sat-1].resp 中。然后调用 filter 进行 Kalman 滤波运算。
    5. 4 中循环结束后,再次调用 res_ppp,更新残差值,并将残差值存入 rtk->ssat[sat-1].resc 和 rtk->ssat[sat-1].resp 中。
    6. 对某些周整模糊度计算模式,调用 pppamb 进行周整模糊度解算。
    7. 更新 solution 状态。
    • 注意:
    1. 状态变量包含接收机位置、接收机速度、接收机钟差、[对流层参数]、每颗卫星的载波偏移。(参考 RTKLIB Manual P177 E.8.16)。其中载波偏移包含周整模糊度以及小数部分,可参考 RTKLIB Manual P139 E.3.5。
    2. 过程5中的目的是在过程6中的某些周整模糊度计算中会用上,以及一些输出中可能会用上。

    udstate_ppp

    void udstate_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
    
    • 所在文件:ppp.c
    • 功能说明:更新状态值 rtk->x。
    • 参数说明:
    函数参数,4个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    nav_t    *nav      I   导航数据
    返回类型:
    • 处理过程:
    1. 调用 udpos_ppp 根据不同模式初始化状态 rtk->x 中的位置值。
    2. 调用 udclk_ppp 初始化状态 rtk->x 中的钟差值(6个,因有6个系统)。
    3. 对某些对流层模型,初始化状态 rtk->x 中的对流层参数。
    4. 调用 udbias_ppp 更新载波相位偏移状态值以及其误差协方差。
    • 注意:
    1. 状态变量包含接收机位置、接收机速度、接收机钟差、[对流层参数]、每颗卫星的载波偏移。(参考 RTKLIB Manual P177 E.8.16)。其中载波偏移包含周整模糊度以及小数部分,可参考 RTKLIB Manual P139 E.3.5。
    2. 处理过程 1 的 udpos_ppp 中,PPP-static 模式只用 rtk->sol.rr 初始化一次状态 rtk->x,而 PPP-Kinematic 模式每次都会用 rtk->sol.rr 重新初始化 rtk->x。而 rtk->sol.rr 的值来源于单点定位。
    3. 处理过程 1 的 udpos_ppp 中,PPP-Fixed 模式只用于残差分析,不用于定位。

    udbias_ppp

    void udbias_ppp(rtk_t *rtk, const obsd_t *obs, int n, const nav_t *nav)
    
    • 所在文件:ppp.c
    • 功能说明:从观测值 obs 经过计算得到偏移值存入状态 rtk->x 中,若有周跳需要更新状态 rtk->x。更新偏移值的误差协方差到 rtk->P。
    • 参数说明:
    函数参数,4个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    nav_t    *nav      I   导航数据
    返回类型:
    • 处理过程:
    1. 调用 detslp_ll 通过 LLI 检查是否有周跳。
    2. 调用 detslp_gf 通过 geometry-free phase jump 检查是否有周跳。
    3. 若观测中断需要 reset 载波偏移值。
    4. 接收机位置转换到大地坐标系。
    5. 对每一组观测数据,调用 corrmeas 计算观测值 meas,是否有周跳,然后计算出偏移值 bias。
    6. 若载波和伪距跳变太大,为了保持一致性,需要进行校正。
    7. 对每一组观测值更新偏移值的误差协方差到 rtk->P。若有周跳,或者 rtk->x 状态还未初始化,则用偏移值 bias 重置 rtk->x。

    res_ppp

    int res_ppp(int iter, const obsd_t *obs, int n, const double *rs,
                const double *dts, const double *vare, const int *svh,
                const nav_t *nav, const double *x, rtk_t *rtk, double *v,
                double *H, double *R, double *azel)
    
    • 所在文件:ppp.c
    • 功能说明:计算载波相位和伪距残差 v 和观测矩阵 H,以及测量误差的协方差 R。并将残差值存入 rtk->ssat[sat-1].resc 和 rtk->ssat[sat-1].resp 中。
    • 参数说明:
    函数参数:14int     iter    I   迭代次数(该函数中没有用到)   
    obsd_t  *obs    I   观测数据
    int      n      I   观测数据的数量
    double  *rs     I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double  *dts    I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    double  *vare   I   卫星位置和钟差的协方差 (m^2)
    int     *svh    I   卫星健康标志 (-1:correction not available)
    nav_t   *nav    I   导航数据
    double  *x      I   状态变量
    rtk_t   *rtk    IO  rtk控制结构体
    double  *v      O   实际观测量与预测观测量的残差 (2n个,因每个观测数据有phase和code两个观测值)
    double  *H      O   观测矩阵
    double  *R      O   测量误差的协方差
    double  *azel   O   方位角和俯仰角 (rad)
    返回类型:
    int             O   残差个数,<=0表示失败
    
    • 处理过程
    1. 接收机位置传给 rr。
    2. 若需要地球潮校正,调用 tidedisp 对 rr 进行校正。地球潮包含固体潮、极潮和海潮负荷。
    3. rr 转换坐标系到大地坐标系 pos。
    4. 大循环,对每一个观测量
    5. 计算卫星到接收机的几何距离 r,计算仰角,若仰角低于阈值,排除此观测量。
    6. 若设置有卫星需要排除的,排除掉。
    7. 根据对流层模型设置,计算对流层延时校正值 dtrp。
    8. 调用 satantpcv 根据卫星天线模型计算校正值 dants。
    9. 调用 antmodel 根据接收机天线模型计算校正值 dantr。
    10. 调用 windupcorr 计算相位缠绕校正值存入 rtk->ssat[sat-1].phw 中。
    11. 调用 corrmeas 计算经过电离层和天线相位校正后的测量值 meas(包含相位和伪距两个值),把 8~10 步中的计算值都合并到 meas 中了。
    12. 对几何距离 r 进行卫星钟差和对流层校正。
    13. 构造残差 v 和观测矩阵 H。v 由经过电离层和天线相位校正后的测量值 meas 减去经过卫星钟差和对流层校正后的 r,再减去接收机钟差(若 meas 为载波,再减去载波偏移)得到。 H 参考 RTKLIB Manual P177 E.8.21。并将残差值存入 rtk->ssat[sat-1].resc 和 rtk->ssat[sat-1].resp 中。
    14. 重复 5~13 直到大循环结束。
    15. 计算测量误差的协方差 R。
    • 注意:
    1. 状态变量包含接收机位置、接收机速度、接收机钟差、[对流层参数]、载波偏移。(参考 RTKLIB Manual P177 E.8.16)。其中载波偏移包含周整模糊度以及小数部分,可参考 RTKLIB Manual P139 E.3.5。

    satantpcv

    void satantpcv(const double *rs, const double *rr, const pcv_t *pcv,
                    double *dant)
    
    • 所在文件:ppp.c
    • 功能说明:根据模型计算卫星天线偏移校正值dant。
    • 参数说明:
    函数参数:4double   *rs     I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    double   *rr     I   接收机位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    pcv_t    *pcv    I   天线相位中心参数结构体
    double   *dant   O   卫星天线校正值
    返回类型:无
    
    • 处理过程:
    1. 根据卫星位置和接收机位置计算天底角。
    2. 调用 antmodel_s 计算卫星天线偏移校正值 dant。
    • 注意:
    1. antmodel_s 中的计算只是对 phase center variation 进行了简单的插值,并没有考虑 phase center offset。这里存疑!?
    2. 类似的对接收机天线偏移进行校正计算的 antmodel 函数中则不仅考虑了phase center variation,也同时考虑了phase center offset。

    antmodel

    void antmodel(const pcv_t *pcv, const double *del, const double *azel,
                  int opt, double *dant)
    
    • 所在文件:rtkcmn.c
    • 功能说明:根据模型计算接收机天线偏移校正值dant。
    • 参数说明:
    函数参数:5个
    pcv_t    *pcv    I   天线相位中心参数结构体
    double   *del    I   相对天线参考点偏移值
    double   *azel   I   方位角和俯仰角
    int       opt    I   选项(非0则需要考虑pcv)
    double   *dant   O   接收机天线校正值
    返回类型:无
    
    • 处理过程:
    1. 根据方位角和俯仰角计算单位观测矢量。
    2. 对每一个频率,先计算偏移,再根据opt值看是否要加上pcv的影响,最后得出dant。

    windupcorr

    void windupcorr(gtime_t time, const double *rs, const double *rr,
                    double *phw)
    
    • 所在文件:rtkcmn.c
    • 功能说明:根据模型计算相位缠绕校正值 phw。
    • 参数说明:
    函数参数:4个
    gtime_t time     I   time (GPST)
    double  *rs      I   卫星位置 (ecef) {x,y,z} (m)
    double  *rr      I   接收机位置 (ecef) {x,y,z} (m)
    double  *phw     IO  相位缠绕校正值 (cycle)
    返回类型:无
    
    • 处理过程:
    1. 调用 sunmoonpos 获取太阳的位置。
    2. 计算卫星到接收机的单位矢量 ek。
    3. 计算卫星天线坐标系的三个轴的单位矢量 exs, eys, ezs。
    4. 计算站点坐标系的三个轴的单位矢量 exr, eyr。
    5. 计算有效偶极 ds 和 dr。
    6. 计算相位缠绕校正值 phw。
    • 注意:
    1. 使用这个函数时需要传入之前的相位缠绕校正值,因为此函数假设校正值不会跳变超过0.5个载波周期。

    corrmeas

    int corrmeas(const obsd_t *obs, const nav_t *nav, const double *pos,
                const double *azel, const prcopt_t *opt,
                const double *dantr, const double *dants, double phw,
                double *meas, double *var, int *brk)
    
    • 所在文件:ppp.c
    • 功能说明:计算经过电离层、DCB、卫星天线、接收机天线、相位缠绕校正后的测量值 meas(包含相位和伪距两个值)。
    • 参数说明:
    函数参数:11个
    obsd_t   *obs    I   观测数据
    nav_t    *nav    I   导航数据
    double   *pos    I   接收机位置 (lat,lon,h)(rad,m)
    double   *azel   I   方位角和俯仰角 (rad)
    prcopt_t *opt    I   处理过程选项
    double   *dantr  I   接收机天线校正值
    double   *dants  I   卫星天线校正值
    double   phw     I   相位缠绕校正值
    double   *meas   O   校正后的测量值
    double   *var    O   校正后的测量值的误差协方差
    int      *brk    O   用与判断是否有周跳
    返回类型:
    int              O   (>0:ok,0:sth wrong)
    
    • 处理过程:
    1. 若电离层校正模式为 iono-free LC,调用 ifmeas 计算 meas 值,然后返回。否则进行下面的步骤。
    2. 调用 testsnr 看观测值的信噪比是否过低,过低则忽略此观测值。
    3. 进行 DCB 校正。
    4. 调用 corr_ion 计算 slant ionospheric delay 值 ion。
    5. 合并各种校正值得到 meas 值。

    ifmeas

    int ifmeas(const obsd_t *obs, const nav_t *nav, const double *azel,
              const prcopt_t *opt, const double *dantr, const double *dants,
              double phw, double *meas, double *var)
    
    • 所在文件:ppp.c
    • 功能说明:计算经过电离层、DCB、卫星天线、接收机天线、相位缠绕校正后的测量值 meas(包含相位和伪距两个值)。电离层延时计算需要双频点数据。
    • 参数说明:
    函数参数:9个
    obsd_t   *obs    I   观测数据
    nav_t    *nav    I   导航数据
    double   *azel   I   方位角和俯仰角 (rad)
    prcopt_t *opt    I   处理过程选项
    double   *dantr  I   接收机天线校正值
    double   *dants  I   卫星天线校正值
    double   phw     I   相位缠绕校正值
    double   *meas   O   校正后的测量值
    double   *var    O   校正后的测量值的误差协方差
    返回类型:
    int              O   (>0:ok,0:sth wrong)
    
    • 处理过程:
    1. 选择所用频段,没有足够可用的频段则返回。
    2. 调用 testsnr 看观测值的信噪比是否过低,过低则返回。
    3. 按公式计算 γ,c1,c2\gamma, c_1, c_2, 获取L1, L2, P1, P2, P1_C1, P1_P2的值。
    4. 按公式计算电离层校正和相位缠绕校正后的载波测量值。
    5. 按公式计算电离层校正和DCB校正后的伪距测量值。
    6. 若有SBAS,加上SBAS钟差校正到伪距测量值。
    7. 若有GLONASS,加上GPS和GLONASS的硬件偏差校正到伪距测量值。
    8. 加上卫星天线、接收机天线校正值到载波测量值和伪距测量值。

    filter

    int filter(double *x, double *P, const double *H, const double *v,
              const double *R, int n, int m)
    
    • 所在文件:rtkcmn.c
    • 功能说明:kalman滤波运算
    • 参数说明:
    函数参数,7个:
    double   *x        IO 状态变量 (n x 1)
    double   *P        IO 状态变量的误差协方差阵 (n x n)
    double   *H        I  观测矩阵的转置 (n x m)
    double   *v        I  实际观测量与预测观测量的残差 (measurement - model) (m x 1)
    double   *R        I  测量误差的协方差 (m x m)
    int      n         I  状态变量个数
    int      m         I  观测值个数
    返回类型:
    int                O (0:ok,<0:error)
    
    • 处理过程:
    1. 选择需要更新的状态 x 和对应的 P、H 到 x_、p_、H_ 中。
    2. 调用 filter_ 进行kalman滤波更新。
    3. 将更新值存到 x、P中。
    • 注意:
    1. 若状态 x[i]==0.0, 则不会更新 x[i] 和 P[i+i*n]

    filter_

    int filter_(const double *x, const double *P, const double *H,
               const double *v, const double *R, int n, int m,
               double *xp, double *Pp)
    
    • 所在文件:rtkcmn.c
    • 功能说明:kalman滤波运算,K=PH(HTPH+R)1,xp=x+Kv,Pp=(IKHT)PK=PH(H^TPH+R)^{-1}, xp=x+Kv, Pp=(I-KH^T)P
    • 参数说明:
    函数参数,9个:
    double   *x        I  状态变量 (n x 1)
    double   *P        I  状态变量的误差协方差阵 (n x n)
    double   *H        I  观测矩阵的转置 (n x m)
    double   *v        I  实际观测量与预测观测量的残差 (measurement - model) (m x 1)
    double   *R        I  测量误差的协方差 (m x m)
    int      n         I  状态变量个数
    int      m         I  观测值个数
    double   *xp       O  更新后的状态变量 (n x 1)
    double   *Pp       O  更新后的状态变量的误差协方差阵 (n x n)
    返回类型:
    int                O (0:ok,<0:error)
    
    • 处理过程:
    1. 调用矩阵运算函数按照公式进行矩阵运算
    • 注意:
    1. 矩阵是按列优先的顺序存的 (fortran convention)

    relpos

    int relpos(rtk_t *rtk, const obsd_t *obs, int nu, int nr, const nav_t *nav)
    
    • 所在文件:rtkpos.c
    • 功能说明:相对定位
    • 参数说明:
    函数参数,5个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      nu        I   接收机观测数据的数量
    int      nr        I   基站观测数据的数量
    nav_t    *nav      I   导航数据
    返回类型:
    int                O   (1:ok,0:error)
    
    • 处理过程:
    1. 一系列值、状态、中间变量的初始化。
    2. 调用 satposs 计算卫星们的位置、速度和钟差。
    3. 调用 zdres 计算基站的没有差分的相位/码残差,若出错则返回0。
    4. 若为后处理,需要插值的,调用 intpres 进行插值。
    5. 调用 selsat 选择接收机与基站共同观测的卫星,返回共同观测的卫星个数,输出卫星号列表sat、在接收机观测值中的index值列表 iu 和在基站观测值中的index值列表 ir。
    6. 调用 udstate 更新状态值 rtk->x 及其误差协方差 rtk->P。
    7. 按迭代次数循环第8~10步。
    8. 调用 zdres 计算接收机的没有差分的相位/码残差。
    9. 调用 ddres 计算双差相位/码残差。
    10. 调用 filter 进行 Kalman 滤波运算。
    11. 再次调用 zdresddres 计算双差相位/码残差,调用 valpos 进行验证,若通过则更新 rtk->x 以及 rtk->P,并更新模糊度控制结构体。
    12. 根据选项模式调用不同的 resamb_* 函数解析周整模糊度。
    13. 保存solution状态。

    zdres

    int zdres(int base, const obsd_t *obs, int n, const double *rs,
              const double *dts, const int *svh, const nav_t *nav,
              const double *rr, const prcopt_t *opt, int index, double *y,
              double *e, double *azel)
    
    • 所在文件:rtkpos.c
    • 功能说明:计算接收机或基站的没有差分的相位/码残差(Zero-Difference Residuals)
    • 参数说明:
    函数参数,13个:
    int      base      I   0表示接收机,1表示基站
    obsd_t   *obs      I   观测数据
    int      n         I   观测数据的数量
    double   *rs       I   卫星位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)   
    double   *dts      I   卫星钟差,长度为2*n, {bias,drift} (s|s/s)
    int      *svh      I   卫星健康标志 (-1:correction not available)   
    nav_t    *nav      I   导航数据
    double   *rr       I   接收机/基站的位置和速度,长度为6*n,{x,y,z,vx,vy,vz}(ecef)(m,m/s)
    prcopt_t *opt      I   处理过程选项
    int      index     I   0表示接收机,1表示基站,与参数 base 重复了
    double   *y        O   相位/码残差
    double   *e        O   观测矢量 (ecef)
    double   *azel     O   方位角和俯仰角 (rad)
    返回类型:
    int                O   (1:ok,0:error)
    
    • 处理过程:
    1. 若没有接收机位置,返回0。
    2. 接收机位置传给 rr_。
    3. 若需要地球潮校正,调用 tidedisp 对 rr_ 进行校正。地球潮包含固体潮、极潮和海潮负荷。
    4. rr_ 转换坐标系到大地坐标系 pos。
    5. 大循环,对每一个观测量
    6. 调用 geodist 计算卫星到接收机的几何距离 r,调用 satazel 计算仰角,若仰角低于阈值,排除此观测量。
    7. 若设置有卫星需要排除的,排除掉。
    8. 根据卫星钟差校正 r。
    9. 根据对流层模型设置,调用 tropmodel 和 tropmapf 计算对流层延时校正值并校正 r。
    10. 根据接收机天线模型调用 antmodel 计算校正值 dant(对每一个频率都有一个值)。
    11. 调用 zdres_sat 计算没有差分的相位/码残差 y。
    12. 重复6~11直到大循环结束。

    zdres_sat

    void zdres_sat(int base, double r, const obsd_t *obs, const nav_t *nav,
                  const double *azel, const double *dant,
                  const prcopt_t *opt, double *y)
    
    • 所在文件:rtkpos.c
    • 功能说明:计算接收机或基站对某一颗卫星的没有差分的相位/码残差(Zero-Difference Residuals)。y = 观测值 - r - dant。
    • 参数说明:
    函数参数,13个:
    int      base      I   0表示接收机,1表示基站
    double   r         I   经过钟差和对流层校正后的几何距离。
    obsd_t   *obs      I   观测数据
    nav_t    *nav      I   导航数据
    double   *azel     I   方位角和俯仰角 (rad)
    double   *dant     I   接收机天线校正值
    prcopt_t *opt      I   处理过程选项
    double   *y        O   相位/码残差
    返回类型:
    • 处理过程:
    1. 按电离层校正模式是否为 IONOOPT_IFLC 分两种情况。
    2. 若是:检测信噪比,计算天线校正值 dant_if,然后计算残差。
    3. 若否:检测信噪比,然后计算残差。

    tidedisp

    void tidedisp(gtime_t tutc, const double *rr, int opt, const erp_t *erp,
                  const double *odisp, double *dr)
    
    • 所在文件:ppp.c
    • 功能说明:计算因地球潮汐而引起的站点位移校正值 dr。
    • 参数说明:
    函数参数,6个:
    gtime_t  tutc     I   time in utc
    double  *rr       I   站点位置 (ecef) (m)
    int      opt      I   选项(指定包含哪些潮的影响)
                          1: solid earth tide
                          2: ocean tide loading
                          4: pole tide
                          8: elimate permanent deformation
    double  *erp      I   地球自转参数
    double  *odisp    I   海潮负荷参数
                          odisp[0+i*6]: consituent i amplitude radial(m)
                          odisp[1+i*6]: consituent i amplitude west  (m)
                          odisp[2+i*6]: consituent i amplitude south (m)
                          odisp[3+i*6]: consituent i phase radial  (deg)
                          odisp[4+i*6]: consituent i phase west    (deg)
                          odisp[5+i*6]: consituent i phase south   (deg)
                          (i=0:M2,1:S2,2:N2,3:K2,4:K1,5:O1,6:P1,7:Q1,8:Mf,9:Mm,10:Ssa)
    double  *dr       O   因地球潮汐而引起的站点位移校正值 (ecef) (m)
    返回类型:
    • 处理过程:
    1. 若有erp,调用 geterp 获取erp参数。
    2. 若有选项,调用 tide_solid 计算固体潮。
    3. 若有选项,调用 tide_oload 计算海潮负荷。
    4. 若有选项,调用 tide_pole 计算极潮。

    geterp

    int geterp(const erp_t *erp, gtime_t time, double *erpv)
    
    • 所在文件:rtkcmn.c
    • 功能说明:获取ERP参数值。
    • 参数说明:
    函数参数,3个:
    erp_t  *erp        I   earth rotation parameters
    gtime_t time       I   time (gpst)
    double *erpv       O   erp values {xp,yp,ut1_utc,lod} (rad,rad,s,s/d)
    返回类型:
    int                O   (1:ok,0:error)
    
    • 处理过程:
    1. 计算当前时间与ERP参数中时间的插值。
    2. 若当前时间早于ERP参数中最早的时间,采用最早的时间来计算。
    3. 若当前时间晚于ERP参数中最晚的时间,采用最晚的时间来计算。
    4. 若当前时间在ERP参数中最早与最晚的时间之间,则先找到最接近的两个时间,然后用插值。

    udstate

    void udstate(rtk_t *rtk, const obsd_t *obs, const int *sat,
                const int *iu, const int *ir, int ns, const nav_t *nav)
    
    • 所在文件:rtkpos.c
    • 功能说明:更新状态值 rtk->x 及其误差协方差 rtk->P。
    • 参数说明:
    函数参数,7个:
    rtk_t    *rtk      IO  rtk控制结构体
    obsd_t   *obs      I   观测数据
    int      sat       I   接收机和基站共同观测的卫星号列表
    int      *iu       I   接收机和基站共同观测的卫星在接收机观测值中的index值列表
    int      *ir       I   接收机和基站共同观测的卫星在基站观测值中的index值列表
    int      ns        I   接收机和基站共同观测的卫星个数
    nav_t    *nav      I   导航数据
    返回类型:
    • 处理过程:
    1. 调用 udpos 根据不同模式更新rtk中的位置、速度、加速度值和协方差。
    2. 若 电离层模式>=IONOOPT_EST,调用 udion 更新状态 rtk->x 中的电离层参数(MAXSAT个)及其协方差。
    3. 若 对流层模式>=TROPOPT_EST,调用 udtrop 更新状态 rtk->x 中的对流层参数(2或6个)及其协方差。TROPOPT_EST初始化状态 rtk->x 中的对流层参数。
    4. 若为 GLONASS AR模式,调用 udrcvbias 更新接收机硬件偏移。
    5. 若 模式>PMODE_DGPS,调用 udbias 更新载波相位偏移状态值以及其误差协方差。
    • 注意:
    1. 状态变量包含接收机位置、速度、加速度值、[每颗卫星的电离层参数]、[对流层参数]、[接收机硬件偏移]、每颗卫星的载波偏移。其中载波偏移包含周整模糊度以及小数部分,可参考 RTKLIB Manual P139 E.3.5。
    2. 过程2、3中更新状态x,只根据需要作初始化,给初值;更新时只更新协方差。

    udpos

    void udpos(rtk_t *rtk, double tt)
    
    • 所在文件:rtkpos.c
    • 功能说明:更新rtk中的位置、速度、加速度值和协方差。
    • 参数说明:
    函数参数,2个:
    rtk_t    *rtk      IO  rtk控制结构体
    double   tt        I   本次更新与上次更新的时间差
    返回类型:
    • 处理过程:
    1. 若为 PMODE_FIXED 模式,直接从选项中取得位置值给rtk->x,然后返回。
    2. 若为第一个历元,用rtk->sol中的位置值初始化rtk->x。若为dynamics模式(即需要估计速度和加速度),一并初始化。
    3. 若为 PMODE_STATIC 模式,返回。
    4. 若非dynamics模式,用rtk->sol中的位置值初始化rtk->x,然后返回。
    5. 检查位置协方差,若大于阈值VAR_POS则用rtk->sol中的位置值重置rtk->x
    6. 根据Kalman滤波的预测方程 x=Fx 和 P=FP*F+Q 更新(参考RTKLIB Manual P161 E.7.4, E.7.5)。其中更新Q时需要坐标转换。

    udbias

    void udbias(rtk_t *rtk, double tt, const obsd_t *obs, const int *sat,
                const int *iu, const int *ir, int ns, const nav_t *nav)
    
    • 所在文件:rtkpos.c
    • 功能说明:更新载波相位偏移状态值到 rtk->x 以及更新其误差协方差到 rtk->P。
    • 参数说明:
    函数参数,8个:
    rtk_t    *rtk      IO  rtk控制结构体
    double   tt        I   本次更新与上次更新的时间差
    obsd_t   *obs      I   观测数据
    int      sat       I   接收机和基站共同观测的卫星号列表
    int      *iu       I   接收机和基站共同观测的卫星在接收机观测值中的index值列表
    int      *ir       I   接收机和基站共同观测的卫星在基站观测值中的index值列表
    int      ns        I   接收机和基站共同观测的卫星个数
    nav_t    *nav      I   导航数据
    返回类型:
    • 处理过程:
    1. 对每一颗卫星,循环2~4步。
    2. 调用 detslp_ll 通过 LLI 检查接收机和基站观测数据是否有周跳。
    3. 调用 detslp_gf_L1L2 和 detslp_gf_L1L5 通过 geometry-free phase jump 检查是否有周跳。
    4. 调用 detslp_dop 通过多普勒和相位差检查接收机和基站观测数据是否有周跳。
    5. 对每一个频率,循环6~10步。
    6. 若为instantaneous AR 模式或者超出 obs outage counter,重置载波偏移值。
    7. 若检测到周跳,重置载波偏移值。
    8. 用 phase - code 的值 (ϕρλ\phi - \frac{\rho}{\lambda}) 来估计载波偏移值。
    9. 校正载波偏移值以保持载波与伪距的一致性。
    10. 更新载波偏移值及其误差协方差。

    ddres

    int ddres(rtk_t *rtk, const nav_t *nav, double dt, const double *x,
              const double *P, const int *sat, double *y, double *e,
              double *azel, const int *iu, const int *ir, int ns, double *v,
              double *H, double *R, int *vflg)
    
    • 所在文件:rtkpos.c
    • 功能说明:计算接收机或基站的双差相位/码残差(Double-Difference Residuals)
    • 参数说明:
    函数参数,16个:
    rtk_t    *rtk      IO  rtk控制结构体
    nav_t    *nav      I   导航数据
    double   dt        I   接收机和基站的时间差
    double   *x        IO  状态变量
    double   *P        IO  状态变量的误差协方差阵
    int      sat       I   接收机和基站共同观测的卫星号列表
    double   *y        IO  相位/码残差
    double   *e        IO  观测矢量 (ecef)
    double   *azel     O   方位角和俯仰角 (rad)
    int      *iu       I   接收机和基站共同观测的卫星在接收机观测值中的index值列表
    int      *ir       I   接收机和基站共同观测的卫星在基站观测值中的index值列表
    int      ns        I   接收机和基站共同观测的卫星个数
    double   *v        O   实际观测量与预测观测量的残差
    double   *H        O   观测矩阵
    double   *R        O   测量误差的协方差
    int      *vflg     O   数据有效标志
    返回类型:
    int                O   (>0:ok,0:error)
    
    • 处理过程:
    1. 一些初始化和坐标转换。
    2. 若 电离层模式>=IONOOPT_EST,调用 ionmapf 计算电离层延迟因子。
    3. 若 对流层模式>=TROPOPT_EST,调用 prectrop 计算对流层延迟因子。
    4. 大循环,对每一个频率,循环5~
    5. 寻找仰角最高的参考卫星。
    6. 小循环,对每一种导航系统,对每一颗卫星,循环7~14步,计算双差。
    7. 用传入的没有差分的相位/码残差y计算双差残差v,并计算对应的H。
    8. 若要估计电离层参数,模式IONOOPT_EST,用电离层延迟因子修正v和H。
    9. 若要估计对流层参数,模式TROPOPT_EST,用对流层延迟因子修正v和H。
    10. 用相位偏移修正v和H。
    11. 若是GLONASS系统观测值,做相关修正。
    12. 根据选项maxinno的值检测是否要排除此观测数据。
    13. 计算单差的测量误差协方差Ri、Rj。
    14. 设置数据有效标志。
    15. 若为移动基站模式PMODE_MOVEB,计算移动基站限制并设置相应的数据有效标志。
    16. 用Ri、Rj计算双差的测量误差协方差R。
    展开全文
  • 高斯分布函数解析

    万次阅读 2012-06-26 15:20:10
    高斯模糊是种图像模糊滤波器,它用正态分布计算图像中每个像素的变换。 N 维空间正态分布方程为 在二维空间定义为 其中 r 是模糊半径 (r2 = u2 + v2),σ 是正态分布的标准偏差。 在二维空间中...
    
    

    高斯模糊是一种图像模糊滤波器,它用正态分布计算图像中每个像素的变换

    N 维空间正态分布方程为

    在二维空间定义为

    其中 r 是模糊半径 (r2 = u2 + v2),σ 是正态分布的标准偏差

    在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果,参见尺度空间实现

    理论上来讲,图像中每点的分布都不为零,这也就是说每个像素的计算都需要包含整幅图像。在实际应用中,在计算高斯函数的离散近似时,在大概3σ距离之外的像素都可以看作不起作用,这些像素的计算也就可以忽略。通常,图像处理程序只需要计算(6σ + 1) * (6σ + 1)的矩阵就可以保证相关像素影响。

    除了圆形对称之外,高斯模糊也可以在二维图像上对两个独立的一维空间分别进行计算,这叫作线性可分。这也就是说,使用二维矩阵变换得到的效果也可以通过在水平方向进行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换得到。从计算的角度来看,这是一项有用的特性,因为这样只需要 O(n * M * N) + O(m * M * N)计算,而不可分的矩阵则需要O(m * n * M * N)次计算,其中 M,N 是需要进行滤波的图像的维数,mn 是滤波器的维数。

    对一幅图像进行多次连续高斯模糊的效果与一次更大的高斯模糊可以产生同样的效果,大的高斯模糊的半径是所用多个高斯模糊半径平方和的平方根。例如,使用半径分别为 6 和 8 的两次高斯模糊变换得到的效果等同于一次半径为 10 的高斯模糊效果,sqrt(6 * 6 + 8 * 8) = 10。根据这个关系,使用多个连续较小的高斯模糊处理不会比单个高斯较大处理时间要少。

    在减小图像尺寸的场合经常使用高斯模糊。在进行欠采样的时候,通常在采样之前对图像进行低通滤波处理。这样就可以保证在采样图像中不会出现虚假的高频信息。高斯模糊有很好的特性,如没有明显的边界,这样就不会在滤波图像中形成震荡。

     

    将原来的模板改为:

         / 1   2   1 /

    H = | 2   4   2  | * 1 / 16

         / 1   2   1 /

    新的模板可一方面除去点状噪声,同时能较好地保留原图像的对比度,因此该模板得到了广泛的应用。由于这个模板是通过二维高斯(Gauss)函数得到的,故称为高斯模板。

     

     

     

     

     

    高斯函数有两个特性:  

      1:一个高斯函数跟另外一个高斯函数的卷积仍然是一个高斯函数,A*B=C   C的标准差的平方是A和B的标准差的平方和,也就是说卷积后的高斯函数更宽,模糊的效果更明显(直观上看,连续做高斯模糊运算,图像会越来越模糊。)  

      2:高斯函数的傅立叶变换仍然是一个高斯函数,如果原来的高斯函数越宽(标准差越大),变换后的高斯函数就越窄(标准差越小),也就是说一个越宽的高斯函数,低通(高阻)滤波的效果越明显,处理后的图像的细节就越不清楚(更模糊)。  

      要对数字图像做高斯模糊,就是用一个符合高斯函数分布的卷积核对数字图像做卷积运算。  

      要确定的有标准差的大小,卷积核的大小,最后的比例系数的大小。  

      一个标准差为1.4的高斯5x5的卷积核:  

      2   4   5   4   2  

      4   9   12   9   4  

      5   12   15   12   5  

      4   9   12   9   4  

      2   4   5   4   2        

         最后乘以比例系数   1/115 

     

    高斯(核)函数简介

    1函数的基本概念

    所谓径向基函数 (Radial Basis Function 简称 RBF), 就是某种沿径向对称的标量函数。 通常定义为空间中任一点x到某一中心xc之间欧氏距离的单调函数 , 可记作 k(||x-xc||), 其作用往往是局部的 , 即当x远离xc时函数取值很小。最常用的径向基函数是高斯核函数 ,形式为 k(||x-xc||)=exp{- ||x-xc||^2/(2*σ)^2) } 其中xc为核函数中心,σ为函数的宽度参数 , 控制了函数的径向作用范围。

    高斯函数具有五个重要的性质,这些性质使得它在早期图像处理中特别有用.这些性质表明,高斯平滑滤波器无论在空间域还是在频率域都是十分有效的低通滤波器,且在实际图像处理中得到了工程人员的有效使用.高斯函数具有五个十分重要的性质,它们是:

    (1)二维高斯函数具有旋转对称性,即滤波器在各个方向上的平滑程度是相同的.一般来说,一幅图像的边缘方向是事先不知道的,因此,在滤波前是无法确定一个方向上比另一方向上需要更多的平滑.旋转对称性意味着高斯平滑滤波器在后续边缘检测中不会偏向任一方向.

    (2)高斯函数是单值函数.这表明,高斯滤波器用像素邻域的加权均值来代替该点的像素值,而每一邻域像素点权值是随该点与中心点的距离单调增减的.这一性质是很重要的,因为边缘是一种图像局部特征,如果平滑运算对离算子中心很远的像素点仍然有很大作用,则平滑运算会使图像失真.

    (3)高斯函数的付立叶变换频谱是单瓣的.正如下面所示,这一性质是高斯函数付立叶变换等于高斯函数本身这一事实的直接推论.图像常被不希望的高频信号所污染(噪声和细纹理).而所希望的图像特征(如边缘),既含有低频分量,又含有高频分量.高斯函数付立叶变换的单瓣意味着平滑图像不会被不需要的高频信号所污染,同时保留了大部分所需信号.

    (4)高斯滤波器宽度(决定着平滑程度)是由参数σ表征的,而且σ和平滑程度的关系是非常简单的.σ越大,高斯滤波器的频带就越宽,平滑程度就越好.通过调节平滑程度参数σ,可在图像特征过分模糊(过平滑)与平滑图像中由于噪声和细纹理所引起的过多的不希望突变量(欠平滑)之间取得折衷.

    (5)由于高斯函数的可分离性,大高斯滤波器可以得以有效地实现.二维高斯函数卷积可以分两步来进行,首先将图像与一维高斯函数进行卷积,然后将卷积结果与方向垂直的相同一维高斯函数卷积.因此,二维高斯滤波的计算量随滤波模板宽度成线性增长而不是成平方增长.

    MATLAB代码

    close all;
    clc;
    alf=3;
    n=7;%定义模板大小
    n1=floor((n+1)/2);%确定中心
    for i=1:n
    a(i)= exp(-((i-n1).^2)/(2*alf^2));
    for j=1:n
    b(i,j) =exp(-((i-n1)^2+(j-n1)^2)/(4*alf))/(4*pi*alf);
    end
    end
    subplot(121),plot(a),title('一维高斯函数' );
    subplot(122),surf(b),title('二维高斯函数' );


     图像滤波

    1 图像滤波的基本概念

    图像常常被强度随机信号(也称为噪声)所污染.一些常见的噪声有椒盐(Salt & Pepper)噪声、脉冲噪声、高斯噪声等.椒盐噪声含有随机出现的黑白强度值.而脉冲噪声则只含有随机的白强度值(正脉冲噪声)或黑强度值(负脉冲噪声).与前两者不同,高斯噪声含有强度服从高斯或正态分布的噪声.研究滤波就是为了消除噪声干扰。

    图像滤波总体上讲包括空域滤波和频域滤波。频率滤波需要先进行傅立叶变换至频域处理然后再反变换回空间域还原图像,空域滤波是直接对图像的数据做空间变换达到滤波的目的。它是一种邻域运算,即输出图像中任何像素的值都是通过采用一定的算法,根据输入图像中对用像素周围一定邻域内像素的值得来的。如果输出像素是输入像素邻域像素的线性组合则称为线性滤波(例如最常见的均值滤波和高斯滤波),否则为非线性滤波(中值滤波、边缘保持滤波等)。

    线性平滑滤波器去除高斯噪声的效果很好,且在大多数情况下,对其它类型的噪声也有很好的效果。线性滤波器使用连续窗函数内像素加权和来实现滤波。特别典型的是,同一模式的权重因子可以作用在每一个窗口内,也就意味着线性滤波器是空间不变的,这样就可以使用卷积模板来实现滤波。如果图像的不同部分使用不同的滤波权重因子,且仍然可以用滤波器完成加权运算,那么线性滤波器就是空间可变的。任何不是像素加权运算的滤波器都属于非线性滤波器.非线性滤波器也可以是空间不变的,也就是说,在图像的任何位置上可以进行相同的运算而不考虑图像位置或空间的变化。

    2 图像滤波的计算过程分析

    滤波通常是用卷积或者相关来描述,而线性滤波一般是通过卷积来描述的。他们非常类似,但是还是会有不同。下面我们来根据相关和卷积计算过程来体会一下他们的具体区别:

    卷积的计算步骤:

    (1)       卷积核绕自己的核心元素顺时针旋转180

    (2)       移动卷积核的中心元素,使它位于输入图像待处理像素的正上方

    (3)       在旋转后的卷积核中,将输入图像的像素值作为权重相乘

    (4)       第三步各结果的和做为该输入像素对应的输出像素

    相关的计算步骤:

    1移动相关核的中心元素,使它位于输入图像待处理像素的正上方

    2将输入图像的像素值作为权重,乘以相关核

    3将上面各步得到的结果相加做为输出

    可以看出他们的主要区别在于计算卷积的时候,卷积核要先做旋转。而计算相关过程中不需要旋转相关核。

    例如: magic(3) =[8 1 6;3 5 7;4 9 2],旋转180度后就成了[2 9 4;7 5 3;6 1 8]

     高斯平滑滤波器的设计

      高斯函数的最佳逼近由二项式展开的系数决定,换句话说,用杨辉三角形(也称Pascal三角形)的第n行作为高斯滤波器的一个具有n个点的一维逼近,例如,五点逼近为:

    1 4 6 4 1


     它们对应于Pascal三角形的第5行.这一模板被用来在水平方向上平滑图像.在高斯函数可分离性性质中曾指出,二维高斯滤波器能用两个一维高斯滤波器逐次卷积来实现,一个沿水平方向,一个沿垂直方向.实际中,这种运算可以通过使用单个一维高斯模板,对两次卷积之间的图像和最后卷积的结果图像进行转置来完成.

    这一技术在模板尺寸N约为10时的滤波效果极好.对较大的滤波器,二项式展开系数对大多数计算机来说都太多.但是,任意大的高斯滤波器都能通过重复使用小高斯滤波器来实现.高斯滤波器的二项式逼近的σ可用高斯函数拟合二项式系数的最小方差来计算.


      设计高斯滤波器的另一途径是直接从离散高斯分布中计算模板权值。为了计算方便,一般希望滤波器权值是整数。在模板的一个角点处取一个值,并选择一个K使该角点处值为1。通过这个系数可以使滤波器整数化,由于整数化后的模板权值之和不等于1,为了保证图像的均匀灰度区域不受影响,必须对滤波模板进行权值规范化。


     

     


     

      高斯滤波器的采样值或者高斯滤波器的二项式展开系数可以形成离散高斯滤波器.当用离散高斯滤波器进行卷积时,其结果是一个更大的高斯离散滤波器.若一幅图像用N*N离散高斯滤波器进行平滑,接着再用M*M离散高斯滤波器平滑的话,那么平滑结果就和用(N+M-1)*(N+M-1)离散高斯滤波器平滑的结果一样.换言之,在杨辉三角形中用第N行和第M行卷积形成了第N+M-1行.


     

     使用高斯滤波器进行图像的平滑


     

       如果适应卷积运算对图像进行滤波,在matlab中可以通过2个不同的函数来实现conv2imfliter。他们的调用方式如下:


     

    Img_n = conv2(Img,g,'same');  和
    Img_n = imfilter(Img,g,'conv');


     

    这两种函数处理的结果是完全一样的。


     


       
     imfiler函数在默认的情况下,对图像的滤波计算用的是相关


     

    Img_n = imfilter(Img,g);%使用相关运算滤波


     

    下面是一个简单的例子展示了使用相同的高斯滤波核函数,相关运算和卷积运算对图像平滑的效果可以直接后边附的程序查看。


     


    由结果可以看出相关运算和卷积运算的在用于图像平滑滤波时效果差别不大。当模板大小N>50的时候。边界的系数已经非常小,对运算起到的作用和微乎其微,所以平滑的结果差别已经非常细微,肉眼几乎难以察觉。


     

    example.m


    clear all


     

    I = imread('lena.bmp');


     

    Img = double(I);


     

    alf=3;


     

    n=10;%定义模板大小


     

    n1=floor((n+1)/2);%计算中心


     

    for i=1:n


     


    for j=1:n


     


    b(i,j) =exp(-((i-n1)^2+(j-n1)^2)/(4*alf))/(4*pi*alf);


     


    end


     

    end


     

    Img_n = uint8(conv2(Img,b,'same'));


     

    K=uint8(imfilter(Img,b));


     

    Img_n2=uint8(imfilter(Img,b,'conv'));


     

    J=(Img_n2)-Img_n;


    flag=mean(J()


     

    subplot(131),imshow(I);title('原图')


     

    subplot(132),imshow(Img_n);title('卷积运算图')


     

    subplot(133),imshow(K);title('相关运算图')


     

    figure(2),surf(b);


    展开全文
  • JDK8函数式接口

    千次阅读 2019-05-12 11:54:34
    Supplier函数式接口 Consumer函数式接口 Function函数式接口 Predicate函数式接口

    主要内容

    • Supplier函数式接口
    • Consumer函数式接口
    • Function函数式接口
    • Predicate函数式接口

    第一章 函数式接口

    1.1 概念

    函数式接口在Java中是指:有且仅有一个抽象方法的接口。

    函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

    备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。

    1.2 格式

    只要确保接口中有且仅有一个抽象方法即可:

    修饰符 interface 接口名称 {

    public abstract 返回值类型 方法名称(可选参数信息);

    // 其他非抽象方法内容

    }

    由于接口当中抽象方法的 public abstract 是可以省略的,所以定义一个函数式接口很简单:

    public interface MyFunctionalInterface {
    
    void myMethod();
    
    }
    

    1.3 @FunctionalInterface注解

    与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上:

    @FunctionalInterface
    
    public interface MyFunctionalInterface {
    
    void myMethod();
    
    }
    

    一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

    1.4 自定义函数式接口

    对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:

    public class Demo09FunctionalInterface {
    
    //	使用自定义的函数式接口作为方法参数
    
    private static void doSomething(MyFunctionalInterface inter) {
    
    inter.myMethod(); // 调用自定义的函数式接口方法
    
    }
    
    public static void main(String[] args) {
    
    //	调用使用函数式接口的方法
    
    doSomething(() ‐> System.out.println("Lambda执行啦!"));
    
    }
    
    }
    

    第二章 函数式编程

    在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。

    下面我们做一个初探。

    2.1 Lambda的延迟执行

    有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

    性能浪费的日志案例

    注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。

    一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:

    public class Demo01Logger {
    
    private static void log(int level, String msg) {
    
    if (level == 1) {
    
    System.out.println(msg);
    
    }
    
    }
    
    public static void main(String[] args) {
    
    String msgA = "Hello";
    
    String msgB = "World";
    
    String msgC = "Java";
    
    log(1, msgA + msgB + msgC);
    
    }
    
    }
    

    这段代码存在问题:无论级别是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

    备注:SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行字符串的拼接,而是将字符串的若干部分作为可变参数传入方法中,仅在日志级别满足要求的情况下才会进行字符串拼接。例如:
    LOGGER.debug(“变量{}的取值为{}。”, “os”, “macOS”) ,其中的大括号 {}
    为占位符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字符串拼接。这也是一种可行解决方案,但Lambda可以做到更好。

    体验Lambda的更优写法

    使用Lambda必然需要一个函数式接口:

    @FunctionalInterface
    
    public interface MessageBuilder {
    
    String buildMessage();
    
    }
    

    然后对 log 方法进行改造:

    public class Demo02LoggerLambda {
    
    private static void log(int level, MessageBuilder builder) { if (level == 1) {
    
    System.out.println(builder.buildMessage());
    
    }
    
    }
     
    public static void main(String[] args) {
    
    String msgA = "Hello";
    
    String msgB = "World";
    
    String msgC = "Java";
    
    log(1, () ‐> msgA + msgB + msgC );
    
    }
    
    }
    

    这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。

    证明Lambda的延迟

    下面的代码可以通过结果进行验证:

    public class Demo03LoggerDelay {
    
    private static void log(int level, MessageBuilder builder) { if (level == 1) {
    
    System.out.println(builder.buildMessage());
    
    }
    
    }
    
    public static void main(String[] args) {
    
    String msgA = "Hello";
    
    String msgB = "World";
    
    String msgC = "Java";
    
    log(2, () ‐> {
    
    System.out.println("Lambda执行!");
    
    return msgA + msgB + msgC;
    
    });
    
    }
    
    }
    

    从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。

    扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调用其所在方法是在条件判断之后才执行的。

    2.2 使用Lambda作为参数和返回值

    在Java8 以前,我们无法将函数作为函数作为参数传递给一个方法,也无法声明返回一个函数的方法。
    在Javascript中,函数参数是一个函数,返回值是另外一个函数的情况是非常常见的;JavaScrpt是一门非常典型的函数式语言。

    如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。

    例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。

    public class Demo04Runnable {
    
    private static void startThread(Runnable task) {
    
    new Thread(task).start();
    
    }
    
    public static void main(String[] args) {
    
    startThread(() ‐> System.out.println("线程任务执行!"));
    
    }
    
    }
    

    类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。

    import java.util.Arrays;
    
    import java.util.Comparator;
    
    public class Demo06Comparator {
    
    private static Comparator<String> newComparator() { return (a, b) ‐> b.length() ‐ a.length();
    
    }
    
    public static void main(String[] args) {
    
    String[] array = { "abc", "ab", "abcd" };
    
    System.out.println(Arrays.toString(array));
    
    Arrays.sort(array, newComparator());
    
    System.out.println(Arrays.toString(array));
    
    }
    
    }
    

    其中直接return一个Lambda表达式即可。

    第三章 常用函数式接口

    JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在 java.util.function 包中被提供。

    下面是最简单的几个接口及使用示例。

    3.1 Supplier接口

    java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

    import java.util.function.Supplier;
    
    public class Demo08Supplier {
    
    private static String getString(Supplier<String> function) { return function.get();
    
    }
    
    public static void main(String[] args) {
    
    String msgA = "Hello";
    
    String msgB = "World";
    
    System.out.println(getString(() ‐> msgA + msgB));
    
    }
    
    }
    

    3.2 练习:求数组元素最大值

    题目

    使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。

    解答

    public class Demo02Test {
    
    //定一个方法,方法的参数传递Supplier,泛型使用Integer
    
    public static int getMax(Supplier<Integer> sup){
    
    return sup.get();
    
    }
    
    public static void main(String[] args) {
    
    int arr[] = {2,3,4,52,333,23};
    
    //调用getMax方法,参数传递Lambda
    
    int maxNum = getMax(()‐>{
    
    //计算数组的最大值
    
    int max = arr[0];
    
    for(int i : arr){
    
    if(i>max){
    
    max = i;
    
    }
    
    }
    
    return max;
    
    });
    
    System.out.println(maxNum);
    
    }
    
    }
    

    3.3 Consumer接口

    java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,

    其数据类型由泛型决定。

    抽象方法:accept

    Consumer 接口中包含抽象方法 void accept(T t) ,意为消费一个指定泛型的数据。基本使用如:

    import java.util.function.Consumer;
    
    public class Demo09Consumer {
    
    private static void consumeString(Consumer<String> function) { function.accept("Hello");
    
    }
    
    public static void main(String[] args) {
    
    consumeString(s ‐> System.out.println(s));
    
    }
    
    }
    

    当然,更好的写法是使用方法引用。

    默认方法:andThen

    如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代码:

    default Consumer andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);

    return (T t) ‐> { accept(t); after.accept(t); };

    }

    备注: java.util.Objects 的 requireNonNull 静态方法将会在参数为null时主动抛出

    NullPointerException 异常。这省去了重复编写if语句和抛出空指针异常的麻烦。

    要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:

    import java.util.function.Consumer;
    
    public class Demo10ConsumerAndThen {
    
    private static void consumeString(Consumer<String> one, Consumer<String> two) { one.andThen(two).accept("Hello");
    
    }
    
    public static void main(String[] args) {
    
    consumeString(
    
    s ‐> System.out.println(s.toUpperCase()),
    
    s ‐> System.out.println(s.toLowerCase()));
    
    }
    
    }
    

    运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

    3.4 练习:格式化打印信息

    题目

    下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。要求将打印姓名的动作作为第一个 Consumer 接口的Lambda实例,将打印性别的动作作为第二个 Consumer 接口的Lambda实例,将两个 Consumer 接口按照顺序“拼接”到一起。

    public static void main(String[] args) {
    
    String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };
    
    }
    

    解答

    import java.util.function.Consumer;
    
    public class DemoConsumer {
    
    public static void main(String[] args) {
    
    String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" }; printInfo(s ‐> System.out.print("姓名:" + s.split(",")[0]),
    
    s ‐> System.out.println("。性别:" + s.split(",")[1] + "。"), array);
    
    }
    
    private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) { for (String info : array) {
    
    one.andThen(two).accept(info); // 姓名:迪丽热巴。性别:女。
    
    }
    
    }
    
    }
    

    3.5 Predicate接口

    有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。

    抽象方法:test

    Predicate 接口中包含一个抽象方法: boolean test(T t) 。用于条件判断的场景:

    import java.util.function.Predicate;
    
    public class Demo15PredicateTest {
    
    private static void method(Predicate<String> predicate) { boolean veryLong = predicate.test("HelloWorld"); System.out.println("字符串很长吗:" + veryLong);
    
    }
    
    public static void main(String[] args) {
    
    method(s ‐> s.length() > 5);
    
    }
    
    }
    

    条件判断的标准是传入的Lambda表达式逻辑,只要字符串长度大于5则认为很长。

    默认方法:and

    既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法 and 。其JDK源码为:

    default Predicate and(Predicate<? super T> other) {
    Objects.requireNonNull(other);

    return (t) ‐> test(t) && other.test(t);

    }

    如果要判断一个字符串既要包含大写“H”,又要包含大写“W”,那么:

    import java.util.function.Predicate;
    
    public class Demo16PredicateAnd {
    
    private static void method(Predicate<String> one, Predicate<String> two) { boolean isValid = one.and(two).test("Helloworld"); System.out.println("字符串符合要求吗:" + isValid);
    
    }
    
    public static void main(String[] args) {
    
    method(s ‐> s.contains("H"), s ‐> s.contains("W"));
    
    }
    
    }
    

    默认方法:or

    与 and 的“与”类似,默认方法 or 实现逻辑关系中的“或”。JDK源码为:

    default Predicate or(Predicate<? super T> other) {
    Objects.requireNonNull(other);

    return (t) ‐> test(t) || other.test(t);

    }

    如果希望实现逻辑“字符串包含大写H或者包含大写W”,那么代码只需要将“and”修改为“or”名称即可,其他都不变:

    import java.util.function.Predicate;
    
    public class Demo16PredicateAnd {
    
    private static void method(Predicate<String> one, Predicate<String> two) { boolean isValid = one.or(two).test("Helloworld"); System.out.println("字符串符合要求吗:" + isValid);
    
    }
    
    public static void main(String[] args) {
    
    method(s ‐> s.contains("H"), s ‐> s.contains("W"));
    
    }
    
    }
    

    默认方法:negate

    “与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法 negate 的JDK源代码为:

    default Predicate negate() {

    return (t) ‐> !test(t);

    }

    从实现中很容易看出,它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前调用 negate 方法,正如 and 和 or 方法一样:

    import java.util.function.Predicate;
    
    public class Demo17PredicateNegate {
    
    private static void method(Predicate<String> predicate) { boolean veryLong = predicate.negate().test("HelloWorld"); System.out.println("字符串很长吗:" + veryLong);
    
    }
    
    public static void main(String[] args) {
    
    method(s ‐> s.length() < 5);
    
    }
    
    }
    

    3.6 练习:集合信息筛选

    题目

    数组当中有多条“姓名+性别”的信息如下,请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合

    ArrayList 中,需要同时满足两个条件:

    1. 必须为女生;

    2. 姓名为4个字。

      public class DemoPredicate {

      public static void main(String[] args) {
      
      String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
      
      }
      
      }
      

    解答

    import java.util.ArrayList;
    
    import java.util.List;
    
    import java.util.function.Predicate;
    
    public class DemoPredicate {
    
    public static void main(String[] args) {
    
    String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" }; List<String> list = filter(array,
    
    s ‐> "女".equals(s.split(",")[1]),
    
    s ‐> s.split(",")[0].length() == 4);
    
    System.out.println(list);
    
    }
    
    private static List<String> filter(String[] array, Predicate<String> one, Predicate<String> two) {
    
    List<String> list = new ArrayList<>();
    
    for (String info : array) {
    
    if (one.and(two).test(info)) {
    
    list.add(info);
    
    }
    
    }
    
    return list;
    
    }
    
    }
    

    3.7 Function接口

    java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

    抽象方法:apply

    Function 接口中最主要的抽象方法为: R apply(T t) ,根据类型T的参数获取类型R的结果。

    使用的场景例如:将 String 类型转换为 Integer 类型。

    import java.util.function.Function;
    
    public class Demo11FunctionApply {
    
    private static void method(Function<String, Integer> function) { int num = function.apply("10");
    
    System.out.println(num + 20);
    
    }
    
    public static void main(String[] args) {
    
    method(s ‐> Integer.parseInt(s));
    
    }
    
    }
    

    当然,最好是通过方法引用的写法。

    默认方法:andThen

    Function 接口中有一个默认的 andThen 方法,用来进行组合操作。JDK源代码如:

    default Function<T, V> andThen(Function<? super R, ? extends V>
    after) { Objects.requireNonNull(after);

    return (T t) ‐> after.apply(apply(t));

    }

    该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:

    import java.util.function.Function;
    
    public class Demo12FunctionAndThen {
    
    private static void method(Function<String, Integer> one, Function<Integer, Integer> two) { int num = one.andThen(two).apply("10");
    
    System.out.println(num + 20);
    
    }
    
    public static void main(String[] args) {
    
    method(str‐>Integer.parseInt(str)+10, i ‐> i *= 10);
    
    }
    
    }
    

    第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一起。

    请注意,Function的前置条件泛型和后置条件泛型可以相同。

    3.8 练习:自定义函数模型拼接

    题目

    请使用 Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为:

    String str = “赵丽颖,20”;

    1. 将字符串截取数字年龄部分,得到字符串;

    2. 将上一步的字符串转换成为int类型的数字;

    3. 将上一步的int数字累加100,得到结果int数字。

    解答

    import java.util.function.Function;
    
    public class DemoFunction {
    
    public static void main(String[] args) {
    
    String str = "赵丽颖,20";
    
    int age = getAgeNum(str, s ‐> s.split(",")[1],
    
    s	‐>Integer.parseInt(s), n ‐> n += 100);
    System.out.println(age);
    
    }
    
    private static int getAgeNum(String str, Function<String, String> one,
    
    Function<String, Integer> two,
    
    Function<Integer, Integer> three) {
    
    return one.andThen(two).andThen(three).apply(str);
    
    }
    
    }
    

    4.补充接口

    4.1 public interface Enumeration ()

    这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中

    实现枚举接口的对象生成一系列元素,一次一个。 连续调用nextElement方法返回系列的连续元素。

    在这里插入图片描述

    例如,获取所有请求头时当作迭代器使用:

         
            //1.获取所有请求头名称
            Enumeration<String> headerNames = request.getHeaderNames();
            //2.遍历
            while(headerNames.hasMoreElements()){
                String name = headerNames.nextElement();
                //根据名称获取请求头的值
                String value = request.getHeader(name);
                System.out.println(name+"---"+value);
            }
    

    注意:该接口的功能由Iterator接口复制。 此外,Iterator还添加了一个可选的删除操作,并具有较短的方法名称。
    新的实现应该考虑使用迭代器优先于枚举。

    展开全文
  • Tensorflow操作与函数全面解析

    千次阅读 2018-09-14 16:23:46
    转载自:http://blog.csdn.net/lenbow/article/details/521527661、tensorflow的基本运作为了快速的熟悉TensorFlow编程,下面从段简单的代码开始:import ...
  • 利用反汇编手段解析C语言函数

    万次阅读 2012-12-29 14:23:17
    函数是 C语言中的重要概念。利用好函数能够充分利用系统库的功能写出模块独立、易于维护和修改的程序。函数并不是 C 语言独有的概念,其他语言中的方法、过程等本质上都是函数。可见函数在教学中的重要意义。在教学...
  • 到目前为止,在本系列的每期文章中,我都说明了为什么理解函数式编程非常重要。但是,有些原因是在多期文章中进行说明的,只有在综合思路的更大背景中,才可以完全了解这些原因。在本期文章中,我会探讨函数式编程...
  • 函数式编程详解

    万次阅读 2018-01-10 14:20:36
    函数式编程中函数有三种不同的解读方式,分别为纯函数、高阶函数和一等函数。本文分别对这三者的概念、应用和联系进行详解。纯函数定义:1. 相同的输入必定产生相同的输出2. 在计算的过程中,不会产生副作用满足上述...
  • 在看机器学习实战时候,到第三章的对决策树画图的时候,有段递归函数怎么都看不懂,因为以后想选这个方向为自己的职业导向,抱着精看的态度,对这本树进行地毯扫描,所以就没跳过,一直卡了天多,才差不多搞懂...
  • 牛客网刷题汇总()附解析

    千次阅读 2017-02-09 10:32:31
    纯虚函数是在基类声明的虚函数,它在基类中没有定义,但是要求派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后面添加“=0”,比如 virtual void f()=0;而C++中包含纯虚函数的类称为抽象类...
  • JAVA8新特性 函数式编程Lambda

    千次阅读 2016-08-30 13:17:37
    函数式编程:以处理数据的方式处理代码,这意味着函数可以被赋值给变量,传递给函数等,函数应该是第等级的值。 纯函数:对于给定的输入总会返回相同的输出,并且不会产生影响或者改变任何的程序状态 ...
  • 在 JavaScript 中函数就是个普通的对象 (可以通过 new Function() ),我们可以把函数存储到变量/数组中,它还可以作为另函数的参数和返回值,甚至我们可以在程序运行的时候通过 new Function(‘alert(1)’) 来...
  • 一文读懂JavaScript函数式编程重点-- 实践 总结 好记性不如烂笔头,有时间将JS函数式编程,在JS方面毕竟有限,如果真要...柯里化是把个多参数函数转化为个嵌套的一元函数的过程。下面我们用介绍柯里化时候很...
  • 过滤 | filter 过滤符合一定条件的元素 定义 方法签名 def filter(p: (A) ⇒ Boolean): ...传入函数对象接收个集合类型的参数返回布尔类型,满足条件返回true, 不满足返回false 返回值 Traversabl...
  • 将约束条件转化成函数表达式的部分,使新的函数式变为无约束的二规划问题: 约束条件转换: 将等式和不等式转换后的子融合: 算法步骤: 1 确定初始点x0,初始罚银子Mk(可取M1=1),设置精确度 2 ...
  • YOLO完成一次前向过程只用8.52 billion 运算,而VGG-16要30.69billion。YOLO框架使用了类似GoogLeNet的网络结构,计算量比VGG-16小,但YOLO精度只是稍低于VGG-16。 Darknet-19 YOLO v2基于一个新的分类model,...
  • Apriori算法解析

    万次阅读 2017-03-09 10:46:22
    事务(transaction):一个事务,可以看做是发生的一次事件,比如一个人一次的购物清单,用符号 T T 表示,一般来说, T T 包含一个它本身的身份—— T I D TID 以及事务中的一个项集。当然,如果我们收集很多这样的...
  • 1 积性函数   如果读者没有深入学过数论,第一次看到这些词语:积性函数、欧拉函数、狄利克雷卷积、莫比乌斯函数、莫比乌斯反演、杜教筛,是不是感到高深莫测、头皮发怵?   即使读者学过一些数论,但是还没有...
  • 本文分为三部分,前言和实验基础copy自tornadomeet博客;后面代码详解为自己原创。...本次实验验要完成的任务是对MINST进行手写数字识别,实验内容及步骤参考网页教程Exercise: Implement deep network
  • 与React类组件相比,React函数式组件究竟有何不同? 一般的回答都是: 类组件比函数式组件多了更多的特性,比如 state,那如果有 Hooks 之后呢? 函数组件性能比类组件好,但是在现代浏览器中,闭包和类的原始性能...
  • http://dawnote.net/2018/01/13/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B8%AD%E6%A0%B8%E5%87%BD%E6%95%B0-Kernel-%E7%9A%84%E7%90%86%E8%A7%A3%E4%B8%8EKernel-SVM%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/第节 ...
  • ReactiveX是Reactive Extensions的缩写,一般简写为Rx,微软给的定义是,Rx是函数库,让开发者可以利用可观察序列和LINQ风格查询操作符来编写异步和基于事件的程序,开发者可以用Observables表示异步数据流,用...
  • 高斯过程可以被认为是种机器学习算法,它利用点与点之间同质性的度量作为核函数,以从输入的训练数据预测未知点的值。本文从理论推导和实现详细地介绍了高斯过程,并在后面提供了用它来近似求未知函数最优解的方法...
  • Kotlin是种增加许多新功能的语言,允许编写更简洁易读的代码,这使得我们的代码更易于维护。例如使用顶层函数和属性从此消除Java中的static、中缀表达式调用和解构声明等。 1、为什么要用顶层函数替代Java中的...
  • 2016大家新年好!这是今年的第篇文章,那么应CSDN工作人员的建议,为了...另外,作为个对新鲜事物从来后知后觉的人,我终于也在新的一年里改用MarkDown编辑器来写博客了,希望大家在我的博客里也能体验到新年新...
  • 介绍优先队列的重要实现——左堆的合并,插入,删除,建堆等基本原理以及java代码实现。
  • 一般的,在个变化过程中,假设有两个变量x、y,如果对于任意个x都有唯一确定的个y和它对应,那么就称y是x的函数,其中x是自变量,y是因变量,x的取值范围叫做这个函数的定义域,相应y的取值范围叫做函数的值域...
  • 【学术】外罚函数与内罚函数

    千次阅读 2017-12-14 21:50:11
    (http://blog.csdn.net/ice110956/article/details/17557795 )(http://blog.csdn.net/ice110956/article/details/17562429 )关于无约束问题,我们通过最优性条件能够直接求出解,那么这种方法称为解析法。...
  • Canny边缘检测算法解析

    千次阅读 2018-07-15 09:20:49
    3) 图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。 为了满足这些要求,Canny使用了变分法。Canny检测器中的最优函数使用四个指数项的和来描述,它可以由高斯函数的一阶导数...
  • 如需转载请注明出处:python–爬虫–获取和解析存储网页内容–以薄荷网为例 我们在之前的文章中已经学习了如何进行数据抓包和截取 以及分析 访问网页。 例如: 抓取app数据教程–fiddler抓包数据截取-薄荷app为例 本章...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,278
精华内容 22,111
关键字:

一次函数的解析式步骤